From cadd3f71c0dbd6f35933037b4cb1a2951f35a3ed Mon Sep 17 00:00:00 2001 From: Sotr Date: Mon, 4 Mar 2019 18:16:32 +0800 Subject: [PATCH] Upstream Paper --- .gitignore | 39 + CONTRIBUTING.md | 51 + LGPL.txt | 165 + LICENCE.txt | 674 ++++ README.md | 284 ++ pom.xml | 322 ++ .../co/aikar/timings/MinecraftTimings.java | 132 + .../co/aikar/timings/WorldTimingsHandler.java | 108 + .../java/com/destroystokyo/paper/Metrics.java | 627 ++++ .../com/destroystokyo/paper/PaperCommand.java | 238 ++ .../com/destroystokyo/paper/PaperConfig.java | 465 +++ .../destroystokyo/paper/PaperWorldConfig.java | 597 +++ .../paper/PaperWorldEntityList.java | 121 + .../destroystokyo/paper/PaperWorldMap.java | 191 + .../ServerSchedulerReportingWrapper.java | 38 + .../antixray/ChunkPacketBlockController.java | 45 + .../ChunkPacketBlockControllerAntiXray.java | 684 ++++ .../paper/antixray/ChunkPacketInfo.java | 81 + .../antixray/ChunkPacketInfoAntiXray.java | 29 + .../paper/antixray/DataBitsReader.java | 56 + .../paper/antixray/DataBitsWriter.java | 84 + .../paper/console/PaperConsole.java | 40 + .../console/TerminalConsoleCommandSender.java | 17 + .../paper/entity/CraftRangedEntity.java | 19 + .../paper/entity/PaperPathfinder.java | 113 + .../PaperLootableBlockInventory.java | 33 + .../PaperLootableEntityInventory.java | 28 + .../loottable/PaperLootableInventory.java | 71 + .../loottable/PaperLootableInventoryData.java | 179 + .../PaperMinecartLootableInventory.java | 64 + .../PaperTileEntityLootableInventory.java | 67 + .../network/PaperLegacyStatusClient.java | 73 + .../paper/network/PaperNetworkClient.java | 50 + .../network/PaperServerListPingEventImpl.java | 31 + .../paper/network/PaperStatusClient.java | 11 + .../StandardPaperServerListPingEventImpl.java | 112 + .../paper/profile/CraftPlayerProfile.java | 280 ++ .../profile/PaperAuthenticationService.java | 30 + .../profile/PaperGameProfileRepository.java | 68 + .../profile/PaperMinecraftSessionService.java | 39 + .../profile/PaperUserAuthentication.java | 11 + .../paper/proxy/VelocityProxy.java | 67 + .../paper/util/PriorityQueuedExecutor.java | 347 ++ .../paper/util/RedstoneWireTurbo.java | 910 +++++ .../YggdrasilGameProfileRepository.java | 101 + .../mojang/brigadier/tree/CommandNode.java | 207 ++ .../net/minecraft/server/Advancement.java | 436 +++ .../server/AdvancementDataPlayer.java | 466 +++ .../server/AdvancementDataWorld.java | 119 + .../net/minecraft/server/Advancements.java | 109 + .../net/minecraft/server/ArgumentBlock.java | 541 +++ .../net/minecraft/server/ArgumentEntity.java | 171 + .../server/ArgumentParserSelector.java | 630 ++++ .../minecraft/server/AttributeInstance.java | 33 + .../net/minecraft/server/AttributeRanged.java | 39 + .../net/minecraft/server/AxisAlignedBB.java | 303 ++ .../minecraft/server/BaseBlockPosition.java | 105 + .../java/net/minecraft/server/BiomeBase.java | 626 ++++ src/main/java/net/minecraft/server/Block.java | 1574 ++++++++ .../net/minecraft/server/BlockBeacon.java | 78 + .../java/net/minecraft/server/BlockBed.java | 310 ++ .../minecraft/server/BlockButtonAbstract.java | 224 ++ .../net/minecraft/server/BlockCactus.java | 117 + .../java/net/minecraft/server/BlockCake.java | 91 + .../net/minecraft/server/BlockCauldron.java | 267 ++ .../java/net/minecraft/server/BlockChest.java | 289 ++ .../minecraft/server/BlockChorusFlower.java | 259 ++ .../java/net/minecraft/server/BlockCocoa.java | 125 + .../net/minecraft/server/BlockCommand.java | 229 ++ .../minecraft/server/BlockConcretePowder.java | 92 + .../java/net/minecraft/server/BlockCoral.java | 67 + .../net/minecraft/server/BlockCoralFan.java | 42 + .../minecraft/server/BlockCoralFanWall.java | 42 + .../net/minecraft/server/BlockCoralPlant.java | 47 + .../java/net/minecraft/server/BlockCrops.java | 186 + .../java/net/minecraft/server/BlockData.java | 25 + .../server/BlockDaylightDetector.java | 85 + .../minecraft/server/BlockDiodeAbstract.java | 207 ++ .../server/BlockDirtSnowSpreadable.java | 51 + .../net/minecraft/server/BlockDispenser.java | 155 + .../java/net/minecraft/server/BlockDoor.java | 232 ++ .../net/minecraft/server/BlockDragonEgg.java | 81 + .../net/minecraft/server/BlockDropper.java | 72 + .../minecraft/server/BlockEnderPortal.java | 49 + .../java/net/minecraft/server/BlockFire.java | 459 +++ .../net/minecraft/server/BlockFluids.java | 186 + .../java/net/minecraft/server/BlockGrass.java | 81 + .../java/net/minecraft/server/BlockIce.java | 72 + .../net/minecraft/server/BlockIceFrost.java | 128 + .../net/minecraft/server/BlockJukeBox.java | 106 + .../java/net/minecraft/server/BlockKelp.java | 97 + .../net/minecraft/server/BlockLeaves.java | 174 + .../java/net/minecraft/server/BlockLever.java | 138 + .../java/net/minecraft/server/BlockMagma.java | 60 + .../server/BlockMinecartDetector.java | 271 ++ .../net/minecraft/server/BlockMobSpawner.java | 45 + .../minecraft/server/BlockMonsterEggs.java | 50 + .../net/minecraft/server/BlockMushroom.java | 103 + .../net/minecraft/server/BlockNetherWart.java | 63 + .../java/net/minecraft/server/BlockNote.java | 78 + .../net/minecraft/server/BlockObserver.java | 108 + .../java/net/minecraft/server/BlockOre.java | 84 + .../net/minecraft/server/BlockPiston.java | 433 +++ .../java/net/minecraft/server/BlockPlant.java | 47 + .../net/minecraft/server/BlockPortal.java | 381 ++ .../net/minecraft/server/BlockPosition.java | 422 +++ .../minecraft/server/BlockPoweredRail.java | 246 ++ .../server/BlockPressurePlateAbstract.java | 146 + .../server/BlockPressurePlateBinary.java | 105 + .../server/BlockPressurePlateWeighted.java | 75 + .../minecraft/server/BlockPumpkinCarved.java | 161 + .../server/BlockRedstoneComparator.java | 161 + .../minecraft/server/BlockRedstoneLamp.java | 67 + .../minecraft/server/BlockRedstoneOre.java | 139 + .../minecraft/server/BlockRedstoneTorch.java | 187 + .../minecraft/server/BlockRedstoneWire.java | 585 +++ .../java/net/minecraft/server/BlockReed.java | 86 + .../net/minecraft/server/BlockSapling.java | 86 + .../net/minecraft/server/BlockShulkerBox.java | 242 ++ .../minecraft/server/BlockSkullAbstract.java | 84 + .../java/net/minecraft/server/BlockSnow.java | 125 + .../net/minecraft/server/BlockSnowBlock.java | 35 + .../java/net/minecraft/server/BlockSoil.java | 142 + .../net/minecraft/server/BlockSponge.java | 124 + .../java/net/minecraft/server/BlockState.java | 41 + .../minecraft/server/BlockStateBoolean.java | 46 + .../net/minecraft/server/BlockStateEnum.java | 85 + .../minecraft/server/BlockStateInteger.java | 73 + .../java/net/minecraft/server/BlockStem.java | 117 + .../java/net/minecraft/server/BlockTNT.java | 140 + .../net/minecraft/server/BlockTallPlant.java | 91 + .../net/minecraft/server/BlockTrapdoor.java | 186 + .../net/minecraft/server/BlockTripwire.java | 222 ++ .../minecraft/server/BlockTripwireHook.java | 245 ++ .../net/minecraft/server/BlockTurtleEgg.java | 174 + .../java/net/minecraft/server/BlockVine.java | 385 ++ .../net/minecraft/server/BlockWaterLily.java | 28 + .../minecraft/server/BlockWitherSkull.java | 116 + .../minecraft/server/BossBattleCustom.java | 230 ++ src/main/java/net/minecraft/server/Chunk.java | 1530 ++++++++ .../java/net/minecraft/server/ChunkCache.java | 220 ++ .../minecraft/server/ChunkCoordIntPair.java | 97 + .../net/minecraft/server/ChunkGenerator.java | 45 + .../server/ChunkGeneratorAbstract.java | 165 + .../java/net/minecraft/server/ChunkMap.java | 142 + .../server/ChunkProviderGenerate.java | 274 ++ .../minecraft/server/ChunkProviderServer.java | 507 +++ .../minecraft/server/ChunkRegionLoader.java | 1155 ++++++ .../net/minecraft/server/ChunkSection.java | 163 + .../minecraft/server/ChunkTaskScheduler.java | 148 + .../net/minecraft/server/CombatTracker.java | 200 + .../server/CommandBlockListenerAbstract.java | 194 + .../net/minecraft/server/CommandDebug.java | 149 + .../minecraft/server/CommandDispatcher.java | 351 ++ .../net/minecraft/server/CommandEffect.java | 131 + .../minecraft/server/CommandForceload.java | 142 + .../net/minecraft/server/CommandGamemode.java | 71 + .../net/minecraft/server/CommandGamerule.java | 43 + .../server/CommandListenerWrapper.java | 256 ++ .../server/CommandSpreadPlayers.java | 305 ++ .../net/minecraft/server/CommandSummon.java | 52 + .../net/minecraft/server/CommandTeleport.java | 223 ++ .../java/net/minecraft/server/Container.java | 649 ++++ .../net/minecraft/server/ContainerAnvil.java | 408 +++ .../net/minecraft/server/ContainerBeacon.java | 137 + .../server/ContainerBrewingStand.java | 206 ++ .../net/minecraft/server/ContainerChest.java | 107 + .../minecraft/server/ContainerDispenser.java | 93 + .../server/ContainerEnchantTable.java | 409 +++ .../minecraft/server/ContainerFurnace.java | 188 + .../net/minecraft/server/ContainerHopper.java | 87 + .../net/minecraft/server/ContainerHorse.java | 115 + .../minecraft/server/ContainerMerchant.java | 124 + .../net/minecraft/server/ContainerPlayer.java | 198 + .../minecraft/server/ContainerShulkerBox.java | 88 + .../minecraft/server/ContainerWorkbench.java | 157 + .../net/minecraft/server/ControllerJump.java | 21 + .../net/minecraft/server/CraftingManager.java | 161 + .../net/minecraft/server/CrashReport.java | 265 ++ .../net/minecraft/server/CustomFunction.java | 169 + .../minecraft/server/CustomFunctionData.java | 255 ++ .../net/minecraft/server/DamageSource.java | 213 ++ .../java/net/minecraft/server/DataBits.java | 75 + .../server/DataConverterFlatten.java | 399 ++ .../minecraft/server/DataConverterMap.java | 41 + .../server/DataConverterRegistry.java | 346 ++ .../net/minecraft/server/DataPalette.java | 19 + .../minecraft/server/DataPaletteBlock.java | 276 ++ .../net/minecraft/server/DataWatcher.java | 311 ++ .../net/minecraft/server/DedicatedServer.java | 772 ++++ .../minecraft/server/DefinedStructure.java | 733 ++++ .../server/DefinedStructureManager.java | 236 ++ .../server/DispenseBehaviorItem.java | 101 + .../server/DispenseBehaviorProjectile.java | 66 + .../minecraft/server/DispenserRegistry.java | 959 +++++ .../server/DragonControllerLandedFlame.java | 94 + .../server/DragonControllerManager.java | 64 + .../server/DragonControllerStrafe.java | 183 + src/main/java/net/minecraft/server/EULA.java | 64 + .../net/minecraft/server/Enchantment.java | 181 + .../server/EnchantmentFrostWalker.java | 65 + .../minecraft/server/EnchantmentManager.java | 393 ++ .../minecraft/server/EnchantmentThorns.java | 52 + .../server/EnchantmentWeaponDamage.java | 52 + .../minecraft/server/EnderDragonBattle.java | 566 +++ .../java/net/minecraft/server/Entity.java | 3237 +++++++++++++++++ .../net/minecraft/server/EntityAgeable.java | 191 + .../net/minecraft/server/EntityAnimal.java | 173 + .../server/EntityAreaEffectCloud.java | 448 +++ .../minecraft/server/EntityArmorStand.java | 816 +++++ .../net/minecraft/server/EntityArrow.java | 613 ++++ .../java/net/minecraft/server/EntityBat.java | 218 ++ .../java/net/minecraft/server/EntityBoat.java | 942 +++++ .../minecraft/server/EntityCaveSpider.java | 52 + .../net/minecraft/server/EntityChicken.java | 150 + .../java/net/minecraft/server/EntityCow.java | 97 + .../net/minecraft/server/EntityCreature.java | 111 + .../net/minecraft/server/EntityCreeper.java | 267 ++ .../server/EntityDamageSourceIndirect.java | 38 + .../net/minecraft/server/EntityDolphin.java | 544 +++ .../server/EntityDragonFireball.java | 67 + .../net/minecraft/server/EntityDrowned.java | 438 +++ .../java/net/minecraft/server/EntityEgg.java | 71 + .../minecraft/server/EntityEnderCrystal.java | 147 + .../minecraft/server/EntityEnderDragon.java | 967 +++++ .../minecraft/server/EntityEnderPearl.java | 125 + .../net/minecraft/server/EntityEnderman.java | 429 +++ .../net/minecraft/server/EntityEvoker.java | 310 ++ .../minecraft/server/EntityEvokerFangs.java | 127 + .../minecraft/server/EntityExperienceOrb.java | 288 ++ .../minecraft/server/EntityFallingBlock.java | 287 ++ .../net/minecraft/server/EntityFireball.java | 206 ++ .../net/minecraft/server/EntityFireworks.java | 229 ++ .../java/net/minecraft/server/EntityFish.java | 196 + .../minecraft/server/EntityFishingHook.java | 548 +++ .../net/minecraft/server/EntityGhast.java | 292 ++ .../minecraft/server/EntityGuardianElder.java | 80 + .../net/minecraft/server/EntityHanging.java | 299 ++ .../net/minecraft/server/EntityHorse.java | 276 ++ .../minecraft/server/EntityHorseAbstract.java | 913 +++++ .../server/EntityHorseChestedAbstract.java | 191 + .../minecraft/server/EntityHorseDonkey.java | 41 + .../minecraft/server/EntityHorseSkeleton.java | 170 + .../minecraft/server/EntityHorseZombie.java | 79 + .../net/minecraft/server/EntityHuman.java | 2136 +++++++++++ .../server/EntityIllagerIllusioner.java | 220 ++ .../minecraft/server/EntityInsentient.java | 1312 +++++++ .../net/minecraft/server/EntityIronGolem.java | 193 + .../java/net/minecraft/server/EntityItem.java | 462 +++ .../net/minecraft/server/EntityItemFrame.java | 281 ++ .../minecraft/server/EntityLargeFireball.java | 55 + .../net/minecraft/server/EntityLeash.java | 152 + .../net/minecraft/server/EntityLightning.java | 153 + .../net/minecraft/server/EntityLiving.java | 2913 +++++++++++++++ .../net/minecraft/server/EntityLlama.java | 445 +++ .../net/minecraft/server/EntityLlamaSpit.java | 195 + .../server/EntityMinecartAbstract.java | 866 +++++ .../server/EntityMinecartCommandBlock.java | 103 + .../server/EntityMinecartContainer.java | 276 ++ .../net/minecraft/server/EntityMonster.java | 100 + .../minecraft/server/EntityMushroomCow.java | 84 + .../net/minecraft/server/EntityOcelot.java | 250 ++ .../net/minecraft/server/EntityPainting.java | 98 + .../net/minecraft/server/EntityParrot.java | 346 ++ .../net/minecraft/server/EntityPhantom.java | 468 +++ .../java/net/minecraft/server/EntityPig.java | 251 ++ .../net/minecraft/server/EntityPigZombie.java | 206 ++ .../net/minecraft/server/EntityPlayer.java | 1670 +++++++++ .../net/minecraft/server/EntityPolarBear.java | 261 ++ .../net/minecraft/server/EntityPotion.java | 258 ++ .../minecraft/server/EntityProjectile.java | 273 ++ .../minecraft/server/EntityPufferFish.java | 204 ++ .../net/minecraft/server/EntityRabbit.java | 536 +++ .../net/minecraft/server/EntitySelector.java | 243 ++ .../net/minecraft/server/EntitySheep.java | 300 ++ .../net/minecraft/server/EntityShulker.java | 601 +++ .../minecraft/server/EntityShulkerBullet.java | 341 ++ .../minecraft/server/EntitySilverfish.java | 227 ++ .../net/minecraft/server/EntitySkeleton.java | 64 + .../server/EntitySkeletonAbstract.java | 200 + .../server/EntitySkeletonWither.java | 85 + .../net/minecraft/server/EntitySlice.java | 133 + .../net/minecraft/server/EntitySlime.java | 522 +++ .../minecraft/server/EntitySmallFireball.java | 76 + .../net/minecraft/server/EntitySnowman.java | 158 + .../minecraft/server/EntitySpectralArrow.java | 50 + .../net/minecraft/server/EntitySpider.java | 200 + .../net/minecraft/server/EntitySquid.java | 270 ++ .../net/minecraft/server/EntityTNTPrimed.java | 195 + .../server/EntityThrownExpBottle.java | 46 + .../minecraft/server/EntityThrownTrident.java | 174 + .../minecraft/server/EntityTippedArrow.java | 230 ++ .../net/minecraft/server/EntityTracker.java | 302 ++ .../minecraft/server/EntityTrackerEntry.java | 666 ++++ .../net/minecraft/server/EntityTurtle.java | 718 ++++ .../net/minecraft/server/EntityTypes.java | 358 ++ .../java/net/minecraft/server/EntityVex.java | 309 ++ .../net/minecraft/server/EntityVillager.java | 893 +++++ .../minecraft/server/EntityVindicator.java | 120 + .../net/minecraft/server/EntityWitch.java | 202 + .../net/minecraft/server/EntityWither.java | 541 +++ .../minecraft/server/EntityWitherSkull.java | 100 + .../java/net/minecraft/server/EntityWolf.java | 425 +++ .../net/minecraft/server/EntityZombie.java | 608 ++++ .../minecraft/server/EntityZombieHusk.java | 64 + .../server/EntityZombieVillager.java | 213 ++ .../minecraft/server/EnumCreatureType.java | 36 + .../net/minecraft/server/EnumDirection.java | 349 ++ .../net/minecraft/server/EnumItemSlot.java | 57 + .../minecraft/server/ExpirableListEntry.java | 97 + .../net/minecraft/server/ExpiringMap.java | 220 ++ .../java/net/minecraft/server/Explosion.java | 397 ++ .../net/minecraft/server/FileIOThread.java | 84 + .../minecraft/server/FluidTypeFlowing.java | 525 +++ .../net/minecraft/server/FluidTypeLava.java | 210 ++ .../net/minecraft/server/FoodMetaData.java | 130 + .../net/minecraft/server/FurnaceRecipe.java | 129 + .../minecraft/server/GameProfileBanEntry.java | 64 + .../minecraft/server/GenericAttributes.java | 122 + .../minecraft/server/HandshakeListener.java | 139 + .../java/net/minecraft/server/IBlockData.java | 295 ++ .../minecraft/server/IChatBaseComponent.java | 439 +++ .../net/minecraft/server/IChunkLoader.java | 22 + .../minecraft/server/ICommandListener.java | 14 + .../net/minecraft/server/IDataManager.java | 34 + .../net/minecraft/server/IEntitySelector.java | 97 + .../java/net/minecraft/server/IInventory.java | 71 + .../net/minecraft/server/IRangedEntity.java | 8 + .../java/net/minecraft/server/IRecipe.java | 38 + .../net/minecraft/server/IWorldReader.java | 363 ++ .../net/minecraft/server/IWorldWriter.java | 16 + .../server/InventoryCraftResult.java | 137 + .../minecraft/server/InventoryCrafting.java | 191 + .../minecraft/server/InventoryEnderChest.java | 88 + .../minecraft/server/InventoryHorseChest.java | 10 + .../minecraft/server/InventoryLargeChest.java | 189 + .../minecraft/server/InventoryMerchant.java | 215 ++ .../server/InventorySubcontainer.java | 235 ++ .../java/net/minecraft/server/ItemArmor.java | 127 + .../net/minecraft/server/ItemArmorStand.java | 73 + .../java/net/minecraft/server/ItemBlock.java | 141 + .../java/net/minecraft/server/ItemBoat.java | 87 + .../java/net/minecraft/server/ItemBow.java | 167 + .../java/net/minecraft/server/ItemBucket.java | 182 + .../net/minecraft/server/ItemChorusFruit.java | 60 + .../net/minecraft/server/ItemDebugStick.java | 83 + .../java/net/minecraft/server/ItemDye.java | 51 + .../java/net/minecraft/server/ItemEgg.java | 45 + .../net/minecraft/server/ItemEndCrystal.java | 55 + .../net/minecraft/server/ItemEnderPearl.java | 50 + .../net/minecraft/server/ItemExpBottle.java | 44 + .../net/minecraft/server/ItemFireball.java | 34 + .../net/minecraft/server/ItemFireworks.java | 75 + .../java/net/minecraft/server/ItemFish.java | 86 + .../net/minecraft/server/ItemFishingRod.java | 78 + .../minecraft/server/ItemFlintAndSteel.java | 58 + .../java/net/minecraft/server/ItemFood.java | 90 + .../net/minecraft/server/ItemGoldenApple.java | 18 + .../server/ItemGoldenAppleEnchanted.java | 20 + .../net/minecraft/server/ItemHanging.java | 63 + .../java/net/minecraft/server/ItemLeash.java | 73 + .../minecraft/server/ItemLingeringPotion.java | 48 + .../net/minecraft/server/ItemMilkBucket.java | 40 + .../net/minecraft/server/ItemMinecart.java | 133 + .../java/net/minecraft/server/ItemPotion.java | 86 + .../java/net/minecraft/server/ItemRecord.java | 57 + .../net/minecraft/server/ItemSkullPlayer.java | 65 + .../net/minecraft/server/ItemSnowball.java | 47 + .../minecraft/server/ItemSplashPotion.java | 48 + .../java/net/minecraft/server/ItemStack.java | 894 +++++ .../net/minecraft/server/ItemTrident.java | 149 + .../net/minecraft/server/ItemWaterLily.java | 59 + .../net/minecraft/server/ItemWorldMap.java | 366 ++ .../java/net/minecraft/server/JsonList.java | 241 ++ .../net/minecraft/server/KeyedObject.java | 9 + .../minecraft/server/LegacyPingHandler.java | 241 ++ .../net/minecraft/server/LocaleLanguage.java | 70 + .../net/minecraft/server/LoginListener.java | 386 ++ .../minecraft/server/LootEnchantFunction.java | 65 + ...tItemConditionRandomChanceWithLooting.java | 48 + .../minecraft/server/LootSelectorEntry.java | 114 + .../net/minecraft/server/LootTableInfo.java | 182 + .../java/net/minecraft/server/MCUtil.java | 357 ++ .../java/net/minecraft/server/MathHelper.java | 353 ++ .../net/minecraft/server/MerchantRecipe.java | 140 + .../net/minecraft/server/MethodProfiler.java | 188 + .../net/minecraft/server/MinecraftServer.java | 1871 ++++++++++ .../net/minecraft/server/MobEffectList.java | 244 ++ .../minecraft/server/MobSpawnerAbstract.java | 284 ++ .../minecraft/server/MobSpawnerPhantom.java | 82 + .../java/net/minecraft/server/NBTBase.java | 102 + .../server/NBTCompressedStreamTools.java | 97 + .../net/minecraft/server/NBTTagByteArray.java | 121 + .../net/minecraft/server/NBTTagCompound.java | 496 +++ .../net/minecraft/server/NBTTagIntArray.java | 130 + .../java/net/minecraft/server/NBTTagList.java | 297 ++ .../server/NameReferencingFileConverter.java | 532 +++ .../minecraft/server/NavigationAbstract.java | 334 ++ .../net/minecraft/server/NetworkManager.java | 413 +++ .../net/minecraft/server/NibbleArray.java | 56 + .../java/net/minecraft/server/Packet.java | 23 + .../server/PacketDataSerializer.java | 1088 ++++++ .../net/minecraft/server/PacketEncoder.java | 79 + .../PacketHandshakingInSetProtocol.java | 40 + .../server/PacketLoginInCustomPayload.java | 42 + .../server/PacketLoginOutCustomPayload.java | 42 + .../server/PacketPlayInBlockPlace.java | 32 + .../minecraft/server/PacketPlayInChat.java | 51 + .../server/PacketPlayInCloseWindow.java | 28 + .../server/PacketPlayInUseEntity.java | 75 + .../minecraft/server/PacketPlayInUseItem.java | 63 + .../minecraft/server/PacketPlayOutChat.java | 60 + .../server/PacketPlayOutMapChunk.java | 207 ++ .../server/PacketPlayOutScoreboardTeam.java | 119 + .../minecraft/server/PacketPlayOutTitle.java | 87 + .../server/PacketPlayOutUpdateTime.java | 46 + .../server/PacketPlayOutWindowItems.java | 61 + .../server/PacketPlayOutWorldBorder.java | 111 + .../server/PacketStatusListener.java | 141 + .../server/PaperAsyncChunkProvider.java | 658 ++++ .../minecraft/server/PaperLightingQueue.java | 98 + .../java/net/minecraft/server/PathEntity.java | 95 + .../java/net/minecraft/server/PathPoint.java | 93 + .../java/net/minecraft/server/Pathfinder.java | 123 + .../minecraft/server/PathfinderAbstract.java | 78 + .../net/minecraft/server/PathfinderGoal.java | 35 + .../server/PathfinderGoalBreakDoor.java | 59 + .../minecraft/server/PathfinderGoalBreed.java | 132 + .../server/PathfinderGoalDefendVillage.java | 38 + .../server/PathfinderGoalDoorInteract.java | 109 + .../server/PathfinderGoalEatTile.java | 78 + .../minecraft/server/PathfinderGoalFloat.java | 26 + .../server/PathfinderGoalFollowOwner.java | 113 + .../server/PathfinderGoalGotoTarget.java | 116 + .../server/PathfinderGoalHorseTrap.java | 67 + .../server/PathfinderGoalHurtByTarget.java | 71 + .../server/PathfinderGoalMakeLove.java | 98 + ...PathfinderGoalNearestAttackableTarget.java | 87 + ...GoalNearestAttackableTargetInsentient.java | 79 + .../PathfinderGoalOwnerHurtByTarget.java | 42 + .../server/PathfinderGoalOwnerHurtTarget.java | 42 + .../minecraft/server/PathfinderGoalPanic.java | 93 + .../server/PathfinderGoalRemoveBlock.java | 131 + .../server/PathfinderGoalSelector.java | 190 + .../minecraft/server/PathfinderGoalSit.java | 39 + .../minecraft/server/PathfinderGoalTame.java | 69 + .../server/PathfinderGoalTarget.java | 163 + .../PathfinderGoalTargetNearestPlayer.java | 102 + .../minecraft/server/PathfinderGoalTempt.java | 114 + .../server/PathfinderGoalVillagerFarm.java | 117 + .../minecraft/server/PathfinderNormal.java | 423 +++ .../server/PersistentScoreboard.java | 252 ++ .../minecraft/server/PersistentVillage.java | 268 ++ .../net/minecraft/server/PlayerChunk.java | 329 ++ .../net/minecraft/server/PlayerChunkMap.java | 561 +++ .../minecraft/server/PlayerConnection.java | 2653 ++++++++++++++ .../server/PlayerConnectionUtils.java | 21 + .../server/PlayerInteractManager.java | 548 +++ .../net/minecraft/server/PlayerInventory.java | 703 ++++ .../java/net/minecraft/server/PlayerList.java | 1514 ++++++++ .../minecraft/server/PortalTravelAgent.java | 524 +++ .../java/net/minecraft/server/PotionUtil.java | 144 + .../net/minecraft/server/PropertyManager.java | 153 + .../java/net/minecraft/server/ProtoChunk.java | 514 +++ .../server/RandomPositionGenerator.java | 162 + .../net/minecraft/server/RecipeArmorDye.java | 125 + .../net/minecraft/server/RecipeBannerAdd.java | 176 + .../server/RecipeBannerDuplicate.java | 93 + .../net/minecraft/server/RecipeBookClone.java | 101 + .../minecraft/server/RecipeBookServer.java | 142 + .../net/minecraft/server/RecipeFireworks.java | 84 + .../minecraft/server/RecipeFireworksFade.java | 74 + .../minecraft/server/RecipeFireworksStar.java | 119 + .../net/minecraft/server/RecipeItemStack.java | 263 ++ .../net/minecraft/server/RecipeMapClone.java | 79 + .../net/minecraft/server/RecipeRepair.java | 98 + .../minecraft/server/RecipeShulkerBox.java | 72 + .../minecraft/server/RecipeTippedArrow.java | 62 + .../net/minecraft/server/RecipiesShield.java | 85 + .../java/net/minecraft/server/RegionFile.java | 531 +++ .../net/minecraft/server/RegionFileCache.java | 283 ++ .../server/RegionLimitedWorldAccess.java | 309 ++ .../java/net/minecraft/server/Registry.java | 8 + .../net/minecraft/server/RegistryBlockID.java | 63 + .../java/net/minecraft/server/RegistryID.java | 166 + .../minecraft/server/RegistryMaterials.java | 102 + .../server/RemoteControlCommandListener.java | 52 + .../server/RemoteControlListener.java | 111 + .../net/minecraft/server/SchedulerBatch.java | 70 + .../minecraft/server/ScoreboardServer.java | 244 ++ .../server/SecondaryWorldServer.java | 62 + .../minecraft/server/ServerConnection.java | 164 + .../java/net/minecraft/server/ServerPing.java | 226 ++ .../server/ServerStatisticManager.java | 248 ++ .../net/minecraft/server/ShapeDetector.java | 175 + .../net/minecraft/server/ShapedRecipes.java | 373 ++ .../minecraft/server/ShapelessRecipes.java | 178 + .../minecraft/server/SlotFurnaceResult.java | 97 + .../net/minecraft/server/SpawnerCreature.java | 358 ++ .../minecraft/server/StatisticManager.java | 32 + .../minecraft/server/StructureGenerator.java | 247 ++ .../net/minecraft/server/StructurePiece.java | 491 +++ .../net/minecraft/server/StructureStart.java | 194 + .../net/minecraft/server/SystemUtils.java | 201 + .../net/minecraft/server/TagRegistry.java | 62 + .../java/net/minecraft/server/TagsServer.java | 55 + .../net/minecraft/server/TickListServer.java | 234 ++ .../java/net/minecraft/server/TileEntity.java | 230 ++ .../minecraft/server/TileEntityBanner.java | 152 + .../minecraft/server/TileEntityBeacon.java | 491 +++ .../server/TileEntityBrewingStand.java | 347 ++ .../net/minecraft/server/TileEntityChest.java | 311 ++ .../minecraft/server/TileEntityCommand.java | 159 + .../minecraft/server/TileEntityConduit.java | 290 ++ .../minecraft/server/TileEntityContainer.java | 45 + .../minecraft/server/TileEntityDispenser.java | 150 + .../server/TileEntityEndGateway.java | 271 ++ .../server/TileEntityEnderChest.java | 109 + .../minecraft/server/TileEntityFurnace.java | 566 +++ .../minecraft/server/TileEntityHopper.java | 753 ++++ .../minecraft/server/TileEntityLootable.java | 158 + .../server/TileEntityShulkerBox.java | 347 ++ .../net/minecraft/server/TileEntitySign.java | 192 + .../net/minecraft/server/TileEntitySkull.java | 273 ++ .../java/net/minecraft/server/UserCache.java | 357 ++ .../java/net/minecraft/server/Village.java | 493 +++ .../net/minecraft/server/VillageDoor.java | 96 + .../net/minecraft/server/VillageSiege.java | 166 + src/main/java/net/minecraft/server/World.java | 3115 ++++++++++++++++ .../net/minecraft/server/WorldBorder.java | 359 ++ .../java/net/minecraft/server/WorldData.java | 768 ++++ .../server/WorldGenEndCityPieces.java | 287 ++ .../server/WorldGenFeatureDesertPyramid.java | 39 + .../server/WorldGenFeatureIgloo.java | 44 + .../server/WorldGenFeatureJunglePyramid.java | 39 + .../server/WorldGenFeatureOceanRuin.java | 61 + .../WorldGenFeatureOceanRuinPieces.java | 218 ++ .../WorldGenFeatureRandomScattered.java | 56 + .../server/WorldGenFeatureShipwreck.java | 49 + .../server/WorldGenFeatureSwampHut.java | 60 + .../minecraft/server/WorldGenGroundBush.java | 58 + .../server/WorldGenMegaTreeProvider.java | 55 + .../minecraft/server/WorldGenMonument.java | 159 + .../server/WorldGenMonumentPieces.java | 1938 ++++++++++ .../minecraft/server/WorldGenStronghold.java | 221 ++ .../server/WorldGenTreeProvider.java | 56 + .../net/minecraft/server/WorldGenVillage.java | 121 + .../server/WorldGenVillagePieces.java | 1814 +++++++++ .../minecraft/server/WorldGenWitchHut.java | 96 + .../server/WorldGenWoodlandMansionPieces.java | 1084 ++++++ .../net/minecraft/server/WorldManager.java | 97 + .../java/net/minecraft/server/WorldMap.java | 481 +++ .../net/minecraft/server/WorldNBTStorage.java | 363 ++ .../minecraft/server/WorldPersistentData.java | 199 + .../net/minecraft/server/WorldProvider.java | 92 + .../minecraft/server/WorldProviderHell.java | 71 + .../minecraft/server/WorldProviderTheEnd.java | 83 + .../net/minecraft/server/WorldServer.java | 1289 +++++++ .../java/org/bukkit/craftbukkit/CraftArt.java | 34 + .../org/bukkit/craftbukkit/CraftChunk.java | 305 ++ .../craftbukkit/CraftChunkSnapshot.java | 119 + .../bukkit/craftbukkit/CraftCrashReport.java | 45 + .../org/bukkit/craftbukkit/CraftEffect.java | 69 + .../craftbukkit/CraftEquipmentSlot.java | 32 + .../craftbukkit/CraftFluidCollisionMode.java | 24 + .../bukkit/craftbukkit/CraftIpBanEntry.java | 88 + .../bukkit/craftbukkit/CraftIpBanList.java | 79 + .../bukkit/craftbukkit/CraftLootTable.java | 108 + .../craftbukkit/CraftOfflinePlayer.java | 332 ++ .../org/bukkit/craftbukkit/CraftParticle.java | 152 + .../craftbukkit/CraftProfileBanEntry.java | 89 + .../craftbukkit/CraftProfileBanList.java | 99 + .../org/bukkit/craftbukkit/CraftServer.java | 2210 +++++++++++ .../org/bukkit/craftbukkit/CraftSound.java | 709 ++++ .../bukkit/craftbukkit/CraftStatistic.java | 180 + .../bukkit/craftbukkit/CraftTravelAgent.java | 86 + .../org/bukkit/craftbukkit/CraftWorld.java | 1885 ++++++++++ .../bukkit/craftbukkit/CraftWorldBorder.java | 120 + .../craftbukkit/LoggerOutputStream.java | 31 + .../java/org/bukkit/craftbukkit/Main.java | 253 ++ .../org/bukkit/craftbukkit/Overridden.java | 14 + .../java/org/bukkit/craftbukkit/TrigMath.java | 47 + .../advancement/CraftAdvancement.java | 30 + .../advancement/CraftAdvancementProgress.java | 59 + .../attribute/CraftAttributeInstance.java | 75 + .../attribute/CraftAttributeMap.java | 55 + .../bukkit/craftbukkit/block/CraftBanner.java | 109 + .../bukkit/craftbukkit/block/CraftBeacon.java | 98 + .../bukkit/craftbukkit/block/CraftBed.java | 63 + .../bukkit/craftbukkit/block/CraftBlock.java | 678 ++++ .../block/CraftBlockEntityState.java | 145 + .../craftbukkit/block/CraftBlockState.java | 276 ++ .../craftbukkit/block/CraftBrewingStand.java | 74 + .../bukkit/craftbukkit/block/CraftChest.java | 60 + .../craftbukkit/block/CraftCommandBlock.java | 57 + .../craftbukkit/block/CraftComparator.java | 17 + .../craftbukkit/block/CraftConduit.java | 17 + .../craftbukkit/block/CraftContainer.java | 33 + .../block/CraftCreatureSpawner.java | 125 + .../block/CraftDaylightDetector.java | 17 + .../craftbukkit/block/CraftDispenser.java | 66 + .../craftbukkit/block/CraftDropper.java | 50 + .../block/CraftEnchantingTable.java | 38 + .../craftbukkit/block/CraftEndGateway.java | 66 + .../craftbukkit/block/CraftEnderChest.java | 17 + .../craftbukkit/block/CraftFurnace.java | 101 + .../bukkit/craftbukkit/block/CraftHopper.java | 33 + .../craftbukkit/block/CraftJukebox.java | 92 + .../craftbukkit/block/CraftLootable.java | 77 + .../craftbukkit/block/CraftShulkerBox.java | 43 + .../bukkit/craftbukkit/block/CraftSign.java | 94 + .../bukkit/craftbukkit/block/CraftSkull.java | 182 + .../block/CraftStructureBlock.java | 187 + .../craftbukkit/block/data/CraftAgeable.java | 23 + .../block/data/CraftAnaloguePowerable.java | 23 + .../block/data/CraftAttachable.java | 18 + .../craftbukkit/block/data/CraftBisected.java | 18 + .../block/data/CraftBlockData.java | 537 +++ .../block/data/CraftDirectional.java | 23 + .../craftbukkit/block/data/CraftLevelled.java | 23 + .../block/data/CraftLightable.java | 18 + .../block/data/CraftMultipleFacing.java | 46 + .../craftbukkit/block/data/CraftOpenable.java | 18 + .../block/data/CraftOrientable.java | 23 + .../block/data/CraftPowerable.java | 18 + .../craftbukkit/block/data/CraftRail.java | 23 + .../block/data/CraftRotatable.java | 107 + .../craftbukkit/block/data/CraftSnowable.java | 18 + .../block/data/CraftWaterlogged.java | 19 + .../craftbukkit/block/data/type/CraftBed.java | 25 + .../block/data/type/CraftBrewingStand.java | 39 + .../block/data/type/CraftBubbleColumn.java | 19 + .../block/data/type/CraftCake.java | 24 + .../block/data/type/CraftChest.java | 19 + .../block/data/type/CraftCommandBlock.java | 19 + .../block/data/type/CraftComparator.java | 19 + .../data/type/CraftDaylightDetector.java | 19 + .../block/data/type/CraftDispenser.java | 19 + .../block/data/type/CraftDoor.java | 19 + .../block/data/type/CraftEndPortalFrame.java | 19 + .../block/data/type/CraftFarmland.java | 24 + .../block/data/type/CraftGate.java | 19 + .../block/data/type/CraftHopper.java | 19 + .../block/data/type/CraftJukebox.java | 14 + .../block/data/type/CraftLeaves.java | 30 + .../block/data/type/CraftNoteBlock.java | 30 + .../block/data/type/CraftPiston.java | 19 + .../block/data/type/CraftPistonHead.java | 19 + .../block/data/type/CraftRedstoneWire.java | 53 + .../block/data/type/CraftRepeater.java | 40 + .../block/data/type/CraftSapling.java | 24 + .../block/data/type/CraftSeaPickle.java | 29 + .../block/data/type/CraftSlab.java | 19 + .../block/data/type/CraftSnow.java | 29 + .../block/data/type/CraftStairs.java | 19 + .../block/data/type/CraftStructureBlock.java | 19 + .../block/data/type/CraftSwitch.java | 19 + .../craftbukkit/block/data/type/CraftTNT.java | 19 + .../block/data/type/CraftTechnicalPiston.java | 19 + .../block/data/type/CraftTripwire.java | 19 + .../block/data/type/CraftTurtleEgg.java | 45 + .../craftbukkit/block/impl/CraftAnvil.java | 34 + .../craftbukkit/block/impl/CraftBanner.java | 118 + .../block/impl/CraftBannerWall.java | 34 + .../craftbukkit/block/impl/CraftBed.java | 54 + .../craftbukkit/block/impl/CraftBeetroot.java | 34 + .../block/impl/CraftBrewingStand.java | 49 + .../block/impl/CraftBubbleColumn.java | 29 + .../craftbukkit/block/impl/CraftCactus.java | 34 + .../craftbukkit/block/impl/CraftCake.java | 34 + .../craftbukkit/block/impl/CraftCarrots.java | 34 + .../craftbukkit/block/impl/CraftCauldron.java | 34 + .../craftbukkit/block/impl/CraftChest.java | 62 + .../block/impl/CraftChestTrapped.java | 62 + .../block/impl/CraftChorusFlower.java | 34 + .../block/impl/CraftChorusFruit.java | 57 + .../block/impl/CraftCobbleWall.java | 71 + .../craftbukkit/block/impl/CraftCocoa.java | 53 + .../craftbukkit/block/impl/CraftCommand.java | 48 + .../craftbukkit/block/impl/CraftConduit.java | 29 + .../block/impl/CraftCoralDead.java | 29 + .../craftbukkit/block/impl/CraftCoralFan.java | 29 + .../block/impl/CraftCoralFanAbstract.java | 29 + .../block/impl/CraftCoralFanWall.java | 48 + .../block/impl/CraftCoralFanWallAbstract.java | 48 + .../block/impl/CraftCoralPlant.java | 29 + .../craftbukkit/block/impl/CraftCrops.java | 34 + .../block/impl/CraftDaylightDetector.java | 48 + .../craftbukkit/block/impl/CraftDirtSnow.java | 29 + .../block/impl/CraftDispenser.java | 48 + .../craftbukkit/block/impl/CraftDoor.java | 90 + .../craftbukkit/block/impl/CraftDropper.java | 48 + .../craftbukkit/block/impl/CraftEndRod.java | 34 + .../block/impl/CraftEnderChest.java | 48 + .../block/impl/CraftEnderPortalFrame.java | 48 + .../craftbukkit/block/impl/CraftFence.java | 71 + .../block/impl/CraftFenceGate.java | 76 + .../craftbukkit/block/impl/CraftFire.java | 76 + .../block/impl/CraftFloorSign.java | 132 + .../craftbukkit/block/impl/CraftFluids.java | 34 + .../craftbukkit/block/impl/CraftFurnace.java | 48 + .../block/impl/CraftGlassPane.java | 71 + .../block/impl/CraftGlazedTerracotta.java | 34 + .../craftbukkit/block/impl/CraftGrass.java | 29 + .../craftbukkit/block/impl/CraftHay.java | 34 + .../craftbukkit/block/impl/CraftHopper.java | 48 + .../block/impl/CraftHugeMushroom.java | 57 + .../craftbukkit/block/impl/CraftIceFrost.java | 34 + .../craftbukkit/block/impl/CraftIronBars.java | 71 + .../craftbukkit/block/impl/CraftJukeBox.java | 24 + .../craftbukkit/block/impl/CraftKelp.java | 34 + .../craftbukkit/block/impl/CraftLadder.java | 48 + .../craftbukkit/block/impl/CraftLeaves.java | 40 + .../craftbukkit/block/impl/CraftLever.java | 62 + .../block/impl/CraftLogAbstract.java | 34 + .../block/impl/CraftMinecartDetector.java | 48 + .../block/impl/CraftMinecartTrack.java | 34 + .../craftbukkit/block/impl/CraftMycel.java | 29 + .../block/impl/CraftNetherWart.java | 34 + .../craftbukkit/block/impl/CraftNote.java | 54 + .../craftbukkit/block/impl/CraftObserver.java | 48 + .../craftbukkit/block/impl/CraftPiston.java | 48 + .../block/impl/CraftPistonExtension.java | 62 + .../block/impl/CraftPistonMoving.java | 48 + .../craftbukkit/block/impl/CraftPortal.java | 34 + .../craftbukkit/block/impl/CraftPotatoes.java | 34 + .../block/impl/CraftPoweredRail.java | 48 + .../block/impl/CraftPressurePlateBinary.java | 29 + .../impl/CraftPressurePlateWeighted.java | 34 + .../block/impl/CraftPumpkinCarved.java | 34 + .../block/impl/CraftRedstoneComparator.java | 62 + .../block/impl/CraftRedstoneLamp.java | 29 + .../block/impl/CraftRedstoneOre.java | 29 + .../block/impl/CraftRedstoneTorch.java | 29 + .../block/impl/CraftRedstoneTorchWall.java | 48 + .../block/impl/CraftRedstoneWire.java | 82 + .../craftbukkit/block/impl/CraftReed.java | 34 + .../craftbukkit/block/impl/CraftRepeater.java | 83 + .../block/impl/CraftRotatable.java | 34 + .../craftbukkit/block/impl/CraftSapling.java | 34 + .../block/impl/CraftSeaPickle.java | 53 + .../block/impl/CraftShulkerBox.java | 34 + .../craftbukkit/block/impl/CraftSkull.java | 118 + .../block/impl/CraftSkullPlayer.java | 118 + .../block/impl/CraftSkullPlayerWall.java | 34 + .../block/impl/CraftSkullWall.java | 34 + .../craftbukkit/block/impl/CraftSnow.java | 39 + .../craftbukkit/block/impl/CraftSoil.java | 34 + .../block/impl/CraftStainedGlassPane.java | 71 + .../craftbukkit/block/impl/CraftStairs.java | 76 + .../craftbukkit/block/impl/CraftStem.java | 34 + .../block/impl/CraftStemAttached.java | 34 + .../block/impl/CraftStepAbstract.java | 43 + .../block/impl/CraftStoneButton.java | 62 + .../block/impl/CraftStructure.java | 29 + .../craftbukkit/block/impl/CraftTNT.java | 29 + .../block/impl/CraftTallPlantFlower.java | 29 + .../block/impl/CraftTallPlantShearable.java | 29 + .../block/impl/CraftTallSeaGrass.java | 29 + .../block/impl/CraftTorchWall.java | 34 + .../craftbukkit/block/impl/CraftTrapdoor.java | 90 + .../craftbukkit/block/impl/CraftTripwire.java | 99 + .../block/impl/CraftTripwireHook.java | 62 + .../block/impl/CraftTurtleEgg.java | 55 + .../craftbukkit/block/impl/CraftVine.java | 57 + .../craftbukkit/block/impl/CraftWallSign.java | 48 + .../block/impl/CraftWitherSkull.java | 118 + .../block/impl/CraftWitherSkullWall.java | 34 + .../block/impl/CraftWoodButton.java | 62 + .../bukkit/craftbukkit/boss/CraftBossBar.java | 228 ++ .../craftbukkit/boss/CraftKeyedBossbar.java | 23 + .../craftbukkit/chunkio/ChunkIOExecutor.java | 43 + .../craftbukkit/chunkio/ChunkIOProvider.java | 61 + .../craftbukkit/chunkio/QueuedChunk.java | 38 + .../command/BukkitCommandWrapper.java | 60 + .../command/ConsoleCommandCompleter.java | 109 + .../command/CraftBlockCommandSender.java | 56 + .../craftbukkit/command/CraftCommandMap.java | 17 + .../command/CraftConsoleCommandSender.java | 66 + .../CraftRemoteConsoleCommandSender.java | 41 + .../command/ProxiedNativeCommandSender.java | 133 + .../command/ServerCommandSender.java | 103 + .../command/VanillaCommandWrapper.java | 96 + .../conversations/ConversationTracker.java | 77 + .../enchantments/CraftEnchantment.java | 184 + .../entity/AbstractProjectile.java | 23 + .../entity/CraftAbstractHorse.java | 101 + .../craftbukkit/entity/CraftAgeable.java | 67 + .../craftbukkit/entity/CraftAmbient.java | 26 + .../craftbukkit/entity/CraftAnimals.java | 50 + .../entity/CraftAreaEffectCloud.java | 231 ++ .../craftbukkit/entity/CraftArmorStand.java | 311 ++ .../bukkit/craftbukkit/entity/CraftArrow.java | 130 + .../bukkit/craftbukkit/entity/CraftBat.java | 36 + .../bukkit/craftbukkit/entity/CraftBlaze.java | 27 + .../bukkit/craftbukkit/entity/CraftBoat.java | 110 + .../craftbukkit/entity/CraftCaveSpider.java | 27 + .../craftbukkit/entity/CraftChestedHorse.java | 29 + .../craftbukkit/entity/CraftChicken.java | 28 + .../bukkit/craftbukkit/entity/CraftCod.java | 28 + .../entity/CraftComplexLivingEntity.java | 22 + .../craftbukkit/entity/CraftComplexPart.java | 48 + .../bukkit/craftbukkit/entity/CraftCow.java | 28 + .../craftbukkit/entity/CraftCreature.java | 21 + .../craftbukkit/entity/CraftCreeper.java | 97 + .../craftbukkit/entity/CraftDolphin.java | 28 + .../craftbukkit/entity/CraftDonkey.java | 29 + .../entity/CraftDragonFireball.java | 22 + .../craftbukkit/entity/CraftDrowned.java | 28 + .../bukkit/craftbukkit/entity/CraftEgg.java | 26 + .../entity/CraftElderGuardian.java | 28 + .../craftbukkit/entity/CraftEnderCrystal.java | 55 + .../craftbukkit/entity/CraftEnderDragon.java | 77 + .../entity/CraftEnderDragonPart.java | 56 + .../craftbukkit/entity/CraftEnderPearl.java | 26 + .../craftbukkit/entity/CraftEnderSignal.java | 61 + .../craftbukkit/entity/CraftEnderman.java | 54 + .../craftbukkit/entity/CraftEndermite.java | 38 + .../craftbukkit/entity/CraftEntity.java | 849 +++++ .../craftbukkit/entity/CraftEvoker.java | 39 + .../craftbukkit/entity/CraftEvokerFangs.java | 42 + .../entity/CraftExperienceOrb.java | 46 + .../craftbukkit/entity/CraftFallingBlock.java | 66 + .../craftbukkit/entity/CraftFireball.java | 75 + .../craftbukkit/entity/CraftFirework.java | 90 + .../bukkit/craftbukkit/entity/CraftFish.java | 22 + .../craftbukkit/entity/CraftFishHook.java | 76 + .../craftbukkit/entity/CraftFlying.java | 22 + .../bukkit/craftbukkit/entity/CraftGhast.java | 28 + .../bukkit/craftbukkit/entity/CraftGiant.java | 28 + .../bukkit/craftbukkit/entity/CraftGolem.java | 21 + .../craftbukkit/entity/CraftGuardian.java | 33 + .../craftbukkit/entity/CraftHanging.java | 69 + .../bukkit/craftbukkit/entity/CraftHorse.java | 73 + .../craftbukkit/entity/CraftHumanEntity.java | 676 ++++ .../bukkit/craftbukkit/entity/CraftHusk.java | 23 + .../craftbukkit/entity/CraftIllager.java | 22 + .../craftbukkit/entity/CraftIllusioner.java | 29 + .../craftbukkit/entity/CraftIronGolem.java | 35 + .../bukkit/craftbukkit/entity/CraftItem.java | 89 + .../craftbukkit/entity/CraftItemFrame.java | 141 + .../entity/CraftLargeFireball.java | 32 + .../bukkit/craftbukkit/entity/CraftLeash.java | 27 + .../entity/CraftLightningStrike.java | 46 + .../entity/CraftLingeringPotion.java | 42 + .../craftbukkit/entity/CraftLivingEntity.java | 636 ++++ .../bukkit/craftbukkit/entity/CraftLlama.java | 68 + .../craftbukkit/entity/CraftLlamaSpit.java | 39 + .../craftbukkit/entity/CraftMagmaCube.java | 27 + .../craftbukkit/entity/CraftMinecart.java | 108 + .../entity/CraftMinecartChest.java | 33 + .../entity/CraftMinecartCommand.java | 139 + .../entity/CraftMinecartContainer.java | 54 + .../entity/CraftMinecartFurnace.java | 23 + .../entity/CraftMinecartHopper.java | 42 + .../entity/CraftMinecartMobSpawner.java | 22 + .../entity/CraftMinecartRideable.java | 22 + .../craftbukkit/entity/CraftMinecartTNT.java | 22 + .../bukkit/craftbukkit/entity/CraftMob.java | 77 + .../craftbukkit/entity/CraftMonster.java | 23 + .../bukkit/craftbukkit/entity/CraftMule.java | 29 + .../craftbukkit/entity/CraftMushroomCow.java | 27 + .../craftbukkit/entity/CraftOcelot.java | 37 + .../craftbukkit/entity/CraftPainting.java | 79 + .../craftbukkit/entity/CraftParrot.java | 41 + .../craftbukkit/entity/CraftPhantom.java | 44 + .../bukkit/craftbukkit/entity/CraftPig.java | 34 + .../craftbukkit/entity/CraftPigZombie.java | 59 + .../craftbukkit/entity/CraftPlayer.java | 2044 +++++++++++ .../craftbukkit/entity/CraftPolarBear.java | 27 + .../craftbukkit/entity/CraftProjectile.java | 39 + .../craftbukkit/entity/CraftPufferFish.java | 38 + .../craftbukkit/entity/CraftRabbit.java | 88 + .../craftbukkit/entity/CraftSalmon.java | 28 + .../bukkit/craftbukkit/entity/CraftSheep.java | 45 + .../craftbukkit/entity/CraftShulker.java | 40 + .../entity/CraftShulkerBullet.java | 55 + .../craftbukkit/entity/CraftSilverfish.java | 27 + .../craftbukkit/entity/CraftSkeleton.java | 39 + .../entity/CraftSkeletonHorse.java | 48 + .../bukkit/craftbukkit/entity/CraftSlime.java | 46 + .../entity/CraftSmallFireball.java | 26 + .../craftbukkit/entity/CraftSnowball.java | 26 + .../craftbukkit/entity/CraftSnowman.java | 37 + .../entity/CraftSpectralArrow.java | 38 + .../craftbukkit/entity/CraftSpellcaster.java | 35 + .../craftbukkit/entity/CraftSpider.java | 28 + .../craftbukkit/entity/CraftSplashPotion.java | 43 + .../bukkit/craftbukkit/entity/CraftSquid.java | 28 + .../bukkit/craftbukkit/entity/CraftStray.java | 28 + .../craftbukkit/entity/CraftTNTPrimed.java | 60 + .../entity/CraftTameableAnimal.java | 84 + .../entity/CraftThrownExpBottle.java | 26 + .../craftbukkit/entity/CraftThrownPotion.java | 41 + .../craftbukkit/entity/CraftTippedArrow.java | 133 + .../craftbukkit/entity/CraftTrident.java | 28 + .../craftbukkit/entity/CraftTropicalFish.java | 117 + .../craftbukkit/entity/CraftTurtle.java | 62 + .../craftbukkit/entity/CraftVehicle.java | 15 + .../bukkit/craftbukkit/entity/CraftVex.java | 41 + .../craftbukkit/entity/CraftVillager.java | 165 + .../entity/CraftVillagerZombie.java | 63 + .../craftbukkit/entity/CraftVindicator.java | 38 + .../craftbukkit/entity/CraftWaterMob.java | 23 + .../craftbukkit/entity/CraftWeather.java | 26 + .../bukkit/craftbukkit/entity/CraftWitch.java | 53 + .../craftbukkit/entity/CraftWither.java | 41 + .../entity/CraftWitherSkeleton.java | 28 + .../craftbukkit/entity/CraftWitherSkull.java | 36 + .../bukkit/craftbukkit/entity/CraftWolf.java | 40 + .../craftbukkit/entity/CraftZombie.java | 110 + .../craftbukkit/entity/CraftZombieHorse.java | 29 + .../craftbukkit/event/CraftEventFactory.java | 1443 ++++++++ .../craftbukkit/generator/CraftChunkData.java | 158 + .../generator/CustomChunkGenerator.java | 196 + .../generator/InternalChunkGenerator.java | 8 + .../generator/NetherChunkGenerator.java | 12 + .../generator/NormalChunkGenerator.java | 123 + .../generator/SkyLandsChunkGenerator.java | 12 + .../help/CommandAliasHelpTopic.java | 46 + .../craftbukkit/help/CustomHelpTopic.java | 31 + .../help/CustomIndexHelpTopic.java | 40 + .../craftbukkit/help/HelpTopicAmendment.java | 50 + .../craftbukkit/help/HelpYamlReader.java | 119 + .../help/MultipleCommandAliasHelpTopic.java | 54 + .../MultipleCommandAliasHelpTopicFactory.java | 15 + .../craftbukkit/help/SimpleHelpMap.java | 218 ++ .../craftbukkit/inventory/CraftContainer.java | 220 ++ .../inventory/CraftCustomTagTypeRegistry.java | 217 ++ .../inventory/CraftEntityEquipment.java | 193 + .../inventory/CraftFurnaceRecipe.java | 30 + .../craftbukkit/inventory/CraftInventory.java | 510 +++ .../CraftInventoryAbstractHorse.java | 22 + .../inventory/CraftInventoryAnvil.java | 86 + .../inventory/CraftInventoryBeacon.java | 19 + .../inventory/CraftInventoryBrewer.java | 36 + .../inventory/CraftInventoryCrafting.java | 121 + .../inventory/CraftInventoryCustom.java | 227 ++ .../inventory/CraftInventoryDoubleChest.java | 68 + .../inventory/CraftInventoryEnchanting.java | 31 + .../inventory/CraftInventoryFurnace.java | 42 + .../inventory/CraftInventoryHorse.java | 20 + .../inventory/CraftInventoryLlama.java | 22 + .../inventory/CraftInventoryMerchant.java | 28 + .../inventory/CraftInventoryPlayer.java | 303 ++ .../inventory/CraftInventoryView.java | 75 + .../inventory/CraftItemFactory.java | 318 ++ .../craftbukkit/inventory/CraftItemStack.java | 533 +++ .../craftbukkit/inventory/CraftMerchant.java | 80 + .../inventory/CraftMerchantCustom.java | 76 + .../inventory/CraftMerchantRecipe.java | 82 + .../inventory/CraftMetaArmorStand.java | 311 ++ .../inventory/CraftMetaBanner.java | 246 ++ .../inventory/CraftMetaBlockState.java | 615 ++++ .../craftbukkit/inventory/CraftMetaBook.java | 427 +++ .../inventory/CraftMetaBookSigned.java | 134 + .../inventory/CraftMetaCharge.java | 131 + .../inventory/CraftMetaEnchantedBook.java | 168 + .../inventory/CraftMetaFirework.java | 395 ++ .../craftbukkit/inventory/CraftMetaItem.java | 1622 +++++++++ .../inventory/CraftMetaKnowledgeBook.java | 173 + .../inventory/CraftMetaLeatherArmor.java | 139 + .../craftbukkit/inventory/CraftMetaMap.java | 280 ++ .../inventory/CraftMetaPotion.java | 344 ++ .../craftbukkit/inventory/CraftMetaSkull.java | 237 ++ .../inventory/CraftMetaSpawnEgg.java | 269 ++ .../CraftMetaTropicalFishBucket.java | 168 + .../craftbukkit/inventory/CraftRecipe.java | 62 + .../inventory/CraftSaddledInventory.java | 15 + .../inventory/CraftShapedRecipe.java | 62 + .../inventory/CraftShapelessRecipe.java | 50 + .../inventory/InventoryIterator.java | 64 + .../inventory/InventoryWrapper.java | 191 + .../craftbukkit/inventory/RecipeIterator.java | 28 + .../tags/CraftCustomItemTagContainer.java | 133 + .../tags/CraftItemTagAdapterContext.java | 24 + .../util/CraftCustomInventoryConverter.java | 27 + .../inventory/util/CraftInventoryCreator.java | 65 + .../util/CraftTileInventoryConverter.java | 126 + .../craftbukkit/map/CraftMapCanvas.java | 111 + .../craftbukkit/map/CraftMapRenderer.java | 50 + .../bukkit/craftbukkit/map/CraftMapView.java | 178 + .../bukkit/craftbukkit/map/RenderData.java | 16 + .../metadata/BlockMetadataStore.java | 94 + .../metadata/EntityMetadataStore.java | 23 + .../metadata/PlayerMetadataStore.java | 23 + .../metadata/WorldMetadataStore.java | 22 + .../craftbukkit/potion/CraftPotionBrewer.java | 48 + .../potion/CraftPotionEffectType.java | 102 + .../craftbukkit/potion/CraftPotionUtil.java | 122 + .../CraftBlockProjectileSource.java | 161 + .../scheduler/CraftAsyncDebugger.java | 37 + .../scheduler/CraftAsyncScheduler.java | 122 + .../craftbukkit/scheduler/CraftAsyncTask.java | 109 + .../craftbukkit/scheduler/CraftFuture.java | 104 + .../craftbukkit/scheduler/CraftScheduler.java | 616 ++++ .../craftbukkit/scheduler/CraftTask.java | 137 + .../craftbukkit/scoreboard/CraftCriteria.java | 65 + .../scoreboard/CraftObjective.java | 155 + .../craftbukkit/scoreboard/CraftScore.java | 69 + .../scoreboard/CraftScoreboard.java | 186 + .../scoreboard/CraftScoreboardComponent.java | 17 + .../scoreboard/CraftScoreboardManager.java | 110 + .../CraftScoreboardTranslations.java | 35 + .../craftbukkit/scoreboard/CraftTeam.java | 295 ++ .../bukkit/craftbukkit/tag/CraftBlockTag.java | 27 + .../bukkit/craftbukkit/tag/CraftItemTag.java | 27 + .../org/bukkit/craftbukkit/tag/CraftTag.java | 36 + .../util/AsynchronousExecutor.java | 360 ++ .../util/BlockStateListPopulator.java | 48 + .../bukkit/craftbukkit/util/Commodore.java | 434 +++ .../craftbukkit/util/CraftChatMessage.java | 276 ++ .../craftbukkit/util/CraftDamageSource.java | 31 + .../bukkit/craftbukkit/util/CraftEvil.java | 98 + .../craftbukkit/util/CraftIconCache.java | 12 + .../bukkit/craftbukkit/util/CraftLegacy.java | 437 +++ .../craftbukkit/util/CraftMagicNumbers.java | 267 ++ .../util/CraftNBTTagConfigSerializer.java | 98 + .../craftbukkit/util/CraftNamespacedKey.java | 30 + .../craftbukkit/util/CraftRayTraceResult.java | 42 + .../craftbukkit/util/DatFileFilter.java | 10 + .../util/DummyGeneratorAccess.java | 235 ++ .../craftbukkit/util/ForwardLogHandler.java | 52 + .../bukkit/craftbukkit/util/HashTreeSet.java | 117 + .../bukkit/craftbukkit/util/LazyHashSet.java | 97 + .../craftbukkit/util/LazyPlayerSet.java | 30 + .../org/bukkit/craftbukkit/util/LongHash.java | 15 + .../bukkit/craftbukkit/util/LongHashSet.java | 302 ++ .../craftbukkit/util/LongObjectHashMap.java | 422 +++ .../craftbukkit/util/MojangNameLookup.java | 63 + .../util/ServerShutdownThread.java | 28 + .../util/ShortConsoleLogFormatter.java | 61 + .../bukkit/craftbukkit/util/UnsafeList.java | 278 ++ .../bukkit/craftbukkit/util/Versioning.java | 29 + .../org/bukkit/craftbukkit/util/Waitable.java | 46 + .../craftbukkit/util/WeakCollection.java | 169 + .../util/permissions/CommandPermissions.java | 38 + .../permissions/CraftDefaultPermissions.java | 22 + .../java/org/spigotmc/ActivationRange.java | 333 ++ src/main/java/org/spigotmc/AsyncCatcher.java | 18 + src/main/java/org/spigotmc/LimitStream.java | 39 + src/main/java/org/spigotmc/Metrics.java | 645 ++++ .../java/org/spigotmc/RestartCommand.java | 168 + .../org/spigotmc/SlackActivityAccountant.java | 78 + src/main/java/org/spigotmc/SpigotCommand.java | 44 + src/main/java/org/spigotmc/SpigotConfig.java | 396 ++ .../java/org/spigotmc/SpigotWorldConfig.java | 323 ++ src/main/java/org/spigotmc/SupplierUtils.java | 69 + src/main/java/org/spigotmc/TickLimiter.java | 20 + .../org/spigotmc/TicksPerSecondCommand.java | 44 + src/main/java/org/spigotmc/TrackingRange.java | 51 + src/main/java/org/spigotmc/ValidateUtils.java | 14 + .../java/org/spigotmc/WatchdogThread.java | 177 + src/main/resources/configurations/bukkit.yml | 37 + .../resources/configurations/commands.yml | 17 + src/main/resources/configurations/help.yml | 55 + .../resources/log4j2.component.properties | 1 + src/main/resources/log4j2.xml | 40 + src/test/java/org/bukkit/ArtTest.java | 69 + src/test/java/org/bukkit/BiomeTest.java | 26 + .../org/bukkit/BlockDataConversionTest.java | 38 + src/test/java/org/bukkit/BlockDataTest.java | 172 + src/test/java/org/bukkit/ChatTest.java | 41 + src/test/java/org/bukkit/DyeColorsTest.java | 46 + src/test/java/org/bukkit/EnchantmentTest.java | 25 + src/test/java/org/bukkit/GameRuleTest.java | 56 + src/test/java/org/bukkit/LegacyTest.java | 92 + src/test/java/org/bukkit/LootTablesTest.java | 22 + src/test/java/org/bukkit/MaterialTest.java | 50 + src/test/java/org/bukkit/NibbleArrayTest.java | 29 + src/test/java/org/bukkit/ParticleTest.java | 35 + src/test/java/org/bukkit/PerMaterialTest.java | 239 ++ src/test/java/org/bukkit/SoundTest.java | 42 + .../bukkit/StatisticsAndAchievementsTest.java | 60 + .../java/org/bukkit/StructureTypeTest.java | 45 + src/test/java/org/bukkit/WorldTypeTest.java | 19 + .../craftbukkit/generator/ChunkDataTest.java | 81 + .../inventory/CompositeSerialization.java | 62 + .../inventory/FactoryItemMaterialTest.java | 156 + .../inventory/ItemFactoryTest.java | 47 + .../inventory/ItemMetaCloneTest.java | 22 + .../inventory/ItemMetaCustomValueTest.java | 327 ++ .../ItemMetaImplementationOverrideTest.java | 80 + .../craftbukkit/inventory/ItemMetaTest.java | 423 +++ .../inventory/ItemStackBookTest.java | 213 ++ .../ItemStackEnchantStorageTest.java | 108 + .../ItemStackFireworkChargeTest.java | 128 + .../inventory/ItemStackFireworkTest.java | 184 + .../inventory/ItemStackLeatherTest.java | 89 + .../ItemStackLoreEnchantmentTest.java | 297 ++ .../inventory/ItemStackMapTest.java | 121 + .../inventory/ItemStackPotionsTest.java | 146 + .../inventory/ItemStackSkullTest.java | 88 + .../craftbukkit/inventory/ItemStackTest.java | 493 +++ .../inventory/NMSCraftItemStackTest.java | 35 + .../inventory/PlayerInventoryTest.java | 60 + .../bukkit/entity/EnderDragonPhaseTest.java | 50 + .../org/bukkit/entity/EntityTypesTest.java | 31 + .../org/bukkit/entity/TropicalFishTest.java | 45 + src/test/java/org/bukkit/map/MapTest.java | 62 + .../java/org/bukkit/potion/PotionTest.java | 35 + .../bukkit/support/AbstractTestingBase.java | 53 + .../org/bukkit/support/DummyEnchantments.java | 12 + .../java/org/bukkit/support/DummyServer.java | 111 + .../java/org/bukkit/support/Matchers.java | 30 + src/test/java/org/bukkit/support/Util.java | 31 + 1106 files changed, 193757 insertions(+) create mode 100644 .gitignore create mode 100644 CONTRIBUTING.md create mode 100644 LGPL.txt create mode 100644 LICENCE.txt create mode 100644 README.md create mode 100644 pom.xml create mode 100644 src/main/java/co/aikar/timings/MinecraftTimings.java create mode 100644 src/main/java/co/aikar/timings/WorldTimingsHandler.java create mode 100644 src/main/java/com/destroystokyo/paper/Metrics.java create mode 100644 src/main/java/com/destroystokyo/paper/PaperCommand.java create mode 100644 src/main/java/com/destroystokyo/paper/PaperConfig.java create mode 100644 src/main/java/com/destroystokyo/paper/PaperWorldConfig.java create mode 100644 src/main/java/com/destroystokyo/paper/PaperWorldEntityList.java create mode 100644 src/main/java/com/destroystokyo/paper/PaperWorldMap.java create mode 100644 src/main/java/com/destroystokyo/paper/ServerSchedulerReportingWrapper.java create mode 100644 src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java create mode 100644 src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java create mode 100644 src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java create mode 100644 src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java create mode 100644 src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java create mode 100644 src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java create mode 100644 src/main/java/com/destroystokyo/paper/console/PaperConsole.java create mode 100644 src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java create mode 100644 src/main/java/com/destroystokyo/paper/entity/CraftRangedEntity.java create mode 100644 src/main/java/com/destroystokyo/paper/entity/PaperPathfinder.java create mode 100644 src/main/java/com/destroystokyo/paper/loottable/PaperLootableBlockInventory.java create mode 100644 src/main/java/com/destroystokyo/paper/loottable/PaperLootableEntityInventory.java create mode 100644 src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventory.java create mode 100644 src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java create mode 100644 src/main/java/com/destroystokyo/paper/loottable/PaperMinecartLootableInventory.java create mode 100644 src/main/java/com/destroystokyo/paper/loottable/PaperTileEntityLootableInventory.java create mode 100644 src/main/java/com/destroystokyo/paper/network/PaperLegacyStatusClient.java create mode 100644 src/main/java/com/destroystokyo/paper/network/PaperNetworkClient.java create mode 100644 src/main/java/com/destroystokyo/paper/network/PaperServerListPingEventImpl.java create mode 100644 src/main/java/com/destroystokyo/paper/network/PaperStatusClient.java create mode 100644 src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java create mode 100644 src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java create mode 100644 src/main/java/com/destroystokyo/paper/profile/PaperAuthenticationService.java create mode 100644 src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java create mode 100644 src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java create mode 100644 src/main/java/com/destroystokyo/paper/profile/PaperUserAuthentication.java create mode 100644 src/main/java/com/destroystokyo/paper/proxy/VelocityProxy.java create mode 100644 src/main/java/com/destroystokyo/paper/util/PriorityQueuedExecutor.java create mode 100644 src/main/java/com/destroystokyo/paper/util/RedstoneWireTurbo.java create mode 100644 src/main/java/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java create mode 100644 src/main/java/com/mojang/brigadier/tree/CommandNode.java create mode 100644 src/main/java/net/minecraft/server/Advancement.java create mode 100644 src/main/java/net/minecraft/server/AdvancementDataPlayer.java create mode 100644 src/main/java/net/minecraft/server/AdvancementDataWorld.java create mode 100644 src/main/java/net/minecraft/server/Advancements.java create mode 100644 src/main/java/net/minecraft/server/ArgumentBlock.java create mode 100644 src/main/java/net/minecraft/server/ArgumentEntity.java create mode 100644 src/main/java/net/minecraft/server/ArgumentParserSelector.java create mode 100644 src/main/java/net/minecraft/server/AttributeInstance.java create mode 100644 src/main/java/net/minecraft/server/AttributeRanged.java create mode 100644 src/main/java/net/minecraft/server/AxisAlignedBB.java create mode 100644 src/main/java/net/minecraft/server/BaseBlockPosition.java create mode 100644 src/main/java/net/minecraft/server/BiomeBase.java create mode 100644 src/main/java/net/minecraft/server/Block.java create mode 100644 src/main/java/net/minecraft/server/BlockBeacon.java create mode 100644 src/main/java/net/minecraft/server/BlockBed.java create mode 100644 src/main/java/net/minecraft/server/BlockButtonAbstract.java create mode 100644 src/main/java/net/minecraft/server/BlockCactus.java create mode 100644 src/main/java/net/minecraft/server/BlockCake.java create mode 100644 src/main/java/net/minecraft/server/BlockCauldron.java create mode 100644 src/main/java/net/minecraft/server/BlockChest.java create mode 100644 src/main/java/net/minecraft/server/BlockChorusFlower.java create mode 100644 src/main/java/net/minecraft/server/BlockCocoa.java create mode 100644 src/main/java/net/minecraft/server/BlockCommand.java create mode 100644 src/main/java/net/minecraft/server/BlockConcretePowder.java create mode 100644 src/main/java/net/minecraft/server/BlockCoral.java create mode 100644 src/main/java/net/minecraft/server/BlockCoralFan.java create mode 100644 src/main/java/net/minecraft/server/BlockCoralFanWall.java create mode 100644 src/main/java/net/minecraft/server/BlockCoralPlant.java create mode 100644 src/main/java/net/minecraft/server/BlockCrops.java create mode 100644 src/main/java/net/minecraft/server/BlockData.java create mode 100644 src/main/java/net/minecraft/server/BlockDaylightDetector.java create mode 100644 src/main/java/net/minecraft/server/BlockDiodeAbstract.java create mode 100644 src/main/java/net/minecraft/server/BlockDirtSnowSpreadable.java create mode 100644 src/main/java/net/minecraft/server/BlockDispenser.java create mode 100644 src/main/java/net/minecraft/server/BlockDoor.java create mode 100644 src/main/java/net/minecraft/server/BlockDragonEgg.java create mode 100644 src/main/java/net/minecraft/server/BlockDropper.java create mode 100644 src/main/java/net/minecraft/server/BlockEnderPortal.java create mode 100644 src/main/java/net/minecraft/server/BlockFire.java create mode 100644 src/main/java/net/minecraft/server/BlockFluids.java create mode 100644 src/main/java/net/minecraft/server/BlockGrass.java create mode 100644 src/main/java/net/minecraft/server/BlockIce.java create mode 100644 src/main/java/net/minecraft/server/BlockIceFrost.java create mode 100644 src/main/java/net/minecraft/server/BlockJukeBox.java create mode 100644 src/main/java/net/minecraft/server/BlockKelp.java create mode 100644 src/main/java/net/minecraft/server/BlockLeaves.java create mode 100644 src/main/java/net/minecraft/server/BlockLever.java create mode 100644 src/main/java/net/minecraft/server/BlockMagma.java create mode 100644 src/main/java/net/minecraft/server/BlockMinecartDetector.java create mode 100644 src/main/java/net/minecraft/server/BlockMobSpawner.java create mode 100644 src/main/java/net/minecraft/server/BlockMonsterEggs.java create mode 100644 src/main/java/net/minecraft/server/BlockMushroom.java create mode 100644 src/main/java/net/minecraft/server/BlockNetherWart.java create mode 100644 src/main/java/net/minecraft/server/BlockNote.java create mode 100644 src/main/java/net/minecraft/server/BlockObserver.java create mode 100644 src/main/java/net/minecraft/server/BlockOre.java create mode 100644 src/main/java/net/minecraft/server/BlockPiston.java create mode 100644 src/main/java/net/minecraft/server/BlockPlant.java create mode 100644 src/main/java/net/minecraft/server/BlockPortal.java create mode 100644 src/main/java/net/minecraft/server/BlockPosition.java create mode 100644 src/main/java/net/minecraft/server/BlockPoweredRail.java create mode 100644 src/main/java/net/minecraft/server/BlockPressurePlateAbstract.java create mode 100644 src/main/java/net/minecraft/server/BlockPressurePlateBinary.java create mode 100644 src/main/java/net/minecraft/server/BlockPressurePlateWeighted.java create mode 100644 src/main/java/net/minecraft/server/BlockPumpkinCarved.java create mode 100644 src/main/java/net/minecraft/server/BlockRedstoneComparator.java create mode 100644 src/main/java/net/minecraft/server/BlockRedstoneLamp.java create mode 100644 src/main/java/net/minecraft/server/BlockRedstoneOre.java create mode 100644 src/main/java/net/minecraft/server/BlockRedstoneTorch.java create mode 100644 src/main/java/net/minecraft/server/BlockRedstoneWire.java create mode 100644 src/main/java/net/minecraft/server/BlockReed.java create mode 100644 src/main/java/net/minecraft/server/BlockSapling.java create mode 100644 src/main/java/net/minecraft/server/BlockShulkerBox.java create mode 100644 src/main/java/net/minecraft/server/BlockSkullAbstract.java create mode 100644 src/main/java/net/minecraft/server/BlockSnow.java create mode 100644 src/main/java/net/minecraft/server/BlockSnowBlock.java create mode 100644 src/main/java/net/minecraft/server/BlockSoil.java create mode 100644 src/main/java/net/minecraft/server/BlockSponge.java create mode 100644 src/main/java/net/minecraft/server/BlockState.java create mode 100644 src/main/java/net/minecraft/server/BlockStateBoolean.java create mode 100644 src/main/java/net/minecraft/server/BlockStateEnum.java create mode 100644 src/main/java/net/minecraft/server/BlockStateInteger.java create mode 100644 src/main/java/net/minecraft/server/BlockStem.java create mode 100644 src/main/java/net/minecraft/server/BlockTNT.java create mode 100644 src/main/java/net/minecraft/server/BlockTallPlant.java create mode 100644 src/main/java/net/minecraft/server/BlockTrapdoor.java create mode 100644 src/main/java/net/minecraft/server/BlockTripwire.java create mode 100644 src/main/java/net/minecraft/server/BlockTripwireHook.java create mode 100644 src/main/java/net/minecraft/server/BlockTurtleEgg.java create mode 100644 src/main/java/net/minecraft/server/BlockVine.java create mode 100644 src/main/java/net/minecraft/server/BlockWaterLily.java create mode 100644 src/main/java/net/minecraft/server/BlockWitherSkull.java create mode 100644 src/main/java/net/minecraft/server/BossBattleCustom.java create mode 100644 src/main/java/net/minecraft/server/Chunk.java create mode 100644 src/main/java/net/minecraft/server/ChunkCache.java create mode 100644 src/main/java/net/minecraft/server/ChunkCoordIntPair.java create mode 100644 src/main/java/net/minecraft/server/ChunkGenerator.java create mode 100644 src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java create mode 100644 src/main/java/net/minecraft/server/ChunkMap.java create mode 100644 src/main/java/net/minecraft/server/ChunkProviderGenerate.java create mode 100644 src/main/java/net/minecraft/server/ChunkProviderServer.java create mode 100644 src/main/java/net/minecraft/server/ChunkRegionLoader.java create mode 100644 src/main/java/net/minecraft/server/ChunkSection.java create mode 100644 src/main/java/net/minecraft/server/ChunkTaskScheduler.java create mode 100644 src/main/java/net/minecraft/server/CombatTracker.java create mode 100644 src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java create mode 100644 src/main/java/net/minecraft/server/CommandDebug.java create mode 100644 src/main/java/net/minecraft/server/CommandDispatcher.java create mode 100644 src/main/java/net/minecraft/server/CommandEffect.java create mode 100644 src/main/java/net/minecraft/server/CommandForceload.java create mode 100644 src/main/java/net/minecraft/server/CommandGamemode.java create mode 100644 src/main/java/net/minecraft/server/CommandGamerule.java create mode 100644 src/main/java/net/minecraft/server/CommandListenerWrapper.java create mode 100644 src/main/java/net/minecraft/server/CommandSpreadPlayers.java create mode 100644 src/main/java/net/minecraft/server/CommandSummon.java create mode 100644 src/main/java/net/minecraft/server/CommandTeleport.java create mode 100644 src/main/java/net/minecraft/server/Container.java create mode 100644 src/main/java/net/minecraft/server/ContainerAnvil.java create mode 100644 src/main/java/net/minecraft/server/ContainerBeacon.java create mode 100644 src/main/java/net/minecraft/server/ContainerBrewingStand.java create mode 100644 src/main/java/net/minecraft/server/ContainerChest.java create mode 100644 src/main/java/net/minecraft/server/ContainerDispenser.java create mode 100644 src/main/java/net/minecraft/server/ContainerEnchantTable.java create mode 100644 src/main/java/net/minecraft/server/ContainerFurnace.java create mode 100644 src/main/java/net/minecraft/server/ContainerHopper.java create mode 100644 src/main/java/net/minecraft/server/ContainerHorse.java create mode 100644 src/main/java/net/minecraft/server/ContainerMerchant.java create mode 100644 src/main/java/net/minecraft/server/ContainerPlayer.java create mode 100644 src/main/java/net/minecraft/server/ContainerShulkerBox.java create mode 100644 src/main/java/net/minecraft/server/ContainerWorkbench.java create mode 100644 src/main/java/net/minecraft/server/ControllerJump.java create mode 100644 src/main/java/net/minecraft/server/CraftingManager.java create mode 100644 src/main/java/net/minecraft/server/CrashReport.java create mode 100644 src/main/java/net/minecraft/server/CustomFunction.java create mode 100644 src/main/java/net/minecraft/server/CustomFunctionData.java create mode 100644 src/main/java/net/minecraft/server/DamageSource.java create mode 100644 src/main/java/net/minecraft/server/DataBits.java create mode 100644 src/main/java/net/minecraft/server/DataConverterFlatten.java create mode 100644 src/main/java/net/minecraft/server/DataConverterMap.java create mode 100644 src/main/java/net/minecraft/server/DataConverterRegistry.java create mode 100644 src/main/java/net/minecraft/server/DataPalette.java create mode 100644 src/main/java/net/minecraft/server/DataPaletteBlock.java create mode 100644 src/main/java/net/minecraft/server/DataWatcher.java create mode 100644 src/main/java/net/minecraft/server/DedicatedServer.java create mode 100644 src/main/java/net/minecraft/server/DefinedStructure.java create mode 100644 src/main/java/net/minecraft/server/DefinedStructureManager.java create mode 100644 src/main/java/net/minecraft/server/DispenseBehaviorItem.java create mode 100644 src/main/java/net/minecraft/server/DispenseBehaviorProjectile.java create mode 100644 src/main/java/net/minecraft/server/DispenserRegistry.java create mode 100644 src/main/java/net/minecraft/server/DragonControllerLandedFlame.java create mode 100644 src/main/java/net/minecraft/server/DragonControllerManager.java create mode 100644 src/main/java/net/minecraft/server/DragonControllerStrafe.java create mode 100644 src/main/java/net/minecraft/server/EULA.java create mode 100644 src/main/java/net/minecraft/server/Enchantment.java create mode 100644 src/main/java/net/minecraft/server/EnchantmentFrostWalker.java create mode 100644 src/main/java/net/minecraft/server/EnchantmentManager.java create mode 100644 src/main/java/net/minecraft/server/EnchantmentThorns.java create mode 100644 src/main/java/net/minecraft/server/EnchantmentWeaponDamage.java create mode 100644 src/main/java/net/minecraft/server/EnderDragonBattle.java create mode 100644 src/main/java/net/minecraft/server/Entity.java create mode 100644 src/main/java/net/minecraft/server/EntityAgeable.java create mode 100644 src/main/java/net/minecraft/server/EntityAnimal.java create mode 100644 src/main/java/net/minecraft/server/EntityAreaEffectCloud.java create mode 100644 src/main/java/net/minecraft/server/EntityArmorStand.java create mode 100644 src/main/java/net/minecraft/server/EntityArrow.java create mode 100644 src/main/java/net/minecraft/server/EntityBat.java create mode 100644 src/main/java/net/minecraft/server/EntityBoat.java create mode 100644 src/main/java/net/minecraft/server/EntityCaveSpider.java create mode 100644 src/main/java/net/minecraft/server/EntityChicken.java create mode 100644 src/main/java/net/minecraft/server/EntityCow.java create mode 100644 src/main/java/net/minecraft/server/EntityCreature.java create mode 100644 src/main/java/net/minecraft/server/EntityCreeper.java create mode 100644 src/main/java/net/minecraft/server/EntityDamageSourceIndirect.java create mode 100644 src/main/java/net/minecraft/server/EntityDolphin.java create mode 100644 src/main/java/net/minecraft/server/EntityDragonFireball.java create mode 100644 src/main/java/net/minecraft/server/EntityDrowned.java create mode 100644 src/main/java/net/minecraft/server/EntityEgg.java create mode 100644 src/main/java/net/minecraft/server/EntityEnderCrystal.java create mode 100644 src/main/java/net/minecraft/server/EntityEnderDragon.java create mode 100644 src/main/java/net/minecraft/server/EntityEnderPearl.java create mode 100644 src/main/java/net/minecraft/server/EntityEnderman.java create mode 100644 src/main/java/net/minecraft/server/EntityEvoker.java create mode 100644 src/main/java/net/minecraft/server/EntityEvokerFangs.java create mode 100644 src/main/java/net/minecraft/server/EntityExperienceOrb.java create mode 100644 src/main/java/net/minecraft/server/EntityFallingBlock.java create mode 100644 src/main/java/net/minecraft/server/EntityFireball.java create mode 100644 src/main/java/net/minecraft/server/EntityFireworks.java create mode 100644 src/main/java/net/minecraft/server/EntityFish.java create mode 100644 src/main/java/net/minecraft/server/EntityFishingHook.java create mode 100644 src/main/java/net/minecraft/server/EntityGhast.java create mode 100644 src/main/java/net/minecraft/server/EntityGuardianElder.java create mode 100644 src/main/java/net/minecraft/server/EntityHanging.java create mode 100644 src/main/java/net/minecraft/server/EntityHorse.java create mode 100644 src/main/java/net/minecraft/server/EntityHorseAbstract.java create mode 100644 src/main/java/net/minecraft/server/EntityHorseChestedAbstract.java create mode 100644 src/main/java/net/minecraft/server/EntityHorseDonkey.java create mode 100644 src/main/java/net/minecraft/server/EntityHorseSkeleton.java create mode 100644 src/main/java/net/minecraft/server/EntityHorseZombie.java create mode 100644 src/main/java/net/minecraft/server/EntityHuman.java create mode 100644 src/main/java/net/minecraft/server/EntityIllagerIllusioner.java create mode 100644 src/main/java/net/minecraft/server/EntityInsentient.java create mode 100644 src/main/java/net/minecraft/server/EntityIronGolem.java create mode 100644 src/main/java/net/minecraft/server/EntityItem.java create mode 100644 src/main/java/net/minecraft/server/EntityItemFrame.java create mode 100644 src/main/java/net/minecraft/server/EntityLargeFireball.java create mode 100644 src/main/java/net/minecraft/server/EntityLeash.java create mode 100644 src/main/java/net/minecraft/server/EntityLightning.java create mode 100644 src/main/java/net/minecraft/server/EntityLiving.java create mode 100644 src/main/java/net/minecraft/server/EntityLlama.java create mode 100644 src/main/java/net/minecraft/server/EntityLlamaSpit.java create mode 100644 src/main/java/net/minecraft/server/EntityMinecartAbstract.java create mode 100644 src/main/java/net/minecraft/server/EntityMinecartCommandBlock.java create mode 100644 src/main/java/net/minecraft/server/EntityMinecartContainer.java create mode 100644 src/main/java/net/minecraft/server/EntityMonster.java create mode 100644 src/main/java/net/minecraft/server/EntityMushroomCow.java create mode 100644 src/main/java/net/minecraft/server/EntityOcelot.java create mode 100644 src/main/java/net/minecraft/server/EntityPainting.java create mode 100644 src/main/java/net/minecraft/server/EntityParrot.java create mode 100644 src/main/java/net/minecraft/server/EntityPhantom.java create mode 100644 src/main/java/net/minecraft/server/EntityPig.java create mode 100644 src/main/java/net/minecraft/server/EntityPigZombie.java create mode 100644 src/main/java/net/minecraft/server/EntityPlayer.java create mode 100644 src/main/java/net/minecraft/server/EntityPolarBear.java create mode 100644 src/main/java/net/minecraft/server/EntityPotion.java create mode 100644 src/main/java/net/minecraft/server/EntityProjectile.java create mode 100644 src/main/java/net/minecraft/server/EntityPufferFish.java create mode 100644 src/main/java/net/minecraft/server/EntityRabbit.java create mode 100644 src/main/java/net/minecraft/server/EntitySelector.java create mode 100644 src/main/java/net/minecraft/server/EntitySheep.java create mode 100644 src/main/java/net/minecraft/server/EntityShulker.java create mode 100644 src/main/java/net/minecraft/server/EntityShulkerBullet.java create mode 100644 src/main/java/net/minecraft/server/EntitySilverfish.java create mode 100644 src/main/java/net/minecraft/server/EntitySkeleton.java create mode 100644 src/main/java/net/minecraft/server/EntitySkeletonAbstract.java create mode 100644 src/main/java/net/minecraft/server/EntitySkeletonWither.java create mode 100644 src/main/java/net/minecraft/server/EntitySlice.java create mode 100644 src/main/java/net/minecraft/server/EntitySlime.java create mode 100644 src/main/java/net/minecraft/server/EntitySmallFireball.java create mode 100644 src/main/java/net/minecraft/server/EntitySnowman.java create mode 100644 src/main/java/net/minecraft/server/EntitySpectralArrow.java create mode 100644 src/main/java/net/minecraft/server/EntitySpider.java create mode 100644 src/main/java/net/minecraft/server/EntitySquid.java create mode 100644 src/main/java/net/minecraft/server/EntityTNTPrimed.java create mode 100644 src/main/java/net/minecraft/server/EntityThrownExpBottle.java create mode 100644 src/main/java/net/minecraft/server/EntityThrownTrident.java create mode 100644 src/main/java/net/minecraft/server/EntityTippedArrow.java create mode 100644 src/main/java/net/minecraft/server/EntityTracker.java create mode 100644 src/main/java/net/minecraft/server/EntityTrackerEntry.java create mode 100644 src/main/java/net/minecraft/server/EntityTurtle.java create mode 100644 src/main/java/net/minecraft/server/EntityTypes.java create mode 100644 src/main/java/net/minecraft/server/EntityVex.java create mode 100644 src/main/java/net/minecraft/server/EntityVillager.java create mode 100644 src/main/java/net/minecraft/server/EntityVindicator.java create mode 100644 src/main/java/net/minecraft/server/EntityWitch.java create mode 100644 src/main/java/net/minecraft/server/EntityWither.java create mode 100644 src/main/java/net/minecraft/server/EntityWitherSkull.java create mode 100644 src/main/java/net/minecraft/server/EntityWolf.java create mode 100644 src/main/java/net/minecraft/server/EntityZombie.java create mode 100644 src/main/java/net/minecraft/server/EntityZombieHusk.java create mode 100644 src/main/java/net/minecraft/server/EntityZombieVillager.java create mode 100644 src/main/java/net/minecraft/server/EnumCreatureType.java create mode 100644 src/main/java/net/minecraft/server/EnumDirection.java create mode 100644 src/main/java/net/minecraft/server/EnumItemSlot.java create mode 100644 src/main/java/net/minecraft/server/ExpirableListEntry.java create mode 100644 src/main/java/net/minecraft/server/ExpiringMap.java create mode 100644 src/main/java/net/minecraft/server/Explosion.java create mode 100644 src/main/java/net/minecraft/server/FileIOThread.java create mode 100644 src/main/java/net/minecraft/server/FluidTypeFlowing.java create mode 100644 src/main/java/net/minecraft/server/FluidTypeLava.java create mode 100644 src/main/java/net/minecraft/server/FoodMetaData.java create mode 100644 src/main/java/net/minecraft/server/FurnaceRecipe.java create mode 100644 src/main/java/net/minecraft/server/GameProfileBanEntry.java create mode 100644 src/main/java/net/minecraft/server/GenericAttributes.java create mode 100644 src/main/java/net/minecraft/server/HandshakeListener.java create mode 100644 src/main/java/net/minecraft/server/IBlockData.java create mode 100644 src/main/java/net/minecraft/server/IChatBaseComponent.java create mode 100644 src/main/java/net/minecraft/server/IChunkLoader.java create mode 100644 src/main/java/net/minecraft/server/ICommandListener.java create mode 100644 src/main/java/net/minecraft/server/IDataManager.java create mode 100644 src/main/java/net/minecraft/server/IEntitySelector.java create mode 100644 src/main/java/net/minecraft/server/IInventory.java create mode 100644 src/main/java/net/minecraft/server/IRangedEntity.java create mode 100644 src/main/java/net/minecraft/server/IRecipe.java create mode 100644 src/main/java/net/minecraft/server/IWorldReader.java create mode 100644 src/main/java/net/minecraft/server/IWorldWriter.java create mode 100644 src/main/java/net/minecraft/server/InventoryCraftResult.java create mode 100644 src/main/java/net/minecraft/server/InventoryCrafting.java create mode 100644 src/main/java/net/minecraft/server/InventoryEnderChest.java create mode 100644 src/main/java/net/minecraft/server/InventoryHorseChest.java create mode 100644 src/main/java/net/minecraft/server/InventoryLargeChest.java create mode 100644 src/main/java/net/minecraft/server/InventoryMerchant.java create mode 100644 src/main/java/net/minecraft/server/InventorySubcontainer.java create mode 100644 src/main/java/net/minecraft/server/ItemArmor.java create mode 100644 src/main/java/net/minecraft/server/ItemArmorStand.java create mode 100644 src/main/java/net/minecraft/server/ItemBlock.java create mode 100644 src/main/java/net/minecraft/server/ItemBoat.java create mode 100644 src/main/java/net/minecraft/server/ItemBow.java create mode 100644 src/main/java/net/minecraft/server/ItemBucket.java create mode 100644 src/main/java/net/minecraft/server/ItemChorusFruit.java create mode 100644 src/main/java/net/minecraft/server/ItemDebugStick.java create mode 100644 src/main/java/net/minecraft/server/ItemDye.java create mode 100644 src/main/java/net/minecraft/server/ItemEgg.java create mode 100644 src/main/java/net/minecraft/server/ItemEndCrystal.java create mode 100644 src/main/java/net/minecraft/server/ItemEnderPearl.java create mode 100644 src/main/java/net/minecraft/server/ItemExpBottle.java create mode 100644 src/main/java/net/minecraft/server/ItemFireball.java create mode 100644 src/main/java/net/minecraft/server/ItemFireworks.java create mode 100644 src/main/java/net/minecraft/server/ItemFish.java create mode 100644 src/main/java/net/minecraft/server/ItemFishingRod.java create mode 100644 src/main/java/net/minecraft/server/ItemFlintAndSteel.java create mode 100644 src/main/java/net/minecraft/server/ItemFood.java create mode 100644 src/main/java/net/minecraft/server/ItemGoldenApple.java create mode 100644 src/main/java/net/minecraft/server/ItemGoldenAppleEnchanted.java create mode 100644 src/main/java/net/minecraft/server/ItemHanging.java create mode 100644 src/main/java/net/minecraft/server/ItemLeash.java create mode 100644 src/main/java/net/minecraft/server/ItemLingeringPotion.java create mode 100644 src/main/java/net/minecraft/server/ItemMilkBucket.java create mode 100644 src/main/java/net/minecraft/server/ItemMinecart.java create mode 100644 src/main/java/net/minecraft/server/ItemPotion.java create mode 100644 src/main/java/net/minecraft/server/ItemRecord.java create mode 100644 src/main/java/net/minecraft/server/ItemSkullPlayer.java create mode 100644 src/main/java/net/minecraft/server/ItemSnowball.java create mode 100644 src/main/java/net/minecraft/server/ItemSplashPotion.java create mode 100644 src/main/java/net/minecraft/server/ItemStack.java create mode 100644 src/main/java/net/minecraft/server/ItemTrident.java create mode 100644 src/main/java/net/minecraft/server/ItemWaterLily.java create mode 100644 src/main/java/net/minecraft/server/ItemWorldMap.java create mode 100644 src/main/java/net/minecraft/server/JsonList.java create mode 100644 src/main/java/net/minecraft/server/KeyedObject.java create mode 100644 src/main/java/net/minecraft/server/LegacyPingHandler.java create mode 100644 src/main/java/net/minecraft/server/LocaleLanguage.java create mode 100644 src/main/java/net/minecraft/server/LoginListener.java create mode 100644 src/main/java/net/minecraft/server/LootEnchantFunction.java create mode 100644 src/main/java/net/minecraft/server/LootItemConditionRandomChanceWithLooting.java create mode 100644 src/main/java/net/minecraft/server/LootSelectorEntry.java create mode 100644 src/main/java/net/minecraft/server/LootTableInfo.java create mode 100644 src/main/java/net/minecraft/server/MCUtil.java create mode 100644 src/main/java/net/minecraft/server/MathHelper.java create mode 100644 src/main/java/net/minecraft/server/MerchantRecipe.java create mode 100644 src/main/java/net/minecraft/server/MethodProfiler.java create mode 100644 src/main/java/net/minecraft/server/MinecraftServer.java create mode 100644 src/main/java/net/minecraft/server/MobEffectList.java create mode 100644 src/main/java/net/minecraft/server/MobSpawnerAbstract.java create mode 100644 src/main/java/net/minecraft/server/MobSpawnerPhantom.java create mode 100644 src/main/java/net/minecraft/server/NBTBase.java create mode 100644 src/main/java/net/minecraft/server/NBTCompressedStreamTools.java create mode 100644 src/main/java/net/minecraft/server/NBTTagByteArray.java create mode 100644 src/main/java/net/minecraft/server/NBTTagCompound.java create mode 100644 src/main/java/net/minecraft/server/NBTTagIntArray.java create mode 100644 src/main/java/net/minecraft/server/NBTTagList.java create mode 100644 src/main/java/net/minecraft/server/NameReferencingFileConverter.java create mode 100644 src/main/java/net/minecraft/server/NavigationAbstract.java create mode 100644 src/main/java/net/minecraft/server/NetworkManager.java create mode 100644 src/main/java/net/minecraft/server/NibbleArray.java create mode 100644 src/main/java/net/minecraft/server/Packet.java create mode 100644 src/main/java/net/minecraft/server/PacketDataSerializer.java create mode 100644 src/main/java/net/minecraft/server/PacketEncoder.java create mode 100644 src/main/java/net/minecraft/server/PacketHandshakingInSetProtocol.java create mode 100644 src/main/java/net/minecraft/server/PacketLoginInCustomPayload.java create mode 100644 src/main/java/net/minecraft/server/PacketLoginOutCustomPayload.java create mode 100644 src/main/java/net/minecraft/server/PacketPlayInBlockPlace.java create mode 100644 src/main/java/net/minecraft/server/PacketPlayInChat.java create mode 100644 src/main/java/net/minecraft/server/PacketPlayInCloseWindow.java create mode 100644 src/main/java/net/minecraft/server/PacketPlayInUseEntity.java create mode 100644 src/main/java/net/minecraft/server/PacketPlayInUseItem.java create mode 100644 src/main/java/net/minecraft/server/PacketPlayOutChat.java create mode 100644 src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java create mode 100644 src/main/java/net/minecraft/server/PacketPlayOutScoreboardTeam.java create mode 100644 src/main/java/net/minecraft/server/PacketPlayOutTitle.java create mode 100644 src/main/java/net/minecraft/server/PacketPlayOutUpdateTime.java create mode 100644 src/main/java/net/minecraft/server/PacketPlayOutWindowItems.java create mode 100644 src/main/java/net/minecraft/server/PacketPlayOutWorldBorder.java create mode 100644 src/main/java/net/minecraft/server/PacketStatusListener.java create mode 100644 src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java create mode 100644 src/main/java/net/minecraft/server/PaperLightingQueue.java create mode 100644 src/main/java/net/minecraft/server/PathEntity.java create mode 100644 src/main/java/net/minecraft/server/PathPoint.java create mode 100644 src/main/java/net/minecraft/server/Pathfinder.java create mode 100644 src/main/java/net/minecraft/server/PathfinderAbstract.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoal.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalBreakDoor.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalBreed.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalDefendVillage.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalDoorInteract.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalEatTile.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalFloat.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalFollowOwner.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalGotoTarget.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalHorseTrap.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalHurtByTarget.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalMakeLove.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalNearestAttackableTarget.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalNearestAttackableTargetInsentient.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalOwnerHurtByTarget.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalOwnerHurtTarget.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalPanic.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalRemoveBlock.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalSelector.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalSit.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalTame.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalTarget.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalTargetNearestPlayer.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalTempt.java create mode 100644 src/main/java/net/minecraft/server/PathfinderGoalVillagerFarm.java create mode 100644 src/main/java/net/minecraft/server/PathfinderNormal.java create mode 100644 src/main/java/net/minecraft/server/PersistentScoreboard.java create mode 100644 src/main/java/net/minecraft/server/PersistentVillage.java create mode 100644 src/main/java/net/minecraft/server/PlayerChunk.java create mode 100644 src/main/java/net/minecraft/server/PlayerChunkMap.java create mode 100644 src/main/java/net/minecraft/server/PlayerConnection.java create mode 100644 src/main/java/net/minecraft/server/PlayerConnectionUtils.java create mode 100644 src/main/java/net/minecraft/server/PlayerInteractManager.java create mode 100644 src/main/java/net/minecraft/server/PlayerInventory.java create mode 100644 src/main/java/net/minecraft/server/PlayerList.java create mode 100644 src/main/java/net/minecraft/server/PortalTravelAgent.java create mode 100644 src/main/java/net/minecraft/server/PotionUtil.java create mode 100644 src/main/java/net/minecraft/server/PropertyManager.java create mode 100644 src/main/java/net/minecraft/server/ProtoChunk.java create mode 100644 src/main/java/net/minecraft/server/RandomPositionGenerator.java create mode 100644 src/main/java/net/minecraft/server/RecipeArmorDye.java create mode 100644 src/main/java/net/minecraft/server/RecipeBannerAdd.java create mode 100644 src/main/java/net/minecraft/server/RecipeBannerDuplicate.java create mode 100644 src/main/java/net/minecraft/server/RecipeBookClone.java create mode 100644 src/main/java/net/minecraft/server/RecipeBookServer.java create mode 100644 src/main/java/net/minecraft/server/RecipeFireworks.java create mode 100644 src/main/java/net/minecraft/server/RecipeFireworksFade.java create mode 100644 src/main/java/net/minecraft/server/RecipeFireworksStar.java create mode 100644 src/main/java/net/minecraft/server/RecipeItemStack.java create mode 100644 src/main/java/net/minecraft/server/RecipeMapClone.java create mode 100644 src/main/java/net/minecraft/server/RecipeRepair.java create mode 100644 src/main/java/net/minecraft/server/RecipeShulkerBox.java create mode 100644 src/main/java/net/minecraft/server/RecipeTippedArrow.java create mode 100644 src/main/java/net/minecraft/server/RecipiesShield.java create mode 100644 src/main/java/net/minecraft/server/RegionFile.java create mode 100644 src/main/java/net/minecraft/server/RegionFileCache.java create mode 100644 src/main/java/net/minecraft/server/RegionLimitedWorldAccess.java create mode 100644 src/main/java/net/minecraft/server/Registry.java create mode 100644 src/main/java/net/minecraft/server/RegistryBlockID.java create mode 100644 src/main/java/net/minecraft/server/RegistryID.java create mode 100644 src/main/java/net/minecraft/server/RegistryMaterials.java create mode 100644 src/main/java/net/minecraft/server/RemoteControlCommandListener.java create mode 100644 src/main/java/net/minecraft/server/RemoteControlListener.java create mode 100644 src/main/java/net/minecraft/server/SchedulerBatch.java create mode 100644 src/main/java/net/minecraft/server/ScoreboardServer.java create mode 100644 src/main/java/net/minecraft/server/SecondaryWorldServer.java create mode 100644 src/main/java/net/minecraft/server/ServerConnection.java create mode 100644 src/main/java/net/minecraft/server/ServerPing.java create mode 100644 src/main/java/net/minecraft/server/ServerStatisticManager.java create mode 100644 src/main/java/net/minecraft/server/ShapeDetector.java create mode 100644 src/main/java/net/minecraft/server/ShapedRecipes.java create mode 100644 src/main/java/net/minecraft/server/ShapelessRecipes.java create mode 100644 src/main/java/net/minecraft/server/SlotFurnaceResult.java create mode 100644 src/main/java/net/minecraft/server/SpawnerCreature.java create mode 100644 src/main/java/net/minecraft/server/StatisticManager.java create mode 100644 src/main/java/net/minecraft/server/StructureGenerator.java create mode 100644 src/main/java/net/minecraft/server/StructurePiece.java create mode 100644 src/main/java/net/minecraft/server/StructureStart.java create mode 100644 src/main/java/net/minecraft/server/SystemUtils.java create mode 100644 src/main/java/net/minecraft/server/TagRegistry.java create mode 100644 src/main/java/net/minecraft/server/TagsServer.java create mode 100644 src/main/java/net/minecraft/server/TickListServer.java create mode 100644 src/main/java/net/minecraft/server/TileEntity.java create mode 100644 src/main/java/net/minecraft/server/TileEntityBanner.java create mode 100644 src/main/java/net/minecraft/server/TileEntityBeacon.java create mode 100644 src/main/java/net/minecraft/server/TileEntityBrewingStand.java create mode 100644 src/main/java/net/minecraft/server/TileEntityChest.java create mode 100644 src/main/java/net/minecraft/server/TileEntityCommand.java create mode 100644 src/main/java/net/minecraft/server/TileEntityConduit.java create mode 100644 src/main/java/net/minecraft/server/TileEntityContainer.java create mode 100644 src/main/java/net/minecraft/server/TileEntityDispenser.java create mode 100644 src/main/java/net/minecraft/server/TileEntityEndGateway.java create mode 100644 src/main/java/net/minecraft/server/TileEntityEnderChest.java create mode 100644 src/main/java/net/minecraft/server/TileEntityFurnace.java create mode 100644 src/main/java/net/minecraft/server/TileEntityHopper.java create mode 100644 src/main/java/net/minecraft/server/TileEntityLootable.java create mode 100644 src/main/java/net/minecraft/server/TileEntityShulkerBox.java create mode 100644 src/main/java/net/minecraft/server/TileEntitySign.java create mode 100644 src/main/java/net/minecraft/server/TileEntitySkull.java create mode 100644 src/main/java/net/minecraft/server/UserCache.java create mode 100644 src/main/java/net/minecraft/server/Village.java create mode 100644 src/main/java/net/minecraft/server/VillageDoor.java create mode 100644 src/main/java/net/minecraft/server/VillageSiege.java create mode 100644 src/main/java/net/minecraft/server/World.java create mode 100644 src/main/java/net/minecraft/server/WorldBorder.java create mode 100644 src/main/java/net/minecraft/server/WorldData.java create mode 100644 src/main/java/net/minecraft/server/WorldGenEndCityPieces.java create mode 100644 src/main/java/net/minecraft/server/WorldGenFeatureDesertPyramid.java create mode 100644 src/main/java/net/minecraft/server/WorldGenFeatureIgloo.java create mode 100644 src/main/java/net/minecraft/server/WorldGenFeatureJunglePyramid.java create mode 100644 src/main/java/net/minecraft/server/WorldGenFeatureOceanRuin.java create mode 100644 src/main/java/net/minecraft/server/WorldGenFeatureOceanRuinPieces.java create mode 100644 src/main/java/net/minecraft/server/WorldGenFeatureRandomScattered.java create mode 100644 src/main/java/net/minecraft/server/WorldGenFeatureShipwreck.java create mode 100644 src/main/java/net/minecraft/server/WorldGenFeatureSwampHut.java create mode 100644 src/main/java/net/minecraft/server/WorldGenGroundBush.java create mode 100644 src/main/java/net/minecraft/server/WorldGenMegaTreeProvider.java create mode 100644 src/main/java/net/minecraft/server/WorldGenMonument.java create mode 100644 src/main/java/net/minecraft/server/WorldGenMonumentPieces.java create mode 100644 src/main/java/net/minecraft/server/WorldGenStronghold.java create mode 100644 src/main/java/net/minecraft/server/WorldGenTreeProvider.java create mode 100644 src/main/java/net/minecraft/server/WorldGenVillage.java create mode 100644 src/main/java/net/minecraft/server/WorldGenVillagePieces.java create mode 100644 src/main/java/net/minecraft/server/WorldGenWitchHut.java create mode 100644 src/main/java/net/minecraft/server/WorldGenWoodlandMansionPieces.java create mode 100644 src/main/java/net/minecraft/server/WorldManager.java create mode 100644 src/main/java/net/minecraft/server/WorldMap.java create mode 100644 src/main/java/net/minecraft/server/WorldNBTStorage.java create mode 100644 src/main/java/net/minecraft/server/WorldPersistentData.java create mode 100644 src/main/java/net/minecraft/server/WorldProvider.java create mode 100644 src/main/java/net/minecraft/server/WorldProviderHell.java create mode 100644 src/main/java/net/minecraft/server/WorldProviderTheEnd.java create mode 100644 src/main/java/net/minecraft/server/WorldServer.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftArt.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftChunk.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftCrashReport.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftEffect.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftEquipmentSlot.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftFluidCollisionMode.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftIpBanEntry.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftIpBanList.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftLootTable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftParticle.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftProfileBanEntry.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftProfileBanList.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftServer.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftSound.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftStatistic.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftTravelAgent.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftWorld.java create mode 100644 src/main/java/org/bukkit/craftbukkit/CraftWorldBorder.java create mode 100644 src/main/java/org/bukkit/craftbukkit/LoggerOutputStream.java create mode 100644 src/main/java/org/bukkit/craftbukkit/Main.java create mode 100644 src/main/java/org/bukkit/craftbukkit/Overridden.java create mode 100644 src/main/java/org/bukkit/craftbukkit/TrigMath.java create mode 100644 src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancement.java create mode 100644 src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementProgress.java create mode 100644 src/main/java/org/bukkit/craftbukkit/attribute/CraftAttributeInstance.java create mode 100644 src/main/java/org/bukkit/craftbukkit/attribute/CraftAttributeMap.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftBanner.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftBeacon.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftBed.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftBrewingStand.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftChest.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftComparator.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftContainer.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftDaylightDetector.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftDispenser.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftDropper.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftEnchantingTable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftEndGateway.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftEnderChest.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftHopper.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftJukebox.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftLootable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftShulkerBox.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftSign.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftSkull.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/CraftStructureBlock.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/CraftAgeable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/CraftAnaloguePowerable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/CraftAttachable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/CraftBisected.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/CraftDirectional.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/CraftLevelled.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/CraftLightable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/CraftMultipleFacing.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/CraftOpenable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/CraftOrientable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/CraftPowerable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/CraftRail.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/CraftRotatable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/CraftSnowable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/CraftWaterlogged.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftBed.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftBrewingStand.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftBubbleColumn.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftCake.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftChest.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftCommandBlock.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftComparator.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftDaylightDetector.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftDispenser.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftDoor.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftEndPortalFrame.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftFarmland.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftGate.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftHopper.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftJukebox.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftLeaves.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftNoteBlock.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftPiston.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftPistonHead.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftRedstoneWire.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftRepeater.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSapling.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSeaPickle.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSlab.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSnow.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftStairs.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftStructureBlock.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSwitch.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftTNT.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftTechnicalPiston.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftTripwire.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/data/type/CraftTurtleEgg.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftAnvil.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftBanner.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftBannerWall.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftBed.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftBeetroot.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftBrewingStand.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftBubbleColumn.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftCactus.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftCake.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftCarrots.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftCauldron.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftChest.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftChestTrapped.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftChorusFlower.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftChorusFruit.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftCobbleWall.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftCocoa.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftCommand.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftConduit.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralDead.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralFan.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralFanAbstract.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralFanWall.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralFanWallAbstract.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralPlant.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftCrops.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftDaylightDetector.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftDirtSnow.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftDispenser.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftDoor.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftDropper.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftEndRod.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftEnderChest.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftEnderPortalFrame.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftFence.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftFenceGate.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftFire.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftFloorSign.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftFluids.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftFurnace.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftGlassPane.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftGlazedTerracotta.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftGrass.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftHay.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftHopper.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftHugeMushroom.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftIceFrost.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftIronBars.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftJukeBox.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftKelp.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftLadder.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftLeaves.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftLever.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftLogAbstract.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftMinecartDetector.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftMinecartTrack.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftMycel.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftNetherWart.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftNote.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftObserver.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftPiston.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftPistonExtension.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftPistonMoving.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftPortal.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftPotatoes.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftPoweredRail.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftPressurePlateBinary.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftPressurePlateWeighted.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftPumpkinCarved.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneComparator.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneLamp.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneOre.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneTorch.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneTorchWall.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneWire.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftReed.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftRepeater.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftRotatable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftSapling.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftSeaPickle.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftShulkerBox.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftSkull.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftSkullPlayer.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftSkullPlayerWall.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftSkullWall.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftSnow.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftSoil.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftStainedGlassPane.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftStairs.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftStem.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftStemAttached.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftStepAbstract.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftStoneButton.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftStructure.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftTNT.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftTallPlantFlower.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftTallPlantShearable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftTallSeaGrass.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftTorchWall.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftTrapdoor.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftTripwire.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftTripwireHook.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftTurtleEgg.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftVine.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftWallSign.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftWitherSkull.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftWitherSkullWall.java create mode 100644 src/main/java/org/bukkit/craftbukkit/block/impl/CraftWoodButton.java create mode 100644 src/main/java/org/bukkit/craftbukkit/boss/CraftBossBar.java create mode 100644 src/main/java/org/bukkit/craftbukkit/boss/CraftKeyedBossbar.java create mode 100644 src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOExecutor.java create mode 100644 src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java create mode 100644 src/main/java/org/bukkit/craftbukkit/chunkio/QueuedChunk.java create mode 100644 src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java create mode 100644 src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java create mode 100644 src/main/java/org/bukkit/craftbukkit/command/CraftBlockCommandSender.java create mode 100644 src/main/java/org/bukkit/craftbukkit/command/CraftCommandMap.java create mode 100644 src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java create mode 100644 src/main/java/org/bukkit/craftbukkit/command/CraftRemoteConsoleCommandSender.java create mode 100644 src/main/java/org/bukkit/craftbukkit/command/ProxiedNativeCommandSender.java create mode 100644 src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java create mode 100644 src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java create mode 100644 src/main/java/org/bukkit/craftbukkit/conversations/ConversationTracker.java create mode 100644 src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractHorse.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftAgeable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftAmbient.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftAnimals.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftArmorStand.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftBlaze.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftBoat.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftCaveSpider.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftChestedHorse.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftChicken.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftCod.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftComplexLivingEntity.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftComplexPart.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftCow.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftCreature.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftCreeper.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftDonkey.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftDragonFireball.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftDrowned.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftEgg.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftElderGuardian.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftEnderCrystal.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragon.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragonPart.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftEnderPearl.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftEnderSignal.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftEnderman.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftEvoker.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftEvokerFangs.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftFish.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftFlying.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftGhast.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftGiant.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftGolem.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftGuardian.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftHanging.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftHorse.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftHusk.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftIllager.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftIllusioner.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftLargeFireball.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftLeash.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftLightningStrike.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftLingeringPotion.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftMagmaCube.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftMinecart.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartCommand.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartContainer.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartFurnace.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartMobSpawner.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartRideable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartTNT.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftMonster.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftMule.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftMushroomCow.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftOcelot.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftPainting.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftParrot.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftPig.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftPigZombie.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftPolarBear.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftPufferFish.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftRabbit.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftSalmon.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftSheep.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftShulker.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftSilverfish.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftSkeleton.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftSkeletonHorse.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftSlime.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftSmallFireball.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftSnowball.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftSpectralArrow.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftSpellcaster.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftSpider.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftSplashPotion.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftSquid.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftStray.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftTameableAnimal.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftThrownExpBottle.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftTippedArrow.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftTropicalFish.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftVehicle.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftVex.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftVillagerZombie.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftVindicator.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftWaterMob.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftWeather.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftWitch.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftWitherSkeleton.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftWitherSkull.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java create mode 100644 src/main/java/org/bukkit/craftbukkit/entity/CraftZombieHorse.java create mode 100644 src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java create mode 100644 src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java create mode 100644 src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java create mode 100644 src/main/java/org/bukkit/craftbukkit/generator/InternalChunkGenerator.java create mode 100644 src/main/java/org/bukkit/craftbukkit/generator/NetherChunkGenerator.java create mode 100644 src/main/java/org/bukkit/craftbukkit/generator/NormalChunkGenerator.java create mode 100644 src/main/java/org/bukkit/craftbukkit/generator/SkyLandsChunkGenerator.java create mode 100644 src/main/java/org/bukkit/craftbukkit/help/CommandAliasHelpTopic.java create mode 100644 src/main/java/org/bukkit/craftbukkit/help/CustomHelpTopic.java create mode 100644 src/main/java/org/bukkit/craftbukkit/help/CustomIndexHelpTopic.java create mode 100644 src/main/java/org/bukkit/craftbukkit/help/HelpTopicAmendment.java create mode 100644 src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java create mode 100644 src/main/java/org/bukkit/craftbukkit/help/MultipleCommandAliasHelpTopic.java create mode 100644 src/main/java/org/bukkit/craftbukkit/help/MultipleCommandAliasHelpTopicFactory.java create mode 100644 src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftCustomTagTypeRegistry.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftEntityEquipment.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftFurnaceRecipe.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAbstractHorse.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryBeacon.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryBrewer.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCrafting.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryEnchanting.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryFurnace.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryHorse.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryLlama.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryMerchant.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryView.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchant.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantCustom.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantRecipe.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaKnowledgeBook.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftSaddledInventory.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftShapedRecipe.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/InventoryIterator.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/InventoryWrapper.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/RecipeIterator.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/tags/CraftCustomItemTagContainer.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/tags/CraftItemTagAdapterContext.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/util/CraftCustomInventoryConverter.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/util/CraftInventoryCreator.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/util/CraftTileInventoryConverter.java create mode 100644 src/main/java/org/bukkit/craftbukkit/map/CraftMapCanvas.java create mode 100644 src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java create mode 100644 src/main/java/org/bukkit/craftbukkit/map/CraftMapView.java create mode 100644 src/main/java/org/bukkit/craftbukkit/map/RenderData.java create mode 100644 src/main/java/org/bukkit/craftbukkit/metadata/BlockMetadataStore.java create mode 100644 src/main/java/org/bukkit/craftbukkit/metadata/EntityMetadataStore.java create mode 100644 src/main/java/org/bukkit/craftbukkit/metadata/PlayerMetadataStore.java create mode 100644 src/main/java/org/bukkit/craftbukkit/metadata/WorldMetadataStore.java create mode 100644 src/main/java/org/bukkit/craftbukkit/potion/CraftPotionBrewer.java create mode 100644 src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java create mode 100644 src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java create mode 100644 src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java create mode 100644 src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncDebugger.java create mode 100644 src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java create mode 100644 src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java create mode 100644 src/main/java/org/bukkit/craftbukkit/scheduler/CraftFuture.java create mode 100644 src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java create mode 100644 src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java create mode 100644 src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java create mode 100644 src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java create mode 100644 src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java create mode 100644 src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java create mode 100644 src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardComponent.java create mode 100644 src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java create mode 100644 src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java create mode 100644 src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java create mode 100644 src/main/java/org/bukkit/craftbukkit/tag/CraftBlockTag.java create mode 100644 src/main/java/org/bukkit/craftbukkit/tag/CraftItemTag.java create mode 100644 src/main/java/org/bukkit/craftbukkit/tag/CraftTag.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/AsynchronousExecutor.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/Commodore.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/CraftDamageSource.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/CraftEvil.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/CraftIconCache.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/CraftLegacy.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/CraftNBTTagConfigSerializer.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/CraftNamespacedKey.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/CraftRayTraceResult.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/DatFileFilter.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/ForwardLogHandler.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/HashTreeSet.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/LazyHashSet.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/LazyPlayerSet.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/LongHash.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/LongHashSet.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/LongObjectHashMap.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/MojangNameLookup.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/ShortConsoleLogFormatter.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/UnsafeList.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/Versioning.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/Waitable.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/WeakCollection.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java create mode 100644 src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java create mode 100644 src/main/java/org/spigotmc/ActivationRange.java create mode 100644 src/main/java/org/spigotmc/AsyncCatcher.java create mode 100644 src/main/java/org/spigotmc/LimitStream.java create mode 100644 src/main/java/org/spigotmc/Metrics.java create mode 100644 src/main/java/org/spigotmc/RestartCommand.java create mode 100644 src/main/java/org/spigotmc/SlackActivityAccountant.java create mode 100644 src/main/java/org/spigotmc/SpigotCommand.java create mode 100644 src/main/java/org/spigotmc/SpigotConfig.java create mode 100644 src/main/java/org/spigotmc/SpigotWorldConfig.java create mode 100644 src/main/java/org/spigotmc/SupplierUtils.java create mode 100644 src/main/java/org/spigotmc/TickLimiter.java create mode 100644 src/main/java/org/spigotmc/TicksPerSecondCommand.java create mode 100644 src/main/java/org/spigotmc/TrackingRange.java create mode 100644 src/main/java/org/spigotmc/ValidateUtils.java create mode 100644 src/main/java/org/spigotmc/WatchdogThread.java create mode 100644 src/main/resources/configurations/bukkit.yml create mode 100644 src/main/resources/configurations/commands.yml create mode 100644 src/main/resources/configurations/help.yml create mode 100644 src/main/resources/log4j2.component.properties create mode 100644 src/main/resources/log4j2.xml create mode 100644 src/test/java/org/bukkit/ArtTest.java create mode 100644 src/test/java/org/bukkit/BiomeTest.java create mode 100644 src/test/java/org/bukkit/BlockDataConversionTest.java create mode 100644 src/test/java/org/bukkit/BlockDataTest.java create mode 100644 src/test/java/org/bukkit/ChatTest.java create mode 100644 src/test/java/org/bukkit/DyeColorsTest.java create mode 100644 src/test/java/org/bukkit/EnchantmentTest.java create mode 100644 src/test/java/org/bukkit/GameRuleTest.java create mode 100644 src/test/java/org/bukkit/LegacyTest.java create mode 100644 src/test/java/org/bukkit/LootTablesTest.java create mode 100644 src/test/java/org/bukkit/MaterialTest.java create mode 100644 src/test/java/org/bukkit/NibbleArrayTest.java create mode 100644 src/test/java/org/bukkit/ParticleTest.java create mode 100644 src/test/java/org/bukkit/PerMaterialTest.java create mode 100644 src/test/java/org/bukkit/SoundTest.java create mode 100644 src/test/java/org/bukkit/StatisticsAndAchievementsTest.java create mode 100644 src/test/java/org/bukkit/StructureTypeTest.java create mode 100644 src/test/java/org/bukkit/WorldTypeTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/generator/ChunkDataTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/CompositeSerialization.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/FactoryItemMaterialTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/ItemFactoryTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaCloneTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaCustomValueTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaImplementationOverrideTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/ItemStackBookTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/ItemStackEnchantStorageTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/ItemStackFireworkChargeTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/ItemStackFireworkTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/ItemStackLeatherTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/ItemStackLoreEnchantmentTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/ItemStackMapTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/ItemStackPotionsTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/ItemStackSkullTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/ItemStackTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/NMSCraftItemStackTest.java create mode 100644 src/test/java/org/bukkit/craftbukkit/inventory/PlayerInventoryTest.java create mode 100644 src/test/java/org/bukkit/entity/EnderDragonPhaseTest.java create mode 100644 src/test/java/org/bukkit/entity/EntityTypesTest.java create mode 100644 src/test/java/org/bukkit/entity/TropicalFishTest.java create mode 100644 src/test/java/org/bukkit/map/MapTest.java create mode 100644 src/test/java/org/bukkit/potion/PotionTest.java create mode 100644 src/test/java/org/bukkit/support/AbstractTestingBase.java create mode 100644 src/test/java/org/bukkit/support/DummyEnchantments.java create mode 100644 src/test/java/org/bukkit/support/DummyServer.java create mode 100644 src/test/java/org/bukkit/support/Matchers.java create mode 100644 src/test/java/org/bukkit/support/Util.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000000..67fb370cad69 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +# Eclipse stuff +/.classpath +/.project +/.settings + +# netbeans +/nbproject +nb*.xml + +# we use maven! +/build.xml + +# maven +/target +dependency-reduced-pom.xml + +# vim +.*.sw[a-p] + +# various other potential build files +/build +/bin +/dist +/manifest.mf + +/world +/logs + +# Mac filesystem dust +.DS_Store + +# intellij +*.iml +*.ipr +*.iws +.idea/ + +/src/main/resources/achievement +/src/main/resources/lang diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000000..4e3513bc40cd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,51 @@ +Creating a Pull Request +----------------------- +##### Pull Request Title Example +The first line in a Pull Request(PR) message is an imperative statement briefly explaining what the PR is achieving. +If the PR fixes a bug, or implements a new feature as requested from the [JIRA](http://hub.spigotmc.org/jira/), then it should reference that ticket. +This is accomplished by simply type SPIGOT-####, where #### is the ticket number. (i.e. SPIGOT-3510). +You can reference multiple tickets in a single commit message; for example: "SPIGOT-1, SPIGOT-2" without the quotes. + +__For Example:__ +* SPIGOT-3510: Velocity broken for certain entities +* MC-111753, SPIGOT-2971: Brewing stand not reloading + +As you can see, Minecraft tickets can be referenced by including the appropriate ticket number (i.e. MC-111753) + +##### Pull Request Message Expectations +The body of a PR needs to describe how the ticket was resolved, or if there was no ticket, describe the problem itself. +If a PR is for both Bukkit and CraftBukkit it should include a link to the appropriate PR. [Read this to learn how to link to a PR.](https://confluence.atlassian.com/bitbucketserver053/markdown-syntax-guide-938022413.html?utm_campaign=in-app-help&utm_medium=in-app-help&utm_source=stash#Markdownsyntaxguide-Linkingtopullrequests) + +##### Submitting the Changes +* Push your changes to a topic branch in your fork of the repository. +* Submit a pull request to the relevant repository in the Spigot organization. + * Make sure your pull request meets our [code requirements.](README.md) +* If you are fixing a JIRA ticket, be sure to update the ticket with a link to the pull request. +* Ensure all changes (including in patches) have our Minimal Diff Policy in mind. + +##### Pull Request Feedback +Spigot has a lot of Pull Requests open. Some of these are old and no longer maintained. As a general rule, we do not delete PRs because they are old or abandoned. + +You will eventually receive feedback on your Pull Requests. If the criticism is rough, do not take it personally. We have all started out, or just made bad PRs. +Take the advice and use it to make yourself better. If you think someone is being malicious contact an [admin](https://www.spigotmc.org/XenStaff/#Administrator). + +Feedback goes both ways. If you believe a feature should be present then be prepared to argue your case. +Per our Code Requirements, formatting issues are almost always required to be fixed before a PR is accepted. + +Although we have many discussions on Stash, there are also many discussions in the #spigot-dev channel of IRC. +Join us there if you plan on contributing to Spigot! + +##### Tips to Get Your Pull Request Accepted +Making sure to follow the conventions! + +* Your change should fit with Bukkit's goals. +* Make sure you follow our conventions to the letter. +* Check for formatting errors. They may be invisible, but [we notice.](https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/pull-requests/298/diff) +* Provide proper JavaDocs where appropriate. + * JavaDocs should detail every limitation, caveat, and gotcha the code has. +* Provide proper accompanying documentation where appropriate. +* Test your code and provide adequate testing material and/or proof. + * For example: adding an event? Test it with a plugin and provide us with the source. +* Make sure you follow our conventions to the letter. + +__Note:__ The project is often on a code freeze leading up to the release of a Minecraft update in order to give the team a static code base to work with. diff --git a/LGPL.txt b/LGPL.txt new file mode 100644 index 000000000000..65c5ca88a67c --- /dev/null +++ b/LGPL.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/LICENCE.txt b/LICENCE.txt new file mode 100644 index 000000000000..94a9ed024d38 --- /dev/null +++ b/LICENCE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 000000000000..283f37814484 --- /dev/null +++ b/README.md @@ -0,0 +1,284 @@ +CraftBukkit +====== +An implementation of the [Bukkit](https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit) plugin API for [Minecraft](https://minecraft.net/) servers, currently maintained by [SpigotMC](http://www.spigotmc.org/). + +#### Index + +* [Bug Reporting](#bug-reporting) +* [Compilation](#compilation) +* [Contributing](#contributing) +* [Code Requirements](#code-requirements) + * [Applying Patches](#applying-patches) + * [Making Changes to Minecraft](#making-changes-to-minecraft) + * [Minimal Diff Policy](#minimal-diff-policy) + * [CraftBukkit Comments](#craftbukkit-comments) +* [Creating Pull Requests](#creating-pull-requests) +* [Useful Resources](#useful-resources) + +Bug Reporting +------------- +The development team is very open to both bug and feature requests / suggestions. You can submit these on the [JIRA Issue Tracker](http://hub.spigotmc.org/jira/). + +Compilation +----------- +CraftBukkit is a Java program which uses [Maven 3](http://maven.apache.org/) for compilation. To compile fresh from Git, simply perform the following steps: + +* Install Git using your preferred installation methods. +* Download and run [BuildTools](https://www.spigotmc.org/wiki/buildtools/) + +Some IDEs such as [NetBeans](https://netbeans.org/) can perform these steps for you. Any Maven capable Java IDE can be used to develop with CraftBukkit, however the current team's personal preference is to use NetBeans. + +Contributing +------------ +Contributions of all sorts are welcome. To manage community contributions, we use the pull request functionality of Stash. In to gain access to Stash and create a pull request, you will first need to perform the following steps: + +* Create an account on [JIRA](http://hub.spigotmc.org/jira/). +* Fill in the [SpigotMC CLA](http://www.spigotmc.org/go/cla) and wait up to 24 hours for your Stash account to be activated. Please ensure that your username and email addresses match. +* Log into Stash using your JIRA credentials. + +Once you have performed these steps you can create a fork, push your code changes, and then submit it for review. + +If you submit a PR involving both Bukkit and CraftBukkit, each PR should link the other. + +Although the minimum requirement for compilation & usage is Java 8, we prefer all contributions to be written in Java 7 style code unless there is a compelling reason otherwise. + +Code Requirements +----------------- +* For the most part, CraftBukkit and Bukkit use the [Sun/Oracle coding standards](http://www.oracle.com/technetwork/java/javase/documentation/codeconvtoc-136057.html). +* No tabs; use 4 spaces instead. + * Empty lines should contain no spaces. +* No trailing whitespaces. +* No 80 character column limit, or 'weird' mid-statement newlines unless absolutely necessary. + * The 80 character column limit still applies to documentation. +* No one-line methods. +* All major additions should have documentation. +* Try to follow test driven development where available. +* All code should be free of magic values. If this is not possible, it should be marked with a TODO comment indicating it should be addressed in the future. + * If magic values are absolutely necessary for your change, what those values represent should be documented in the code as well as an explanation in the Pull Request description on why those values are necessary. +* No unnecessary code changes. Look through all your changes before you submit it. +* Do not attempt to fix multiple problems with a single patch or pull request. +* Do not submit your personal changes to configuration files. +* Avoid moving or renaming classes. + +Bukkit/CraftBukkit employs [JUnit 4](http://www.vogella.com/articles/JUnit/article.html) for testing. Pull Requests(PR) should attempt to integrate within that framework as appropriate. +Bukkit is a large project and what seems simple to a PR author at the time of writing may easily be overlooked by other authors and updates. Including unit tests with your PR +will help to ensure the PR can be easily maintained over time and encourage the Spigot team to pull the PR. + +* There needs to be a new line at the end of every file. +* Imports should be organised in a logical manner. + * Do not group packages unless already grouped. + * All new imports should be within existing CraftBukkit comments if any are present. If not, make them. + * __Absolutely no wildcard imports.__ + * If you only use an import once, don't import it. Use the fully qualified name. + +Any questions about these requirements can be asked in #spigot-dev in IRC. + +Applying Patches +---------------- +Any time new patches are created and/or updated in CraftBukkit, you need to apply them to your development environment. + +1. Pull changes from CraftBukkit repo. +2. Run the `applyPatches.sh` script in the CraftBukkit directory. + - This script requires a decompile directory from BuildTools. (e.g. /work/decompile-XXXXXX) +3. Your development environment is now up to date with the latest CraftBukkit patches! + +Making Changes to Minecraft +--------------------------- +Importing new NMS classes to CraftBukkit is actually very simple. + +1. Find the `work/decompile-XXXXXX` folder in your BuildTools folder. +2. Find the class you want to add in the `net/minecraft/server` folder and copy it. +3. Copy the selected file to the `src/main/java/net/minecraft/server` folder in CraftBukkit. +4. Implement changes. +5. Run `makePatches.sh` to create a new patch for the new class. + * _Be sure that Git recognizes the new file. This might require manually adding the file._ +6. Commit new patch. + +Done! You have added a new patch for CraftBukkit! + +**Making Changes to NMS Classes** + +Bukkit/CB employs a Minimal Diff policy to help guide when changes should be changed to Minecraft and what those changes should be. +This is to ensure that any changes have the smallest impact possible on the update process whenever a new Minecraft version is released. +As well as the Minimal Diff Policy, *every* change made to a Minecraft class must be marked with the appropriate CraftBukkit comment. +At no point should you rename an existing/obfuscated field or method. All references to existing/obfusacted fields/methods should be marked with the `// PAIL rename` comment. +Mapping of new fields/methods are done when there are enough changes to warrant the work. (Any questions can be asked in #spigot-dev in [IRC](https://www.spigotmc.org/wiki/irc-guide/)) + +__*Key Points*__: +* All additions to patches must be accompanied by an appropriate comment. +* To avoid large patches, avoid adding methods where possible. We prefer making fields public over adding methods in patches. + * If you *have* to add a method to a patch, please explain why in the Pull Request description. +* __Never__ rename an existing field or method. If you want something renamed, include a ```PAIL rename``` comment +* Converting a method/class from one access level to another (i.e. private to public) is fine as long as that method is not overridden in subclasses. + * If a method is overridden in its' subclasses, create a new method that calls that method instead (along with appropriate CraftBukkit comments). +* If you can use a field to accomplish something, use that over creating a new method. + +Minimal Diff Policy +------------------- + +The Minimal Diff Policy is key to any changes made within Minecraft classes. When people think of the phrase "minimal diffs", they often take it +to the extreme - they go completely out of their way to abstract the changes they are trying to make away from editing Minecraft's classes as much as possible. +However, this is not what is meant by "minimal diffs". Instead, when trying to understand this policy, it helps to keep in mind its goal: to reduce the impact of changes we make +to Minecraft's internals have on our update process. + +To put it simply, the Minimal Diffs Policy simply means to make the smallest change in a Minecraft class possible without duplicating logic. + +Here are a few tips you should keep in mind, or common areas you should focus on: + +* Try to avoid duplicating logic or code when making changes. +* Try to keep your changes easily discernible - don't nest or group several unrelated changes together. + * All changes must be surrounded by [CraftBukkit comments](#craftbukkit-comments). +* If you only use an import once within a class, don't import it and use a fully qualified name instead. +* Try to employ "short-circuiting" of logic if at all possible. This means you should force a conditional to be the value needed to side step the code block to achieve your desired effect. + +__For example, to short circuit this:__ +```java +if (!this.world.isClientSide && !this.isDead && (d0*d0) + d1 + (d2*d2) > 0.0D) { + this.die(); + this.h(); +} +``` +__You would do this:__ +```java +if (false && !this.world.isClientSide && !this.isDead && (d0*d0) + d1 + (d2*d2) > 0.0D) { + this.die(); + this.h(); +} +``` + +* When adding a validation check, this applies everywhere not just in Minecraft classes, see if the Validate package has a better, more concise method you can use instead. + * The Preconditions package works just as well. Either are acceptable; though these are, by no means, the only accepted validation strategies. + +__For example, you should use:__ +```java +Validate.notNull(sender, "Sender cannot be null"); +``` +__Instead of:__ +```java +if (sender == null) { + throw new IllegalArgumentException("Sender cannot be null"); +} +``` + +* When the change you are trying to make involves removing code, or delegating it somewhere else, instead of removing it, you should comment it out. + +__For example:__ +```java +// CraftBukkit start - special case dropping so we can get info from the tile entity +public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + if (world.random.nextFloat() < f) { + ItemStack itemstack = new ItemStack(Item.SKULL, 1, this.getDropData(world, i, j, k)); + TileEntitySkull tileentityskull = (TileEntitySkull) world.getTileEntity(i, j, k); + + if (tileentityskull.getSkullType() == 3 && tileentityskull.getExtraType() != null && tileentityskull.getExtraType().length() > 0) { + itemstack.setTag(new NBTTagCompound()); + itemstack.getTag().setString("SkullOwner", tileentityskull.getExtraType()); + } + + this.b(world, i, j, k, itemstack); + } +} +// CraftBukkit end + +public void remove(World world, int i, int j, int k, int l, int i1) { + if (!world.isStatic) { + /* CraftBukkit start - drop item in code above, not here + if ((i1 & 8) == 0) { + ItemStack itemstack = new ItemStack(Item.SKULL, 1, this.getDropData(world, i, j, k)); + TileEntitySkull tileentityskull = (TileEntitySkull) world.getTileEntity(i, j, k); + + if (tileentityskull.getSkullType() == 3 && tileentityskull.getExtraType() != null && tileentityskull.getExtraType().length() > 0) { + itemstack.setTag(new NBTTagCompound()); + itemstack.getTag().setString("SkullOwner", tileentityskull.getExtraType()); + } + + this.b(world, i, j, k, itemstack); + } + // CraftBukkit end */ + + super.remove(world, i, j, k, l, i1); + } +} +``` + +#### CraftBukkit Comments + +Changes to a Minecraft class should be clearly marked using CraftBukkit comments. + +* All CraftBukkit comments should be capitalised appropriately. (i.e. CraftBukkit, not CB/craftBukkit, etc.) +* If the change only affects one line of code, use an end of line CraftBukkit comment + +__Examples:__ + +If the change is obvious, then you need a simple end of line comment. +```java +if (true || minecraftserver.getAllowNether()) { // CraftBukkit +``` + +Every reference to an obfuscated field/method in NMS should be marked with: +```java +// PAIL rename newName +``` +All PAIL rename comments must include a new name. + +If, however, the change is something important to note or difficult to discern, you should include a reason at the end of the comment +```java +public int fireTicks; // PAIL private -> public +``` +Changing access levels must include a PAIL comment indicating the previous access level and the new access level. + +If adding the CraftBukkit comment negatively affects the readability of the code, then you should place the comment on a new line *above* the change you made. +```java +// CraftBukkit +if (!isEffect && !world.isStatic && world.difficulty >= 2 && world.areChunksLoaded(MathHelper.floor(d0), MathHelper.floor(d1), MathHelper.floor(d2), 10)) { +``` + +* If the change affects more than one line, you should use a multi-line CraftBukkit comment. + +__Example:__ + +The majority of the time, multi-line changes should be accompanied by a reason since they're usually much more complicated than a single line change. +*If the change is something important to note or difficult to discern, you should include a reason at the end of line CraftBukkit comment.* +```java +// CraftBukkit start - special case dropping so we can get info from the tile entity +public void dropNaturally(World world, int i, int j, int k, int l, float f, int i1) { + if (world.random.nextFloat() < f) { + ItemStack itemstack = new ItemStack(Item.SKULL, 1, this.getDropData(world, i, j, k)); + TileEntitySkull tileentityskull = (TileEntitySkull) world.getTileEntity(i, j, k); + + if (tileentityskull.getSkullType() == 3 && tileentityskull.getExtraType() != null && tileentityskull.getExtraType().length() > 0) { + itemstack.setTag(new NBTTagCompound()); + itemstack.getTag().setString("SkullOwner", tileentityskull.getExtraType()); + } + + this.b(world, i, j, k, itemstack); + } +} +// CraftBukkit end +``` +Otherwise, if the change is obvious, such as firing an event, then you can simply use a multi-line comment. +```java + // CraftBukkit start + BlockIgniteEvent event = new BlockIgniteEvent(this.cworld.getBlockAt(i, j, k), BlockIgniteEvent.IgniteCause.LIGHTNING, null); + world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + world.setTypeIdUpdate(i, j, k, Block.FIRE); + } + // CraftBukkit end +``` +* All CraftBukkit comments should be on the same indentation level the code block it is in. + +__Imports in Minecraft Classes__ +* Do not remove unused imports if they are not marked by CraftBukkit comments. + +Creating Pull Requests +---------------------- +To learn what Spigot expects of a Pull Request please view the [Contributing guidelines](CONTRIBUTING.md) + +Useful Resources +---------------- + +* [An example pull request demonstrating the things we look out for](https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/pull-requests/365/overview) +* [JIRA, our bug tracker](http://hub.spigotmc.org/jira/) +* [Join us on IRC - #spigot-dev @ irc.spi.gt](https://www.spigotmc.org/wiki/irc-guide/) diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000000..a8ee1c097705 --- /dev/null +++ b/pom.xml @@ -0,0 +1,322 @@ + + 4.0.0 + paper + jar + 1.13.2-R0.1-SNAPSHOT + Paper + https://papermc.io + + + + UTF-8 + unknown + 1.13.2 + 1_13_R2 + git-Bukkit- + + yyyyMMdd-HHmm + 1.8 + 1.8 + + + + com.destroystokyo.paper + paper-parent + dev-SNAPSHOT + ../pom.xml + + + + + com.destroystokyo.paper + paper-api + ${project.version} + compile + + + org.spigotmc + minecraft-server + ${minecraft.version}-SNAPSHOT + compile + + + net.minecrell + terminalconsoleappender + 1.1.1 + + + net.java.dev.jna + jna + 4.5.2 + runtime + + + + org.apache.logging.log4j + log4j-core + 2.8.1 + compile + + + org.apache.logging.log4j + log4j-slf4j-impl + 2.8.1 + runtime + + + org.apache.logging.log4j + log4j-iostreams + 2.8.1 + + + + com.lmax + disruptor + 3.4.2 + runtime + + + org.ow2.asm + asm + 7.0 + compile + + + org.xerial + sqlite-jdbc + 3.25.2 + runtime + + + mysql + mysql-connector-java + 5.1.47 + runtime + + + + junit + junit + 4.12 + test + + + org.hamcrest + hamcrest-library + 1.3 + test + + + + + + + spigotmc-public + https://hub.spigotmc.org/nexus/content/groups/public/ + + + + + + spigotmc-public + https://hub.spigotmc.org/nexus/content/groups/public/ + + + + + + paper-${minecraft.version} + clean install + + + com.lukegb.mojo + gitdescribe-maven-plugin + 1.3 + + git-Paper- + .. + + + + compile + + gitdescribe + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + true + + + org.bukkit.craftbukkit.Main + CraftBukkit + + ${describe} + ${maven.build.timestamp} + Bukkit + ${api.version} + Bukkit Team + + + + net/bukkit/ + + true + + + + com/bukkit/ + + true + + + + org/bukkit/ + + true + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.1.1 + + + package + + shade + + + ${project.build.directory}/dependency-reduced-pom.xml + ${shadeSourcesJar} + + + + + + + + jline + org.bukkit.craftbukkit.libs.jline + + + + + + + + org.bukkit.craftbukkit + org.bukkit.craftbukkit.v${minecraft_version} + + org.bukkit.craftbukkit.Main* + + + + net.minecraft.server + net.minecraft.server.v${minecraft_version} + + + + + META-INF/services/java.sql.Driver + + + + + + + + + com.github.edwgiz + maven-shade-plugin.log4j2-cachefile-transformer + 2.8.1 + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + + + org.codehaus.plexus + plexus-compiler-eclipse + 2.8.5-spigotmc + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12.4 + + ${basedir}/target/test-server + + org/bukkit/craftbukkit/inventory/ItemStack*Test.java + + + + + + + + + shadeSourcesJar + + true + true + + + + development + + false + + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + 1.17 + + + process-classes + + check + + + + + + org.codehaus.mojo.signature + java18 + 1.0 + + + + + + + + diff --git a/src/main/java/co/aikar/timings/MinecraftTimings.java b/src/main/java/co/aikar/timings/MinecraftTimings.java new file mode 100644 index 000000000000..66d02e048b35 --- /dev/null +++ b/src/main/java/co/aikar/timings/MinecraftTimings.java @@ -0,0 +1,132 @@ +package co.aikar.timings; + +import com.google.common.collect.MapMaker; +import net.minecraft.server.*; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitTask; + +import org.bukkit.craftbukkit.scheduler.CraftTask; + +import java.util.Map; + +public final class MinecraftTimings { + + public static final Timing playerListTimer = Timings.ofSafe("Player List"); + public static final Timing commandFunctionsTimer = Timings.ofSafe("Command Functions"); + public static final Timing connectionTimer = Timings.ofSafe("Connection Handler"); + public static final Timing tickablesTimer = Timings.ofSafe("Tickables"); + public static final Timing minecraftSchedulerTimer = Timings.ofSafe("Minecraft Scheduler"); + public static final Timing bukkitSchedulerTimer = Timings.ofSafe("Bukkit Scheduler"); + public static final Timing bukkitSchedulerPendingTimer = Timings.ofSafe("Bukkit Scheduler - Pending"); + public static final Timing bukkitSchedulerFinishTimer = Timings.ofSafe("Bukkit Scheduler - Finishing"); + public static final Timing chunkIOTickTimer = Timings.ofSafe("ChunkIOTick"); + public static final Timing timeUpdateTimer = Timings.ofSafe("Time Update"); + public static final Timing serverCommandTimer = Timings.ofSafe("Server Command"); + public static final Timing savePlayers = Timings.ofSafe("Save Players"); + + public static final Timing tickEntityTimer = Timings.ofSafe("## tickEntity"); + public static final Timing tickTileEntityTimer = Timings.ofSafe("## tickTileEntity"); + public static final Timing packetProcessTimer = Timings.ofSafe("## Packet Processing"); + public static final Timing scheduledBlocksTimer = Timings.ofSafe("## Scheduled Blocks"); + public static final Timing structureGenerationTimer = Timings.ofSafe("Structure Generation"); + + public static final Timing processQueueTimer = Timings.ofSafe("processQueue"); + + public static final Timing playerCommandTimer = Timings.ofSafe("playerCommand"); + + public static final Timing entityActivationCheckTimer = Timings.ofSafe("entityActivationCheck"); + + public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update"); + public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate"); + + private static final Map, String> taskNameCache = new MapMaker().weakKeys().makeMap(); + + private MinecraftTimings() {} + + /** + * Gets a timer associated with a plugins tasks. + * @param bukkitTask + * @param period + * @return + */ + public static Timing getPluginTaskTimings(BukkitTask bukkitTask, long period) { + if (!bukkitTask.isSync()) { + return null; + } + Plugin plugin; + + CraftTask craftTask = (CraftTask) bukkitTask; + + final Class taskClass = craftTask.getTaskClass(); + if (bukkitTask.getOwner() != null) { + plugin = bukkitTask.getOwner(); + } else { + plugin = TimingsManager.getPluginByClassloader(taskClass); + } + + final String taskname = taskNameCache.computeIfAbsent(taskClass, clazz -> + clazz.isAnonymousClass() || clazz.isLocalClass() + ? clazz.getName() + : clazz.getCanonicalName()); + + StringBuilder name = new StringBuilder(64); + name.append("Task: ").append(taskname); + if (period > 0) { + name.append(" (interval:").append(period).append(")"); + } else { + name.append(" (Single)"); + } + + if (plugin == null) { + return Timings.ofSafe(null, name.toString()); + } + + return Timings.ofSafe(plugin, name.toString()); + } + + /** + * Get a named timer for the specified entity type to track type specific timings. + * @param entity + * @return + */ + public static Timing getEntityTimings(Entity entity) { + String entityType = entity.getClass().getName(); + return Timings.ofSafe("Minecraft", "## tickEntity - " + entityType, tickEntityTimer); + } + + /** + * Get a named timer for the specified tile entity type to track type specific timings. + * @param entity + * @return + */ + public static Timing getTileEntityTimings(TileEntity entity) { + String entityType = entity.getClass().getName(); + return Timings.ofSafe("Minecraft", "## tickTileEntity - " + entityType, tickTileEntityTimer); + } + public static Timing getCancelTasksTimer() { + return Timings.ofSafe("Cancel Tasks"); + } + public static Timing getCancelTasksTimer(Plugin plugin) { + return Timings.ofSafe(plugin, "Cancel Tasks"); + } + + public static void stopServer() { + TimingsManager.stopServer(); + } + + public static Timing getBlockTiming(Block block) { + return Timings.ofSafe("## Scheduled Block: " + block.toString(), scheduledBlocksTimer); + } +/* + public static Timing getStructureTiming(StructureGenerator structureGenerator) { + return Timings.ofSafe("Structure Generator - " + structureGenerator.getName(), structureGenerationTimer); + }*/ + + public static Timing getPacketTiming(Packet packet) { + return Timings.ofSafe("## Packet - " + packet.getClass().getSimpleName(), packetProcessTimer); + } + + public static Timing getCommandFunctionTiming(CustomFunction function) { + return Timings.ofSafe("Command Function - " + function.getMinecraftKey().toString()); + } +} diff --git a/src/main/java/co/aikar/timings/WorldTimingsHandler.java b/src/main/java/co/aikar/timings/WorldTimingsHandler.java new file mode 100644 index 000000000000..eff9dcf54feb --- /dev/null +++ b/src/main/java/co/aikar/timings/WorldTimingsHandler.java @@ -0,0 +1,108 @@ +package co.aikar.timings; + +import net.minecraft.server.World; +import net.minecraft.server.WorldServer; + +/** + * Set of timers per world, to track world specific timings. + */ +public class WorldTimingsHandler { + public final Timing mobSpawn; + public final Timing doChunkUnload; + public final Timing doPortalForcer; + public final Timing scheduledBlocks; + public final Timing scheduledBlocksCleanup; + public final Timing scheduledBlocksTicking; + public final Timing chunkTicks; + public final Timing lightChunk; + public final Timing chunkTicksBlocks; + public final Timing doVillages; + public final Timing doChunkMap; + public final Timing doChunkMapUpdate; + public final Timing doChunkMapToUpdate; + public final Timing doChunkMapSortMissing; + public final Timing doChunkMapSortSendToPlayers; + public final Timing doChunkMapPlayersNeedingChunks; + public final Timing doChunkMapPendingSendToPlayers; + public final Timing doChunkMapUnloadChunks; + public final Timing doChunkGC; + public final Timing doSounds; + public final Timing entityRemoval; + public final Timing entityTick; + public final Timing tileEntityTick; + public final Timing tileEntityPending; + public final Timing tracker1; + public final Timing tracker2; + public final Timing doTick; + public final Timing tickEntities; + + public final Timing syncChunkLoadTimer; + public final Timing syncChunkLoadDataTimer; + public final Timing syncChunkLoadStructuresTimer; + public final Timing syncChunkLoadPostTimer; + public final Timing syncChunkLoadPopulateTimer; + public final Timing chunkLoadLevelTimer; + public final Timing chunkGeneration; + public final Timing chunkIOStage1; + public final Timing chunkIOStage2; + public final Timing worldSave; + public final Timing worldSaveChunks; + public final Timing worldSaveLevel; + public final Timing chunkSaveData; + + public final Timing lightingQueueTimer; + + public WorldTimingsHandler(World server) { + String name = server.worldData.getName() +" - "; + + mobSpawn = Timings.ofSafe(name + "mobSpawn"); + doChunkUnload = Timings.ofSafe(name + "doChunkUnload"); + scheduledBlocks = Timings.ofSafe(name + "Scheduled Blocks"); + scheduledBlocksCleanup = Timings.ofSafe(name + "Scheduled Blocks - Cleanup"); + scheduledBlocksTicking = Timings.ofSafe(name + "Scheduled Blocks - Ticking"); + chunkTicks = Timings.ofSafe(name + "Chunk Ticks"); + lightChunk = Timings.ofSafe(name + "Light Chunk"); + chunkTicksBlocks = Timings.ofSafe(name + "Chunk Ticks - Blocks"); + doVillages = Timings.ofSafe(name + "doVillages"); + doChunkMap = Timings.ofSafe(name + "doChunkMap"); + doChunkMapUpdate = Timings.ofSafe(name + "doChunkMap - Update"); + doChunkMapToUpdate = Timings.ofSafe(name + "doChunkMap - To Update"); + doChunkMapSortMissing = Timings.ofSafe(name + "doChunkMap - Sort Missing"); + doChunkMapSortSendToPlayers = Timings.ofSafe(name + "doChunkMap - Sort Send To Players"); + doChunkMapPlayersNeedingChunks = Timings.ofSafe(name + "doChunkMap - Players Needing Chunks"); + doChunkMapPendingSendToPlayers = Timings.ofSafe(name + "doChunkMap - Pending Send To Players"); + doChunkMapUnloadChunks = Timings.ofSafe(name + "doChunkMap - Unload Chunks"); + doSounds = Timings.ofSafe(name + "doSounds"); + doChunkGC = Timings.ofSafe(name + "doChunkGC"); + doPortalForcer = Timings.ofSafe(name + "doPortalForcer"); + entityTick = Timings.ofSafe(name + "entityTick"); + entityRemoval = Timings.ofSafe(name + "entityRemoval"); + tileEntityTick = Timings.ofSafe(name + "tileEntityTick"); + tileEntityPending = Timings.ofSafe(name + "tileEntityPending"); + + syncChunkLoadTimer = Timings.ofSafe(name + "syncChunkLoad"); + syncChunkLoadDataTimer = Timings.ofSafe(name + "syncChunkLoad - Data"); + syncChunkLoadStructuresTimer = Timings.ofSafe(name + "chunkLoad - recreateStructures"); + syncChunkLoadPostTimer = Timings.ofSafe(name + "chunkLoad - Post"); + syncChunkLoadPopulateTimer = Timings.ofSafe(name + "chunkLoad - Populate"); + chunkLoadLevelTimer = Timings.ofSafe(name + "chunkLoad - Load Level"); + chunkGeneration = Timings.ofSafe(name + "chunkGeneration"); + chunkIOStage1 = Timings.ofSafe(name + "ChunkIO Stage 1 - DiskIO"); + chunkIOStage2 = Timings.ofSafe(name + "ChunkIO Stage 2 - Post Load"); + worldSave = Timings.ofSafe(name + "World Save"); + worldSaveLevel = Timings.ofSafe(name + "World Save - Level"); + worldSaveChunks = Timings.ofSafe(name + "World Save - Chunks"); + chunkSaveData = Timings.ofSafe(name + "Chunk Save - Data"); + + tracker1 = Timings.ofSafe(name + "tracker stage 1"); + tracker2 = Timings.ofSafe(name + "tracker stage 2"); + doTick = Timings.ofSafe(name + "doTick"); + tickEntities = Timings.ofSafe(name + "tickEntities"); + + lightingQueueTimer = Timings.ofSafe(name + "Lighting Queue"); + } + + public static Timing getTickList(WorldServer worldserver, String timingsType) { + return Timings.ofSafe(worldserver.getWorldData().getName() + " - Scheduled " + timingsType); + } +} diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java new file mode 100644 index 000000000000..e257d6b36e0e --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/Metrics.java @@ -0,0 +1,627 @@ +package com.destroystokyo.paper; + +import net.minecraft.server.MinecraftServer; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import javax.net.ssl.HttpsURLConnection; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.*; +import java.util.concurrent.Callable; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.GZIPOutputStream; + +/** + * bStats collects some data for plugin authors. + * + * Check out https://bStats.org/ to learn more about bStats! + */ +public class Metrics { + + // The version of this bStats class + public static final int B_STATS_VERSION = 1; + + // The url to which the data is sent + private static final String URL = "https://bStats.org/submitData/server-implementation"; + + // Should failed requests be logged? + private static boolean logFailedRequests = false; + + // The logger for the failed requests + private static Logger logger = Logger.getLogger("bStats"); + + // The name of the server software + private final String name; + + // The uuid of the server + private final String serverUUID; + + // A list with all custom charts + private final List charts = new ArrayList<>(); + + /** + * Class constructor. + * + * @param name The name of the server software. + * @param serverUUID The uuid of the server. + * @param logFailedRequests Whether failed requests should be logged or not. + * @param logger The logger for the failed requests. + */ + public Metrics(String name, String serverUUID, boolean logFailedRequests, Logger logger) { + this.name = name; + this.serverUUID = serverUUID; + Metrics.logFailedRequests = logFailedRequests; + Metrics.logger = logger; + + // Start submitting the data + startSubmitting(); + } + + /** + * Adds a custom chart. + * + * @param chart The chart to add. + */ + public void addCustomChart(CustomChart chart) { + if (chart == null) { + throw new IllegalArgumentException("Chart cannot be null!"); + } + charts.add(chart); + } + + /** + * Starts the Scheduler which submits our data every 30 minutes. + */ + private void startSubmitting() { + final Timer timer = new Timer(true); + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + submitData(); + } + }, 1000 * 60 * 5, 1000 * 60 * 30); + // Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start + // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted! + // WARNING: Just don't do it! + } + + /** + * Gets the plugin specific data. + * + * @return The plugin specific data. + */ + private JSONObject getPluginData() { + JSONObject data = new JSONObject(); + + data.put("pluginName", name); // Append the name of the server software + JSONArray customCharts = new JSONArray(); + for (CustomChart customChart : charts) { + // Add the data of the custom charts + JSONObject chart = customChart.getRequestJsonObject(); + if (chart == null) { // If the chart is null, we skip it + continue; + } + customCharts.add(chart); + } + data.put("customCharts", customCharts); + + return data; + } + + /** + * Gets the server specific data. + * + * @return The server specific data. + */ + private JSONObject getServerData() { + // OS specific data + String osName = System.getProperty("os.name"); + String osArch = System.getProperty("os.arch"); + String osVersion = System.getProperty("os.version"); + int coreCount = Runtime.getRuntime().availableProcessors(); + + JSONObject data = new JSONObject(); + + data.put("serverUUID", serverUUID); + + data.put("osName", osName); + data.put("osArch", osArch); + data.put("osVersion", osVersion); + data.put("coreCount", coreCount); + + return data; + } + + /** + * Collects the data and sends it afterwards. + */ + private void submitData() { + final JSONObject data = getServerData(); + + JSONArray pluginData = new JSONArray(); + pluginData.add(getPluginData()); + data.put("plugins", pluginData); + + try { + // We are still in the Thread of the timer, so nothing get blocked :) + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logFailedRequests) { + logger.log(Level.WARNING, "Could not submit stats of " + name, e); + } + } + } + + /** + * Sends the data to the bStats server. + * + * @param data The data to send. + * @throws Exception If the request failed. + */ + private static void sendData(JSONObject data) throws Exception { + if (data == null) { + throw new IllegalArgumentException("Data cannot be null!"); + } + HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); + + // Compress the data to save bandwidth + byte[] compressedData = compress(data.toString()); + + // Add headers + connection.setRequestMethod("POST"); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request + connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); + connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format + connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); + + // Send data + connection.setDoOutput(true); + DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); + outputStream.write(compressedData); + outputStream.flush(); + outputStream.close(); + + connection.getInputStream().close(); // We don't care about the response - Just send our data :) + } + + /** + * Gzips the given String. + * + * @param str The string to gzip. + * @return The gzipped String. + * @throws IOException If the compression failed. + */ + private static byte[] compress(final String str) throws IOException { + if (str == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + GZIPOutputStream gzip = new GZIPOutputStream(outputStream); + gzip.write(str.getBytes("UTF-8")); + gzip.close(); + return outputStream.toByteArray(); + } + + /** + * Represents a custom chart. + */ + public static abstract class CustomChart { + + // The id of the chart + final String chartId; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + */ + CustomChart(String chartId) { + if (chartId == null || chartId.isEmpty()) { + throw new IllegalArgumentException("ChartId cannot be null or empty!"); + } + this.chartId = chartId; + } + + private JSONObject getRequestJsonObject() { + JSONObject chart = new JSONObject(); + chart.put("chartId", chartId); + try { + JSONObject data = getChartData(); + if (data == null) { + // If the data is null we don't send the chart. + return null; + } + chart.put("data", data); + } catch (Throwable t) { + if (logFailedRequests) { + logger.log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t); + } + return null; + } + return chart; + } + + protected abstract JSONObject getChartData() throws Exception; + + } + + /** + * Represents a custom simple pie. + */ + public static class SimplePie extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimplePie(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + data.put("value", value); + return data; + } + } + + /** + * Represents a custom advanced pie. + */ + public static class AdvancedPie extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedPie(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + continue; // Skip this invalid + } + allSkipped = false; + values.put(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + } + + /** + * Represents a custom drilldown pie. + */ + public static class DrilldownPie extends CustomChart { + + private final Callable>> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public DrilldownPie(String chartId, Callable>> callable) { + super(chartId); + this.callable = callable; + } + + @Override + public JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map> map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean reallyAllSkipped = true; + for (Map.Entry> entryValues : map.entrySet()) { + JSONObject value = new JSONObject(); + boolean allSkipped = true; + for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { + value.put(valueEntry.getKey(), valueEntry.getValue()); + allSkipped = false; + } + if (!allSkipped) { + reallyAllSkipped = false; + values.put(entryValues.getKey(), value); + } + } + if (reallyAllSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + } + + /** + * Represents a custom single line chart. + */ + public static class SingleLineChart extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SingleLineChart(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + int value = callable.call(); + if (value == 0) { + // Null = skip the chart + return null; + } + data.put("value", value); + return data; + } + + } + + /** + * Represents a custom multi line chart. + */ + public static class MultiLineChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public MultiLineChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + continue; // Skip this invalid + } + allSkipped = false; + values.put(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + + } + + /** + * Represents a custom simple bar chart. + */ + public static class SimpleBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimpleBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + for (Map.Entry entry : map.entrySet()) { + JSONArray categoryValues = new JSONArray(); + categoryValues.add(entry.getValue()); + values.put(entry.getKey(), categoryValues); + } + data.put("values", values); + return data; + } + + } + + /** + * Represents a custom advanced bar chart. + */ + public static class AdvancedBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + continue; // Skip this invalid + } + allSkipped = false; + JSONArray categoryValues = new JSONArray(); + for (int categoryValue : entry.getValue()) { + categoryValues.add(categoryValue); + } + values.put(entry.getKey(), categoryValues); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + + } + + static class PaperMetrics { + static void startMetrics() { + // Get the config file + File configFile = new File(new File((File) MinecraftServer.getServer().options.valueOf("plugins"), "bStats"), "config.yml"); + YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); + + // Check if the config file exists + if (!config.isSet("serverUuid")) { + + // Add default values + config.addDefault("enabled", true); + // Every server gets it's unique random id. + config.addDefault("serverUuid", UUID.randomUUID().toString()); + // Should failed request be logged? + config.addDefault("logFailedRequests", false); + + // Inform the server owners about bStats + config.options().header( + "bStats collects some data for plugin authors like how many servers are using their plugins.\n" + + "To honor their work, you should not disable it.\n" + + "This has nearly no effect on the server performance!\n" + + "Check out https://bStats.org/ to learn more :)" + ).copyDefaults(true); + try { + config.save(configFile); + } catch (IOException ignored) { + } + } + // Load the data + String serverUUID = config.getString("serverUuid"); + boolean logFailedRequests = config.getBoolean("logFailedRequests", false); + // Only start Metrics, if it's enabled in the config + if (config.getBoolean("enabled", true)) { + Metrics metrics = new Metrics("Paper", serverUUID, logFailedRequests, Bukkit.getLogger()); + + metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> { + String minecraftVersion = Bukkit.getVersion(); + minecraftVersion = minecraftVersion.substring(minecraftVersion.indexOf("MC: ") + 4, minecraftVersion.length() - 1); + return minecraftVersion; + })); + + metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size())); + metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : "offline")); + metrics.addCustomChart(new Metrics.SimplePie("paper_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown")); + + metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> { + Map> map = new HashMap<>(); + String javaVersion = System.getProperty("java.version"); + Map entry = new HashMap<>(); + entry.put(javaVersion, 1); + + // http://openjdk.java.net/jeps/223 + // Java decided to change their versioning scheme and in doing so modified the java.version system + // property to return $major[.$minor][.$secuity][-ea], as opposed to 1.$major.0_$identifier + // we can handle pre-9 by checking if the "major" is equal to "1", otherwise, 9+ + String majorVersion = javaVersion.split("\\.")[0]; + String release; + + int indexOf = javaVersion.lastIndexOf('.'); + + if (majorVersion.equals("1")) { + release = "Java " + javaVersion.substring(0, indexOf); + } else { + // of course, it really wouldn't be all that simple if they didn't add a quirk, now would it + // valid strings for the major may potentially include values such as -ea to deannotate a pre release + Matcher versionMatcher = Pattern.compile("\\d+").matcher(majorVersion); + if (versionMatcher.find()) { + majorVersion = versionMatcher.group(0); + } + release = "Java " + majorVersion; + } + map.put(release, entry); + + return map; + })); + } + + } + } +} diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java new file mode 100644 index 000000000000..9b9c8361e992 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java @@ -0,0 +1,238 @@ +package com.destroystokyo.paper; + +import com.google.common.base.Functions; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import net.minecraft.server.*; +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.entity.Player; + +import java.io.File; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; + +public class PaperCommand extends Command { + + public PaperCommand(String name) { + super(name); + this.description = "Paper related commands"; + this.usageMessage = "/paper [heap | entity | reload | version]"; + this.setPermission("bukkit.command.paper"); + } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { + if (args.length <= 1) + return getListMatchingLast(args, "heap", "entity", "reload", "version"); + + switch (args[0].toLowerCase(Locale.ENGLISH)) + { + case "entity": + if (args.length == 2) + return getListMatchingLast(args, "help", "list"); + if (args.length == 3) + return getListMatchingLast(args, EntityTypes.getEntityNameList().stream().map(MinecraftKey::toString).sorted().toArray(String[]::new)); + break; + } + return Collections.emptyList(); + } + + // Code from Mojang - copyright them + public static List getListMatchingLast(String[] args, String... matches) { + return getListMatchingLast(args, (Collection) Arrays.asList(matches)); + } + + public static boolean matches(String s, String s1) { + return s1.regionMatches(true, 0, s, 0, s.length()); + } + + public static List getListMatchingLast(String[] strings, Collection collection) { + String last = strings[strings.length - 1]; + ArrayList results = Lists.newArrayList(); + + if (!collection.isEmpty()) { + Iterator iterator = Iterables.transform(collection, Functions.toStringFunction()).iterator(); + + while (iterator.hasNext()) { + String s1 = (String) iterator.next(); + + if (matches(last, s1)) { + results.add(s1); + } + } + + if (results.isEmpty()) { + iterator = collection.iterator(); + + while (iterator.hasNext()) { + Object object = iterator.next(); + + if (object instanceof MinecraftKey && matches(last, ((MinecraftKey) object).getKey())) { + results.add(String.valueOf(object)); + } + } + } + } + + return results; + } + // end copy stuff + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + if (!testPermission(sender)) return true; + + if (args.length == 0) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + switch (args[0].toLowerCase(Locale.ENGLISH)) { + case "heap": + dumpHeap(sender); + break; + case "entity": + listEntities(sender, args); + break; + case "reload": + doReload(sender); + break; + case "ver": + case "version": + org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version").execute(sender, commandLabel, new String[0]); + break; + default: + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + return true; + } + + /* + * Ported from MinecraftForge - author: LexManos - License: LGPLv2.1 + */ + private void listEntities(CommandSender sender, String[] args) { + if (args.length < 2 || args[1].toLowerCase(Locale.ENGLISH).equals("help")) { + sender.sendMessage(ChatColor.RED + "Use /paper entity [list] help for more information on a specific command."); + return; + } + + switch (args[1].toLowerCase(Locale.ENGLISH)) { + case "list": + String filter = "*"; + if (args.length > 2) { + if (args[2].toLowerCase(Locale.ENGLISH).equals("help")) { + sender.sendMessage(ChatColor.RED + "Use /paper entity list [filter] [worldName] to get entity info that matches the optional filter."); + return; + } + filter = args[2]; + } + final String cleanfilter = filter.replace("?", ".?").replace("*", ".*?"); + Set names = EntityTypes.getEntityNameList().stream() + .filter(n -> n.toString().matches(cleanfilter)) + .collect(Collectors.toSet()); + + if (names.isEmpty()) { + sender.sendMessage(ChatColor.RED + "Invalid filter, does not match any entities. Use /paper entity list for a proper list"); + return; + } + + String worldName; + if (args.length > 3) { + worldName = args[3]; + } else if (sender instanceof Player) { + worldName = ((Player) sender).getWorld().getName(); + } else { + sender.sendMessage(ChatColor.RED + "Please specify the name of a world"); + sender.sendMessage(ChatColor.RED + "To do so without a filter, specify '*' as the filter"); + return; + } + + Map>> list = Maps.newHashMap(); + World bukkitWorld = Bukkit.getWorld(worldName); + if (bukkitWorld == null) { + sender.sendMessage(ChatColor.RED + "Could not load world for " + worldName + ". Please select a valid world."); + return; + } + WorldServer world = ((CraftWorld) Bukkit.getWorld(worldName)).getHandle(); + + List entities = world.entityList; + entities.forEach(e -> { + MinecraftKey key = e.getMinecraftKey(); + if (e.shouldBeRemoved) return; // Paper + + MutablePair> info = list.computeIfAbsent(key, k -> MutablePair.of(0, Maps.newHashMap())); + ChunkCoordIntPair chunk = new ChunkCoordIntPair(e.getChunkX(), e.getChunkZ()); + info.left++; + info.right.put(chunk, info.right.getOrDefault(chunk, 0) + 1); + }); + + if (names.size() == 1) { + MinecraftKey name = names.iterator().next(); + Pair> info = list.get(name); + if (info == null) { + sender.sendMessage(ChatColor.RED + "No entities found."); + return; + } + sender.sendMessage("Entity: " + name + " Total: " + info.getLeft()); + info.getRight().entrySet().stream() + .sorted((a, b) -> !a.getValue().equals(b.getValue()) ? b.getValue() - a.getValue() : a.getKey().toString().compareTo(b.getKey().toString())) + .limit(10).forEach(e -> sender.sendMessage(" " + e.getValue() + ": " + e.getKey().x + ", " + e.getKey().z)); + } else { + List> info = list.entrySet().stream() + .filter(e -> names.contains(e.getKey())) + .map(e -> Pair.of(e.getKey(), e.getValue().left)) + .sorted((a, b) -> !a.getRight().equals(b.getRight()) ? b.getRight() - a.getRight() : a.getKey().toString().compareTo(b.getKey().toString())) + .collect(Collectors.toList()); + + if (info == null || info.size() == 0) { + sender.sendMessage(ChatColor.RED + "No entities found."); + return; + } + + int count = info.stream().mapToInt(Pair::getRight).sum(); + sender.sendMessage("Total: " + count); + info.forEach(e -> sender.sendMessage(" " + e.getValue() + ": " + e.getKey())); + } + break; + } + } + + private void dumpHeap(CommandSender sender) { + File file = new File(new File(new File("."), "dumps"), + "heap-dump-" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()) + "-server.hprof"); + Command.broadcastCommandMessage(sender, ChatColor.YELLOW + "Writing JVM heap data to " + file); + if (CraftServer.dumpHeap(file)) { + Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Heap dump complete"); + } else { + Command.broadcastCommandMessage(sender, ChatColor.RED + "Failed to write heap dump, see sever log for details"); + } + } + + private void doReload(CommandSender sender) { + Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues."); + Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server."); + + MinecraftServer console = MinecraftServer.getServer(); + com.destroystokyo.paper.PaperConfig.init((File) console.options.valueOf("paper-settings")); + for (WorldServer world : console.getWorlds()) { + world.paperConfig.init(); + } + console.server.reloadCount++; + + Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Paper config reload complete."); + } +} diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java new file mode 100644 index 000000000000..a7673dd49df1 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -0,0 +1,465 @@ +package com.destroystokyo.paper; + +import com.google.common.base.Strings; +import com.google.common.base.Throwables; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +import com.google.common.collect.Lists; +import net.minecraft.server.MinecraftServer; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import co.aikar.timings.Timings; +import co.aikar.timings.TimingsManager; +import org.spigotmc.SpigotConfig; +import org.spigotmc.WatchdogThread; + +public class PaperConfig { + + private static File CONFIG_FILE; + private static final String HEADER = "This is the main configuration file for Paper.\n" + + "As you can see, there's tons to configure. Some options may impact gameplay, so use\n" + + "with caution, and make sure you know what each option does before configuring.\n" + + "\n" + + "If you need help with the configuration or have any questions related to Paper,\n" + + "join us in our Discord or IRC channel.\n" + + "\n" + + "Discord: https://paperdiscord.emc.gs\n" + + "IRC: #paper @ irc.spi.gt ( http://irc.spi.gt/iris/?channels=paper )\n" + + "Website: https://papermc.io/ \n" + + "Docs: https://paper.readthedocs.org/ \n"; + /*========================================================================*/ + public static YamlConfiguration config; + static int version; + static Map commands; + private static boolean verbose; + private static boolean fatalError; + /*========================================================================*/ + private static boolean metricsStarted; + + public static void init(File configFile) { + CONFIG_FILE = configFile; + config = new YamlConfiguration(); + try { + config.load(CONFIG_FILE); + } catch (IOException ex) { + } catch (InvalidConfigurationException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Could not load paper.yml, please correct your syntax errors", ex); + throw Throwables.propagate(ex); + } + config.options().header(HEADER); + config.options().copyDefaults(true); + verbose = getBoolean("verbose", false); + + commands = new HashMap(); + commands.put("paper", new PaperCommand("paper")); + + version = getInt("config-version", 17); + set("config-version", 17); + readConfig(PaperConfig.class, null); + } + + protected static void logError(String s) { + Bukkit.getLogger().severe(s); + } + + protected static void fatal(String s) { + fatalError = true; + throw new RuntimeException("Fatal paper.yml config error: " + s); + } + + protected static void log(String s) { + if (verbose) { + Bukkit.getLogger().info(s); + } + } + + public static void registerCommands() { + for (Map.Entry entry : commands.entrySet()) { + MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Paper", entry.getValue()); + } + + if (!metricsStarted) { + Metrics.PaperMetrics.startMetrics(); + metricsStarted = true; + } + } + + static void readConfig(Class clazz, Object instance) { + for (Method method : clazz.getDeclaredMethods()) { + if (Modifier.isPrivate(method.getModifiers())) { + if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) { + try { + method.setAccessible(true); + method.invoke(instance); + } catch (InvocationTargetException ex) { + throw Throwables.propagate(ex.getCause()); + } catch (Exception ex) { + Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex); + } + } + } + } + + try { + config.save(CONFIG_FILE); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex); + } + } + + private static final Pattern SPACE = Pattern.compile(" "); + private static final Pattern NOT_NUMERIC = Pattern.compile("[^-\\d.]"); + public static int getSeconds(String str) { + str = SPACE.matcher(str).replaceAll(""); + final char unit = str.charAt(str.length() - 1); + str = NOT_NUMERIC.matcher(str).replaceAll(""); + double num; + try { + num = Double.parseDouble(str); + } catch (Exception e) { + num = 0D; + } + switch (unit) { + case 'd': num *= (double) 60*60*24; break; + case 'h': num *= (double) 60*60; break; + case 'm': num *= (double) 60; break; + default: case 's': break; + } + return (int) num; + } + + protected static String timeSummary(int seconds) { + String time = ""; + + if (seconds > 60 * 60 * 24) { + time += TimeUnit.SECONDS.toDays(seconds) + "d"; + seconds %= 60 * 60 * 24; + } + + if (seconds > 60 * 60) { + time += TimeUnit.SECONDS.toHours(seconds) + "h"; + seconds %= 60 * 60; + } + + if (seconds > 0) { + time += TimeUnit.SECONDS.toMinutes(seconds) + "m"; + } + return time; + } + + private static void set(String path, Object val) { + config.set(path, val); + } + + private static boolean getBoolean(String path, boolean def) { + config.addDefault(path, def); + return config.getBoolean(path, config.getBoolean(path)); + } + + private static double getDouble(String path, double def) { + config.addDefault(path, def); + return config.getDouble(path, config.getDouble(path)); + } + + private static float getFloat(String path, float def) { + // TODO: Figure out why getFloat() always returns the default value. + return (float) getDouble(path, (double) def); + } + + private static int getInt(String path, int def) { + config.addDefault(path, def); + return config.getInt(path, config.getInt(path)); + } + + private static List getList(String path, T def) { + config.addDefault(path, def); + return (List) config.getList(path, config.getList(path)); + } + + private static String getString(String path, String def) { + config.addDefault(path, def); + return config.getString(path, config.getString(path)); + } + + public static int maxTickMsLostLightQueue; + private static void lightQueue() { + int badSetting = config.getInt("queue-light-updates-max-loss", 10); + config.set("queue-light-updates-max-loss", null); + maxTickMsLostLightQueue = getInt("settings.queue-light-updates-max-loss", badSetting); + } + + private static void timings() { + boolean timings = getBoolean("timings.enabled", true); + boolean verboseTimings = getBoolean("timings.verbose", true); + TimingsManager.privacy = getBoolean("timings.server-name-privacy", false); + TimingsManager.hiddenConfigs = getList("timings.hidden-config-entries", Lists.newArrayList("database", "settings.bungeecord-addresses")); + int timingHistoryInterval = getInt("timings.history-interval", 300); + int timingHistoryLength = getInt("timings.history-length", 3600); + + + Timings.setVerboseTimingsEnabled(verboseTimings); + Timings.setTimingsEnabled(timings); + Timings.setHistoryInterval(timingHistoryInterval * 20); + Timings.setHistoryLength(timingHistoryLength * 20); + + log("Timings: " + timings + + " - Verbose: " + verboseTimings + + " - Interval: " + timeSummary(Timings.getHistoryInterval() / 20) + + " - Length: " + timeSummary(Timings.getHistoryLength() / 20)); + } + + public static boolean enableFileIOThreadSleep; + private static void enableFileIOThreadSleep() { + enableFileIOThreadSleep = getBoolean("settings.sleep-between-chunk-saves", false); + if (enableFileIOThreadSleep) Bukkit.getLogger().info("Enabled sleeping between chunk saves, beware of memory issues"); + } + + public static boolean loadPermsBeforePlugins = true; + private static void loadPermsBeforePlugins() { + loadPermsBeforePlugins = getBoolean("settings.load-permissions-yml-before-plugins", true); + } + + public static int regionFileCacheSize = 256; + private static void regionFileCacheSize() { + regionFileCacheSize = getInt("settings.region-file-cache-size", 256); + } + + public static boolean enablePlayerCollisions = true; + private static void enablePlayerCollisions() { + enablePlayerCollisions = getBoolean("settings.enable-player-collisions", true); + } + + public static boolean saveEmptyScoreboardTeams = false; + private static void saveEmptyScoreboardTeams() { + saveEmptyScoreboardTeams = getBoolean("settings.save-empty-scoreboard-teams", false); + } + + public static boolean bungeeOnlineMode = true; + private static void bungeeOnlineMode() { + bungeeOnlineMode = getBoolean("settings.bungee-online-mode", true); + } + + public static boolean isProxyOnlineMode() { + return Bukkit.getOnlineMode() || (SpigotConfig.bungee && bungeeOnlineMode) || (velocitySupport && velocityOnlineMode); + } + + public static int packetInSpamThreshold = 300; + private static void packetInSpamThreshold() { + if (version < 11) { + int oldValue = getInt("settings.play-in-use-item-spam-threshold", 300); + set("settings.incoming-packet-spam-threshold", oldValue); + } + packetInSpamThreshold = getInt("settings.incoming-packet-spam-threshold", 300); + } + + public static String flyingKickPlayerMessage = "Flying is not enabled on this server"; + public static String flyingKickVehicleMessage = "Flying is not enabled on this server"; + private static void flyingKickMessages() { + flyingKickPlayerMessage = getString("messages.kick.flying-player", flyingKickPlayerMessage); + flyingKickVehicleMessage = getString("messages.kick.flying-vehicle", flyingKickVehicleMessage); + } + + public static int playerAutoSaveRate = -1; + public static int maxPlayerAutoSavePerTick = 10; + private static void playerAutoSaveRate() { + playerAutoSaveRate = getInt("settings.player-auto-save-rate", -1); + maxPlayerAutoSavePerTick = getInt("settings.max-player-auto-save-per-tick", -1); + if (maxPlayerAutoSavePerTick == -1) { // -1 Automatic / "Recommended" + // 10 should be safe for everyone unless your mass spamming player auto save + maxPlayerAutoSavePerTick = (playerAutoSaveRate == -1 || playerAutoSaveRate > 100) ? 10 : 20; + } + } + + public static boolean suggestPlayersWhenNullTabCompletions = true; + private static void suggestPlayersWhenNull() { + suggestPlayersWhenNullTabCompletions = getBoolean("settings.suggest-player-names-when-null-tab-completions", suggestPlayersWhenNullTabCompletions); + } + + public static String authenticationServersDownKickMessage = ""; // empty = use translatable message + private static void authenticationServersDownKickMessage() { + authenticationServersDownKickMessage = Strings.emptyToNull(getString("messages.kick.authentication-servers-down", authenticationServersDownKickMessage)); + } + + public static String connectionThrottleKickMessage = "Connection throttled! Please wait before reconnecting."; + private static void connectionThrottleKickMessage() { + connectionThrottleKickMessage = getString("messages.kick.connection-throttle", connectionThrottleKickMessage); + } + + public static String noPermissionMessage = "&cI'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is in error."; + private static void noPermissionMessage() { + noPermissionMessage = ChatColor.translateAlternateColorCodes('&', getString("messages.no-permission", noPermissionMessage)); + } + + public static boolean savePlayerData = true; + private static void savePlayerData() { + savePlayerData = getBoolean("settings.save-player-data", savePlayerData); + if(!savePlayerData) { + Bukkit.getLogger().log(Level.WARNING, "Player Data Saving is currently disabled. Any changes to your players data, " + + "such as inventories, experience points, advancements and the like will not be saved when they log out."); + } + } + + public static boolean useAlternativeLuckFormula = false; + private static void useAlternativeLuckFormula() { + useAlternativeLuckFormula = getBoolean("settings.use-alternative-luck-formula", false); + if (useAlternativeLuckFormula) { + Bukkit.getLogger().log(Level.INFO, "Using Aikar's Alternative Luck Formula to apply Luck attribute to all loot pool calculations. See https://luckformula.emc.gs"); + } + } + + public static boolean useVersionedWorld = false; + private static void useVersionedWorld() { + useVersionedWorld = getBoolean("settings.use-versioned-world", false); + if (useVersionedWorld) { + Logger logger = Bukkit.getLogger(); + String ver = MinecraftServer.getServer().getVersion(); + logger.log(Level.INFO, "******************************************************"); + logger.log(Level.INFO, "*** Using a versioned world folder. Your world will be saved"); + logger.log(Level.INFO, "*** to into the " + ver + " folder, but copied from your current world."); + logger.log(Level.INFO, "*** "); + logger.log(Level.INFO, "*** This setting should not be used in your real world!!!"); + logger.log(Level.INFO, "*** If you want to retain the new world, you need to move "); + logger.log(Level.INFO, "*** the folders out of the " + ver + " folder and overwrite existing"); + logger.log(Level.INFO, "*** "); + logger.log(Level.INFO, "*** Deleting the " + ver + " folder will cause it to recreate again"); + logger.log(Level.INFO, "*** from your unversioned world files."); + logger.log(Level.INFO, "*** "); + logger.log(Level.INFO, "*** You should backup your original world files incase something goes"); + logger.log(Level.INFO, "*** wrong with this system! This is not a backup system."); + logger.log(Level.INFO, "******************************************************"); + } + } + + public static int watchdogPrintEarlyWarningEvery = 5000; + public static int watchdogPrintEarlyWarningDelay = 10000; + private static void watchdogEarlyWarning() { + watchdogPrintEarlyWarningEvery = getInt("settings.watchdog.early-warning-every", 5000); + watchdogPrintEarlyWarningDelay = getInt("settings.watchdog.early-warning-delay", 10000); + WatchdogThread.doStart(SpigotConfig.timeoutTime, SpigotConfig.restartOnCrash ); + } + + public static int tabSpamIncrement = 1; + public static int tabSpamLimit = 500; + private static void tabSpamLimiters() { + tabSpamIncrement = getInt("settings.spam-limiter.tab-spam-increment", tabSpamIncrement); + // Older versions used a smaller limit, which is too low for 1.13, we'll bump this up if default + if (version < 14) { + if (tabSpamIncrement == 10) { + set("settings.spam-limiter.tab-spam-increment", 2); + tabSpamIncrement = 2; + } + } + tabSpamLimit = getInt("settings.spam-limiter.tab-spam-limit", tabSpamLimit); + } + + public static Map seedOverride = new java.util.HashMap<>(); + private static void worldSeedOverrides() { + ConfigurationSection seeds = config.getConfigurationSection("seed-overrides"); + if (seeds != null) { + TimingsManager.hiddenConfigs.add("seed-overrides"); + for (String key : seeds.getKeys(false)) { + String seedString = seeds.getString(key); + long seed; + try { + seed = Long.parseLong(seedString); + } catch (Exception e) { + seed = (long) seedString.hashCode(); + } + log("Seed Override: " + key + " => " + seed); + seedOverride.put(key, seed); + } + } + } + + public static boolean asyncChunks = false; + public static boolean asyncChunkGeneration = true; + public static boolean asyncChunkGenThreadPerWorld = true; + public static int asyncChunkLoadThreads = -1; + private static void asyncChunks() { + if (version < 15) { + boolean enabled = config.getBoolean("settings.async-chunks", true); + ConfigurationSection section = config.createSection("settings.async-chunks"); + section.set("enable", enabled); + section.set("load-threads", -1); + section.set("generation", true); + section.set("thread-per-world-generation", true); + } + + asyncChunks = getBoolean("settings.async-chunks.enable", true); + asyncChunkGeneration = getBoolean("settings.async-chunks.generation", true); + asyncChunkGenThreadPerWorld = getBoolean("settings.async-chunks.thread-per-world-generation", true); + asyncChunkLoadThreads = getInt("settings.async-chunks.load-threads", -1); + if (asyncChunkLoadThreads <= 0) { + asyncChunkLoadThreads = (int) Math.min(Integer.getInteger("paper.maxChunkThreads", 8), Runtime.getRuntime().availableProcessors() * 1.5); + } + + // Let Shared Host set some limits + String sharedHostEnvGen = System.getenv("PAPER_ASYNC_CHUNKS_SHARED_HOST_GEN"); + String sharedHostEnvLoad = System.getenv("PAPER_ASYNC_CHUNKS_SHARED_HOST_LOAD"); + if ("1".equals(sharedHostEnvGen)) { + log("Async Chunks - Generation: Your host has requested to use a single thread world generation"); + asyncChunkGenThreadPerWorld = false; + } else if ("2".equals(sharedHostEnvGen)) { + log("Async Chunks - Generation: Your host has disabled async world generation - You will experience lag from world generation"); + asyncChunkGeneration = false; + } + + if (sharedHostEnvLoad != null) { + try { + asyncChunkLoadThreads = Math.max(1, Math.min(asyncChunkLoadThreads, Integer.parseInt(sharedHostEnvLoad))); + } catch (NumberFormatException ignored) {} + } + + if (!asyncChunks) { + log("Async Chunks: Disabled - Chunks will be managed synchronosuly, and will cause tremendous lag."); + } else { + log("Async Chunks: Enabled - Chunks will be loaded much faster, without lag."); + if (!asyncChunkGeneration) { + log("Async Chunks - Generation: Disabled - Chunks will be generated synchronosuly, and will cause tremendous lag."); + } else if (asyncChunkGenThreadPerWorld) { + log("Async Chunks - Generation: Enabled - Chunks will be generated much faster, without lag."); + } else { + log("Async Chunks - Generation: Enabled (Single Thread) - Chunks will be generated much faster, without lag."); + } + } + } + + public static boolean velocitySupport; + public static boolean velocityOnlineMode; + public static byte[] velocitySecretKey; + private static void velocitySupport() { + velocitySupport = getBoolean("settings.velocity-support.enabled", false); + velocityOnlineMode = getBoolean("settings.velocity-support.online-mode", false); + String secret = getString("settings.velocity-support.secret", ""); + if (velocitySupport && secret.isEmpty()) { + fatal("Velocity support is enabled, but no secret key was specified. A secret key is required!"); + } else { + velocitySecretKey = secret.getBytes(StandardCharsets.UTF_8); + } + } + + public static int maxBookPageSize = 2560; + public static double maxBookTotalSizeMultiplier = 0.98D; + private static void maxBookSize() { + maxBookPageSize = getInt("settings.book-size.page-max", maxBookPageSize); + maxBookTotalSizeMultiplier = getDouble("settings.book-size.total-multiplier", maxBookTotalSizeMultiplier); + } +} diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java new file mode 100644 index 000000000000..f259c4e51418 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -0,0 +1,597 @@ +package com.destroystokyo.paper; + +import java.util.Arrays; +import java.util.List; + +import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.ChunkEdgeMode; +import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.EngineMode; +import net.minecraft.server.MinecraftServer; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; +import org.spigotmc.SpigotWorldConfig; + +import static com.destroystokyo.paper.PaperConfig.log; +import static com.destroystokyo.paper.PaperConfig.logError; + +public class PaperWorldConfig { + + private final String worldName; + private final SpigotWorldConfig spigotConfig; + private final YamlConfiguration config; + private boolean verbose; + + public PaperWorldConfig(String worldName, SpigotWorldConfig spigotConfig) { + this.worldName = worldName; + this.spigotConfig = spigotConfig; + this.config = PaperConfig.config; + init(); + } + + public void init() { + log("-------- World Settings For [" + worldName + "] --------"); + PaperConfig.readConfig(PaperWorldConfig.class, this); + } + + private void set(String path, Object val) { + config.set("world-settings.default." + path, val); + if (config.get("world-settings." + worldName + "." + path) != null) { + config.set("world-settings." + worldName + "." + path, val); + } + } + + private boolean getBoolean(String path, boolean def) { + config.addDefault("world-settings.default." + path, def); + return config.getBoolean("world-settings." + worldName + "." + path, config.getBoolean("world-settings.default." + path)); + } + + private double getDouble(String path, double def) { + config.addDefault("world-settings.default." + path, def); + return config.getDouble("world-settings." + worldName + "." + path, config.getDouble("world-settings.default." + path)); + } + + private int getInt(String path, int def) { + config.addDefault("world-settings.default." + path, def); + return config.getInt("world-settings." + worldName + "." + path, config.getInt("world-settings.default." + path)); + } + + private float getFloat(String path, float def) { + // TODO: Figure out why getFloat() always returns the default value. + return (float) getDouble(path, (double) def); + } + + private List getList(String path, List def) { + config.addDefault("world-settings.default." + path, def); + return (List) config.getList("world-settings." + worldName + "." + path, config.getList("world-settings.default." + path)); + } + + private String getString(String path, String def) { + config.addDefault("world-settings.default." + path, def); + return config.getString("world-settings." + worldName + "." + path, config.getString("world-settings.default." + path)); + } + + public int cactusMaxHeight; + public int reedMaxHeight; + private void blockGrowthHeight() { + cactusMaxHeight = getInt("max-growth-height.cactus", 3); + reedMaxHeight = getInt("max-growth-height.reeds", 3); + log("Max height for cactus growth " + cactusMaxHeight + ". Max height for reed growth " + reedMaxHeight); + + } + + public double babyZombieMovementSpeed; + private void babyZombieMovementSpeed() { + babyZombieMovementSpeed = getDouble("baby-zombie-movement-speed", 0.5D); // Player moves at 0.1F, for reference + log("Baby zombies will move at the speed of " + babyZombieMovementSpeed); + } + + public int fishingMinTicks; + public int fishingMaxTicks; + private void fishingTickRange() { + fishingMinTicks = getInt("fishing-time-range.MinimumTicks", 100); + fishingMaxTicks = getInt("fishing-time-range.MaximumTicks", 600); + log("Fishing time ranges are between " + fishingMinTicks +" and " + fishingMaxTicks + " ticks"); + } + + public boolean nerfedMobsShouldJump; + private void nerfedMobsShouldJump() { + nerfedMobsShouldJump = getBoolean("spawner-nerfed-mobs-should-jump", false); + } + + public int softDespawnDistance; + public int hardDespawnDistance; + private void despawnDistances() { + softDespawnDistance = getInt("despawn-ranges.soft", 32); // 32^2 = 1024, Minecraft Default + hardDespawnDistance = getInt("despawn-ranges.hard", 128); // 128^2 = 16384, Minecraft Default + + if (softDespawnDistance > hardDespawnDistance) { + softDespawnDistance = hardDespawnDistance; + } + + log("Living Entity Despawn Ranges: Soft: " + softDespawnDistance + " Hard: " + hardDespawnDistance); + + softDespawnDistance = softDespawnDistance*softDespawnDistance; + hardDespawnDistance = hardDespawnDistance*hardDespawnDistance; + } + + public boolean keepSpawnInMemory; + private void keepSpawnInMemory() { + keepSpawnInMemory = getBoolean("keep-spawn-loaded", true); + log("Keep spawn chunk loaded: " + keepSpawnInMemory); + } + + public int fallingBlockHeightNerf; + public int entityTNTHeightNerf; + private void heightNerfs() { + fallingBlockHeightNerf = getInt("falling-block-height-nerf", 0); + entityTNTHeightNerf = getInt("tnt-entity-height-nerf", 0); + + if (fallingBlockHeightNerf != 0) log("Falling Block Height Limit set to Y: " + fallingBlockHeightNerf); + if (entityTNTHeightNerf != 0) log("TNT Entity Height Limit set to Y: " + entityTNTHeightNerf); + } + + public boolean netherVoidTopDamage; + private void netherVoidTopDamage() { + netherVoidTopDamage = getBoolean( "nether-ceiling-void-damage", false ); + log("Top of the nether void damage: " + netherVoidTopDamage); + } + + public boolean queueLightUpdates; + private void queueLightUpdates() { + queueLightUpdates = getBoolean("queue-light-updates", false); + log("Lighting Queue enabled: " + queueLightUpdates); + log("Warning: This feature may help reduce TPS loss from light, but comes at the cost of buggy light data"); + log("We are working to improve this feature."); + } + + public boolean disableEndCredits; + private void disableEndCredits() { + disableEndCredits = getBoolean("game-mechanics.disable-end-credits", false); + log("End credits disabled: " + disableEndCredits); + } + + public boolean optimizeExplosions; + private void optimizeExplosions() { + optimizeExplosions = getBoolean("optimize-explosions", false); + log("Optimize explosions: " + optimizeExplosions); + } + + public boolean disableExplosionKnockback; + private void disableExplosionKnockback(){ + disableExplosionKnockback = getBoolean("disable-explosion-knockback", false); + } + + public boolean disableThunder; + private void disableThunder() { + disableThunder = getBoolean("disable-thunder", false); + } + + public boolean disableIceAndSnow; + private void disableIceAndSnow(){ + disableIceAndSnow = getBoolean("disable-ice-and-snow", false); + } + + public int mobSpawnerTickRate; + private void mobSpawnerTickRate() { + mobSpawnerTickRate = getInt("mob-spawner-tick-rate", 1); + } + + public int containerUpdateTickRate; + private void containerUpdateTickRate() { + containerUpdateTickRate = getInt("container-update-tick-rate", 1); + } + + public boolean disableChestCatDetection; + private void disableChestCatDetection() { + disableChestCatDetection = getBoolean("game-mechanics.disable-chest-cat-detection", false); + } + + public boolean disablePlayerCrits; + private void disablePlayerCrits() { + disablePlayerCrits = getBoolean("game-mechanics.disable-player-crits", false); + } + + public boolean allChunksAreSlimeChunks; + private void allChunksAreSlimeChunks() { + allChunksAreSlimeChunks = getBoolean("all-chunks-are-slime-chunks", false); + } + + public int portalSearchRadius; + private void portalSearchRadius() { + portalSearchRadius = getInt("portal-search-radius", 128); + } + + public boolean disableTeleportationSuffocationCheck; + private void disableTeleportationSuffocationCheck() { + disableTeleportationSuffocationCheck = getBoolean("disable-teleportation-suffocation-check", false); + } + + public boolean nonPlayerEntitiesOnScoreboards = false; + private void nonPlayerEntitiesOnScoreboards() { + nonPlayerEntitiesOnScoreboards = getBoolean("allow-non-player-entities-on-scoreboards", false); + } + + public boolean allowLeashingUndeadHorse = false; + private void allowLeashingUndeadHorse() { + allowLeashingUndeadHorse = getBoolean("allow-leashing-undead-horse", false); + } + + public int nonPlayerArrowDespawnRate = -1; + public int creativeArrowDespawnRate = -1; + private void nonPlayerArrowDespawnRate() { + nonPlayerArrowDespawnRate = getInt("non-player-arrow-despawn-rate", -1); + if (nonPlayerArrowDespawnRate == -1) { + nonPlayerArrowDespawnRate = spigotConfig.arrowDespawnRate; + } + creativeArrowDespawnRate = getInt("creative-arrow-despawn-rate", -1); + if (creativeArrowDespawnRate == -1) { + creativeArrowDespawnRate = spigotConfig.arrowDespawnRate; + } + log("Non Player Arrow Despawn Rate: " + nonPlayerArrowDespawnRate); + log("Creative Arrow Despawn Rate: " + creativeArrowDespawnRate); + } + + public double skeleHorseSpawnChance; + private void skeleHorseSpawnChance() { + skeleHorseSpawnChance = getDouble("skeleton-horse-thunder-spawn-chance", 0.01D); + if (skeleHorseSpawnChance < 0) { + skeleHorseSpawnChance = 0.01D; // Vanilla value + } + } + + public double sqrMaxThunderDistance; + public double sqrMaxLightningImpactSoundDistance; + public double maxLightningFlashDistance; + private void lightningStrikeDistanceLimit() { + sqrMaxThunderDistance = getInt("lightning-strike-distance-limit.sound", -1); + if (sqrMaxThunderDistance > 0) { + sqrMaxThunderDistance *= sqrMaxThunderDistance; + } + + sqrMaxLightningImpactSoundDistance = getInt("lightning-strike-distance-limit.impact-sound", -1); + if (sqrMaxLightningImpactSoundDistance < 0) { + sqrMaxLightningImpactSoundDistance = 32 * 32; //Vanilla value + } else { + sqrMaxLightningImpactSoundDistance *= sqrMaxLightningImpactSoundDistance; + } + + maxLightningFlashDistance = getInt("lightning-strike-distance-limit.flash", -1); + if (maxLightningFlashDistance < 0) { + maxLightningFlashDistance = 512; // Vanilla value + } + } + + public boolean firePhysicsEventForRedstone = false; + private void firePhysicsEventForRedstone() { + firePhysicsEventForRedstone = getBoolean("fire-physics-event-for-redstone", firePhysicsEventForRedstone); + } + + public int fixedInhabitedTime; + private void fixedInhabitedTime() { + if (PaperConfig.version < 16) { + if (!config.getBoolean("world-settings.default.use-chunk-inhabited-timer", true)) config.set("world-settings.default.fixed-chunk-inhabited-time", 0); + if (!config.getBoolean("world-settings." + worldName + ".use-chunk-inhabited-timer", true)) config.set("world-settings." + worldName + ".fixed-chunk-inhabited-time", 0); + set("use-chunk-inhabited-timer", null); + } + fixedInhabitedTime = getInt("fixed-chunk-inhabited-time", -1); + } + + public int grassUpdateRate = 1; + private void grassUpdateRate() { + grassUpdateRate = Math.max(0, getInt("grass-spread-tick-rate", grassUpdateRate)); + log("Grass Spread Tick Rate: " + grassUpdateRate); + } + + public short keepLoadedRange; + private void keepLoadedRange() { + keepLoadedRange = (short) (getInt("keep-spawn-loaded-range", Math.min(spigotConfig.viewDistance, 8)) * 16); + log( "Keep Spawn Loaded Range: " + (keepLoadedRange/16)); + } + + public boolean useVanillaScoreboardColoring; + private void useVanillaScoreboardColoring() { + useVanillaScoreboardColoring = getBoolean("use-vanilla-world-scoreboard-name-coloring", false); + } + + public boolean frostedIceEnabled = true; + public int frostedIceDelayMin = 20; + public int frostedIceDelayMax = 40; + private void frostedIce() { + this.frostedIceEnabled = this.getBoolean("frosted-ice.enabled", this.frostedIceEnabled); + this.frostedIceDelayMin = this.getInt("frosted-ice.delay.min", this.frostedIceDelayMin); + this.frostedIceDelayMax = this.getInt("frosted-ice.delay.max", this.frostedIceDelayMax); + log("Frosted Ice: " + (this.frostedIceEnabled ? "enabled" : "disabled") + " / delay: min=" + this.frostedIceDelayMin + ", max=" + this.frostedIceDelayMax); + } + + public boolean autoReplenishLootables; + public boolean restrictPlayerReloot; + public boolean changeLootTableSeedOnFill; + public int maxLootableRefills; + public int lootableRegenMin; + public int lootableRegenMax; + private void enhancedLootables() { + autoReplenishLootables = getBoolean("lootables.auto-replenish", false); + restrictPlayerReloot = getBoolean("lootables.restrict-player-reloot", true); + changeLootTableSeedOnFill = getBoolean("lootables.reset-seed-on-fill", true); + maxLootableRefills = getInt("lootables.max-refills", -1); + lootableRegenMin = PaperConfig.getSeconds(getString("lootables.refresh-min", "12h")); + lootableRegenMax = PaperConfig.getSeconds(getString("lootables.refresh-max", "2d")); + if (autoReplenishLootables) { + log("Lootables: Replenishing every " + + PaperConfig.timeSummary(lootableRegenMin) + " to " + + PaperConfig.timeSummary(lootableRegenMax) + + (restrictPlayerReloot ? " (restricting reloot)" : "") + ); + } + } + + public boolean preventTntFromMovingInWater; + private void preventTntFromMovingInWater() { + if (PaperConfig.version < 13) { + boolean oldVal = getBoolean("enable-old-tnt-cannon-behaviors", false); + set("prevent-tnt-from-moving-in-water", oldVal); + } + preventTntFromMovingInWater = getBoolean("prevent-tnt-from-moving-in-water", false); + log("Prevent TNT from moving in water: " + preventTntFromMovingInWater); + } + + public long delayChunkUnloadsBy; + private void delayChunkUnloadsBy() { + delayChunkUnloadsBy = PaperConfig.getSeconds(getString("delay-chunk-unloads-by", "10s")); + if (delayChunkUnloadsBy > 0) { + log("Delaying chunk unloads by " + delayChunkUnloadsBy + " seconds"); + delayChunkUnloadsBy *= 1000; + } + } + + public boolean skipEntityTickingInChunksScheduledForUnload = true; + private void skipEntityTickingInChunksScheduledForUnload() { + skipEntityTickingInChunksScheduledForUnload = getBoolean("skip-entity-ticking-in-chunks-scheduled-for-unload", skipEntityTickingInChunksScheduledForUnload); + } + + public int autoSavePeriod = -1; + private void autoSavePeriod() { + autoSavePeriod = getInt("auto-save-interval", -1); + if (autoSavePeriod > 0) { + log("Auto Save Interval: " +autoSavePeriod + " (" + (autoSavePeriod / 20) + "s)"); + } else if (autoSavePeriod < 0) { + autoSavePeriod = MinecraftServer.getServer().autosavePeriod; + } + } + + public int maxAutoSaveChunksPerTick = 24; + private void maxAutoSaveChunksPerTick() { + maxAutoSaveChunksPerTick = getInt("max-auto-save-chunks-per-tick", 24); + } + + public int queueSizeAutoSaveThreshold = 50; + private void queueSizeAutoSaveThreshold() { + queueSizeAutoSaveThreshold = getInt("save-queue-limit-for-auto-save", 50); + } + + public boolean removeCorruptTEs = false; + private void removeCorruptTEs() { + removeCorruptTEs = getBoolean("remove-corrupt-tile-entities", false); + } + + public boolean filterNBTFromSpawnEgg = true; + private void fitlerNBTFromSpawnEgg() { + filterNBTFromSpawnEgg = getBoolean("filter-nbt-data-from-spawn-eggs-and-related", true); + if (!filterNBTFromSpawnEgg) { + Bukkit.getLogger().warning("Spawn Egg and Armor Stand NBT filtering disabled, this is a potential security risk"); + } + } + + public boolean enableTreasureMaps = true; + public boolean treasureMapsAlreadyDiscovered = false; + private void treasureMapsAlreadyDiscovered() { + enableTreasureMaps = getBoolean("enable-treasure-maps", true); + treasureMapsAlreadyDiscovered = getBoolean("treasure-maps-return-already-discovered", false); + if (treasureMapsAlreadyDiscovered) { + log("Treasure Maps will return already discovered locations"); + } + } + + public boolean armorStandEntityLookups = true; + private void armorStandEntityLookups() { + armorStandEntityLookups = getBoolean("armor-stands-do-collision-entity-lookups", true); + } + + public int maxCollisionsPerEntity; + private void maxEntityCollision() { + maxCollisionsPerEntity = getInt( "max-entity-collisions", this.spigotConfig.getInt("max-entity-collisions", 8) ); + log( "Max Entity Collisions: " + maxCollisionsPerEntity ); + } + + public boolean parrotsHangOnBetter; + private void parrotsHangOnBetter() { + parrotsHangOnBetter = getBoolean("parrots-are-unaffected-by-player-movement", false); + log("Parrots are unaffected by player movement: " + parrotsHangOnBetter); + } + + public boolean disableCreeperLingeringEffect; + private void setDisableCreeperLingeringEffect() { + disableCreeperLingeringEffect = getBoolean("disable-creeper-lingering-effect", false); + log("Creeper lingering effect: " + disableCreeperLingeringEffect); + } + + public int expMergeMaxValue; + private void expMergeMaxValue() { + expMergeMaxValue = getInt("experience-merge-max-value", -1); + log("Experience Merge Max Value: " + expMergeMaxValue); + } + + public int maxChunkSendsPerTick = 81; + private void maxChunkSendsPerTick() { + maxChunkSendsPerTick = getInt("max-chunk-sends-per-tick", maxChunkSendsPerTick); + if (maxChunkSendsPerTick <= 0) { + maxChunkSendsPerTick = 81; + } + log("Max Chunk Sends Per Tick: " + maxChunkSendsPerTick); + } + + public int maxChunkGensPerTick = 10; + private void maxChunkGensPerTick() { + maxChunkGensPerTick = getInt("max-chunk-gens-per-tick", maxChunkGensPerTick); + if (maxChunkGensPerTick <= 0) { + maxChunkGensPerTick = Integer.MAX_VALUE; + log("Max Chunk Gens Per Tick: Unlimited (NOT RECOMMENDED)"); + } else { + log("Max Chunk Gens Per Tick: " + maxChunkGensPerTick); + } + } + + public double squidMaxSpawnHeight; + private void squidMaxSpawnHeight() { + squidMaxSpawnHeight = getDouble("squid-spawn-height.maximum", 0.0D); + } + + public boolean cooldownHopperWhenFull = true; + public boolean disableHopperMoveEvents = false; + private void hopperOptimizations() { + cooldownHopperWhenFull = getBoolean("hopper.cooldown-when-full", cooldownHopperWhenFull); + log("Cooldown Hoppers when Full: " + (cooldownHopperWhenFull ? "enabled" : "disabled")); + disableHopperMoveEvents = getBoolean("hopper.disable-move-event", disableHopperMoveEvents); + log("Hopper Move Item Events: " + (disableHopperMoveEvents ? "disabled" : "enabled")); + } + + public boolean disableSprintInterruptionOnAttack; + private void disableSprintInterruptionOnAttack() { + disableSprintInterruptionOnAttack = getBoolean("game-mechanics.disable-sprint-interruption-on-attack", false); + } + + public boolean disableEnderpearlExploit = true; + private void disableEnderpearlExploit() { + disableEnderpearlExploit = getBoolean("game-mechanics.disable-unloaded-chunk-enderpearl-exploit", disableEnderpearlExploit); + log("Disable Unloaded Chunk Enderpearl Exploit: " + (disableEnderpearlExploit ? "enabled" : "disabled")); + } + + public boolean villagesLoadChunks = false; + private void villagesLoadChunks() { + villagesLoadChunks = getBoolean("game-mechanics.villages-load-chunks", false); + if (villagesLoadChunks) { + log("Villages can load chunks - Warning this can cause intense TPS loss. Strongly consider disabling this."); + } + } + + public int shieldBlockingDelay = 5; + private void shieldBlockingDelay() { + shieldBlockingDelay = getInt("game-mechanics.shield-blocking-delay", 5); + } + + public boolean scanForLegacyEnderDragon = true; + private void scanForLegacyEnderDragon() { + scanForLegacyEnderDragon = getBoolean("game-mechanics.scan-for-legacy-ender-dragon", true); + } + + public int bedSearchRadius = 1; + private void bedSearchRadius() { + bedSearchRadius = getInt("bed-search-radius", 1); + if (bedSearchRadius < 1) { + bedSearchRadius = 1; + } + if (bedSearchRadius > 1) { + log("Bed Search Radius: " + bedSearchRadius); + } + } + + public int waterOverLavaFlowSpeed; + private void waterOverLavaFlowSpeed() { + waterOverLavaFlowSpeed = getInt("water-over-lava-flow-speed", 5); + log("Water over lava flow speed: " + waterOverLavaFlowSpeed); + } + + public enum DuplicateUUIDMode { + SAFE_REGEN, DELETE, NOTHING, WARN + } + public DuplicateUUIDMode duplicateUUIDMode = DuplicateUUIDMode.SAFE_REGEN; + public int duplicateUUIDDeleteRange = 32; + private void repairDuplicateUUID() { + String desiredMode = getString("duplicate-uuid-resolver", "saferegen").toLowerCase().trim(); + duplicateUUIDDeleteRange = getInt("duplicate-uuid-saferegen-delete-range", duplicateUUIDDeleteRange); + switch (desiredMode.toLowerCase()) { + case "regen": + case "regenerate": + case "saferegen": + case "saferegenerate": + duplicateUUIDMode = DuplicateUUIDMode.SAFE_REGEN; + log("Duplicate UUID Resolve: Regenerate New UUID if distant (Delete likely duplicates within " + duplicateUUIDDeleteRange + " blocks)"); + break; + case "remove": + case "delete": + duplicateUUIDMode = DuplicateUUIDMode.DELETE; + log("Duplicate UUID Resolve: Delete Entity"); + break; + case "silent": + case "nothing": + duplicateUUIDMode = DuplicateUUIDMode.NOTHING; + logError("Duplicate UUID Resolve: Do Nothing (no logs) - Warning, may lose indication of bad things happening"); + break; + case "log": + case "warn": + duplicateUUIDMode = DuplicateUUIDMode.WARN; + log("Duplicate UUID Resolve: Warn (do nothing but log it happened, may be spammy)"); + break; + default: + duplicateUUIDMode = DuplicateUUIDMode.WARN; + logError("Warning: Invalid duplicate-uuid-resolver config " + desiredMode + " - must be one of: regen, delete, nothing, warn"); + log("Duplicate UUID Resolve: Warn (do nothing but log it happened, may be spammy)"); + break; + } + } + + public boolean armorStandTick = true; + private void armorStandTick() { + this.armorStandTick = this.getBoolean("armor-stands-tick", this.armorStandTick); + log("ArmorStand ticking is " + (this.armorStandTick ? "enabled" : "disabled") + " by default"); + } + + public boolean optimizeLight = true; + private void optimizeLight() { + this.optimizeLight = getBoolean("optimize-light", optimizeLight); + } + + public boolean antiXray; + public boolean asynchronous; + public EngineMode engineMode; + public ChunkEdgeMode chunkEdgeMode; + public int maxChunkSectionIndex; + public int updateRadius; + public List hiddenBlocks; + public List replacementBlocks; + private void antiXray() { + antiXray = getBoolean("anti-xray.enabled", false); + asynchronous = true; + engineMode = EngineMode.getById(getInt("anti-xray.engine-mode", EngineMode.HIDE.getId())); + engineMode = engineMode == null ? EngineMode.HIDE : engineMode; + chunkEdgeMode = ChunkEdgeMode.getById(getInt("anti-xray.chunk-edge-mode", ChunkEdgeMode.WAIT.getId())); + chunkEdgeMode = chunkEdgeMode == null ? ChunkEdgeMode.DEFAULT : chunkEdgeMode; + + if (chunkEdgeMode != ChunkEdgeMode.WAIT) { + log("Migrating anti-xray chunk edge mode to " + ChunkEdgeMode.WAIT + " (" + ChunkEdgeMode.WAIT.getId() + ")"); + chunkEdgeMode = ChunkEdgeMode.WAIT; + set("anti-xray.chunk-edge-mode", ChunkEdgeMode.WAIT.getId()); + } + + maxChunkSectionIndex = getInt("anti-xray.max-chunk-section-index", 3); + maxChunkSectionIndex = maxChunkSectionIndex > 15 ? 15 : maxChunkSectionIndex; + updateRadius = getInt("anti-xray.update-radius", 2); + hiddenBlocks = getList("anti-xray.hidden-blocks", Arrays.asList("gold_ore", "iron_ore", "coal_ore", "lapis_ore", "mossy_cobblestone", "obsidian", "chest", "diamond_ore", "redstone_ore", "lit_redstone_ore", "clay", "emerald_ore", "ender_chest")); + replacementBlocks = getList("anti-xray.replacement-blocks", Arrays.asList("stone", "planks")); + log("Anti-Xray: " + (antiXray ? "enabled" : "disabled") + " / Engine Mode: " + engineMode.getDescription() + " / Chunk Edge Mode: " + chunkEdgeMode.getDescription() + " / Up to " + ((maxChunkSectionIndex + 1) * 16) + " blocks / Update Radius: " + updateRadius); + } + + public boolean preventMovingIntoUnloadedChunks = false; + private void preventMovingIntoUnloadedChunks() { + preventMovingIntoUnloadedChunks = getBoolean("prevent-moving-into-unloaded-chunks", false); + } + + public boolean useEigencraftRedstone = false; + private void useEigencraftRedstone() { + useEigencraftRedstone = this.getBoolean("use-faster-eigencraft-redstone", false); + if (useEigencraftRedstone) { + log("Using Eigencraft redstone algorithm by theosib."); + } else { + log("Using vanilla redstone algorithm."); + } + } +} diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldEntityList.java b/src/main/java/com/destroystokyo/paper/PaperWorldEntityList.java new file mode 100644 index 000000000000..a10a5bc138c4 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/PaperWorldEntityList.java @@ -0,0 +1,121 @@ +package com.destroystokyo.paper; + +import net.minecraft.server.Entity; +import net.minecraft.server.EntityInsentient; +import net.minecraft.server.EnumCreatureType; +import net.minecraft.server.IAnimal; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.World; +import net.minecraft.server.WorldServer; + +import java.util.ArrayList; +import java.util.Collection; + +public class PaperWorldEntityList extends ArrayList { + + private final WorldServer world; + private final int[] entityCounts = new int[EnumCreatureType.values().length]; + + + public PaperWorldEntityList(World world) { + this.world = (WorldServer) world; + } + + @Override + public boolean addAll(Collection c) { + for (Entity e : c) { + updateEntityCount(e, 1); + } + + return super.addAll(c); + } + + @Override + public boolean removeAll(Collection c) { + for (Object e : c) { + if (e instanceof Entity && ((Entity) e).getWorld() == world) { + updateEntityCount((Entity) e, -1); + } + } + + return super.removeAll(c); + } + + @Override + public boolean add(Entity e) { + updateEntityCount(e, 1); + + return super.add(e); + } + + @Override + public Entity remove(int index) { + guard(); + Entity entity = super.remove(index); + if (entity != null) updateEntityCount(entity, -1); + return entity; + } + + @Override + public boolean remove(Object o) { + guard(); + if (super.remove(o)) { + updateEntityCount((Entity) o, -1); + return true; + } + return false; + } + + private void guard() { + if (world.guardEntityList) { + throw new java.util.ConcurrentModificationException(); + } + } + + public int getCreatureCount(EnumCreatureType type) { + return entityCounts[type.ordinal()]; + } + + private void updateEntityCount(EnumCreatureType type, int amt) { + int count = entityCounts[type.ordinal()]; + + count += amt; + + if (count < 0) { + MinecraftServer.LOGGER.error("Paper - Entity count cache has gone negative"); + count = 0; + } + + entityCounts[type.ordinal()] = count; + } + + public void updateEntityCount(Entity entity, int amt) { + if (!(entity instanceof IAnimal)) return; + + if (entity instanceof EntityInsentient) { + EntityInsentient entityinsentient = (EntityInsentient) entity; + if (amt > 0 && entityinsentient.isTypeNotPersistent() && entityinsentient.isPersistent()) { + return; + } + } + if (amt < 0) { + if (!entity.hasBeenCounted) { + return; + } + // Only remove once, we remove from if the entity list is guarded, but may be called later + entity.hasBeenCounted = false; + } else { + if (entity.hasBeenCounted) { + return; + } + entity.hasBeenCounted = true; + } + + for (EnumCreatureType type : EnumCreatureType.values()) { + if (type.matches(entity)) { + updateEntityCount(type, amt); + break; + } + } + } +} diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldMap.java b/src/main/java/com/destroystokyo/paper/PaperWorldMap.java new file mode 100644 index 000000000000..af9e4455c668 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/PaperWorldMap.java @@ -0,0 +1,191 @@ +package com.destroystokyo.paper; + +import net.minecraft.server.DimensionManager; +import net.minecraft.server.WorldServer; + +import javax.annotation.Nonnull; +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class PaperWorldMap extends HashMap { + private final List worlds = new ArrayList<>(); + private final List worldsIterable = new ArrayList() { + @Override + public Iterator iterator() { + Iterator iterator = super.iterator(); + return new Iterator() { + private WorldServer last; + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public WorldServer next() { + this.last = iterator.next(); + return last; + } + + @Override + public void remove() { + worlds.set(last.dimension.getDimensionID()+1, null); + } + }; + } + }; + @Override + public int size() { + return worldsIterable.size(); + } + + @Override + public boolean isEmpty() { + return worldsIterable.isEmpty(); + } + + @Override + public WorldServer get(Object key) { + // Will hit the below method + return key instanceof DimensionManager ? get((DimensionManager) key) : null; + } + + public WorldServer get(DimensionManager key) { + int id = key.getDimensionID()+1; + return worlds.size() > id ? worlds.get(id) : null; + } + + @Override + public boolean containsKey(Object key) { + // will hit below method + return key instanceof DimensionManager && containsKey((DimensionManager) key); + } + public boolean containsKey(DimensionManager key) { + return get(key) != null; + } + + @Override + public WorldServer put(DimensionManager key, WorldServer value) { + while (worlds.size() <= key.getDimensionID()+1) { + worlds.add(null); + } + WorldServer old = worlds.set(key.getDimensionID()+1, value); + if (old != null) { + worldsIterable.remove(old); + } + worldsIterable.add(value); + return old; + } + + @Override + public void putAll(Map m) { + for (Entry e : m.entrySet()) { + put(e.getKey(), e.getValue()); + } + } + + @Override + public WorldServer remove(Object key) { + return key instanceof DimensionManager ? remove((DimensionManager) key) : null; + } + + public WorldServer remove(DimensionManager key) { + WorldServer old; + if (key.getDimensionID()+1 == worlds.size() - 1) { + old = worlds.remove(key.getDimensionID()+1); + } else { + old = worlds.set(key.getDimensionID() + 1, null); + } + if (old != null) { + worldsIterable.remove(old); + } + return old; + } + + @Override + public void clear() { + throw new RuntimeException("What the hell are you doing?"); + } + + @Override + public boolean containsValue(Object value) { + return value instanceof WorldServer && get(((WorldServer) value).dimension) != null; + } + + @Nonnull + @Override + public Set keySet() { + return new AbstractSet() { + @Override + public Iterator iterator() { + Iterator iterator = worldsIterable.iterator(); + return new Iterator() { + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public DimensionManager next() { + return iterator.next().dimension; + } + + @Override + public void remove() { + iterator.remove(); + } + }; + } + + @Override + public int size() { + return worlds.size(); + } + }; + } + + @Override + public Collection values() { + return worldsIterable; + } + + @Override + public Set> entrySet() { + return new AbstractSet>() { + @Override + public Iterator> iterator() { + Iterator iterator = worldsIterable.iterator(); + return new Iterator>() { + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public Entry next() { + WorldServer entry = iterator.next(); + return new SimpleEntry<>(entry.dimension, entry); + } + + @Override + public void remove() { + iterator.remove(); + } + }; + } + + @Override + public int size() { + return worldsIterable.size(); + } + }; + } +} diff --git a/src/main/java/com/destroystokyo/paper/ServerSchedulerReportingWrapper.java b/src/main/java/com/destroystokyo/paper/ServerSchedulerReportingWrapper.java new file mode 100644 index 000000000000..f699ce18ca04 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/ServerSchedulerReportingWrapper.java @@ -0,0 +1,38 @@ +package com.destroystokyo.paper; + +import com.google.common.base.Preconditions; +import org.bukkit.craftbukkit.scheduler.CraftTask; +import com.destroystokyo.paper.event.server.ServerExceptionEvent; +import com.destroystokyo.paper.exception.ServerSchedulerException; + +/** + * Reporting wrapper to catch exceptions not natively + */ +public class ServerSchedulerReportingWrapper implements Runnable { + + private final CraftTask internalTask; + + public ServerSchedulerReportingWrapper(CraftTask internalTask) { + this.internalTask = Preconditions.checkNotNull(internalTask, "internalTask"); + } + + @Override + public void run() { + try { + internalTask.run(); + } catch (RuntimeException e) { + internalTask.getOwner().getServer().getPluginManager().callEvent( + new ServerExceptionEvent(new ServerSchedulerException(e, internalTask)) + ); + throw e; + } catch (Throwable t) { + internalTask.getOwner().getServer().getPluginManager().callEvent( + new ServerExceptionEvent(new ServerSchedulerException(t, internalTask)) + ); //Do not rethrow, since it is not permitted with Runnable#run + } + } + + public CraftTask getInternalTask() { + return internalTask; + } +} diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java new file mode 100644 index 000000000000..1ba8477bf9d0 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java @@ -0,0 +1,45 @@ +package com.destroystokyo.paper.antixray; + +import net.minecraft.server.BlockPosition; +import net.minecraft.server.Chunk; +import net.minecraft.server.ChunkSection; +import net.minecraft.server.EnumDirection; +import net.minecraft.server.IBlockData; +import net.minecraft.server.IChunkAccess; +import net.minecraft.server.IWorldReader; +import net.minecraft.server.PacketPlayOutMapChunk; +import net.minecraft.server.PlayerInteractManager; +import net.minecraft.server.World; + +public class ChunkPacketBlockController { + + public static final ChunkPacketBlockController NO_OPERATION_INSTANCE = new ChunkPacketBlockController(); + + protected ChunkPacketBlockController() { + + } + + public IBlockData[] getPredefinedBlockData(IWorldReader world, IChunkAccess chunk, ChunkSection chunkSection, boolean skyLight, boolean initializeBlocks) { + return null; + } + + public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) { + return true; + } + + public ChunkPacketInfo getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { + return null; + } + + public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo chunkPacketInfo) { + packetPlayOutMapChunk.setReady(true); + } + + public void onBlockChange(World world, BlockPosition blockPosition, IBlockData newBlockData, IBlockData oldBlockData, int flag) { + + } + + public void onPlayerLeftClickBlock(PlayerInteractManager playerInteractManager, BlockPosition blockPosition, EnumDirection enumDirection) { + + } +} diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java new file mode 100644 index 000000000000..e3da35b6ba95 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java @@ -0,0 +1,684 @@ +package com.destroystokyo.paper.antixray; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import net.minecraft.server.IRegistry; +import net.minecraft.server.MinecraftKey; +import org.bukkit.World.Environment; + +import com.destroystokyo.paper.PaperWorldConfig; + +import net.minecraft.server.Block; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.Blocks; +import net.minecraft.server.Chunk; +import net.minecraft.server.ChunkSection; +import net.minecraft.server.DataPalette; +import net.minecraft.server.EnumDirection; +import net.minecraft.server.GeneratorAccess; +import net.minecraft.server.IBlockData; +import net.minecraft.server.IChunkAccess; +import net.minecraft.server.IWorldReader; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.PacketPlayOutMapChunk; +import net.minecraft.server.PlayerInteractManager; +import net.minecraft.server.World; +import net.minecraft.server.WorldServer; + +public class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockController { + + private static ExecutorService executorServiceInstance = null; + private final ExecutorService executorService; + private final boolean asynchronous; + private final EngineMode engineMode; + private final ChunkEdgeMode chunkEdgeMode; + private final int maxChunkSectionIndex; + private final int updateRadius; + private final IBlockData[] predefinedBlockData; + private final IBlockData[] predefinedBlockDataStone; + private final IBlockData[] predefinedBlockDataNetherrack; + private final IBlockData[] predefinedBlockDataEndStone; + private final int[] predefinedBlockDataBitsGlobal; + private final int[] predefinedBlockDataBitsStoneGlobal; + private final int[] predefinedBlockDataBitsNetherrackGlobal; + private final int[] predefinedBlockDataBitsEndStoneGlobal; + private final boolean[] solidGlobal = new boolean[Block.REGISTRY_ID.size()]; + private final boolean[] obfuscateGlobal = new boolean[Block.REGISTRY_ID.size()]; + private final ChunkSection[] emptyNearbyChunkSections = {Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION}; + private final int maxBlockYUpdatePosition; + + public ChunkPacketBlockControllerAntiXray(PaperWorldConfig paperWorldConfig) { + asynchronous = paperWorldConfig.asynchronous; + engineMode = paperWorldConfig.engineMode; + chunkEdgeMode = paperWorldConfig.chunkEdgeMode; + maxChunkSectionIndex = paperWorldConfig.maxChunkSectionIndex; + updateRadius = paperWorldConfig.updateRadius; + + if (asynchronous) { + executorService = getExecutorServiceInstance(); + } else { + executorService = null; + } + + if (engineMode == EngineMode.HIDE) { + predefinedBlockData = null; + predefinedBlockDataStone = new IBlockData[] {Blocks.STONE.getBlockData()}; + predefinedBlockDataNetherrack = new IBlockData[] {Blocks.NETHERRACK.getBlockData()}; + predefinedBlockDataEndStone = new IBlockData[] {Blocks.END_STONE.getBlockData()}; + predefinedBlockDataBitsGlobal = null; + predefinedBlockDataBitsStoneGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.STONE.getBlockData())}; + predefinedBlockDataBitsNetherrackGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.NETHERRACK.getBlockData())}; + predefinedBlockDataBitsEndStoneGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.END_STONE.getBlockData())}; + } else { + Set predefinedBlockDataSet = new HashSet(); + + for (String id : paperWorldConfig.hiddenBlocks) { + Block block = IRegistry.BLOCK.get(new MinecraftKey(id)); + + if (block != null && !block.isTileEntity()) { + predefinedBlockDataSet.add(block.getBlockData()); + } + } + + predefinedBlockData = predefinedBlockDataSet.size() == 0 ? new IBlockData[] {Blocks.DIAMOND_ORE.getBlockData()} : predefinedBlockDataSet.toArray(new IBlockData[predefinedBlockDataSet.size()]); + predefinedBlockDataStone = null; + predefinedBlockDataNetherrack = null; + predefinedBlockDataEndStone = null; + predefinedBlockDataBitsGlobal = new int[predefinedBlockData.length]; + + for (int i = 0; i < predefinedBlockData.length; i++) { + predefinedBlockDataBitsGlobal[i] = ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(predefinedBlockData[i]); + } + + predefinedBlockDataBitsStoneGlobal = null; + predefinedBlockDataBitsNetherrackGlobal = null; + predefinedBlockDataBitsEndStoneGlobal = null; + } + + for (String id : (engineMode == EngineMode.HIDE) ? paperWorldConfig.hiddenBlocks : paperWorldConfig.replacementBlocks) { + Block block = IRegistry.BLOCK.get(new MinecraftKey(id)); + + if (block != null) { + obfuscateGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(block.getBlockData())] = true; + } + } + + for (int i = 0; i < solidGlobal.length; i++) { + IBlockData blockData = ChunkSection.GLOBAL_PALETTE.getObject(i); + + if (blockData != null) { + solidGlobal[i] = blockData.getBlock().isOccluding(blockData) && blockData.getBlock() != Blocks.SPAWNER && blockData.getBlock() != Blocks.BARRIER; + } + } + + this.maxBlockYUpdatePosition = (maxChunkSectionIndex + 1) * 16 + updateRadius - 1; + } + + private static ExecutorService getExecutorServiceInstance() { + if (executorServiceInstance == null) { + executorServiceInstance = Executors.newSingleThreadExecutor(); + } + + return executorServiceInstance; + } + + @Override + public IBlockData[] getPredefinedBlockData(IWorldReader world, IChunkAccess chunk, ChunkSection chunkSection, boolean skyLight, boolean initializeBlocks) { + //Return the block data which should be added to the data palettes so that they can be used for the obfuscation + if (chunkSection.getYPosition() >> 4 <= maxChunkSectionIndex) { + switch (engineMode) { + case HIDE: + if (world instanceof GeneratorAccess) { + switch (((GeneratorAccess) world).getMinecraftWorld().getWorld().getEnvironment()) { + case NETHER: + return predefinedBlockDataNetherrack; + case THE_END: + return predefinedBlockDataEndStone; + default: + return predefinedBlockDataStone; + } + } + + return null; + default: + return predefinedBlockData; + } + } + + return null; + } + + @Override + public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) { + //Load nearby chunks if necessary + if (force) { + // if forced, load NOW; + chunk.world.getChunkAt(chunk.locX - 1, chunk.locZ); + chunk.world.getChunkAt(chunk.locX + 1, chunk.locZ); + chunk.world.getChunkAt(chunk.locX, chunk.locZ - 1); + chunk.world.getChunkAt(chunk.locX, chunk.locZ + 1); + } else if (chunkEdgeMode == ChunkEdgeMode.WAIT && !force) { + if (chunk.world.getChunkIfLoaded(chunk.locX - 1, chunk.locZ) == null || chunk.world.getChunkIfLoaded(chunk.locX + 1, chunk.locZ) == null || chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ - 1) == null || chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ + 1) == null) { + //Don't create the chunk packet now, wait until nearby chunks are loaded and create it later + return false; + } + } else if (chunkEdgeMode == ChunkEdgeMode.LOAD) { + boolean missingChunk = false; + //noinspection ConstantConditions + missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX - 1, chunk.locZ, true, true, c -> {}) == null; + missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX + 1, chunk.locZ, true, true, c -> {}) == null; + missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX, chunk.locZ - 1, true, true, c -> {}) == null; + missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX, chunk.locZ + 1, true, true, c -> {}) == null; + + if (missingChunk) { + return false; + } + } + + //Create the chunk packet now + return true; + } + + @Override + public ChunkPacketInfoAntiXray getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { + //Return a new instance to collect data and objects in the right state while creating the chunk packet for thread safe access later + ChunkPacketInfoAntiXray chunkPacketInfoAntiXray = new ChunkPacketInfoAntiXray(packetPlayOutMapChunk, chunk, chunkSectionSelector, this); + chunkPacketInfoAntiXray.setNearbyChunks(chunk.world.getChunkIfLoaded(chunk.locX - 1, chunk.locZ), chunk.world.getChunkIfLoaded(chunk.locX + 1, chunk.locZ), chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ - 1), chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ + 1)); + return chunkPacketInfoAntiXray; + } + + @Override + public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo chunkPacketInfo) { + if (asynchronous) { + executorService.submit((ChunkPacketInfoAntiXray) chunkPacketInfo); + } else { + obfuscate((ChunkPacketInfoAntiXray) chunkPacketInfo); + } + } + + //Actually these fields should be variables inside the obfuscate method but in sync mode or with SingleThreadExecutor in async mode it's okay + private int[] predefinedBlockDataBits; + private final boolean[] solid = new boolean[Block.REGISTRY_ID.size()]; + private final boolean[] obfuscate = new boolean[Block.REGISTRY_ID.size()]; + //These boolean arrays represent chunk layers, true means don't obfuscate, false means obfuscate + private boolean[][] current = new boolean[16][16]; + private boolean[][] next = new boolean[16][16]; + private boolean[][] nextNext = new boolean[16][16]; + private final DataBitsReader dataBitsReader = new DataBitsReader(); + private final DataBitsWriter dataBitsWriter = new DataBitsWriter(); + private final ChunkSection[] nearbyChunkSections = new ChunkSection[4]; + + public void obfuscate(ChunkPacketInfoAntiXray chunkPacketInfoAntiXray) { + boolean[] solidTemp = null; + boolean[] obfuscateTemp = null; + dataBitsReader.setDataBits(chunkPacketInfoAntiXray.getData()); + dataBitsWriter.setDataBits(chunkPacketInfoAntiXray.getData()); + int counter = 0; + + for (int chunkSectionIndex = 0; chunkSectionIndex <= maxChunkSectionIndex; chunkSectionIndex++) { + if (chunkPacketInfoAntiXray.isWritten(chunkSectionIndex) && chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex) != null) { + int[] predefinedBlockDataBitsTemp; + + if (chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex) == ChunkSection.GLOBAL_PALETTE) { + predefinedBlockDataBitsTemp = engineMode == EngineMode.HIDE ? chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.NETHER ? predefinedBlockDataBitsNetherrackGlobal : chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.THE_END ? predefinedBlockDataBitsEndStoneGlobal : predefinedBlockDataBitsStoneGlobal : predefinedBlockDataBitsGlobal; + } else { + predefinedBlockDataBitsTemp = predefinedBlockDataBits == null ? predefinedBlockDataBits = engineMode == EngineMode.HIDE ? new int[1] : new int[predefinedBlockData.length] : predefinedBlockDataBits; + + for (int i = 0; i < predefinedBlockDataBitsTemp.length; i++) { + predefinedBlockDataBitsTemp[i] = chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex).getOrCreateIdFor(chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex)[i]); + } + } + + dataBitsWriter.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex)); + + //Check if the chunk section below was not obfuscated + if (chunkSectionIndex == 0 || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex - 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex - 1) == null) { + //If so, initialize some stuff + dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex)); + dataBitsReader.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex)); + solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), solid, solidGlobal); + obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), obfuscate, obfuscateGlobal); + //Read the blocks of the upper layer of the chunk section below if it exists + ChunkSection belowChunkSection = null; + boolean skipFirstLayer = chunkSectionIndex == 0 || (belowChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex - 1]) == Chunk.EMPTY_CHUNK_SECTION; + + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + current[z][x] = true; + next[z][x] = skipFirstLayer || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(belowChunkSection.getType(x, 15, z))]; + } + } + + //Abuse the obfuscateLayer method to read the blocks of the first layer of the current chunk section + dataBitsWriter.setBitsPerObject(0); + obfuscateLayer(-1, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, emptyNearbyChunkSections, counter); + } + + dataBitsWriter.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex)); + nearbyChunkSections[0] = chunkPacketInfoAntiXray.getNearbyChunks()[0] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[0].getSections()[chunkSectionIndex]; + nearbyChunkSections[1] = chunkPacketInfoAntiXray.getNearbyChunks()[1] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[1].getSections()[chunkSectionIndex]; + nearbyChunkSections[2] = chunkPacketInfoAntiXray.getNearbyChunks()[2] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[2].getSections()[chunkSectionIndex]; + nearbyChunkSections[3] = chunkPacketInfoAntiXray.getNearbyChunks()[3] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[3].getSections()[chunkSectionIndex]; + + //Obfuscate all layers of the current chunk section except the upper one + for (int y = 0; y < 15; y++) { + boolean[][] temp = current; + current = next; + next = nextNext; + nextNext = temp; + counter = obfuscateLayer(y, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); + } + + //Check if the chunk section above doesn't need obfuscation + if (chunkSectionIndex == maxChunkSectionIndex || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex + 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex + 1) == null) { + //If so, obfuscate the upper layer of the current chunk section by reading blocks of the first layer from the chunk section above if it exists + ChunkSection aboveChunkSection; + + if (chunkSectionIndex != 15 && (aboveChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex + 1]) != Chunk.EMPTY_CHUNK_SECTION) { + boolean[][] temp = current; + current = next; + next = nextNext; + nextNext = temp; + + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + if (!solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(aboveChunkSection.getType(x, 0, z))]) { + current[z][x] = true; + } + } + } + + //There is nothing to read anymore + dataBitsReader.setBitsPerObject(0); + solid[0] = true; + counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solid, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); + } + } else { + //If not, initialize the reader and other stuff for the chunk section above to obfuscate the upper layer of the current chunk section + dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex + 1)); + dataBitsReader.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex + 1)); + solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), solid, solidGlobal); + obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), obfuscate, obfuscateGlobal); + boolean[][] temp = current; + current = next; + next = nextNext; + nextNext = temp; + counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); + } + + dataBitsWriter.finish(); + } + } + + chunkPacketInfoAntiXray.getPacketPlayOutMapChunk().setReady(true); + } + + private int obfuscateLayer(int y, DataBitsReader dataBitsReader, DataBitsWriter dataBitsWriter, boolean[] solid, boolean[] obfuscate, int[] predefinedBlockDataBits, boolean[][] current, boolean[][] next, boolean[][] nextNext, ChunkSection[] nearbyChunkSections, int counter) { + //First block of first line + int dataBits = dataBitsReader.read(); + + if (nextNext[0][0] = !solid[dataBits]) { + dataBitsWriter.skip(); + next[0][1] = true; + next[1][0] = true; + } else { + if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(0, y, 15))] || nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, 0))] || current[0][0]) { + dataBitsWriter.skip(); + } else { + if (counter >= predefinedBlockDataBits.length) { + counter = 0; + } + + dataBitsWriter.write(predefinedBlockDataBits[counter++]); + } + } + + if (!obfuscate[dataBits]) { + next[0][0] = true; + } + + //First line + for (int x = 1; x < 15; x++) { + dataBits = dataBitsReader.read(); + + if (nextNext[0][x] = !solid[dataBits]) { + dataBitsWriter.skip(); + next[0][x - 1] = true; + next[0][x + 1] = true; + next[1][x] = true; + } else { + if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(x, y, 15))] || current[0][x]) { + dataBitsWriter.skip(); + } else { + if (counter >= predefinedBlockDataBits.length) { + counter = 0; + } + + dataBitsWriter.write(predefinedBlockDataBits[counter++]); + } + } + + if (!obfuscate[dataBits]) { + next[0][x] = true; + } + } + + //Last block of first line + dataBits = dataBitsReader.read(); + + if (nextNext[0][15] = !solid[dataBits]) { + dataBitsWriter.skip(); + next[0][14] = true; + next[1][15] = true; + } else { + if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(15, y, 15))] || nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, 0))] || current[0][15]) { + dataBitsWriter.skip(); + } else { + if (counter >= predefinedBlockDataBits.length) { + counter = 0; + } + + dataBitsWriter.write(predefinedBlockDataBits[counter++]); + } + } + + if (!obfuscate[dataBits]) { + next[0][15] = true; + } + + //All inner lines + for (int z = 1; z < 15; z++) { + //First block + dataBits = dataBitsReader.read(); + + if (nextNext[z][0] = !solid[dataBits]) { + dataBitsWriter.skip(); + next[z][1] = true; + next[z - 1][0] = true; + next[z + 1][0] = true; + } else { + if (nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, z))] || current[z][0]) { + dataBitsWriter.skip(); + } else { + if (counter >= predefinedBlockDataBits.length) { + counter = 0; + } + + dataBitsWriter.write(predefinedBlockDataBits[counter++]); + } + } + + if (!obfuscate[dataBits]) { + next[z][0] = true; + } + + //All inner blocks + for (int x = 1; x < 15; x++) { + dataBits = dataBitsReader.read(); + + if (nextNext[z][x] = !solid[dataBits]) { + dataBitsWriter.skip(); + next[z][x - 1] = true; + next[z][x + 1] = true; + next[z - 1][x] = true; + next[z + 1][x] = true; + } else { + if (current[z][x]) { + dataBitsWriter.skip(); + } else { + if (counter >= predefinedBlockDataBits.length) { + counter = 0; + } + + dataBitsWriter.write(predefinedBlockDataBits[counter++]); + } + } + + if (!obfuscate[dataBits]) { + next[z][x] = true; + } + } + + //Last block + dataBits = dataBitsReader.read(); + + if (nextNext[z][15] = !solid[dataBits]) { + dataBitsWriter.skip(); + next[z][14] = true; + next[z - 1][15] = true; + next[z + 1][15] = true; + } else { + if (nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, z))] || current[z][15]) { + dataBitsWriter.skip(); + } else { + if (counter >= predefinedBlockDataBits.length) { + counter = 0; + } + + dataBitsWriter.write(predefinedBlockDataBits[counter++]); + } + } + + if (!obfuscate[dataBits]) { + next[z][15] = true; + } + } + + //First block of last line + dataBits = dataBitsReader.read(); + + if (nextNext[15][0] = !solid[dataBits]) { + dataBitsWriter.skip(); + next[15][1] = true; + next[14][0] = true; + } else { + if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(0, y, 0))] || nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, 15))] || current[15][0]) { + dataBitsWriter.skip(); + } else { + if (counter >= predefinedBlockDataBits.length) { + counter = 0; + } + + dataBitsWriter.write(predefinedBlockDataBits[counter++]); + } + } + + if (!obfuscate[dataBits]) { + next[15][0] = true; + } + + //Last line + for (int x = 1; x < 15; x++) { + dataBits = dataBitsReader.read(); + + if (nextNext[15][x] = !solid[dataBits]) { + dataBitsWriter.skip(); + next[15][x - 1] = true; + next[15][x + 1] = true; + next[14][x] = true; + } else { + if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(x, y, 0))] || current[15][x]) { + dataBitsWriter.skip(); + } else { + if (counter >= predefinedBlockDataBits.length) { + counter = 0; + } + + dataBitsWriter.write(predefinedBlockDataBits[counter++]); + } + } + + if (!obfuscate[dataBits]) { + next[15][x] = true; + } + } + + //Last block of last line + dataBits = dataBitsReader.read(); + + if (nextNext[15][15] = !solid[dataBits]) { + dataBitsWriter.skip(); + next[15][14] = true; + next[14][15] = true; + } else { + if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(15, y, 0))] || nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, 15))] || current[15][15]) { + dataBitsWriter.skip(); + } else { + if (counter >= predefinedBlockDataBits.length) { + counter = 0; + } + + dataBitsWriter.write(predefinedBlockDataBits[counter++]); + } + } + + if (!obfuscate[dataBits]) { + next[15][15] = true; + } + + return counter; + } + + private boolean[] readDataPalette(DataPalette dataPalette, boolean[] temp, boolean[] global) { + if (dataPalette == ChunkSection.GLOBAL_PALETTE) { + return global; + } + + IBlockData blockData; + + for (int i = 0; (blockData = dataPalette.getObject(i)) != null; i++) { + temp[i] = global[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(blockData)]; + } + + return temp; + } + + @Override + public void onBlockChange(World world, BlockPosition blockPosition, IBlockData newBlockData, IBlockData oldBlockData, int flag) { + if (oldBlockData != null && solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(oldBlockData)] && !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(newBlockData)] && blockPosition.getY() <= maxBlockYUpdatePosition) { + updateNearbyBlocks(world, blockPosition); + } + } + + @Override + public void onPlayerLeftClickBlock(PlayerInteractManager playerInteractManager, BlockPosition blockPosition, EnumDirection enumDirection) { + if (blockPosition.getY() <= maxBlockYUpdatePosition) { + updateNearbyBlocks(playerInteractManager.world, blockPosition); + } + } + + private void updateNearbyBlocks(World world, BlockPosition blockPosition) { + if (updateRadius >= 2) { + BlockPosition temp = blockPosition.west(); + updateBlock(world, temp); + updateBlock(world, temp.west()); + updateBlock(world, temp.down()); + updateBlock(world, temp.up()); + updateBlock(world, temp.north()); + updateBlock(world, temp.south()); + updateBlock(world, temp = blockPosition.east()); + updateBlock(world, temp.east()); + updateBlock(world, temp.down()); + updateBlock(world, temp.up()); + updateBlock(world, temp.north()); + updateBlock(world, temp.south()); + updateBlock(world, temp = blockPosition.down()); + updateBlock(world, temp.down()); + updateBlock(world, temp.north()); + updateBlock(world, temp.south()); + updateBlock(world, temp = blockPosition.up()); + updateBlock(world, temp.up()); + updateBlock(world, temp.north()); + updateBlock(world, temp.south()); + updateBlock(world, temp = blockPosition.north()); + updateBlock(world, temp.north()); + updateBlock(world, temp = blockPosition.south()); + updateBlock(world, temp.south()); + } else if (updateRadius == 1) { + updateBlock(world, blockPosition.west()); + updateBlock(world, blockPosition.east()); + updateBlock(world, blockPosition.down()); + updateBlock(world, blockPosition.up()); + updateBlock(world, blockPosition.north()); + updateBlock(world, blockPosition.south()); + } else { + //Do nothing if updateRadius <= 0 (test mode) + } + } + + private void updateBlock(World world, BlockPosition blockPosition) { + IBlockData blockData = world.getTypeIfLoaded(blockPosition); + + if (blockData != null && obfuscateGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(blockData)]) { + world.notify(blockPosition, blockData, blockData, 3); + } + } + + public enum EngineMode { + + HIDE(1, "hide ores"), + OBFUSCATE(2, "obfuscate"); + + private final int id; + private final String description; + + EngineMode(int id, String description) { + this.id = id; + this.description = description; + } + + public static EngineMode getById(int id) { + for (EngineMode engineMode : values()) { + if (engineMode.id == id) { + return engineMode; + } + } + + return null; + } + + public int getId() { + return id; + } + + public String getDescription() { + return description; + } + } + + public enum ChunkEdgeMode { + + DEFAULT(1, "default"), + WAIT(2, "wait until nearby chunks are loaded"), + LOAD(3, "load nearby chunks"); + + private final int id; + private final String description; + + ChunkEdgeMode(int id, String description) { + this.id = id; + this.description = description; + } + + public static ChunkEdgeMode getById(int id) { + for (ChunkEdgeMode chunkEdgeMode : values()) { + if (chunkEdgeMode.id == id) { + return chunkEdgeMode; + } + } + + return null; + } + + public int getId() { + return id; + } + + public String getDescription() { + return description; + } + } +} diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java new file mode 100644 index 000000000000..a68bace353e7 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java @@ -0,0 +1,81 @@ +package com.destroystokyo.paper.antixray; + +import net.minecraft.server.Chunk; +import net.minecraft.server.DataPalette; +import net.minecraft.server.PacketPlayOutMapChunk; + +public class ChunkPacketInfo { + + private final PacketPlayOutMapChunk packetPlayOutMapChunk; + private final Chunk chunk; + private final int chunkSectionSelector; + private byte[] data; + private final int[] bitsPerObject = new int[16]; + private final Object[] dataPalettes = new Object[16]; + private final int[] dataBitsIndexes = new int[16]; + private final Object[][] predefinedObjects = new Object[16][]; + + public ChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { + this.packetPlayOutMapChunk = packetPlayOutMapChunk; + this.chunk = chunk; + this.chunkSectionSelector = chunkSectionSelector; + } + + public PacketPlayOutMapChunk getPacketPlayOutMapChunk() { + return packetPlayOutMapChunk; + } + + public Chunk getChunk() { + return chunk; + } + + public int getChunkSectionSelector() { + return chunkSectionSelector; + } + + public byte[] getData() { + return data; + } + + public void setData(byte[] data) { + this.data = data; + } + + public int getBitsPerObject(int chunkSectionIndex) { + return bitsPerObject[chunkSectionIndex]; + } + + public void setBitsPerObject(int chunkSectionIndex, int bitsPerObject) { + this.bitsPerObject[chunkSectionIndex] = bitsPerObject; + } + + @SuppressWarnings("unchecked") + public DataPalette getDataPalette(int chunkSectionIndex) { + return (DataPalette) dataPalettes[chunkSectionIndex]; + } + + public void setDataPalette(int chunkSectionIndex, DataPalette dataPalette) { + dataPalettes[chunkSectionIndex] = dataPalette; + } + + public int getOrCreateIdForIndex(int chunkSectionIndex) { + return dataBitsIndexes[chunkSectionIndex]; + } + + public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) { + dataBitsIndexes[chunkSectionIndex] = dataBitsIndex; + } + + @SuppressWarnings("unchecked") + public T[] getPredefinedObjects(int chunkSectionIndex) { + return (T[]) predefinedObjects[chunkSectionIndex]; + } + + public void setPredefinedObjects(int chunkSectionIndex, T[] predefinedObjects) { + this.predefinedObjects[chunkSectionIndex] = predefinedObjects; + } + + public boolean isWritten(int chunkSectionIndex) { + return bitsPerObject[chunkSectionIndex] != 0; + } +} diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java new file mode 100644 index 000000000000..e255a45fa39b --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java @@ -0,0 +1,29 @@ +package com.destroystokyo.paper.antixray; + +import net.minecraft.server.Chunk; +import net.minecraft.server.IBlockData; +import net.minecraft.server.PacketPlayOutMapChunk; + +public class ChunkPacketInfoAntiXray extends ChunkPacketInfo implements Runnable { + + private Chunk[] nearbyChunks; + private final ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray; + + public ChunkPacketInfoAntiXray(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector, ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray) { + super(packetPlayOutMapChunk, chunk, chunkSectionSelector); + this.chunkPacketBlockControllerAntiXray = chunkPacketBlockControllerAntiXray; + } + + public Chunk[] getNearbyChunks() { + return nearbyChunks; + } + + public void setNearbyChunks(Chunk... nearbyChunks) { + this.nearbyChunks = nearbyChunks; + } + + @Override + public void run() { + chunkPacketBlockControllerAntiXray.obfuscate(this); + } +} diff --git a/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java b/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java new file mode 100644 index 000000000000..cc586827aaa6 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java @@ -0,0 +1,56 @@ +package com.destroystokyo.paper.antixray; + +public class DataBitsReader { + + private byte[] dataBits; + private int bitsPerObject; + private int mask; + private int longInDataBitsIndex; + private int bitInLongIndex; + private long current; + + public void setDataBits(byte[] dataBits) { + this.dataBits = dataBits; + } + + public void setBitsPerObject(int bitsPerObject) { + this.bitsPerObject = bitsPerObject; + mask = (1 << bitsPerObject) - 1; + } + + public void setIndex(int index) { + this.longInDataBitsIndex = index; + bitInLongIndex = 0; + init(); + } + + private void init() { + if (dataBits.length > longInDataBitsIndex + 7) { + current = ((((long) dataBits[longInDataBitsIndex]) << 56) + | (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48) + | (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40) + | (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32) + | (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24) + | (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16) + | (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8) + | (((long) dataBits[longInDataBitsIndex + 7] & 0xff))); + } + } + + public int read() { + int value = (int) (current >>> bitInLongIndex) & mask; + bitInLongIndex += bitsPerObject; + + if (bitInLongIndex > 63) { + bitInLongIndex -= 64; + longInDataBitsIndex += 8; + init(); + + if (bitInLongIndex > 0) { + value |= current << bitsPerObject - bitInLongIndex & mask; + } + } + + return value; + } +} diff --git a/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java b/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java new file mode 100644 index 000000000000..37093419cfd4 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java @@ -0,0 +1,84 @@ +package com.destroystokyo.paper.antixray; + +public class DataBitsWriter { + + private byte[] dataBits; + private int bitsPerObject; + private long mask; + private int longInDataBitsIndex; + private int bitInLongIndex; + private long current; + private boolean dirty; + + public void setDataBits(byte[] dataBits) { + this.dataBits = dataBits; + } + + public void setBitsPerObject(int bitsPerObject) { + this.bitsPerObject = bitsPerObject; + mask = (1 << bitsPerObject) - 1; + } + + public void setIndex(int index) { + this.longInDataBitsIndex = index; + bitInLongIndex = 0; + init(); + } + + private void init() { + if (dataBits.length > longInDataBitsIndex + 7) { + current = ((((long) dataBits[longInDataBitsIndex]) << 56) + | (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48) + | (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40) + | (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32) + | (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24) + | (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16) + | (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8) + | (((long) dataBits[longInDataBitsIndex + 7] & 0xff))); + } + + dirty = false; + } + + public void finish() { + if (dirty && dataBits.length > longInDataBitsIndex + 7) { + dataBits[longInDataBitsIndex] = (byte) (current >> 56 & 0xff); + dataBits[longInDataBitsIndex + 1] = (byte) (current >> 48 & 0xff); + dataBits[longInDataBitsIndex + 2] = (byte) (current >> 40 & 0xff); + dataBits[longInDataBitsIndex + 3] = (byte) (current >> 32 & 0xff); + dataBits[longInDataBitsIndex + 4] = (byte) (current >> 24 & 0xff); + dataBits[longInDataBitsIndex + 5] = (byte) (current >> 16 & 0xff); + dataBits[longInDataBitsIndex + 6] = (byte) (current >> 8 & 0xff); + dataBits[longInDataBitsIndex + 7] = (byte) (current & 0xff); + } + } + + public void write(int value) { + current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex; + dirty = true; + bitInLongIndex += bitsPerObject; + + if (bitInLongIndex > 63) { + finish(); + bitInLongIndex -= 64; + longInDataBitsIndex += 8; + init(); + + if (bitInLongIndex > 0) { + current = current & ~(mask >>> bitsPerObject - bitInLongIndex) | (value & mask) >>> bitsPerObject - bitInLongIndex; + dirty = true; + } + } + } + + public void skip() { + bitInLongIndex += bitsPerObject; + + if (bitInLongIndex > 63) { + finish(); + bitInLongIndex -= 64; + longInDataBitsIndex += 8; + init(); + } + } +} diff --git a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java new file mode 100644 index 000000000000..688b4715ebd1 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java @@ -0,0 +1,40 @@ +package com.destroystokyo.paper.console; + +import net.minecraft.server.DedicatedServer; +import net.minecrell.terminalconsole.SimpleTerminalConsole; +import org.bukkit.craftbukkit.command.ConsoleCommandCompleter; +import org.jline.reader.LineReader; +import org.jline.reader.LineReaderBuilder; + +public final class PaperConsole extends SimpleTerminalConsole { + + private final DedicatedServer server; + + public PaperConsole(DedicatedServer server) { + this.server = server; + } + + @Override + protected LineReader buildReader(LineReaderBuilder builder) { + return super.buildReader(builder + .appName("Paper") + .completer(new ConsoleCommandCompleter(this.server)) + ); + } + + @Override + protected boolean isRunning() { + return !this.server.isStopped() && this.server.isRunning(); + } + + @Override + protected void runCommand(String command) { + this.server.issueCommand(command, this.server.getServerCommandListener()); + } + + @Override + protected void shutdown() { + this.server.safeShutdown(); + } + +} diff --git a/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java b/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java new file mode 100644 index 000000000000..685deaa0e5d1 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java @@ -0,0 +1,17 @@ +package com.destroystokyo.paper.console; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bukkit.craftbukkit.command.CraftConsoleCommandSender; + +public class TerminalConsoleCommandSender extends CraftConsoleCommandSender { + + private static final Logger LOGGER = LogManager.getRootLogger(); + + @Override + public void sendRawMessage(String message) { + // TerminalConsoleAppender supports color codes directly in log messages + LOGGER.info(message); + } + +} diff --git a/src/main/java/com/destroystokyo/paper/entity/CraftRangedEntity.java b/src/main/java/com/destroystokyo/paper/entity/CraftRangedEntity.java new file mode 100644 index 000000000000..696660b089ba --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/entity/CraftRangedEntity.java @@ -0,0 +1,19 @@ +package com.destroystokyo.paper.entity; + +import net.minecraft.server.IRangedEntity; +import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.entity.LivingEntity; + +public interface CraftRangedEntity extends RangedEntity { + T getHandle(); + + @Override + default void rangedAttack(LivingEntity target, float charge) { + getHandle().rangedAttack(((CraftLivingEntity) target).getHandle(), charge); + } + + @Override + default void setChargingAttack(boolean raiseHands) { + getHandle().setChargingAttack(raiseHands); + } +} diff --git a/src/main/java/com/destroystokyo/paper/entity/PaperPathfinder.java b/src/main/java/com/destroystokyo/paper/entity/PaperPathfinder.java new file mode 100644 index 000000000000..ed3d86ddd359 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/entity/PaperPathfinder.java @@ -0,0 +1,113 @@ +package com.destroystokyo.paper.entity; + +import net.minecraft.server.EntityInsentient; +import net.minecraft.server.PathEntity; +import net.minecraft.server.PathPoint; +import org.apache.commons.lang.Validate; +import org.bukkit.Location; +import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Mob; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +public class PaperPathfinder implements com.destroystokyo.paper.entity.Pathfinder { + + private final EntityInsentient entity; + + public PaperPathfinder(EntityInsentient entity) { + this.entity = entity; + } + + @Override + public Mob getEntity() { + return entity.getBukkitMob(); + } + + @Override + public void stopPathfinding() { + entity.getNavigation().stopPathfinding(); + } + + @Override + public boolean hasPath() { + return entity.getNavigation().getPathEntity() != null; + } + + @Nullable + @Override + public PathResult getCurrentPath() { + PathEntity path = entity.getNavigation().getPathEntity(); + return path != null ? new PaperPathResult(path) : null; + } + + @Nullable + @Override + public PathResult findPath(Location loc) { + Validate.notNull(loc, "Location can not be null"); + PathEntity path = entity.getNavigation().calculateDestination(loc.getX(), loc.getY(), loc.getZ()); + return path != null ? new PaperPathResult(path) : null; + } + + @Nullable + @Override + public PathResult findPath(LivingEntity target) { + Validate.notNull(target, "Target can not be null"); + PathEntity path = entity.getNavigation().calculateDestination(((CraftLivingEntity) target).getHandle()); + return path != null ? new PaperPathResult(path) : null; + } + + @Override + public boolean moveTo(@Nonnull PathResult path, double speed) { + Validate.notNull(path, "PathResult can not be null"); + PathEntity pathEntity = ((PaperPathResult) path).path; + return entity.getNavigation().setDestination(pathEntity, speed); + } + + public class PaperPathResult implements com.destroystokyo.paper.entity.PaperPathfinder.PathResult { + + private final PathEntity path; + PaperPathResult(PathEntity path) { + this.path = path; + } + + @Nullable + @Override + public Location getFinalPoint() { + PathPoint point = path.getFinalPoint(); + return point != null ? toLoc(point) : null; + } + + @Override + public List getPoints() { + int pathCount = path.getPathCount(); + List points = new ArrayList<>(); + PathPoint[] pathPoints = path.getPoints(); + for (int i = 0; i < pathCount; i++) { + points.add(toLoc(pathPoints[i])); + } + return points; + } + + @Override + public int getNextPointIndex() { + return path.getNextIndex(); + } + + @Nullable + @Override + public Location getNextPoint() { + if (!path.hasNext()) { + return null; + } + return toLoc(path.getPoints()[path.getNextIndex()]); + } + } + + private Location toLoc(PathPoint point) { + return new Location(entity.world.getWorld(), point.getX(), point.getY(), point.getZ()); + } +} diff --git a/src/main/java/com/destroystokyo/paper/loottable/PaperLootableBlockInventory.java b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableBlockInventory.java new file mode 100644 index 000000000000..d6fce3112ebd --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableBlockInventory.java @@ -0,0 +1,33 @@ +package com.destroystokyo.paper.loottable; + +import net.minecraft.server.BlockPosition; +import net.minecraft.server.TileEntityLootable; +import net.minecraft.server.World; +import org.bukkit.Chunk; +import org.bukkit.block.Block; + +public interface PaperLootableBlockInventory extends LootableBlockInventory, PaperLootableInventory { + + TileEntityLootable getTileEntity(); + + @Override + default LootableInventory getAPILootableInventory() { + return this; + } + + @Override + default World getNMSWorld() { + return getTileEntity().getWorld(); + } + + default Block getBlock() { + final BlockPosition position = getTileEntity().getPosition(); + final Chunk bukkitChunk = getTileEntity().getWorld().getChunkAtWorldCoords(position).bukkitChunk; + return bukkitChunk.getBlock(position.getX(), position.getY(), position.getZ()); + } + + @Override + default PaperLootableInventoryData getLootableData() { + return getTileEntity().lootableData; + } +} diff --git a/src/main/java/com/destroystokyo/paper/loottable/PaperLootableEntityInventory.java b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableEntityInventory.java new file mode 100644 index 000000000000..5e637782d555 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableEntityInventory.java @@ -0,0 +1,28 @@ +package com.destroystokyo.paper.loottable; + +import net.minecraft.server.World; +import org.bukkit.entity.Entity; + +public interface PaperLootableEntityInventory extends LootableEntityInventory, PaperLootableInventory { + + net.minecraft.server.Entity getHandle(); + + @Override + default LootableInventory getAPILootableInventory() { + return this; + } + + default Entity getEntity() { + return getHandle().getBukkitEntity(); + } + + @Override + default World getNMSWorld() { + return getHandle().getWorld(); + } + + @Override + default PaperLootableInventoryData getLootableData() { + return getHandle().lootableData; + } +} diff --git a/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventory.java b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventory.java new file mode 100644 index 000000000000..856843fc917f --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventory.java @@ -0,0 +1,71 @@ +package com.destroystokyo.paper.loottable; + +import net.minecraft.server.World; +import org.bukkit.loot.Lootable; + +import java.util.UUID; + +public interface PaperLootableInventory extends LootableInventory, Lootable { + + PaperLootableInventoryData getLootableData(); + LootableInventory getAPILootableInventory(); + + World getNMSWorld(); + + default org.bukkit.World getBukkitWorld() { + return getNMSWorld().getWorld(); + } + + @Override + default boolean isRefillEnabled() { + return getNMSWorld().paperConfig.autoReplenishLootables; + } + + @Override + default boolean hasBeenFilled() { + return getLastFilled() != -1; + } + + @Override + default boolean hasPlayerLooted(UUID player) { + return getLootableData().hasPlayerLooted(player); + } + + @Override + default Long getLastLooted(UUID player) { + return getLootableData().getLastLooted(player); + } + + @Override + default boolean setHasPlayerLooted(UUID player, boolean looted) { + final boolean hasLooted = hasPlayerLooted(player); + if (hasLooted != looted) { + getLootableData().setPlayerLootedState(player, looted); + } + return hasLooted; + } + + @Override + default boolean hasPendingRefill() { + long nextRefill = getLootableData().getNextRefill(); + return nextRefill != -1 && nextRefill > getLootableData().getLastFill(); + } + + @Override + default long getLastFilled() { + return getLootableData().getLastFill(); + } + + @Override + default long getNextRefill() { + return getLootableData().getNextRefill(); + } + + @Override + default long setNextRefill(long refillAt) { + if (refillAt < -1) { + refillAt = -1; + } + return getLootableData().setNextRefill(refillAt); + } +} diff --git a/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java new file mode 100644 index 000000000000..b5401eaf9748 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/loottable/PaperLootableInventoryData.java @@ -0,0 +1,179 @@ +package com.destroystokyo.paper.loottable; + +import com.destroystokyo.paper.PaperWorldConfig; +import net.minecraft.server.*; +import org.bukkit.entity.Player; +import org.bukkit.loot.LootTable; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.UUID; + +public class PaperLootableInventoryData { + + private static final Random RANDOM = new Random(); + + private long lastFill = -1; + private long nextRefill = -1; + private int numRefills = 0; + private Map lootedPlayers; + private final PaperLootableInventory lootable; + + public PaperLootableInventoryData(PaperLootableInventory lootable) { + this.lootable = lootable; + } + + long getLastFill() { + return this.lastFill; + } + + long getNextRefill() { + return this.nextRefill; + } + + long setNextRefill(long nextRefill) { + long prev = this.nextRefill; + this.nextRefill = nextRefill; + return prev; + } + + public boolean shouldReplenish(@Nullable EntityHuman player) { + LootTable table = this.lootable.getLootTable(); + + // No Loot Table associated + if (table == null) { + return false; + } + + // ALWAYS process the first fill or if the feature is disabled + if (this.lastFill == -1 || !this.lootable.getNMSWorld().paperConfig.autoReplenishLootables) { + return true; + } + + // Only process refills when a player is set + if (player == null) { + return false; + } + + // Chest is not scheduled for refill + if (this.nextRefill == -1) { + return false; + } + + final PaperWorldConfig paperConfig = this.lootable.getNMSWorld().paperConfig; + + // Check if max refills has been hit + if (paperConfig.maxLootableRefills != -1 && this.numRefills >= paperConfig.maxLootableRefills) { + return false; + } + + // Refill has not been reached + if (this.nextRefill > System.currentTimeMillis()) { + return false; + } + + + final Player bukkitPlayer = (Player) player.getBukkitEntity(); + LootableInventoryReplenishEvent event = new LootableInventoryReplenishEvent(bukkitPlayer, lootable.getAPILootableInventory()); + if (paperConfig.restrictPlayerReloot && hasPlayerLooted(player.getUniqueID())) { + event.setCancelled(true); + } + return event.callEvent(); + } + public void processRefill(@Nullable EntityHuman player) { + this.lastFill = System.currentTimeMillis(); + final PaperWorldConfig paperConfig = this.lootable.getNMSWorld().paperConfig; + if (paperConfig.autoReplenishLootables) { + int min = paperConfig.lootableRegenMin; + int max = paperConfig.lootableRegenMax; + this.nextRefill = this.lastFill + (min + RANDOM.nextInt(max - min + 1)) * 1000L; + this.numRefills++; + if (paperConfig.changeLootTableSeedOnFill) { + this.lootable.setSeed(0); + } + if (player != null) { // This means that numRefills can be incremented without a player being in the lootedPlayers list - Seems to be EntityMinecartChest specific + this.setPlayerLootedState(player.getUniqueID(), true); + } + } else { + this.lootable.clearLootTable(); + } + } + + + public void loadNbt(NBTTagCompound base) { + if (!base.hasKeyOfType("Paper.LootableData", 10)) { // 10 = compound + return; + } + NBTTagCompound comp = base.getCompound("Paper.LootableData"); + if (comp.hasKey("lastFill")) { + this.lastFill = comp.getLong("lastFill"); + } + if (comp.hasKey("nextRefill")) { + this.nextRefill = comp.getLong("nextRefill"); + } + + if (comp.hasKey("numRefills")) { + this.numRefills = comp.getInt("numRefills"); + } + if (comp.hasKeyOfType("lootedPlayers", 9)) { // 9 = list + NBTTagList list = comp.getList("lootedPlayers", 10); // 10 = compound + final int size = list.size(); + if (size > 0) { + this.lootedPlayers = new HashMap<>(list.size()); + } + for (int i = 0; i < size; i++) { + final NBTTagCompound cmp = list.getCompound(i); + lootedPlayers.put(cmp.getUUID("UUID"), cmp.getLong("Time")); + } + } + } + public void saveNbt(NBTTagCompound base) { + NBTTagCompound comp = new NBTTagCompound(); + if (this.nextRefill != -1) { + comp.setLong("nextRefill", this.nextRefill); + } + if (this.lastFill != -1) { + comp.setLong("lastFill", this.lastFill); + } + if (this.numRefills != 0) { + comp.setInt("numRefills", this.numRefills); + } + if (this.lootedPlayers != null && !this.lootedPlayers.isEmpty()) { + NBTTagList list = new NBTTagList(); + for (Map.Entry entry : this.lootedPlayers.entrySet()) { + NBTTagCompound cmp = new NBTTagCompound(); + cmp.setUUID("UUID", entry.getKey()); + cmp.setLong("Time", entry.getValue()); + list.add(cmp); + } + comp.set("lootedPlayers", list); + } + + if (!comp.isEmpty()) { + base.set("Paper.LootableData", comp); + } + } + + void setPlayerLootedState(UUID player, boolean looted) { + if (looted && this.lootedPlayers == null) { + this.lootedPlayers = new HashMap<>(); + } + if (looted) { + if (!this.lootedPlayers.containsKey(player)) { + this.lootedPlayers.put(player, System.currentTimeMillis()); + } + } else if (this.lootedPlayers != null) { + this.lootedPlayers.remove(player); + } + } + + boolean hasPlayerLooted(UUID player) { + return this.lootedPlayers != null && this.lootedPlayers.containsKey(player); + } + + Long getLastLooted(UUID player) { + return lootedPlayers != null ? lootedPlayers.get(player) : null; + } +} diff --git a/src/main/java/com/destroystokyo/paper/loottable/PaperMinecartLootableInventory.java b/src/main/java/com/destroystokyo/paper/loottable/PaperMinecartLootableInventory.java new file mode 100644 index 000000000000..f9fbc221bd8f --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/loottable/PaperMinecartLootableInventory.java @@ -0,0 +1,64 @@ +package com.destroystokyo.paper.loottable; + +import net.minecraft.server.Entity; +import net.minecraft.server.EntityMinecartContainer; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.World; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; + +public class PaperMinecartLootableInventory implements PaperLootableEntityInventory { + + private EntityMinecartContainer entity; + + public PaperMinecartLootableInventory(EntityMinecartContainer entity) { + this.entity = entity; + } + + @Override + public org.bukkit.loot.LootTable getLootTable() { + return entity.getLootTableKey() != null ? Bukkit.getLootTable(CraftNamespacedKey.fromMinecraft(entity.getLootTableKey())) : null; + } + + @Override + public void setLootTable(org.bukkit.loot.LootTable table, long seed) { + setLootTable(table); + setSeed(seed); + } + + @Override + public void setSeed(long seed) { + entity.lootTableSeed = seed; + } + + @Override + public long getSeed() { + return entity.lootTableSeed; + } + + @Override + public void setLootTable(org.bukkit.loot.LootTable table) { + MinecraftKey newKey = (table == null) ? null : CraftNamespacedKey.toMinecraft(table.getKey()); + entity.setLootTable(newKey); + } + + @Override + public PaperLootableInventoryData getLootableData() { + return entity.lootableData; + } + + @Override + public Entity getHandle() { + return entity; + } + + @Override + public LootableInventory getAPILootableInventory() { + return (LootableInventory) entity.getBukkitEntity(); + } + + @Override + public World getNMSWorld() { + return entity.world; + } +} diff --git a/src/main/java/com/destroystokyo/paper/loottable/PaperTileEntityLootableInventory.java b/src/main/java/com/destroystokyo/paper/loottable/PaperTileEntityLootableInventory.java new file mode 100644 index 000000000000..d50410532c27 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/loottable/PaperTileEntityLootableInventory.java @@ -0,0 +1,67 @@ +package com.destroystokyo.paper.loottable; + +import net.minecraft.server.MCUtil; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.TileEntityLootable; +import net.minecraft.server.World; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; + +public class PaperTileEntityLootableInventory implements PaperLootableBlockInventory { + private TileEntityLootable tileEntityLootable; + + public PaperTileEntityLootableInventory(TileEntityLootable tileEntityLootable) { + this.tileEntityLootable = tileEntityLootable; + } + + @Override + public org.bukkit.loot.LootTable getLootTable() { + return tileEntityLootable.getLootTableKey() != null ? Bukkit.getLootTable(CraftNamespacedKey.fromMinecraft(tileEntityLootable.getLootTableKey())) : null; + } + + @Override + public void setLootTable(org.bukkit.loot.LootTable table, long seed) { + setLootTable(table); + setSeed(seed); + } + + @Override + public void setLootTable(org.bukkit.loot.LootTable table) { + MinecraftKey newKey = (table == null) ? null : CraftNamespacedKey.toMinecraft(table.getKey()); + tileEntityLootable.setLootTable(newKey); + } + + @Override + public void setSeed(long seed) { + tileEntityLootable.setSeed(seed); + } + + @Override + public long getSeed() { + return tileEntityLootable.getSeed(); + } + + @Override + public PaperLootableInventoryData getLootableData() { + return tileEntityLootable.lootableData; + } + + @Override + public TileEntityLootable getTileEntity() { + return tileEntityLootable; + } + + @Override + public LootableInventory getAPILootableInventory() { + World world = tileEntityLootable.getWorld(); + if (world == null) { + return null; + } + return (LootableInventory) getBukkitWorld().getBlockAt(MCUtil.toLocation(world, tileEntityLootable.getPosition())).getState(); + } + + @Override + public World getNMSWorld() { + return tileEntityLootable.getWorld(); + } +} diff --git a/src/main/java/com/destroystokyo/paper/network/PaperLegacyStatusClient.java b/src/main/java/com/destroystokyo/paper/network/PaperLegacyStatusClient.java new file mode 100644 index 000000000000..74c012fd4049 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/network/PaperLegacyStatusClient.java @@ -0,0 +1,73 @@ +package com.destroystokyo.paper.network; + +import com.destroystokyo.paper.event.server.PaperServerListPingEvent; +import net.minecraft.server.MinecraftServer; +import org.apache.commons.lang3.StringUtils; +import org.bukkit.ChatColor; + +import java.net.InetSocketAddress; + +import javax.annotation.Nullable; + +public final class PaperLegacyStatusClient implements StatusClient { + + private final InetSocketAddress address; + private final int protocolVersion; + @Nullable private final InetSocketAddress virtualHost; + + private PaperLegacyStatusClient(InetSocketAddress address, int protocolVersion, @Nullable InetSocketAddress virtualHost) { + this.address = address; + this.protocolVersion = protocolVersion; + this.virtualHost = virtualHost; + } + + @Override + public InetSocketAddress getAddress() { + return this.address; + } + + @Override + public int getProtocolVersion() { + return this.protocolVersion; + } + + @Nullable + @Override + public InetSocketAddress getVirtualHost() { + return this.virtualHost; + } + + @Override + public boolean isLegacy() { + return true; + } + + public static PaperServerListPingEvent processRequest(MinecraftServer server, + InetSocketAddress address, int protocolVersion, @Nullable InetSocketAddress virtualHost) { + + PaperServerListPingEvent event = new PaperServerListPingEventImpl(server, + new PaperLegacyStatusClient(address, protocolVersion, virtualHost), Byte.MAX_VALUE, null); + server.server.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return null; + } + + return event; + } + + public static String getMotd(PaperServerListPingEvent event) { + return getFirstLine(event.getMotd()); + } + + public static String getUnformattedMotd(PaperServerListPingEvent event) { + // Strip color codes and all other occurrences of the color char (because it's used as delimiter) + return getFirstLine(StringUtils.remove(ChatColor.stripColor(event.getMotd()), ChatColor.COLOR_CHAR)); + } + + private static String getFirstLine(String s) { + int pos = s.indexOf('\n'); + return pos >= 0 ? s.substring(0, pos) : s; + } + +} diff --git a/src/main/java/com/destroystokyo/paper/network/PaperNetworkClient.java b/src/main/java/com/destroystokyo/paper/network/PaperNetworkClient.java new file mode 100644 index 000000000000..5caca6439d21 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/network/PaperNetworkClient.java @@ -0,0 +1,50 @@ +package com.destroystokyo.paper.network; + +import net.minecraft.server.NetworkManager; + +import java.net.InetSocketAddress; + +import javax.annotation.Nullable; + +public class PaperNetworkClient implements NetworkClient { + + private final NetworkManager networkManager; + + PaperNetworkClient(NetworkManager networkManager) { + this.networkManager = networkManager; + } + + @Override + public InetSocketAddress getAddress() { + return (InetSocketAddress) this.networkManager.getSocketAddress(); + } + + @Override + public int getProtocolVersion() { + return this.networkManager.protocolVersion; + } + + @Nullable + @Override + public InetSocketAddress getVirtualHost() { + return this.networkManager.virtualHost; + } + + public static InetSocketAddress prepareVirtualHost(String host, int port) { + int len = host.length(); + + // FML appends a marker to the host to recognize FML clients (\0FML\0) + int pos = host.indexOf('\0'); + if (pos >= 0) { + len = pos; + } + + // When clients connect with a SRV record, their host contains a trailing '.' + if (len > 0 && host.charAt(len - 1) == '.') { + len--; + } + + return InetSocketAddress.createUnresolved(host.substring(0, len), port); + } + +} diff --git a/src/main/java/com/destroystokyo/paper/network/PaperServerListPingEventImpl.java b/src/main/java/com/destroystokyo/paper/network/PaperServerListPingEventImpl.java new file mode 100644 index 000000000000..c1a8e295b665 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/network/PaperServerListPingEventImpl.java @@ -0,0 +1,31 @@ +package com.destroystokyo.paper.network; + +import com.destroystokyo.paper.event.server.PaperServerListPingEvent; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.MinecraftServer; +import org.bukkit.entity.Player; +import org.bukkit.util.CachedServerIcon; + +import javax.annotation.Nullable; + +class PaperServerListPingEventImpl extends PaperServerListPingEvent { + + private final MinecraftServer server; + + PaperServerListPingEventImpl(MinecraftServer server, StatusClient client, int protocolVersion, @Nullable CachedServerIcon icon) { + super(client, server.getMotd(), server.getPlayerCount(), server.getMaxPlayers(), + server.getServerModName() + ' ' + server.getVersion(), protocolVersion, icon); + this.server = server; + } + + @Override + protected final Object[] getOnlinePlayers() { + return this.server.getPlayerList().players.toArray(); + } + + @Override + protected final Player getBukkitPlayer(Object player) { + return ((EntityPlayer) player).getBukkitEntity(); + } + +} diff --git a/src/main/java/com/destroystokyo/paper/network/PaperStatusClient.java b/src/main/java/com/destroystokyo/paper/network/PaperStatusClient.java new file mode 100644 index 000000000000..a2a409e635dd --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/network/PaperStatusClient.java @@ -0,0 +1,11 @@ +package com.destroystokyo.paper.network; + +import net.minecraft.server.NetworkManager; + +class PaperStatusClient extends PaperNetworkClient implements StatusClient { + + PaperStatusClient(NetworkManager networkManager) { + super(networkManager); + } + +} diff --git a/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java b/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java new file mode 100644 index 000000000000..a85466bc7e0a --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java @@ -0,0 +1,112 @@ +package com.destroystokyo.paper.network; + +import com.destroystokyo.paper.profile.CraftPlayerProfile; +import com.destroystokyo.paper.profile.PlayerProfile; +import com.google.common.base.MoreObjects; +import com.google.common.base.Strings; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.ChatComponentText; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.NetworkManager; +import net.minecraft.server.PacketStatusOutServerInfo; +import net.minecraft.server.ServerPing; + +import java.util.List; +import java.util.UUID; + +import javax.annotation.Nonnull; + +public final class StandardPaperServerListPingEventImpl extends PaperServerListPingEventImpl { + + private static final GameProfile[] EMPTY_PROFILES = new GameProfile[0]; + private static final UUID FAKE_UUID = new UUID(0, 0); + + private GameProfile[] originalSample; + + private StandardPaperServerListPingEventImpl(MinecraftServer server, NetworkManager networkManager, ServerPing ping) { + super(server, new PaperStatusClient(networkManager), ping.getServerData() != null ? ping.getServerData().getProtocolVersion() : -1, server.server.getServerIcon()); + this.originalSample = ping.getPlayers() == null ? null : ping.getPlayers().getSample(); // GH-1473 - pre-tick race condition NPE + } + + @Nonnull + @Override + public List getPlayerSample() { + List sample = super.getPlayerSample(); + + if (this.originalSample != null) { + for (GameProfile profile : this.originalSample) { + sample.add(CraftPlayerProfile.asBukkitCopy(profile)); + } + this.originalSample = null; + } + + return sample; + } + + private GameProfile[] getPlayerSampleHandle() { + if (this.originalSample != null) { + return this.originalSample; + } + + List entries = super.getPlayerSample(); + if (entries.isEmpty()) { + return EMPTY_PROFILES; + } + + GameProfile[] profiles = new GameProfile[entries.size()]; + for (int i = 0; i < profiles.length; i++) { + /* + * Avoid null UUIDs/names since that will make the response invalid + * on the client. + * Instead, fall back to a fake/empty UUID and an empty string as name. + * This can be used to create custom lines in the player list that do not + * refer to a specific player. + */ + + PlayerProfile profile = entries.get(i); + if (profile.getId() != null && profile.getName() != null) { + profiles[i] = CraftPlayerProfile.asAuthlib(profile); + } else { + profiles[i] = new GameProfile(MoreObjects.firstNonNull(profile.getId(), FAKE_UUID), Strings.nullToEmpty(profile.getName())); + } + } + + return profiles; + } + + @SuppressWarnings("deprecation") + public static void processRequest(MinecraftServer server, NetworkManager networkManager) { + StandardPaperServerListPingEventImpl event = new StandardPaperServerListPingEventImpl(server, networkManager, server.getServerPing()); + server.server.getPluginManager().callEvent(event); + + // Close connection immediately if event is cancelled + if (event.isCancelled()) { + networkManager.close(null); + return; + } + + // Setup response + ServerPing ping = new ServerPing(); + + // Description + ping.setMOTD(new ChatComponentText(event.getMotd())); + + // Players + if (!event.shouldHidePlayers()) { + ping.setPlayerSample(new ServerPing.ServerPingPlayerSample(event.getMaxPlayers(), event.getNumPlayers())); + ping.getPlayers().setSample(event.getPlayerSampleHandle()); + } + + // Version + ping.setServerInfo(new ServerPing.ServerData(event.getVersion(), event.getProtocolVersion())); + + // Favicon + if (event.getServerIcon() != null) { + ping.setFavicon(event.getServerIcon().getData()); + } + + // Send response + networkManager.sendPacket(new PacketStatusOutServerInfo(ping)); + } + +} diff --git a/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java new file mode 100644 index 000000000000..b151a13c1b90 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java @@ -0,0 +1,280 @@ +package com.destroystokyo.paper.profile; + +import com.destroystokyo.paper.PaperConfig; +import com.google.common.base.Charsets; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.UserCache; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.spigotmc.SpigotConfig; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +public class CraftPlayerProfile implements PlayerProfile { + + private GameProfile profile; + private final PropertySet properties = new PropertySet(); + + public CraftPlayerProfile(CraftPlayer player) { + this.profile = player.getHandle().getProfile(); + } + + public CraftPlayerProfile(UUID id, String name) { + this.profile = new GameProfile(id, name); + } + + public CraftPlayerProfile(GameProfile profile) { + this.profile = profile; + } + + @Override + public boolean hasProperty(String property) { + return profile.getProperties().containsKey(property); + } + + @Override + public void setProperty(ProfileProperty property) { + String name = property.getName(); + PropertyMap properties = profile.getProperties(); + properties.removeAll(name); + properties.put(name, new Property(name, property.getValue(), property.getSignature())); + } + + public GameProfile getGameProfile() { + return profile; + } + + @Nullable + @Override + public UUID getId() { + return profile.getId(); + } + + @Override + public UUID setId(@Nullable UUID uuid) { + GameProfile prev = this.profile; + this.profile = new GameProfile(uuid, prev.getName()); + copyProfileProperties(prev, this.profile); + return prev.getId(); + } + + @Nullable + @Override + public String getName() { + return profile.getName(); + } + + @Override + public String setName(@Nullable String name) { + GameProfile prev = this.profile; + this.profile = new GameProfile(prev.getId(), name); + copyProfileProperties(prev, this.profile); + return prev.getName(); + } + + @Nonnull + @Override + public Set getProperties() { + return properties; + } + + @Override + public void setProperties(Collection properties) { + properties.forEach(this::setProperty); + } + + @Override + public void clearProperties() { + profile.getProperties().clear(); + } + + @Override + public boolean removeProperty(String property) { + return !profile.getProperties().removeAll(property).isEmpty(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CraftPlayerProfile that = (CraftPlayerProfile) o; + return Objects.equals(profile, that.profile); + } + + @Override + public int hashCode() { + return profile.hashCode(); + } + + @Override + public String toString() { + return profile.toString(); + } + + @Override + public CraftPlayerProfile clone() { + CraftPlayerProfile clone = new CraftPlayerProfile(this.getId(), this.getName()); + clone.setProperties(getProperties()); + return clone; + } + + @Override + public boolean isComplete() { + return profile.isComplete(); + } + + @Override + public boolean completeFromCache() { + return completeFromCache(false); + } + + public boolean completeFromCache(boolean lookupName) { + if (profile.isComplete()) { + return true; + } + MinecraftServer server = MinecraftServer.getServer(); + String name = profile.getName(); + UserCache userCache = server.getUserCache(); + if (profile.getId() == null) { + final GameProfile profile; + boolean isOnlineMode = server.getOnlineMode() || (SpigotConfig.bungee && PaperConfig.bungeeOnlineMode); + if (isOnlineMode) { + profile = lookupName ? userCache.getProfile(name) : userCache.getProfileIfCached(name); + } else { + // Make an OfflinePlayer using an offline mode UUID since the name has no profile + profile = new GameProfile(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)), name); + } + if (profile != null) { + this.profile = profile; + } + } + + if (profile.getName() == null) { + // If we need textures, skip this check, as we will get it below anyways. + GameProfile profile = userCache.getProfile(this.profile.getId()); + if (profile != null) { + this.profile = profile; + } + } + return this.profile.isComplete(); + } + + public boolean complete(boolean textures) { + MinecraftServer server = MinecraftServer.getServer(); + + boolean isOnlineMode = server.getOnlineMode() || (SpigotConfig.bungee && PaperConfig.bungeeOnlineMode); + boolean isCompleteFromCache = this.completeFromCache(true); + if (isOnlineMode && (!isCompleteFromCache || textures && !hasTextures())) { + GameProfile result = server.getSessionService().fillProfileProperties(profile, true); + if (result != null) { + this.profile = result; + } + } + return profile.isComplete() && (!isOnlineMode || !textures || hasTextures()); + } + + private static void copyProfileProperties(GameProfile source, GameProfile target) { + PropertyMap sourceProperties = source.getProperties(); + if (sourceProperties.isEmpty()) { + return; + } + PropertyMap properties = target.getProperties(); + properties.clear(); + + for (Property property : sourceProperties.values()) { + properties.put(property.getName(), property); + } + } + + private static ProfileProperty toBukkit(Property property) { + return new ProfileProperty(property.getName(), property.getValue(), property.getSignature()); + } + + public static PlayerProfile asBukkitCopy(GameProfile gameProfile) { + CraftPlayerProfile profile = new CraftPlayerProfile(gameProfile.getId(), gameProfile.getName()); + copyProfileProperties(gameProfile, profile.profile); + return profile; + } + + public static PlayerProfile asBukkitMirror(GameProfile profile) { + return new CraftPlayerProfile(profile); + } + + public static Property asAuthlib(ProfileProperty property) { + return new Property(property.getName(), property.getValue(), property.getSignature()); + } + + public static GameProfile asAuthlibCopy(PlayerProfile profile) { + CraftPlayerProfile craft = ((CraftPlayerProfile) profile); + return asAuthlib(craft.clone()); + } + + public static GameProfile asAuthlib(PlayerProfile profile) { + CraftPlayerProfile craft = ((CraftPlayerProfile) profile); + return craft.getGameProfile(); + } + + private class PropertySet extends AbstractSet { + + @Override + @Nonnull + public Iterator iterator() { + return new ProfilePropertyIterator(profile.getProperties().values().iterator()); + } + + @Override + public int size() { + return profile.getProperties().size(); + } + + @Override + public boolean add(ProfileProperty property) { + setProperty(property); + return true; + } + + @Override + public boolean addAll(Collection c) { + //noinspection unchecked + setProperties((Collection) c); + return true; + } + + @Override + public boolean contains(Object o) { + return o instanceof ProfileProperty && profile.getProperties().containsKey(((ProfileProperty) o).getName()); + } + + private class ProfilePropertyIterator implements Iterator { + private final Iterator iterator; + + ProfilePropertyIterator(Iterator iterator) { + this.iterator = iterator; + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public ProfileProperty next() { + return toBukkit(iterator.next()); + } + + @Override + public void remove() { + iterator.remove(); + } + } + } +} diff --git a/src/main/java/com/destroystokyo/paper/profile/PaperAuthenticationService.java b/src/main/java/com/destroystokyo/paper/profile/PaperAuthenticationService.java new file mode 100644 index 000000000000..25836b975b51 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/profile/PaperAuthenticationService.java @@ -0,0 +1,30 @@ +package com.destroystokyo.paper.profile; + +import com.mojang.authlib.Agent; +import com.mojang.authlib.GameProfileRepository; +import com.mojang.authlib.UserAuthentication; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; + +import java.net.Proxy; + +public class PaperAuthenticationService extends YggdrasilAuthenticationService { + public PaperAuthenticationService(Proxy proxy, String clientToken) { + super(proxy, clientToken); + } + + @Override + public UserAuthentication createUserAuthentication(Agent agent) { + return new PaperUserAuthentication(this, agent); + } + + @Override + public MinecraftSessionService createMinecraftSessionService() { + return new PaperMinecraftSessionService(this); + } + + @Override + public GameProfileRepository createProfileRepository() { + return new PaperGameProfileRepository(this); + } +} diff --git a/src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java b/src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java new file mode 100644 index 000000000000..bb9894318e1f --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/profile/PaperGameProfileRepository.java @@ -0,0 +1,68 @@ +package com.destroystokyo.paper.profile; + +import com.destroystokyo.paper.event.profile.LookupProfileEvent; +import com.destroystokyo.paper.event.profile.PreLookupProfileEvent; +import com.google.common.collect.Sets; +import com.mojang.authlib.Agent; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.ProfileLookupCallback; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import com.mojang.authlib.yggdrasil.YggdrasilGameProfileRepository; + +import java.util.Set; + +public class PaperGameProfileRepository extends YggdrasilGameProfileRepository { + + public PaperGameProfileRepository(YggdrasilAuthenticationService authenticationService) { + super(authenticationService); + } + + @Override + public void findProfilesByNames(String[] names, Agent agent, ProfileLookupCallback callback) { + Set unfoundNames = Sets.newHashSet(); + for (String name : names) { + PreLookupProfileEvent event = new PreLookupProfileEvent(name); + event.callEvent(); + if (event.getUUID() != null) { + // Plugin provided UUI, we can skip network call. + GameProfile gameprofile = new GameProfile(event.getUUID(), name); + // We might even have properties! + Set profileProperties = event.getProfileProperties(); + if (!profileProperties.isEmpty()) { + for (ProfileProperty property : profileProperties) { + gameprofile.getProperties().put(property.getName(), CraftPlayerProfile.asAuthlib(property)); + } + } + callback.onProfileLookupSucceeded(gameprofile); + } else { + unfoundNames.add(name); + } + } + + // Some things were not found.... Proceed to look up. + if (!unfoundNames.isEmpty()) { + String[] namesArr = unfoundNames.toArray(new String[unfoundNames.size()]); + super.findProfilesByNames(namesArr, agent, new PreProfileLookupCallback(callback)); + } + } + + private static class PreProfileLookupCallback implements ProfileLookupCallback { + private final ProfileLookupCallback callback; + + PreProfileLookupCallback(ProfileLookupCallback callback) { + this.callback = callback; + } + + @Override + public void onProfileLookupSucceeded(GameProfile gameProfile) { + PlayerProfile from = CraftPlayerProfile.asBukkitMirror(gameProfile); + new LookupProfileEvent(from).callEvent(); + callback.onProfileLookupSucceeded(gameProfile); + } + + @Override + public void onProfileLookupFailed(GameProfile gameProfile, Exception e) { + callback.onProfileLookupFailed(gameProfile, e); + } + } +} diff --git a/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java b/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java new file mode 100644 index 000000000000..61cfdf73c8a5 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/profile/PaperMinecraftSessionService.java @@ -0,0 +1,39 @@ +package com.destroystokyo.paper.profile; + +import com.destroystokyo.paper.event.profile.FillProfileEvent; +import com.destroystokyo.paper.event.profile.PreFillProfileEvent; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.minecraft.MinecraftProfileTexture; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService; + +import java.util.Map; + +public class PaperMinecraftSessionService extends YggdrasilMinecraftSessionService { + protected PaperMinecraftSessionService(YggdrasilAuthenticationService authenticationService) { + super(authenticationService); + } + + @Override + public Map getTextures(GameProfile profile, boolean requireSecure) { + return super.getTextures(profile, requireSecure); + } + + @Override + public GameProfile fillProfileProperties(GameProfile profile, boolean requireSecure) { + CraftPlayerProfile playerProfile = (CraftPlayerProfile) CraftPlayerProfile.asBukkitMirror(profile); + new PreFillProfileEvent(playerProfile).callEvent(); + profile = playerProfile.getGameProfile(); + if (profile.isComplete() && profile.getProperties().containsKey("textures")) { + return profile; + } + GameProfile gameProfile = super.fillProfileProperties(profile, requireSecure); + new FillProfileEvent(CraftPlayerProfile.asBukkitMirror(gameProfile)).callEvent(); + return gameProfile; + } + + @Override + protected GameProfile fillGameProfile(GameProfile profile, boolean requireSecure) { + return super.fillGameProfile(profile, requireSecure); + } +} diff --git a/src/main/java/com/destroystokyo/paper/profile/PaperUserAuthentication.java b/src/main/java/com/destroystokyo/paper/profile/PaperUserAuthentication.java new file mode 100644 index 000000000000..3aceb0ea8a1a --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/profile/PaperUserAuthentication.java @@ -0,0 +1,11 @@ +package com.destroystokyo.paper.profile; + +import com.mojang.authlib.Agent; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication; + +public class PaperUserAuthentication extends YggdrasilUserAuthentication { + public PaperUserAuthentication(YggdrasilAuthenticationService authenticationService, Agent agent) { + super(authenticationService, agent); + } +} diff --git a/src/main/java/com/destroystokyo/paper/proxy/VelocityProxy.java b/src/main/java/com/destroystokyo/paper/proxy/VelocityProxy.java new file mode 100644 index 000000000000..fdd8708f9747 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/proxy/VelocityProxy.java @@ -0,0 +1,67 @@ +package com.destroystokyo.paper.proxy; + +import com.destroystokyo.paper.PaperConfig; +import com.google.common.net.InetAddresses; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.PacketDataSerializer; + +import java.net.InetAddress; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +public class VelocityProxy { + private static final int SUPPORTED_FORWARDING_VERSION = 1; + public static final MinecraftKey PLAYER_INFO_CHANNEL = new MinecraftKey("velocity", "player_info"); + + public static boolean checkIntegrity(final PacketDataSerializer buf) { + final byte[] signature = new byte[32]; + buf.readBytes(signature); + + final byte[] data = new byte[buf.readableBytes()]; + buf.getBytes(buf.readerIndex(), data); + + try { + final Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(PaperConfig.velocitySecretKey, "HmacSHA256")); + final byte[] mySignature = mac.doFinal(data); + if (!MessageDigest.isEqual(signature, mySignature)) { + return false; + } + } catch (final InvalidKeyException | NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + + int version = buf.readVarInt(); + if (version != SUPPORTED_FORWARDING_VERSION) { + throw new IllegalStateException("Unsupported forwarding version " + version + ", wanted " + SUPPORTED_FORWARDING_VERSION); + } + + return true; + } + + public static InetAddress readAddress(final PacketDataSerializer buf) { + return InetAddresses.forString(buf.readUTF(Short.MAX_VALUE)); + } + + public static GameProfile createProfile(final PacketDataSerializer buf) { + final GameProfile profile = new GameProfile(buf.readUUID(), buf.readUTF(16)); + readProperties(buf, profile); + return profile; + } + + private static void readProperties(final PacketDataSerializer buf, final GameProfile profile) { + final int properties = buf.readVarInt(); + for (int i1 = 0; i1 < properties; i1++) { + final String name = buf.readUTF(Short.MAX_VALUE); + final String value = buf.readUTF(Short.MAX_VALUE); + final String signature = buf.readBoolean() ? buf.readUTF(Short.MAX_VALUE) : null; + profile.getProperties().put(name, new Property(name, value, signature)); + } + } +} diff --git a/src/main/java/com/destroystokyo/paper/util/PriorityQueuedExecutor.java b/src/main/java/com/destroystokyo/paper/util/PriorityQueuedExecutor.java new file mode 100644 index 000000000000..8f18c28695ac --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/util/PriorityQueuedExecutor.java @@ -0,0 +1,347 @@ +package com.destroystokyo.paper.util; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + +/** + * Implements an Executor Service that allows specifying Task Priority + * and bumping of task priority. + * + * This is a non blocking executor with 3 priority levels. + * + * URGENT: Rarely used, something that is critical to take action now. + * HIGH: Something with more importance than the base tasks + * + * @author Daniel Ennis <aikar@aikar.co> + */ +@SuppressWarnings({"WeakerAccess", "UnusedReturnValue", "unused"}) +public class PriorityQueuedExecutor extends AbstractExecutorService { + + private final ConcurrentLinkedQueue urgent = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue high = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue normal = new ConcurrentLinkedQueue<>(); + private final List threads = new ArrayList<>(); + private final RejectionHandler handler; + + private volatile boolean shuttingDown = false; + private volatile boolean shuttingDownNow = false; + + public PriorityQueuedExecutor(String name) { + this(name, Math.max(1, Runtime.getRuntime().availableProcessors() - 1)); + } + + public PriorityQueuedExecutor(String name, int threads) { + this(name, threads, Thread.NORM_PRIORITY, null); + } + + public PriorityQueuedExecutor(String name, int threads, int threadPriority) { + this(name, threads, threadPriority, null); + } + + public PriorityQueuedExecutor(String name, int threads, RejectionHandler handler) { + this(name, threads, Thread.NORM_PRIORITY, handler); + } + + public PriorityQueuedExecutor(String name, int threads, int threadPriority, RejectionHandler handler) { + for (int i = 0; i < threads; i++) { + ExecutorThread thread = new ExecutorThread(this::processQueues); + thread.setDaemon(true); + thread.setName(threads == 1 ? name : name + "-" + (i + 1)); + thread.setPriority(threadPriority); + thread.start(); + this.threads.add(thread); + } + if (handler == null) { + handler = ABORT_POLICY; + } + this.handler = handler; + } + + /** + * If the Current thread belongs to a PriorityQueuedExecutor, return that Executro + * @return The executor that controls this thread + */ + public static PriorityQueuedExecutor getExecutor() { + if (!(Thread.currentThread() instanceof ExecutorThread)) { + return null; + } + return ((ExecutorThread) Thread.currentThread()).getExecutor(); + } + + public void shutdown() { + shuttingDown = true; + synchronized (this) { + this.notifyAll(); + } + } + + @Nonnull + @Override + public List shutdownNow() { + shuttingDown = true; + shuttingDownNow = true; + List tasks = new ArrayList<>(high.size() + normal.size()); + Runnable run; + while ((run = getTask()) != null) { + tasks.add(run); + } + + return tasks; + } + + @Override + public boolean isShutdown() { + return shuttingDown; + } + + @Override + public boolean isTerminated() { + if (!shuttingDown) { + return false; + } + return high.isEmpty() && normal.isEmpty(); + } + + @Override + public boolean awaitTermination(long timeout, @Nonnull TimeUnit unit) { + synchronized (this) { + this.notifyAll(); + } + final long wait = unit.toNanos(timeout); + final long max = System.nanoTime() + wait; + for (;!threads.isEmpty() && System.nanoTime() < max;) { + threads.removeIf(thread -> !thread.isAlive()); + } + return isTerminated(); + } + + + public PendingTask createPendingTask(Runnable task) { + return createPendingTask(task, Priority.NORMAL); + } + public PendingTask createPendingTask(Runnable task, Priority priority) { + return createPendingTask(() -> { + task.run(); + return null; + }, priority); + } + + public PendingTask createPendingTask(Supplier task) { + return createPendingTask(task, Priority.NORMAL); + } + + public PendingTask createPendingTask(Supplier task, Priority priority) { + return new PendingTask<>(task, priority); + } + + public PendingTask submitTask(Runnable run) { + return createPendingTask(run).submit(); + } + + public PendingTask submitTask(Runnable run, Priority priority) { + return createPendingTask(run, priority).submit(); + } + + public PendingTask submitTask(Supplier run) { + return createPendingTask(run).submit(); + } + + public PendingTask submitTask(Supplier run, Priority priority) { + PendingTask task = createPendingTask(run, priority); + return task.submit(); + } + + @Override + public void execute(@Nonnull Runnable command) { + submitTask(command); + } + + public boolean isCurrentThread() { + final Thread thread = Thread.currentThread(); + if (!(thread instanceof ExecutorThread)) { + return false; + } + return ((ExecutorThread) thread).getExecutor() == this; + } + + public Runnable getUrgentTask() { + return urgent.poll(); + } + + public Runnable getTask() { + Runnable run = urgent.poll(); + if (run != null) { + return run; + } + run = high.poll(); + if (run != null) { + return run; + } + return normal.poll(); + } + + private void processQueues() { + Runnable run = null; + while (true) { + if (run != null) { + run.run(); + } + if (shuttingDownNow) { + return; + } + if ((run = getTask()) != null) { + continue; + } + synchronized (PriorityQueuedExecutor.this) { + if ((run = getTask()) != null) { + continue; + } + + if (shuttingDown || shuttingDownNow) { + return; + } + try { + PriorityQueuedExecutor.this.wait(); + } catch (InterruptedException ignored) { + } + } + } + } + + public boolean processUrgentTasks() { + Runnable run; + boolean hadTask = false; + while ((run = getUrgentTask()) != null) { + run.run(); + hadTask = true; + } + return hadTask; + } + + public enum Priority { + NORMAL, HIGH, URGENT + } + + public class ExecutorThread extends Thread { + public ExecutorThread(Runnable runnable) { + super(runnable); + } + + public PriorityQueuedExecutor getExecutor() { + return PriorityQueuedExecutor.this; + } + } + + public class PendingTask implements Runnable { + + private final AtomicBoolean hasRan = new AtomicBoolean(); + private final AtomicInteger submitted = new AtomicInteger(-1); + private final AtomicInteger priority; + private final Supplier run; + private final CompletableFuture future = new CompletableFuture<>(); + private volatile PriorityQueuedExecutor executor; + + public PendingTask(Supplier run) { + this(run, Priority.NORMAL); + } + + public PendingTask(Supplier run, Priority priority) { + this.priority = new AtomicInteger(priority.ordinal()); + this.run = run; + } + + public boolean cancel() { + return hasRan.compareAndSet(false, true); + } + + @Override + public void run() { + if (!hasRan.compareAndSet(false, true)) { + return; + } + + try { + future.complete(run.get()); + } catch (Throwable e) { + future.completeExceptionally(e); + } + } + + public void bumpPriority() { + bumpPriority(Priority.HIGH); + } + + public void bumpPriority(Priority newPriority) { + for (;;) { + int current = this.priority.get(); + int ordinal = newPriority.ordinal(); + if (current >= ordinal || priority.compareAndSet(current, ordinal)) { + break; + } + } + + + if (this.submitted.get() == -1 || this.hasRan.get()) { + return; + } + + // Only resubmit if it hasnt ran yet and has been submitted + submit(); + } + + public CompletableFuture onDone() { + return future; + } + + public PendingTask submit() { + if (shuttingDown) { + handler.onRejection(this, PriorityQueuedExecutor.this); + return this; + } + for (;;) { + final int submitted = this.submitted.get(); + final int priority = this.priority.get(); + if (submitted == priority) { + return this; + } + if (this.submitted.compareAndSet(submitted, priority)) { + if (priority == Priority.URGENT.ordinal()) { + urgent.add(this); + } else if (priority == Priority.HIGH.ordinal()) { + high.add(this); + } else { + normal.add(this); + } + + break; + } + } + + synchronized (PriorityQueuedExecutor.this) { + // Wake up a thread to take this work + PriorityQueuedExecutor.this.notify(); + } + return this; + } + } + public interface RejectionHandler { + void onRejection(Runnable run, PriorityQueuedExecutor executor); + } + + public static final RejectionHandler ABORT_POLICY = (run, executor) -> { + throw new RejectedExecutionException("Executor has been shutdown"); + }; + public static final RejectionHandler CALLER_RUNS_POLICY = (run, executor) -> { + run.run(); + }; + +} diff --git a/src/main/java/com/destroystokyo/paper/util/RedstoneWireTurbo.java b/src/main/java/com/destroystokyo/paper/util/RedstoneWireTurbo.java new file mode 100644 index 000000000000..21d9d6d7edba --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/util/RedstoneWireTurbo.java @@ -0,0 +1,910 @@ +package com.destroystokyo.paper.util; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; + +import org.bukkit.event.block.BlockRedstoneEvent; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import net.minecraft.server.Block; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.BlockRedstoneWire; +import net.minecraft.server.IBlockData; +import net.minecraft.server.World; + +/** + * Used for the faster redstone algorithm. + * Original author: theosib + * Original license: MIT + * + * Ported to Paper and updated to 1.13 by egg82 + */ +public class RedstoneWireTurbo { + /* + * This is Helper class for BlockRedstoneWire. It implements a minimally-invasive + * bolt-on accelerator that performs a breadth-first search through redstone wire blocks + * in order to more efficiently and deterministically compute new redstone wire power levels + * and determine the order in which other blocks should be updated. + * + * Features: + * - Changes to BlockRedstoneWire are very limited, no other classes are affected, and the + * choice between old and new redstone wire update algorithms is switchable on-line. + * - The vanilla implementation relied on World.notifyNeighborsOfStateChange for redstone + * wire blocks to communicate power level changes to each other, generating 36 block + * updates per call. This improved implementation propagates power level changes directly + * between redstone wire blocks. Redstone wire power levels are therefore computed more quickly, + * and block updates are sent only to non-redstone blocks, many of which may perform an + * action when informed of a change in redstone power level. (Note: Block updates are not + * the same as state changes to redstone wire. Wire block states are updated as soon + * as they are computed.) + * - Of the 36 block updates generated by a call to World.notifyNeighborsOfStateChange, + * 12 of them are obviously redundant (e.g. the west neighbor of the east neighbor). + * These are eliminated. + * - Updates to redstone wire and other connected blocks are propagated in a breath-first + * manner, radiating out from the initial trigger (a block update to a redstone wire + * from something other than redstone wire). + * - Updates are scheduled both deterministically and in an intuitive order, addressing bug + * MC-11193. + * - All redstone behavior that used to be locational now works the same in all locations. + * - All behaviors of redstone wire that used to be orientational now work the same in all + * orientations, as long as orientation can be determined; random otherwise. Some other + * redstone components still update directionally (e.g. switches), and this code can't + * compensate for that. + * - Information that is otherwise computed over and over again or which is expensive to + * to compute is cached for faster lookup. This includes coordinates of block position + * neighbors and block states that won't change behind our backs during the execution of + * this search algorithm. + * - Redundant block updates (both to redstone wire and to other blocks) are heavily + * consolidated. For worst-case scenarios (depowering of redstone wire) this results + * in a reduction of block updates by as much as 95% (factor of 1/21). Due to overheads, + * empirical testing shows a speedup better than 10x. This addresses bug MC-81098. + * + * Extensive testing has been performed to ensure that existing redstone contraptions still + * behave as expected. Results of early testing that identified undesirable behavior changes + * were addressed. Additionally, real-time performance testing revealed compute inefficiencies + * With earlier implementations of this accelerator. Some compatibility adjustments and + * performance optimizations resulted in harmless increases in block updates above the + * theoretical minimum. + * + * Only a single redstone machine was found to break: An instant dropper line hack that + * relies on powered rails and quasi-connectivity but doesn't work in all directions. The + * replacement is to lay redstone wire directly on top of the dropper line, which now works + * reliably in any direction. + * + * There are numerous other optimization that can be made, but those will be provided later in + * separate updates. This version is designed to be minimalistic. + * + * Many thanks to the following individuals for their help in testing this functionality: + * - pokechu22, _MethodZz_, WARBEN, NarcolepticFrog, CommandHelper (nessie), ilmango, + * OreoLamp, Xcom6000, tryashtar, RedCMD, Smokey95Dog, EDDxample, Rays Works, + * Nodnam, BlockyPlays, Grumm, NeunEinser, HelVince. + */ + + /* Reference to BlockRedstoneWire object, which uses this accelerator */ + private final BlockRedstoneWire wire; + + /* + * Implementation: + * + * RedstoneWire Blocks are updated in concentric rings or "layers" radiating out from the + * initial block update that came from a call to BlockRedstoneWire.neighborChanged(). + * All nodes put in Layer N are those with Manhattan distance N from the trigger + * position, reachable through connected redstone wire blocks. + * + * Layer 0 represents the trigger block position that was input to neighborChanged. + * Layer 1 contains the immediate neighbors of that position. + * Layer N contains the neighbors of blocks in layer N-1, not including + * those in previous layers. + * + * Layers enforce an update order that is a function of Manhattan distance + * from the initial coordinates input to neighborChanged. The same + * coordinates may appear in multiple layers, but redundant updates are minimized. + * Block updates are sent layer-by-layer. If multiple of a block's neighbors experience + * redstone wire changes before its layer is processed, then those updates will be merged. + * If a block's update has been sent, but its neighboring redstone changes + * after that, then another update will be sent. This preserves compatibility with + * machines that rely on zero-tick behavior, except that the new functionality is non- + * locational. + * + * Within each layer, updates are ordered left-to-right relative to the direction of + * information flow. This makes the implementation non-orientational. Only when + * this direction is ambiguous is randomness applied (intentionally). + */ + private List updateQueue0 = Lists.newArrayList(); + private List updateQueue1 = Lists.newArrayList(); + private List updateQueue2 = Lists.newArrayList(); + + public RedstoneWireTurbo(BlockRedstoneWire wire) { + this.wire = wire; + } + + /* + * Compute neighbors of a block. When a redstone wire value changes, previously it called + * World.notifyNeighborsOfStateChange. That lists immediately neighboring blocks in + * west, east, down, up, north, south order. For each of those neighbors, their own + * neighbors are updated in the same order. This generates 36 updates, but 12 of them are + * redundant; for instance the west neighbor of a block's east neighbor. + * + * Note that this ordering is only used to create the initial list of neighbors. Once + * the direction of signal flow is identified, the ordering of updates is completely + * reorganized. + */ + public static BlockPosition[] computeAllNeighbors(final BlockPosition pos) { + final int x = pos.getX(); + final int y = pos.getY(); + final int z = pos.getZ(); + final BlockPosition[] n = new BlockPosition[24]; + + // Immediate neighbors, in the same order as + // World.notifyNeighborsOfStateChange, etc.: + // west, east, down, up, north, south + n[0] = new BlockPosition(x - 1, y, z); + n[1] = new BlockPosition(x + 1, y, z); + n[2] = new BlockPosition(x, y - 1, z); + n[3] = new BlockPosition(x, y + 1, z); + n[4] = new BlockPosition(x, y, z - 1); + n[5] = new BlockPosition(x, y, z + 1); + + // Neighbors of neighbors, in the same order, + // except that duplicates are not included + n[6] = new BlockPosition(x - 2, y, z); + n[7] = new BlockPosition(x - 1, y - 1, z); + n[8] = new BlockPosition(x - 1, y + 1, z); + n[9] = new BlockPosition(x - 1, y, z - 1); + n[10] = new BlockPosition(x - 1, y, z + 1); + n[11] = new BlockPosition(x + 2, y, z); + n[12] = new BlockPosition(x + 1, y - 1, z); + n[13] = new BlockPosition(x + 1, y + 1, z); + n[14] = new BlockPosition(x + 1, y, z - 1); + n[15] = new BlockPosition(x + 1, y, z + 1); + n[16] = new BlockPosition(x, y - 2, z); + n[17] = new BlockPosition(x, y - 1, z - 1); + n[18] = new BlockPosition(x, y - 1, z + 1); + n[19] = new BlockPosition(x, y + 2, z); + n[20] = new BlockPosition(x, y + 1, z - 1); + n[21] = new BlockPosition(x, y + 1, z + 1); + n[22] = new BlockPosition(x, y, z - 2); + n[23] = new BlockPosition(x, y, z + 2); + return n; + } + + /* + * We only want redstone wires to update redstone wires that are + * immediately adjacent. Some more distant updates can result + * in cross-talk that (a) wastes time and (b) can make the update + * order unintuitive. Therefore (relative to the neighbor order + * computed by computeAllNeighbors), updates are not scheduled + * for redstone wire in those non-connecting positions. On the + * other hand, updates will always be sent to *other* types of blocks + * in any of the 24 neighboring positions. + */ + private static final boolean[] update_redstone = { + true, true, false, false, true, true, // 0 to 5 + false, true, true, false, false, false, // 6 to 11 + true, true, false, false, false, true, // 12 to 17 + true, false, true, true, false, false // 18 to 23 + }; + + // Internal numbering for cardinal directions + private static final int North = 0; + private static final int East = 1; + private static final int South = 2; + private static final int West = 3; + + /* + * These lookup tables completely remap neighbor positions into a left-to-right + * ordering, based on the cardinal direction that is determined to be forward. + * See below for more explanation. + */ + private static final int[] forward_is_north = {2, 3, 16, 19, 0, 4, 1, 5, 7, 8, 17, 20, 12, 13, 18, 21, 6, 9, 22, 14, 11, 10, 23, 15}; + private static final int[] forward_is_east = {2, 3, 16, 19, 4, 1, 5, 0, 17, 20, 12, 13, 18, 21, 7, 8, 22, 14, 11, 15, 23, 9, 6, 10}; + private static final int[] forward_is_south = {2, 3, 16, 19, 1, 5, 0, 4, 12, 13, 18, 21, 7, 8, 17, 20, 11, 15, 23, 10, 6, 14, 22, 9}; + private static final int[] forward_is_west = {2, 3, 16, 19, 5, 0, 4, 1, 18, 21, 7, 8, 17, 20, 12, 13, 23, 10, 6, 9, 22, 15, 11, 14}; + + /* For any orientation, we end up with the update order defined below. This order is relative to any redstone wire block + * that is itself having an update computed, and this center position is marked with C. + * - The update position marked 0 is computed first, and the one marked 23 is last. + * - Forward is determined by the local direction of information flow into position C from prior updates. + * - The first updates are scheduled for the four positions below and above C. + * - Then updates are scheduled for the four horizontal neighbors of C, followed by the positions below and above those neighbors. + * - Finally, updates are scheduled for the remaining positions with Manhattan distance 2 from C (at the same Y coordinate). + * - For a given horizontal distance from C, updates are scheduled starting from directly left and stepping clockwise to directly + * right. The remaining positions behind C are scheduled counterclockwise so as to maintain the left-to-right ordering. + * - If C is in layer N of the update schedule, then all 24 positions may be scheduled for layer N+1. For redstone wire, no + * updates are scheduled for positions that cannot directly connect. Additionally, the four positions above and below C + * are ALSO scheduled for layer N+2. + * - This update order was selected after experimenting with a number of alternative schedules, based on its compatibility + * with existing redstone designs and behaviors that were considered to be intuitive by various testers. WARBEN in particular + * made some of the most challenging test cases, but the 3-tick clocks (made by RedCMD) were also challenging to fix, + * along with the rail-based instant dropper line built by ilmango. Numerous others made test cases as well, including + * NarcolepticFrog, nessie, and Pokechu22. + * + * - The forward direction is determined locally. So when there are branches in the redstone wire, the left one will get updated + * before the right one. Each branch can have its own relative forward direction, resulting in the left side of a left branch + * having priority over the right branch of a left branch, which has priority over the left branch of a right branch, followed + * by the right branch of a right branch. And so forth. Since redstone power reduces to zero after a path distance of 15, + * that imposes a practical limit on the branching. Note that the branching is not tracked explicitly -- relative forward + * directions dictate relative sort order, which maintains the proper global ordering. This also makes it unnecessary to be + * concerned about branches meeting up with each other. + * + * ^ + * | + * Forward + * <-- Left Right --> + * + * 18 + * 10 17 5 19 11 + * 2 8 0 12 16 4 C 6 20 9 1 13 3 + * 14 21 7 23 15 + * Further 22 Further + * Down Down Up Up + * + * Backward + * | + * V + */ + + // This allows the above remapping tables to be looked up by cardial direction index + private static final int[][] reordering = { forward_is_north, forward_is_east, forward_is_south, forward_is_west }; + + /* + * Input: Array of UpdateNode objects in an order corresponding to the positions + * computed by computeAllNeighbors above. + * Output: Array of UpdateNode objects oriented using the above remapping tables + * corresponding to the identified heading (direction of information flow). + */ + private static void orientNeighbors(final UpdateNode[] src, final UpdateNode[] dst, final int heading) { + final int[] re = reordering[heading]; + for (int i = 0; i < 24; i++) { + dst[i] = src[re[i]]; + } + } + + /* + * Structure to keep track of redstone wire blocks and + * neighbors that will receive updates. + */ + private static class UpdateNode { + public static enum Type { + UNKNOWN, REDSTONE, OTHER + } + + IBlockData currentState; // Keep track of redstone wire value + UpdateNode[] neighbor_nodes; // References to neighbors (directed graph edges) + BlockPosition self; // UpdateNode's own position + BlockPosition parent; // Which block pos spawned/updated this node + Type type = Type.UNKNOWN; // unknown, redstone wire, other type of block + int layer; // Highest layer this node is scheduled in + boolean visited; // To keep track of information flow direction, visited restone wire is marked + int xbias, zbias; // Remembers directionality of ancestor nodes; helps eliminate directional ambiguities. + } + + /* + * Keep track of all block positions discovered during search and their current states. + * We want to remember one entry for each position. + */ + private final Map nodeCache = Maps.newHashMap(); + + /* + * For a newly created UpdateNode object, determine what type of block it is. + */ + private void identifyNode(final World worldIn, final UpdateNode upd1) { + final BlockPosition pos = upd1.self; + final IBlockData oldState = worldIn.getType(pos); + upd1.currentState = oldState; + + // Some neighbors of redstone wire are other kinds of blocks. + // These need to receive block updates to inform them that + // redstone wire values have changed. + final Block block = oldState.getBlock(); + if (block != wire) { + // Mark this block as not redstone wire and therefore + // requiring updates + upd1.type = UpdateNode.Type.OTHER; + + // Non-redstone blocks may propagate updates, but those updates + // are not handled by this accelerator. Therefore, we do not + // expand this position's neighbors. + return; + } + + // One job of BlockRedstoneWire.neighborChanged is to convert + // redstone wires to items if the block beneath was removed. + // With this accelerator, BlockRedstoneWire.neighborChanged + // is only typically called for a single wire block, while + // others are processed internally by the breadth first search + // algorithm. To preserve this game behavior, this check must + // be replicated here. + if (!wire.canPlace(null, worldIn, pos)) { + // Pop off the redstone dust + oldState.dropNaturally(worldIn, pos, 0); + worldIn.setAir(pos); + + // Mark this position as not being redstone wire + upd1.type = UpdateNode.Type.OTHER; + + // Note: Sending updates to air blocks leads to an empty method. + // Testing shows this to be faster than explicitly avoiding updates to + // air blocks. + return; + } + + // If the above conditions fail, then this is a redstone wire block. + upd1.type = UpdateNode.Type.REDSTONE; + } + + /* + * Given which redstone wire blocks have been visited and not visited + * around the position currently being updated, compute the cardinal + * direction that is "forward." + * + * rx is the forward direction along the West/East axis + * rz is the forward direction along the North/South axis + */ + static private int computeHeading(final int rx, final int rz) { + // rx and rz can only take on values -1, 0, and 1, so we can + // compute a code number that allows us to use a single switch + // to determine the heading. + final int code = (rx + 1) + 3 * (rz + 1); + switch (code) { + case 0: { + // Both rx and rz are -1 (northwest) + // Randomly choose one to be forward. + final int j = ThreadLocalRandom.current().nextInt(0, 1); + return (j == 0) ? North : West; + } + case 1: { + // rx=0, rz=-1 + // Definitively North + return North; + } + case 2: { + // rx=1, rz=-1 (northeast) + // Choose randomly between north and east + final int j = ThreadLocalRandom.current().nextInt(0, 1); + return (j == 0) ? North : East; + } + case 3: { + // rx=-1, rz=0 + // Definitively West + return West; + } + case 4: { + // rx=0, rz=0 + // Heading is completely ambiguous. Choose + // randomly among the four cardinal directions. + return ThreadLocalRandom.current().nextInt(0, 4); + } + case 5: { + // rx=1, rz=0 + // Definitively East + return East; + } + case 6: { + // rx=-1, rz=1 (southwest) + // Choose randomly between south and west + final int j = ThreadLocalRandom.current().nextInt(0, 1); + return (j == 0) ? South : West; + } + case 7: { + // rx=0, rz=1 + // Definitively South + return South; + } + case 8: { + // rx=1, rz=1 (southeast) + // Choose randomly between south and east + final int j = ThreadLocalRandom.current().nextInt(0, 1); + return (j == 0) ? South : East; + } + } + + // We should never get here + return ThreadLocalRandom.current().nextInt(0, 4); + } + + // Select whether to use updateSurroundingRedstone from BlockRedstoneWire (old) + // or this helper class (new) + private static final boolean old_current_change = false; + + /* + * Process a node whose neighboring redstone wire has experienced value changes. + */ + private void updateNode(final World worldIn, final UpdateNode upd1, final int layer) { + final BlockPosition pos = upd1.self; + + // Mark this redstone wire as having been visited so that it can be used + // to calculate direction of information flow. + upd1.visited = true; + + // Look up the last known state. + // Due to the way other redstone components are updated, we do not + // have to worry about a state changing behind our backs. The rare + // exception is handled by scheduleReentrantNeighborChanged. + final IBlockData oldState = upd1.currentState; + + // Ask the wire block to compute its power level from its neighbors. + // This will also update the wire's power level and return a new + // state if it has changed. When a wire power level is changed, + // calculateCurrentChanges will immediately update the block state in the world + // and return the same value here to be cached in the corresponding + // UpdateNode object. + IBlockData newState; + if (old_current_change) { + newState = wire.calculateCurrentChanges(worldIn, pos, pos, oldState); + } else { + // Looking up block state is slow. This accelerator includes a version of + // calculateCurrentChanges that uses cahed wire values for a + // significant performance boost. + newState = this.calculateCurrentChanges(worldIn, upd1); + } + + // Only inform neighbors if the state has changed + if (newState != oldState) { + // Store the new state + upd1.currentState = newState; + + // Inform neighbors of the change + propagateChanges(worldIn, upd1, layer); + } + } + + /* + * This identifies the neighboring positions of a new UpdateNode object, + * determines their types, and links those to into the graph. Then based on + * what nodes in the redstone wire graph have been visited, the neighbors + * are reordered left-to-right relative to the direction of information flow. + */ + private void findNeighbors(final World worldIn, final UpdateNode upd1) { + final BlockPosition pos = upd1.self; + + // Get the list of neighbor coordinates + final BlockPosition[] neighbors = computeAllNeighbors(pos); + + // Temporary array of neighbors in cardinal ordering + final UpdateNode[] neighbor_nodes = new UpdateNode[24]; + + // Target array of neighbors sorted left-to-right + upd1.neighbor_nodes = new UpdateNode[24]; + + for (int i=0; i<24; i++) { + // Look up each neighbor in the node cache + final BlockPosition pos2 = neighbors[i]; + UpdateNode upd2 = nodeCache.get(pos2); + if (upd2 == null) { + // If this is a previously unreached position, create + // a new update node, add it to the cache, and identify what it is. + upd2 = new UpdateNode(); + upd2.self = pos2; + upd2.parent = pos; + nodeCache.put(pos2, upd2); + identifyNode(worldIn, upd2); + } + + // For non-redstone blocks, any of the 24 neighboring positions + // should receive a block update. However, some block coordinates + // may contain a redstone wire that does not directly connect to the + // one being expanded. To avoid redundant calculations and confusing + // cross-talk, those neighboring positions are not included. + if (update_redstone[i] || upd2.type != UpdateNode.Type.REDSTONE) { + neighbor_nodes[i] = upd2; + } + } + + // Determine the directions from which the redstone signal may have come from. This + // checks for redstone wire at the same Y level and also Y+1 and Y-1, relative to the + // block being expanded. + final boolean fromWest = (neighbor_nodes[0].visited || neighbor_nodes[7].visited || neighbor_nodes[8].visited); + final boolean fromEast = (neighbor_nodes[1].visited || neighbor_nodes[12].visited || neighbor_nodes[13].visited); + final boolean fromNorth = (neighbor_nodes[4].visited || neighbor_nodes[17].visited || neighbor_nodes[20].visited); + final boolean fromSouth = (neighbor_nodes[5].visited || neighbor_nodes[18].visited || neighbor_nodes[21].visited); + + int cx = 0, cz = 0; + if (fromWest) cx += 1; + if (fromEast) cx -= 1; + if (fromNorth) cz += 1; + if (fromSouth) cz -= 1; + + int heading; + if (cx==0 && cz==0) { + // If there is no clear direction, try to inherit the heading from ancestor nodes. + heading = computeHeading(upd1.xbias, upd1.zbias); + + // Propagate that heading to descendant nodes. + for (int i=0; i<24; i++) { + final UpdateNode nn = neighbor_nodes[i]; + if (nn != null) { + nn.xbias = upd1.xbias; + nn.zbias = upd1.zbias; + } + } + } else { + if (cx != 0 && cz != 0) { + // If the heading is somewhat ambiguous, try to disambiguate based on + // ancestor nodes. + if (upd1.xbias != 0) cz = 0; + if (upd1.zbias != 0) cx = 0; + } + heading = computeHeading(cx, cz); + + // Propagate that heading to descendant nodes. + for (int i=0; i<24; i++) { + final UpdateNode nn = neighbor_nodes[i]; + if (nn != null) { + nn.xbias = cx; + nn.zbias = cz; + } + } + } + + // Reorder neighboring UpdateNode objects according to the forward direction + // determined above. + orientNeighbors(neighbor_nodes, upd1.neighbor_nodes, heading); + } + + /* + * For any redstone wire block in layer N, inform neighbors to recompute their states + * in layers N+1 and N+2; + */ + private void propagateChanges(final World worldIn, final UpdateNode upd1, final int layer) { + if (upd1.neighbor_nodes == null) { + // If this node has not been expanded yet, find its neighbors + findNeighbors(worldIn, upd1); + } + + final BlockPosition pos = upd1.self; + + // All neighbors may be scheduled for layer N+1 + final int layer1 = layer + 1; + + // If the node being updated (upd1) has already been expanded, then merely + // schedule updates to its neighbors. + for (int i = 0; i < 24; i++) { + final UpdateNode upd2 = upd1.neighbor_nodes[i]; + + // This test ensures that an UpdateNode is never scheduled to the same layer + // more than once. Also, skip non-connecting redstone wire blocks + if (upd2 != null && layer1 > upd2.layer) { + upd2.layer = layer1; + updateQueue1.add(upd2); + + // Keep track of which block updated this neighbor + upd2.parent = pos; + } + } + + // Nodes above and below are scheduled ALSO for layer N+2 + final int layer2 = layer + 2; + + // Repeat of the loop above, but only for the first four (above and below) neighbors + // and for layer N+2; + for (int i = 0; i < 4; i++) { + final UpdateNode upd2 = upd1.neighbor_nodes[i]; + if (upd2 != null && layer2 > upd2.layer) { + upd2.layer = layer2; + updateQueue2.add(upd2); + upd2.parent = pos; + } + } + } + + // The breadth-first search below will send block updates to blocks + // that are not redstone wire. If one of those updates results in + // a distant redstone wire getting an update, then this.neighborChanged + // will get called. This would be a reentrant call, and + // it is necessary to properly integrate those updates into the + // on-going search through redstone wire. Thus, we make the layer + // currently being processed visible at the object level. + + // The current layer being processed by the breadth-first search + private int currentWalkLayer = 0; + + private void shiftQueue() { + final List t = updateQueue0; + t.clear(); + updateQueue0 = updateQueue1; + updateQueue1 = updateQueue2; + updateQueue2 = t; + } + + /* + * Perform a breadth-first (layer by layer) traversal through redstone + * wire blocks, propagating value changes to neighbors in an order + * that is a function of distance from the initial call to + * this.neighborChanged. + */ + private void breadthFirstWalk(final World worldIn) { + shiftQueue(); + currentWalkLayer = 1; + + // Loop over all layers + while (updateQueue0.size()>0 || updateQueue1.size()>0) { + // Get the set of blocks in this layer + final List thisLayer = updateQueue0; + + // Loop over all blocks in the layer. Recall that + // this is a List, preserving the insertion order of + // left-to-right based on direction of information flow. + for (UpdateNode upd : thisLayer) { + if (upd.type == UpdateNode.Type.REDSTONE) { + // If the node is is redstone wire, + // schedule updates to neighbors if its value + // has changed. + updateNode(worldIn, upd, currentWalkLayer); + } else { + // If this block is not redstone wire, send a block update. + // Redstone wire blocks get state updates, but they don't + // need block updates. Only non-redstone neighbors need updates. + + // World.neighborChanged is called from + // World.notifyNeighborsOfStateChange, and + // notifyNeighborsOfStateExcept. We don't use + // World.notifyNeighborsOfStateChange here, since we are + // already keeping track of all of the neighbor positions + // that need to be updated. All on its own, handling neighbors + // this way reduces block updates by 1/3 (24 instead of 36). + worldIn.neighborChanged(upd.self, wire, upd.parent); + } + } + + // Move on to the next layer + shiftQueue(); + currentWalkLayer++; + } + + currentWalkLayer = 0; + } + + /* + * Normally, when Minecraft is computing redstone wire power changes, and a wire power level + * change sends a block update to a neighboring functional component (e.g. piston, repeater, etc.), + * those updates are queued. Only once all redstone wire updates are complete will any component + * action generate any further block updates to redstone wire. Instant repeater lines, for instance, + * will process all wire updates for one redstone line, after which the pistons will zero-tick, + * after which the next redstone line performs all of its updates. Thus, each wire is processed in its + * own discrete wave. + * + * However, there are some corner cases where this pattern breaks, with a proof of concept discovered + * by Rays Works, which works the same in vanilla. The scenario is as follows: + * (1) A redstone wire is conducting a signal. + * (2) Part-way through that wave of updates, a neighbor is updated that causes an update to a completely + * separate redstone wire. + * (3) This results in a call to BlockRedstoneWire.neighborChanged for that other wire, in the middle of + * an already on-going propagation through the first wire. + * + * The vanilla code, being depth-first, would end up fully processing the second wire before going back + * to finish processing the first one. (Although technically, vanilla has no special concept of "being + * in the middle" of processing updates to a wire.) For the breadth-first algorithm, we give this + * situation special handling, where the updates for the second wire are incorporated into the schedule + * for the first wire, and then the callstack is allowed to unwind back to the on-going search loop in + * order to continue processing both the first and second wire in the order of distance from the initial + * trigger. + */ + private IBlockData scheduleReentrantNeighborChanged(final World worldIn, final BlockPosition pos, final IBlockData newState, final BlockPosition source) { + if (source != null) { + // If the cause of the redstone wire update is known, we can use that to help determine + // direction of information flow. + UpdateNode src = nodeCache.get(source); + if (src == null) { + src = new UpdateNode(); + src.self = source; + src.parent = source; + src.visited = true; + identifyNode(worldIn, src); + nodeCache.put(source, src); + } + } + + // Find or generate a node for the redstone block position receiving the update + UpdateNode upd = nodeCache.get(pos); + if (upd == null) { + upd = new UpdateNode(); + upd.self = pos; + upd.parent = pos; + upd.visited = true; + identifyNode(worldIn, upd); + nodeCache.put(pos, upd); + } + upd.currentState = newState; + + // Receiving this block update may mean something in the world changed. + // Therefore we clear the cached block info about all neighbors of + // the position receiving the update and then re-identify what they are. + if (upd.neighbor_nodes != null) { + for (int i=0; i<24; i++) { + final UpdateNode upd2 = upd.neighbor_nodes[i]; + if (upd2 == null) continue; + upd2.type = UpdateNode.Type.UNKNOWN; + upd2.currentState = null; + identifyNode(worldIn, upd2); + } + } + + // The block at 'pos' is a redstone wire and has been updated already by calling + // wire.calculateCurrentChanges, so we don't schedule that. However, we do need + // to schedule its neighbors. By passing the current value of 'currentWalkLayer' to + // propagateChanges, the neighbors of 'pos' are scheduled for layers currentWalkLayer+1 + // and currentWalkLayer+2. + propagateChanges(worldIn, upd, currentWalkLayer); + + // Return here. The call stack will unwind back to the first call to + // updateSurroundingRedstone, whereupon the new updates just scheduled will + // be propagated. This also facilitates elimination of superfluous and + // redundant block updates. + return newState; + } + + /* + * New version of pre-existing updateSurroundingRedstone, which is called from + * wire.updateSurroundingRedstone, which is called from wire.neighborChanged and a + * few other methods in BlockRedstoneWire. This sets off the breadth-first + * walk through all redstone dust connected to the initial position triggered. + */ + public IBlockData updateSurroundingRedstone(final World worldIn, final BlockPosition pos, final IBlockData state, final BlockPosition source) { + // Check this block's neighbors and see if its power level needs to change + // Use the calculateCurrentChanges method in BlockRedstoneWire since we have no + // cached block states at this point. + final IBlockData newState = wire.calculateCurrentChanges(worldIn, pos, pos, state); + + // If no change, exit + if (newState == state) { + return state; + } + + // Check to see if this update was received during an on-going breadth first search + if (currentWalkLayer > 0 || nodeCache.size() > 0) { + // As breadthFirstWalk progresses, it sends block updates to neighbors. Some of those + // neighbors may affect the world so as to cause yet another redstone wire block to receive + // an update. If that happens, we need to integrate those redstone wire updates into the + // already on-going graph walk being performed by breadthFirstWalk. + return scheduleReentrantNeighborChanged(worldIn, pos, newState, source); + } + // If there are no on-going walks through redstone wire, then start a new walk. + + // If the source of the block update to the redstone wire at 'pos' is known, we can use + // that to help determine the direction of information flow. + if (source != null) { + final UpdateNode src = new UpdateNode(); + src.self = source; + src.parent = source; + src.visited = true; + nodeCache.put(source, src); + identifyNode(worldIn, src); + } + + // Create a node representing the block at 'pos', and then propagate updates + // to its neighbors. As stated above, the call to wire.calculateCurrentChanges + // already performs the update to the block at 'pos', so it is not added to the schedule. + final UpdateNode upd = new UpdateNode(); + upd.self = pos; + upd.parent = source!=null ? source : pos; + upd.currentState = newState; + upd.type = UpdateNode.Type.REDSTONE; + upd.visited = true; + nodeCache.put(pos, upd); + propagateChanges(worldIn, upd, 0); + + // Perform the walk over all directly reachable redstone wire blocks, propagating wire value + // updates in a breadth first order out from the initial update received for the block at 'pos'. + breadthFirstWalk(worldIn); + + // With the whole search completed, clear the list of all known blocks. + // We do not want to keep around state information that may be changed by other code. + // In theory, we could cache the neighbor block positions, but that is a separate + // optimization. + nodeCache.clear(); + + return newState; + } + + // For any array of neighbors in an UpdateNode object, these are always + // the indices of the four immediate neighbors at the same Y coordinate. + private static final int[] rs_neighbors = {4, 5, 6, 7}; + private static final int[] rs_neighbors_up = {9, 11, 13, 15}; + private static final int[] rs_neighbors_dn = {8, 10, 12, 14}; + + /* + * Updated calculateCurrentChanges that is optimized for speed and uses + * the UpdateNode's neighbor array to find the redstone states of neighbors + * that might power it. + */ + private IBlockData calculateCurrentChanges(final World worldIn, final UpdateNode upd) { + IBlockData state = upd.currentState; + final int i = state.get(BlockRedstoneWire.POWER).intValue(); + int j = 0; + j = getMaxCurrentStrength(upd, j); + int l = 0; + + wire.setCanProvidePower(false); + // Unfortunately, World.isBlockIndirectlyGettingPowered is complicated, + // and I'm not ready to try to replicate even more functionality from + // elsewhere in Minecraft into this accelerator. So sadly, we must + // suffer the performance hit of this very expensive call. If there + // is consistency to what this call returns, we may be able to cache it. + final int k = worldIn.isBlockIndirectlyGettingPowered(upd.self); + wire.setCanProvidePower(true); + + // The variable 'k' holds the maximum redstone power value of any adjacent blocks. + // If 'k' has the highest level of all neighbors, then the power level of this + // redstone wire will be set to 'k'. If 'k' is already 15, then nothing inside the + // following loop can affect the power level of the wire. Therefore, the loop is + // skipped if k is already 15. + if (k < 15) { + if (upd.neighbor_nodes == null) { + // If this node's neighbors are not known, expand the node + findNeighbors(worldIn, upd); + } + + // These remain constant, so pull them out of the loop. + // Regardless of which direction is forward, the UpdateNode for the + // position directly above the node being calculated is always + // at index 1. + UpdateNode center_up = upd.neighbor_nodes[1]; + boolean center_up_is_cube = center_up.currentState.isOccluding(); + + for (int m = 0; m < 4; m++) { + // Get the neighbor array index of each of the four cardinal + // neighbors. + int n = rs_neighbors[m]; + + // Get the max redstone power level of each of the cardinal + // neighbors + UpdateNode neighbor = upd.neighbor_nodes[n]; + l = getMaxCurrentStrength(neighbor, l); + + // Also check the positions above and below the cardinal + // neighbors + boolean neighbor_is_cube = neighbor.currentState.isOccluding(); + if (!neighbor_is_cube) { + UpdateNode neighbor_down = upd.neighbor_nodes[rs_neighbors_dn[m]]; + l = getMaxCurrentStrength(neighbor_down, l); + } else + if (!center_up_is_cube) { + UpdateNode neighbor_up = upd.neighbor_nodes[rs_neighbors_up[m]]; + l = getMaxCurrentStrength(neighbor_up, l); + } + } + } + + // The new code sets this RedstoneWire block's power level to the highest neighbor + // minus 1. This usually results in wire power levels dropping by 2 at a time. + // This optimization alone has no impact on update order, only the number of updates. + j = l - 1; + + // If 'l' turns out to be zero, then j will be set to -1, but then since 'k' will + // always be in the range of 0 to 15, the following if will correct that. + if (k > j) j = k; + + // egg82's amendment + // Adding Bukkit's BlockRedstoneEvent - er.. event. + if (i != j) { + BlockRedstoneEvent event = new BlockRedstoneEvent(worldIn.getWorld().getBlockAt(upd.self.getX(), upd.self.getY(), upd.self.getZ()), i, j); + worldIn.getServer().getPluginManager().callEvent(event); + j = event.getNewCurrent(); + } + + if (i != j) { + // If the power level has changed from its previous value, compute a new state + // and set it in the world. + // Possible optimization: Don't commit state changes to the world until they + // need to be known by some nearby non-redstone-wire block. + state = state.set(BlockRedstoneWire.POWER, Integer.valueOf(j)); + worldIn.setTypeAndData(upd.self, state, 2); + } + + return state; + } + + /* + * Optimized function to compute a redstone wire's power level based on cached + * state. + */ + private static int getMaxCurrentStrength(final UpdateNode upd, final int strength) { + if (upd.type != UpdateNode.Type.REDSTONE) return strength; + final int i = upd.currentState.get(BlockRedstoneWire.POWER).intValue(); + return i > strength ? i : strength; + } +} diff --git a/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java b/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java new file mode 100644 index 000000000000..23f1447cfcef --- /dev/null +++ b/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java @@ -0,0 +1,101 @@ +package com.mojang.authlib.yggdrasil; + +import com.google.common.base.Strings; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; +import com.mojang.authlib.Agent; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.GameProfileRepository; +import com.mojang.authlib.HttpAuthenticationService; +import com.mojang.authlib.ProfileLookupCallback; +import com.mojang.authlib.exceptions.AuthenticationException; +import com.mojang.authlib.yggdrasil.response.ProfileSearchResultsResponse; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.List; +import java.util.Set; + +public class YggdrasilGameProfileRepository implements GameProfileRepository { + private static final Logger LOGGER = LogManager.getLogger(); + private static final String BASE_URL = "https://api.mojang.com/"; + private static final String SEARCH_PAGE_URL = BASE_URL + "profiles/"; + private static final int ENTRIES_PER_PAGE = 2; + private static final int MAX_FAIL_COUNT = 3; + private static final int DELAY_BETWEEN_PAGES = 100; + private static final int DELAY_BETWEEN_FAILURES = 750; + + private final YggdrasilAuthenticationService authenticationService; + + public YggdrasilGameProfileRepository(final YggdrasilAuthenticationService authenticationService) { + this.authenticationService = authenticationService; + } + + @Override + public void findProfilesByNames(final String[] names, final Agent agent, final ProfileLookupCallback callback) { + final Set criteria = Sets.newHashSet(); + + for (final String name : names) { + if (!Strings.isNullOrEmpty(name)) { + criteria.add(name.toLowerCase()); + } + } + + final int page = 0; + boolean hasRequested = false; // Paper + + for (final List request : Iterables.partition(criteria, ENTRIES_PER_PAGE)) { + int failCount = 0; + boolean failed; + + do { + failed = false; + + try { + final ProfileSearchResultsResponse response = authenticationService.makeRequest(HttpAuthenticationService.constantURL(SEARCH_PAGE_URL + agent.getName().toLowerCase()), request, ProfileSearchResultsResponse.class); + failCount = 0; + + LOGGER.debug("Page {} returned {} results, parsing", page, response.getProfiles().length); + + final Set missing = Sets.newHashSet(request); + for (final GameProfile profile : response.getProfiles()) { + LOGGER.debug("Successfully looked up profile {}", profile); + missing.remove(profile.getName().toLowerCase()); + callback.onProfileLookupSucceeded(profile); + } + + for (final String name : missing) { + LOGGER.debug("Couldn't find profile {}", name); + callback.onProfileLookupFailed(new GameProfile(null, name), new ProfileNotFoundException("Server did not find the requested profile")); + } + // Paper start + if (!hasRequested) { + hasRequested = true; + continue; + } + // Paper end + + try { + Thread.sleep(DELAY_BETWEEN_PAGES); + } catch (final InterruptedException ignored) { + } + } catch (final AuthenticationException e) { + failCount++; + + if (failCount == MAX_FAIL_COUNT) { + for (final String name : request) { + LOGGER.debug("Couldn't find profile {} because of a server error", name); + callback.onProfileLookupFailed(new GameProfile(null, name), e); + } + } else { + try { + Thread.sleep(DELAY_BETWEEN_FAILURES); + } catch (final InterruptedException ignored) { + } + failed = true; + } + } + } while (failed); + } + } +} diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java new file mode 100644 index 000000000000..98592a3e6359 --- /dev/null +++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java @@ -0,0 +1,207 @@ +package com.mojang.brigadier.tree; + +import com.google.common.collect.ComparisonChain; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.mojang.brigadier.AmbiguityConsumer; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.RedirectModifier; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.context.CommandContextBuilder; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import net.minecraft.server.CommandListenerWrapper; // CraftBukkit + +public abstract class CommandNode implements Comparable> { + private Map> children = Maps.newLinkedHashMap(); + private Map> literals = Maps.newLinkedHashMap(); + private Map> arguments = Maps.newLinkedHashMap(); + private final Predicate requirement; + private final CommandNode redirect; + private final RedirectModifier modifier; + private final boolean forks; + private Command command; + // CraftBukkit start + public void removeCommand(String name) { + children.remove(name); + literals.remove(name); + arguments.remove(name); + } + // CraftBukkit end + + protected CommandNode(final Command command, final Predicate requirement, final CommandNode redirect, final RedirectModifier modifier, final boolean forks) { + this.command = command; + this.requirement = requirement; + this.redirect = redirect; + this.modifier = modifier; + this.forks = forks; + } + + public Command getCommand() { + return command; + } + + public Collection> getChildren() { + return children.values(); + } + + public CommandNode getChild(final String name) { + return children.get(name); + } + + public CommandNode getRedirect() { + return redirect; + } + + public RedirectModifier getRedirectModifier() { + return modifier; + } + + public boolean canUse(final S source) { + // CraftBukkit start + if (source instanceof CommandListenerWrapper) { + try { + ((CommandListenerWrapper) source).currentCommand = this; + return requirement.test(source); + } finally { + ((CommandListenerWrapper) source).currentCommand = null; + } + } + // CraftBukkit end + return requirement.test(source); + } + + public void addChild(final CommandNode node) { + if (node instanceof RootCommandNode) { + throw new UnsupportedOperationException("Cannot add a RootCommandNode as a child to any other CommandNode"); + } + + final CommandNode child = children.get(node.getName()); + if (child != null) { + // We've found something to merge onto + if (node.getCommand() != null) { + child.command = node.getCommand(); + } + for (final CommandNode grandchild : node.getChildren()) { + child.addChild(grandchild); + } + } else { + children.put(node.getName(), node); + if (node instanceof LiteralCommandNode) { + literals.put(node.getName(), (LiteralCommandNode) node); + } else if (node instanceof ArgumentCommandNode) { + arguments.put(node.getName(), (ArgumentCommandNode) node); + } + } + + children = children.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); + } + + public void findAmbiguities(final AmbiguityConsumer consumer) { + Set matches = Sets.newHashSet(); + + for (final CommandNode child : children.values()) { + for (final CommandNode sibling : children.values()) { + if (child == sibling) { + continue; + } + + for (final String input : child.getExamples()) { + if (sibling.isValidInput(input)) { + matches.add(input); + } + } + + if (matches.size() > 0) { + consumer.ambiguous(this, child, sibling, matches); + matches = Sets.newHashSet(); + } + } + + child.findAmbiguities(consumer); + } + } + + protected abstract boolean isValidInput(final String input); + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (!(o instanceof CommandNode)) return false; + + final CommandNode that = (CommandNode) o; + + if (!children.equals(that.children)) return false; + if (command != null ? !command.equals(that.command) : that.command != null) return false; + + return true; + } + + @Override + public int hashCode() { + return 31 * children.hashCode() + (command != null ? command.hashCode() : 0); + } + + public Predicate getRequirement() { + return requirement; + } + + public abstract String getName(); + + public abstract String getUsageText(); + + public abstract void parse(StringReader reader, CommandContextBuilder contextBuilder) throws CommandSyntaxException; + + public abstract CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) throws CommandSyntaxException; + + public abstract ArgumentBuilder createBuilder(); + + protected abstract String getSortedKey(); + + public Collection> getRelevantNodes(final StringReader input) { + if (literals.size() > 0) { + final int cursor = input.getCursor(); + while (input.canRead() && input.peek() != ' ') { + input.skip(); + } + final String text = input.getString().substring(cursor, input.getCursor()); + input.setCursor(cursor); + final LiteralCommandNode literal = literals.get(text); + if (literal != null) { + return Collections.singleton(literal); + } else { + return arguments.values(); + } + } else { + return arguments.values(); + } + } + + @Override + public int compareTo(final CommandNode o) { + return ComparisonChain + .start() + .compareTrueFirst(this instanceof LiteralCommandNode, o instanceof LiteralCommandNode) + .compare(getSortedKey(), o.getSortedKey()) + .result(); + } + + public boolean isFork() { + return forks; + } + + public abstract Collection getExamples(); +} diff --git a/src/main/java/net/minecraft/server/Advancement.java b/src/main/java/net/minecraft/server/Advancement.java new file mode 100644 index 000000000000..b88d0207e12b --- /dev/null +++ b/src/main/java/net/minecraft/server/Advancement.java @@ -0,0 +1,436 @@ +package net.minecraft.server; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import java.util.function.Consumer; +import java.util.function.Function; +import javax.annotation.Nullable; +import org.apache.commons.lang3.ArrayUtils; + +public class Advancement { + + private final Advancement a; + private final AdvancementDisplay b; + private final AdvancementRewards c; + private final MinecraftKey d; + private final Map e; + private final String[][] f; + private final Set g = Sets.newLinkedHashSet(); + private final IChatBaseComponent h; + public final org.bukkit.advancement.Advancement bukkit = new org.bukkit.craftbukkit.advancement.CraftAdvancement(this); // CraftBukkit + + public Advancement(MinecraftKey minecraftkey, @Nullable Advancement advancement, @Nullable AdvancementDisplay advancementdisplay, AdvancementRewards advancementrewards, Map map, String[][] astring) { + this.d = minecraftkey; + this.b = advancementdisplay; + this.e = ImmutableMap.copyOf(map); + this.a = advancement; + this.c = advancementrewards; + this.f = astring; + if (advancement != null) { + advancement.a(this); + } + + if (advancementdisplay == null) { + this.h = new ChatComponentText(minecraftkey.toString()); + } else { + IChatBaseComponent ichatbasecomponent = advancementdisplay.a(); + EnumChatFormat enumchatformat = advancementdisplay.e().c(); + IChatBaseComponent ichatbasecomponent1 = ichatbasecomponent.h().a(enumchatformat).a("\n").addSibling(advancementdisplay.b()); + IChatBaseComponent ichatbasecomponent2 = ichatbasecomponent.h().a((chatmodifier) -> { + chatmodifier.setChatHoverable(new ChatHoverable(ChatHoverable.EnumHoverAction.SHOW_TEXT, ichatbasecomponent1)); + }); + + this.h = (new ChatComponentText("[")).addSibling(ichatbasecomponent2).a("]").a(enumchatformat); + } + + } + + public Advancement.SerializedAdvancement a() { + return new Advancement.SerializedAdvancement(this.a == null ? null : this.a.getName(), this.b, this.c, this.e, this.f); + } + + @Nullable + public Advancement b() { + return this.a; + } + + @Nullable + public AdvancementDisplay c() { + return this.b; + } + + public AdvancementRewards d() { + return this.c; + } + + public String toString() { + return "SimpleAdvancement{id=" + this.getName() + ", parent=" + (this.a == null ? "null" : this.a.getName()) + ", display=" + this.b + ", rewards=" + this.c + ", criteria=" + this.e + ", requirements=" + Arrays.deepToString(this.f) + '}'; + } + + public Iterable e() { + return this.g; + } + + public Map getCriteria() { + return this.e; + } + + public void a(Advancement advancement) { + this.g.add(advancement); + } + + public MinecraftKey getName() { + return this.d; + } + + public boolean equals(Object object) { + if (this == object) { + return true; + } else if (!(object instanceof Advancement)) { + return false; + } else { + Advancement advancement = (Advancement) object; + + return this.d.equals(advancement.d); + } + } + + public int hashCode() { + return this.d.hashCode(); + } + + public String[][] i() { + return this.f; + } + + public IChatBaseComponent j() { + return this.h; + } + + public static class SerializedAdvancement { + + private MinecraftKey a; + private Advancement b; + private AdvancementDisplay c; + private AdvancementRewards d; + private Map e; + private String[][] f; + private AdvancementRequirements g; + + private SerializedAdvancement(@Nullable MinecraftKey minecraftkey, @Nullable AdvancementDisplay advancementdisplay, AdvancementRewards advancementrewards, Map map, String[][] astring) { + this.d = AdvancementRewards.a; + this.e = Maps.newLinkedHashMap(); + this.g = AdvancementRequirements.AND; + this.a = minecraftkey; + this.c = advancementdisplay; + this.d = advancementrewards; + this.e = map; + this.f = astring; + } + + private SerializedAdvancement() { + this.d = AdvancementRewards.a; + this.e = Maps.newLinkedHashMap(); + this.g = AdvancementRequirements.AND; + } + + public static Advancement.SerializedAdvancement a() { + return new Advancement.SerializedAdvancement(); + } + + public Advancement.SerializedAdvancement a(Advancement advancement) { + this.b = advancement; + return this; + } + + public Advancement.SerializedAdvancement a(MinecraftKey minecraftkey) { + this.a = minecraftkey; + return this; + } + + public Advancement.SerializedAdvancement a(IMaterial imaterial, IChatBaseComponent ichatbasecomponent, IChatBaseComponent ichatbasecomponent1, @Nullable MinecraftKey minecraftkey, AdvancementFrameType advancementframetype, boolean flag, boolean flag1, boolean flag2) { + return this.a(new AdvancementDisplay(new ItemStack(imaterial.getItem()), ichatbasecomponent, ichatbasecomponent1, minecraftkey, advancementframetype, flag, flag1, flag2)); + } + + public Advancement.SerializedAdvancement a(AdvancementDisplay advancementdisplay) { + this.c = advancementdisplay; + return this; + } + + public Advancement.SerializedAdvancement a(AdvancementRewards.a advancementrewards_a) { + return this.a(advancementrewards_a.a()); + } + + public Advancement.SerializedAdvancement a(AdvancementRewards advancementrewards) { + this.d = advancementrewards; + return this; + } + + public Advancement.SerializedAdvancement a(String s, CriterionInstance criterioninstance) { + return this.a(s, new Criterion(criterioninstance)); + } + + public Advancement.SerializedAdvancement a(String s, Criterion criterion) { + if (this.e.containsKey(s)) { + throw new IllegalArgumentException("Duplicate criterion " + s); + } else { + this.e.put(s, criterion); + return this; + } + } + + public Advancement.SerializedAdvancement a(AdvancementRequirements advancementrequirements) { + this.g = advancementrequirements; + return this; + } + + public boolean a(Function function) { + if (this.a == null) { + return true; + } else { + if (this.b == null) { + this.b = (Advancement) function.apply(this.a); + } + + return this.b != null; + } + } + + public Advancement b(MinecraftKey minecraftkey) { + if (!this.a((Function) (minecraftkey1) -> { // CraftBukkit - decompile error + return null; + })) { + throw new IllegalStateException("Tried to build incomplete advancement!"); + } else { + if (this.f == null) { + this.f = this.g.createRequirements(this.e.keySet()); + } + + return new Advancement(minecraftkey, this.b, this.c, this.d, this.e, this.f); + } + } + + public Advancement a(Consumer consumer, String s) { + Advancement advancement = this.b(new MinecraftKey(s)); + + consumer.accept(advancement); + return advancement; + } + + public JsonObject b() { + if (this.f == null) { + this.f = this.g.createRequirements(this.e.keySet()); + } + + JsonObject jsonobject = new JsonObject(); + + if (this.b != null) { + jsonobject.addProperty("parent", this.b.getName().toString()); + } else if (this.a != null) { + jsonobject.addProperty("parent", this.a.toString()); + } + + if (this.c != null) { + jsonobject.add("display", this.c.k()); + } + + jsonobject.add("rewards", this.d.b()); + JsonObject jsonobject1 = new JsonObject(); + Iterator iterator = this.e.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + + jsonobject1.add((String) entry.getKey(), ((Criterion) entry.getValue()).b()); + } + + jsonobject.add("criteria", jsonobject1); + JsonArray jsonarray = new JsonArray(); + String[][] astring = this.f; + int i = astring.length; + + for (int j = 0; j < i; ++j) { + String[] astring1 = astring[j]; + JsonArray jsonarray1 = new JsonArray(); + String[] astring2 = astring1; + int k = astring1.length; + + for (int l = 0; l < k; ++l) { + String s = astring2[l]; + + jsonarray1.add(s); + } + + jsonarray.add(jsonarray1); + } + + jsonobject.add("requirements", jsonarray); + return jsonobject; + } + + public void a(PacketDataSerializer packetdataserializer) { + if (this.a == null) { + packetdataserializer.writeBoolean(false); + } else { + packetdataserializer.writeBoolean(true); + packetdataserializer.a(this.a); + } + + if (this.c == null) { + packetdataserializer.writeBoolean(false); + } else { + packetdataserializer.writeBoolean(true); + this.c.a(packetdataserializer); + } + + Criterion.a(this.e, packetdataserializer); + packetdataserializer.d(this.f.length); + String[][] astring = this.f; + int i = astring.length; + + for (int j = 0; j < i; ++j) { + String[] astring1 = astring[j]; + + packetdataserializer.d(astring1.length); + String[] astring2 = astring1; + int k = astring1.length; + + for (int l = 0; l < k; ++l) { + String s = astring2[l]; + + packetdataserializer.a(s); + } + } + + } + + public String toString() { + return "Task Advancement{parentId=" + this.a + ", display=" + this.c + ", rewards=" + this.d + ", criteria=" + this.e + ", requirements=" + Arrays.deepToString(this.f) + '}'; + } + + public static Advancement.SerializedAdvancement a(JsonObject jsonobject, JsonDeserializationContext jsondeserializationcontext) { + MinecraftKey minecraftkey = jsonobject.has("parent") ? new MinecraftKey(ChatDeserializer.h(jsonobject, "parent")) : null; + AdvancementDisplay advancementdisplay = jsonobject.has("display") ? AdvancementDisplay.a(ChatDeserializer.t(jsonobject, "display"), jsondeserializationcontext) : null; + AdvancementRewards advancementrewards = (AdvancementRewards) ChatDeserializer.a(jsonobject, "rewards", AdvancementRewards.a, jsondeserializationcontext, AdvancementRewards.class); + Map map = Criterion.b(ChatDeserializer.t(jsonobject, "criteria"), jsondeserializationcontext); + + if (map.isEmpty()) { + throw new JsonSyntaxException("Advancement criteria cannot be empty"); + } else { + JsonArray jsonarray = ChatDeserializer.a(jsonobject, "requirements", new JsonArray()); + String[][] astring = new String[jsonarray.size()][]; + + int i; + int j; + + for (i = 0; i < jsonarray.size(); ++i) { + JsonArray jsonarray1 = ChatDeserializer.n(jsonarray.get(i), "requirements[" + i + "]"); + + astring[i] = new String[jsonarray1.size()]; + + for (j = 0; j < jsonarray1.size(); ++j) { + astring[i][j] = ChatDeserializer.a(jsonarray1.get(j), "requirements[" + i + "][" + j + "]"); + } + } + + if (astring.length == 0) { + astring = new String[map.size()][]; + i = 0; + + String s; + + for (Iterator iterator = map.keySet().iterator(); iterator.hasNext(); astring[i++] = new String[] { s}) { + s = (String) iterator.next(); + } + } + + String[][] astring1 = astring; + int k = astring.length; + + int l; + + for (j = 0; j < k; ++j) { + String[] astring2 = astring1[j]; + + if (astring2.length == 0 && map.isEmpty()) { + throw new JsonSyntaxException("Requirement entry cannot be empty"); + } + + String[] astring3 = astring2; + + l = astring2.length; + + for (int i1 = 0; i1 < l; ++i1) { + String s1 = astring3[i1]; + + if (!map.containsKey(s1)) { + throw new JsonSyntaxException("Unknown required criterion '" + s1 + "'"); + } + } + } + + Iterator iterator1 = map.keySet().iterator(); + + while (iterator1.hasNext()) { + String s2 = (String) iterator1.next(); + boolean flag = false; + String[][] astring4 = astring; + int j1 = astring.length; + + l = 0; + + while (true) { + if (l < j1) { + String[] astring5 = astring4[l]; + + if (!ArrayUtils.contains(astring5, s2)) { + ++l; + continue; + } + + flag = true; + } + + if (!flag) { + throw new JsonSyntaxException("Criterion '" + s2 + "' isn't a requirement for completion. This isn't supported behaviour, all criteria must be required."); + } + break; + } + } + + return new Advancement.SerializedAdvancement(minecraftkey, advancementdisplay, advancementrewards, map, astring); + } + } + + public static Advancement.SerializedAdvancement b(PacketDataSerializer packetdataserializer) { + MinecraftKey minecraftkey = packetdataserializer.readBoolean() ? packetdataserializer.l() : null; + AdvancementDisplay advancementdisplay = packetdataserializer.readBoolean() ? AdvancementDisplay.b(packetdataserializer) : null; + Map map = Criterion.c(packetdataserializer); + String[][] astring = new String[packetdataserializer.g()][]; + + for (int i = 0; i < astring.length; ++i) { + astring[i] = new String[packetdataserializer.g()]; + + for (int j = 0; j < astring[i].length; ++j) { + astring[i][j] = packetdataserializer.e(32767); + } + } + + return new Advancement.SerializedAdvancement(minecraftkey, advancementdisplay, AdvancementRewards.a, map, astring); + } + + public Map c() { + return this.e; + } + } +} diff --git a/src/main/java/net/minecraft/server/AdvancementDataPlayer.java b/src/main/java/net/minecraft/server/AdvancementDataPlayer.java new file mode 100644 index 000000000000..c7ad71c5f304 --- /dev/null +++ b/src/main/java/net/minecraft/server/AdvancementDataPlayer.java @@ -0,0 +1,466 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.common.io.Files; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.internal.Streams; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.mojang.datafixers.DataFixTypes; +import com.mojang.datafixers.Dynamic; +import com.mojang.datafixers.types.JsonOps; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +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 java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class AdvancementDataPlayer { + + private static final Logger a = LogManager.getLogger(); + private static final Gson b = (new GsonBuilder()).registerTypeAdapter(AdvancementProgress.class, new AdvancementProgress.a()).registerTypeAdapter(MinecraftKey.class, new MinecraftKey.a()).setPrettyPrinting().create(); + private static final TypeToken> c = new TypeToken>() { + }; + private final MinecraftServer d; + private final File e; + public final Map data = Maps.newLinkedHashMap(); + private final Set g = Sets.newLinkedHashSet(); + private final Set h = Sets.newLinkedHashSet(); + private final Set i = Sets.newLinkedHashSet(); + private EntityPlayer player; + @Nullable + private Advancement k; + private boolean l = true; + + public AdvancementDataPlayer(MinecraftServer minecraftserver, File file, EntityPlayer entityplayer) { + this.d = minecraftserver; + this.e = file; + this.player = entityplayer; + this.g(); + } + + public void a(EntityPlayer entityplayer) { + this.player = entityplayer; + } + + public void a() { + Iterator iterator = CriterionTriggers.a().iterator(); + + while (iterator.hasNext()) { + CriterionTrigger criteriontrigger = (CriterionTrigger) iterator.next(); + + criteriontrigger.a(this); + } + + } + + public void b() { + this.a(); + this.data.clear(); + this.g.clear(); + this.h.clear(); + this.i.clear(); + this.l = true; + this.k = null; + this.g(); + } + + private void d() { + Iterator iterator = this.d.getAdvancementData().b().iterator(); + + while (iterator.hasNext()) { + Advancement advancement = (Advancement) iterator.next(); + + this.c(advancement); + } + + } + + private void e() { + List list = Lists.newArrayList(); + Iterator iterator = this.data.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + + if (((AdvancementProgress) entry.getValue()).isDone()) { + list.add(entry.getKey()); + this.i.add(entry.getKey()); + } + } + + iterator = list.iterator(); + + while (iterator.hasNext()) { + Advancement advancement = (Advancement) iterator.next(); + + this.e(advancement); + } + + } + + private void f() { + Iterator iterator = this.d.getAdvancementData().b().iterator(); + + while (iterator.hasNext()) { + Advancement advancement = (Advancement) iterator.next(); + + if (advancement.getCriteria().isEmpty()) { + this.grantCriteria(advancement, ""); + advancement.d().a(this.player); + } + } + + } + + private void g() { + if (this.e.isFile()) { + try { + JsonReader jsonreader = new JsonReader(new StringReader(Files.toString(this.e, StandardCharsets.UTF_8))); + Throwable throwable = null; + + try { + jsonreader.setLenient(false); + Dynamic dynamic = new Dynamic(JsonOps.INSTANCE, Streams.parse(jsonreader)); + + if (!dynamic.get("DataVersion").flatMap(Dynamic::getNumberValue).isPresent()) { + dynamic = dynamic.set("DataVersion", dynamic.createInt(1343)); + } + + dynamic = this.d.az().update(DataFixTypes.ADVANCEMENTS, dynamic, dynamic.getInt("DataVersion"), 1631); + dynamic = dynamic.remove("DataVersion"); + Map map = (Map) AdvancementDataPlayer.b.getAdapter(AdvancementDataPlayer.c).fromJsonTree((JsonElement) dynamic.getValue()); + + if (map == null) { + throw new JsonParseException("Found null for advancements"); + } + + Stream> stream = map.entrySet().stream().sorted(Comparator.comparing(Entry::getValue)); + Iterator iterator = ((List) stream.collect(Collectors.toList())).iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + Advancement advancement = this.d.getAdvancementData().a((MinecraftKey) entry.getKey()); + + if (advancement == null) { + // CraftBukkit start + if (((MinecraftKey) entry.getKey()).b().equals("minecraft")) { + AdvancementDataPlayer.a.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", entry.getKey(), this.e); + } + // CraftBukkit end + } else { + this.a(advancement, (AdvancementProgress) entry.getValue()); + } + } + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (jsonreader != null) { + if (throwable != null) { + try { + jsonreader.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + jsonreader.close(); + } + } + + } + } catch (JsonParseException jsonparseexception) { + AdvancementDataPlayer.a.error("Couldn't parse player advancements in {}", this.e, jsonparseexception); + } catch (IOException ioexception) { + AdvancementDataPlayer.a.error("Couldn't access player advancements in {}", this.e, ioexception); + } + } + + this.f(); + this.e(); + this.d(); + } + + public void c() { + if (org.spigotmc.SpigotConfig.disableAdvancementSaving) return; + Map map = Maps.newHashMap(); + Iterator iterator = this.data.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + AdvancementProgress advancementprogress = (AdvancementProgress) entry.getValue(); + + if (advancementprogress.b()) { + map.put(((Advancement) entry.getKey()).getName(), advancementprogress); + } + } + + if (this.e.getParentFile() != null) { + this.e.getParentFile().mkdirs(); + } + + try { + Files.write(AdvancementDataPlayer.b.toJson(map), this.e, StandardCharsets.UTF_8); + } catch (IOException ioexception) { + AdvancementDataPlayer.a.error("Couldn't save player advancements to {}", this.e, ioexception); + } + + } + + public boolean grantCriteria(Advancement advancement, String s) { + boolean flag = false; + AdvancementProgress advancementprogress = this.getProgress(advancement); + boolean flag1 = advancementprogress.isDone(); + + if (advancementprogress.a(s)) { + // Paper start + if (!new com.destroystokyo.paper.event.player.PlayerAdvancementCriterionGrantEvent(this.player.getBukkitEntity(), advancement.bukkit, s).callEvent()) { + advancementprogress.b(s); + return false; + } + // Paper end + this.d(advancement); + this.i.add(advancement); + flag = true; + if (!flag1 && advancementprogress.isDone()) { + this.player.world.getServer().getPluginManager().callEvent(new org.bukkit.event.player.PlayerAdvancementDoneEvent(this.player.getBukkitEntity(), advancement.bukkit)); // CraftBukkit + advancement.d().a(this.player); + if (advancement.c() != null && advancement.c().i() && this.player.world.getGameRules().getBoolean("announceAdvancements")) { + this.d.getPlayerList().sendMessage(new ChatMessage("chat.type.advancement." + advancement.c().e().a(), new Object[] { this.player.getScoreboardDisplayName(), advancement.j()})); + } + } + } + + if (advancementprogress.isDone()) { + this.e(advancement); + } + + return flag; + } + + public boolean revokeCritera(Advancement advancement, String s) { + boolean flag = false; + AdvancementProgress advancementprogress = this.getProgress(advancement); + + if (advancementprogress.b(s)) { + this.c(advancement); + this.i.add(advancement); + flag = true; + } + + if (!advancementprogress.b()) { + this.e(advancement); + } + + return flag; + } + + private void c(Advancement advancement) { + AdvancementProgress advancementprogress = this.getProgress(advancement); + + if (!advancementprogress.isDone()) { + Iterator iterator = advancement.getCriteria().entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + CriterionProgress criterionprogress = advancementprogress.getCriterionProgress((String) entry.getKey()); + + if (criterionprogress != null && !criterionprogress.a()) { + CriterionInstance criterioninstance = ((Criterion) entry.getValue()).a(); + + if (criterioninstance != null) { + CriterionTrigger criteriontrigger = CriterionTriggers.a(criterioninstance.a()); + + if (criteriontrigger != null) { + criteriontrigger.a(this, new CriterionTrigger.a<>(criterioninstance, advancement, (String) entry.getKey())); + } + } + } + } + + } + } + + private void d(Advancement advancement) { + AdvancementProgress advancementprogress = this.getProgress(advancement); + Iterator iterator = advancement.getCriteria().entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + CriterionProgress criterionprogress = advancementprogress.getCriterionProgress((String) entry.getKey()); + + if (criterionprogress != null && (criterionprogress.a() || advancementprogress.isDone())) { + CriterionInstance criterioninstance = ((Criterion) entry.getValue()).a(); + + if (criterioninstance != null) { + CriterionTrigger criteriontrigger = CriterionTriggers.a(criterioninstance.a()); + + if (criteriontrigger != null) { + criteriontrigger.b(this, new CriterionTrigger.a<>(criterioninstance, advancement, (String) entry.getKey())); + } + } + } + } + + } + + public void b(EntityPlayer entityplayer) { + if (this.l || !this.h.isEmpty() || !this.i.isEmpty()) { + Map map = Maps.newHashMap(); + Set set = Sets.newLinkedHashSet(); + Set set1 = Sets.newLinkedHashSet(); + Iterator iterator = this.i.iterator(); + + Advancement advancement; + + while (iterator.hasNext()) { + advancement = (Advancement) iterator.next(); + if (this.g.contains(advancement)) { + map.put(advancement.getName(), this.data.get(advancement)); + } + } + + iterator = this.h.iterator(); + + while (iterator.hasNext()) { + advancement = (Advancement) iterator.next(); + if (this.g.contains(advancement)) { + set.add(advancement); + } else { + set1.add(advancement.getName()); + } + } + + if (this.l || !map.isEmpty() || !set.isEmpty() || !set1.isEmpty()) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutAdvancements(this.l, set, set1, map)); + this.h.clear(); + this.i.clear(); + } + } + + this.l = false; + } + + public void a(@Nullable Advancement advancement) { + Advancement advancement1 = this.k; + + if (advancement != null && advancement.b() == null && advancement.c() != null) { + this.k = advancement; + } else { + this.k = null; + } + + if (advancement1 != this.k) { + this.player.playerConnection.sendPacket(new PacketPlayOutSelectAdvancementTab(this.k == null ? null : this.k.getName())); + } + + } + + public AdvancementProgress getProgress(Advancement advancement) { + AdvancementProgress advancementprogress = (AdvancementProgress) this.data.get(advancement); + + if (advancementprogress == null) { + advancementprogress = new AdvancementProgress(); + this.a(advancement, advancementprogress); + } + + return advancementprogress; + } + + private void a(Advancement advancement, AdvancementProgress advancementprogress) { + advancementprogress.a(advancement.getCriteria(), advancement.i()); + this.data.put(advancement, advancementprogress); + } + + private void e(Advancement advancement) { + boolean flag = this.f(advancement); + boolean flag1 = this.g.contains(advancement); + + if (flag && !flag1) { + this.g.add(advancement); + this.h.add(advancement); + if (this.data.containsKey(advancement)) { + this.i.add(advancement); + } + } else if (!flag && flag1) { + this.g.remove(advancement); + this.h.add(advancement); + } + + if (flag != flag1 && advancement.b() != null) { + this.e(advancement.b()); + } + + Iterator iterator = advancement.e().iterator(); + + while (iterator.hasNext()) { + Advancement advancement1 = (Advancement) iterator.next(); + + this.e(advancement1); + } + + } + + private boolean f(Advancement advancement) { + for (int i = 0; advancement != null && i <= 2; ++i) { + if (i == 0 && this.g(advancement)) { + return true; + } + + if (advancement.c() == null) { + return false; + } + + AdvancementProgress advancementprogress = this.getProgress(advancement); + + if (advancementprogress.isDone()) { + return true; + } + + if (advancement.c().j()) { + return false; + } + + advancement = advancement.b(); + } + + return false; + } + + private boolean g(Advancement advancement) { + AdvancementProgress advancementprogress = this.getProgress(advancement); + + if (advancementprogress.isDone()) { + return true; + } else { + Iterator iterator = advancement.e().iterator(); + + Advancement advancement1; + + do { + if (!iterator.hasNext()) { + return false; + } + + advancement1 = (Advancement) iterator.next(); + } while (!this.g(advancement1)); + + return true; + } + } +} diff --git a/src/main/java/net/minecraft/server/AdvancementDataWorld.java b/src/main/java/net/minecraft/server/AdvancementDataWorld.java new file mode 100644 index 000000000000..9f9e5484d358 --- /dev/null +++ b/src/main/java/net/minecraft/server/AdvancementDataWorld.java @@ -0,0 +1,119 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import java.io.IOException; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import javax.annotation.Nullable; +import org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class AdvancementDataWorld implements IResourcePackListener { + + private static final Logger c = LogManager.getLogger(); + public static final Gson DESERIALIZER = (new GsonBuilder()).registerTypeHierarchyAdapter(Advancement.SerializedAdvancement.class, (com.google.gson.JsonDeserializer) (jsonelement, type, jsondeserializationcontext) -> { + JsonObject jsonobject = ChatDeserializer.m(jsonelement, "advancement"); + + return Advancement.SerializedAdvancement.a(jsonobject, jsondeserializationcontext); + }).registerTypeAdapter(AdvancementRewards.class, new AdvancementRewards.b()).registerTypeHierarchyAdapter(IChatBaseComponent.class, new IChatBaseComponent.ChatSerializer()).registerTypeHierarchyAdapter(ChatModifier.class, new ChatModifier.ChatModifierSerializer()).registerTypeAdapterFactory(new ChatTypeAdapterFactory()).create(); + public static final Advancements REGISTRY = new Advancements(); + public static final int a = "advancements/".length(); + public static final int b = ".json".length(); + private boolean f; + + public AdvancementDataWorld() {} + + private Map b(IResourceManager iresourcemanager) { + Map map = Maps.newHashMap(); + Iterator iterator = iresourcemanager.a("advancements", (s) -> { + return s.endsWith(".json"); + }).iterator(); + + while (iterator.hasNext()) { + MinecraftKey minecraftkey = (MinecraftKey) iterator.next(); + String s = minecraftkey.getKey(); + MinecraftKey minecraftkey1 = new MinecraftKey(minecraftkey.b(), s.substring(AdvancementDataWorld.a, s.length() - AdvancementDataWorld.b)); + + try { + IResource iresource = iresourcemanager.a(minecraftkey); + Throwable throwable = null; + // Spigot start + if (org.spigotmc.SpigotConfig.disabledAdvancements != null && (org.spigotmc.SpigotConfig.disabledAdvancements.contains("*") || org.spigotmc.SpigotConfig.disabledAdvancements.contains(minecraftkey.toString()))) { + continue; + } + // Spigot end + + try { + Advancement.SerializedAdvancement advancement_serializedadvancement = (Advancement.SerializedAdvancement) ChatDeserializer.a(AdvancementDataWorld.DESERIALIZER, IOUtils.toString(iresource.b(), StandardCharsets.UTF_8), Advancement.SerializedAdvancement.class); + + if (advancement_serializedadvancement == null) { + AdvancementDataWorld.c.error("Couldn't load custom advancement {} from {} as it's empty or null", minecraftkey1, minecraftkey); + } else { + map.put(minecraftkey1, advancement_serializedadvancement); + } + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (iresource != null) { + if (throwable != null) { + try { + iresource.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + iresource.close(); + } + } + + } + } catch (IllegalArgumentException | JsonParseException jsonparseexception) { + AdvancementDataWorld.c.error("Parsing error loading custom advancement {}: {}", minecraftkey1, jsonparseexception.getMessage()); + this.f = true; + } catch (IOException ioexception) { + AdvancementDataWorld.c.error("Couldn't read custom advancement {} from {}", minecraftkey1, minecraftkey, ioexception); + this.f = true; + } + } + + return map; + } + + @Nullable + public Advancement a(MinecraftKey minecraftkey) { + return AdvancementDataWorld.REGISTRY.a(minecraftkey); + } + + public Collection b() { + return AdvancementDataWorld.REGISTRY.c(); + } + + public void a(IResourceManager iresourcemanager) { + this.f = false; + AdvancementDataWorld.REGISTRY.a(); + Map map = this.b(iresourcemanager); + + AdvancementDataWorld.REGISTRY.a(map); + Iterator iterator = AdvancementDataWorld.REGISTRY.b().iterator(); + + while (iterator.hasNext()) { + Advancement advancement = (Advancement) iterator.next(); + + if (advancement.c() != null) { + AdvancementTree.a(advancement); + } + } + + } +} diff --git a/src/main/java/net/minecraft/server/Advancements.java b/src/main/java/net/minecraft/server/Advancements.java new file mode 100644 index 000000000000..5d32caff37ee --- /dev/null +++ b/src/main/java/net/minecraft/server/Advancements.java @@ -0,0 +1,109 @@ +package net.minecraft.server; + +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class Advancements { + + private static final Logger a = LogManager.getLogger(); + public final Map advancements = Maps.newHashMap(); + private final Set c = Sets.newLinkedHashSet(); + private final Set d = Sets.newLinkedHashSet(); + private Advancements.a e; + + public Advancements() {} + + public void a(Map map) { + Function function = Functions.forMap(this.advancements, (Object) null); + + label42: + while (!map.isEmpty()) { + boolean flag = false; + Iterator iterator = map.entrySet().iterator(); + + Entry entry; + + while (iterator.hasNext()) { + entry = (Entry) iterator.next(); + MinecraftKey minecraftkey = (MinecraftKey) entry.getKey(); + Advancement.SerializedAdvancement advancement_serializedadvancement = (Advancement.SerializedAdvancement) entry.getValue(); + + if (advancement_serializedadvancement.a((java.util.function.Function) function)) { + Advancement advancement = advancement_serializedadvancement.b(minecraftkey); + + this.advancements.put(minecraftkey, advancement); + flag = true; + iterator.remove(); + if (advancement.b() == null) { + this.c.add(advancement); + if (this.e != null) { + this.e.a(advancement); + } + } else { + this.d.add(advancement); + if (this.e != null) { + this.e.c(advancement); + } + } + } + } + + if (!flag) { + iterator = map.entrySet().iterator(); + + while (true) { + if (!iterator.hasNext()) { + break label42; + } + + entry = (Entry) iterator.next(); + Advancements.a.error("Couldn't load advancement {}: {}", entry.getKey(), entry.getValue()); + } + } + } + + // Advancements.a.info("Loaded {} advancements", this.advancements.size()); // CraftBukkit - moved to AdvancementDataWorld#reload + } + + public void a() { + this.advancements.clear(); + this.c.clear(); + this.d.clear(); + if (this.e != null) { + this.e.a(); + } + + } + + public Iterable b() { + return this.c; + } + + public Collection c() { + return this.advancements.values(); + } + + @Nullable + public Advancement a(MinecraftKey minecraftkey) { + return (Advancement) this.advancements.get(minecraftkey); + } + + public interface a { + + void a(Advancement advancement); + + void c(Advancement advancement); + + void a(); + } +} diff --git a/src/main/java/net/minecraft/server/ArgumentBlock.java b/src/main/java/net/minecraft/server/ArgumentBlock.java new file mode 100644 index 000000000000..f529365817f3 --- /dev/null +++ b/src/main/java/net/minecraft/server/ArgumentBlock.java @@ -0,0 +1,541 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import com.google.common.collect.UnmodifiableIterator; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; +import com.mojang.brigadier.exceptions.Dynamic3CommandExceptionType; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.Map.Entry; +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import javax.annotation.Nullable; + +public class ArgumentBlock { + + public static final SimpleCommandExceptionType a = new SimpleCommandExceptionType(new ChatMessage("argument.block.tag.disallowed", new Object[0])); + public static final DynamicCommandExceptionType b = new DynamicCommandExceptionType((object) -> { + return new ChatMessage("argument.block.id.invalid", new Object[] { object}); + }); + public static final Dynamic2CommandExceptionType c = new Dynamic2CommandExceptionType((object, object1) -> { + return new ChatMessage("argument.block.property.unknown", new Object[] { object, object1}); + }); + public static final Dynamic2CommandExceptionType d = new Dynamic2CommandExceptionType((object, object1) -> { + return new ChatMessage("argument.block.property.duplicate", new Object[] { object1, object}); + }); + public static final Dynamic3CommandExceptionType e = new Dynamic3CommandExceptionType((object, object1, object2) -> { + return new ChatMessage("argument.block.property.invalid", new Object[] { object, object2, object1}); + }); + public static final Dynamic2CommandExceptionType f = new Dynamic2CommandExceptionType((object, object1) -> { + return new ChatMessage("argument.block.property.novalue", new Object[] { object, object1}); + }); + public static final SimpleCommandExceptionType g = new SimpleCommandExceptionType(new ChatMessage("argument.block.property.unclosed", new Object[0])); + private static final Function> h = SuggestionsBuilder::buildFuture; + private final StringReader i; + private final boolean j; + private final Map, Comparable> k = Maps.newLinkedHashMap(); // CraftBukkit - stable + private final Map l = Maps.newHashMap(); + private MinecraftKey m = new MinecraftKey(""); public MinecraftKey getBlockKey() { return this.m; } // Paper - OBFHELPER + private BlockStateList n; + private IBlockData o; + @Nullable + private NBTTagCompound p; + private MinecraftKey q = new MinecraftKey(""); + private int r; + private Function> s; + + public ArgumentBlock(StringReader stringreader, boolean flag) { + this.s = ArgumentBlock.h; + this.i = stringreader; + this.j = flag; + } + + public Map, Comparable> getStateMap() { + return this.k; + } + + @Nullable + public IBlockData getBlockData() { + return this.o; + } + + @Nullable + public NBTTagCompound c() { + return this.p; + } + + public @Nullable MinecraftKey getTagKey() { return d(); } // Paper - OBFHELPER + @Nullable + public MinecraftKey d() { + return this.q; + } + + public ArgumentBlock parse(boolean parseTile) throws CommandSyntaxException { return this.a(parseTile); } // Paper - OBFHELPER + public ArgumentBlock a(boolean flag) throws CommandSyntaxException { + this.s = this::l; + if (this.i.canRead() && this.i.peek() == '#') { + this.f(); + this.s = this::i; + if (this.i.canRead() && this.i.peek() == '[') { + this.h(); + this.s = this::f; + } + } else { + this.e(); + this.s = this::j; + if (this.i.canRead() && this.i.peek() == '[') { + this.g(); + this.s = this::f; + } + } + + if (flag && this.i.canRead() && this.i.peek() == '{') { + this.s = ArgumentBlock.h; + this.i(); + } + + return this; + } + + private CompletableFuture b(SuggestionsBuilder suggestionsbuilder) { + if (suggestionsbuilder.getRemaining().isEmpty()) { + suggestionsbuilder.suggest(String.valueOf(']')); + } + + return this.d(suggestionsbuilder); + } + + private CompletableFuture c(SuggestionsBuilder suggestionsbuilder) { + if (suggestionsbuilder.getRemaining().isEmpty()) { + suggestionsbuilder.suggest(String.valueOf(']')); + } + + return this.e(suggestionsbuilder); + } + + private CompletableFuture d(SuggestionsBuilder suggestionsbuilder) { + String s = suggestionsbuilder.getRemaining().toLowerCase(Locale.ROOT); + Iterator iterator = this.o.a().iterator(); + + while (iterator.hasNext()) { + IBlockState iblockstate = (IBlockState) iterator.next(); + + if (!this.k.containsKey(iblockstate) && iblockstate.a().startsWith(s)) { + suggestionsbuilder.suggest(iblockstate.a() + '='); + } + } + + return suggestionsbuilder.buildFuture(); + } + + private CompletableFuture e(SuggestionsBuilder suggestionsbuilder) { + String s = suggestionsbuilder.getRemaining().toLowerCase(Locale.ROOT); + + if (this.q != null && !this.q.getKey().isEmpty()) { + Tag tag = TagsBlock.a().a(this.q); + + if (tag != null) { + Iterator iterator = tag.a().iterator(); + + while (iterator.hasNext()) { + Block block = (Block) iterator.next(); + Iterator iterator1 = block.getStates().d().iterator(); + + while (iterator1.hasNext()) { + IBlockState iblockstate = (IBlockState) iterator1.next(); + + if (!this.l.containsKey(iblockstate.a()) && iblockstate.a().startsWith(s)) { + suggestionsbuilder.suggest(iblockstate.a() + '='); + } + } + } + } + } + + return suggestionsbuilder.buildFuture(); + } + + private CompletableFuture f(SuggestionsBuilder suggestionsbuilder) { + if (suggestionsbuilder.getRemaining().isEmpty() && this.k()) { + suggestionsbuilder.suggest(String.valueOf('{')); + } + + return suggestionsbuilder.buildFuture(); + } + + private boolean k() { + if (this.o != null) { + return this.o.getBlock().isTileEntity(); + } else { + if (this.q != null) { + Tag tag = TagsBlock.a().a(this.q); + + if (tag != null) { + Iterator iterator = tag.a().iterator(); + + while (iterator.hasNext()) { + Block block = (Block) iterator.next(); + + if (block.isTileEntity()) { + return true; + } + } + } + } + + return false; + } + } + + private CompletableFuture g(SuggestionsBuilder suggestionsbuilder) { + if (suggestionsbuilder.getRemaining().isEmpty()) { + suggestionsbuilder.suggest(String.valueOf('=')); + } + + return suggestionsbuilder.buildFuture(); + } + + private CompletableFuture h(SuggestionsBuilder suggestionsbuilder) { + if (suggestionsbuilder.getRemaining().isEmpty()) { + suggestionsbuilder.suggest(String.valueOf(']')); + } + + if (suggestionsbuilder.getRemaining().isEmpty() && this.k.size() < this.o.a().size()) { + suggestionsbuilder.suggest(String.valueOf(',')); + } + + return suggestionsbuilder.buildFuture(); + } + + private static > SuggestionsBuilder a(SuggestionsBuilder suggestionsbuilder, IBlockState iblockstate) { + Iterator iterator = iblockstate.d().iterator(); + + while (iterator.hasNext()) { + T t0 = (T) iterator.next(); // CraftBukkit - decompile error + + if (t0 instanceof Integer) { + suggestionsbuilder.suggest((Integer) t0); + } else { + suggestionsbuilder.suggest(iblockstate.a(t0)); + } + } + + return suggestionsbuilder; + } + + private CompletableFuture a(SuggestionsBuilder suggestionsbuilder, String s) { + boolean flag = false; + + if (this.q != null && !this.q.getKey().isEmpty()) { + Tag tag = TagsBlock.a().a(this.q); + + if (tag != null) { + Iterator iterator = tag.a().iterator(); + + while (iterator.hasNext()) { + Block block = (Block) iterator.next(); + IBlockState iblockstate = block.getStates().a(s); + + if (iblockstate != null) { + a(suggestionsbuilder, iblockstate); + } + + if (!flag) { + Iterator iterator1 = block.getStates().d().iterator(); + + while (iterator1.hasNext()) { + IBlockState iblockstate1 = (IBlockState) iterator1.next(); + + if (!this.l.containsKey(iblockstate1.a())) { + flag = true; + break; + } + } + } + } + } + } + + if (flag) { + suggestionsbuilder.suggest(String.valueOf(',')); + } + + suggestionsbuilder.suggest(String.valueOf(']')); + return suggestionsbuilder.buildFuture(); + } + + private CompletableFuture i(SuggestionsBuilder suggestionsbuilder) { + if (suggestionsbuilder.getRemaining().isEmpty()) { + Tag tag = TagsBlock.a().a(this.q); + + if (tag != null) { + boolean flag = false; + boolean flag1 = false; + Iterator iterator = tag.a().iterator(); + + while (iterator.hasNext()) { + Block block = (Block) iterator.next(); + + flag |= !block.getStates().d().isEmpty(); + flag1 |= block.isTileEntity(); + if (flag && flag1) { + break; + } + } + + if (flag) { + suggestionsbuilder.suggest(String.valueOf('[')); + } + + if (flag1) { + suggestionsbuilder.suggest(String.valueOf('{')); + } + } + } + + return this.k(suggestionsbuilder); + } + + private CompletableFuture j(SuggestionsBuilder suggestionsbuilder) { + if (suggestionsbuilder.getRemaining().isEmpty()) { + if (!this.o.getBlock().getStates().d().isEmpty()) { + suggestionsbuilder.suggest(String.valueOf('[')); + } + + if (this.o.getBlock().isTileEntity()) { + suggestionsbuilder.suggest(String.valueOf('{')); + } + } + + return suggestionsbuilder.buildFuture(); + } + + private CompletableFuture k(SuggestionsBuilder suggestionsbuilder) { + return ICompletionProvider.a((Iterable) TagsBlock.a().a(), suggestionsbuilder.createOffset(this.r).add(suggestionsbuilder)); + } + + private CompletableFuture l(SuggestionsBuilder suggestionsbuilder) { + if (this.j) { + ICompletionProvider.a((Iterable) TagsBlock.a().a(), suggestionsbuilder, String.valueOf('#')); + } + + ICompletionProvider.a((Iterable) IRegistry.BLOCK.keySet(), suggestionsbuilder); + return suggestionsbuilder.buildFuture(); + } + + public void e() throws CommandSyntaxException { + int i = this.i.getCursor(); + + this.m = MinecraftKey.a(this.i); + if (IRegistry.BLOCK.c(this.m)) { + Block block = (Block) IRegistry.BLOCK.getOrDefault(this.m); + + this.n = block.getStates(); + this.o = block.getBlockData(); + } else { + this.i.setCursor(i); + throw ArgumentBlock.b.createWithContext(this.i, this.m.toString()); + } + } + + public void f() throws CommandSyntaxException { + if (!this.j) { + throw ArgumentBlock.a.create(); + } else { + this.s = this::k; + this.i.expect('#'); + this.r = this.i.getCursor(); + this.q = MinecraftKey.a(this.i); + } + } + + public void g() throws CommandSyntaxException { + this.i.skip(); + this.s = this::b; + this.i.skipWhitespace(); + + while (true) { + if (this.i.canRead() && this.i.peek() != ']') { + this.i.skipWhitespace(); + int i = this.i.getCursor(); + String s = this.i.readString(); + IBlockState iblockstate = this.n.a(s); + + if (iblockstate == null) { + this.i.setCursor(i); + throw ArgumentBlock.c.createWithContext(this.i, this.m.toString(), s); + } + + if (this.k.containsKey(iblockstate)) { + this.i.setCursor(i); + throw ArgumentBlock.d.createWithContext(this.i, this.m.toString(), s); + } + + this.i.skipWhitespace(); + this.s = this::g; + if (!this.i.canRead() || this.i.peek() != '=') { + throw ArgumentBlock.f.createWithContext(this.i, this.m.toString(), s); + } + + this.i.skip(); + this.i.skipWhitespace(); + this.s = (suggestionsbuilder) -> { + return a(suggestionsbuilder, iblockstate).buildFuture(); + }; + int j = this.i.getCursor(); + + this.a(iblockstate, this.i.readString(), j); + this.s = this::h; + this.i.skipWhitespace(); + if (!this.i.canRead()) { + continue; + } + + if (this.i.peek() == ',') { + this.i.skip(); + this.s = this::d; + continue; + } + + if (this.i.peek() != ']') { + throw ArgumentBlock.g.createWithContext(this.i); + } + } + + if (this.i.canRead()) { + this.i.skip(); + return; + } + + throw ArgumentBlock.g.createWithContext(this.i); + } + } + + public void h() throws CommandSyntaxException { + this.i.skip(); + this.s = this::c; + int i = -1; + + this.i.skipWhitespace(); + + while (true) { + if (this.i.canRead() && this.i.peek() != ']') { + this.i.skipWhitespace(); + int j = this.i.getCursor(); + String s = this.i.readString(); + + if (this.l.containsKey(s)) { + this.i.setCursor(j); + throw ArgumentBlock.d.createWithContext(this.i, this.m.toString(), s); + } + + this.i.skipWhitespace(); + if (!this.i.canRead() || this.i.peek() != '=') { + this.i.setCursor(j); + throw ArgumentBlock.f.createWithContext(this.i, this.m.toString(), s); + } + + this.i.skip(); + this.i.skipWhitespace(); + this.s = (suggestionsbuilder) -> { + return this.a(suggestionsbuilder, s); + }; + i = this.i.getCursor(); + String s1 = this.i.readString(); + + this.l.put(s, s1); + this.i.skipWhitespace(); + if (!this.i.canRead()) { + continue; + } + + i = -1; + if (this.i.peek() == ',') { + this.i.skip(); + this.s = this::e; + continue; + } + + if (this.i.peek() != ']') { + throw ArgumentBlock.g.createWithContext(this.i); + } + } + + if (this.i.canRead()) { + this.i.skip(); + return; + } + + if (i >= 0) { + this.i.setCursor(i); + } + + throw ArgumentBlock.g.createWithContext(this.i); + } + } + + public void i() throws CommandSyntaxException { + this.p = (new MojangsonParser(this.i)).f(); + } + + private > void a(IBlockState iblockstate, String s, int i) throws CommandSyntaxException { + Optional optional = iblockstate.b(s); + + if (optional.isPresent()) { + this.o = (IBlockData) this.o.set(iblockstate, (T) optional.get()); // CraftBukkit - decompile error + this.k.put(iblockstate, optional.get()); + } else { + this.i.setCursor(i); + throw ArgumentBlock.e.createWithContext(this.i, this.m.toString(), iblockstate.a(), s); + } + } + + public static String a(IBlockData iblockdata, @Nullable NBTTagCompound nbttagcompound) { + StringBuilder stringbuilder = new StringBuilder(IRegistry.BLOCK.getKey(iblockdata.getBlock()).toString()); + + if (!iblockdata.a().isEmpty()) { + stringbuilder.append('['); + boolean flag = false; + + for (UnmodifiableIterator unmodifiableiterator = iblockdata.getStateMap().entrySet().iterator(); unmodifiableiterator.hasNext(); flag = true) { + Entry, Comparable> entry = (Entry) unmodifiableiterator.next(); + + if (flag) { + stringbuilder.append(','); + } + + a(stringbuilder, (IBlockState) entry.getKey(), (Comparable) entry.getValue()); + } + + stringbuilder.append(']'); + } + + if (nbttagcompound != null) { + stringbuilder.append(nbttagcompound); + } + + return stringbuilder.toString(); + } + + private static > void a(StringBuilder stringbuilder, IBlockState iblockstate, Comparable comparable) { + stringbuilder.append(iblockstate.a()); + stringbuilder.append('='); + stringbuilder.append(iblockstate.a((T) comparable)); // CraftBukkit - decompile error + } + + public CompletableFuture a(SuggestionsBuilder suggestionsbuilder) { + return (CompletableFuture) this.s.apply(suggestionsbuilder.createOffset(this.i.getCursor())); + } + + public Map j() { + return this.l; + } +} diff --git a/src/main/java/net/minecraft/server/ArgumentEntity.java b/src/main/java/net/minecraft/server/ArgumentEntity.java new file mode 100644 index 000000000000..5164b79b0ee3 --- /dev/null +++ b/src/main/java/net/minecraft/server/ArgumentEntity.java @@ -0,0 +1,171 @@ +package net.minecraft.server; + +import com.google.common.collect.Iterables; +import com.google.gson.JsonObject; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public class ArgumentEntity implements ArgumentType { + + private static final Collection g = Arrays.asList("Player", "0123", "@e", "@e[type=foo]", "dd12be42-52a9-4a91-a8a1-11c01849e498"); + public static final SimpleCommandExceptionType a = new SimpleCommandExceptionType(new ChatMessage("argument.entity.toomany", new Object[0])); + public static final SimpleCommandExceptionType b = new SimpleCommandExceptionType(new ChatMessage("argument.player.toomany", new Object[0])); + public static final SimpleCommandExceptionType c = new SimpleCommandExceptionType(new ChatMessage("argument.player.entities", new Object[0])); + public static final SimpleCommandExceptionType d = new SimpleCommandExceptionType(new ChatMessage("argument.entity.notfound.entity", new Object[0])); + public static final SimpleCommandExceptionType e = new SimpleCommandExceptionType(new ChatMessage("argument.entity.notfound.player", new Object[0])); + public static final SimpleCommandExceptionType f = new SimpleCommandExceptionType(new ChatMessage("argument.entity.selector.not_allowed", new Object[0])); + private final boolean h; + private final boolean i; + + protected ArgumentEntity(boolean flag, boolean flag1) { + this.h = flag; + this.i = flag1; + } + + public static ArgumentEntity a() { + return new ArgumentEntity(true, false); + } + + public static Entity a(CommandContext commandcontext, String s) throws CommandSyntaxException { + return ((EntitySelector) commandcontext.getArgument(s, EntitySelector.class)).a((CommandListenerWrapper) commandcontext.getSource()); + } + + public static ArgumentEntity b() { + return new ArgumentEntity(false, false); + } + + public static Collection b(CommandContext commandcontext, String s) throws CommandSyntaxException { + Collection collection = c(commandcontext, s); + + if (collection.isEmpty()) { + throw ArgumentEntity.d.create(); + } else { + return collection; + } + } + + public static Collection c(CommandContext commandcontext, String s) throws CommandSyntaxException { + return ((EntitySelector) commandcontext.getArgument(s, EntitySelector.class)).b((CommandListenerWrapper) commandcontext.getSource()); + } + + public static Collection d(CommandContext commandcontext, String s) throws CommandSyntaxException { + return ((EntitySelector) commandcontext.getArgument(s, EntitySelector.class)).d((CommandListenerWrapper) commandcontext.getSource()); + } + + public static ArgumentEntity c() { + return new ArgumentEntity(true, true); + } + + public static EntityPlayer e(CommandContext commandcontext, String s) throws CommandSyntaxException { + return ((EntitySelector) commandcontext.getArgument(s, EntitySelector.class)).c((CommandListenerWrapper) commandcontext.getSource()); + } + + public static ArgumentEntity d() { + return new ArgumentEntity(false, true); + } + + public static Collection f(CommandContext commandcontext, String s) throws CommandSyntaxException { + List list = ((EntitySelector) commandcontext.getArgument(s, EntitySelector.class)).d((CommandListenerWrapper) commandcontext.getSource()); + + if (list.isEmpty()) { + throw ArgumentEntity.e.create(); + } else { + return list; + } + } + + public EntitySelector parse(StringReader stringreader) throws CommandSyntaxException { + // CraftBukkit start + return parse(stringreader, false); + } + + public EntitySelector parse(StringReader stringreader, boolean overridePermissions) throws CommandSyntaxException { + // CraftBukkit end + boolean flag = false; + ArgumentParserSelector argumentparserselector = new ArgumentParserSelector(stringreader); + EntitySelector entityselector = argumentparserselector.s(overridePermissions); // CraftBukkit + + if (entityselector.a() > 1 && this.h) { + if (this.i) { + stringreader.setCursor(0); + throw ArgumentEntity.b.createWithContext(stringreader); + } else { + stringreader.setCursor(0); + throw ArgumentEntity.a.createWithContext(stringreader); + } + } else if (entityselector.b() && this.i && !entityselector.c()) { + stringreader.setCursor(0); + throw ArgumentEntity.c.createWithContext(stringreader); + } else { + return entityselector; + } + } + + public CompletableFuture listSuggestions(CommandContext commandcontext, SuggestionsBuilder suggestionsbuilder) { + if (commandcontext.getSource() instanceof ICompletionProvider) { + StringReader stringreader = new StringReader(suggestionsbuilder.getInput()); + + stringreader.setCursor(suggestionsbuilder.getStart()); + ICompletionProvider icompletionprovider = (ICompletionProvider) commandcontext.getSource(); + ArgumentParserSelector argumentparserselector = new ArgumentParserSelector(stringreader, icompletionprovider.hasPermission(2)); + + try { + argumentparserselector.s(); + } catch (CommandSyntaxException commandsyntaxexception) { + ; + } + + return argumentparserselector.a(suggestionsbuilder, (suggestionsbuilder1) -> { + Collection collection = icompletionprovider.l(); + Iterable iterable = this.i ? collection : Iterables.concat(collection, icompletionprovider.p()); + + ICompletionProvider.b((Iterable) iterable, suggestionsbuilder1); + }); + } else { + return Suggestions.empty(); + } + } + + public Collection getExamples() { + return ArgumentEntity.g; + } + + public static class a implements ArgumentSerializer { + + public a() {} + + public void a(ArgumentEntity argumententity, PacketDataSerializer packetdataserializer) { + byte b0 = 0; + + if (argumententity.h) { + b0 = (byte) (b0 | 1); + } + + if (argumententity.i) { + b0 = (byte) (b0 | 2); + } + + packetdataserializer.writeByte(b0); + } + + public ArgumentEntity b(PacketDataSerializer packetdataserializer) { + byte b0 = packetdataserializer.readByte(); + + return new ArgumentEntity((b0 & 1) != 0, (b0 & 2) != 0); + } + + public void a(ArgumentEntity argumententity, JsonObject jsonobject) { + jsonobject.addProperty("amount", argumententity.h ? "single" : "multiple"); + jsonobject.addProperty("type", argumententity.i ? "players" : "entities"); + } + } +} diff --git a/src/main/java/net/minecraft/server/ArgumentParserSelector.java b/src/main/java/net/minecraft/server/ArgumentParserSelector.java new file mode 100644 index 000000000000..d9152ccc75a2 --- /dev/null +++ b/src/main/java/net/minecraft/server/ArgumentParserSelector.java @@ -0,0 +1,630 @@ +package net.minecraft.server; + +import com.google.common.primitives.Doubles; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.ToDoubleFunction; +import javax.annotation.Nullable; + +public class ArgumentParserSelector { + + public static final SimpleCommandExceptionType a = new SimpleCommandExceptionType(new ChatMessage("argument.entity.invalid", new Object[0])); + public static final DynamicCommandExceptionType b = new DynamicCommandExceptionType((object) -> { + return new ChatMessage("argument.entity.selector.unknown", new Object[] { object}); + }); + public static final SimpleCommandExceptionType c = new SimpleCommandExceptionType(new ChatMessage("argument.entity.selector.not_allowed", new Object[0])); + public static final SimpleCommandExceptionType d = new SimpleCommandExceptionType(new ChatMessage("argument.entity.selector.missing", new Object[0])); + public static final SimpleCommandExceptionType e = new SimpleCommandExceptionType(new ChatMessage("argument.entity.options.unterminated", new Object[0])); + public static final DynamicCommandExceptionType f = new DynamicCommandExceptionType((object) -> { + return new ChatMessage("argument.entity.options.valueless", new Object[] { object}); + }); + public static final BiConsumer> g = (vec3d, list) -> { + }; + public static final BiConsumer> h = (vec3d, list) -> { + list.sort((entity, entity1) -> { + return Doubles.compare(entity.a(vec3d), entity1.a(vec3d)); + }); + }; + public static final BiConsumer> i = (vec3d, list) -> { + list.sort((entity, entity1) -> { + return Doubles.compare(entity1.a(vec3d), entity.a(vec3d)); + }); + }; + public static final BiConsumer> j = (vec3d, list) -> { + Collections.shuffle(list); + }; + public static final BiFunction, CompletableFuture> k = (suggestionsbuilder, consumer) -> { + return suggestionsbuilder.buildFuture(); + }; + private final StringReader l; + private final boolean m; + private int n; + private boolean o; + private boolean p; + private CriterionConditionValue.c q; + private CriterionConditionValue.d r; + @Nullable + private Double s; + @Nullable + private Double t; + @Nullable + private Double u; + @Nullable + private Double v; + @Nullable + private Double w; + @Nullable + private Double x; + private CriterionConditionRange y; + private CriterionConditionRange z; + private Predicate A; + private BiConsumer> B; + private boolean C; + @Nullable + private String D; + private int E; + @Nullable + private UUID F; + private BiFunction, CompletableFuture> G; + private boolean H; + private boolean I; + private boolean J; + private boolean K; + private boolean L; + private boolean M; + private boolean N; + private boolean O; + private Class P; + private boolean Q; + private boolean R; + private boolean S; + private boolean T; + + public ArgumentParserSelector(StringReader stringreader) { + this(stringreader, true); + } + + // CraftBukkit start - decompile error + private static final CriterionConditionValue.c DEFAULT_q; + private static final CriterionConditionValue.d DEFAULT_r; + + static { + try { + DEFAULT_q = (CriterionConditionValue.c) Class.forName("net.minecraft.server.CriterionConditionValue$c").getDeclaredField("e").get(null); + DEFAULT_r = (CriterionConditionValue.d) Class.forName("net.minecraft.server.CriterionConditionValue$d").getDeclaredField("e").get(null); + } catch (Exception ex) { + throw new AssertionError(ex); + } + } + + public ArgumentParserSelector(StringReader stringreader, boolean flag) { + this.q = DEFAULT_q; + this.r = DEFAULT_r; + // CraftBukkit end + this.y = CriterionConditionRange.a; + this.z = CriterionConditionRange.a; + this.A = (entity) -> { + return true; + }; + this.B = ArgumentParserSelector.g; + this.G = ArgumentParserSelector.k; + this.l = stringreader; + this.m = flag; + } + + public EntitySelector a() { + AxisAlignedBB axisalignedbb; + + if (this.v == null && this.w == null && this.x == null) { + if (this.q.b() != null) { + float f = (Float) this.q.b(); + + axisalignedbb = new AxisAlignedBB((double) (-f), (double) (-f), (double) (-f), (double) (f + 1.0F), (double) (f + 1.0F), (double) (f + 1.0F)); + } else { + axisalignedbb = null; + } + } else { + axisalignedbb = this.a(this.v == null ? 0.0D : this.v, this.w == null ? 0.0D : this.w, this.x == null ? 0.0D : this.x); + } + + Function function; // CraftBukkit - decompile error + + if (this.s == null && this.t == null && this.u == null) { + function = (vec3d) -> { + return vec3d; + }; + } else { + function = (vec3d) -> { + return new Vec3D(this.s == null ? vec3d.x : this.s, this.t == null ? vec3d.y : this.t, this.u == null ? vec3d.z : this.u); + }; + } + + return new EntitySelector(this.n, this.o, this.p, this.A, this.q, function, axisalignedbb, this.B, this.C, this.D, this.F, this.P == null ? Entity.class : this.P, this.T); + } + + private AxisAlignedBB a(double d0, double d1, double d2) { + boolean flag = d0 < 0.0D; + boolean flag1 = d1 < 0.0D; + boolean flag2 = d2 < 0.0D; + double d3 = flag ? d0 : 0.0D; + double d4 = flag1 ? d1 : 0.0D; + double d5 = flag2 ? d2 : 0.0D; + double d6 = (flag ? 0.0D : d0) + 1.0D; + double d7 = (flag1 ? 0.0D : d1) + 1.0D; + double d8 = (flag2 ? 0.0D : d2) + 1.0D; + + return new AxisAlignedBB(d3, d4, d5, d6, d7, d8); + } + + private void I() { + if (this.y != CriterionConditionRange.a) { + this.A = this.A.and(this.a(this.y, (entity) -> { + return (double) entity.pitch; + })); + } + + if (this.z != CriterionConditionRange.a) { + this.A = this.A.and(this.a(this.z, (entity) -> { + return (double) entity.yaw; + })); + } + + if (!this.r.c()) { + this.A = this.A.and((entity) -> { + return !(entity instanceof EntityPlayer) ? false : this.r.d(((EntityPlayer) entity).expLevel); + }); + } + + } + + private Predicate a(CriterionConditionRange criterionconditionrange, ToDoubleFunction todoublefunction) { + double d0 = (double) MathHelper.g(criterionconditionrange.a() == null ? 0.0F : criterionconditionrange.a()); + double d1 = (double) MathHelper.g(criterionconditionrange.b() == null ? 359.0F : criterionconditionrange.b()); + + return (entity) -> { + double d2 = MathHelper.g(todoublefunction.applyAsDouble(entity)); + + return d0 > d1 ? d2 >= d0 || d2 <= d1 : d2 >= d0 && d2 <= d1; + }; + } + + // CraftBukkit start + protected void b(boolean overridePermissions) throws CommandSyntaxException { + this.T = !overridePermissions; + // CraftBukkit end + this.G = this::d; + if (!this.l.canRead()) { + throw ArgumentParserSelector.d.createWithContext(this.l); + } else { + int i = this.l.getCursor(); + char c0 = this.l.read(); + + if (c0 == 'p') { + this.n = 1; + this.o = false; + this.B = ArgumentParserSelector.h; + this.a(EntityPlayer.class); + } else if (c0 == 'a') { + this.n = Integer.MAX_VALUE; + this.o = false; + this.B = ArgumentParserSelector.g; + this.a(EntityPlayer.class); + } else if (c0 == 'r') { + this.n = 1; + this.o = false; + this.B = ArgumentParserSelector.j; + this.a(EntityPlayer.class); + } else if (c0 == 's') { + this.n = 1; + this.o = true; + this.C = true; + } else { + if (c0 != 'e') { + this.l.setCursor(i); + throw ArgumentParserSelector.b.createWithContext(this.l, '@' + String.valueOf(c0)); + } + + this.n = Integer.MAX_VALUE; + this.o = true; + this.B = ArgumentParserSelector.g; + this.A = Entity::isAlive; + } + + this.G = this::e; + if (this.l.canRead() && this.l.peek() == '[') { + this.l.skip(); + this.G = this::f; + this.d(); + } + + } + } + + protected void c() throws CommandSyntaxException { + if (this.l.canRead()) { + this.G = this::c; + } + + int i = this.l.getCursor(); + String s = this.l.readString(); + + try { + this.F = UUID.fromString(s); + this.o = true; + } catch (IllegalArgumentException illegalargumentexception) { + if (s.isEmpty() || s.length() > 16) { + this.l.setCursor(i); + throw ArgumentParserSelector.a.createWithContext(this.l); + } + + this.o = false; + this.D = s; + } + + this.n = 1; + } + + protected void d() throws CommandSyntaxException { + this.G = this::g; + this.l.skipWhitespace(); + + while (true) { + if (this.l.canRead() && this.l.peek() != ']') { + this.l.skipWhitespace(); + int i = this.l.getCursor(); + String s = this.l.readString(); + PlayerSelector.a playerselector_a = PlayerSelector.a(this, s, i); + + this.l.skipWhitespace(); + if (!this.l.canRead() || this.l.peek() != '=') { + this.l.setCursor(i); + throw ArgumentParserSelector.f.createWithContext(this.l, s); + } + + this.l.skip(); + this.l.skipWhitespace(); + this.G = ArgumentParserSelector.k; + playerselector_a.handle(this); + this.l.skipWhitespace(); + this.G = this::h; + if (!this.l.canRead()) { + continue; + } + + if (this.l.peek() == ',') { + this.l.skip(); + this.G = this::g; + continue; + } + + if (this.l.peek() != ']') { + throw ArgumentParserSelector.e.createWithContext(this.l); + } + } + + if (this.l.canRead()) { + this.l.skip(); + this.G = ArgumentParserSelector.k; + return; + } + + throw ArgumentParserSelector.e.createWithContext(this.l); + } + } + + public boolean e() { + this.l.skipWhitespace(); + if (this.l.canRead() && this.l.peek() == '!') { + this.l.skip(); + this.l.skipWhitespace(); + return true; + } else { + return false; + } + } + + public StringReader f() { + return this.l; + } + + public void a(Predicate predicate) { + this.A = this.A.and(predicate); + } + + public void g() { + this.p = true; + } + + public CriterionConditionValue.c h() { + return this.q; + } + + public void a(CriterionConditionValue.c criterionconditionvalue_c) { + this.q = criterionconditionvalue_c; + } + + public CriterionConditionValue.d i() { + return this.r; + } + + public void a(CriterionConditionValue.d criterionconditionvalue_d) { + this.r = criterionconditionvalue_d; + } + + public CriterionConditionRange j() { + return this.y; + } + + public void a(CriterionConditionRange criterionconditionrange) { + this.y = criterionconditionrange; + } + + public CriterionConditionRange k() { + return this.z; + } + + public void b(CriterionConditionRange criterionconditionrange) { + this.z = criterionconditionrange; + } + + @Nullable + public Double l() { + return this.s; + } + + @Nullable + public Double m() { + return this.t; + } + + @Nullable + public Double n() { + return this.u; + } + + public void a(double d0) { + this.s = d0; + } + + public void b(double d0) { + this.t = d0; + } + + public void c(double d0) { + this.u = d0; + } + + public void d(double d0) { + this.v = d0; + } + + public void e(double d0) { + this.w = d0; + } + + public void f(double d0) { + this.x = d0; + } + + @Nullable + public Double o() { + return this.v; + } + + @Nullable + public Double p() { + return this.w; + } + + @Nullable + public Double q() { + return this.x; + } + + public void a(int i) { + this.n = i; + } + + public void a(boolean flag) { + this.o = flag; + } + + public void a(BiConsumer> biconsumer) { + this.B = biconsumer; + } + + public EntitySelector s() throws CommandSyntaxException { + // CraftBukkit start + return s(false); + } + + public EntitySelector s(boolean overridePermissions) throws CommandSyntaxException { + // CraftBukkit end + this.E = this.l.getCursor(); + this.G = this::b; + if (this.l.canRead() && this.l.peek() == '@') { + if (!this.m) { + throw ArgumentParserSelector.c.createWithContext(this.l); + } + + this.l.skip(); + this.b(overridePermissions); // CraftBukkit + } else { + this.c(); + } + + this.I(); + return this.a(); + } + + private static void a(SuggestionsBuilder suggestionsbuilder) { + suggestionsbuilder.suggest("@p", new ChatMessage("argument.entity.selector.nearestPlayer", new Object[0])); + suggestionsbuilder.suggest("@a", new ChatMessage("argument.entity.selector.allPlayers", new Object[0])); + suggestionsbuilder.suggest("@r", new ChatMessage("argument.entity.selector.randomPlayer", new Object[0])); + suggestionsbuilder.suggest("@s", new ChatMessage("argument.entity.selector.self", new Object[0])); + suggestionsbuilder.suggest("@e", new ChatMessage("argument.entity.selector.allEntities", new Object[0])); + } + + private CompletableFuture b(SuggestionsBuilder suggestionsbuilder, Consumer consumer) { + consumer.accept(suggestionsbuilder); + if (this.m) { + a(suggestionsbuilder); + } + + return suggestionsbuilder.buildFuture(); + } + + private CompletableFuture c(SuggestionsBuilder suggestionsbuilder, Consumer consumer) { + SuggestionsBuilder suggestionsbuilder1 = suggestionsbuilder.createOffset(this.E); + + consumer.accept(suggestionsbuilder1); + return suggestionsbuilder.add(suggestionsbuilder1).buildFuture(); + } + + private CompletableFuture d(SuggestionsBuilder suggestionsbuilder, Consumer consumer) { + SuggestionsBuilder suggestionsbuilder1 = suggestionsbuilder.createOffset(suggestionsbuilder.getStart() - 1); + + a(suggestionsbuilder1); + suggestionsbuilder.add(suggestionsbuilder1); + return suggestionsbuilder.buildFuture(); + } + + private CompletableFuture e(SuggestionsBuilder suggestionsbuilder, Consumer consumer) { + suggestionsbuilder.suggest(String.valueOf('[')); + return suggestionsbuilder.buildFuture(); + } + + private CompletableFuture f(SuggestionsBuilder suggestionsbuilder, Consumer consumer) { + suggestionsbuilder.suggest(String.valueOf(']')); + PlayerSelector.a(this, suggestionsbuilder); + return suggestionsbuilder.buildFuture(); + } + + private CompletableFuture g(SuggestionsBuilder suggestionsbuilder, Consumer consumer) { + PlayerSelector.a(this, suggestionsbuilder); + return suggestionsbuilder.buildFuture(); + } + + private CompletableFuture h(SuggestionsBuilder suggestionsbuilder, Consumer consumer) { + suggestionsbuilder.suggest(String.valueOf(',')); + suggestionsbuilder.suggest(String.valueOf(']')); + return suggestionsbuilder.buildFuture(); + } + + public boolean t() { + return this.C; + } + + public void a(BiFunction, CompletableFuture> bifunction) { + this.G = bifunction; + } + + public CompletableFuture a(SuggestionsBuilder suggestionsbuilder, Consumer consumer) { + return (CompletableFuture) this.G.apply(suggestionsbuilder.createOffset(this.l.getCursor()), consumer); + } + + public boolean u() { + return this.H; + } + + public void c(boolean flag) { + this.H = flag; + } + + public boolean v() { + return this.I; + } + + public void d(boolean flag) { + this.I = flag; + } + + public boolean w() { + return this.J; + } + + public void e(boolean flag) { + this.J = flag; + } + + public boolean x() { + return this.K; + } + + public void f(boolean flag) { + this.K = flag; + } + + public boolean y() { + return this.L; + } + + public void g(boolean flag) { + this.L = flag; + } + + public boolean z() { + return this.M; + } + + public void h(boolean flag) { + this.M = flag; + } + + public boolean A() { + return this.N; + } + + public void i(boolean flag) { + this.N = flag; + } + + public void j(boolean flag) { + this.O = flag; + } + + public void a(Class oclass) { + this.P = oclass; + } + + public void C() { + this.Q = true; + } + + public boolean E() { + return this.P != null; + } + + public boolean F() { + return this.Q; + } + + public boolean G() { + return this.R; + } + + public void k(boolean flag) { + this.R = flag; + } + + public boolean H() { + return this.S; + } + + public void l(boolean flag) { + this.S = flag; + } +} diff --git a/src/main/java/net/minecraft/server/AttributeInstance.java b/src/main/java/net/minecraft/server/AttributeInstance.java new file mode 100644 index 000000000000..c53bc8230af0 --- /dev/null +++ b/src/main/java/net/minecraft/server/AttributeInstance.java @@ -0,0 +1,33 @@ +package net.minecraft.server; + +import java.util.Collection; +import java.util.UUID; +import javax.annotation.Nullable; + +public interface AttributeInstance { + + IAttribute getAttribute(); + + double b(); + + void setValue(double d0); + + Collection a(int i); + + Collection c(); + + boolean a(AttributeModifier attributemodifier); + + @Nullable + AttributeModifier a(UUID uuid); + + default void addModifier(AttributeModifier modifier) { b(modifier); } // Paper - OBFHELPER + void b(AttributeModifier attributemodifier); + + default void removeModifier(AttributeModifier modifier) { c(modifier); } // Paper - OBFHELPER + void c(AttributeModifier attributemodifier); + + void b(UUID uuid); + + double getValue(); +} diff --git a/src/main/java/net/minecraft/server/AttributeRanged.java b/src/main/java/net/minecraft/server/AttributeRanged.java new file mode 100644 index 000000000000..02dea695c8a6 --- /dev/null +++ b/src/main/java/net/minecraft/server/AttributeRanged.java @@ -0,0 +1,39 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class AttributeRanged extends AttributeBase { + + private final double a; + public double maximum; // Spigot + private String c; + + public AttributeRanged(@Nullable IAttribute iattribute, String s, double d0, double d1, double d2) { + super(iattribute, s, d0); + this.a = d1; + this.maximum = d2; + if (d1 > d2) { + throw new IllegalArgumentException("Minimum value cannot be bigger than maximum value!"); + } else if (d0 < d1) { + throw new IllegalArgumentException("Default value cannot be lower than minimum value!"); + } else if (d0 > d2) { + throw new IllegalArgumentException("Default value cannot be bigger than maximum value!"); + } + } + + public AttributeRanged a(String s) { + this.c = s; + return this; + } + + public String g() { + return this.c; + } + + public double a(double d0) { + if (d0 != d0) return getDefault(); // CraftBukkit + + d0 = MathHelper.a(d0, this.a, this.maximum); + return d0; + } +} diff --git a/src/main/java/net/minecraft/server/AxisAlignedBB.java b/src/main/java/net/minecraft/server/AxisAlignedBB.java new file mode 100644 index 000000000000..d6a5c7407917 --- /dev/null +++ b/src/main/java/net/minecraft/server/AxisAlignedBB.java @@ -0,0 +1,303 @@ +package net.minecraft.server; + +import java.util.Iterator; +import javax.annotation.Nullable; + +public class AxisAlignedBB { + + public final double minX; public double getMinX() { return this.minX; } // Paper - OBFHELPER + public final double minY; public double getMinY() { return this.minY; } // Paper - OBFHELPER + public final double minZ; public double getMinZ() { return this.minZ; } // Paper - OBFHELPER + public final double maxX; public double getMaxX() { return this.maxX; } // Paper - OBFHELPER + public final double maxY; public double getMaxY() { return this.maxY; } // Paper - OBFHELPER + public final double maxZ; public double getMaxZ() { return this.maxZ; } // Paper - OBFHELPER + + public AxisAlignedBB(double d0, double d1, double d2, double d3, double d4, double d5) { + this.minX = Math.min(d0, d3); + this.minY = Math.min(d1, d4); + this.minZ = Math.min(d2, d5); + this.maxX = Math.max(d0, d3); + this.maxY = Math.max(d1, d4); + this.maxZ = Math.max(d2, d5); + } + + public AxisAlignedBB(BlockPosition blockposition) { + this((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), (double) (blockposition.getX() + 1), (double) (blockposition.getY() + 1), (double) (blockposition.getZ() + 1)); + } + + public AxisAlignedBB(BlockPosition blockposition, BlockPosition blockposition1) { + this((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), (double) blockposition1.getX(), (double) blockposition1.getY(), (double) blockposition1.getZ()); + } + + public double a(EnumDirection.EnumAxis enumdirection_enumaxis) { + return enumdirection_enumaxis.a(this.minX, this.minY, this.minZ); + } + + public double b(EnumDirection.EnumAxis enumdirection_enumaxis) { + return enumdirection_enumaxis.a(this.maxX, this.maxY, this.maxZ); + } + + public boolean equals(Object object) { + if (this == object) { + return true; + } else if (!(object instanceof AxisAlignedBB)) { + return false; + } else { + AxisAlignedBB axisalignedbb = (AxisAlignedBB) object; + + return Double.compare(axisalignedbb.minX, this.minX) != 0 ? false : (Double.compare(axisalignedbb.minY, this.minY) != 0 ? false : (Double.compare(axisalignedbb.minZ, this.minZ) != 0 ? false : (Double.compare(axisalignedbb.maxX, this.maxX) != 0 ? false : (Double.compare(axisalignedbb.maxY, this.maxY) != 0 ? false : Double.compare(axisalignedbb.maxZ, this.maxZ) == 0)))); + } + } + + public int hashCode() { + long i = Double.doubleToLongBits(this.minX); + int j = (int) (i ^ i >>> 32); + + i = Double.doubleToLongBits(this.minY); + j = 31 * j + (int) (i ^ i >>> 32); + i = Double.doubleToLongBits(this.minZ); + j = 31 * j + (int) (i ^ i >>> 32); + i = Double.doubleToLongBits(this.maxX); + j = 31 * j + (int) (i ^ i >>> 32); + i = Double.doubleToLongBits(this.maxY); + j = 31 * j + (int) (i ^ i >>> 32); + i = Double.doubleToLongBits(this.maxZ); + j = 31 * j + (int) (i ^ i >>> 32); + return j; + } + + public AxisAlignedBB a(double d0, double d1, double d2) { + double d3 = this.minX; + double d4 = this.minY; + double d5 = this.minZ; + double d6 = this.maxX; + double d7 = this.maxY; + double d8 = this.maxZ; + + if (d0 < 0.0D) { + d3 -= d0; + } else if (d0 > 0.0D) { + d6 -= d0; + } + + if (d1 < 0.0D) { + d4 -= d1; + } else if (d1 > 0.0D) { + d7 -= d1; + } + + if (d2 < 0.0D) { + d5 -= d2; + } else if (d2 > 0.0D) { + d8 -= d2; + } + + return new AxisAlignedBB(d3, d4, d5, d6, d7, d8); + } + + public AxisAlignedBB expand(double x, double y, double z) { return b(x, y, z); } // Paper - OBFHELPER + public AxisAlignedBB b(double d0, double d1, double d2) { + double d3 = this.minX; + double d4 = this.minY; + double d5 = this.minZ; + double d6 = this.maxX; + double d7 = this.maxY; + double d8 = this.maxZ; + + if (d0 < 0.0D) { + d3 += d0; + } else if (d0 > 0.0D) { + d6 += d0; + } + + if (d1 < 0.0D) { + d4 += d1; + } else if (d1 > 0.0D) { + d7 += d1; + } + + if (d2 < 0.0D) { + d5 += d2; + } else if (d2 > 0.0D) { + d8 += d2; + } + + return new AxisAlignedBB(d3, d4, d5, d6, d7, d8); + } + + // Paper start + public AxisAlignedBB grow(double d0) { + return grow(d0, d0, d0); + } + // Paper end + + public AxisAlignedBB grow(double d0, double d1, double d2) { + double d3 = this.minX - d0; + double d4 = this.minY - d1; + double d5 = this.minZ - d2; + double d6 = this.maxX + d0; + double d7 = this.maxY + d1; + double d8 = this.maxZ + d2; + + return new AxisAlignedBB(d3, d4, d5, d6, d7, d8); + } + + public AxisAlignedBB g(double d0) { + return this.grow(d0, d0, d0); + } + + public AxisAlignedBB a(AxisAlignedBB axisalignedbb) { + double d0 = Math.max(this.minX, axisalignedbb.minX); + double d1 = Math.max(this.minY, axisalignedbb.minY); + double d2 = Math.max(this.minZ, axisalignedbb.minZ); + double d3 = Math.min(this.maxX, axisalignedbb.maxX); + double d4 = Math.min(this.maxY, axisalignedbb.maxY); + double d5 = Math.min(this.maxZ, axisalignedbb.maxZ); + + return new AxisAlignedBB(d0, d1, d2, d3, d4, d5); + } + + public AxisAlignedBB b(AxisAlignedBB axisalignedbb) { + double d0 = Math.min(this.minX, axisalignedbb.minX); + double d1 = Math.min(this.minY, axisalignedbb.minY); + double d2 = Math.min(this.minZ, axisalignedbb.minZ); + double d3 = Math.max(this.maxX, axisalignedbb.maxX); + double d4 = Math.max(this.maxY, axisalignedbb.maxY); + double d5 = Math.max(this.maxZ, axisalignedbb.maxZ); + + return new AxisAlignedBB(d0, d1, d2, d3, d4, d5); + } + + public AxisAlignedBB d(double d0, double d1, double d2) { + return new AxisAlignedBB(this.minX + d0, this.minY + d1, this.minZ + d2, this.maxX + d0, this.maxY + d1, this.maxZ + d2); + } + + public AxisAlignedBB a(BlockPosition blockposition) { + return new AxisAlignedBB(this.minX + (double) blockposition.getX(), this.minY + (double) blockposition.getY(), this.minZ + (double) blockposition.getZ(), this.maxX + (double) blockposition.getX(), this.maxY + (double) blockposition.getY(), this.maxZ + (double) blockposition.getZ()); + } + + public AxisAlignedBB a(Vec3D vec3d) { + return this.d(vec3d.x, vec3d.y, vec3d.z); + } + + public boolean c(AxisAlignedBB axisalignedbb) { + return this.a(axisalignedbb.minX, axisalignedbb.minY, axisalignedbb.minZ, axisalignedbb.maxX, axisalignedbb.maxY, axisalignedbb.maxZ); + } + + public boolean a(double d0, double d1, double d2, double d3, double d4, double d5) { + return this.minX < d3 && this.maxX > d0 && this.minY < d4 && this.maxY > d1 && this.minZ < d5 && this.maxZ > d2; + } + + public boolean contains(Vec3D vec3d) { return b(vec3d); } // Paper - OBFHELPER + public boolean b(Vec3D vec3d) { + return this.e(vec3d.x, vec3d.y, vec3d.z); + } + + public boolean e(double d0, double d1, double d2) { + return d0 >= this.minX && d0 < this.maxX && d1 >= this.minY && d1 < this.maxY && d2 >= this.minZ && d2 < this.maxZ; + } + + public double a() { + double d0 = this.maxX - this.minX; + double d1 = this.maxY - this.minY; + double d2 = this.maxZ - this.minZ; + + return (d0 + d1 + d2) / 3.0D; + } + + public AxisAlignedBB f(double d0, double d1, double d2) { + return this.grow(-d0, -d1, -d2); + } + + public AxisAlignedBB shrink(double d0) { + return this.g(-d0); + } + + public MovingObjectPosition calculateIntercept(Vec3D vec3d, Vec3D vec3d1) { return b(vec3d, vec3d1); } // Paper - OBFHELPER + @Nullable + public MovingObjectPosition b(Vec3D vec3d, Vec3D vec3d1) { + return this.a(vec3d, vec3d1, (BlockPosition) null); + } + + @Nullable + public MovingObjectPosition a(Vec3D vec3d, Vec3D vec3d1, @Nullable BlockPosition blockposition) { + double[] adouble = new double[] { 1.0D}; + EnumDirection enumdirection = null; + double d0 = vec3d1.x - vec3d.x; + double d1 = vec3d1.y - vec3d.y; + double d2 = vec3d1.z - vec3d.z; + + enumdirection = a(blockposition == null ? this : this.a(blockposition), vec3d, adouble, enumdirection, d0, d1, d2); + if (enumdirection == null) { + return null; + } else { + double d3 = adouble[0]; + + return new MovingObjectPosition(vec3d.add(d3 * d0, d3 * d1, d3 * d2), enumdirection, blockposition == null ? BlockPosition.ZERO : blockposition); + } + } + + @Nullable + public static MovingObjectPosition a(Iterable iterable, Vec3D vec3d, Vec3D vec3d1, BlockPosition blockposition) { + double[] adouble = new double[] { 1.0D}; + EnumDirection enumdirection = null; + double d0 = vec3d1.x - vec3d.x; + double d1 = vec3d1.y - vec3d.y; + double d2 = vec3d1.z - vec3d.z; + + AxisAlignedBB axisalignedbb; + + for (Iterator iterator = iterable.iterator(); iterator.hasNext(); enumdirection = a(axisalignedbb.a(blockposition), vec3d, adouble, enumdirection, d0, d1, d2)) { + axisalignedbb = (AxisAlignedBB) iterator.next(); + } + + if (enumdirection == null) { + return null; + } else { + double d3 = adouble[0]; + + return new MovingObjectPosition(vec3d.add(d3 * d0, d3 * d1, d3 * d2), enumdirection, blockposition); + } + } + + @Nullable + private static EnumDirection a(AxisAlignedBB axisalignedbb, Vec3D vec3d, double[] adouble, @Nullable EnumDirection enumdirection, double d0, double d1, double d2) { + if (d0 > 1.0E-7D) { + enumdirection = a(adouble, enumdirection, d0, d1, d2, axisalignedbb.minX, axisalignedbb.minY, axisalignedbb.maxY, axisalignedbb.minZ, axisalignedbb.maxZ, EnumDirection.WEST, vec3d.x, vec3d.y, vec3d.z); + } else if (d0 < -1.0E-7D) { + enumdirection = a(adouble, enumdirection, d0, d1, d2, axisalignedbb.maxX, axisalignedbb.minY, axisalignedbb.maxY, axisalignedbb.minZ, axisalignedbb.maxZ, EnumDirection.EAST, vec3d.x, vec3d.y, vec3d.z); + } + + if (d1 > 1.0E-7D) { + enumdirection = a(adouble, enumdirection, d1, d2, d0, axisalignedbb.minY, axisalignedbb.minZ, axisalignedbb.maxZ, axisalignedbb.minX, axisalignedbb.maxX, EnumDirection.DOWN, vec3d.y, vec3d.z, vec3d.x); + } else if (d1 < -1.0E-7D) { + enumdirection = a(adouble, enumdirection, d1, d2, d0, axisalignedbb.maxY, axisalignedbb.minZ, axisalignedbb.maxZ, axisalignedbb.minX, axisalignedbb.maxX, EnumDirection.UP, vec3d.y, vec3d.z, vec3d.x); + } + + if (d2 > 1.0E-7D) { + enumdirection = a(adouble, enumdirection, d2, d0, d1, axisalignedbb.minZ, axisalignedbb.minX, axisalignedbb.maxX, axisalignedbb.minY, axisalignedbb.maxY, EnumDirection.NORTH, vec3d.z, vec3d.x, vec3d.y); + } else if (d2 < -1.0E-7D) { + enumdirection = a(adouble, enumdirection, d2, d0, d1, axisalignedbb.maxZ, axisalignedbb.minX, axisalignedbb.maxX, axisalignedbb.minY, axisalignedbb.maxY, EnumDirection.SOUTH, vec3d.z, vec3d.x, vec3d.y); + } + + return enumdirection; + } + + @Nullable + private static EnumDirection a(double[] adouble, @Nullable EnumDirection enumdirection, double d0, double d1, double d2, double d3, double d4, double d5, double d6, double d7, EnumDirection enumdirection1, double d8, double d9, double d10) { + double d11 = (d3 - d8) / d0; + double d12 = d9 + d11 * d1; + double d13 = d10 + d11 * d2; + + if (0.0D < d11 && d11 < adouble[0] && d4 - 1.0E-7D < d12 && d12 < d5 + 1.0E-7D && d6 - 1.0E-7D < d13 && d13 < d7 + 1.0E-7D) { + adouble[0] = d11; + return enumdirection1; + } else { + return enumdirection; + } + } + + public String toString() { + return "box[" + this.minX + ", " + this.minY + ", " + this.minZ + " -> " + this.maxX + ", " + this.maxY + ", " + this.maxZ + "]"; + } +} diff --git a/src/main/java/net/minecraft/server/BaseBlockPosition.java b/src/main/java/net/minecraft/server/BaseBlockPosition.java new file mode 100644 index 000000000000..cc17a414f5a3 --- /dev/null +++ b/src/main/java/net/minecraft/server/BaseBlockPosition.java @@ -0,0 +1,105 @@ +package net.minecraft.server; + +import com.google.common.base.MoreObjects; +import javax.annotation.concurrent.Immutable; + +@Immutable +public class BaseBlockPosition implements Comparable { + + public static final BaseBlockPosition ZERO = new BaseBlockPosition(0, 0, 0); + // Paper start + protected int x; + protected int y; + protected int z; + public boolean isValidLocation() { + return x >= -30000000 && z >= -30000000 && x < 30000000 && z < 30000000 && y >= 0 && y < 256; + } + public boolean isInvalidYLocation() { + return y < 0 || y >= 256; + } + // Paper end + + public BaseBlockPosition(int i, int j, int k) { + this.x = i; + this.y = j; + this.z = k; + } + + public BaseBlockPosition(double d0, double d1, double d2) { + this(MathHelper.floor(d0), MathHelper.floor(d1), MathHelper.floor(d2)); + } + + public boolean equals(Object object) { + if (this == object) { + return true; + } else if (!(object instanceof BaseBlockPosition)) { + return false; + } else { + BaseBlockPosition baseblockposition = (BaseBlockPosition) object; + + return this.getX() != baseblockposition.getX() ? false : (this.getY() != baseblockposition.getY() ? false : this.getZ() == baseblockposition.getZ()); + } + } + + public int hashCode() { + return (this.getY() + this.getZ() * 31) * 31 + this.getX(); + } + + public int compareTo(BaseBlockPosition baseblockposition) { + return this.getY() == baseblockposition.getY() ? (this.getZ() == baseblockposition.getZ() ? this.getX() - baseblockposition.getX() : this.getZ() - baseblockposition.getZ()) : this.getY() - baseblockposition.getY(); + } + + // Paper start + public final int getX() { + return this.x; + } + + public int getY() { + return this.y; + } + + public int getZ() { + return this.z; + } + // Paper end + + public BaseBlockPosition d(BaseBlockPosition baseblockposition) { + return new BaseBlockPosition(this.getY() * baseblockposition.getZ() - this.getZ() * baseblockposition.getY(), this.getZ() * baseblockposition.getX() - this.getX() * baseblockposition.getZ(), this.getX() * baseblockposition.getY() - this.getY() * baseblockposition.getX()); + } + + public double h(int i, int j, int k) { + double d0 = (double) (this.getX() - i); + double d1 = (double) (this.getY() - j); + double d2 = (double) (this.getZ() - k); + + return Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + } + + public double m(BaseBlockPosition baseblockposition) { + return this.h(baseblockposition.getX(), baseblockposition.getY(), baseblockposition.getZ()); + } + + public double distanceSquared(double d0, double d1, double d2) { + double d3 = (double) this.getX() - d0; + double d4 = (double) this.getY() - d1; + double d5 = (double) this.getZ() - d2; + + return d3 * d3 + d4 * d4 + d5 * d5; + } + + public double g(double d0, double d1, double d2) { + double d3 = (double) this.getX() + 0.5D - d0; + double d4 = (double) this.getY() + 0.5D - d1; + double d5 = (double) this.getZ() + 0.5D - d2; + + return d3 * d3 + d4 * d4 + d5 * d5; + } + + public double n(BaseBlockPosition baseblockposition) { + return this.distanceSquared((double) baseblockposition.getX(), (double) baseblockposition.getY(), (double) baseblockposition.getZ()); + } + + public String toString() { + return MoreObjects.toStringHelper(this).add("x", this.getX()).add("y", this.getY()).add("z", this.getZ()).toString(); + } +} diff --git a/src/main/java/net/minecraft/server/BiomeBase.java b/src/main/java/net/minecraft/server/BiomeBase.java new file mode 100644 index 000000000000..3496d4236d39 --- /dev/null +++ b/src/main/java/net/minecraft/server/BiomeBase.java @@ -0,0 +1,626 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public abstract class BiomeBase { + + public static final Logger a = LogManager.getLogger(); + public static final WorldGenCarverAbstract b = new WorldGenCaves(); + public static final WorldGenCarverAbstract c = new WorldGenCavesHell(); + public static final WorldGenCarverAbstract d = new WorldGenCanyon(); + public static final WorldGenCarverAbstract e = new WorldGenCanyonOcean(); + public static final WorldGenCarverAbstract f = new WorldGenCavesOcean(); + public static final WorldGenDecorator g = new WorldGenDecoratorHeight(); + public static final WorldGenDecorator h = new WorldGenDecoratorSkyVisible(); + public static final WorldGenDecorator i = new WorldGenDecoratorHeight32(); + public static final WorldGenDecorator j = new WorldGenDecoratorHeightDouble(); + public static final WorldGenDecorator k = new WorldGenDecoratorHeight64(); + public static final WorldGenDecorator l = new WorldGenDecoratorNoiseHeight32(); + public static final WorldGenDecorator m = new WorldGenDecoratorNoiseHeightDouble(); + public static final WorldGenDecorator n = new WorldGenDecoratorEmpty(); + public static final WorldGenDecorator o = new WorldGenDecoratorChance(); + public static final WorldGenDecorator p = new WorldGenDecoratorChanceHeight(); + public static final WorldGenDecorator q = new WorldGenDecoratorChancePass(); + public static final WorldGenDecorator r = new WorldGenDecoratorSkyVisibleBiased(); + public static final WorldGenDecorator s = new WorldGenDecoratorHeightExtraChance(); + public static final WorldGenDecorator t = new WorldGenDecoratorNetherHeight(); + public static final WorldGenDecorator u = new WorldGenDecoratorHeightBiased(); + public static final WorldGenDecorator v = new WorldGenDecoratorHeightBiased2(); + public static final WorldGenDecorator w = new WorldGenDecoratorNetherRandomCount(); + public static final WorldGenDecorator x = new WorldGenDecoratorNetherChance(); + public static final WorldGenDecorator y = new WorldGenFeatureChanceDecorator(); + public static final WorldGenDecorator z = new WorldGenFeatureChanceDecoratorHeight(); + public static final WorldGenDecorator A = new WorldGenDecoratorHeightAverage(); + public static final WorldGenDecorator B = new WorldGenDecoratorSolidTop(); + public static final WorldGenDecorator C = new WorldGenDecoratorSolidTopHeight(); + public static final WorldGenDecorator D = new WorldGenDecoratorSolidTopNoise(); + public static final WorldGenDecorator E = new WorldGenDecoratorCarveMask(); + public static final WorldGenDecorator F = new WorldGenDecoratorForestRock(); + public static final WorldGenDecorator G = new WorldGenDecoratorNetherFire(); + public static final WorldGenDecorator H = new WorldGenDecoratorNetherMagma(); + public static final WorldGenDecorator I = new WorldGenDecoratorEmerald(); + public static final WorldGenDecorator J = new WorldGenDecoratorLakeLava(); + public static final WorldGenDecorator K = new WorldGenDecoratorLakeWater(); + public static final WorldGenDecorator L = new WorldGenDecoratorDungeon(); + public static final WorldGenDecorator M = new WorldGenDecoratorRoofedTree(); + public static final WorldGenDecorator N = new WorldGenDecoratorIceburg(); + public static final WorldGenDecorator O = new WorldGenDecoratorNetherGlowstone(); + public static final WorldGenDecorator P = new WorldGenDecoratorSpike(); + public static final WorldGenDecorator Q = new WorldGenDecoratorEndIsland(); + public static final WorldGenDecorator R = new WorldGenDecoratorChorusPlant(); + public static final WorldGenDecorator S = new WorldGenDecoratorEndGateway(); + protected static final IBlockData T = Blocks.AIR.getBlockData(); + protected static final IBlockData U = Blocks.DIRT.getBlockData(); + protected static final IBlockData V = Blocks.GRASS_BLOCK.getBlockData(); + protected static final IBlockData W = Blocks.PODZOL.getBlockData(); + protected static final IBlockData X = Blocks.GRAVEL.getBlockData(); + protected static final IBlockData Y = Blocks.STONE.getBlockData(); + protected static final IBlockData Z = Blocks.COARSE_DIRT.getBlockData(); + protected static final IBlockData aa = Blocks.SAND.getBlockData(); + protected static final IBlockData ab = Blocks.RED_SAND.getBlockData(); + protected static final IBlockData ac = Blocks.WHITE_TERRACOTTA.getBlockData(); + protected static final IBlockData ad = Blocks.MYCELIUM.getBlockData(); + protected static final IBlockData ae = Blocks.NETHERRACK.getBlockData(); + protected static final IBlockData af = Blocks.END_STONE.getBlockData(); + public static final WorldGenSurfaceConfigurationBase ag = new WorldGenSurfaceConfigurationBase(BiomeBase.T, BiomeBase.T, BiomeBase.T); + public static final WorldGenSurfaceConfigurationBase ah = new WorldGenSurfaceConfigurationBase(BiomeBase.U, BiomeBase.U, BiomeBase.X); + public static final WorldGenSurfaceConfigurationBase ai = new WorldGenSurfaceConfigurationBase(BiomeBase.V, BiomeBase.U, BiomeBase.X); + public static final WorldGenSurfaceConfigurationBase aj = new WorldGenSurfaceConfigurationBase(BiomeBase.Y, BiomeBase.Y, BiomeBase.X); + public static final WorldGenSurfaceConfigurationBase ak = new WorldGenSurfaceConfigurationBase(BiomeBase.X, BiomeBase.X, BiomeBase.X); + public static final WorldGenSurfaceConfigurationBase al = new WorldGenSurfaceConfigurationBase(BiomeBase.Z, BiomeBase.U, BiomeBase.X); + public static final WorldGenSurfaceConfigurationBase am = new WorldGenSurfaceConfigurationBase(BiomeBase.W, BiomeBase.U, BiomeBase.X); + public static final WorldGenSurfaceConfigurationBase an = new WorldGenSurfaceConfigurationBase(BiomeBase.aa, BiomeBase.aa, BiomeBase.aa); + public static final WorldGenSurfaceConfigurationBase ao = new WorldGenSurfaceConfigurationBase(BiomeBase.V, BiomeBase.U, BiomeBase.aa); + public static final WorldGenSurfaceConfigurationBase ap = new WorldGenSurfaceConfigurationBase(BiomeBase.aa, BiomeBase.aa, BiomeBase.X); + public static final WorldGenSurfaceConfigurationBase aq = new WorldGenSurfaceConfigurationBase(BiomeBase.ab, BiomeBase.ac, BiomeBase.X); + public static final WorldGenSurfaceConfigurationBase ar = new WorldGenSurfaceConfigurationBase(BiomeBase.ad, BiomeBase.U, BiomeBase.X); + public static final WorldGenSurfaceConfigurationBase as = new WorldGenSurfaceConfigurationBase(BiomeBase.ae, BiomeBase.ae, BiomeBase.ae); + public static final WorldGenSurfaceConfigurationBase at = new WorldGenSurfaceConfigurationBase(BiomeBase.af, BiomeBase.af, BiomeBase.af); + public static final WorldGenSurface au = new WorldGenSurfaceDefaultBlock(); + public static final WorldGenSurface av = new WorldGenSurfaceExtremeHills(); + public static final WorldGenSurface aw = new WorldGenSurfaceSavannaMutated(); + public static final WorldGenSurface ax = new WorldGenSurfaceExtremeHillMutated(); + public static final WorldGenSurface ay = new WorldGenSurfaceTaigaMega(); + public static final WorldGenSurface az = new WorldGenSurfaceSwamp(); + public static final WorldGenSurface aA = new WorldGenSurfaceMesa(); + public static final WorldGenSurface aB = new WorldGenSurfaceMesaForest(); + public static final WorldGenSurface aC = new WorldGenSurfaceMesaBryce(); + public static final WorldGenSurface aD = new WorldGenSurfaceFrozenOcean(); + public static final WorldGenSurface aE = new WorldGenSurfaceNether(); + public static final WorldGenSurface aF = new WorldGenSurfaceEmpty(); + public static final Set aG = Sets.newHashSet(); + public static final RegistryBlockID aH = new RegistryBlockID<>(); + protected static final NoiseGenerator3 aI = new NoiseGenerator3(new Random(1234L), 1); + public static final NoiseGenerator3 aJ = new NoiseGenerator3(new Random(2345L), 1); + @Nullable + protected String aK; + protected final float aL; + protected final float aM; + protected final float aN; + protected final float aO; + protected final int aP; + protected final int aQ; + @Nullable + protected final String aR; + protected final WorldGenSurfaceComposite aS; + protected final BiomeBase.Geography aT; + protected final BiomeBase.Precipitation aU; + protected final Map>> aV = Maps.newHashMap(); + protected final Map>> aW = Maps.newHashMap(); + protected final List> aX = Lists.newArrayList(); + protected final Map, WorldGenFeatureConfiguration> aY = Maps.newHashMap(); + private final java.util.EnumMap> aZ = Maps.newEnumMap(EnumCreatureType.class); // Paper + + @Nullable + public static BiomeBase a(BiomeBase biomebase) { + return (BiomeBase) BiomeBase.aH.fromId(IRegistry.BIOME.a(biomebase)); // Paper - decompile fix + } + + public static WorldGenCarverWrapper a(WorldGenCarver worldgencarver, C c0) { + return new WorldGenCarverWrapper<>(worldgencarver, c0); + } + + public static WorldGenFeatureComposite a(WorldGenerator worldgenerator, F f0, WorldGenDecorator worldgendecorator, D d0) { + return new WorldGenFeatureComposite<>(worldgenerator, f0, worldgendecorator, d0); + } + + public static WorldGenFeatureCompositeFlower a(WorldGenFlowers worldgenflowers, WorldGenDecorator worldgendecorator, D d0) { + return new WorldGenFeatureCompositeFlower<>(worldgenflowers, worldgendecorator, d0); + } + + protected BiomeBase(BiomeBase.a biomebase_a) { + if (biomebase_a.a != null && biomebase_a.b != null && biomebase_a.c != null && biomebase_a.d != null && biomebase_a.e != null && biomebase_a.f != null && biomebase_a.g != null && biomebase_a.h != null && biomebase_a.i != null) { + this.aS = biomebase_a.a; + this.aU = biomebase_a.b; + this.aT = biomebase_a.c; + this.aL = biomebase_a.d; + this.aM = biomebase_a.e; + this.aN = biomebase_a.f; + this.aO = biomebase_a.g; + this.aP = biomebase_a.h; + this.aQ = biomebase_a.i; + this.aR = biomebase_a.j; + WorldGenStage.Decoration[] aworldgenstage_decoration = WorldGenStage.Decoration.values(); + int i = aworldgenstage_decoration.length; + + int j; + + for (j = 0; j < i; ++j) { + WorldGenStage.Decoration worldgenstage_decoration = aworldgenstage_decoration[j]; + + this.aW.put(worldgenstage_decoration, Lists.newArrayList()); + } + + EnumCreatureType[] aenumcreaturetype = EnumCreatureType.values(); + + i = aenumcreaturetype.length; + + for (j = 0; j < i; ++j) { + EnumCreatureType enumcreaturetype = aenumcreaturetype[j]; + + this.aZ.put(enumcreaturetype, new MobList()); // Paper + } + + } else { + throw new IllegalStateException("You are missing parameters to build a proper biome for " + this.getClass().getSimpleName() + "\n" + biomebase_a); + } + } + + protected void a() { + this.a(WorldGenStage.Decoration.UNDERGROUND_STRUCTURES, a(WorldGenerator.f, new WorldGenMineshaftConfiguration(0.004000000189989805D, WorldGenMineshaft.Type.NORMAL), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e)); + this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.e, new WorldGenFeatureVillageConfiguration(0, WorldGenVillagePieces.Material.OAK), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e)); + this.a(WorldGenStage.Decoration.UNDERGROUND_STRUCTURES, a(WorldGenerator.m, new WorldGenFeatureStrongholdConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e)); + this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.l, new WorldGenFeatureSwampHutConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e)); + this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.i, new WorldGenFeatureDesertPyramidConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e)); + this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.h, new WorldGenFeatureJunglePyramidConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e)); + this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.j, new WorldGenFeatureIglooConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e)); + this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.k, new WorldGenFeatureShipwreckConfiguration(false), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e)); + this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.n, new WorldGenMonumentConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e)); + this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.g, new WorldGenMansionConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e)); + this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.o, new WorldGenFeatureOceanRuinConfiguration(WorldGenFeatureOceanRuin.Temperature.COLD, 0.3F, 0.9F), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e)); + this.a(WorldGenStage.Decoration.UNDERGROUND_STRUCTURES, a(WorldGenerator.r, new WorldGenBuriedTreasureConfiguration(0.01F), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e)); + } + + public boolean b() { + return this.aR != null; + } + + protected void a(EnumCreatureType enumcreaturetype, BiomeBase.BiomeMeta biomebase_biomemeta) { + ((List) this.aZ.get(enumcreaturetype)).add(biomebase_biomemeta); + } + + public List getMobs(EnumCreatureType enumcreaturetype) { + return (List) this.aZ.get(enumcreaturetype); + } + + public BiomeBase.Precipitation c() { + return this.aU; + } + + public boolean d() { + return this.getHumidity() > 0.85F; + } + + public float e() { + return 0.1F; + } + + public float getAdjustedTemperature(BlockPosition blockposition) { + if (blockposition.getY() > 64) { + float f = (float) (BiomeBase.aI.a((double) ((float) blockposition.getX() / 8.0F), (double) ((float) blockposition.getZ() / 8.0F)) * 4.0D); + + return this.getTemperature() - (f + (float) blockposition.getY() - 64.0F) * 0.05F / 30.0F; + } else { + return this.getTemperature(); + } + } + + public boolean a(IWorldReader iworldreader, BlockPosition blockposition) { + return this.a(iworldreader, blockposition, true); + } + + public boolean a(IWorldReader iworldreader, BlockPosition blockposition, boolean flag) { + if (this.getAdjustedTemperature(blockposition) >= 0.15F) { + return false; + } else { + if (blockposition.getY() >= 0 && blockposition.getY() < 256 && iworldreader.getBrightness(EnumSkyBlock.BLOCK, blockposition) < 10) { + IBlockData iblockdata = iworldreader.getType(blockposition); + Fluid fluid = iworldreader.getFluid(blockposition); + + if (fluid.c() == FluidTypes.WATER && iblockdata.getBlock() instanceof BlockFluids) { + if (!flag) { + return true; + } + + boolean flag1 = iworldreader.B(blockposition.west()) && iworldreader.B(blockposition.east()) && iworldreader.B(blockposition.north()) && iworldreader.B(blockposition.south()); + + if (!flag1) { + return true; + } + } + } + + return false; + } + } + + public boolean b(IWorldReader iworldreader, BlockPosition blockposition) { + if (this.getAdjustedTemperature(blockposition) >= 0.15F) { + return false; + } else { + if (blockposition.getY() >= 0 && blockposition.getY() < 256 && iworldreader.getBrightness(EnumSkyBlock.BLOCK, blockposition) < 10) { + IBlockData iblockdata = iworldreader.getType(blockposition); + + if (iblockdata.isAir() && Blocks.SNOW.getBlockData().canPlace(iworldreader, blockposition)) { + return true; + } + } + + return false; + } + } + + public void a(WorldGenStage.Decoration worldgenstage_decoration, WorldGenFeatureComposite worldgenfeaturecomposite) { + if (worldgenfeaturecomposite instanceof WorldGenFeatureCompositeFlower) { + this.aX.add((WorldGenFeatureCompositeFlower) worldgenfeaturecomposite); + } + + ((List) this.aW.get(worldgenstage_decoration)).add(worldgenfeaturecomposite); + } + + public void a(WorldGenStage.Features worldgenstage_features, WorldGenCarverWrapper worldgencarverwrapper) { + ((List) this.aV.computeIfAbsent(worldgenstage_features, (worldgenstage_features1) -> { + return Lists.newArrayList(); + })).add(worldgencarverwrapper); + } + + public List> a(WorldGenStage.Features worldgenstage_features) { + return (List) this.aV.computeIfAbsent(worldgenstage_features, (worldgenstage_features1) -> { + return Lists.newArrayList(); + }); + } + + public void a(StructureGenerator structuregenerator, C c0) { + this.aY.put(structuregenerator, c0); + } + + public boolean a(StructureGenerator structuregenerator) { + return this.aY.containsKey(structuregenerator); + } + + @Nullable + public WorldGenFeatureConfiguration b(StructureGenerator structuregenerator) { + return (WorldGenFeatureConfiguration) this.aY.get(structuregenerator); + } + + public List> f() { + return this.aX; + } + + public List> a(WorldGenStage.Decoration worldgenstage_decoration) { + return (List) this.aW.get(worldgenstage_decoration); + } + + public void a(WorldGenStage.Decoration worldgenstage_decoration, ChunkGenerator chunkgenerator, GeneratorAccess generatoraccess, long i, SeededRandom seededrandom, BlockPosition blockposition) { + int j = 0; + + for (Iterator iterator = ((List) this.aW.get(worldgenstage_decoration)).iterator(); iterator.hasNext(); ++j) { + WorldGenFeatureComposite worldgenfeaturecomposite = (WorldGenFeatureComposite) iterator.next(); + + seededrandom.b(i, j, worldgenstage_decoration.ordinal()); + worldgenfeaturecomposite.a(generatoraccess, chunkgenerator, seededrandom, blockposition, WorldGenFeatureConfiguration.e); + } + + } + + public void a(Random random, IChunkAccess ichunkaccess, int i, int j, int k, double d0, IBlockData iblockdata, IBlockData iblockdata1, int l, long i1) { + this.aS.a(i1); + this.aS.a(random, ichunkaccess, this, i, j, k, d0, iblockdata, iblockdata1, l, i1, BiomeBase.ag); + } + + public BiomeBase.EnumTemperature g() { + return this.aT == BiomeBase.Geography.OCEAN ? BiomeBase.EnumTemperature.OCEAN : ((double) this.getTemperature() < 0.2D ? BiomeBase.EnumTemperature.COLD : ((double) this.getTemperature() < 1.0D ? BiomeBase.EnumTemperature.MEDIUM : BiomeBase.EnumTemperature.WARM)); + } + + public static BiomeBase getBiome(int i, BiomeBase biomebase) { + BiomeBase biomebase1 = (BiomeBase) IRegistry.BIOME.fromId(i); + + return biomebase1 == null ? biomebase : biomebase1; + } + + public final float h() { + return this.aL; + } + + public final float getHumidity() { + return this.aO; + } + + public String k() { + if (this.aK == null) { + this.aK = SystemUtils.a("biome", IRegistry.BIOME.getKey(this)); + } + + return this.aK; + } + + public final float l() { + return this.aM; + } + + public final float getTemperature() { + return this.aN; + } + + public final int n() { + return this.aP; + } + + public final int o() { + return this.aQ; + } + + public final BiomeBase.Geography p() { + return this.aT; + } + + public WorldGenSurfaceComposite q() { + return this.aS; + } + + public WorldGenSurfaceConfiguration r() { + return this.aS.a(); + } + + @Nullable + public String s() { + return this.aR; + } + + public static void t() { + a(0, "ocean", new BiomeOcean()); + a(1, "plains", new BiomePlains()); + a(2, "desert", new BiomeDesert()); + a(3, "mountains", new BiomeBigHills()); + a(4, "forest", new BiomeForest()); + a(5, "taiga", new BiomeTaiga()); + a(6, "swamp", new BiomeSwamp()); + a(7, "river", new BiomeRiver()); + a(8, "nether", new BiomeHell()); + a(9, "the_end", new BiomeTheEnd()); + a(10, "frozen_ocean", new BiomeFrozenOcean()); + a(11, "frozen_river", new BiomeFrozenRiver()); + a(12, "snowy_tundra", new BiomeIcePlains()); + a(13, "snowy_mountains", new BiomeIceMountains()); + a(14, "mushroom_fields", new BiomeMushrooms()); + a(15, "mushroom_field_shore", new BiomeMushroomIslandShore()); + a(16, "beach", new BiomeBeach()); + a(17, "desert_hills", new BiomeDesertHills()); + a(18, "wooded_hills", new BiomeForestHills()); + a(19, "taiga_hills", new BiomeTaigaHills()); + a(20, "mountain_edge", new BiomeExtremeHillsEdge()); + a(21, "jungle", new BiomeJungle()); + a(22, "jungle_hills", new BiomeJungleHills()); + a(23, "jungle_edge", new BiomeJungleEdge()); + a(24, "deep_ocean", new BiomeDeepOcean()); + a(25, "stone_shore", new BiomeStoneBeach()); + a(26, "snowy_beach", new BiomeColdBeach()); + a(27, "birch_forest", new BiomeBirchForest()); + a(28, "birch_forest_hills", new BiomeBirchForestHills()); + a(29, "dark_forest", new BiomeRoofedForest()); + a(30, "snowy_taiga", new BiomeColdTaiga()); + a(31, "snowy_taiga_hills", new BiomeColdTaigaHills()); + a(32, "giant_tree_taiga", new BiomeMegaTaiga()); + a(33, "giant_tree_taiga_hills", new BiomeMegaTaigaHills()); + a(34, "wooded_mountains", new BiomeExtremeHillsWithTrees()); + a(35, "savanna", new BiomeSavanna()); + a(36, "savanna_plateau", new BiomeSavannaPlateau()); + a(37, "badlands", new BiomeMesa()); + a(38, "wooded_badlands_plateau", new BiomeMesaPlataeu()); + a(39, "badlands_plateau", new BiomeMesaPlataeuClear()); + a(40, "small_end_islands", new BiomeTheEndFloatingIslands()); + a(41, "end_midlands", new BiomeTheEndMediumIsland()); + a(42, "end_highlands", new BiomeTheEndHighIsland()); + a(43, "end_barrens", new BiomeTheEndBarrenIsland()); + a(44, "warm_ocean", new BiomeWarmOcean()); + a(45, "lukewarm_ocean", new BiomeLukewarmOcean()); + a(46, "cold_ocean", new BiomeColdOcean()); + a(47, "deep_warm_ocean", new BiomeWarmDeepOcean()); + a(48, "deep_lukewarm_ocean", new BiomeLukewarmDeepOcean()); + a(49, "deep_cold_ocean", new BiomeColdDeepOcean()); + a(50, "deep_frozen_ocean", new BiomeFrozenDeepOcean()); + a(127, "the_void", new BiomeVoid()); + a(129, "sunflower_plains", new BiomeSunflowerPlains()); + a(130, "desert_lakes", new BiomeDesertMutated()); + a(131, "gravelly_mountains", new BiomeExtremeHillsMutated()); + a(132, "flower_forest", new BiomeFlowerForest()); + a(133, "taiga_mountains", new BiomeTaigaMutated()); + a(134, "swamp_hills", new BiomeSwamplandMutated()); + a(140, "ice_spikes", new BiomeIcePlainsSpikes()); + a(149, "modified_jungle", new BiomeJungleMutated()); + a(151, "modified_jungle_edge", new BiomeJungleEdgeMutated()); + a(155, "tall_birch_forest", new BiomeBirchForestMutated()); + a(156, "tall_birch_hills", new BiomeBirchForestHillsMutated()); + a(157, "dark_forest_hills", new BiomeRoofedForestMutated()); + a(158, "snowy_taiga_mountains", new BiomeColdTaigaMutated()); + a(160, "giant_spruce_taiga", new BiomeMegaSpruceTaiga()); + a(161, "giant_spruce_taiga_hills", new BiomeRedwoodTaigaHillsMutated()); + a(162, "modified_gravelly_mountains", new BiomeExtremeHillsWithTreesMutated()); + a(163, "shattered_savanna", new BiomeSavannaMutated()); + a(164, "shattered_savanna_plateau", new BiomeSavannaPlateauMutated()); + a(165, "eroded_badlands", new BiomeMesaBryce()); + a(166, "modified_wooded_badlands_plateau", new BiomeMesaPlateauMutated()); + a(167, "modified_badlands_plateau", new BiomeMesaPlateauClearMutated()); + Collections.addAll(BiomeBase.aG, new BiomeBase[] { Biomes.OCEAN, Biomes.PLAINS, Biomes.DESERT, Biomes.MOUNTAINS, Biomes.FOREST, Biomes.TAIGA, Biomes.SWAMP, Biomes.RIVER, Biomes.FROZEN_RIVER, Biomes.SNOWY_TUNDRA, Biomes.SNOWY_MOUNTAINS, Biomes.MUSHROOM_FIELDS, Biomes.MUSHROOM_FIELD_SHORE, Biomes.BEACH, Biomes.DESERT_HILLS, Biomes.WOODED_HILLS, Biomes.TAIGA_HILLS, Biomes.JUNGLE, Biomes.JUNGLE_HILLS, Biomes.JUNGLE_EDGE, Biomes.DEEP_OCEAN, Biomes.STONE_SHORE, Biomes.SNOWY_BEACH, Biomes.BIRCH_FOREST, Biomes.BIRCH_FOREST_HILLS, Biomes.DARK_FOREST, Biomes.SNOWY_TAIGA, Biomes.SNOWY_TAIGA_HILLS, Biomes.GIANT_TREE_TAIGA, Biomes.GIANT_TREE_TAIGA_HILLS, Biomes.WOODED_MOUNTAINS, Biomes.SAVANNA, Biomes.SAVANNA_PLATEAU, Biomes.BADLANDS, Biomes.WOODED_BADLANDS_PLATEAU, Biomes.BADLANDS_PLATEAU}); + } + + private static void a(int i, String s, BiomeBase biomebase) { + IRegistry.BIOME.a(i, new MinecraftKey(s), biomebase); + if (biomebase.b()) { + BiomeBase.aH.a(biomebase, IRegistry.BIOME.a(IRegistry.BIOME.get(new MinecraftKey(biomebase.aR)))); + } + + } + + // Paper start - keep track of data in a pair set to give O(1) contains calls - we have to hook removals incase plugins mess with it + public static class MobList extends java.util.ArrayList { + java.util.Set biomes = new java.util.HashSet<>(); + + @Override + public boolean contains(Object o) { + return biomes.contains(o); + } + + @Override + public boolean add(BiomeMeta biomeMeta) { + biomes.add(biomeMeta); + return super.add(biomeMeta); + } + + @Override + public BiomeMeta remove(int index) { + BiomeMeta removed = super.remove(index); + if (removed != null) { + biomes.remove(removed); + } + return removed; + } + + @Override + public void clear() { + biomes.clear(); + super.clear(); + } + } + // Paper end + + public static class a { + + @Nullable + private WorldGenSurfaceComposite a; + @Nullable + private BiomeBase.Precipitation b; + @Nullable + private BiomeBase.Geography c; + @Nullable + private Float d; + @Nullable + private Float e; + @Nullable + private Float f; + @Nullable + private Float g; + @Nullable + private Integer h; + @Nullable + private Integer i; + @Nullable + private String j; + + public a() {} + + public BiomeBase.a a(WorldGenSurfaceComposite worldgensurfacecomposite) { + this.a = worldgensurfacecomposite; + return this; + } + + public BiomeBase.a a(BiomeBase.Precipitation biomebase_precipitation) { + this.b = biomebase_precipitation; + return this; + } + + public BiomeBase.a a(BiomeBase.Geography biomebase_geography) { + this.c = biomebase_geography; + return this; + } + + public BiomeBase.a a(float f) { + this.d = f; + return this; + } + + public BiomeBase.a b(float f) { + this.e = f; + return this; + } + + public BiomeBase.a c(float f) { + this.f = f; + return this; + } + + public BiomeBase.a d(float f) { + this.g = f; + return this; + } + + public BiomeBase.a a(int i) { + this.h = i; + return this; + } + + public BiomeBase.a b(int i) { + this.i = i; + return this; + } + + public BiomeBase.a a(@Nullable String s) { + this.j = s; + return this; + } + + public String toString() { + return "BiomeBuilder{\nsurfaceBuilder=" + this.a + ",\nprecipitation=" + this.b + ",\nbiomeCategory=" + this.c + ",\ndepth=" + this.d + ",\nscale=" + this.e + ",\ntemperature=" + this.f + ",\ndownfall=" + this.g + ",\nwaterColor=" + this.h + ",\nwaterFogColor=" + this.i + ",\nparent='" + this.j + '\'' + "\n" + '}'; + } + } + + public static class BiomeMeta extends WeightedRandom.WeightedRandomChoice { + + public EntityTypes b; + public int c; + public int d; + + public BiomeMeta(EntityTypes entitytypes, int i, int j, int k) { + super(i); + this.b = entitytypes; + this.c = j; + this.d = k; + } + + public String toString() { + return EntityTypes.getName(this.b) + "*(" + this.c + "-" + this.d + "):" + this.a; + } + } + + public static enum Precipitation { + + NONE, RAIN, SNOW; + + private Precipitation() {} + } + + public static enum Geography { + + NONE, TAIGA, EXTREME_HILLS, JUNGLE, MESA, PLAINS, SAVANNA, ICY, THEEND, BEACH, FOREST, OCEAN, DESERT, RIVER, SWAMP, MUSHROOM, NETHER; + + private Geography() {} + } + + public static enum EnumTemperature { + + OCEAN, COLD, MEDIUM, WARM; + + private EnumTemperature() {} + } +} diff --git a/src/main/java/net/minecraft/server/Block.java b/src/main/java/net/minecraft/server/Block.java new file mode 100644 index 000000000000..28efb4d4f0a5 --- /dev/null +++ b/src/main/java/net/minecraft/server/Block.java @@ -0,0 +1,1574 @@ +package net.minecraft.server; + +import com.google.common.collect.UnmodifiableIterator; +import it.unimi.dsi.fastutil.objects.Object2ByteLinkedOpenHashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Random; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class Block implements IMaterial { + + protected static final Logger d = LogManager.getLogger(); + public static final RegistryBlockID REGISTRY_ID = new RegistryBlockID<>(); + private static final EnumDirection[] a = new EnumDirection[] { EnumDirection.WEST, EnumDirection.EAST, EnumDirection.NORTH, EnumDirection.SOUTH, EnumDirection.DOWN, EnumDirection.UP}; + protected final int f; + public final float strength; + protected final float durability; + protected final boolean i; + protected final SoundEffectType stepSound; + protected final Material material; + // Paper start + public co.aikar.timings.Timing timing; + public co.aikar.timings.Timing getTiming() { + if (timing == null) { + timing = co.aikar.timings.MinecraftTimings.getBlockTiming(this); + } + return timing; + } + // Paper end + protected final MaterialMapColor l; + private final float frictionFactor; + protected final BlockStateList blockStateList; + private IBlockData blockData; + protected final boolean n; + private final boolean o; + @Nullable + private String name; + private static final ThreadLocal> q = ThreadLocal.withInitial(() -> { + Object2ByteLinkedOpenHashMap object2bytelinkedopenhashmap = new Object2ByteLinkedOpenHashMap(200) { + protected void rehash(int i) {} + }; + + object2bytelinkedopenhashmap.defaultReturnValue((byte) 127); + return object2bytelinkedopenhashmap; + }); + + public static int getCombinedId(@Nullable IBlockData iblockdata) { + if (iblockdata == null) { + return 0; + } else { + int i = Block.REGISTRY_ID.getId(iblockdata); + + return i == -1 ? 0 : i; + } + } + + public static IBlockData getByCombinedId(int i) { + IBlockData iblockdata = (IBlockData) Block.REGISTRY_ID.fromId(i); + + return iblockdata == null ? Blocks.AIR.getBlockData() : iblockdata; + } + + public static Block asBlock(@Nullable Item item) { + return item instanceof ItemBlock ? ((ItemBlock) item).getBlock() : Blocks.AIR; + } + + public static IBlockData a(IBlockData iblockdata, IBlockData iblockdata1, World world, BlockPosition blockposition) { + VoxelShape voxelshape = VoxelShapes.b(iblockdata.getCollisionShape(world, blockposition), iblockdata1.getCollisionShape(world, blockposition), OperatorBoolean.ONLY_SECOND).a((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ()); + List list = world.getEntities((Entity) null, voxelshape.getBoundingBox()); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + double d0 = VoxelShapes.a(EnumDirection.EnumAxis.Y, entity.getBoundingBox().d(0.0D, 1.0D, 0.0D), Stream.of(voxelshape), -1.0D); + + entity.enderTeleportTo(entity.locX, entity.locY + 1.0D + d0, entity.locZ); + } + + return iblockdata1; + } + + public static VoxelShape a(double d0, double d1, double d2, double d3, double d4, double d5) { + return VoxelShapes.create(d0 / 16.0D, d1 / 16.0D, d2 / 16.0D, d3 / 16.0D, d4 / 16.0D, d5 / 16.0D); + } + + @Deprecated + public boolean a(IBlockData iblockdata, Entity entity) { + return true; + } + + @Deprecated + public boolean e(IBlockData iblockdata) { + return false; + } + + @Deprecated + public int m(IBlockData iblockdata) { + return this.f; + } + + @Deprecated + public Material n(IBlockData iblockdata) { + return this.material; + } + + @Deprecated + public MaterialMapColor c(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return this.l; + } + + @Deprecated + public void a(IBlockData iblockdata, GeneratorAccess generatoraccess, BlockPosition blockposition, int i) { + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + EnumDirection[] aenumdirection = Block.a; + int j = aenumdirection.length; + + for (int k = 0; k < j; ++k) { + EnumDirection enumdirection = aenumdirection[k]; + + blockposition_b.g(blockposition).c(enumdirection); + IBlockData iblockdata1 = generatoraccess.getType(blockposition_b); + IBlockData iblockdata2 = iblockdata1.updateState(enumdirection.opposite(), iblockdata, generatoraccess, blockposition_b, blockposition); + + a(iblockdata1, iblockdata2, generatoraccess, blockposition_b, i); + } + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + + } + + public boolean a(Tag tag) { + return tag.isTagged(this); + } + + public static IBlockData getValidBlockForPosition(IBlockData iblockdata, GeneratorAccess generatoraccess, BlockPosition blockposition) { return Block.b(iblockdata, generatoraccess, blockposition); } // Paper - OBFHELPER + public static IBlockData b(IBlockData iblockdata, GeneratorAccess generatoraccess, BlockPosition blockposition) { + IBlockData iblockdata1 = iblockdata; + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + EnumDirection[] aenumdirection = Block.a; + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + + blockposition_mutableblockposition.g(blockposition).c(enumdirection); + iblockdata1 = iblockdata1.updateState(enumdirection, generatoraccess.getType(blockposition_mutableblockposition), generatoraccess, blockposition, blockposition_mutableblockposition); + } + + return iblockdata1; + } + + public static void a(IBlockData iblockdata, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, int i) { + if (iblockdata1 != iblockdata) { + if (iblockdata1.isAir()) { + if (!generatoraccess.e()) { + generatoraccess.setAir(blockposition, (i & 32) == 0); + } + } else { + generatoraccess.setTypeAndData(blockposition, iblockdata1, i & -33); + } + } + + } + + @Deprecated + public void b(IBlockData iblockdata, GeneratorAccess generatoraccess, BlockPosition blockposition, int i) {} + + @Deprecated + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + return iblockdata; + } + + @Deprecated + public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) { + return iblockdata; + } + + @Deprecated + public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) { + return iblockdata; + } + + public Block(Block.Info block_info) { + BlockStateList.a blockstatelist_a = new BlockStateList.a<>(this); + + this.a(blockstatelist_a); + this.blockStateList = blockstatelist_a.a(BlockData::new); + this.v((IBlockData) this.blockStateList.getBlockData()); + this.material = block_info.a; + this.l = block_info.b; + this.n = block_info.c; + this.stepSound = block_info.d; + this.f = block_info.e; + this.durability = block_info.f; + this.strength = block_info.g; + this.i = block_info.h; + this.frictionFactor = block_info.i; + this.o = block_info.j; + } + + protected static boolean a(Block block) { + return block instanceof BlockShulkerBox || block instanceof BlockLeaves || block.a(TagsBlock.TRAPDOORS) || block instanceof BlockStainedGlass || block == Blocks.BEACON || block == Blocks.CAULDRON || block == Blocks.GLASS || block == Blocks.GLOWSTONE || block == Blocks.ICE || block == Blocks.SEA_LANTERN || block == Blocks.CONDUIT; + } + + public static boolean b(Block block) { + return a(block) || block == Blocks.PISTON || block == Blocks.STICKY_PISTON || block == Blocks.PISTON_HEAD; + } + + @Deprecated + public boolean o(IBlockData iblockdata) { + return iblockdata.getMaterial().isSolid() && iblockdata.g(); + } + + @Deprecated + public boolean isOccluding(IBlockData iblockdata) { + return iblockdata.getMaterial().f() && iblockdata.g() && !iblockdata.isPowerSource(); + } + + @Deprecated + public boolean q(IBlockData iblockdata) { + return this.material.isSolid() && iblockdata.g(); + } + + @Deprecated + public boolean a(IBlockData iblockdata) { + return true; + } + + @Deprecated + public boolean r(IBlockData iblockdata) { + return iblockdata.getMaterial().f() && iblockdata.g(); + } + + @Deprecated + public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) { + switch (pathmode) { + case LAND: + return !a(this.f(iblockdata, iblockaccess, blockposition)); + case WATER: + return iblockaccess.getFluid(blockposition).a(TagsFluid.WATER); + case AIR: + return !a(this.f(iblockdata, iblockaccess, blockposition)); + default: + return false; + } + } + + @Deprecated + public EnumRenderType c(IBlockData iblockdata) { + return EnumRenderType.MODEL; + } + + @Deprecated + public boolean a(IBlockData iblockdata, BlockActionContext blockactioncontext) { + return this.material.isReplaceable() && blockactioncontext.getItemStack().getItem() != this.getItem(); + } + + @Deprecated + public float d(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return this.strength; + } + + public boolean isTicking(IBlockData iblockdata) { + return this.i; + } + + public boolean isTileEntity() { + return this instanceof ITileEntity; + } + + @Deprecated + public boolean e(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return false; + } + + @Deprecated + public boolean f(IBlockData iblockdata) { + return this.n && iblockdata.getBlock().c() == TextureType.SOLID; + } + + @Deprecated + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.SOLID; + } + + @Deprecated + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return VoxelShapes.b(); + } + + @Deprecated + public VoxelShape f(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return this.n ? iblockdata.getShape(iblockaccess, blockposition) : VoxelShapes.a(); + } + + @Deprecated + public VoxelShape g(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return iblockdata.getShape(iblockaccess, blockposition); + } + + @Deprecated + public VoxelShape h(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return VoxelShapes.a(); + } + + public static boolean a(VoxelShape voxelshape, EnumDirection enumdirection) { + VoxelShape voxelshape1 = voxelshape.a(enumdirection); + + return a(voxelshape1); + } + + public static boolean a(VoxelShape voxelshape) { + return !VoxelShapes.c(VoxelShapes.b(), voxelshape, OperatorBoolean.ONLY_FIRST); + } + + @Deprecated + public final boolean i(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + boolean flag = iblockdata.p(); + VoxelShape voxelshape = flag ? iblockdata.i(iblockaccess, blockposition) : VoxelShapes.a(); + + return a(voxelshape); + } + + public boolean a_(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return !a(iblockdata.getShape(iblockaccess, blockposition)) && iblockdata.s().e(); + } + + @Deprecated + public int j(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return iblockdata.f(iblockaccess, blockposition) ? iblockaccess.K() : (iblockdata.a(iblockaccess, blockposition) ? 0 : 1); + } + + @Deprecated + public final boolean k(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return !iblockdata.f(iblockaccess, blockposition) && iblockdata.b(iblockaccess, blockposition) == iblockaccess.K(); + } + + public boolean isCollidable(IBlockData iblockdata) { + return this.j(); + } + + public boolean j() { + return true; + } + + @Deprecated + public void b(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + this.a(iblockdata, world, blockposition, random); + } + + @Deprecated + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {} + + public void postBreak(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata) {} + + @Deprecated + public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {} + + public int a(IWorldReader iworldreader) { + return 10; + } + + @Deprecated + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + org.spigotmc.AsyncCatcher.catchOp( "block onPlace"); // Spigot + } + + @Deprecated + public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) { + org.spigotmc.AsyncCatcher.catchOp( "block remove"); // Spigot + } + + public int a(IBlockData iblockdata, Random random) { + return 1; + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return this; + } + + @Deprecated + public float getDamage(IBlockData iblockdata, EntityHuman entityhuman, IBlockAccess iblockaccess, BlockPosition blockposition) { + float f = iblockdata.e(iblockaccess, blockposition); + + if (f == -1.0F) { + return 0.0F; + } else { + int i = entityhuman.hasBlock(iblockdata) ? 30 : 100; + + return entityhuman.b(iblockdata) / f / (float) i; + } + } + + @Deprecated + public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) { + if (!world.isClientSide) { + int j = this.getDropCount(iblockdata, i, world, blockposition, world.random); + + for (int k = 0; k < j; ++k) { + // CraftBukkit - <= to < to allow for plugins to completely disable block drops from explosions + if (f >= 1.0F || world.random.nextFloat() < f) { + Item item = this.getDropType(iblockdata, world, blockposition, i).getItem(); + + if (item != Items.AIR) { + a(world, blockposition, new ItemStack(item)); + } + } + } + + } + } + + public static void a(World world, BlockPosition blockposition, ItemStack itemstack) { + if (!world.isClientSide && !itemstack.isEmpty() && world.getGameRules().getBoolean("doTileDrops")) { + float f = 0.5F; + double d0 = (double) (world.random.nextFloat() * 0.5F) + 0.25D; + double d1 = (double) (world.random.nextFloat() * 0.5F) + 0.25D; + double d2 = (double) (world.random.nextFloat() * 0.5F) + 0.25D; + EntityItem entityitem = new EntityItem(world, (double) blockposition.getX() + d0, (double) blockposition.getY() + d1, (double) blockposition.getZ() + d2, itemstack); + + entityitem.n(); + // CraftBukkit start + if (world.captureDrops != null) { + world.captureDrops.add(entityitem); + } else { + world.addEntity(entityitem); + } + // CraftBukkit end + } + } + + protected void dropExperience(World world, BlockPosition blockposition, int i, EntityPlayer player) { // Paper + if (!world.isClientSide && world.getGameRules().getBoolean("doTileDrops")) { + while (i > 0) { + int j = EntityExperienceOrb.getOrbValue(i); + + i -= j; + world.addEntity(new EntityExperienceOrb(world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, j, org.bukkit.entity.ExperienceOrb.SpawnReason.BLOCK_BREAK, player)); // Paper + } + } + + } + + public float getDurability() { + return this.durability; + } + + @Nullable + public static MovingObjectPosition rayTrace(IBlockData iblockdata, World world, BlockPosition blockposition, Vec3D vec3d, Vec3D vec3d1) { + MovingObjectPosition movingobjectposition = iblockdata.getShape(world, blockposition).rayTrace(vec3d, vec3d1, blockposition); + + if (movingobjectposition != null) { + MovingObjectPosition movingobjectposition1 = iblockdata.j(world, blockposition).rayTrace(vec3d, vec3d1, blockposition); + + if (movingobjectposition1 != null && movingobjectposition1.pos.d(vec3d).c() < movingobjectposition.pos.d(vec3d).c()) { + movingobjectposition.direction = movingobjectposition1.direction; + } + } + + return movingobjectposition; + } + + public void wasExploded(World world, BlockPosition blockposition, Explosion explosion) {} + + public TextureType c() { + return TextureType.SOLID; + } + + @Deprecated + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + return true; + } + + @Deprecated + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + return false; + } + + public void stepOn(World world, BlockPosition blockposition, Entity entity) {} + + @Nullable + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + return this.getBlockData(); + } + + @Deprecated + public void attack(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman) {} + + @Deprecated + public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return 0; + } + + @Deprecated + public boolean isPowerSource(IBlockData iblockdata) { + return false; + } + + @Deprecated + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) {} + + @Deprecated + public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return 0; + } + + public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) { + entityhuman.b(StatisticList.BLOCK_MINED.b(this)); + entityhuman.applyExhaustion(0.005F); + if (this.X_() && EnchantmentManager.getEnchantmentLevel(Enchantments.SILK_TOUCH, itemstack) > 0) { + ItemStack itemstack1 = this.t(iblockdata); + + a(world, blockposition, itemstack1); + } else { + int i = EnchantmentManager.getEnchantmentLevel(Enchantments.LOOT_BONUS_BLOCKS, itemstack); + + iblockdata.a(world, blockposition, i); + } + + } + + protected boolean X_() { + return this.getBlockData().g() && !this.isTileEntity(); + } + + protected ItemStack t(IBlockData iblockdata) { + return new ItemStack(this); + } + + public int getDropCount(IBlockData iblockdata, int i, World world, BlockPosition blockposition, Random random) { + return this.a(iblockdata, random); + } + + public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, @Nullable EntityLiving entityliving, ItemStack itemstack) {} + + public boolean a() { + return !this.material.isBuildable() && !this.material.isLiquid(); + } + + public String m() { + if (this.name == null) { + this.name = SystemUtils.a("block", IRegistry.BLOCK.getKey(this)); + } + + return this.name; + } + + @Deprecated + public boolean a(IBlockData iblockdata, World world, BlockPosition blockposition, int i, int j) { + return false; + } + + @Deprecated + public EnumPistonReaction getPushReaction(IBlockData iblockdata) { + return this.material.getPushReaction(); + } + + public void fallOn(World world, BlockPosition blockposition, Entity entity, float f) { + entity.c(f, 1.0F); + } + + public void a(IBlockAccess iblockaccess, Entity entity) { + entity.motY = 0.0D; + } + + public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) { + return new ItemStack(this); + } + + public void a(CreativeModeTab creativemodetab, NonNullList nonnulllist) { + nonnulllist.add(new ItemStack(this)); + } + + @Deprecated + public Fluid h(IBlockData iblockdata) { + return FluidTypes.EMPTY.i(); + } + + public float n() { + return this.frictionFactor; + } + + public void a(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) { + world.a(entityhuman, 2001, blockposition, getCombinedId(iblockdata)); + } + + public void c(World world, BlockPosition blockposition) {} + + public boolean a(Explosion explosion) { + return true; + } + + @Deprecated + public boolean isComplexRedstone(IBlockData iblockdata) { + return false; + } + + @Deprecated + public int a(IBlockData iblockdata, World world, BlockPosition blockposition) { + return 0; + } + + protected void a(BlockStateList.a blockstatelist_a) {} + + public BlockStateList getStates() { + return this.blockStateList; + } + + protected final void v(IBlockData iblockdata) { + this.blockData = iblockdata; + } + + public final IBlockData getBlockData() { + return this.blockData; + } + + public Block.EnumRandomOffset q() { + return Block.EnumRandomOffset.NONE; + } + + @Deprecated + public Vec3D l(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + Block.EnumRandomOffset block_enumrandomoffset = this.q(); + + if (block_enumrandomoffset == Block.EnumRandomOffset.NONE) { + return Vec3D.a; + } else { + long i = MathHelper.c(blockposition.getX(), 0, blockposition.getZ()); + + return new Vec3D(((double) ((float) (i & 15L) / 15.0F) - 0.5D) * 0.5D, block_enumrandomoffset == Block.EnumRandomOffset.XYZ ? ((double) ((float) (i >> 4 & 15L) / 15.0F) - 1.0D) * 0.2D : 0.0D, ((double) ((float) (i >> 8 & 15L) / 15.0F) - 0.5D) * 0.5D); + } + } + + public SoundEffectType getStepSound() { + return this.stepSound; + } + + public Item getItem() { + return Item.getItemOf(this); + } + + public boolean s() { + return this.o; + } + + public String toString() { + return "Block{" + IRegistry.BLOCK.getKey(this) + "}"; + } + + public static boolean c(Block block) { + return block == Blocks.STONE || block == Blocks.GRANITE || block == Blocks.DIORITE || block == Blocks.ANDESITE; + } + + public static boolean d(Block block) { + return block == Blocks.DIRT || block == Blocks.COARSE_DIRT || block == Blocks.PODZOL; + } + + public static void t() { + BlockAir blockair = new BlockAir(Block.Info.a(Material.AIR).a()); + + a(IRegistry.BLOCK.b(), (Block) blockair); + BlockStone blockstone = new BlockStone(Block.Info.a(Material.STONE, MaterialMapColor.m).a(1.5F, 6.0F)); + + a("stone", (Block) blockstone); + a("granite", new Block(Block.Info.a(Material.STONE, MaterialMapColor.l).a(1.5F, 6.0F))); + a("polished_granite", new Block(Block.Info.a(Material.STONE, MaterialMapColor.l).a(1.5F, 6.0F))); + a("diorite", new Block(Block.Info.a(Material.STONE, MaterialMapColor.p).a(1.5F, 6.0F))); + a("polished_diorite", new Block(Block.Info.a(Material.STONE, MaterialMapColor.p).a(1.5F, 6.0F))); + a("andesite", new Block(Block.Info.a(Material.STONE, MaterialMapColor.m).a(1.5F, 6.0F))); + a("polished_andesite", new Block(Block.Info.a(Material.STONE, MaterialMapColor.m).a(1.5F, 6.0F))); + a("grass_block", (Block) (new BlockGrass(Block.Info.a(Material.GRASS).c().b(0.6F).a(SoundEffectType.c)))); + a("dirt", new Block(Block.Info.a(Material.EARTH, MaterialMapColor.l).b(0.5F).a(SoundEffectType.b))); + a("coarse_dirt", new Block(Block.Info.a(Material.EARTH, MaterialMapColor.l).b(0.5F).a(SoundEffectType.b))); + a("podzol", (Block) (new BlockDirtSnow(Block.Info.a(Material.EARTH, MaterialMapColor.J).b(0.5F).a(SoundEffectType.b)))); + Block block = new Block(Block.Info.a(Material.STONE).a(2.0F, 6.0F)); + + a("cobblestone", block); + Block block1 = new Block(Block.Info.a(Material.WOOD, MaterialMapColor.o).a(2.0F, 3.0F).a(SoundEffectType.a)); + Block block2 = new Block(Block.Info.a(Material.WOOD, MaterialMapColor.J).a(2.0F, 3.0F).a(SoundEffectType.a)); + Block block3 = new Block(Block.Info.a(Material.WOOD, MaterialMapColor.d).a(2.0F, 3.0F).a(SoundEffectType.a)); + Block block4 = new Block(Block.Info.a(Material.WOOD, MaterialMapColor.l).a(2.0F, 3.0F).a(SoundEffectType.a)); + Block block5 = new Block(Block.Info.a(Material.WOOD, MaterialMapColor.q).a(2.0F, 3.0F).a(SoundEffectType.a)); + Block block6 = new Block(Block.Info.a(Material.WOOD, MaterialMapColor.B).a(2.0F, 3.0F).a(SoundEffectType.a)); + + a("oak_planks", block1); + a("spruce_planks", block2); + a("birch_planks", block3); + a("jungle_planks", block4); + a("acacia_planks", block5); + a("dark_oak_planks", block6); + BlockSapling blocksapling = new BlockSapling(new WorldGenTreeProviderOak(), Block.Info.a(Material.PLANT).a().c().b().a(SoundEffectType.c)); + BlockSapling blocksapling1 = new BlockSapling(new WorldGenTreeProviderSpruce(), Block.Info.a(Material.PLANT).a().c().b().a(SoundEffectType.c)); + BlockSapling blocksapling2 = new BlockSapling(new WorldGenTreeProviderBirch(), Block.Info.a(Material.PLANT).a().c().b().a(SoundEffectType.c)); + BlockSapling blocksapling3 = new BlockSapling(new WorldGenMegaTreeProviderJungle(), Block.Info.a(Material.PLANT).a().c().b().a(SoundEffectType.c)); + BlockSapling blocksapling4 = new BlockSapling(new WorldGenTreeProviderAcacia(), Block.Info.a(Material.PLANT).a().c().b().a(SoundEffectType.c)); + BlockSapling blocksapling5 = new BlockSapling(new WorldGenMegaTreeProviderDarkOak(), Block.Info.a(Material.PLANT).a().c().b().a(SoundEffectType.c)); + + a("oak_sapling", (Block) blocksapling); + a("spruce_sapling", (Block) blocksapling1); + a("birch_sapling", (Block) blocksapling2); + a("jungle_sapling", (Block) blocksapling3); + a("acacia_sapling", (Block) blocksapling4); + a("dark_oak_sapling", (Block) blocksapling5); + a("bedrock", (Block) (new BlockNoDrop(Block.Info.a(Material.STONE).a(-1.0F, 3600000.0F)))); + a("water", (Block) (new BlockFluids(FluidTypes.WATER, Block.Info.a(Material.WATER).a().b(100.0F)))); + a("lava", (Block) (new BlockFluids(FluidTypes.LAVA, Block.Info.a(Material.LAVA).a().c().b(100.0F).a(15)))); + a("sand", (Block) (new BlockSand(14406560, Block.Info.a(Material.SAND, MaterialMapColor.d).b(0.5F).a(SoundEffectType.h)))); + a("red_sand", (Block) (new BlockSand(11098145, Block.Info.a(Material.SAND, MaterialMapColor.q).b(0.5F).a(SoundEffectType.h)))); + a("gravel", (Block) (new BlockGravel(Block.Info.a(Material.SAND, MaterialMapColor.m).b(0.6F).a(SoundEffectType.b)))); + a("gold_ore", (Block) (new BlockOre(Block.Info.a(Material.STONE).a(3.0F, 3.0F)))); + a("iron_ore", (Block) (new BlockOre(Block.Info.a(Material.STONE).a(3.0F, 3.0F)))); + a("coal_ore", (Block) (new BlockOre(Block.Info.a(Material.STONE).a(3.0F, 3.0F)))); + a("oak_log", (Block) (new BlockLogAbstract(MaterialMapColor.o, Block.Info.a(Material.WOOD, MaterialMapColor.J).b(2.0F).a(SoundEffectType.a)))); + a("spruce_log", (Block) (new BlockLogAbstract(MaterialMapColor.J, Block.Info.a(Material.WOOD, MaterialMapColor.B).b(2.0F).a(SoundEffectType.a)))); + a("birch_log", (Block) (new BlockLogAbstract(MaterialMapColor.d, Block.Info.a(Material.WOOD, MaterialMapColor.p).b(2.0F).a(SoundEffectType.a)))); + a("jungle_log", (Block) (new BlockLogAbstract(MaterialMapColor.l, Block.Info.a(Material.WOOD, MaterialMapColor.J).b(2.0F).a(SoundEffectType.a)))); + a("acacia_log", (Block) (new BlockLogAbstract(MaterialMapColor.q, Block.Info.a(Material.WOOD, MaterialMapColor.m).b(2.0F).a(SoundEffectType.a)))); + a("dark_oak_log", (Block) (new BlockLogAbstract(MaterialMapColor.B, Block.Info.a(Material.WOOD, MaterialMapColor.B).b(2.0F).a(SoundEffectType.a)))); + a("stripped_spruce_log", (Block) (new BlockLogAbstract(MaterialMapColor.J, Block.Info.a(Material.WOOD, MaterialMapColor.J).b(2.0F).a(SoundEffectType.a)))); + a("stripped_birch_log", (Block) (new BlockLogAbstract(MaterialMapColor.d, Block.Info.a(Material.WOOD, MaterialMapColor.d).b(2.0F).a(SoundEffectType.a)))); + a("stripped_jungle_log", (Block) (new BlockLogAbstract(MaterialMapColor.l, Block.Info.a(Material.WOOD, MaterialMapColor.l).b(2.0F).a(SoundEffectType.a)))); + a("stripped_acacia_log", (Block) (new BlockLogAbstract(MaterialMapColor.q, Block.Info.a(Material.WOOD, MaterialMapColor.q).b(2.0F).a(SoundEffectType.a)))); + a("stripped_dark_oak_log", (Block) (new BlockLogAbstract(MaterialMapColor.B, Block.Info.a(Material.WOOD, MaterialMapColor.B).b(2.0F).a(SoundEffectType.a)))); + a("stripped_oak_log", (Block) (new BlockLogAbstract(MaterialMapColor.o, Block.Info.a(Material.WOOD, MaterialMapColor.o).b(2.0F).a(SoundEffectType.a)))); + a("oak_wood", (Block) (new BlockRotatable(Block.Info.a(Material.WOOD, MaterialMapColor.o).b(2.0F).a(SoundEffectType.a)))); + a("spruce_wood", (Block) (new BlockRotatable(Block.Info.a(Material.WOOD, MaterialMapColor.J).b(2.0F).a(SoundEffectType.a)))); + a("birch_wood", (Block) (new BlockRotatable(Block.Info.a(Material.WOOD, MaterialMapColor.d).b(2.0F).a(SoundEffectType.a)))); + a("jungle_wood", (Block) (new BlockRotatable(Block.Info.a(Material.WOOD, MaterialMapColor.l).b(2.0F).a(SoundEffectType.a)))); + a("acacia_wood", (Block) (new BlockRotatable(Block.Info.a(Material.WOOD, MaterialMapColor.q).b(2.0F).a(SoundEffectType.a)))); + a("dark_oak_wood", (Block) (new BlockRotatable(Block.Info.a(Material.WOOD, MaterialMapColor.B).b(2.0F).a(SoundEffectType.a)))); + a("stripped_oak_wood", (Block) (new BlockRotatable(Block.Info.a(Material.WOOD, MaterialMapColor.o).b(2.0F).a(SoundEffectType.a)))); + a("stripped_spruce_wood", (Block) (new BlockRotatable(Block.Info.a(Material.WOOD, MaterialMapColor.J).b(2.0F).a(SoundEffectType.a)))); + a("stripped_birch_wood", (Block) (new BlockRotatable(Block.Info.a(Material.WOOD, MaterialMapColor.d).b(2.0F).a(SoundEffectType.a)))); + a("stripped_jungle_wood", (Block) (new BlockRotatable(Block.Info.a(Material.WOOD, MaterialMapColor.l).b(2.0F).a(SoundEffectType.a)))); + a("stripped_acacia_wood", (Block) (new BlockRotatable(Block.Info.a(Material.WOOD, MaterialMapColor.q).b(2.0F).a(SoundEffectType.a)))); + a("stripped_dark_oak_wood", (Block) (new BlockRotatable(Block.Info.a(Material.WOOD, MaterialMapColor.B).b(2.0F).a(SoundEffectType.a)))); + a("oak_leaves", (Block) (new BlockLeaves(Block.Info.a(Material.LEAVES).b(0.2F).c().a(SoundEffectType.c)))); + a("spruce_leaves", (Block) (new BlockLeaves(Block.Info.a(Material.LEAVES).b(0.2F).c().a(SoundEffectType.c)))); + a("birch_leaves", (Block) (new BlockLeaves(Block.Info.a(Material.LEAVES).b(0.2F).c().a(SoundEffectType.c)))); + a("jungle_leaves", (Block) (new BlockLeaves(Block.Info.a(Material.LEAVES).b(0.2F).c().a(SoundEffectType.c)))); + a("acacia_leaves", (Block) (new BlockLeaves(Block.Info.a(Material.LEAVES).b(0.2F).c().a(SoundEffectType.c)))); + a("dark_oak_leaves", (Block) (new BlockLeaves(Block.Info.a(Material.LEAVES).b(0.2F).c().a(SoundEffectType.c)))); + a("sponge", (Block) (new BlockSponge(Block.Info.a(Material.SPONGE).b(0.6F).a(SoundEffectType.c)))); + a("wet_sponge", (Block) (new BlockWetSponge(Block.Info.a(Material.SPONGE).b(0.6F).a(SoundEffectType.c)))); + a("glass", (Block) (new BlockGlass(Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("lapis_ore", (Block) (new BlockOre(Block.Info.a(Material.STONE).a(3.0F, 3.0F)))); + a("lapis_block", new Block(Block.Info.a(Material.ORE, MaterialMapColor.H).a(3.0F, 3.0F))); + a("dispenser", (Block) (new BlockDispenser(Block.Info.a(Material.STONE).b(3.5F)))); + Block block7 = new Block(Block.Info.a(Material.STONE, MaterialMapColor.d).b(0.8F)); + + a("sandstone", block7); + a("chiseled_sandstone", new Block(Block.Info.a(Material.STONE, MaterialMapColor.d).b(0.8F))); + a("cut_sandstone", new Block(Block.Info.a(Material.STONE, MaterialMapColor.d).b(0.8F))); + a("note_block", (Block) (new BlockNote(Block.Info.a(Material.WOOD).a(SoundEffectType.a).b(0.8F)))); + a("white_bed", (Block) (new BlockBed(EnumColor.WHITE, Block.Info.a(Material.CLOTH).a(SoundEffectType.a).b(0.2F)))); + a("orange_bed", (Block) (new BlockBed(EnumColor.ORANGE, Block.Info.a(Material.CLOTH).a(SoundEffectType.a).b(0.2F)))); + a("magenta_bed", (Block) (new BlockBed(EnumColor.MAGENTA, Block.Info.a(Material.CLOTH).a(SoundEffectType.a).b(0.2F)))); + a("light_blue_bed", (Block) (new BlockBed(EnumColor.LIGHT_BLUE, Block.Info.a(Material.CLOTH).a(SoundEffectType.a).b(0.2F)))); + a("yellow_bed", (Block) (new BlockBed(EnumColor.YELLOW, Block.Info.a(Material.CLOTH).a(SoundEffectType.a).b(0.2F)))); + a("lime_bed", (Block) (new BlockBed(EnumColor.LIME, Block.Info.a(Material.CLOTH).a(SoundEffectType.a).b(0.2F)))); + a("pink_bed", (Block) (new BlockBed(EnumColor.PINK, Block.Info.a(Material.CLOTH).a(SoundEffectType.a).b(0.2F)))); + a("gray_bed", (Block) (new BlockBed(EnumColor.GRAY, Block.Info.a(Material.CLOTH).a(SoundEffectType.a).b(0.2F)))); + a("light_gray_bed", (Block) (new BlockBed(EnumColor.LIGHT_GRAY, Block.Info.a(Material.CLOTH).a(SoundEffectType.a).b(0.2F)))); + a("cyan_bed", (Block) (new BlockBed(EnumColor.CYAN, Block.Info.a(Material.CLOTH).a(SoundEffectType.a).b(0.2F)))); + a("purple_bed", (Block) (new BlockBed(EnumColor.PURPLE, Block.Info.a(Material.CLOTH).a(SoundEffectType.a).b(0.2F)))); + a("blue_bed", (Block) (new BlockBed(EnumColor.BLUE, Block.Info.a(Material.CLOTH).a(SoundEffectType.a).b(0.2F)))); + a("brown_bed", (Block) (new BlockBed(EnumColor.BROWN, Block.Info.a(Material.CLOTH).a(SoundEffectType.a).b(0.2F)))); + a("green_bed", (Block) (new BlockBed(EnumColor.GREEN, Block.Info.a(Material.CLOTH).a(SoundEffectType.a).b(0.2F)))); + a("red_bed", (Block) (new BlockBed(EnumColor.RED, Block.Info.a(Material.CLOTH).a(SoundEffectType.a).b(0.2F)))); + a("black_bed", (Block) (new BlockBed(EnumColor.BLACK, Block.Info.a(Material.CLOTH).a(SoundEffectType.a).b(0.2F)))); + a("powered_rail", (Block) (new BlockPoweredRail(Block.Info.a(Material.ORIENTABLE).a().b(0.7F).a(SoundEffectType.e)))); + a("detector_rail", (Block) (new BlockMinecartDetector(Block.Info.a(Material.ORIENTABLE).a().b(0.7F).a(SoundEffectType.e)))); + a("sticky_piston", (Block) (new BlockPiston(true, Block.Info.a(Material.PISTON).b(0.5F)))); + a("cobweb", (Block) (new BlockWeb(Block.Info.a(Material.WEB).a().b(4.0F)))); + BlockLongGrass blocklonggrass = new BlockLongGrass(Block.Info.a(Material.REPLACEABLE_PLANT).a().b().a(SoundEffectType.c)); + BlockLongGrass blocklonggrass1 = new BlockLongGrass(Block.Info.a(Material.REPLACEABLE_PLANT).a().b().a(SoundEffectType.c)); + BlockDeadBush blockdeadbush = new BlockDeadBush(Block.Info.a(Material.REPLACEABLE_PLANT, MaterialMapColor.o).a().b().a(SoundEffectType.c)); + + a("grass", (Block) blocklonggrass); + a("fern", (Block) blocklonggrass1); + a("dead_bush", (Block) blockdeadbush); + BlockSeaGrass blockseagrass = new BlockSeaGrass(Block.Info.a(Material.REPLACEABLE_WATER_PLANT).a().b().a(SoundEffectType.m)); + + a("seagrass", (Block) blockseagrass); + a("tall_seagrass", (Block) (new BlockTallSeaGrass(blockseagrass, Block.Info.a(Material.REPLACEABLE_WATER_PLANT).a().b().a(SoundEffectType.m)))); + a("piston", (Block) (new BlockPiston(false, Block.Info.a(Material.PISTON).b(0.5F)))); + a("piston_head", (Block) (new BlockPistonExtension(Block.Info.a(Material.PISTON).b(0.5F)))); + a("white_wool", new Block(Block.Info.a(Material.CLOTH, MaterialMapColor.j).b(0.8F).a(SoundEffectType.g))); + a("orange_wool", new Block(Block.Info.a(Material.CLOTH, MaterialMapColor.q).b(0.8F).a(SoundEffectType.g))); + a("magenta_wool", new Block(Block.Info.a(Material.CLOTH, MaterialMapColor.r).b(0.8F).a(SoundEffectType.g))); + a("light_blue_wool", new Block(Block.Info.a(Material.CLOTH, MaterialMapColor.s).b(0.8F).a(SoundEffectType.g))); + a("yellow_wool", new Block(Block.Info.a(Material.CLOTH, MaterialMapColor.t).b(0.8F).a(SoundEffectType.g))); + a("lime_wool", new Block(Block.Info.a(Material.CLOTH, MaterialMapColor.u).b(0.8F).a(SoundEffectType.g))); + a("pink_wool", new Block(Block.Info.a(Material.CLOTH, MaterialMapColor.v).b(0.8F).a(SoundEffectType.g))); + a("gray_wool", new Block(Block.Info.a(Material.CLOTH, MaterialMapColor.w).b(0.8F).a(SoundEffectType.g))); + a("light_gray_wool", new Block(Block.Info.a(Material.CLOTH, MaterialMapColor.x).b(0.8F).a(SoundEffectType.g))); + a("cyan_wool", new Block(Block.Info.a(Material.CLOTH, MaterialMapColor.y).b(0.8F).a(SoundEffectType.g))); + a("purple_wool", new Block(Block.Info.a(Material.CLOTH, MaterialMapColor.z).b(0.8F).a(SoundEffectType.g))); + a("blue_wool", new Block(Block.Info.a(Material.CLOTH, MaterialMapColor.A).b(0.8F).a(SoundEffectType.g))); + a("brown_wool", new Block(Block.Info.a(Material.CLOTH, MaterialMapColor.B).b(0.8F).a(SoundEffectType.g))); + a("green_wool", new Block(Block.Info.a(Material.CLOTH, MaterialMapColor.C).b(0.8F).a(SoundEffectType.g))); + a("red_wool", new Block(Block.Info.a(Material.CLOTH, MaterialMapColor.D).b(0.8F).a(SoundEffectType.g))); + a("black_wool", new Block(Block.Info.a(Material.CLOTH, MaterialMapColor.E).b(0.8F).a(SoundEffectType.g))); + a("moving_piston", (Block) (new BlockPistonMoving(Block.Info.a(Material.PISTON).b(-1.0F).d()))); + BlockFlowers blockflowers = new BlockFlowers(Block.Info.a(Material.PLANT).a().b().a(SoundEffectType.c)); + BlockFlowers blockflowers1 = new BlockFlowers(Block.Info.a(Material.PLANT).a().b().a(SoundEffectType.c)); + BlockFlowers blockflowers2 = new BlockFlowers(Block.Info.a(Material.PLANT).a().b().a(SoundEffectType.c)); + BlockFlowers blockflowers3 = new BlockFlowers(Block.Info.a(Material.PLANT).a().b().a(SoundEffectType.c)); + BlockFlowers blockflowers4 = new BlockFlowers(Block.Info.a(Material.PLANT).a().b().a(SoundEffectType.c)); + BlockFlowers blockflowers5 = new BlockFlowers(Block.Info.a(Material.PLANT).a().b().a(SoundEffectType.c)); + BlockFlowers blockflowers6 = new BlockFlowers(Block.Info.a(Material.PLANT).a().b().a(SoundEffectType.c)); + BlockFlowers blockflowers7 = new BlockFlowers(Block.Info.a(Material.PLANT).a().b().a(SoundEffectType.c)); + BlockFlowers blockflowers8 = new BlockFlowers(Block.Info.a(Material.PLANT).a().b().a(SoundEffectType.c)); + BlockFlowers blockflowers9 = new BlockFlowers(Block.Info.a(Material.PLANT).a().b().a(SoundEffectType.c)); + + a("dandelion", (Block) blockflowers); + a("poppy", (Block) blockflowers1); + a("blue_orchid", (Block) blockflowers2); + a("allium", (Block) blockflowers3); + a("azure_bluet", (Block) blockflowers4); + a("red_tulip", (Block) blockflowers5); + a("orange_tulip", (Block) blockflowers6); + a("white_tulip", (Block) blockflowers7); + a("pink_tulip", (Block) blockflowers8); + a("oxeye_daisy", (Block) blockflowers9); + BlockMushroom blockmushroom = new BlockMushroom(Block.Info.a(Material.PLANT).a().c().b().a(SoundEffectType.c).a(1)); + BlockMushroom blockmushroom1 = new BlockMushroom(Block.Info.a(Material.PLANT).a().c().b().a(SoundEffectType.c)); + + a("brown_mushroom", (Block) blockmushroom); + a("red_mushroom", (Block) blockmushroom1); + a("gold_block", new Block(Block.Info.a(Material.ORE, MaterialMapColor.F).a(3.0F, 6.0F).a(SoundEffectType.e))); + a("iron_block", new Block(Block.Info.a(Material.ORE, MaterialMapColor.h).a(5.0F, 6.0F).a(SoundEffectType.e))); + Block block8 = new Block(Block.Info.a(Material.STONE, MaterialMapColor.D).a(2.0F, 6.0F)); + + a("bricks", block8); + a("tnt", (Block) (new BlockTNT(Block.Info.a(Material.TNT).b().a(SoundEffectType.c)))); + a("bookshelf", (Block) (new BlockBookshelf(Block.Info.a(Material.WOOD).b(1.5F).a(SoundEffectType.a)))); + a("mossy_cobblestone", new Block(Block.Info.a(Material.STONE).a(2.0F, 6.0F))); + a("obsidian", new Block(Block.Info.a(Material.STONE, MaterialMapColor.E).a(50.0F, 1200.0F))); + a("torch", (Block) (new BlockTorch(Block.Info.a(Material.ORIENTABLE).a().b().a(14).a(SoundEffectType.a)))); + a("wall_torch", (Block) (new BlockTorchWall(Block.Info.a(Material.ORIENTABLE).a().b().a(14).a(SoundEffectType.a)))); + a("fire", (Block) (new BlockFire(Block.Info.a(Material.FIRE, MaterialMapColor.f).a().c().b().a(15).a(SoundEffectType.g)))); + a("spawner", (Block) (new BlockMobSpawner(Block.Info.a(Material.STONE).b(5.0F).a(SoundEffectType.e)))); + a("oak_stairs", (Block) (new BlockStairs(block1.getBlockData(), Block.Info.a(block1)))); + a("chest", (Block) (new BlockChest(Block.Info.a(Material.WOOD).b(2.5F).a(SoundEffectType.a)))); + a("redstone_wire", (Block) (new BlockRedstoneWire(Block.Info.a(Material.ORIENTABLE).a().b()))); + a("diamond_ore", (Block) (new BlockOre(Block.Info.a(Material.STONE).a(3.0F, 3.0F)))); + a("diamond_block", new Block(Block.Info.a(Material.ORE, MaterialMapColor.G).a(5.0F, 6.0F).a(SoundEffectType.e))); + a("crafting_table", (Block) (new BlockWorkbench(Block.Info.a(Material.WOOD).b(2.5F).a(SoundEffectType.a)))); + a("wheat", (Block) (new BlockCrops(Block.Info.a(Material.PLANT).a().c().b().a(SoundEffectType.c)))); + BlockSoil blocksoil = new BlockSoil(Block.Info.a(Material.EARTH).c().b(0.6F).a(SoundEffectType.b)); + + a("farmland", (Block) blocksoil); + a("furnace", (Block) (new BlockFurnace(Block.Info.a(Material.STONE).b(3.5F).a(13)))); + a("sign", (Block) (new BlockFloorSign(Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("oak_door", (Block) (new BlockDoor(Block.Info.a(Material.WOOD, block1.l).b(3.0F).a(SoundEffectType.a)))); + a("ladder", (Block) (new BlockLadder(Block.Info.a(Material.ORIENTABLE).b(0.4F).a(SoundEffectType.j)))); + a("rail", (Block) (new BlockMinecartTrack(Block.Info.a(Material.ORIENTABLE).a().b(0.7F).a(SoundEffectType.e)))); + a("cobblestone_stairs", (Block) (new BlockStairs(block.getBlockData(), Block.Info.a(block)))); + a("wall_sign", (Block) (new BlockWallSign(Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("lever", (Block) (new BlockLever(Block.Info.a(Material.ORIENTABLE).a().b(0.5F).a(SoundEffectType.a)))); + a("stone_pressure_plate", (Block) (new BlockPressurePlateBinary(BlockPressurePlateBinary.EnumMobType.MOBS, Block.Info.a(Material.STONE).a().b(0.5F)))); + a("iron_door", (Block) (new BlockDoor(Block.Info.a(Material.ORE, MaterialMapColor.h).b(5.0F).a(SoundEffectType.e)))); + a("oak_pressure_plate", (Block) (new BlockPressurePlateBinary(BlockPressurePlateBinary.EnumMobType.EVERYTHING, Block.Info.a(Material.WOOD, block1.l).a().b(0.5F).a(SoundEffectType.a)))); + a("spruce_pressure_plate", (Block) (new BlockPressurePlateBinary(BlockPressurePlateBinary.EnumMobType.EVERYTHING, Block.Info.a(Material.WOOD, block2.l).a().b(0.5F).a(SoundEffectType.a)))); + a("birch_pressure_plate", (Block) (new BlockPressurePlateBinary(BlockPressurePlateBinary.EnumMobType.EVERYTHING, Block.Info.a(Material.WOOD, block3.l).a().b(0.5F).a(SoundEffectType.a)))); + a("jungle_pressure_plate", (Block) (new BlockPressurePlateBinary(BlockPressurePlateBinary.EnumMobType.EVERYTHING, Block.Info.a(Material.WOOD, block4.l).a().b(0.5F).a(SoundEffectType.a)))); + a("acacia_pressure_plate", (Block) (new BlockPressurePlateBinary(BlockPressurePlateBinary.EnumMobType.EVERYTHING, Block.Info.a(Material.WOOD, block5.l).a().b(0.5F).a(SoundEffectType.a)))); + a("dark_oak_pressure_plate", (Block) (new BlockPressurePlateBinary(BlockPressurePlateBinary.EnumMobType.EVERYTHING, Block.Info.a(Material.WOOD, block6.l).a().b(0.5F).a(SoundEffectType.a)))); + a("redstone_ore", (Block) (new BlockRedstoneOre(Block.Info.a(Material.STONE).c().a(9).a(3.0F, 3.0F)))); + a("redstone_torch", (Block) (new BlockRedstoneTorch(Block.Info.a(Material.ORIENTABLE).a().b().a(7).a(SoundEffectType.a)))); + a("redstone_wall_torch", (Block) (new BlockRedstoneTorchWall(Block.Info.a(Material.ORIENTABLE).a().b().a(7).a(SoundEffectType.a)))); + a("stone_button", (Block) (new BlockStoneButton(Block.Info.a(Material.ORIENTABLE).a().b(0.5F)))); + a("snow", (Block) (new BlockSnow(Block.Info.a(Material.PACKED_ICE).c().b(0.1F).a(SoundEffectType.i)))); + a("ice", (Block) (new BlockIce(Block.Info.a(Material.ICE).a(0.98F).c().b(0.5F).a(SoundEffectType.f)))); + a("snow_block", (Block) (new BlockSnowBlock(Block.Info.a(Material.SNOW_BLOCK).c().b(0.2F).a(SoundEffectType.i)))); + BlockCactus blockcactus = new BlockCactus(Block.Info.a(Material.CACTUS).c().b(0.4F).a(SoundEffectType.g)); + + a("cactus", (Block) blockcactus); + a("clay", (Block) (new BlockClay(Block.Info.a(Material.CLAY).b(0.6F).a(SoundEffectType.b)))); + a("sugar_cane", (Block) (new BlockReed(Block.Info.a(Material.PLANT).a().c().b().a(SoundEffectType.c)))); + a("jukebox", (Block) (new BlockJukeBox(Block.Info.a(Material.WOOD, MaterialMapColor.l).a(2.0F, 6.0F)))); + a("oak_fence", (Block) (new BlockFence(Block.Info.a(Material.WOOD, block1.l).a(2.0F, 3.0F).a(SoundEffectType.a)))); + BlockPumpkin blockpumpkin = new BlockPumpkin(Block.Info.a(Material.PUMPKIN, MaterialMapColor.q).b(1.0F).a(SoundEffectType.a)); + + a("pumpkin", (Block) blockpumpkin); + a("netherrack", new Block(Block.Info.a(Material.STONE, MaterialMapColor.K).b(0.4F))); + a("soul_sand", (Block) (new BlockSlowSand(Block.Info.a(Material.SAND, MaterialMapColor.B).c().b(0.5F).a(SoundEffectType.h)))); + a("glowstone", (Block) (new BlockLightStone(Block.Info.a(Material.SHATTERABLE, MaterialMapColor.d).b(0.3F).a(SoundEffectType.f).a(15)))); + a("nether_portal", (Block) (new BlockPortal(Block.Info.a(Material.PORTAL).a().c().b(-1.0F).a(SoundEffectType.f).a(11)))); + a("carved_pumpkin", (Block) (new BlockPumpkinCarved(Block.Info.a(Material.PUMPKIN, MaterialMapColor.q).b(1.0F).a(SoundEffectType.a)))); + a("jack_o_lantern", (Block) (new BlockPumpkinCarved(Block.Info.a(Material.PUMPKIN, MaterialMapColor.q).b(1.0F).a(SoundEffectType.a).a(15)))); + a("cake", (Block) (new BlockCake(Block.Info.a(Material.CAKE).b(0.5F).a(SoundEffectType.g)))); + a("repeater", (Block) (new BlockRepeater(Block.Info.a(Material.ORIENTABLE).b().a(SoundEffectType.a)))); + a("white_stained_glass", (Block) (new BlockStainedGlass(EnumColor.WHITE, Block.Info.a(Material.SHATTERABLE, EnumColor.WHITE).b(0.3F).a(SoundEffectType.f)))); + a("orange_stained_glass", (Block) (new BlockStainedGlass(EnumColor.ORANGE, Block.Info.a(Material.SHATTERABLE, EnumColor.ORANGE).b(0.3F).a(SoundEffectType.f)))); + a("magenta_stained_glass", (Block) (new BlockStainedGlass(EnumColor.MAGENTA, Block.Info.a(Material.SHATTERABLE, EnumColor.MAGENTA).b(0.3F).a(SoundEffectType.f)))); + a("light_blue_stained_glass", (Block) (new BlockStainedGlass(EnumColor.LIGHT_BLUE, Block.Info.a(Material.SHATTERABLE, EnumColor.LIGHT_BLUE).b(0.3F).a(SoundEffectType.f)))); + a("yellow_stained_glass", (Block) (new BlockStainedGlass(EnumColor.YELLOW, Block.Info.a(Material.SHATTERABLE, EnumColor.YELLOW).b(0.3F).a(SoundEffectType.f)))); + a("lime_stained_glass", (Block) (new BlockStainedGlass(EnumColor.LIME, Block.Info.a(Material.SHATTERABLE, EnumColor.LIME).b(0.3F).a(SoundEffectType.f)))); + a("pink_stained_glass", (Block) (new BlockStainedGlass(EnumColor.PINK, Block.Info.a(Material.SHATTERABLE, EnumColor.PINK).b(0.3F).a(SoundEffectType.f)))); + a("gray_stained_glass", (Block) (new BlockStainedGlass(EnumColor.GRAY, Block.Info.a(Material.SHATTERABLE, EnumColor.GRAY).b(0.3F).a(SoundEffectType.f)))); + a("light_gray_stained_glass", (Block) (new BlockStainedGlass(EnumColor.LIGHT_GRAY, Block.Info.a(Material.SHATTERABLE, EnumColor.LIGHT_GRAY).b(0.3F).a(SoundEffectType.f)))); + a("cyan_stained_glass", (Block) (new BlockStainedGlass(EnumColor.CYAN, Block.Info.a(Material.SHATTERABLE, EnumColor.CYAN).b(0.3F).a(SoundEffectType.f)))); + a("purple_stained_glass", (Block) (new BlockStainedGlass(EnumColor.PURPLE, Block.Info.a(Material.SHATTERABLE, EnumColor.PURPLE).b(0.3F).a(SoundEffectType.f)))); + a("blue_stained_glass", (Block) (new BlockStainedGlass(EnumColor.BLUE, Block.Info.a(Material.SHATTERABLE, EnumColor.BLUE).b(0.3F).a(SoundEffectType.f)))); + a("brown_stained_glass", (Block) (new BlockStainedGlass(EnumColor.BROWN, Block.Info.a(Material.SHATTERABLE, EnumColor.BROWN).b(0.3F).a(SoundEffectType.f)))); + a("green_stained_glass", (Block) (new BlockStainedGlass(EnumColor.GREEN, Block.Info.a(Material.SHATTERABLE, EnumColor.GREEN).b(0.3F).a(SoundEffectType.f)))); + a("red_stained_glass", (Block) (new BlockStainedGlass(EnumColor.RED, Block.Info.a(Material.SHATTERABLE, EnumColor.RED).b(0.3F).a(SoundEffectType.f)))); + a("black_stained_glass", (Block) (new BlockStainedGlass(EnumColor.BLACK, Block.Info.a(Material.SHATTERABLE, EnumColor.BLACK).b(0.3F).a(SoundEffectType.f)))); + a("oak_trapdoor", (Block) (new BlockTrapdoor(Block.Info.a(Material.WOOD, MaterialMapColor.o).b(3.0F).a(SoundEffectType.a)))); + a("spruce_trapdoor", (Block) (new BlockTrapdoor(Block.Info.a(Material.WOOD, MaterialMapColor.J).b(3.0F).a(SoundEffectType.a)))); + a("birch_trapdoor", (Block) (new BlockTrapdoor(Block.Info.a(Material.WOOD, MaterialMapColor.d).b(3.0F).a(SoundEffectType.a)))); + a("jungle_trapdoor", (Block) (new BlockTrapdoor(Block.Info.a(Material.WOOD, MaterialMapColor.l).b(3.0F).a(SoundEffectType.a)))); + a("acacia_trapdoor", (Block) (new BlockTrapdoor(Block.Info.a(Material.WOOD, MaterialMapColor.q).b(3.0F).a(SoundEffectType.a)))); + a("dark_oak_trapdoor", (Block) (new BlockTrapdoor(Block.Info.a(Material.WOOD, MaterialMapColor.B).b(3.0F).a(SoundEffectType.a)))); + Block block9 = new Block(Block.Info.a(Material.STONE).a(1.5F, 6.0F)); + Block block10 = new Block(Block.Info.a(Material.STONE).a(1.5F, 6.0F)); + Block block11 = new Block(Block.Info.a(Material.STONE).a(1.5F, 6.0F)); + Block block12 = new Block(Block.Info.a(Material.STONE).a(1.5F, 6.0F)); + + a("infested_stone", (Block) (new BlockMonsterEggs(blockstone, Block.Info.a(Material.CLAY).a(0.0F, 0.75F)))); + a("infested_cobblestone", (Block) (new BlockMonsterEggs(block, Block.Info.a(Material.CLAY).a(0.0F, 0.75F)))); + a("infested_stone_bricks", (Block) (new BlockMonsterEggs(block9, Block.Info.a(Material.CLAY).a(0.0F, 0.75F)))); + a("infested_mossy_stone_bricks", (Block) (new BlockMonsterEggs(block10, Block.Info.a(Material.CLAY).a(0.0F, 0.75F)))); + a("infested_cracked_stone_bricks", (Block) (new BlockMonsterEggs(block11, Block.Info.a(Material.CLAY).a(0.0F, 0.75F)))); + a("infested_chiseled_stone_bricks", (Block) (new BlockMonsterEggs(block12, Block.Info.a(Material.CLAY).a(0.0F, 0.75F)))); + a("stone_bricks", block9); + a("mossy_stone_bricks", block10); + a("cracked_stone_bricks", block11); + a("chiseled_stone_bricks", block12); + BlockHugeMushroom blockhugemushroom = new BlockHugeMushroom(blockmushroom, Block.Info.a(Material.WOOD, MaterialMapColor.l).b(0.2F).a(SoundEffectType.a)); + + a("brown_mushroom_block", (Block) blockhugemushroom); + BlockHugeMushroom blockhugemushroom1 = new BlockHugeMushroom(blockmushroom1, Block.Info.a(Material.WOOD, MaterialMapColor.D).b(0.2F).a(SoundEffectType.a)); + + a("red_mushroom_block", (Block) blockhugemushroom1); + a("mushroom_stem", (Block) (new BlockHugeMushroom((Block) null, Block.Info.a(Material.WOOD, MaterialMapColor.L).b(0.2F).a(SoundEffectType.a)))); + a("iron_bars", (Block) (new BlockIronBars(Block.Info.a(Material.ORE, MaterialMapColor.b).a(5.0F, 6.0F).a(SoundEffectType.e)))); + a("glass_pane", (Block) (new BlockGlassPane(Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + BlockMelon blockmelon = new BlockMelon(Block.Info.a(Material.PUMPKIN, MaterialMapColor.u).b(1.0F).a(SoundEffectType.a)); + + a("melon", (Block) blockmelon); + a("attached_pumpkin_stem", (Block) (new BlockStemAttached(blockpumpkin, Block.Info.a(Material.PLANT).a().b().a(SoundEffectType.a)))); + a("attached_melon_stem", (Block) (new BlockStemAttached(blockmelon, Block.Info.a(Material.PLANT).a().b().a(SoundEffectType.a)))); + a("pumpkin_stem", (Block) (new BlockStem(blockpumpkin, Block.Info.a(Material.PLANT).a().c().b().a(SoundEffectType.a)))); + a("melon_stem", (Block) (new BlockStem(blockmelon, Block.Info.a(Material.PLANT).a().c().b().a(SoundEffectType.a)))); + a("vine", (Block) (new BlockVine(Block.Info.a(Material.REPLACEABLE_PLANT).a().c().b(0.2F).a(SoundEffectType.c)))); + a("oak_fence_gate", (Block) (new BlockFenceGate(Block.Info.a(Material.WOOD, block1.l).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("brick_stairs", (Block) (new BlockStairs(block8.getBlockData(), Block.Info.a(block8)))); + a("stone_brick_stairs", (Block) (new BlockStairs(block9.getBlockData(), Block.Info.a(block9)))); + a("mycelium", (Block) (new BlockMycel(Block.Info.a(Material.GRASS, MaterialMapColor.z).c().b(0.6F).a(SoundEffectType.c)))); + a("lily_pad", (Block) (new BlockWaterLily(Block.Info.a(Material.PLANT).b().a(SoundEffectType.c)))); + Block block13 = new Block(Block.Info.a(Material.STONE, MaterialMapColor.K).a(2.0F, 6.0F)); + + a("nether_bricks", block13); + a("nether_brick_fence", (Block) (new BlockFence(Block.Info.a(Material.STONE, MaterialMapColor.K).a(2.0F, 6.0F)))); + a("nether_brick_stairs", (Block) (new BlockStairs(block13.getBlockData(), Block.Info.a(block13)))); + a("nether_wart", (Block) (new BlockNetherWart(Block.Info.a(Material.PLANT, MaterialMapColor.D).a().c()))); + a("enchanting_table", (Block) (new BlockEnchantmentTable(Block.Info.a(Material.STONE, MaterialMapColor.D).a(5.0F, 1200.0F)))); + a("brewing_stand", (Block) (new BlockBrewingStand(Block.Info.a(Material.ORE).b(0.5F).a(1)))); + a("cauldron", (Block) (new BlockCauldron(Block.Info.a(Material.ORE, MaterialMapColor.m).b(2.0F)))); + a("end_portal", (Block) (new BlockEnderPortal(Block.Info.a(Material.PORTAL, MaterialMapColor.E).a().a(15).a(-1.0F, 3600000.0F)))); + a("end_portal_frame", (Block) (new BlockEnderPortalFrame(Block.Info.a(Material.STONE, MaterialMapColor.C).a(SoundEffectType.f).a(1).a(-1.0F, 3600000.0F)))); + a("end_stone", new Block(Block.Info.a(Material.STONE, MaterialMapColor.d).a(3.0F, 9.0F))); + a("dragon_egg", (Block) (new BlockDragonEgg(Block.Info.a(Material.DRAGON_EGG, MaterialMapColor.E).a(3.0F, 9.0F).a(1)))); + a("redstone_lamp", (Block) (new BlockRedstoneLamp(Block.Info.a(Material.BUILDABLE_GLASS).a(15).b(0.3F).a(SoundEffectType.f)))); + a("cocoa", (Block) (new BlockCocoa(Block.Info.a(Material.PLANT).c().a(0.2F, 3.0F).a(SoundEffectType.a)))); + a("sandstone_stairs", (Block) (new BlockStairs(block7.getBlockData(), Block.Info.a(block7)))); + a("emerald_ore", (Block) (new BlockOre(Block.Info.a(Material.STONE).a(3.0F, 3.0F)))); + a("ender_chest", (Block) (new BlockEnderChest(Block.Info.a(Material.STONE).a(22.5F, 600.0F).a(7)))); + BlockTripwireHook blocktripwirehook = new BlockTripwireHook(Block.Info.a(Material.ORIENTABLE).a()); + + a("tripwire_hook", (Block) blocktripwirehook); + a("tripwire", (Block) (new BlockTripwire(blocktripwirehook, Block.Info.a(Material.ORIENTABLE).a()))); + a("emerald_block", new Block(Block.Info.a(Material.ORE, MaterialMapColor.I).a(5.0F, 6.0F).a(SoundEffectType.e))); + a("spruce_stairs", (Block) (new BlockStairs(block2.getBlockData(), Block.Info.a(block2)))); + a("birch_stairs", (Block) (new BlockStairs(block3.getBlockData(), Block.Info.a(block3)))); + a("jungle_stairs", (Block) (new BlockStairs(block4.getBlockData(), Block.Info.a(block4)))); + a("command_block", (Block) (new BlockCommand(Block.Info.a(Material.ORE, MaterialMapColor.B).a(-1.0F, 3600000.0F)))); + a("beacon", (Block) (new BlockBeacon(Block.Info.a(Material.SHATTERABLE, MaterialMapColor.G).b(3.0F).a(15)))); + a("cobblestone_wall", (Block) (new BlockCobbleWall(Block.Info.a(block)))); + a("mossy_cobblestone_wall", (Block) (new BlockCobbleWall(Block.Info.a(block)))); + a("flower_pot", (Block) (new BlockFlowerPot(blockair, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_oak_sapling", (Block) (new BlockFlowerPot(blocksapling, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_spruce_sapling", (Block) (new BlockFlowerPot(blocksapling1, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_birch_sapling", (Block) (new BlockFlowerPot(blocksapling2, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_jungle_sapling", (Block) (new BlockFlowerPot(blocksapling3, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_acacia_sapling", (Block) (new BlockFlowerPot(blocksapling4, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_dark_oak_sapling", (Block) (new BlockFlowerPot(blocksapling5, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_fern", (Block) (new BlockFlowerPot(blocklonggrass1, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_dandelion", (Block) (new BlockFlowerPot(blockflowers, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_poppy", (Block) (new BlockFlowerPot(blockflowers1, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_blue_orchid", (Block) (new BlockFlowerPot(blockflowers2, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_allium", (Block) (new BlockFlowerPot(blockflowers3, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_azure_bluet", (Block) (new BlockFlowerPot(blockflowers4, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_red_tulip", (Block) (new BlockFlowerPot(blockflowers5, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_orange_tulip", (Block) (new BlockFlowerPot(blockflowers6, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_white_tulip", (Block) (new BlockFlowerPot(blockflowers7, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_pink_tulip", (Block) (new BlockFlowerPot(blockflowers8, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_oxeye_daisy", (Block) (new BlockFlowerPot(blockflowers9, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_red_mushroom", (Block) (new BlockFlowerPot(blockmushroom1, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_brown_mushroom", (Block) (new BlockFlowerPot(blockmushroom, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_dead_bush", (Block) (new BlockFlowerPot(blockdeadbush, Block.Info.a(Material.ORIENTABLE).b()))); + a("potted_cactus", (Block) (new BlockFlowerPot(blockcactus, Block.Info.a(Material.ORIENTABLE).b()))); + a("carrots", (Block) (new BlockCarrots(Block.Info.a(Material.PLANT).a().c().b().a(SoundEffectType.c)))); + a("potatoes", (Block) (new BlockPotatoes(Block.Info.a(Material.PLANT).a().c().b().a(SoundEffectType.c)))); + a("oak_button", (Block) (new BlockWoodButton(Block.Info.a(Material.ORIENTABLE).a().b(0.5F).a(SoundEffectType.a)))); + a("spruce_button", (Block) (new BlockWoodButton(Block.Info.a(Material.ORIENTABLE).a().b(0.5F).a(SoundEffectType.a)))); + a("birch_button", (Block) (new BlockWoodButton(Block.Info.a(Material.ORIENTABLE).a().b(0.5F).a(SoundEffectType.a)))); + a("jungle_button", (Block) (new BlockWoodButton(Block.Info.a(Material.ORIENTABLE).a().b(0.5F).a(SoundEffectType.a)))); + a("acacia_button", (Block) (new BlockWoodButton(Block.Info.a(Material.ORIENTABLE).a().b(0.5F).a(SoundEffectType.a)))); + a("dark_oak_button", (Block) (new BlockWoodButton(Block.Info.a(Material.ORIENTABLE).a().b(0.5F).a(SoundEffectType.a)))); + a("skeleton_wall_skull", (Block) (new BlockSkullWall(BlockSkull.Type.SKELETON, Block.Info.a(Material.ORIENTABLE).b(1.0F)))); + a("skeleton_skull", (Block) (new BlockSkull(BlockSkull.Type.SKELETON, Block.Info.a(Material.ORIENTABLE).b(1.0F)))); + a("wither_skeleton_wall_skull", (Block) (new BlockWitherSkullWall(Block.Info.a(Material.ORIENTABLE).b(1.0F)))); + a("wither_skeleton_skull", (Block) (new BlockWitherSkull(Block.Info.a(Material.ORIENTABLE).b(1.0F)))); + a("zombie_wall_head", (Block) (new BlockSkullWall(BlockSkull.Type.ZOMBIE, Block.Info.a(Material.ORIENTABLE).b(1.0F)))); + a("zombie_head", (Block) (new BlockSkull(BlockSkull.Type.ZOMBIE, Block.Info.a(Material.ORIENTABLE).b(1.0F)))); + a("player_wall_head", (Block) (new BlockSkullPlayerWall(Block.Info.a(Material.ORIENTABLE).b(1.0F)))); + a("player_head", (Block) (new BlockSkullPlayer(Block.Info.a(Material.ORIENTABLE).b(1.0F)))); + a("creeper_wall_head", (Block) (new BlockSkullWall(BlockSkull.Type.CREEPER, Block.Info.a(Material.ORIENTABLE).b(1.0F)))); + a("creeper_head", (Block) (new BlockSkull(BlockSkull.Type.CREEPER, Block.Info.a(Material.ORIENTABLE).b(1.0F)))); + a("dragon_wall_head", (Block) (new BlockSkullWall(BlockSkull.Type.DRAGON, Block.Info.a(Material.ORIENTABLE).b(1.0F)))); + a("dragon_head", (Block) (new BlockSkull(BlockSkull.Type.DRAGON, Block.Info.a(Material.ORIENTABLE).b(1.0F)))); + a("anvil", (Block) (new BlockAnvil(Block.Info.a(Material.HEAVY, MaterialMapColor.h).a(5.0F, 1200.0F).a(SoundEffectType.k)))); + a("chipped_anvil", (Block) (new BlockAnvil(Block.Info.a(Material.HEAVY, MaterialMapColor.h).a(5.0F, 1200.0F).a(SoundEffectType.k)))); + a("damaged_anvil", (Block) (new BlockAnvil(Block.Info.a(Material.HEAVY, MaterialMapColor.h).a(5.0F, 1200.0F).a(SoundEffectType.k)))); + a("trapped_chest", (Block) (new BlockChestTrapped(Block.Info.a(Material.WOOD).b(2.5F).a(SoundEffectType.a)))); + a("light_weighted_pressure_plate", (Block) (new BlockPressurePlateWeighted(15, Block.Info.a(Material.ORE, MaterialMapColor.F).a().b(0.5F).a(SoundEffectType.a)))); + a("heavy_weighted_pressure_plate", (Block) (new BlockPressurePlateWeighted(150, Block.Info.a(Material.ORE).a().b(0.5F).a(SoundEffectType.a)))); + a("comparator", (Block) (new BlockRedstoneComparator(Block.Info.a(Material.ORIENTABLE).b().a(SoundEffectType.a)))); + a("daylight_detector", (Block) (new BlockDaylightDetector(Block.Info.a(Material.WOOD).b(0.2F).a(SoundEffectType.a)))); + a("redstone_block", (Block) (new BlockPowered(Block.Info.a(Material.ORE, MaterialMapColor.f).a(5.0F, 6.0F).a(SoundEffectType.e)))); + a("nether_quartz_ore", (Block) (new BlockOre(Block.Info.a(Material.STONE, MaterialMapColor.K).a(3.0F, 3.0F)))); + a("hopper", (Block) (new BlockHopper(Block.Info.a(Material.ORE, MaterialMapColor.m).a(3.0F, 4.8F).a(SoundEffectType.e)))); + Block block14 = new Block(Block.Info.a(Material.STONE, MaterialMapColor.p).b(0.8F)); + + a("quartz_block", block14); + a("chiseled_quartz_block", new Block(Block.Info.a(Material.STONE, MaterialMapColor.p).b(0.8F))); + a("quartz_pillar", (Block) (new BlockRotatable(Block.Info.a(Material.STONE, MaterialMapColor.p).b(0.8F)))); + a("quartz_stairs", (Block) (new BlockStairs(block14.getBlockData(), Block.Info.a(block14)))); + a("activator_rail", (Block) (new BlockPoweredRail(Block.Info.a(Material.ORIENTABLE).a().b(0.7F).a(SoundEffectType.e)))); + a("dropper", (Block) (new BlockDropper(Block.Info.a(Material.STONE).b(3.5F)))); + a("white_terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.L).a(1.25F, 4.2F))); + a("orange_terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.M).a(1.25F, 4.2F))); + a("magenta_terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.N).a(1.25F, 4.2F))); + a("light_blue_terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.O).a(1.25F, 4.2F))); + a("yellow_terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.P).a(1.25F, 4.2F))); + a("lime_terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.Q).a(1.25F, 4.2F))); + a("pink_terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.R).a(1.25F, 4.2F))); + a("gray_terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.S).a(1.25F, 4.2F))); + a("light_gray_terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.T).a(1.25F, 4.2F))); + a("cyan_terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.U).a(1.25F, 4.2F))); + a("purple_terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.V).a(1.25F, 4.2F))); + a("blue_terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.W).a(1.25F, 4.2F))); + a("brown_terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.X).a(1.25F, 4.2F))); + a("green_terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.Y).a(1.25F, 4.2F))); + a("red_terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.Z).a(1.25F, 4.2F))); + a("black_terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.aa).a(1.25F, 4.2F))); + a("white_stained_glass_pane", (Block) (new BlockStainedGlassPane(EnumColor.WHITE, Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("orange_stained_glass_pane", (Block) (new BlockStainedGlassPane(EnumColor.ORANGE, Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("magenta_stained_glass_pane", (Block) (new BlockStainedGlassPane(EnumColor.MAGENTA, Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("light_blue_stained_glass_pane", (Block) (new BlockStainedGlassPane(EnumColor.LIGHT_BLUE, Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("yellow_stained_glass_pane", (Block) (new BlockStainedGlassPane(EnumColor.YELLOW, Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("lime_stained_glass_pane", (Block) (new BlockStainedGlassPane(EnumColor.LIME, Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("pink_stained_glass_pane", (Block) (new BlockStainedGlassPane(EnumColor.PINK, Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("gray_stained_glass_pane", (Block) (new BlockStainedGlassPane(EnumColor.GRAY, Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("light_gray_stained_glass_pane", (Block) (new BlockStainedGlassPane(EnumColor.LIGHT_GRAY, Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("cyan_stained_glass_pane", (Block) (new BlockStainedGlassPane(EnumColor.CYAN, Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("purple_stained_glass_pane", (Block) (new BlockStainedGlassPane(EnumColor.PURPLE, Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("blue_stained_glass_pane", (Block) (new BlockStainedGlassPane(EnumColor.BLUE, Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("brown_stained_glass_pane", (Block) (new BlockStainedGlassPane(EnumColor.BROWN, Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("green_stained_glass_pane", (Block) (new BlockStainedGlassPane(EnumColor.GREEN, Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("red_stained_glass_pane", (Block) (new BlockStainedGlassPane(EnumColor.RED, Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("black_stained_glass_pane", (Block) (new BlockStainedGlassPane(EnumColor.BLACK, Block.Info.a(Material.SHATTERABLE).b(0.3F).a(SoundEffectType.f)))); + a("acacia_stairs", (Block) (new BlockStairs(block5.getBlockData(), Block.Info.a(block5)))); + a("dark_oak_stairs", (Block) (new BlockStairs(block6.getBlockData(), Block.Info.a(block6)))); + a("slime_block", (Block) (new BlockSlime(Block.Info.a(Material.CLAY, MaterialMapColor.c).a(0.8F).a(SoundEffectType.l)))); + a("barrier", (Block) (new BlockBarrier(Block.Info.a(Material.BANNER).a(-1.0F, 3600000.8F)))); + a("iron_trapdoor", (Block) (new BlockTrapdoor(Block.Info.a(Material.ORE).b(5.0F).a(SoundEffectType.e)))); + Block block15 = new Block(Block.Info.a(Material.STONE, MaterialMapColor.y).a(1.5F, 6.0F)); + + a("prismarine", block15); + Block block16 = new Block(Block.Info.a(Material.STONE, MaterialMapColor.G).a(1.5F, 6.0F)); + + a("prismarine_bricks", block16); + Block block17 = new Block(Block.Info.a(Material.STONE, MaterialMapColor.G).a(1.5F, 6.0F)); + + a("dark_prismarine", block17); + a("prismarine_stairs", (Block) (new BlockStairs(block15.getBlockData(), Block.Info.a(block15)))); + a("prismarine_brick_stairs", (Block) (new BlockStairs(block16.getBlockData(), Block.Info.a(block16)))); + a("dark_prismarine_stairs", (Block) (new BlockStairs(block17.getBlockData(), Block.Info.a(block17)))); + a("prismarine_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.STONE, MaterialMapColor.y).a(1.5F, 6.0F)))); + a("prismarine_brick_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.STONE, MaterialMapColor.G).a(1.5F, 6.0F)))); + a("dark_prismarine_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.STONE, MaterialMapColor.G).a(1.5F, 6.0F)))); + a("sea_lantern", (Block) (new BlockSeaLantern(Block.Info.a(Material.SHATTERABLE, MaterialMapColor.p).b(0.3F).a(SoundEffectType.f).a(15)))); + a("hay_block", (Block) (new BlockHay(Block.Info.a(Material.GRASS, MaterialMapColor.t).b(0.5F).a(SoundEffectType.c)))); + a("white_carpet", (Block) (new BlockCarpet(EnumColor.WHITE, Block.Info.a(Material.WOOL, MaterialMapColor.j).b(0.1F).a(SoundEffectType.g)))); + a("orange_carpet", (Block) (new BlockCarpet(EnumColor.ORANGE, Block.Info.a(Material.WOOL, MaterialMapColor.q).b(0.1F).a(SoundEffectType.g)))); + a("magenta_carpet", (Block) (new BlockCarpet(EnumColor.MAGENTA, Block.Info.a(Material.WOOL, MaterialMapColor.r).b(0.1F).a(SoundEffectType.g)))); + a("light_blue_carpet", (Block) (new BlockCarpet(EnumColor.LIGHT_BLUE, Block.Info.a(Material.WOOL, MaterialMapColor.s).b(0.1F).a(SoundEffectType.g)))); + a("yellow_carpet", (Block) (new BlockCarpet(EnumColor.YELLOW, Block.Info.a(Material.WOOL, MaterialMapColor.t).b(0.1F).a(SoundEffectType.g)))); + a("lime_carpet", (Block) (new BlockCarpet(EnumColor.LIME, Block.Info.a(Material.WOOL, MaterialMapColor.u).b(0.1F).a(SoundEffectType.g)))); + a("pink_carpet", (Block) (new BlockCarpet(EnumColor.PINK, Block.Info.a(Material.WOOL, MaterialMapColor.v).b(0.1F).a(SoundEffectType.g)))); + a("gray_carpet", (Block) (new BlockCarpet(EnumColor.GRAY, Block.Info.a(Material.WOOL, MaterialMapColor.w).b(0.1F).a(SoundEffectType.g)))); + a("light_gray_carpet", (Block) (new BlockCarpet(EnumColor.LIGHT_GRAY, Block.Info.a(Material.WOOL, MaterialMapColor.x).b(0.1F).a(SoundEffectType.g)))); + a("cyan_carpet", (Block) (new BlockCarpet(EnumColor.CYAN, Block.Info.a(Material.WOOL, MaterialMapColor.y).b(0.1F).a(SoundEffectType.g)))); + a("purple_carpet", (Block) (new BlockCarpet(EnumColor.PURPLE, Block.Info.a(Material.WOOL, MaterialMapColor.z).b(0.1F).a(SoundEffectType.g)))); + a("blue_carpet", (Block) (new BlockCarpet(EnumColor.BLUE, Block.Info.a(Material.WOOL, MaterialMapColor.A).b(0.1F).a(SoundEffectType.g)))); + a("brown_carpet", (Block) (new BlockCarpet(EnumColor.BROWN, Block.Info.a(Material.WOOL, MaterialMapColor.B).b(0.1F).a(SoundEffectType.g)))); + a("green_carpet", (Block) (new BlockCarpet(EnumColor.GREEN, Block.Info.a(Material.WOOL, MaterialMapColor.C).b(0.1F).a(SoundEffectType.g)))); + a("red_carpet", (Block) (new BlockCarpet(EnumColor.RED, Block.Info.a(Material.WOOL, MaterialMapColor.D).b(0.1F).a(SoundEffectType.g)))); + a("black_carpet", (Block) (new BlockCarpet(EnumColor.BLACK, Block.Info.a(Material.WOOL, MaterialMapColor.E).b(0.1F).a(SoundEffectType.g)))); + a("terracotta", new Block(Block.Info.a(Material.STONE, MaterialMapColor.q).a(1.25F, 4.2F))); + a("coal_block", new Block(Block.Info.a(Material.STONE, MaterialMapColor.E).a(5.0F, 6.0F))); + a("packed_ice", (Block) (new BlockPackedIce(Block.Info.a(Material.SNOW_LAYER).a(0.98F).b(0.5F).a(SoundEffectType.f)))); + a("sunflower", (Block) (new BlockTallPlantFlower(Block.Info.a(Material.REPLACEABLE_PLANT).a().b().a(SoundEffectType.c)))); + a("lilac", (Block) (new BlockTallPlantFlower(Block.Info.a(Material.REPLACEABLE_PLANT).a().b().a(SoundEffectType.c)))); + a("rose_bush", (Block) (new BlockTallPlantFlower(Block.Info.a(Material.REPLACEABLE_PLANT).a().b().a(SoundEffectType.c)))); + a("peony", (Block) (new BlockTallPlantFlower(Block.Info.a(Material.REPLACEABLE_PLANT).a().b().a(SoundEffectType.c)))); + a("tall_grass", (Block) (new BlockTallPlantShearable(blocklonggrass, Block.Info.a(Material.REPLACEABLE_PLANT).a().b().a(SoundEffectType.c)))); + a("large_fern", (Block) (new BlockTallPlantShearable(blocklonggrass1, Block.Info.a(Material.REPLACEABLE_PLANT).a().b().a(SoundEffectType.c)))); + a("white_banner", (Block) (new BlockBanner(EnumColor.WHITE, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("orange_banner", (Block) (new BlockBanner(EnumColor.ORANGE, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("magenta_banner", (Block) (new BlockBanner(EnumColor.MAGENTA, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("light_blue_banner", (Block) (new BlockBanner(EnumColor.LIGHT_BLUE, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("yellow_banner", (Block) (new BlockBanner(EnumColor.YELLOW, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("lime_banner", (Block) (new BlockBanner(EnumColor.LIME, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("pink_banner", (Block) (new BlockBanner(EnumColor.PINK, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("gray_banner", (Block) (new BlockBanner(EnumColor.GRAY, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("light_gray_banner", (Block) (new BlockBanner(EnumColor.LIGHT_GRAY, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("cyan_banner", (Block) (new BlockBanner(EnumColor.CYAN, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("purple_banner", (Block) (new BlockBanner(EnumColor.PURPLE, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("blue_banner", (Block) (new BlockBanner(EnumColor.BLUE, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("brown_banner", (Block) (new BlockBanner(EnumColor.BROWN, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("green_banner", (Block) (new BlockBanner(EnumColor.GREEN, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("red_banner", (Block) (new BlockBanner(EnumColor.RED, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("black_banner", (Block) (new BlockBanner(EnumColor.BLACK, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("white_wall_banner", (Block) (new BlockBannerWall(EnumColor.WHITE, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("orange_wall_banner", (Block) (new BlockBannerWall(EnumColor.ORANGE, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("magenta_wall_banner", (Block) (new BlockBannerWall(EnumColor.MAGENTA, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("light_blue_wall_banner", (Block) (new BlockBannerWall(EnumColor.LIGHT_BLUE, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("yellow_wall_banner", (Block) (new BlockBannerWall(EnumColor.YELLOW, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("lime_wall_banner", (Block) (new BlockBannerWall(EnumColor.LIME, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("pink_wall_banner", (Block) (new BlockBannerWall(EnumColor.PINK, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("gray_wall_banner", (Block) (new BlockBannerWall(EnumColor.GRAY, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("light_gray_wall_banner", (Block) (new BlockBannerWall(EnumColor.LIGHT_GRAY, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("cyan_wall_banner", (Block) (new BlockBannerWall(EnumColor.CYAN, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("purple_wall_banner", (Block) (new BlockBannerWall(EnumColor.PURPLE, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("blue_wall_banner", (Block) (new BlockBannerWall(EnumColor.BLUE, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("brown_wall_banner", (Block) (new BlockBannerWall(EnumColor.BROWN, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("green_wall_banner", (Block) (new BlockBannerWall(EnumColor.GREEN, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("red_wall_banner", (Block) (new BlockBannerWall(EnumColor.RED, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + a("black_wall_banner", (Block) (new BlockBannerWall(EnumColor.BLACK, Block.Info.a(Material.WOOD).a().b(1.0F).a(SoundEffectType.a)))); + Block block18 = new Block(Block.Info.a(Material.STONE, MaterialMapColor.q).b(0.8F)); + + a("red_sandstone", block18); + a("chiseled_red_sandstone", new Block(Block.Info.a(Material.STONE, MaterialMapColor.q).b(0.8F))); + a("cut_red_sandstone", new Block(Block.Info.a(Material.STONE, MaterialMapColor.q).b(0.8F))); + a("red_sandstone_stairs", (Block) (new BlockStairs(block18.getBlockData(), Block.Info.a(block18)))); + a("oak_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.WOOD, MaterialMapColor.o).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("spruce_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.WOOD, MaterialMapColor.J).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("birch_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.WOOD, MaterialMapColor.d).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("jungle_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.WOOD, MaterialMapColor.l).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("acacia_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.WOOD, MaterialMapColor.q).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("dark_oak_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.WOOD, MaterialMapColor.B).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("stone_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.STONE, MaterialMapColor.m).a(2.0F, 6.0F)))); + a("sandstone_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.STONE, MaterialMapColor.d).a(2.0F, 6.0F)))); + a("petrified_oak_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.STONE, MaterialMapColor.o).a(2.0F, 6.0F)))); + a("cobblestone_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.STONE, MaterialMapColor.m).a(2.0F, 6.0F)))); + a("brick_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.STONE, MaterialMapColor.D).a(2.0F, 6.0F)))); + a("stone_brick_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.STONE, MaterialMapColor.m).a(2.0F, 6.0F)))); + a("nether_brick_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.STONE, MaterialMapColor.K).a(2.0F, 6.0F)))); + a("quartz_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.STONE, MaterialMapColor.p).a(2.0F, 6.0F)))); + a("red_sandstone_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.STONE, MaterialMapColor.q).a(2.0F, 6.0F)))); + a("purpur_slab", (Block) (new BlockStepAbstract(Block.Info.a(Material.STONE, MaterialMapColor.r).a(2.0F, 6.0F)))); + a("smooth_stone", new Block(Block.Info.a(Material.STONE, MaterialMapColor.m).a(2.0F, 6.0F))); + a("smooth_sandstone", new Block(Block.Info.a(Material.STONE, MaterialMapColor.d).a(2.0F, 6.0F))); + a("smooth_quartz", new Block(Block.Info.a(Material.STONE, MaterialMapColor.p).a(2.0F, 6.0F))); + a("smooth_red_sandstone", new Block(Block.Info.a(Material.STONE, MaterialMapColor.q).a(2.0F, 6.0F))); + a("spruce_fence_gate", (Block) (new BlockFenceGate(Block.Info.a(Material.WOOD, block2.l).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("birch_fence_gate", (Block) (new BlockFenceGate(Block.Info.a(Material.WOOD, block3.l).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("jungle_fence_gate", (Block) (new BlockFenceGate(Block.Info.a(Material.WOOD, block4.l).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("acacia_fence_gate", (Block) (new BlockFenceGate(Block.Info.a(Material.WOOD, block5.l).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("dark_oak_fence_gate", (Block) (new BlockFenceGate(Block.Info.a(Material.WOOD, block6.l).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("spruce_fence", (Block) (new BlockFence(Block.Info.a(Material.WOOD, block2.l).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("birch_fence", (Block) (new BlockFence(Block.Info.a(Material.WOOD, block3.l).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("jungle_fence", (Block) (new BlockFence(Block.Info.a(Material.WOOD, block4.l).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("acacia_fence", (Block) (new BlockFence(Block.Info.a(Material.WOOD, block5.l).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("dark_oak_fence", (Block) (new BlockFence(Block.Info.a(Material.WOOD, block6.l).a(2.0F, 3.0F).a(SoundEffectType.a)))); + a("spruce_door", (Block) (new BlockDoor(Block.Info.a(Material.WOOD, block2.l).b(3.0F).a(SoundEffectType.a)))); + a("birch_door", (Block) (new BlockDoor(Block.Info.a(Material.WOOD, block3.l).b(3.0F).a(SoundEffectType.a)))); + a("jungle_door", (Block) (new BlockDoor(Block.Info.a(Material.WOOD, block4.l).b(3.0F).a(SoundEffectType.a)))); + a("acacia_door", (Block) (new BlockDoor(Block.Info.a(Material.WOOD, block5.l).b(3.0F).a(SoundEffectType.a)))); + a("dark_oak_door", (Block) (new BlockDoor(Block.Info.a(Material.WOOD, block6.l).b(3.0F).a(SoundEffectType.a)))); + a("end_rod", (Block) (new BlockEndRod(Block.Info.a(Material.ORIENTABLE).b().a(14).a(SoundEffectType.a)))); + BlockChorusFruit blockchorusfruit = new BlockChorusFruit(Block.Info.a(Material.PLANT, MaterialMapColor.z).b(0.4F).a(SoundEffectType.a)); + + a("chorus_plant", (Block) blockchorusfruit); + a("chorus_flower", (Block) (new BlockChorusFlower(blockchorusfruit, Block.Info.a(Material.PLANT, MaterialMapColor.z).c().b(0.4F).a(SoundEffectType.a)))); + Block block19 = new Block(Block.Info.a(Material.STONE, MaterialMapColor.r).a(1.5F, 6.0F)); + + a("purpur_block", block19); + a("purpur_pillar", (Block) (new BlockRotatable(Block.Info.a(Material.STONE, MaterialMapColor.r).a(1.5F, 6.0F)))); + a("purpur_stairs", (Block) (new BlockStairs(block19.getBlockData(), Block.Info.a(block19)))); + a("end_stone_bricks", new Block(Block.Info.a(Material.STONE, MaterialMapColor.d).b(0.8F))); + a("beetroots", (Block) (new BlockBeetroot(Block.Info.a(Material.PLANT).a().c().b().a(SoundEffectType.c)))); + BlockGrassPath blockgrasspath = new BlockGrassPath(Block.Info.a(Material.EARTH).b(0.65F).a(SoundEffectType.c)); + + a("grass_path", (Block) blockgrasspath); + a("end_gateway", (Block) (new BlockEndGateway(Block.Info.a(Material.PORTAL, MaterialMapColor.E).a().a(15).a(-1.0F, 3600000.0F)))); + a("repeating_command_block", (Block) (new BlockCommand(Block.Info.a(Material.ORE, MaterialMapColor.z).a(-1.0F, 3600000.0F)))); + a("chain_command_block", (Block) (new BlockCommand(Block.Info.a(Material.ORE, MaterialMapColor.C).a(-1.0F, 3600000.0F)))); + a("frosted_ice", (Block) (new BlockIceFrost(Block.Info.a(Material.ICE).a(0.98F).c().b(0.5F).a(SoundEffectType.f)))); + a("magma_block", (Block) (new BlockMagma(Block.Info.a(Material.STONE, MaterialMapColor.K).a(3).c().b(0.5F)))); + a("nether_wart_block", new Block(Block.Info.a(Material.GRASS, MaterialMapColor.D).b(1.0F).a(SoundEffectType.a))); + a("red_nether_bricks", new Block(Block.Info.a(Material.STONE, MaterialMapColor.K).a(2.0F, 6.0F))); + a("bone_block", (Block) (new BlockRotatable(Block.Info.a(Material.STONE, MaterialMapColor.d).b(2.0F)))); + a("structure_void", (Block) (new BlockStructureVoid(Block.Info.a(Material.STRUCTURE_VOID).a()))); + a("observer", (Block) (new BlockObserver(Block.Info.a(Material.STONE).b(3.0F)))); + a("shulker_box", (Block) (new BlockShulkerBox((EnumColor) null, Block.Info.a(Material.STONE, MaterialMapColor.z).b(2.0F).d()))); + a("white_shulker_box", (Block) (new BlockShulkerBox(EnumColor.WHITE, Block.Info.a(Material.STONE, MaterialMapColor.j).b(2.0F).d()))); + a("orange_shulker_box", (Block) (new BlockShulkerBox(EnumColor.ORANGE, Block.Info.a(Material.STONE, MaterialMapColor.q).b(2.0F).d()))); + a("magenta_shulker_box", (Block) (new BlockShulkerBox(EnumColor.MAGENTA, Block.Info.a(Material.STONE, MaterialMapColor.r).b(2.0F).d()))); + a("light_blue_shulker_box", (Block) (new BlockShulkerBox(EnumColor.LIGHT_BLUE, Block.Info.a(Material.STONE, MaterialMapColor.s).b(2.0F).d()))); + a("yellow_shulker_box", (Block) (new BlockShulkerBox(EnumColor.YELLOW, Block.Info.a(Material.STONE, MaterialMapColor.t).b(2.0F).d()))); + a("lime_shulker_box", (Block) (new BlockShulkerBox(EnumColor.LIME, Block.Info.a(Material.STONE, MaterialMapColor.u).b(2.0F).d()))); + a("pink_shulker_box", (Block) (new BlockShulkerBox(EnumColor.PINK, Block.Info.a(Material.STONE, MaterialMapColor.v).b(2.0F).d()))); + a("gray_shulker_box", (Block) (new BlockShulkerBox(EnumColor.GRAY, Block.Info.a(Material.STONE, MaterialMapColor.w).b(2.0F).d()))); + a("light_gray_shulker_box", (Block) (new BlockShulkerBox(EnumColor.LIGHT_GRAY, Block.Info.a(Material.STONE, MaterialMapColor.x).b(2.0F).d()))); + a("cyan_shulker_box", (Block) (new BlockShulkerBox(EnumColor.CYAN, Block.Info.a(Material.STONE, MaterialMapColor.y).b(2.0F).d()))); + a("purple_shulker_box", (Block) (new BlockShulkerBox(EnumColor.PURPLE, Block.Info.a(Material.STONE, MaterialMapColor.V).b(2.0F).d()))); + a("blue_shulker_box", (Block) (new BlockShulkerBox(EnumColor.BLUE, Block.Info.a(Material.STONE, MaterialMapColor.A).b(2.0F).d()))); + a("brown_shulker_box", (Block) (new BlockShulkerBox(EnumColor.BROWN, Block.Info.a(Material.STONE, MaterialMapColor.B).b(2.0F).d()))); + a("green_shulker_box", (Block) (new BlockShulkerBox(EnumColor.GREEN, Block.Info.a(Material.STONE, MaterialMapColor.C).b(2.0F).d()))); + a("red_shulker_box", (Block) (new BlockShulkerBox(EnumColor.RED, Block.Info.a(Material.STONE, MaterialMapColor.D).b(2.0F).d()))); + a("black_shulker_box", (Block) (new BlockShulkerBox(EnumColor.BLACK, Block.Info.a(Material.STONE, MaterialMapColor.E).b(2.0F).d()))); + a("white_glazed_terracotta", (Block) (new BlockGlazedTerracotta(Block.Info.a(Material.STONE, EnumColor.WHITE).b(1.4F)))); + a("orange_glazed_terracotta", (Block) (new BlockGlazedTerracotta(Block.Info.a(Material.STONE, EnumColor.ORANGE).b(1.4F)))); + a("magenta_glazed_terracotta", (Block) (new BlockGlazedTerracotta(Block.Info.a(Material.STONE, EnumColor.MAGENTA).b(1.4F)))); + a("light_blue_glazed_terracotta", (Block) (new BlockGlazedTerracotta(Block.Info.a(Material.STONE, EnumColor.LIGHT_BLUE).b(1.4F)))); + a("yellow_glazed_terracotta", (Block) (new BlockGlazedTerracotta(Block.Info.a(Material.STONE, EnumColor.YELLOW).b(1.4F)))); + a("lime_glazed_terracotta", (Block) (new BlockGlazedTerracotta(Block.Info.a(Material.STONE, EnumColor.LIME).b(1.4F)))); + a("pink_glazed_terracotta", (Block) (new BlockGlazedTerracotta(Block.Info.a(Material.STONE, EnumColor.PINK).b(1.4F)))); + a("gray_glazed_terracotta", (Block) (new BlockGlazedTerracotta(Block.Info.a(Material.STONE, EnumColor.GRAY).b(1.4F)))); + a("light_gray_glazed_terracotta", (Block) (new BlockGlazedTerracotta(Block.Info.a(Material.STONE, EnumColor.LIGHT_GRAY).b(1.4F)))); + a("cyan_glazed_terracotta", (Block) (new BlockGlazedTerracotta(Block.Info.a(Material.STONE, EnumColor.CYAN).b(1.4F)))); + a("purple_glazed_terracotta", (Block) (new BlockGlazedTerracotta(Block.Info.a(Material.STONE, EnumColor.PURPLE).b(1.4F)))); + a("blue_glazed_terracotta", (Block) (new BlockGlazedTerracotta(Block.Info.a(Material.STONE, EnumColor.BLUE).b(1.4F)))); + a("brown_glazed_terracotta", (Block) (new BlockGlazedTerracotta(Block.Info.a(Material.STONE, EnumColor.BROWN).b(1.4F)))); + a("green_glazed_terracotta", (Block) (new BlockGlazedTerracotta(Block.Info.a(Material.STONE, EnumColor.GREEN).b(1.4F)))); + a("red_glazed_terracotta", (Block) (new BlockGlazedTerracotta(Block.Info.a(Material.STONE, EnumColor.RED).b(1.4F)))); + a("black_glazed_terracotta", (Block) (new BlockGlazedTerracotta(Block.Info.a(Material.STONE, EnumColor.BLACK).b(1.4F)))); + Block block20 = new Block(Block.Info.a(Material.STONE, EnumColor.WHITE).b(1.8F)); + Block block21 = new Block(Block.Info.a(Material.STONE, EnumColor.ORANGE).b(1.8F)); + Block block22 = new Block(Block.Info.a(Material.STONE, EnumColor.MAGENTA).b(1.8F)); + Block block23 = new Block(Block.Info.a(Material.STONE, EnumColor.LIGHT_BLUE).b(1.8F)); + Block block24 = new Block(Block.Info.a(Material.STONE, EnumColor.YELLOW).b(1.8F)); + Block block25 = new Block(Block.Info.a(Material.STONE, EnumColor.LIME).b(1.8F)); + Block block26 = new Block(Block.Info.a(Material.STONE, EnumColor.PINK).b(1.8F)); + Block block27 = new Block(Block.Info.a(Material.STONE, EnumColor.GRAY).b(1.8F)); + Block block28 = new Block(Block.Info.a(Material.STONE, EnumColor.LIGHT_GRAY).b(1.8F)); + Block block29 = new Block(Block.Info.a(Material.STONE, EnumColor.CYAN).b(1.8F)); + Block block30 = new Block(Block.Info.a(Material.STONE, EnumColor.PURPLE).b(1.8F)); + Block block31 = new Block(Block.Info.a(Material.STONE, EnumColor.BLUE).b(1.8F)); + Block block32 = new Block(Block.Info.a(Material.STONE, EnumColor.BROWN).b(1.8F)); + Block block33 = new Block(Block.Info.a(Material.STONE, EnumColor.GREEN).b(1.8F)); + Block block34 = new Block(Block.Info.a(Material.STONE, EnumColor.RED).b(1.8F)); + Block block35 = new Block(Block.Info.a(Material.STONE, EnumColor.BLACK).b(1.8F)); + + a("white_concrete", block20); + a("orange_concrete", block21); + a("magenta_concrete", block22); + a("light_blue_concrete", block23); + a("yellow_concrete", block24); + a("lime_concrete", block25); + a("pink_concrete", block26); + a("gray_concrete", block27); + a("light_gray_concrete", block28); + a("cyan_concrete", block29); + a("purple_concrete", block30); + a("blue_concrete", block31); + a("brown_concrete", block32); + a("green_concrete", block33); + a("red_concrete", block34); + a("black_concrete", block35); + a("white_concrete_powder", (Block) (new BlockConcretePowder(block20, Block.Info.a(Material.SAND, EnumColor.WHITE).b(0.5F).a(SoundEffectType.h)))); + a("orange_concrete_powder", (Block) (new BlockConcretePowder(block21, Block.Info.a(Material.SAND, EnumColor.ORANGE).b(0.5F).a(SoundEffectType.h)))); + a("magenta_concrete_powder", (Block) (new BlockConcretePowder(block22, Block.Info.a(Material.SAND, EnumColor.MAGENTA).b(0.5F).a(SoundEffectType.h)))); + a("light_blue_concrete_powder", (Block) (new BlockConcretePowder(block23, Block.Info.a(Material.SAND, EnumColor.LIGHT_BLUE).b(0.5F).a(SoundEffectType.h)))); + a("yellow_concrete_powder", (Block) (new BlockConcretePowder(block24, Block.Info.a(Material.SAND, EnumColor.YELLOW).b(0.5F).a(SoundEffectType.h)))); + a("lime_concrete_powder", (Block) (new BlockConcretePowder(block25, Block.Info.a(Material.SAND, EnumColor.LIME).b(0.5F).a(SoundEffectType.h)))); + a("pink_concrete_powder", (Block) (new BlockConcretePowder(block26, Block.Info.a(Material.SAND, EnumColor.PINK).b(0.5F).a(SoundEffectType.h)))); + a("gray_concrete_powder", (Block) (new BlockConcretePowder(block27, Block.Info.a(Material.SAND, EnumColor.GRAY).b(0.5F).a(SoundEffectType.h)))); + a("light_gray_concrete_powder", (Block) (new BlockConcretePowder(block28, Block.Info.a(Material.SAND, EnumColor.LIGHT_GRAY).b(0.5F).a(SoundEffectType.h)))); + a("cyan_concrete_powder", (Block) (new BlockConcretePowder(block29, Block.Info.a(Material.SAND, EnumColor.CYAN).b(0.5F).a(SoundEffectType.h)))); + a("purple_concrete_powder", (Block) (new BlockConcretePowder(block30, Block.Info.a(Material.SAND, EnumColor.PURPLE).b(0.5F).a(SoundEffectType.h)))); + a("blue_concrete_powder", (Block) (new BlockConcretePowder(block31, Block.Info.a(Material.SAND, EnumColor.BLUE).b(0.5F).a(SoundEffectType.h)))); + a("brown_concrete_powder", (Block) (new BlockConcretePowder(block32, Block.Info.a(Material.SAND, EnumColor.BROWN).b(0.5F).a(SoundEffectType.h)))); + a("green_concrete_powder", (Block) (new BlockConcretePowder(block33, Block.Info.a(Material.SAND, EnumColor.GREEN).b(0.5F).a(SoundEffectType.h)))); + a("red_concrete_powder", (Block) (new BlockConcretePowder(block34, Block.Info.a(Material.SAND, EnumColor.RED).b(0.5F).a(SoundEffectType.h)))); + a("black_concrete_powder", (Block) (new BlockConcretePowder(block35, Block.Info.a(Material.SAND, EnumColor.BLACK).b(0.5F).a(SoundEffectType.h)))); + BlockKelp blockkelp = new BlockKelp(Block.Info.a(Material.WATER_PLANT).a().c().b().a(SoundEffectType.m)); + + a("kelp", (Block) blockkelp); + a("kelp_plant", (Block) (new BlockKelpPlant(blockkelp, Block.Info.a(Material.WATER_PLANT).a().b().a(SoundEffectType.m)))); + a("dried_kelp_block", new Block(Block.Info.a(Material.GRASS, MaterialMapColor.B).a(0.5F, 2.5F).a(SoundEffectType.c))); + a("turtle_egg", (Block) (new BlockTurtleEgg(Block.Info.a(Material.DRAGON_EGG, MaterialMapColor.x).b(0.5F).a(SoundEffectType.e).c()))); + Block block36 = new Block(Block.Info.a(Material.STONE, MaterialMapColor.w).a(1.5F, 6.0F)); + Block block37 = new Block(Block.Info.a(Material.STONE, MaterialMapColor.w).a(1.5F, 6.0F)); + Block block38 = new Block(Block.Info.a(Material.STONE, MaterialMapColor.w).a(1.5F, 6.0F)); + Block block39 = new Block(Block.Info.a(Material.STONE, MaterialMapColor.w).a(1.5F, 6.0F)); + Block block40 = new Block(Block.Info.a(Material.STONE, MaterialMapColor.w).a(1.5F, 6.0F)); + + a("dead_tube_coral_block", block36); + a("dead_brain_coral_block", block37); + a("dead_bubble_coral_block", block38); + a("dead_fire_coral_block", block39); + a("dead_horn_coral_block", block40); + a("tube_coral_block", (Block) (new BlockCoral(block36, Block.Info.a(Material.STONE, MaterialMapColor.A).a(1.5F, 6.0F).a(SoundEffectType.n)))); + a("brain_coral_block", (Block) (new BlockCoral(block37, Block.Info.a(Material.STONE, MaterialMapColor.v).a(1.5F, 6.0F).a(SoundEffectType.n)))); + a("bubble_coral_block", (Block) (new BlockCoral(block38, Block.Info.a(Material.STONE, MaterialMapColor.z).a(1.5F, 6.0F).a(SoundEffectType.n)))); + a("fire_coral_block", (Block) (new BlockCoral(block39, Block.Info.a(Material.STONE, MaterialMapColor.D).a(1.5F, 6.0F).a(SoundEffectType.n)))); + a("horn_coral_block", (Block) (new BlockCoral(block40, Block.Info.a(Material.STONE, MaterialMapColor.t).a(1.5F, 6.0F).a(SoundEffectType.n)))); + BlockCoralDead blockcoraldead = new BlockCoralDead(Block.Info.a(Material.STONE, MaterialMapColor.w).a().b()); + BlockCoralDead blockcoraldead1 = new BlockCoralDead(Block.Info.a(Material.STONE, MaterialMapColor.w).a().b()); + BlockCoralDead blockcoraldead2 = new BlockCoralDead(Block.Info.a(Material.STONE, MaterialMapColor.w).a().b()); + BlockCoralDead blockcoraldead3 = new BlockCoralDead(Block.Info.a(Material.STONE, MaterialMapColor.w).a().b()); + BlockCoralDead blockcoraldead4 = new BlockCoralDead(Block.Info.a(Material.STONE, MaterialMapColor.w).a().b()); + + a("dead_tube_coral", (Block) blockcoraldead); + a("dead_brain_coral", (Block) blockcoraldead1); + a("dead_bubble_coral", (Block) blockcoraldead2); + a("dead_fire_coral", (Block) blockcoraldead3); + a("dead_horn_coral", (Block) blockcoraldead4); + a("tube_coral", (Block) (new BlockCoralPlant(blockcoraldead, Block.Info.a(Material.WATER_PLANT, MaterialMapColor.A).a().b().a(SoundEffectType.m)))); + a("brain_coral", (Block) (new BlockCoralPlant(blockcoraldead1, Block.Info.a(Material.WATER_PLANT, MaterialMapColor.v).a().b().a(SoundEffectType.m)))); + a("bubble_coral", (Block) (new BlockCoralPlant(blockcoraldead2, Block.Info.a(Material.WATER_PLANT, MaterialMapColor.z).a().b().a(SoundEffectType.m)))); + a("fire_coral", (Block) (new BlockCoralPlant(blockcoraldead3, Block.Info.a(Material.WATER_PLANT, MaterialMapColor.D).a().b().a(SoundEffectType.m)))); + a("horn_coral", (Block) (new BlockCoralPlant(blockcoraldead4, Block.Info.a(Material.WATER_PLANT, MaterialMapColor.t).a().b().a(SoundEffectType.m)))); + BlockCoralFanWallAbstract blockcoralfanwallabstract = new BlockCoralFanWallAbstract(Block.Info.a(Material.STONE, MaterialMapColor.w).a().b()); + BlockCoralFanWallAbstract blockcoralfanwallabstract1 = new BlockCoralFanWallAbstract(Block.Info.a(Material.STONE, MaterialMapColor.w).a().b()); + BlockCoralFanWallAbstract blockcoralfanwallabstract2 = new BlockCoralFanWallAbstract(Block.Info.a(Material.STONE, MaterialMapColor.w).a().b()); + BlockCoralFanWallAbstract blockcoralfanwallabstract3 = new BlockCoralFanWallAbstract(Block.Info.a(Material.STONE, MaterialMapColor.w).a().b()); + BlockCoralFanWallAbstract blockcoralfanwallabstract4 = new BlockCoralFanWallAbstract(Block.Info.a(Material.STONE, MaterialMapColor.w).a().b()); + + a("dead_tube_coral_wall_fan", (Block) blockcoralfanwallabstract); + a("dead_brain_coral_wall_fan", (Block) blockcoralfanwallabstract1); + a("dead_bubble_coral_wall_fan", (Block) blockcoralfanwallabstract2); + a("dead_fire_coral_wall_fan", (Block) blockcoralfanwallabstract3); + a("dead_horn_coral_wall_fan", (Block) blockcoralfanwallabstract4); + a("tube_coral_wall_fan", (Block) (new BlockCoralFanWall(blockcoralfanwallabstract, Block.Info.a(Material.WATER_PLANT, MaterialMapColor.A).a().b().a(SoundEffectType.m)))); + a("brain_coral_wall_fan", (Block) (new BlockCoralFanWall(blockcoralfanwallabstract1, Block.Info.a(Material.WATER_PLANT, MaterialMapColor.v).a().b().a(SoundEffectType.m)))); + a("bubble_coral_wall_fan", (Block) (new BlockCoralFanWall(blockcoralfanwallabstract2, Block.Info.a(Material.WATER_PLANT, MaterialMapColor.z).a().b().a(SoundEffectType.m)))); + a("fire_coral_wall_fan", (Block) (new BlockCoralFanWall(blockcoralfanwallabstract3, Block.Info.a(Material.WATER_PLANT, MaterialMapColor.D).a().b().a(SoundEffectType.m)))); + a("horn_coral_wall_fan", (Block) (new BlockCoralFanWall(blockcoralfanwallabstract4, Block.Info.a(Material.WATER_PLANT, MaterialMapColor.t).a().b().a(SoundEffectType.m)))); + BlockCoralFanAbstract blockcoralfanabstract = new BlockCoralFanAbstract(Block.Info.a(Material.STONE, MaterialMapColor.w).a().b()); + BlockCoralFanAbstract blockcoralfanabstract1 = new BlockCoralFanAbstract(Block.Info.a(Material.STONE, MaterialMapColor.w).a().b()); + BlockCoralFanAbstract blockcoralfanabstract2 = new BlockCoralFanAbstract(Block.Info.a(Material.STONE, MaterialMapColor.w).a().b()); + BlockCoralFanAbstract blockcoralfanabstract3 = new BlockCoralFanAbstract(Block.Info.a(Material.STONE, MaterialMapColor.w).a().b()); + BlockCoralFanAbstract blockcoralfanabstract4 = new BlockCoralFanAbstract(Block.Info.a(Material.STONE, MaterialMapColor.w).a().b()); + + a("dead_tube_coral_fan", (Block) blockcoralfanabstract); + a("dead_brain_coral_fan", (Block) blockcoralfanabstract1); + a("dead_bubble_coral_fan", (Block) blockcoralfanabstract2); + a("dead_fire_coral_fan", (Block) blockcoralfanabstract3); + a("dead_horn_coral_fan", (Block) blockcoralfanabstract4); + a("tube_coral_fan", (Block) (new BlockCoralFan(blockcoralfanabstract, Block.Info.a(Material.WATER_PLANT, MaterialMapColor.A).a().b().a(SoundEffectType.m)))); + a("brain_coral_fan", (Block) (new BlockCoralFan(blockcoralfanabstract1, Block.Info.a(Material.WATER_PLANT, MaterialMapColor.v).a().b().a(SoundEffectType.m)))); + a("bubble_coral_fan", (Block) (new BlockCoralFan(blockcoralfanabstract2, Block.Info.a(Material.WATER_PLANT, MaterialMapColor.z).a().b().a(SoundEffectType.m)))); + a("fire_coral_fan", (Block) (new BlockCoralFan(blockcoralfanabstract3, Block.Info.a(Material.WATER_PLANT, MaterialMapColor.D).a().b().a(SoundEffectType.m)))); + a("horn_coral_fan", (Block) (new BlockCoralFan(blockcoralfanabstract4, Block.Info.a(Material.WATER_PLANT, MaterialMapColor.t).a().b().a(SoundEffectType.m)))); + a("sea_pickle", (Block) (new BlockSeaPickle(Block.Info.a(Material.WATER_PLANT, MaterialMapColor.C).a(3).a(SoundEffectType.l)))); + a("blue_ice", (Block) (new BlockBlueIce(Block.Info.a(Material.SNOW_LAYER).b(2.8F).a(0.989F).a(SoundEffectType.f)))); + a("conduit", (Block) (new BlockConduit(Block.Info.a(Material.SHATTERABLE, MaterialMapColor.G).b(3.0F).a(15)))); + a("void_air", (Block) (new BlockAir(Block.Info.a(Material.AIR).a()))); + a("cave_air", (Block) (new BlockAir(Block.Info.a(Material.AIR).a()))); + a("bubble_column", (Block) (new BlockBubbleColumn(Block.Info.a(Material.BUBBLE_COLUMN).a()))); + a("structure_block", (Block) (new BlockStructure(Block.Info.a(Material.ORE, MaterialMapColor.x).a(-1.0F, 3600000.0F)))); + Iterator iterator = IRegistry.BLOCK.iterator(); + + while (iterator.hasNext()) { + Block block41 = (Block) iterator.next(); + UnmodifiableIterator unmodifiableiterator = block41.getStates().a().iterator(); + + while (unmodifiableiterator.hasNext()) { + IBlockData iblockdata = (IBlockData) unmodifiableiterator.next(); + + Block.REGISTRY_ID.b(iblockdata); + } + } + + } + + // CraftBukkit start + public int getExpDrop(IBlockData iblockdata, World world, BlockPosition blockposition, int enchantmentLevel) { + return 0; + } + // CraftBukkit end + + private static void a(MinecraftKey minecraftkey, Block block) { + IRegistry.BLOCK.a(minecraftkey, block); // CraftBukkit - decompile error + } + + private static void a(String s, Block block) { + a(new MinecraftKey(s), block); + } + + // Spigot start + public static float range(float min, float value, float max) { + if (value < min) { + return min; + } + if (value > max) { + return max; + } + return value; + } + // Spigot end + + public static enum EnumRandomOffset { + + NONE, XZ, XYZ; + + private EnumRandomOffset() {} + } + + public static class Info { + + private Material a; + private MaterialMapColor b; + private boolean c = true; + private SoundEffectType d; + private int e; + private float f; + private float g; + private boolean h; + private float i; + private boolean j; + + private Info(Material material, MaterialMapColor materialmapcolor) { + this.d = SoundEffectType.d; + this.i = 0.6F; + this.a = material; + this.b = materialmapcolor; + } + + public static Block.Info a(Material material) { + return a(material, material.i()); + } + + public static Block.Info a(Material material, EnumColor enumcolor) { + return a(material, enumcolor.e()); + } + + public static Block.Info a(Material material, MaterialMapColor materialmapcolor) { + return new Block.Info(material, materialmapcolor); + } + + public static Block.Info a(Block block) { + Block.Info block_info = new Block.Info(block.material, block.l); + + block_info.a = block.material; + block_info.g = block.strength; + block_info.f = block.durability; + block_info.c = block.n; + block_info.h = block.i; + block_info.e = block.f; + block_info.a = block.material; + block_info.b = block.l; + block_info.d = block.stepSound; + block_info.i = block.n(); + block_info.j = block.o; + return block_info; + } + + public Block.Info a() { + this.c = false; + return this; + } + + public Block.Info a(float f) { + this.i = f; + return this; + } + + protected Block.Info a(SoundEffectType soundeffecttype) { + this.d = soundeffecttype; + return this; + } + + protected Block.Info a(int i) { + this.e = i; + return this; + } + + public Block.Info a(float f, float f1) { + this.g = f; + this.f = Math.max(0.0F, f1); + return this; + } + + protected Block.Info b() { + return this.b(0.0F); + } + + protected Block.Info b(float f) { + this.a(f, f); + return this; + } + + protected Block.Info c() { + this.h = true; + return this; + } + + protected Block.Info d() { + this.j = true; + return this; + } + } + + public static final class a { + + private final IBlockData a; + private final IBlockData b; + private final EnumDirection c; + + public a(IBlockData iblockdata, IBlockData iblockdata1, EnumDirection enumdirection) { + this.a = iblockdata; + this.b = iblockdata1; + this.c = enumdirection; + } + + public boolean equals(Object object) { + if (this == object) { + return true; + } else if (!(object instanceof Block.a)) { + return false; + } else { + Block.a block_a = (Block.a) object; + + return this.a == block_a.a && this.b == block_a.b && this.c == block_a.c; + } + } + + public int hashCode() { + return Objects.hash(new Object[] { this.a, this.b, this.c}); + } + } +} diff --git a/src/main/java/net/minecraft/server/BlockBeacon.java b/src/main/java/net/minecraft/server/BlockBeacon.java new file mode 100644 index 000000000000..e45ad95fb0ee --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockBeacon.java @@ -0,0 +1,78 @@ +package net.minecraft.server; + +public class BlockBeacon extends BlockTileEntity { + + public BlockBeacon(Block.Info block_info) { + super(block_info); + } + + public TileEntity a(IBlockAccess iblockaccess) { + return new TileEntityBeacon(); + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + if (world.isClientSide) { + return true; + } else { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityBeacon) { + entityhuman.openContainer((TileEntityBeacon) tileentity); + entityhuman.a(StatisticList.INTERACT_WITH_BEACON); + } + + return true; + } + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public EnumRenderType c(IBlockData iblockdata) { + return EnumRenderType.MODEL; + } + + public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) { + if (itemstack.hasName()) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityBeacon) { + ((TileEntityBeacon) tileentity).setCustomName(itemstack.getName()); + } + } + + } + + public TextureType c() { + return TextureType.CUTOUT; + } + + public static void a(World world, BlockPosition blockposition) { + //HttpUtilities.a.submit(() -> { // Paper + Chunk chunk = world.getChunkAtWorldCoords(blockposition); + + for (int i = blockposition.getY() - 1; i >= 0; --i) { + BlockPosition blockposition1 = new BlockPosition(blockposition.getX(), i, blockposition.getZ()); + + if (!chunk.c(blockposition1)) { + break; + } + + IBlockData iblockdata = world.getType(blockposition1); + + if (iblockdata.getBlock() == Blocks.BEACON) { + ((WorldServer) world).postToMainThread(() -> { + TileEntity tileentity = world.getTileEntity(blockposition1); + + if (tileentity instanceof TileEntityBeacon) { + ((TileEntityBeacon) tileentity).p(); + world.playBlockAction(blockposition1, Blocks.BEACON, 1, 0); + } + + }); + } + } + // }); // Paper + } +} diff --git a/src/main/java/net/minecraft/server/BlockBed.java b/src/main/java/net/minecraft/server/BlockBed.java new file mode 100644 index 000000000000..d81a2db6cd7f --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockBed.java @@ -0,0 +1,310 @@ +package net.minecraft.server; + +import java.util.Iterator; +import javax.annotation.Nullable; + +public class BlockBed extends BlockFacingHorizontal implements ITileEntity { + + public static final BlockStateEnum PART = BlockProperties.ao; + public static final BlockStateBoolean OCCUPIED = BlockProperties.q; + protected static final VoxelShape c = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 9.0D, 16.0D); + private final EnumColor color; + + public BlockBed(EnumColor enumcolor, Block.Info block_info) { + super(block_info); + this.color = enumcolor; + this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockBed.PART, BlockPropertyBedPart.FOOT)).set(BlockBed.OCCUPIED, false)); + } + + public MaterialMapColor c(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return iblockdata.get(BlockBed.PART) == BlockPropertyBedPart.FOOT ? this.color.e() : MaterialMapColor.e; + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + if (world.isClientSide) { + return true; + } else { + if (iblockdata.get(BlockBed.PART) != BlockPropertyBedPart.HEAD) { + blockposition = blockposition.shift((EnumDirection) iblockdata.get(BlockBed.FACING)); + iblockdata = world.getType(blockposition); + if (iblockdata.getBlock() != this) { + return true; + } + } + + // CraftBukkit - moved world and biome check into EntityHuman + if (true || world.worldProvider.canRespawn() && world.getBiome(blockposition) != Biomes.NETHER) { + if ((Boolean) iblockdata.get(BlockBed.OCCUPIED)) { + EntityHuman entityhuman1 = this.a(world, blockposition); + + if (entityhuman1 != null) { + entityhuman.a((IChatBaseComponent) (new ChatMessage("block.minecraft.bed.occupied", new Object[0])), true); + return true; + } + + iblockdata = (IBlockData) iblockdata.set(BlockBed.OCCUPIED, false); + world.setTypeAndData(blockposition, iblockdata, 4); + } + + EntityHuman.EnumBedResult entityhuman_enumbedresult = entityhuman.a(blockposition); + + if (entityhuman_enumbedresult == EntityHuman.EnumBedResult.OK) { + iblockdata = (IBlockData) iblockdata.set(BlockBed.OCCUPIED, true); + world.setTypeAndData(blockposition, iblockdata, 4); + return true; + } else { + if (entityhuman_enumbedresult == EntityHuman.EnumBedResult.NOT_POSSIBLE_NOW) { + entityhuman.a((IChatBaseComponent) (new ChatMessage("block.minecraft.bed.no_sleep", new Object[0])), true); + } else if (entityhuman_enumbedresult == EntityHuman.EnumBedResult.NOT_SAFE) { + entityhuman.a((IChatBaseComponent) (new ChatMessage("block.minecraft.bed.not_safe", new Object[0])), true); + } else if (entityhuman_enumbedresult == EntityHuman.EnumBedResult.TOO_FAR_AWAY) { + entityhuman.a((IChatBaseComponent) (new ChatMessage("block.minecraft.bed.too_far_away", new Object[0])), true); + } + // CraftBukkit start - handling bed explosion from below here + else if (entityhuman_enumbedresult == EntityHuman.EnumBedResult.NOT_POSSIBLE_HERE) { + this.explodeBed(iblockdata, world, blockposition); + } + // CraftBukkit end + + return true; + } + // CraftBukkit start - moved bed explosion into separate method + } else { + return true; + } + } + } + + private boolean explodeBed(IBlockData iblockdata, World world, BlockPosition blockposition) { + world.setAir(blockposition); + BlockPosition blockposition1 = blockposition.shift(((EnumDirection) iblockdata.get(BlockBed.FACING)).opposite()); + + if (world.getType(blockposition1).getBlock() == this) { + world.setAir(blockposition1); + } + + world.createExplosion((Entity) null, DamageSource.a(), (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, 5.0F, true, true); + return true; + // CraftBukkit end + } + + @Nullable + private EntityHuman a(World world, BlockPosition blockposition) { + Iterator iterator = world.players.iterator(); + + EntityHuman entityhuman; + + do { + if (!iterator.hasNext()) { + return null; + } + + entityhuman = (EntityHuman) iterator.next(); + } while (!entityhuman.isSleeping() || !entityhuman.bedPosition.equals(blockposition)); + + return entityhuman; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public void fallOn(World world, BlockPosition blockposition, Entity entity, float f) { + super.fallOn(world, blockposition, entity, f * 0.5F); + } + + public void a(IBlockAccess iblockaccess, Entity entity) { + if (entity.isSneaking()) { + super.a(iblockaccess, entity); + } else if (entity.motY < 0.0D) { + entity.motY = -entity.motY * 0.6600000262260437D; + if (!(entity instanceof EntityLiving)) { + entity.motY *= 0.8D; + } + } + + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + return enumdirection == a((BlockPropertyBedPart) iblockdata.get(BlockBed.PART), (EnumDirection) iblockdata.get(BlockBed.FACING)) ? (iblockdata1.getBlock() == this && iblockdata1.get(BlockBed.PART) != iblockdata.get(BlockBed.PART) ? (IBlockData) iblockdata.set(BlockBed.OCCUPIED, iblockdata1.get(BlockBed.OCCUPIED)) : Blocks.AIR.getBlockData()) : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + private static EnumDirection a(BlockPropertyBedPart blockpropertybedpart, EnumDirection enumdirection) { + return blockpropertybedpart == BlockPropertyBedPart.FOOT ? enumdirection : enumdirection.opposite(); + } + + public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) { + super.a(world, entityhuman, blockposition, Blocks.AIR.getBlockData(), tileentity, itemstack); + } + + public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) { + if (iblockdata.getBlock() != iblockdata1.getBlock()) { + super.remove(iblockdata, world, blockposition, iblockdata1, flag); + world.n(blockposition); + } + } + + public void a(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) { + BlockPropertyBedPart blockpropertybedpart = (BlockPropertyBedPart) iblockdata.get(BlockBed.PART); + boolean flag = blockpropertybedpart == BlockPropertyBedPart.HEAD; + BlockPosition blockposition1 = blockposition.shift(a(blockpropertybedpart, (EnumDirection) iblockdata.get(BlockBed.FACING))); + IBlockData iblockdata1 = world.getType(blockposition1); + + if (iblockdata1.getBlock() == this && iblockdata1.get(BlockBed.PART) != blockpropertybedpart) { + world.setTypeAndData(blockposition1, Blocks.AIR.getBlockData(), 35); + world.a(entityhuman, 2001, blockposition1, Block.getCombinedId(iblockdata1)); + if (!world.isClientSide && !entityhuman.u()) { + if (flag) { + iblockdata.a(world, blockposition, 0); + } else { + iblockdata1.a(world, blockposition1, 0); + } + } + + entityhuman.b(StatisticList.BLOCK_MINED.b(this)); + } + + super.a(world, blockposition, iblockdata, entityhuman); + } + + @Nullable + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + EnumDirection enumdirection = blockactioncontext.f(); + BlockPosition blockposition = blockactioncontext.getClickPosition(); + BlockPosition blockposition1 = blockposition.shift(enumdirection); + + return blockactioncontext.getWorld().getType(blockposition1).a(blockactioncontext) ? (IBlockData) this.getBlockData().set(BlockBed.FACING, enumdirection) : null; + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return (IMaterial) (iblockdata.get(BlockBed.PART) == BlockPropertyBedPart.FOOT ? Items.AIR : super.getDropType(iblockdata, world, blockposition, i)); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockBed.c; + } + + @Nullable + public static BlockPosition a(IBlockAccess iblockaccess, BlockPosition blockposition, int i) { + EnumDirection enumdirection = (EnumDirection) iblockaccess.getType(blockposition).get(BlockBed.FACING); + // Paper - replace whole method + World world = (World) iblockaccess; + int radius = world.paperConfig.bedSearchRadius; + for (int r = 1; r <= radius; r++) { + int x = -r; + int z = r; + + // Iterates the edge of half of the box; then negates for other half. + while (x <= r && z > -r) { + for (int y = -1; y <= 1; y++) { + BlockPosition pos = blockposition.add(x, y, z); + if (isSafeRespawn(world, pos)) { + if (i-- <= 0) { + return pos; + } + } + pos = blockposition.add(-x, y, -z); + if (isSafeRespawn(world, pos)) { + if (i-- <= 0) { + return pos; + } + } + + pos = blockposition.add(enumdirection.getAdjacentX() + x, y, enumdirection.getAdjacentZ() + z); + if (isSafeRespawn(world, pos)) { + if (i-- <= 0) { + return pos; + } + } + + pos = blockposition.add(enumdirection.getAdjacentX() - x, y, enumdirection.getAdjacentZ() - z); + if (isSafeRespawn(world, pos)) { + if (i-- <= 0) { + return pos; + } + } + } + if (x < r) { + x++; + } else { + z--; + } + } + } + + return null; /* // Paper comment out + int j = blockposition.getX(); + int k = blockposition.getY(); + int l = blockposition.getZ(); + + for (int i1 = 0; i1 <= 1; ++i1) { + int j1 = j - enumdirection.getAdjacentX() * i1 - 1; + int k1 = l - enumdirection.getAdjacentZ() * i1 - 1; + int l1 = j1 + 2; + int i2 = k1 + 2; + + for (int j2 = j1; j2 <= l1; ++j2) { + for (int k2 = k1; k2 <= i2; ++k2) { + BlockPosition blockposition1 = new BlockPosition(j2, k, k2); + + if (a(iblockaccess, blockposition1)) { + if (i <= 0) { + return blockposition1; + } + + --i; + } + } + } + } + + return null;*/ // Paper + } + + protected static boolean isSafeRespawn(IBlockAccess iblockaccess, BlockPosition blockposition) { // Paper - OBFHELPER + behavior improvement + return a(iblockaccess, blockposition) && iblockaccess.getType(blockposition.down()).getMaterial().isBuildable(); // Paper - ensure solid block + } + protected static boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) { + return iblockaccess.getType(blockposition.down()).q() && !iblockaccess.getType(blockposition).getMaterial().isBuildable() && !iblockaccess.getType(blockposition.up()).getMaterial().isBuildable(); + } + + public EnumPistonReaction getPushReaction(IBlockData iblockdata) { + return EnumPistonReaction.DESTROY; + } + + public TextureType c() { + return TextureType.CUTOUT; + } + + public EnumRenderType c(IBlockData iblockdata) { + return EnumRenderType.ENTITYBLOCK_ANIMATED; + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockBed.FACING, BlockBed.PART, BlockBed.OCCUPIED); + } + + public TileEntity a(IBlockAccess iblockaccess) { + return new TileEntityBed(this.color); + } + + public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, @Nullable EntityLiving entityliving, ItemStack itemstack) { + super.postPlace(world, blockposition, iblockdata, entityliving, itemstack); + if (!world.isClientSide) { + BlockPosition blockposition1 = blockposition.shift((EnumDirection) iblockdata.get(BlockBed.FACING)); + + world.setTypeAndData(blockposition1, (IBlockData) iblockdata.set(BlockBed.PART, BlockPropertyBedPart.HEAD), 3); + world.update(blockposition, Blocks.AIR); + iblockdata.a((GeneratorAccess) world, blockposition, 3); + } + + } + + public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/BlockButtonAbstract.java b/src/main/java/net/minecraft/server/BlockButtonAbstract.java new file mode 100644 index 000000000000..665329baea9a --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockButtonAbstract.java @@ -0,0 +1,224 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.Random; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.event.block.BlockRedstoneEvent; +import org.bukkit.event.entity.EntityInteractEvent; +// CraftBukkit end + +public abstract class BlockButtonAbstract extends BlockAttachable { + + public static final BlockStateBoolean POWERED = BlockProperties.t; + protected static final VoxelShape b = Block.a(6.0D, 14.0D, 5.0D, 10.0D, 16.0D, 11.0D); + protected static final VoxelShape c = Block.a(5.0D, 14.0D, 6.0D, 11.0D, 16.0D, 10.0D); + protected static final VoxelShape o = Block.a(6.0D, 0.0D, 5.0D, 10.0D, 2.0D, 11.0D); + protected static final VoxelShape p = Block.a(5.0D, 0.0D, 6.0D, 11.0D, 2.0D, 10.0D); + protected static final VoxelShape q = Block.a(5.0D, 6.0D, 14.0D, 11.0D, 10.0D, 16.0D); + protected static final VoxelShape r = Block.a(5.0D, 6.0D, 0.0D, 11.0D, 10.0D, 2.0D); + protected static final VoxelShape s = Block.a(14.0D, 6.0D, 5.0D, 16.0D, 10.0D, 11.0D); + protected static final VoxelShape t = Block.a(0.0D, 6.0D, 5.0D, 2.0D, 10.0D, 11.0D); + protected static final VoxelShape u = Block.a(6.0D, 15.0D, 5.0D, 10.0D, 16.0D, 11.0D); + protected static final VoxelShape v = Block.a(5.0D, 15.0D, 6.0D, 11.0D, 16.0D, 10.0D); + protected static final VoxelShape w = Block.a(6.0D, 0.0D, 5.0D, 10.0D, 1.0D, 11.0D); + protected static final VoxelShape x = Block.a(5.0D, 0.0D, 6.0D, 11.0D, 1.0D, 10.0D); + protected static final VoxelShape y = Block.a(5.0D, 6.0D, 15.0D, 11.0D, 10.0D, 16.0D); + protected static final VoxelShape z = Block.a(5.0D, 6.0D, 0.0D, 11.0D, 10.0D, 1.0D); + protected static final VoxelShape A = Block.a(15.0D, 6.0D, 5.0D, 16.0D, 10.0D, 11.0D); + protected static final VoxelShape B = Block.a(0.0D, 6.0D, 5.0D, 1.0D, 10.0D, 11.0D); + private final boolean E; + + protected BlockButtonAbstract(boolean flag, Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockButtonAbstract.FACING, EnumDirection.NORTH)).set(BlockButtonAbstract.POWERED, false)).set(BlockButtonAbstract.FACE, BlockPropertyAttachPosition.WALL)); + this.E = flag; + } + + public int a(IWorldReader iworldreader) { + return this.E ? 30 : 20; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockButtonAbstract.FACING); + boolean flag = (Boolean) iblockdata.get(BlockButtonAbstract.POWERED); + + switch ((BlockPropertyAttachPosition) iblockdata.get(BlockButtonAbstract.FACE)) { + case FLOOR: + if (enumdirection.k() == EnumDirection.EnumAxis.X) { + return flag ? BlockButtonAbstract.w : BlockButtonAbstract.o; + } + + return flag ? BlockButtonAbstract.x : BlockButtonAbstract.p; + case WALL: + switch (enumdirection) { + case EAST: + return flag ? BlockButtonAbstract.B : BlockButtonAbstract.t; + case WEST: + return flag ? BlockButtonAbstract.A : BlockButtonAbstract.s; + case SOUTH: + return flag ? BlockButtonAbstract.z : BlockButtonAbstract.r; + case NORTH: + default: + return flag ? BlockButtonAbstract.y : BlockButtonAbstract.q; + } + case CEILING: + default: + return enumdirection.k() == EnumDirection.EnumAxis.X ? (flag ? BlockButtonAbstract.u : BlockButtonAbstract.b) : (flag ? BlockButtonAbstract.v : BlockButtonAbstract.c); + } + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + if ((Boolean) iblockdata.get(BlockButtonAbstract.POWERED)) { + return true; + } else { + // CraftBukkit start + boolean powered = ((Boolean) iblockdata.get(POWERED)); + org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + int old = (powered) ? 15 : 0; + int current = (!powered) ? 15 : 0; + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current); + world.getServer().getPluginManager().callEvent(eventRedstone); + + if ((eventRedstone.getNewCurrent() > 0) != (!powered)) { + return true; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockButtonAbstract.POWERED, true), 3); + this.a(entityhuman, world, blockposition, true); + this.c(iblockdata, world, blockposition); + world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world)); + return true; + } + } + + protected void a(@Nullable EntityHuman entityhuman, GeneratorAccess generatoraccess, BlockPosition blockposition, boolean flag) { + generatoraccess.a(flag ? entityhuman : null, blockposition, this.a(flag), SoundCategory.BLOCKS, 0.3F, flag ? 0.6F : 0.5F); + } + + protected abstract SoundEffect a(boolean flag); + + public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) { + if (!flag && iblockdata.getBlock() != iblockdata1.getBlock()) { + if ((Boolean) iblockdata.get(BlockButtonAbstract.POWERED)) { + this.c(iblockdata, world, blockposition); + } + + super.remove(iblockdata, world, blockposition, iblockdata1, flag); + } + } + + public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return (Boolean) iblockdata.get(BlockButtonAbstract.POWERED) ? 15 : 0; + } + + public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return (Boolean) iblockdata.get(BlockButtonAbstract.POWERED) && k(iblockdata) == enumdirection ? 15 : 0; + } + + public boolean isPowerSource(IBlockData iblockdata) { + return true; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!world.isClientSide && (Boolean) iblockdata.get(BlockButtonAbstract.POWERED)) { + if (this.E) { + this.b(iblockdata, world, blockposition); + } else { + // CraftBukkit start + org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, 15, 0); + world.getServer().getPluginManager().callEvent(eventRedstone); + + if (eventRedstone.getNewCurrent() > 0) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockButtonAbstract.POWERED, false), 3); + this.c(iblockdata, world, blockposition); + this.a((EntityHuman) null, world, blockposition, false); + } + + } + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) { + if (!world.isClientSide && this.E && !(Boolean) iblockdata.get(BlockButtonAbstract.POWERED)) { + this.b(iblockdata, world, blockposition); + } + } + + private void b(IBlockData iblockdata, World world, BlockPosition blockposition) { + List list = world.a(EntityArrow.class, iblockdata.getShape(world, blockposition).getBoundingBox().a(blockposition)); + boolean flag = !list.isEmpty(); + boolean flag1 = (Boolean) iblockdata.get(BlockButtonAbstract.POWERED); + + // CraftBukkit start - Call interact event when arrows turn on wooden buttons + if (flag1 != flag && flag) { + org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + boolean allowed = false; + + // If all of the events are cancelled block the button press, else allow + for (Object object : list) { + if (object != null) { + EntityInteractEvent event = new EntityInteractEvent(((Entity) object).getBukkitEntity(), block); + world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + allowed = true; + break; + } + } + } + + if (!allowed) { + return; + } + } + // CraftBukkit end + + if (flag != flag1) { + // CraftBukkit start + boolean powered = flag1; + org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + int old = (powered) ? 15 : 0; + int current = (!powered) ? 15 : 0; + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current); + world.getServer().getPluginManager().callEvent(eventRedstone); + + if ((flag && eventRedstone.getNewCurrent() <= 0) || (!flag && eventRedstone.getNewCurrent() > 0)) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockButtonAbstract.POWERED, flag), 3); + this.c(iblockdata, world, blockposition); + this.a((EntityHuman) null, world, blockposition, flag); + } + + if (flag) { + world.getBlockTickList().a(new BlockPosition(blockposition), this, this.a((IWorldReader) world)); + } + + } + + private void c(IBlockData iblockdata, World world, BlockPosition blockposition) { + world.applyPhysics(blockposition, this); + world.applyPhysics(blockposition.shift(k(iblockdata).opposite()), this); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockButtonAbstract.FACING, BlockButtonAbstract.POWERED, BlockButtonAbstract.FACE); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } +} diff --git a/src/main/java/net/minecraft/server/BlockCactus.java b/src/main/java/net/minecraft/server/BlockCactus.java new file mode 100644 index 000000000000..b6806d8db389 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockCactus.java @@ -0,0 +1,117 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.Random; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockCactus extends Block { + + public static final BlockStateInteger AGE = BlockProperties.X; + protected static final VoxelShape b = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 15.0D, 15.0D); + protected static final VoxelShape c = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D); + + protected BlockCactus(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockCactus.AGE, 0)); + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!iblockdata.canPlace(world, blockposition)) { + world.setAir(blockposition, true); + } else { + BlockPosition blockposition1 = blockposition.up(); + + if (world.isEmpty(blockposition1)) { + int i; + + for (i = 1; world.getType(blockposition.down(i)).getBlock() == this; ++i) { + ; + } + + if (i < world.paperConfig.cactusMaxHeight) { // Paper - Configurable growth height + int j = (Integer) iblockdata.get(BlockCactus.AGE); + + if (j >= (byte) range(3, ((100.0F / world.spigotConfig.cactusModifier) * 15) + 0.5F, 15)) { // Spigot + CraftEventFactory.handleBlockGrowEvent(world, blockposition1, this.getBlockData()); // CraftBukkit + IBlockData iblockdata1 = (IBlockData) iblockdata.set(BlockCactus.AGE, 0); + + world.setTypeAndData(blockposition, iblockdata1, 4); + iblockdata1.doPhysics(world, blockposition1, this, blockposition); + } else { + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockCactus.AGE, j + 1), 4); + } + + } + } + } + } + + public VoxelShape f(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockCactus.b; + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockCactus.c; + } + + public boolean f(IBlockData iblockdata) { + return true; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + if (!iblockdata.canPlace(generatoraccess, blockposition)) { + generatoraccess.getBlockTickList().a(blockposition, this, 1); + } + + return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + EnumDirection enumdirection; + Material material; + + do { + if (!iterator.hasNext()) { + Block block = iworldreader.getType(blockposition.down()).getBlock(); + + return (block == Blocks.CACTUS || block == Blocks.SAND || block == Blocks.RED_SAND) && !iworldreader.getType(blockposition.up()).getMaterial().isLiquid(); + } + + enumdirection = (EnumDirection) iterator.next(); + IBlockData iblockdata1 = iworldreader.getType(blockposition.shift(enumdirection)); + + material = iblockdata1.getMaterial(); + } while (!material.isBuildable() && !iworldreader.getFluid(blockposition.shift(enumdirection)).a(TagsFluid.LAVA)); + + return false; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) { + CraftEventFactory.blockDamage = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); // CraftBukkit + entity.damageEntity(DamageSource.CACTUS, 1.0F); + CraftEventFactory.blockDamage = null; // CraftBukkit + } + + public TextureType c() { + return TextureType.CUTOUT; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockCactus.AGE); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } + + public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/BlockCake.java b/src/main/java/net/minecraft/server/BlockCake.java new file mode 100644 index 000000000000..d49dca9869cc --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockCake.java @@ -0,0 +1,91 @@ +package net.minecraft.server; + +public class BlockCake extends Block { + + public static final BlockStateInteger BITES = BlockProperties.Z; + protected static final VoxelShape[] b = new VoxelShape[] { Block.a(1.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(3.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(5.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(7.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(9.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(11.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(13.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D)}; + + protected BlockCake(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockCake.BITES, 0)); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockCake.b[(Integer) iblockdata.get(BlockCake.BITES)]; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + if (!world.isClientSide) { + return this.a((GeneratorAccess) world, blockposition, iblockdata, entityhuman); + } else { + ItemStack itemstack = entityhuman.b(enumhand); + + return this.a((GeneratorAccess) world, blockposition, iblockdata, entityhuman) || itemstack.isEmpty(); + } + } + + private boolean a(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) { + if (!entityhuman.q(false)) { + return false; + } else { + entityhuman.a(StatisticList.EAT_CAKE_SLICE); + // CraftBukkit start + // entityhuman.getFoodData().eat(2, 0.1F); + int oldFoodLevel = entityhuman.getFoodData().foodLevel; + + org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(entityhuman, 2 + oldFoodLevel); + + if (!event.isCancelled()) { + entityhuman.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 0.1F); + } + + ((EntityPlayer) entityhuman).getBukkitEntity().sendHealthUpdate(); + // CraftBukkit end + int i = (Integer) iblockdata.get(BlockCake.BITES); + + if (i < 6) { + generatoraccess.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockCake.BITES, i + 1), 3); + } else { + generatoraccess.setAir(blockposition); + } + + return true; + } + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + return enumdirection == EnumDirection.DOWN && !iblockdata.canPlace(generatoraccess, blockposition) ? Blocks.AIR.getBlockData() : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + return iworldreader.getType(blockposition.down()).getMaterial().isBuildable(); + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return Items.AIR; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockCake.BITES); + } + + public int a(IBlockData iblockdata, World world, BlockPosition blockposition) { + return (7 - (Integer) iblockdata.get(BlockCake.BITES)) * 2; + } + + public boolean isComplexRedstone(IBlockData iblockdata) { + return true; + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } + + public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/BlockCauldron.java b/src/main/java/net/minecraft/server/BlockCauldron.java new file mode 100644 index 000000000000..4a20bdec25c9 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockCauldron.java @@ -0,0 +1,267 @@ +package net.minecraft.server; + +import org.bukkit.event.block.CauldronLevelChangeEvent; // CraftBukkit + +public class BlockCauldron extends Block { + + public static final BlockStateInteger LEVEL = BlockProperties.af; + protected static final VoxelShape b = Block.a(2.0D, 4.0D, 2.0D, 14.0D, 16.0D, 14.0D); + protected static final VoxelShape c = VoxelShapes.a(VoxelShapes.b(), BlockCauldron.b, OperatorBoolean.ONLY_FIRST); + + public BlockCauldron(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockCauldron.LEVEL, 0)); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockCauldron.c; + } + + public boolean f(IBlockData iblockdata) { + return false; + } + + public VoxelShape h(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockCauldron.b; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) { + int i = (Integer) iblockdata.get(BlockCauldron.LEVEL); + float f = (float) blockposition.getY() + (6.0F + (float) (3 * i)) / 16.0F; + + if (!world.isClientSide && entity.isBurning() && i > 0 && entity.getBoundingBox().minY <= (double) f) { + // CraftBukkit start + if (!this.changeLevel(world, blockposition, iblockdata, i - 1, entity, CauldronLevelChangeEvent.ChangeReason.EXTINGUISH)) { + return; + } + entity.extinguish(); + // this.a(world, blockposition, iblockdata, i - 1); + // CraftBukkit end + } + + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (itemstack.isEmpty()) { + return true; + } else { + int i = (Integer) iblockdata.get(BlockCauldron.LEVEL); + Item item = itemstack.getItem(); + + if (item == Items.WATER_BUCKET) { + if (i < 3 && !world.isClientSide) { + // CraftBukkit start + if (!this.changeLevel(world, blockposition, iblockdata, 3, entityhuman, CauldronLevelChangeEvent.ChangeReason.BUCKET_EMPTY)) { + return true; + } + if (!entityhuman.abilities.canInstantlyBuild) { + entityhuman.a(enumhand, new ItemStack(Items.BUCKET)); + } + + entityhuman.a(StatisticList.FILL_CAULDRON); + // this.a(world, blockposition, iblockdata, 3); + // CraftBukkit end + world.a((EntityHuman) null, blockposition, SoundEffects.ITEM_BUCKET_EMPTY, SoundCategory.BLOCKS, 1.0F, 1.0F); + } + + return true; + } else if (item == Items.BUCKET) { + if (i == 3 && !world.isClientSide) { + // CraftBukkit start + if (!this.changeLevel(world, blockposition, iblockdata, 0, entityhuman, CauldronLevelChangeEvent.ChangeReason.BUCKET_FILL)) { + return true; + } + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + if (itemstack.isEmpty()) { + entityhuman.a(enumhand, new ItemStack(Items.WATER_BUCKET)); + } else if (!entityhuman.inventory.pickup(new ItemStack(Items.WATER_BUCKET))) { + entityhuman.drop(new ItemStack(Items.WATER_BUCKET), false); + } + } + + entityhuman.a(StatisticList.USE_CAULDRON); + // this.a(world, blockposition, iblockdata, 0); + // CraftBukkit end + world.a((EntityHuman) null, blockposition, SoundEffects.ITEM_BUCKET_FILL, SoundCategory.BLOCKS, 1.0F, 1.0F); + } + + return true; + } else { + ItemStack itemstack1; + + if (item == Items.GLASS_BOTTLE) { + if (i > 0 && !world.isClientSide) { + // CraftBukkit start + if (!this.changeLevel(world, blockposition, iblockdata, i - 1, entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_FILL)) { + return true; + } + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack1 = PotionUtil.a(new ItemStack(Items.POTION), Potions.b); + entityhuman.a(StatisticList.USE_CAULDRON); + itemstack.subtract(1); + if (itemstack.isEmpty()) { + entityhuman.a(enumhand, itemstack1); + } else if (!entityhuman.inventory.pickup(itemstack1)) { + entityhuman.drop(itemstack1, false); + } else if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).updateInventory(entityhuman.defaultContainer); + } + } + + world.a((EntityHuman) null, blockposition, SoundEffects.ITEM_BOTTLE_FILL, SoundCategory.BLOCKS, 1.0F, 1.0F); + // this.a(world, blockposition, iblockdata, i - 1); + // CraftBukkit end + } + + return true; + } else if (item == Items.POTION && PotionUtil.d(itemstack) == Potions.b) { + if (i < 3 && !world.isClientSide) { + // CraftBukkit start + if (!this.changeLevel(world, blockposition, iblockdata, i + 1, entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY)) { + return true; + } + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack1 = new ItemStack(Items.GLASS_BOTTLE); + entityhuman.a(StatisticList.USE_CAULDRON); + entityhuman.a(enumhand, itemstack1); + if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).updateInventory(entityhuman.defaultContainer); + } + } + + world.a((EntityHuman) null, blockposition, SoundEffects.ITEM_BOTTLE_EMPTY, SoundCategory.BLOCKS, 1.0F, 1.0F); + // this.a(world, blockposition, iblockdata, i + 1); + // CraftBukkit end + } + + return true; + } else { + if (i > 0 && item instanceof ItemArmorColorable) { + ItemArmorColorable itemarmorcolorable = (ItemArmorColorable) item; + + if (itemarmorcolorable.e(itemstack) && !world.isClientSide) { + // CraftBukkit start + if (!this.changeLevel(world, blockposition, iblockdata, i - 1, entityhuman, CauldronLevelChangeEvent.ChangeReason.ARMOR_WASH)) { + return true; + } + itemarmorcolorable.g(itemstack); + // this.a(world, blockposition, iblockdata, i - 1); + // CraftBukkit end + entityhuman.a(StatisticList.CLEAN_ARMOR); + return true; + } + } + + if (i > 0 && item instanceof ItemBanner) { + if (TileEntityBanner.a(itemstack) > 0 && !world.isClientSide) { + // CraftBukkit start + if (!this.changeLevel(world, blockposition, iblockdata, i - 1, entityhuman, CauldronLevelChangeEvent.ChangeReason.BANNER_WASH)) { + return true; + } + itemstack1 = itemstack.cloneItemStack(); + itemstack1.setCount(1); + TileEntityBanner.b(itemstack1); + entityhuman.a(StatisticList.CLEAN_BANNER); + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + // this.a(world, blockposition, iblockdata, i - 1); + // CraftBukkit end + } + + if (itemstack.isEmpty()) { + entityhuman.a(enumhand, itemstack1); + } else if (!entityhuman.inventory.pickup(itemstack1)) { + entityhuman.drop(itemstack1, false); + } else if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).updateInventory(entityhuman.defaultContainer); + } + } + + return true; + } else if (i > 0 && item instanceof ItemBlock) { + Block block = ((ItemBlock) item).getBlock(); + + if (block instanceof BlockShulkerBox && !world.e()) { + ItemStack itemstack2 = new ItemStack(Blocks.SHULKER_BOX, 1); + + if (itemstack.hasTag()) { + itemstack2.setTag(itemstack.getTag().clone()); + } + + entityhuman.a(enumhand, itemstack2); + this.a(world, blockposition, iblockdata, i - 1); + entityhuman.a(StatisticList.CLEAN_SHULKER_BOX); + } + + return true; + } else { + return false; + } + } + } + } + } + + // CraftBukkit start + public void a(World world, BlockPosition blockposition, IBlockData iblockdata, int i) { + this.changeLevel(world, blockposition, iblockdata, i, null, CauldronLevelChangeEvent.ChangeReason.UNKNOWN); + } + + private boolean changeLevel(World world, BlockPosition blockposition, IBlockData iblockdata, int i, Entity entity, CauldronLevelChangeEvent.ChangeReason reason) { + int newLevel = Integer.valueOf(MathHelper.clamp(i, 0, 3)); + CauldronLevelChangeEvent event = new CauldronLevelChangeEvent( + world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), + (entity == null) ? null : entity.getBukkitEntity(), reason, iblockdata.get(BlockCauldron.LEVEL), newLevel + ); + world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockCauldron.LEVEL, event.getNewLevel()), 2); + world.updateAdjacentComparators(blockposition, this); + return true; + // CraftBukkit end + } + + public void c(World world, BlockPosition blockposition) { + if (world.random.nextInt(20) == 1) { + float f = world.getBiome(blockposition).getAdjustedTemperature(blockposition); + + if (f >= 0.15F) { + IBlockData iblockdata = world.getType(blockposition); + + if ((Integer) iblockdata.get(BlockCauldron.LEVEL) < 3) { + this.a(world, blockposition, (IBlockData) iblockdata.a((IBlockState) BlockCauldron.LEVEL), 2); // CraftBukkit + } + + } + } + } + + public boolean isComplexRedstone(IBlockData iblockdata) { + return true; + } + + public int a(IBlockData iblockdata, World world, BlockPosition blockposition) { + return (Integer) iblockdata.get(BlockCauldron.LEVEL); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockCauldron.LEVEL); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return enumdirection == EnumDirection.UP ? EnumBlockFaceShape.BOWL : (enumdirection == EnumDirection.DOWN ? EnumBlockFaceShape.UNDEFINED : EnumBlockFaceShape.SOLID); + } + + public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/BlockChest.java b/src/main/java/net/minecraft/server/BlockChest.java new file mode 100644 index 000000000000..c61721bc3d05 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockChest.java @@ -0,0 +1,289 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; + +public class BlockChest extends BlockTileEntity implements IFluidSource, IFluidContainer { + + public static final BlockStateDirection FACING = BlockFacingHorizontal.FACING; + public static final BlockStateEnum b = BlockProperties.ap; + public static final BlockStateBoolean c = BlockProperties.y; + protected static final VoxelShape o = Block.a(1.0D, 0.0D, 0.0D, 15.0D, 14.0D, 15.0D); + protected static final VoxelShape p = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 14.0D, 16.0D); + protected static final VoxelShape q = Block.a(0.0D, 0.0D, 1.0D, 15.0D, 14.0D, 15.0D); + protected static final VoxelShape r = Block.a(1.0D, 0.0D, 1.0D, 16.0D, 14.0D, 15.0D); + protected static final VoxelShape s = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 14.0D, 15.0D); + + protected BlockChest(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockChest.FACING, EnumDirection.NORTH)).set(BlockChest.b, BlockPropertyChestType.SINGLE)).set(BlockChest.c, false)); + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public EnumRenderType c(IBlockData iblockdata) { + return EnumRenderType.ENTITYBLOCK_ANIMATED; + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + if ((Boolean) iblockdata.get(BlockChest.c)) { + generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess)); + } + + if (iblockdata1.getBlock() == this && enumdirection.k().c()) { + BlockPropertyChestType blockpropertychesttype = (BlockPropertyChestType) iblockdata1.get(BlockChest.b); + + if (iblockdata.get(BlockChest.b) == BlockPropertyChestType.SINGLE && blockpropertychesttype != BlockPropertyChestType.SINGLE && iblockdata.get(BlockChest.FACING) == iblockdata1.get(BlockChest.FACING) && k(iblockdata1) == enumdirection.opposite()) { + return (IBlockData) iblockdata.set(BlockChest.b, blockpropertychesttype.a()); + } + } else if (k(iblockdata) == enumdirection) { + return (IBlockData) iblockdata.set(BlockChest.b, BlockPropertyChestType.SINGLE); + } + + return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + if (iblockdata.get(BlockChest.b) == BlockPropertyChestType.SINGLE) { + return BlockChest.s; + } else { + switch (k(iblockdata)) { + case NORTH: + default: + return BlockChest.o; + case SOUTH: + return BlockChest.p; + case WEST: + return BlockChest.q; + case EAST: + return BlockChest.r; + } + } + } + + public static EnumDirection k(IBlockData iblockdata) { + EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockChest.FACING); + + return iblockdata.get(BlockChest.b) == BlockPropertyChestType.LEFT ? enumdirection.e() : enumdirection.f(); + } + + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + BlockPropertyChestType blockpropertychesttype = BlockPropertyChestType.SINGLE; + EnumDirection enumdirection = blockactioncontext.f().opposite(); + Fluid fluid = blockactioncontext.getWorld().getFluid(blockactioncontext.getClickPosition()); + boolean flag = blockactioncontext.isSneaking(); + EnumDirection enumdirection1 = blockactioncontext.getClickedFace(); + + if (enumdirection1.k().c() && flag) { + EnumDirection enumdirection2 = this.a(blockactioncontext, enumdirection1.opposite()); + + if (enumdirection2 != null && enumdirection2.k() != enumdirection1.k()) { + enumdirection = enumdirection2; + blockpropertychesttype = enumdirection2.f() == enumdirection1.opposite() ? BlockPropertyChestType.RIGHT : BlockPropertyChestType.LEFT; + } + } + + if (blockpropertychesttype == BlockPropertyChestType.SINGLE && !flag) { + if (enumdirection == this.a(blockactioncontext, enumdirection.e())) { + blockpropertychesttype = BlockPropertyChestType.LEFT; + } else if (enumdirection == this.a(blockactioncontext, enumdirection.f())) { + blockpropertychesttype = BlockPropertyChestType.RIGHT; + } + } + + return (IBlockData) ((IBlockData) ((IBlockData) this.getBlockData().set(BlockChest.FACING, enumdirection)).set(BlockChest.b, blockpropertychesttype)).set(BlockChest.c, fluid.c() == FluidTypes.WATER); + } + + public FluidType removeFluid(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata) { + if ((Boolean) iblockdata.get(BlockChest.c)) { + generatoraccess.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockChest.c, false), 3); + return FluidTypes.WATER; + } else { + return FluidTypes.EMPTY; + } + } + + public Fluid h(IBlockData iblockdata) { + return (Boolean) iblockdata.get(BlockChest.c) ? FluidTypes.WATER.a(false) : super.h(iblockdata); + } + + public boolean canPlace(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, FluidType fluidtype) { + return !(Boolean) iblockdata.get(BlockChest.c) && fluidtype == FluidTypes.WATER; + } + + public boolean place(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, Fluid fluid) { + if (!(Boolean) iblockdata.get(BlockChest.c) && fluid.c() == FluidTypes.WATER) { + if (!generatoraccess.e()) { + generatoraccess.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockChest.c, true), 3); + generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess)); + } + + return true; + } else { + return false; + } + } + + @Nullable + private EnumDirection a(BlockActionContext blockactioncontext, EnumDirection enumdirection) { + IBlockData iblockdata = blockactioncontext.getWorld().getType(blockactioncontext.getClickPosition().shift(enumdirection)); + + return iblockdata.getBlock() == this && iblockdata.get(BlockChest.b) == BlockPropertyChestType.SINGLE ? (EnumDirection) iblockdata.get(BlockChest.FACING) : null; + } + + public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) { + if (itemstack.hasName()) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityChest) { + ((TileEntityChest) tileentity).setCustomName(itemstack.getName()); + } + } + + } + + public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) { + if (iblockdata.getBlock() != iblockdata1.getBlock()) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof IInventory) { + InventoryUtils.dropInventory(world, blockposition, (IInventory) tileentity); + world.updateAdjacentComparators(blockposition, this); + } + + super.remove(iblockdata, world, blockposition, iblockdata1, flag); + } + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + if (world.isClientSide) { + return true; + } else { + ITileInventory itileinventory = this.getInventory(iblockdata, world, blockposition, false); + + if (itileinventory != null) { + entityhuman.openContainer(itileinventory); + entityhuman.b(this.d()); + } + + return true; + } + } + + protected Statistic d() { + return StatisticList.CUSTOM.b(StatisticList.OPEN_CHEST); + } + + @Nullable + public ITileInventory getInventory(IBlockData iblockdata, World world, BlockPosition blockposition, boolean flag) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (!(tileentity instanceof TileEntityChest)) { + return null; + } else if (!flag && this.a(world, blockposition)) { + return null; + } else { + Object object = (TileEntityChest) tileentity; + BlockPropertyChestType blockpropertychesttype = (BlockPropertyChestType) iblockdata.get(BlockChest.b); + + if (blockpropertychesttype == BlockPropertyChestType.SINGLE) { + return (ITileInventory) object; + } else { + BlockPosition blockposition1 = blockposition.shift(k(iblockdata)); + // Paper start - don't load chunks if the other side of the chest is in unloaded chunk + final IBlockData iblockdata1 = world.getTypeIfLoaded(blockposition1); // Paper + if (iblockdata1 == null) { + return null; + } + // Paper end + + if (iblockdata1.getBlock() == this) { + BlockPropertyChestType blockpropertychesttype1 = (BlockPropertyChestType) iblockdata1.get(BlockChest.b); + + if (blockpropertychesttype1 != BlockPropertyChestType.SINGLE && blockpropertychesttype != blockpropertychesttype1 && iblockdata1.get(BlockChest.FACING) == iblockdata.get(BlockChest.FACING)) { + if (!flag && this.a(world, blockposition1)) { + return null; + } + + TileEntity tileentity1 = world.getTileEntity(blockposition1); + + if (tileentity1 instanceof TileEntityChest) { + Object object1 = blockpropertychesttype == BlockPropertyChestType.RIGHT ? object : (ITileInventory) tileentity1; + Object object2 = blockpropertychesttype == BlockPropertyChestType.RIGHT ? (ITileInventory) tileentity1 : object; + + object = new InventoryLargeChest(new ChatMessage("container.chestDouble", new Object[0]), (ITileInventory) object1, (ITileInventory) object2); + } + } + } + + return (ITileInventory) object; + } + } + } + + public TileEntity a(IBlockAccess iblockaccess) { + return new TileEntityChest(); + } + + private boolean a(World world, BlockPosition blockposition) { + return this.a((IBlockAccess) world, blockposition) || this.b(world, blockposition); + } + + private boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) { + return iblockaccess.getType(blockposition.up()).isOccluding(); + } + + private boolean b(World world, BlockPosition blockposition) { + // Paper start - Option to disable chest cat detection + if (world.paperConfig.disableChestCatDetection) { + return false; + } + // Paper end + List list = world.a(EntityOcelot.class, new AxisAlignedBB((double) blockposition.getX(), (double) (blockposition.getY() + 1), (double) blockposition.getZ(), (double) (blockposition.getX() + 1), (double) (blockposition.getY() + 2), (double) (blockposition.getZ() + 1))); + + if (!list.isEmpty()) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityOcelot entityocelot = (EntityOcelot) iterator.next(); + + if (entityocelot.isSitting()) { + return true; + } + } + } + + return false; + } + + public boolean isComplexRedstone(IBlockData iblockdata) { + return true; + } + + public int a(IBlockData iblockdata, World world, BlockPosition blockposition) { + return Container.b((IInventory) this.getInventory(iblockdata, world, blockposition, false)); + } + + public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) { + return (IBlockData) iblockdata.set(BlockChest.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockChest.FACING))); + } + + public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) { + return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockChest.FACING))); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockChest.FACING, BlockChest.b, BlockChest.c); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } + + public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/BlockChorusFlower.java b/src/main/java/net/minecraft/server/BlockChorusFlower.java new file mode 100644 index 000000000000..5063c94c0221 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockChorusFlower.java @@ -0,0 +1,259 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.Random; +import javax.annotation.Nullable; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockChorusFlower extends Block { + + public static final BlockStateInteger AGE = BlockProperties.V; + private final BlockChorusFruit b; + + protected BlockChorusFlower(BlockChorusFruit blockchorusfruit, Block.Info block_info) { + super(block_info); + this.b = blockchorusfruit; + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockChorusFlower.AGE, 0)); + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return Items.AIR; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!iblockdata.canPlace(world, blockposition)) { + world.setAir(blockposition, true); + } else { + BlockPosition blockposition1 = blockposition.up(); + + if (world.isEmpty(blockposition1) && blockposition1.getY() < 256) { + int i = (Integer) iblockdata.get(BlockChorusFlower.AGE); + + if (i < 5) { + boolean flag = false; + boolean flag1 = false; + IBlockData iblockdata1 = world.getType(blockposition.down()); + Block block = iblockdata1.getBlock(); + int j; + + if (block == Blocks.END_STONE) { + flag = true; + } else if (block == this.b) { + j = 1; + + for (int k = 0; k < 4; ++k) { + Block block1 = world.getType(blockposition.down(j + 1)).getBlock(); + + if (block1 != this.b) { + if (block1 == Blocks.END_STONE) { + flag1 = true; + } + break; + } + + ++j; + } + + if (j < 2 || j <= random.nextInt(flag1 ? 5 : 4)) { + flag = true; + } + } else if (iblockdata1.isAir()) { + flag = true; + } + + if (flag && a((IWorldReader) world, blockposition1, (EnumDirection) null) && world.isEmpty(blockposition.up(2))) { + // CraftBukkit start - add event + if (CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition1, this.getBlockData().set(BlockChorusFlower.AGE, Integer.valueOf(i)), 2)) { + world.setTypeAndData(blockposition, this.b.a((IBlockAccess) world, blockposition), 2); + this.b(world, blockposition1, i); + } + // CraftBukkit end + } else if (i < 4) { + j = random.nextInt(4); + if (flag1) { + ++j; + } + + boolean flag2 = false; + + for (int l = 0; l < j; ++l) { + EnumDirection enumdirection = EnumDirection.EnumDirectionLimit.HORIZONTAL.a(random); + BlockPosition blockposition2 = blockposition.shift(enumdirection); + + if (world.isEmpty(blockposition2) && world.isEmpty(blockposition2.down()) && a((IWorldReader) world, blockposition2, enumdirection.opposite())) { + // CraftBukkit start - add event + if (CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition2, this.getBlockData().set(BlockChorusFlower.AGE, Integer.valueOf(i + 1)), 2)) { + this.b(world, blockposition2, i + 1); + flag2 = true; + } + // CraftBukkit end + } + } + + if (flag2) { + world.setTypeAndData(blockposition, this.b.a((IBlockAccess) world, blockposition), 2); + } else { + // CraftBukkit - add event + if (CraftEventFactory.handleBlockGrowEvent(world, blockposition, this.getBlockData().set(BlockChorusFlower.AGE, Integer.valueOf(5)), 2)) { + this.a(world, blockposition); + } + // CraftBukkit end + } + } else { + // CraftBukkit - add event + if (CraftEventFactory.handleBlockGrowEvent(world, blockposition, this.getBlockData().set(BlockChorusFlower.AGE, Integer.valueOf(5)), 2)) { + this.a(world, blockposition); + } + // CraftBukkit end + } + + } + } + } + } + + private void b(World world, BlockPosition blockposition, int i) { + world.setTypeAndData(blockposition, (IBlockData) this.getBlockData().set(BlockChorusFlower.AGE, i), 2); + world.triggerEffect(1033, blockposition, 0); + } + + private void a(World world, BlockPosition blockposition) { + world.setTypeAndData(blockposition, (IBlockData) this.getBlockData().set(BlockChorusFlower.AGE, 5), 2); + world.triggerEffect(1034, blockposition, 0); + } + + private static boolean a(IWorldReader iworldreader, BlockPosition blockposition, @Nullable EnumDirection enumdirection) { + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + EnumDirection enumdirection1; + + do { + if (!iterator.hasNext()) { + return true; + } + + enumdirection1 = (EnumDirection) iterator.next(); + } while (enumdirection1 == enumdirection || iworldreader.isEmpty(blockposition.shift(enumdirection1))); + + return false; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + if (enumdirection != EnumDirection.UP && !iblockdata.canPlace(generatoraccess, blockposition)) { + generatoraccess.getBlockTickList().a(blockposition, this, 1); + } + + return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + IBlockData iblockdata1 = iworldreader.getType(blockposition.down()); + Block block = iblockdata1.getBlock(); + + if (block != this.b && block != Blocks.END_STONE) { + if (!iblockdata1.isAir()) { + return false; + } else { + boolean flag = false; + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + IBlockData iblockdata2 = iworldreader.getType(blockposition.shift(enumdirection)); + + if (iblockdata2.getBlock() == this.b) { + if (flag) { + return false; + } + + flag = true; + } else if (!iblockdata2.isAir()) { + return false; + } + } + + return flag; + } + } else { + return true; + } + } + + public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) { + super.a(world, entityhuman, blockposition, iblockdata, tileentity, itemstack); + a(world, blockposition, new ItemStack(this)); + } + + protected ItemStack t(IBlockData iblockdata) { + return ItemStack.a; + } + + public TextureType c() { + return TextureType.CUTOUT; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockChorusFlower.AGE); + } + + public static void a(GeneratorAccess generatoraccess, BlockPosition blockposition, Random random, int i) { + generatoraccess.setTypeAndData(blockposition, ((BlockChorusFruit) Blocks.CHORUS_PLANT).a((IBlockAccess) generatoraccess, blockposition), 2); + a(generatoraccess, blockposition, random, blockposition, i, 0); + } + + private static void a(GeneratorAccess generatoraccess, BlockPosition blockposition, Random random, BlockPosition blockposition1, int i, int j) { + BlockChorusFruit blockchorusfruit = (BlockChorusFruit) Blocks.CHORUS_PLANT; + int k = random.nextInt(4) + 1; + + if (j == 0) { + ++k; + } + + for (int l = 0; l < k; ++l) { + BlockPosition blockposition2 = blockposition.up(l + 1); + + if (!a((IWorldReader) generatoraccess, blockposition2, (EnumDirection) null)) { + return; + } + + generatoraccess.setTypeAndData(blockposition2, blockchorusfruit.a((IBlockAccess) generatoraccess, blockposition2), 2); + generatoraccess.setTypeAndData(blockposition2.down(), blockchorusfruit.a((IBlockAccess) generatoraccess, blockposition2.down()), 2); + } + + boolean flag = false; + + if (j < 4) { + int i1 = random.nextInt(4); + + if (j == 0) { + ++i1; + } + + for (int j1 = 0; j1 < i1; ++j1) { + EnumDirection enumdirection = EnumDirection.EnumDirectionLimit.HORIZONTAL.a(random); + BlockPosition blockposition3 = blockposition.up(k).shift(enumdirection); + + if (Math.abs(blockposition3.getX() - blockposition1.getX()) < i && Math.abs(blockposition3.getZ() - blockposition1.getZ()) < i && generatoraccess.isEmpty(blockposition3) && generatoraccess.isEmpty(blockposition3.down()) && a((IWorldReader) generatoraccess, blockposition3, enumdirection.opposite())) { + flag = true; + generatoraccess.setTypeAndData(blockposition3, blockchorusfruit.a((IBlockAccess) generatoraccess, blockposition3), 2); + generatoraccess.setTypeAndData(blockposition3.shift(enumdirection.opposite()), blockchorusfruit.a((IBlockAccess) generatoraccess, blockposition3.shift(enumdirection.opposite())), 2); + a(generatoraccess, blockposition3, random, blockposition1, i, j + 1); + } + } + } + + if (!flag) { + generatoraccess.setTypeAndData(blockposition.up(k), (IBlockData) Blocks.CHORUS_FLOWER.getBlockData().set(BlockChorusFlower.AGE, 5), 2); + } + + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } +} diff --git a/src/main/java/net/minecraft/server/BlockCocoa.java b/src/main/java/net/minecraft/server/BlockCocoa.java new file mode 100644 index 000000000000..a69b2f0431b5 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockCocoa.java @@ -0,0 +1,125 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockCocoa extends BlockFacingHorizontal implements IBlockFragilePlantElement { + + public static final BlockStateInteger AGE = BlockProperties.T; + protected static final VoxelShape[] b = new VoxelShape[] { Block.a(11.0D, 7.0D, 6.0D, 15.0D, 12.0D, 10.0D), Block.a(9.0D, 5.0D, 5.0D, 15.0D, 12.0D, 11.0D), Block.a(7.0D, 3.0D, 4.0D, 15.0D, 12.0D, 12.0D)}; + protected static final VoxelShape[] c = new VoxelShape[] { Block.a(1.0D, 7.0D, 6.0D, 5.0D, 12.0D, 10.0D), Block.a(1.0D, 5.0D, 5.0D, 7.0D, 12.0D, 11.0D), Block.a(1.0D, 3.0D, 4.0D, 9.0D, 12.0D, 12.0D)}; + protected static final VoxelShape[] o = new VoxelShape[] { Block.a(6.0D, 7.0D, 1.0D, 10.0D, 12.0D, 5.0D), Block.a(5.0D, 5.0D, 1.0D, 11.0D, 12.0D, 7.0D), Block.a(4.0D, 3.0D, 1.0D, 12.0D, 12.0D, 9.0D)}; + protected static final VoxelShape[] p = new VoxelShape[] { Block.a(6.0D, 7.0D, 11.0D, 10.0D, 12.0D, 15.0D), Block.a(5.0D, 5.0D, 9.0D, 11.0D, 12.0D, 15.0D), Block.a(4.0D, 3.0D, 7.0D, 12.0D, 12.0D, 15.0D)}; + + public BlockCocoa(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockCocoa.FACING, EnumDirection.NORTH)).set(BlockCocoa.AGE, 0)); + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (world.random.nextInt(Math.max(1, (int) (100.0F / world.spigotConfig.cocoaModifier) * 5)) == 0) { // Spigot + int i = (Integer) iblockdata.get(BlockCocoa.AGE); + + if (i < 2) { + CraftEventFactory.handleBlockGrowEvent(world, blockposition, (IBlockData) iblockdata.set(BlockCocoa.AGE, i + 1), 2); // CraftBukkkit + } + } + + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + Block block = iworldreader.getType(blockposition.shift((EnumDirection) iblockdata.get(BlockCocoa.FACING))).getBlock(); + + return block.a(TagsBlock.JUNGLE_LOGS); + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + int i = (Integer) iblockdata.get(BlockCocoa.AGE); + + switch ((EnumDirection) iblockdata.get(BlockCocoa.FACING)) { + case SOUTH: + return BlockCocoa.p[i]; + case NORTH: + default: + return BlockCocoa.o[i]; + case WEST: + return BlockCocoa.c[i]; + case EAST: + return BlockCocoa.b[i]; + } + } + + @Nullable + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + IBlockData iblockdata = this.getBlockData(); + World world = blockactioncontext.getWorld(); + BlockPosition blockposition = blockactioncontext.getClickPosition(); + EnumDirection[] aenumdirection = blockactioncontext.e(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + + if (enumdirection.k().c()) { + iblockdata = (IBlockData) iblockdata.set(BlockCocoa.FACING, enumdirection); + if (iblockdata.canPlace(world, blockposition)) { + return iblockdata; + } + } + } + + return null; + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + return enumdirection == iblockdata.get(BlockCocoa.FACING) && !iblockdata.canPlace(generatoraccess, blockposition) ? Blocks.AIR.getBlockData() : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) { + int j = (Integer) iblockdata.get(BlockCocoa.AGE); + byte b0 = 1; + + if (j >= 2) { + b0 = 3; + } + + for (int k = 0; k < b0; ++k) { + a(world, blockposition, new ItemStack(Items.COCOA_BEANS)); + } + + } + + public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) { + return new ItemStack(Items.COCOA_BEANS); + } + + public boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, boolean flag) { + return (Integer) iblockdata.get(BlockCocoa.AGE) < 2; + } + + public boolean a(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) { + return true; + } + + public void b(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) { + CraftEventFactory.handleBlockGrowEvent(world, blockposition, (IBlockData) iblockdata.set(BlockCocoa.AGE, (Integer) iblockdata.get(BlockCocoa.AGE) + 1), 2); // CraftBukkit + } + + public TextureType c() { + return TextureType.CUTOUT; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockCocoa.FACING, BlockCocoa.AGE); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } +} diff --git a/src/main/java/net/minecraft/server/BlockCommand.java b/src/main/java/net/minecraft/server/BlockCommand.java new file mode 100644 index 000000000000..ca1cc4091d82 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockCommand.java @@ -0,0 +1,229 @@ +package net.minecraft.server; + +import java.util.Random; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public class BlockCommand extends BlockTileEntity { + + private static final Logger c = LogManager.getLogger(); + public static final BlockStateDirection a = BlockDirectional.FACING; + public static final BlockStateBoolean b = BlockProperties.b; + + public BlockCommand(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockCommand.a, EnumDirection.NORTH)).set(BlockCommand.b, false)); + } + + public TileEntity a(IBlockAccess iblockaccess) { + TileEntityCommand tileentitycommand = new TileEntityCommand(); + + tileentitycommand.b(this == Blocks.CHAIN_COMMAND_BLOCK); + return tileentitycommand; + } + + public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + if (!world.isClientSide) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityCommand) { + TileEntityCommand tileentitycommand = (TileEntityCommand) tileentity; + boolean flag = world.isBlockIndirectlyPowered(blockposition); + boolean flag1 = tileentitycommand.d(); + // CraftBukkit start + org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + int old = flag1 ? 15 : 0; + int current = flag ? 15 : 0; + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, old, current); + world.getServer().getPluginManager().callEvent(eventRedstone); + flag = eventRedstone.getNewCurrent() > 0; + // CraftBukkit end + + tileentitycommand.a(flag); + if (!flag1 && !tileentitycommand.e() && tileentitycommand.j() != TileEntityCommand.Type.SEQUENCE) { + if (flag) { + tileentitycommand.h(); + world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world)); + } + + } + } + } + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!world.isClientSide) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityCommand) { + TileEntityCommand tileentitycommand = (TileEntityCommand) tileentity; + CommandBlockListenerAbstract commandblocklistenerabstract = tileentitycommand.getCommandBlock(); + boolean flag = !UtilColor.b(commandblocklistenerabstract.getCommand()); + TileEntityCommand.Type tileentitycommand_type = tileentitycommand.j(); + boolean flag1 = tileentitycommand.f(); + + if (tileentitycommand_type == TileEntityCommand.Type.AUTO) { + tileentitycommand.h(); + if (flag1) { + this.a(iblockdata, world, blockposition, commandblocklistenerabstract, flag); + } else if (tileentitycommand.k()) { + commandblocklistenerabstract.a(0); + } + + if (tileentitycommand.d() || tileentitycommand.e()) { + world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world)); + } + } else if (tileentitycommand_type == TileEntityCommand.Type.REDSTONE) { + if (flag1) { + this.a(iblockdata, world, blockposition, commandblocklistenerabstract, flag); + } else if (tileentitycommand.k()) { + commandblocklistenerabstract.a(0); + } + } + + world.updateAdjacentComparators(blockposition, this); + } + + } + } + + private void a(IBlockData iblockdata, World world, BlockPosition blockposition, CommandBlockListenerAbstract commandblocklistenerabstract, boolean flag) { + if (flag) { + commandblocklistenerabstract.a(world); + } else { + commandblocklistenerabstract.a(0); + } + + a(world, blockposition, (EnumDirection) iblockdata.get(BlockCommand.a)); + } + + public int a(IWorldReader iworldreader) { + return 1; + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityCommand && entityhuman.isCreativeAndOp()) { + entityhuman.a((TileEntityCommand) tileentity); + return true; + } else { + return false; + } + } + + public boolean isComplexRedstone(IBlockData iblockdata) { + return true; + } + + public int a(IBlockData iblockdata, World world, BlockPosition blockposition) { + TileEntity tileentity = world.getTileEntity(blockposition); + + return tileentity instanceof TileEntityCommand ? ((TileEntityCommand) tileentity).getCommandBlock().i() : 0; + } + + public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityCommand) { + TileEntityCommand tileentitycommand = (TileEntityCommand) tileentity; + CommandBlockListenerAbstract commandblocklistenerabstract = tileentitycommand.getCommandBlock(); + + if (itemstack.hasName()) { + commandblocklistenerabstract.setName(itemstack.getName()); + } + + if (!world.isClientSide) { + if (itemstack.b("BlockEntityTag") == null) { + commandblocklistenerabstract.a(world.getGameRules().getBoolean("sendCommandFeedback")); + tileentitycommand.b(this == Blocks.CHAIN_COMMAND_BLOCK); + } + + if (tileentitycommand.j() == TileEntityCommand.Type.SEQUENCE) { + boolean flag = world.isBlockIndirectlyPowered(blockposition); + + tileentitycommand.a(flag); + } + } + + } + } + + public int a(IBlockData iblockdata, Random random) { + return 0; + } + + public EnumRenderType c(IBlockData iblockdata) { + return EnumRenderType.MODEL; + } + + public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) { + return (IBlockData) iblockdata.set(BlockCommand.a, enumblockrotation.a((EnumDirection) iblockdata.get(BlockCommand.a))); + } + + public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) { + return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockCommand.a))); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockCommand.a, BlockCommand.b); + } + + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + return (IBlockData) this.getBlockData().set(BlockCommand.a, blockactioncontext.d().opposite()); + } + + private static void a(World world, BlockPosition blockposition, EnumDirection enumdirection) { + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(blockposition); + GameRules gamerules = world.getGameRules(); + + IBlockData iblockdata; + int i; + + for (i = gamerules.c("maxCommandChainLength"); i-- > 0; enumdirection = (EnumDirection) iblockdata.get(BlockCommand.a)) { + blockposition_mutableblockposition.c(enumdirection); + iblockdata = world.getType(blockposition_mutableblockposition); + Block block = iblockdata.getBlock(); + + if (block != Blocks.CHAIN_COMMAND_BLOCK) { + break; + } + + TileEntity tileentity = world.getTileEntity(blockposition_mutableblockposition); + + if (!(tileentity instanceof TileEntityCommand)) { + break; + } + + TileEntityCommand tileentitycommand = (TileEntityCommand) tileentity; + + if (tileentitycommand.j() != TileEntityCommand.Type.SEQUENCE) { + break; + } + + if (tileentitycommand.d() || tileentitycommand.e()) { + CommandBlockListenerAbstract commandblocklistenerabstract = tileentitycommand.getCommandBlock(); + + if (tileentitycommand.h()) { + if (!commandblocklistenerabstract.a(world)) { + break; + } + + world.updateAdjacentComparators(blockposition_mutableblockposition, block); + } else if (tileentitycommand.k()) { + commandblocklistenerabstract.a(0); + } + } + } + + if (i <= 0) { + int j = Math.max(gamerules.c("maxCommandChainLength"), 0); + + BlockCommand.c.warn("Command Block chain tried to execute more than {} steps!", j); + } + + } +} diff --git a/src/main/java/net/minecraft/server/BlockConcretePowder.java b/src/main/java/net/minecraft/server/BlockConcretePowder.java new file mode 100644 index 000000000000..b071ca114787 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockConcretePowder.java @@ -0,0 +1,92 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.block.CraftBlockState; +import org.bukkit.event.block.BlockFormEvent; +// CraftBukkit end + +public class BlockConcretePowder extends BlockFalling { + + private final IBlockData a; + + public BlockConcretePowder(Block block, Block.Info block_info) { + super(block_info); + this.a = block.getBlockData(); + } + + public void a(World world, BlockPosition blockposition, IBlockData iblockdata, IBlockData iblockdata1) { + if (x(iblockdata1)) { + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(world, blockposition, this.a, 3); // CraftBukkit + } + + } + + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + World world = blockactioncontext.getWorld(); + BlockPosition blockposition = blockactioncontext.getClickPosition(); + + // CraftBukkit start + if (!x(world.getType(blockposition)) && !a((IBlockAccess) world, blockposition)) { + return super.getPlacedState(blockactioncontext); + } + + // TODO: An event factory call for methods like this + CraftBlockState blockState = CraftBlockState.getBlockState(world, blockposition); + blockState.setData(this.a); + + BlockFormEvent event = new BlockFormEvent(blockState.getBlock(), blockState); + world.getMinecraftServer().server.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + return blockState.getHandle(); + } + + return super.getPlacedState(blockactioncontext); + // CraftBukkit end + } + + private static boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) { + boolean flag = false; + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(blockposition); + EnumDirection[] aenumdirection = EnumDirection.values(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + IBlockData iblockdata = iblockaccess.getType(blockposition_mutableblockposition); + + if (enumdirection != EnumDirection.DOWN || x(iblockdata)) { + blockposition_mutableblockposition.g(blockposition).c(enumdirection); + iblockdata = iblockaccess.getType(blockposition_mutableblockposition); + if (x(iblockdata) && !Block.a(iblockdata.getCollisionShape(iblockaccess, blockposition), enumdirection.opposite())) { + flag = true; + break; + } + } + } + + return flag; + } + + private static boolean x(IBlockData iblockdata) { + return iblockdata.s().a(TagsFluid.WATER); + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + // CraftBukkit start + if (a((IBlockAccess) generatoraccess, blockposition)) { + CraftBlockState blockState = CraftBlockState.getBlockState(generatoraccess, blockposition); + blockState.setData(this.a); + + BlockFormEvent event = new BlockFormEvent(blockState.getBlock(), blockState); + generatoraccess.getMinecraftWorld().getMinecraftServer().server.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + return blockState.getHandle(); + } + } + + return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + // CraftBukkit end + } +} diff --git a/src/main/java/net/minecraft/server/BlockCoral.java b/src/main/java/net/minecraft/server/BlockCoral.java new file mode 100644 index 000000000000..66ce0a4d9377 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockCoral.java @@ -0,0 +1,67 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; + +public class BlockCoral extends Block { + + private final Block a; + + public BlockCoral(Block block, Block.Info block_info) { + super(block_info); + this.a = block; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!this.a((IBlockAccess) world, blockposition)) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, this.a.getBlockData()).isCancelled()) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, this.a.getBlockData(), 2); + } + + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + if (!this.a((IBlockAccess) generatoraccess, blockposition)) { + generatoraccess.getBlockTickList().a(blockposition, this, 60 + generatoraccess.m().nextInt(40)); + } + + return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + protected boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) { + EnumDirection[] aenumdirection = EnumDirection.values(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + Fluid fluid = iblockaccess.getFluid(blockposition.shift(enumdirection)); + + if (fluid.a(TagsFluid.WATER)) { + return true; + } + } + + return false; + } + + @Nullable + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + if (!this.a((IBlockAccess) blockactioncontext.getWorld(), blockactioncontext.getClickPosition())) { + blockactioncontext.getWorld().getBlockTickList().a(blockactioncontext.getClickPosition(), this, 60 + blockactioncontext.getWorld().m().nextInt(40)); + } + + return this.getBlockData(); + } + + protected boolean X_() { + return true; + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return this.a; + } +} diff --git a/src/main/java/net/minecraft/server/BlockCoralFan.java b/src/main/java/net/minecraft/server/BlockCoralFan.java new file mode 100644 index 000000000000..6d0c683191b1 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockCoralFan.java @@ -0,0 +1,42 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockCoralFan extends BlockCoralFanAbstract { + + private final Block a; + + protected BlockCoralFan(Block block, Block.Info block_info) { + super(block_info); + this.a = block; + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + this.a(iblockdata, (GeneratorAccess) world, blockposition); + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!b_(iblockdata, world, blockposition)) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, this.a.getBlockData().set(BlockCoralFan.b, false)).isCancelled()) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) this.a.getBlockData().set(BlockCoralFan.b, false), 2); + } + + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + if (enumdirection == EnumDirection.DOWN && !iblockdata.canPlace(generatoraccess, blockposition)) { + return Blocks.AIR.getBlockData(); + } else { + this.a(iblockdata, generatoraccess, blockposition); + if ((Boolean) iblockdata.get(BlockCoralFan.b)) { + generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess)); + } + + return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + } +} diff --git a/src/main/java/net/minecraft/server/BlockCoralFanWall.java b/src/main/java/net/minecraft/server/BlockCoralFanWall.java new file mode 100644 index 000000000000..997746ec83f2 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockCoralFanWall.java @@ -0,0 +1,42 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockCoralFanWall extends BlockCoralFanWallAbstract { + + private final Block c; + + protected BlockCoralFanWall(Block block, Block.Info block_info) { + super(block_info); + this.c = block; + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + this.a(iblockdata, (GeneratorAccess) world, blockposition); + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!b_(iblockdata, world, blockposition)) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, this.c.getBlockData().set(BlockCoralFanWall.b, false).set(BlockCoralFanWall.a, iblockdata.get(BlockCoralFanWall.a))).isCancelled()) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) ((IBlockData) this.c.getBlockData().set(BlockCoralFanWall.b, false)).set(BlockCoralFanWall.a, iblockdata.get(BlockCoralFanWall.a)), 2); + } + + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + if (enumdirection.opposite() == iblockdata.get(BlockCoralFanWall.a) && !iblockdata.canPlace(generatoraccess, blockposition)) { + return Blocks.AIR.getBlockData(); + } else { + if ((Boolean) iblockdata.get(BlockCoralFanWall.b)) { + generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess)); + } + + this.a(iblockdata, generatoraccess, blockposition); + return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + } +} diff --git a/src/main/java/net/minecraft/server/BlockCoralPlant.java b/src/main/java/net/minecraft/server/BlockCoralPlant.java new file mode 100644 index 000000000000..1e6d839884ee --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockCoralPlant.java @@ -0,0 +1,47 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockCoralPlant extends BlockCoralBase { + + private final Block c; + protected static final VoxelShape a = Block.a(2.0D, 0.0D, 2.0D, 14.0D, 15.0D, 14.0D); + + protected BlockCoralPlant(Block block, Block.Info block_info) { + super(block_info); + this.c = block; + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + this.a(iblockdata, (GeneratorAccess) world, blockposition); + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!b_(iblockdata, world, blockposition)) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, this.c.getBlockData().set(BlockCoralPlant.b, false)).isCancelled()) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) this.c.getBlockData().set(BlockCoralPlant.b, false), 2); + } + + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + if (enumdirection == EnumDirection.DOWN && !iblockdata.canPlace(generatoraccess, blockposition)) { + return Blocks.AIR.getBlockData(); + } else { + this.a(iblockdata, generatoraccess, blockposition); + if ((Boolean) iblockdata.get(BlockCoralPlant.b)) { + generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess)); + } + + return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockCoralPlant.a; + } +} diff --git a/src/main/java/net/minecraft/server/BlockCrops.java b/src/main/java/net/minecraft/server/BlockCrops.java new file mode 100644 index 000000000000..9d53f1118c3f --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockCrops.java @@ -0,0 +1,186 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockCrops extends BlockPlant implements IBlockFragilePlantElement { + + public static final BlockStateInteger AGE = BlockProperties.W; + private static final VoxelShape[] a = new VoxelShape[] { Block.a(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 4.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 6.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 10.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 12.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 14.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D)}; + + protected BlockCrops(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(this.d(), 0)); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockCrops.a[(Integer) iblockdata.get(this.d())]; + } + + protected boolean b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return iblockdata.getBlock() == Blocks.FARMLAND; + } + + public BlockStateInteger d() { + return BlockCrops.AGE; + } + + public int e() { + return 7; + } + + protected int k(IBlockData iblockdata) { + return (Integer) iblockdata.get(this.d()); + } + + public IBlockData setAge(int i) { + return (IBlockData) this.getBlockData().set(this.d(), i); + } + + public boolean w(IBlockData iblockdata) { + return (Integer) iblockdata.get(this.d()) >= this.e(); + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + super.a(iblockdata, world, blockposition, random); + if (world.isLightLevel(blockposition.up(), 9)) { // Paper + int i = this.k(iblockdata); + + if (i < this.e()) { + float f = a((Block) this, (IBlockAccess) world, blockposition); + + // Spigot start + int modifier; + if (this == Blocks.BEETROOTS) { + modifier = world.spigotConfig.beetrootModifier; + } else if (this == Blocks.CARROTS) { + modifier = world.spigotConfig.carrotModifier; + } else if (this == Blocks.POTATOES) { + modifier = world.spigotConfig.potatoModifier; + } else { + modifier = world.spigotConfig.wheatModifier; + } + + if (random.nextInt((int) ((100.0F / modifier) * (25.0F / f)) + 1) == 0) { + // Spigot end + CraftEventFactory.handleBlockGrowEvent(world, blockposition, this.setAge(i + 1), 2); // CraftBukkit + } + } + } + + } + + public void a(World world, BlockPosition blockposition, IBlockData iblockdata) { + int i = this.k(iblockdata) + this.a(world); + int j = this.e(); + + if (i > j) { + i = j; + } + + CraftEventFactory.handleBlockGrowEvent(world, blockposition, this.setAge(i), 2); // CraftBukkit + } + + protected int a(World world) { + return MathHelper.nextInt(world.random, 2, 5); + } + + protected static float a(Block block, IBlockAccess iblockaccess, BlockPosition blockposition) { + float f = 1.0F; + BlockPosition blockposition1 = blockposition.down(); + + for (int i = -1; i <= 1; ++i) { + for (int j = -1; j <= 1; ++j) { + float f1 = 0.0F; + IBlockData iblockdata = iblockaccess.getType(blockposition1.a(i, 0, j)); + + if (iblockdata.getBlock() == Blocks.FARMLAND) { + f1 = 1.0F; + if ((Integer) iblockdata.get(BlockSoil.MOISTURE) > 0) { + f1 = 3.0F; + } + } + + if (i != 0 || j != 0) { + f1 /= 4.0F; + } + + f += f1; + } + } + + BlockPosition blockposition2 = blockposition.north(); + BlockPosition blockposition3 = blockposition.south(); + BlockPosition blockposition4 = blockposition.west(); + BlockPosition blockposition5 = blockposition.east(); + boolean flag = block == iblockaccess.getType(blockposition4).getBlock() || block == iblockaccess.getType(blockposition5).getBlock(); + boolean flag1 = block == iblockaccess.getType(blockposition2).getBlock() || block == iblockaccess.getType(blockposition3).getBlock(); + + if (flag && flag1) { + f /= 2.0F; + } else { + boolean flag2 = block == iblockaccess.getType(blockposition4.north()).getBlock() || block == iblockaccess.getType(blockposition5.north()).getBlock() || block == iblockaccess.getType(blockposition5.south()).getBlock() || block == iblockaccess.getType(blockposition4.south()).getBlock(); + + if (flag2) { + f /= 2.0F; + } + } + + return f; + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + return (iworldreader.getLightLevel(blockposition, 0) >= 8 || iworldreader.e(blockposition)) && super.canPlace(iblockdata, iworldreader, blockposition); + } + + protected IMaterial f() { + return Items.WHEAT_SEEDS; + } + + protected IMaterial g() { + return Items.WHEAT; + } + + public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) { + super.dropNaturally(iblockdata, world, blockposition, f, 0); + if (!world.isClientSide) { + int j = this.k(iblockdata); + + if (j >= this.e()) { + int k = 3 + i; + + for (int l = 0; l < k; ++l) { + if (world.random.nextInt(2 * this.e()) <= j) { + a(world, blockposition, new ItemStack(this.f())); + } + } + } + + } + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return this.w(iblockdata) ? this.g() : this.f(); + } + + public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) { + return new ItemStack(this.f()); + } + + public boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, boolean flag) { + return !this.w(iblockdata); + } + + public boolean a(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) { + return true; + } + + public void b(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) { + this.a(world, blockposition, iblockdata); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockCrops.AGE); + } +} diff --git a/src/main/java/net/minecraft/server/BlockData.java b/src/main/java/net/minecraft/server/BlockData.java new file mode 100644 index 000000000000..1b226a77e08c --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockData.java @@ -0,0 +1,25 @@ +package net.minecraft.server; + +import com.google.common.collect.ImmutableMap; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public class BlockData extends BlockDataAbstract implements IBlockData { + + public BlockData(Block block, ImmutableMap, Comparable> immutablemap) { + super(block, immutablemap); + } + + public Block getBlock() { + return (Block) this.e_; + } + + // Paper start - impl cached craft block data, lazy load to fix issue with loading at the wrong time + private CraftBlockData cachedCraftBlockData; + + @Override + public CraftBlockData createCraftBlockData() { + if(cachedCraftBlockData == null) cachedCraftBlockData = CraftBlockData.createData(this); + return (CraftBlockData) cachedCraftBlockData.clone(); + } + // Paper end +} diff --git a/src/main/java/net/minecraft/server/BlockDaylightDetector.java b/src/main/java/net/minecraft/server/BlockDaylightDetector.java new file mode 100644 index 000000000000..cf12f96fb7df --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockDaylightDetector.java @@ -0,0 +1,85 @@ +package net.minecraft.server; + +public class BlockDaylightDetector extends BlockTileEntity { + + public static final BlockStateInteger POWER = BlockProperties.al; + public static final BlockStateBoolean b = BlockProperties.m; + protected static final VoxelShape c = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 6.0D, 16.0D); + + public BlockDaylightDetector(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockDaylightDetector.POWER, 0)).set(BlockDaylightDetector.b, false)); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockDaylightDetector.c; + } + + public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return (Integer) iblockdata.get(BlockDaylightDetector.POWER); + } + + public static void b(IBlockData iblockdata, World world, BlockPosition blockposition) { + if (world.worldProvider.g()) { + int i = world.getBrightness(EnumSkyBlock.SKY, blockposition) - world.c(); + float f = world.c(1.0F); + boolean flag = (Boolean) iblockdata.get(BlockDaylightDetector.b); + + if (flag) { + i = 15 - i; + } else if (i > 0) { + float f1 = f < 3.1415927F ? 0.0F : 6.2831855F; + + f += (f1 - f) * 0.2F; + i = Math.round((float) i * MathHelper.cos(f)); + } + + i = MathHelper.clamp(i, 0, 15); + if ((Integer) iblockdata.get(BlockDaylightDetector.POWER) != i) { + i = org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(world, blockposition, ((Integer) iblockdata.get(POWER)), i).getNewCurrent(); // CraftBukkit - Call BlockRedstoneEvent + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDaylightDetector.POWER, i), 3); + } + + } + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + if (entityhuman.dy()) { + if (world.isClientSide) { + return true; + } else { + IBlockData iblockdata1 = (IBlockData) iblockdata.a((IBlockState) BlockDaylightDetector.b); + + world.setTypeAndData(blockposition, iblockdata1, 4); + b(iblockdata1, world, blockposition); + return true; + } + } else { + return super.interact(iblockdata, world, blockposition, entityhuman, enumhand, enumdirection, f, f1, f2); + } + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public EnumRenderType c(IBlockData iblockdata) { + return EnumRenderType.MODEL; + } + + public boolean isPowerSource(IBlockData iblockdata) { + return true; + } + + public TileEntity a(IBlockAccess iblockaccess) { + return new TileEntityLightDetector(); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockDaylightDetector.POWER, BlockDaylightDetector.b); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return enumdirection == EnumDirection.DOWN ? EnumBlockFaceShape.SOLID : EnumBlockFaceShape.UNDEFINED; + } +} diff --git a/src/main/java/net/minecraft/server/BlockDiodeAbstract.java b/src/main/java/net/minecraft/server/BlockDiodeAbstract.java new file mode 100644 index 000000000000..b09e9fe3a2ec --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockDiodeAbstract.java @@ -0,0 +1,207 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public abstract class BlockDiodeAbstract extends BlockFacingHorizontal { + + protected static final VoxelShape b = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D); + public static final BlockStateBoolean c = BlockProperties.t; + + protected BlockDiodeAbstract(Block.Info block_info) { + super(block_info); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockDiodeAbstract.b; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + return iworldreader.getType(blockposition.down()).q(); + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!this.a((IWorldReader) world, blockposition, iblockdata)) { + boolean flag = (Boolean) iblockdata.get(BlockDiodeAbstract.c); + boolean flag1 = this.a(world, blockposition, iblockdata); + + if (flag && !flag1) { + // CraftBukkit start + if (CraftEventFactory.callRedstoneChange(world, blockposition, 15, 0).getNewCurrent() != 0) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDiodeAbstract.c, false), 2); + } else if (!flag) { + // CraftBukkit start + if (CraftEventFactory.callRedstoneChange(world, blockposition, 0, 15).getNewCurrent() != 15) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDiodeAbstract.c, true), 2); + if (!flag1) { + world.getBlockTickList().a(blockposition, this, this.k(iblockdata), TickListPriority.HIGH); + } + } + + } + } + + public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return iblockdata.a(iblockaccess, blockposition, enumdirection); + } + + public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return !(Boolean) iblockdata.get(BlockDiodeAbstract.c) ? 0 : (iblockdata.get(BlockDiodeAbstract.FACING) == enumdirection ? this.b(iblockaccess, blockposition, iblockdata) : 0); + } + + public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + if (iblockdata.canPlace(world, blockposition)) { + this.c(world, blockposition, iblockdata); + } else { + iblockdata.a(world, blockposition, 0); + world.setAir(blockposition); + EnumDirection[] aenumdirection = EnumDirection.values(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + + world.applyPhysics(blockposition.shift(enumdirection), this); + } + + } + } + + protected void c(World world, BlockPosition blockposition, IBlockData iblockdata) { + if (!this.a((IWorldReader) world, blockposition, iblockdata)) { + boolean flag = (Boolean) iblockdata.get(BlockDiodeAbstract.c); + boolean flag1 = this.a(world, blockposition, iblockdata); + + if (flag != flag1 && !world.getBlockTickList().b(blockposition, this)) { + TickListPriority ticklistpriority = TickListPriority.HIGH; + + if (this.c((IBlockAccess) world, blockposition, iblockdata)) { + ticklistpriority = TickListPriority.EXTREMELY_HIGH; + } else if (flag) { + ticklistpriority = TickListPriority.VERY_HIGH; + } + + world.getBlockTickList().a(blockposition, this, this.k(iblockdata), ticklistpriority); + } + + } + } + + public boolean a(IWorldReader iworldreader, BlockPosition blockposition, IBlockData iblockdata) { + return false; + } + + protected boolean a(World world, BlockPosition blockposition, IBlockData iblockdata) { + return this.b(world, blockposition, iblockdata) > 0; + } + + protected int b(World world, BlockPosition blockposition, IBlockData iblockdata) { + EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockDiodeAbstract.FACING); + BlockPosition blockposition1 = blockposition.shift(enumdirection); + int i = world.getBlockFacePower(blockposition1, enumdirection); + + if (i >= 15) { + return i; + } else { + IBlockData iblockdata1 = world.getType(blockposition1); + + return Math.max(i, iblockdata1.getBlock() == Blocks.REDSTONE_WIRE ? (Integer) iblockdata1.get(BlockRedstoneWire.POWER) : 0); + } + } + + protected int b(IWorldReader iworldreader, BlockPosition blockposition, IBlockData iblockdata) { + EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockDiodeAbstract.FACING); + EnumDirection enumdirection1 = enumdirection.e(); + EnumDirection enumdirection2 = enumdirection.f(); + + return Math.max(this.a(iworldreader, blockposition.shift(enumdirection1), enumdirection1), this.a(iworldreader, blockposition.shift(enumdirection2), enumdirection2)); + } + + protected int a(IWorldReader iworldreader, BlockPosition blockposition, EnumDirection enumdirection) { + IBlockData iblockdata = iworldreader.getType(blockposition); + Block block = iblockdata.getBlock(); + + return this.w(iblockdata) ? (block == Blocks.REDSTONE_BLOCK ? 15 : (block == Blocks.REDSTONE_WIRE ? (Integer) iblockdata.get(BlockRedstoneWire.POWER) : iworldreader.a(blockposition, enumdirection))) : 0; + } + + public boolean isPowerSource(IBlockData iblockdata) { + return true; + } + + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + return (IBlockData) this.getBlockData().set(BlockDiodeAbstract.FACING, blockactioncontext.f().opposite()); + } + + public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) { + if (this.a(world, blockposition, iblockdata)) { + world.getBlockTickList().a(blockposition, this, 1); + } + + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + this.d(world, blockposition, iblockdata); + } + + public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) { + if (!flag && iblockdata.getBlock() != iblockdata1.getBlock()) { + super.remove(iblockdata, world, blockposition, iblockdata1, flag); + this.a(world, blockposition); + this.d(world, blockposition, iblockdata); + } + } + + protected void a(World world, BlockPosition blockposition) {} + + protected void d(World world, BlockPosition blockposition, IBlockData iblockdata) { + EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockDiodeAbstract.FACING); + BlockPosition blockposition1 = blockposition.shift(enumdirection.opposite()); + + world.a(blockposition1, (Block) this, blockposition); + world.a(blockposition1, (Block) this, enumdirection); + } + + protected boolean w(IBlockData iblockdata) { + return iblockdata.isPowerSource(); + } + + protected int b(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) { + return 15; + } + + public static boolean isDiode(IBlockData iblockdata) { + return iblockdata.getBlock() instanceof BlockDiodeAbstract; + } + + public boolean c(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) { + EnumDirection enumdirection = ((EnumDirection) iblockdata.get(BlockDiodeAbstract.FACING)).opposite(); + IBlockData iblockdata1 = iblockaccess.getType(blockposition.shift(enumdirection)); + + return isDiode(iblockdata1) && iblockdata1.get(BlockDiodeAbstract.FACING) != enumdirection; + } + + protected abstract int k(IBlockData iblockdata); + + public TextureType c() { + return TextureType.CUTOUT; + } + + public boolean f(IBlockData iblockdata) { + return true; + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return enumdirection == EnumDirection.DOWN ? EnumBlockFaceShape.SOLID : EnumBlockFaceShape.UNDEFINED; + } +} diff --git a/src/main/java/net/minecraft/server/BlockDirtSnowSpreadable.java b/src/main/java/net/minecraft/server/BlockDirtSnowSpreadable.java new file mode 100644 index 000000000000..f1174825c27d --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockDirtSnowSpreadable.java @@ -0,0 +1,51 @@ +package net.minecraft.server; + +import java.util.Random; + +public abstract class BlockDirtSnowSpreadable extends BlockDirtSnow { + + protected BlockDirtSnowSpreadable(Block.Info block_info) { + super(block_info); + } + + private static boolean a(IWorldReader iworldreader, BlockPosition blockposition) { + BlockPosition blockposition1 = blockposition.up(); + + return iworldreader.getLightLevel(blockposition1) >= 4 || iworldreader.getType(blockposition1).b(iworldreader, blockposition1) < iworldreader.K(); + } + + private static boolean b(IWorldReader iworldreader, BlockPosition blockposition) { + BlockPosition blockposition1 = blockposition.up(); + + return iworldreader.getLightLevel(blockposition1) >= 4 && iworldreader.getType(blockposition1).b(iworldreader, blockposition1) < iworldreader.K() && !iworldreader.getFluid(blockposition1).a(TagsFluid.WATER); + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (this instanceof BlockGrass && world.paperConfig.grassUpdateRate != 1 && (world.paperConfig.grassUpdateRate < 1 || (MinecraftServer.currentTick + blockposition.hashCode()) % world.paperConfig.grassUpdateRate != 0)) { return; } // Paper + if (!world.isClientSide) { + if (!a((IWorldReader) world, blockposition)) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, Blocks.DIRT.getBlockData()).isCancelled()) { + return; + } + // CraftBukkit end + world.setTypeUpdate(blockposition, Blocks.DIRT.getBlockData()); + } else { + if (world.getLightLevel(blockposition.up()) >= 9) { + for (int i = 0; i < 4; ++i) { + BlockPosition blockposition1 = blockposition.a(random.nextInt(3) - 1, random.nextInt(5) - 3, random.nextInt(3) - 1); + + if (!world.p(blockposition1)) { + return; + } + + if (world.getType(blockposition1).getBlock() == Blocks.DIRT && b(world, blockposition1)) { + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition1, this.getBlockData()); // CraftBukkit + } + } + } + + } + } + } +} diff --git a/src/main/java/net/minecraft/server/BlockDispenser.java b/src/main/java/net/minecraft/server/BlockDispenser.java new file mode 100644 index 000000000000..a0b81abafb7f --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockDispenser.java @@ -0,0 +1,155 @@ +package net.minecraft.server; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import java.util.Map; +import java.util.Random; + +public class BlockDispenser extends BlockTileEntity { + + public static final BlockStateDirection FACING = BlockDirectional.FACING; + public static final BlockStateBoolean TRIGGERED = BlockProperties.w; + public static final Map REGISTRY = (Map) SystemUtils.a((new Object2ObjectOpenHashMap()), (object2objectopenhashmap) -> { // CraftBukkit - decompile error + object2objectopenhashmap.defaultReturnValue(new DispenseBehaviorItem()); + }); + public static boolean eventFired = false; // CraftBukkit + + public static void a(IMaterial imaterial, IDispenseBehavior idispensebehavior) { + BlockDispenser.REGISTRY.put(imaterial.getItem(), idispensebehavior); + } + + protected BlockDispenser(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockDispenser.FACING, EnumDirection.NORTH)).set(BlockDispenser.TRIGGERED, false)); + } + + public int a(IWorldReader iworldreader) { + return 4; + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + if (world.isClientSide) { + return true; + } else { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityDispenser) { + entityhuman.openContainer((TileEntityDispenser) tileentity); + if (tileentity instanceof TileEntityDropper) { + entityhuman.a(StatisticList.INSPECT_DROPPER); + } else { + entityhuman.a(StatisticList.INSPECT_DISPENSER); + } + } + + return true; + } + } + + public void dispense(World world, BlockPosition blockposition) { + SourceBlock sourceblock = new SourceBlock(world, blockposition); + TileEntityDispenser tileentitydispenser = (TileEntityDispenser) sourceblock.getTileEntity(); + int i = tileentitydispenser.p(); + + if (i < 0) { + world.triggerEffect(1001, blockposition, 0); + } else { + ItemStack itemstack = tileentitydispenser.getItem(i); + IDispenseBehavior idispensebehavior = this.a(itemstack); + + if (idispensebehavior != IDispenseBehavior.NONE) { + eventFired = false; // CraftBukkit - reset event status + tileentitydispenser.setItem(i, idispensebehavior.dispense(sourceblock, itemstack)); + } + + } + } + + protected IDispenseBehavior a(ItemStack itemstack) { + return (IDispenseBehavior) BlockDispenser.REGISTRY.get(itemstack.getItem()); + } + + public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + boolean flag = world.isBlockIndirectlyPowered(blockposition) || world.isBlockIndirectlyPowered(blockposition.up()); + boolean flag1 = (Boolean) iblockdata.get(BlockDispenser.TRIGGERED); + + if (flag && !flag1) { + world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world)); + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDispenser.TRIGGERED, true), 4); + } else if (!flag && flag1) { + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDispenser.TRIGGERED, false), 4); + } + + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!world.isClientSide) { + this.dispense(world, blockposition); + } + + } + + public TileEntity a(IBlockAccess iblockaccess) { + return new TileEntityDispenser(); + } + + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + return (IBlockData) this.getBlockData().set(BlockDispenser.FACING, blockactioncontext.d().opposite()); + } + + public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) { + if (itemstack.hasName()) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityDispenser) { + ((TileEntityDispenser) tileentity).setCustomName(itemstack.getName()); + } + } + + } + + public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) { + if (iblockdata.getBlock() != iblockdata1.getBlock()) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityDispenser) { + InventoryUtils.dropInventory(world, blockposition, (TileEntityDispenser) tileentity); + world.updateAdjacentComparators(blockposition, this); + } + + super.remove(iblockdata, world, blockposition, iblockdata1, flag); + } + } + + public static IPosition a(ISourceBlock isourceblock) { + EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING); + double d0 = isourceblock.getX() + 0.7D * (double) enumdirection.getAdjacentX(); + double d1 = isourceblock.getY() + 0.7D * (double) enumdirection.getAdjacentY(); + double d2 = isourceblock.getZ() + 0.7D * (double) enumdirection.getAdjacentZ(); + + return new Position(d0, d1, d2); + } + + public boolean isComplexRedstone(IBlockData iblockdata) { + return true; + } + + public int a(IBlockData iblockdata, World world, BlockPosition blockposition) { + return Container.a(world.getTileEntity(blockposition)); + } + + public EnumRenderType c(IBlockData iblockdata) { + return EnumRenderType.MODEL; + } + + public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) { + return (IBlockData) iblockdata.set(BlockDispenser.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockDispenser.FACING))); + } + + public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) { + return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockDispenser.FACING))); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockDispenser.FACING, BlockDispenser.TRIGGERED); + } +} diff --git a/src/main/java/net/minecraft/server/BlockDoor.java b/src/main/java/net/minecraft/server/BlockDoor.java new file mode 100644 index 000000000000..208bd6b1838f --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockDoor.java @@ -0,0 +1,232 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public class BlockDoor extends Block { + + public static final BlockStateDirection FACING = BlockFacingHorizontal.FACING; + public static final BlockStateBoolean OPEN = BlockProperties.r; + public static final BlockStateEnum HINGE = BlockProperties.ar; + public static final BlockStateBoolean POWERED = BlockProperties.t; + public static final BlockStateEnum HALF = BlockProperties.P; + protected static final VoxelShape q = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 3.0D); + protected static final VoxelShape r = Block.a(0.0D, 0.0D, 13.0D, 16.0D, 16.0D, 16.0D); + protected static final VoxelShape s = Block.a(13.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D); + protected static final VoxelShape t = Block.a(0.0D, 0.0D, 0.0D, 3.0D, 16.0D, 16.0D); + + protected BlockDoor(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockDoor.FACING, EnumDirection.NORTH)).set(BlockDoor.OPEN, false)).set(BlockDoor.HINGE, BlockPropertyDoorHinge.LEFT)).set(BlockDoor.POWERED, false)).set(BlockDoor.HALF, BlockPropertyDoubleBlockHalf.LOWER)); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockDoor.FACING); + boolean flag = !(Boolean) iblockdata.get(BlockDoor.OPEN); + boolean flag1 = iblockdata.get(BlockDoor.HINGE) == BlockPropertyDoorHinge.RIGHT; + + switch (enumdirection) { + case EAST: + default: + return flag ? BlockDoor.t : (flag1 ? BlockDoor.r : BlockDoor.q); + case SOUTH: + return flag ? BlockDoor.q : (flag1 ? BlockDoor.t : BlockDoor.s); + case WEST: + return flag ? BlockDoor.s : (flag1 ? BlockDoor.q : BlockDoor.r); + case NORTH: + return flag ? BlockDoor.r : (flag1 ? BlockDoor.s : BlockDoor.t); + } + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + BlockPropertyDoubleBlockHalf blockpropertydoubleblockhalf = (BlockPropertyDoubleBlockHalf) iblockdata.get(BlockDoor.HALF); + + return enumdirection.k() == EnumDirection.EnumAxis.Y && blockpropertydoubleblockhalf == BlockPropertyDoubleBlockHalf.LOWER == (enumdirection == EnumDirection.UP) ? (iblockdata1.getBlock() == this && iblockdata1.get(BlockDoor.HALF) != blockpropertydoubleblockhalf ? (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) iblockdata.set(BlockDoor.FACING, iblockdata1.get(BlockDoor.FACING))).set(BlockDoor.OPEN, iblockdata1.get(BlockDoor.OPEN))).set(BlockDoor.HINGE, iblockdata1.get(BlockDoor.HINGE))).set(BlockDoor.POWERED, iblockdata1.get(BlockDoor.POWERED)) : Blocks.AIR.getBlockData()) : (blockpropertydoubleblockhalf == BlockPropertyDoubleBlockHalf.LOWER && enumdirection == EnumDirection.DOWN && !iblockdata.canPlace(generatoraccess, blockposition) ? Blocks.AIR.getBlockData() : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1)); + } + + public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) { + super.a(world, entityhuman, blockposition, Blocks.AIR.getBlockData(), tileentity, itemstack); + } + + public void a(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) { + BlockPropertyDoubleBlockHalf blockpropertydoubleblockhalf = (BlockPropertyDoubleBlockHalf) iblockdata.get(BlockDoor.HALF); + boolean flag = blockpropertydoubleblockhalf == BlockPropertyDoubleBlockHalf.LOWER; + BlockPosition blockposition1 = flag ? blockposition.up() : blockposition.down(); + IBlockData iblockdata1 = world.getType(blockposition1); + + if (iblockdata1.getBlock() == this && iblockdata1.get(BlockDoor.HALF) != blockpropertydoubleblockhalf) { + world.setTypeAndData(blockposition1, Blocks.AIR.getBlockData(), 35); + world.a(entityhuman, 2001, blockposition1, Block.getCombinedId(iblockdata1)); + if (!world.isClientSide && !entityhuman.u()) { + if (flag) { + iblockdata.a(world, blockposition, 0); + } else { + iblockdata1.a(world, blockposition1, 0); + } + } + } + + super.a(world, blockposition, iblockdata, entityhuman); + } + + public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) { + switch (pathmode) { + case LAND: + return (Boolean) iblockdata.get(BlockDoor.OPEN); + case WATER: + return false; + case AIR: + return (Boolean) iblockdata.get(BlockDoor.OPEN); + default: + return false; + } + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + private int d() { + return this.material == Material.ORE ? 1011 : 1012; + } + + private int e() { + return this.material == Material.ORE ? 1005 : 1006; + } + + @Nullable + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + BlockPosition blockposition = blockactioncontext.getClickPosition(); + + if (blockposition.getY() < 255 && blockactioncontext.getWorld().getType(blockposition.up()).a(blockactioncontext)) { + World world = blockactioncontext.getWorld(); + boolean flag = world.isBlockIndirectlyPowered(blockposition) || world.isBlockIndirectlyPowered(blockposition.up()); + + return (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.getBlockData().set(BlockDoor.FACING, blockactioncontext.f())).set(BlockDoor.HINGE, this.b(blockactioncontext))).set(BlockDoor.POWERED, flag)).set(BlockDoor.OPEN, flag)).set(BlockDoor.HALF, BlockPropertyDoubleBlockHalf.LOWER); + } else { + return null; + } + } + + public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) { + world.setTypeAndData(blockposition.up(), (IBlockData) iblockdata.set(BlockDoor.HALF, BlockPropertyDoubleBlockHalf.UPPER), 3); + } + + private BlockPropertyDoorHinge b(BlockActionContext blockactioncontext) { + World world = blockactioncontext.getWorld(); + BlockPosition blockposition = blockactioncontext.getClickPosition(); + EnumDirection enumdirection = blockactioncontext.f(); + BlockPosition blockposition1 = blockposition.up(); + EnumDirection enumdirection1 = enumdirection.f(); + IBlockData iblockdata = world.getType(blockposition.shift(enumdirection1)); + IBlockData iblockdata1 = world.getType(blockposition1.shift(enumdirection1)); + EnumDirection enumdirection2 = enumdirection.e(); + IBlockData iblockdata2 = world.getType(blockposition.shift(enumdirection2)); + IBlockData iblockdata3 = world.getType(blockposition1.shift(enumdirection2)); + int i = (iblockdata.k() ? -1 : 0) + (iblockdata1.k() ? -1 : 0) + (iblockdata2.k() ? 1 : 0) + (iblockdata3.k() ? 1 : 0); + boolean flag = iblockdata.getBlock() == this && iblockdata.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.LOWER; + boolean flag1 = iblockdata2.getBlock() == this && iblockdata2.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.LOWER; + + if ((!flag || flag1) && i <= 0) { + if ((!flag1 || flag) && i >= 0) { + int j = enumdirection.getAdjacentX(); + int k = enumdirection.getAdjacentZ(); + float f = blockactioncontext.m(); + float f1 = blockactioncontext.o(); + + return (j >= 0 || f1 >= 0.5F) && (j <= 0 || f1 <= 0.5F) && (k >= 0 || f <= 0.5F) && (k <= 0 || f >= 0.5F) ? BlockPropertyDoorHinge.LEFT : BlockPropertyDoorHinge.RIGHT; + } else { + return BlockPropertyDoorHinge.LEFT; + } + } else { + return BlockPropertyDoorHinge.RIGHT; + } + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + if (this.material == Material.ORE) { + return false; + } else { + iblockdata = (IBlockData) iblockdata.a((IBlockState) BlockDoor.OPEN); + world.setTypeAndData(blockposition, iblockdata, 10); + world.a(entityhuman, (Boolean) iblockdata.get(BlockDoor.OPEN) ? this.e() : this.d(), blockposition, 0); + return true; + } + } + + public void setDoor(World world, BlockPosition blockposition, boolean flag) { + IBlockData iblockdata = world.getType(blockposition); + + if (iblockdata.getBlock() == this && (Boolean) iblockdata.get(BlockDoor.OPEN) != flag) { + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDoor.OPEN, flag), 10); + this.b(world, blockposition, flag); + } + } + + public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + // CraftBukkit start + BlockPosition otherHalf = blockposition.shift(iblockdata.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.LOWER ? EnumDirection.UP : EnumDirection.DOWN); + + org.bukkit.World bworld = world.getWorld(); + org.bukkit.block.Block bukkitBlock = bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + org.bukkit.block.Block blockTop = bworld.getBlockAt(otherHalf.getX(), otherHalf.getY(), otherHalf.getZ()); + + int power = bukkitBlock.getBlockPower(); + int powerTop = blockTop.getBlockPower(); + if (powerTop > power) power = powerTop; + int oldPower = (Boolean) iblockdata.get(BlockDoor.POWERED) ? 15 : 0; + + if (oldPower == 0 ^ power == 0) { + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, oldPower, power); + world.getServer().getPluginManager().callEvent(eventRedstone); + + boolean flag = eventRedstone.getNewCurrent() > 0; + // CraftBukkit end + if (flag != (Boolean) iblockdata.get(BlockDoor.OPEN)) { + this.b(world, blockposition, flag); + } + + world.setTypeAndData(blockposition, (IBlockData) ((IBlockData) iblockdata.set(BlockDoor.POWERED, flag)).set(BlockDoor.OPEN, flag), 2); + } + + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + IBlockData iblockdata1 = iworldreader.getType(blockposition.down()); + + return iblockdata.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.LOWER ? iblockdata1.q() : iblockdata1.getBlock() == this; + } + + private void b(World world, BlockPosition blockposition, boolean flag) { + world.a((EntityHuman) null, flag ? this.e() : this.d(), blockposition, 0); + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return (IMaterial) (iblockdata.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.UPPER ? Items.AIR : super.getDropType(iblockdata, world, blockposition, i)); + } + + public EnumPistonReaction getPushReaction(IBlockData iblockdata) { + return EnumPistonReaction.DESTROY; + } + + public TextureType c() { + return TextureType.CUTOUT; + } + + public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) { + return (IBlockData) iblockdata.set(BlockDoor.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockDoor.FACING))); + } + + public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) { + return enumblockmirror == EnumBlockMirror.NONE ? iblockdata : (IBlockData) iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockDoor.FACING))).a((IBlockState) BlockDoor.HINGE); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockDoor.HALF, BlockDoor.FACING, BlockDoor.OPEN, BlockDoor.HINGE, BlockDoor.POWERED); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } +} diff --git a/src/main/java/net/minecraft/server/BlockDragonEgg.java b/src/main/java/net/minecraft/server/BlockDragonEgg.java new file mode 100644 index 000000000000..bfee7ec22f2f --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockDragonEgg.java @@ -0,0 +1,81 @@ +package net.minecraft.server; + +import org.bukkit.event.block.BlockFromToEvent; // CraftBukkit + +public class BlockDragonEgg extends BlockFalling { + + protected static final VoxelShape a = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D); + + public BlockDragonEgg(Block.Info block_info) { + super(block_info); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockDragonEgg.a; + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + this.b(iblockdata, world, blockposition); + return true; + } + + public void attack(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman) { + this.b(iblockdata, world, blockposition); + } + + private void b(IBlockData iblockdata, World world, BlockPosition blockposition) { + for (int i = 0; i < 1000; ++i) { + BlockPosition blockposition1 = blockposition.a(world.random.nextInt(16) - world.random.nextInt(16), world.random.nextInt(8) - world.random.nextInt(8), world.random.nextInt(16) - world.random.nextInt(16)); + + if (world.getType(blockposition1).isAir()) { + // CraftBukkit start + org.bukkit.block.Block from = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + org.bukkit.block.Block to = world.getWorld().getBlockAt(blockposition1.getX(), blockposition1.getY(), blockposition1.getZ()); + BlockFromToEvent event = new BlockFromToEvent(from, to); + org.bukkit.Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + + blockposition1 = new BlockPosition(event.getToBlock().getX(), event.getToBlock().getY(), event.getToBlock().getZ()); + // CraftBukkit end + if (world.isClientSide) { + for (int j = 0; j < 128; ++j) { + double d0 = world.random.nextDouble(); + float f = (world.random.nextFloat() - 0.5F) * 0.2F; + float f1 = (world.random.nextFloat() - 0.5F) * 0.2F; + float f2 = (world.random.nextFloat() - 0.5F) * 0.2F; + double d1 = (double) blockposition1.getX() + (double) (blockposition.getX() - blockposition1.getX()) * d0 + (world.random.nextDouble() - 0.5D) + 0.5D; + double d2 = (double) blockposition1.getY() + (double) (blockposition.getY() - blockposition1.getY()) * d0 + world.random.nextDouble() - 0.5D; + double d3 = (double) blockposition1.getZ() + (double) (blockposition.getZ() - blockposition1.getZ()) * d0 + (world.random.nextDouble() - 0.5D) + 0.5D; + + world.addParticle(Particles.K, d1, d2, d3, (double) f, (double) f1, (double) f2); + } + } else { + world.setTypeAndData(blockposition1, iblockdata, 2); + world.setAir(blockposition); + } + + return; + } + } + + } + + public int a(IWorldReader iworldreader) { + return 5; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } + + public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/BlockDropper.java b/src/main/java/net/minecraft/server/BlockDropper.java new file mode 100644 index 000000000000..6bff4c1b3206 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockDropper.java @@ -0,0 +1,72 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.inventory.InventoryMoveItemEvent; +// CraftBukkit end + +public class BlockDropper extends BlockDispenser { + + private static final IDispenseBehavior c = new DispenseBehaviorItem(); + + public BlockDropper(Block.Info block_info) { + super(block_info); + } + + protected IDispenseBehavior a(ItemStack itemstack) { + return BlockDropper.c; + } + + public TileEntity a(IBlockAccess iblockaccess) { + return new TileEntityDropper(); + } + + public void dispense(World world, BlockPosition blockposition) { + SourceBlock sourceblock = new SourceBlock(world, blockposition); + TileEntityDispenser tileentitydispenser = (TileEntityDispenser) sourceblock.getTileEntity(); + int i = tileentitydispenser.p(); + + if (i < 0) { + world.triggerEffect(1001, blockposition, 0); + } else { + ItemStack itemstack = tileentitydispenser.getItem(i); + + if (!itemstack.isEmpty()) { + EnumDirection enumdirection = (EnumDirection) world.getType(blockposition).get(BlockDropper.FACING); + IInventory iinventory = TileEntityHopper.a(world, blockposition.shift(enumdirection)); + ItemStack itemstack1; + + if (iinventory == null) { + itemstack1 = BlockDropper.c.dispense(sourceblock, itemstack); + } else { + // CraftBukkit start - Fire event when pushing items into other inventories + CraftItemStack oitemstack = CraftItemStack.asCraftMirror(itemstack.cloneItemStack().cloneAndSubtract(1)); + + org.bukkit.inventory.Inventory destinationInventory; + // Have to special case large chests as they work oddly + if (iinventory instanceof InventoryLargeChest) { + destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory); + } else { + destinationInventory = iinventory.getOwner().getInventory(); + } + + InventoryMoveItemEvent event = new InventoryMoveItemEvent(tileentitydispenser.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true); + world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + itemstack1 = TileEntityHopper.addItem(tileentitydispenser, iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection.opposite()); + if (event.getItem().equals(oitemstack) && itemstack1.isEmpty()) { + // CraftBukkit end + itemstack1 = itemstack.cloneItemStack(); + itemstack1.subtract(1); + } else { + itemstack1 = itemstack.cloneItemStack(); + } + } + + tileentitydispenser.setItem(i, itemstack1); + } + } + } +} diff --git a/src/main/java/net/minecraft/server/BlockEnderPortal.java b/src/main/java/net/minecraft/server/BlockEnderPortal.java new file mode 100644 index 000000000000..d060037a16ad --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockEnderPortal.java @@ -0,0 +1,49 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.event.entity.EntityPortalEnterEvent; // CraftBukkit + +public class BlockEnderPortal extends BlockTileEntity { + + protected static final VoxelShape a = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 12.0D, 16.0D); + + protected BlockEnderPortal(Block.Info block_info) { + super(block_info); + } + + public TileEntity a(IBlockAccess iblockaccess) { + return new TileEntityEnderPortal(); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockEnderPortal.a; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public int a(IBlockData iblockdata, Random random) { + return 0; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) { + if (!world.isClientSide && !entity.isPassenger() && !entity.isVehicle() && entity.bm() && VoxelShapes.c(VoxelShapes.a(entity.getBoundingBox().d((double) (-blockposition.getX()), (double) (-blockposition.getY()), (double) (-blockposition.getZ()))), iblockdata.getShape(world, blockposition), OperatorBoolean.AND)) { + // CraftBukkit start - Entity in portal + EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ())); + world.getServer().getPluginManager().callEvent(event); + // CraftBukkit end + entity.a(DimensionManager.THE_END); + } + + } + + public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) { + return ItemStack.a; + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } +} diff --git a/src/main/java/net/minecraft/server/BlockFire.java b/src/main/java/net/minecraft/server/BlockFire.java new file mode 100644 index 000000000000..0fbcd352c8e7 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockFire.java @@ -0,0 +1,459 @@ +package net.minecraft.server; + +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import com.destroystokyo.paper.event.block.TNTPrimeEvent; // Paper - TNTPrimeEvent +import java.util.Map; +import java.util.Random; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +// CraftBukkit start + +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.block.CraftBlockState; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockFadeEvent; +import org.bukkit.event.block.BlockSpreadEvent; +// CraftBukkit end + +public class BlockFire extends Block { + + public static final BlockStateInteger AGE = BlockProperties.X; + public static final BlockStateBoolean NORTH = BlockSprawling.a; + public static final BlockStateBoolean EAST = BlockSprawling.b; + public static final BlockStateBoolean SOUTH = BlockSprawling.c; + public static final BlockStateBoolean WEST = BlockSprawling.o; + public static final BlockStateBoolean UPPER = BlockSprawling.p; + private static final Map r = (Map) BlockSprawling.r.entrySet().stream().filter((entry) -> { + return entry.getKey() != EnumDirection.DOWN; + }).collect(SystemUtils.a()); + private final Object2IntMap flameChances = new Object2IntOpenHashMap(); + private final Object2IntMap t = new Object2IntOpenHashMap(); + + protected BlockFire(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockFire.AGE, 0)).set(BlockFire.NORTH, false)).set(BlockFire.EAST, false)).set(BlockFire.SOUTH, false)).set(BlockFire.WEST, false)).set(BlockFire.UPPER, false)); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return VoxelShapes.a(); + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + // CraftBukkit start + if (!iblockdata.canPlace(generatoraccess, blockposition)) { + CraftBlockState blockState = CraftBlockState.getBlockState(generatoraccess, blockposition); + blockState.setData(Blocks.AIR.getBlockData()); + + BlockFadeEvent event = new BlockFadeEvent(blockState.getBlock(), blockState); + generatoraccess.getMinecraftWorld().getMinecraftServer().server.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + return blockState.getHandle(); + } + } + return this.a((IBlockAccess) generatoraccess, blockposition).set(BlockFire.AGE, iblockdata.get(BlockFire.AGE)); + // CraftBukkit end + } + + @Nullable + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + return this.a((IBlockAccess) blockactioncontext.getWorld(), blockactioncontext.getClickPosition()); + } + + public IBlockData a(IBlockAccess iblockaccess, BlockPosition blockposition) { + IBlockData iblockdata = iblockaccess.getType(blockposition.down()); + + if (!iblockdata.q() && !this.k(iblockdata)) { + IBlockData iblockdata1 = this.getBlockData(); + EnumDirection[] aenumdirection = EnumDirection.values(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + BlockStateBoolean blockstateboolean = (BlockStateBoolean) BlockFire.r.get(enumdirection); + + if (blockstateboolean != null) { + iblockdata1 = (IBlockData) iblockdata1.set(blockstateboolean, this.k(iblockaccess.getType(blockposition.shift(enumdirection)))); + } + } + + return iblockdata1; + } else { + return this.getBlockData(); + } + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + return iworldreader.getType(blockposition.down()).q() || this.d(iworldreader, blockposition); + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public int a(IBlockData iblockdata, Random random) { + return 0; + } + + public int a(IWorldReader iworldreader) { + return 30; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (world.getGameRules().getBoolean("doFireTick")) { + if (!iblockdata.canPlace(world, blockposition)) { + fireExtinguished(world, blockposition); // CraftBukkit - invalid place location + } + + Block block = world.getType(blockposition.down()).getBlock(); + boolean flag = world.worldProvider instanceof WorldProviderTheEnd && block == Blocks.BEDROCK || block == Blocks.NETHERRACK || block == Blocks.MAGMA_BLOCK; + int i = (Integer) iblockdata.get(BlockFire.AGE); + + if (!flag && world.isRaining() && this.a(world, blockposition) && random.nextFloat() < 0.2F + (float) i * 0.03F) { + fireExtinguished(world, blockposition); // CraftBukkit - extinguished by rain + } else { + int j = Math.min(15, i + random.nextInt(3) / 2); + + if (i != j) { + iblockdata = (IBlockData) iblockdata.set(BlockFire.AGE, j); + world.setTypeAndData(blockposition, iblockdata, 4); + } + + if (!flag) { + world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world) + random.nextInt(10)); + if (!this.d(world, blockposition)) { + if (!world.getType(blockposition.down()).q() || i > 3) { + fireExtinguished(world, blockposition); // CraftBukkit + } + + return; + } + + if (i == 15 && random.nextInt(4) == 0 && !this.k(world.getType(blockposition.down()))) { + fireExtinguished(world, blockposition); // CraftBukkit + return; + } + } + + boolean flag1 = world.x(blockposition); + int k = flag1 ? -50 : 0; + + // CraftBukkit start - add source blockposition to burn calls + this.a(world, blockposition.east(), 300 + k, random, i, blockposition); + this.a(world, blockposition.west(), 300 + k, random, i, blockposition); + this.a(world, blockposition.down(), 250 + k, random, i, blockposition); + this.a(world, blockposition.up(), 250 + k, random, i, blockposition); + this.a(world, blockposition.north(), 300 + k, random, i, blockposition); + this.a(world, blockposition.south(), 300 + k, random, i, blockposition); + // CraftBukkit end + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + for (int l = -1; l <= 1; ++l) { + for (int i1 = -1; i1 <= 1; ++i1) { + for (int j1 = -1; j1 <= 4; ++j1) { + if (l != 0 || j1 != 0 || i1 != 0) { + int k1 = 100; + + if (j1 > 1) { + k1 += (j1 - 1) * 100; + } + + blockposition_mutableblockposition.g(blockposition).d(l, j1, i1); + if (!world.isLoaded(blockposition_mutableblockposition)) continue; // Paper + int l1 = this.a((IWorldReader) world, (BlockPosition) blockposition_mutableblockposition); + + if (l1 > 0) { + int i2 = (l1 + 40 + world.getDifficulty().a() * 7) / (i + 30); + + if (flag1) { + i2 /= 2; + } + + if (i2 > 0 && random.nextInt(k1) <= i2 && (!world.isRaining() || !this.a(world, (BlockPosition) blockposition_mutableblockposition))) { + int j2 = Math.min(15, i + random.nextInt(5) / 4); + + // CraftBukkit start - Call to stop spread of fire + if (world.getType(blockposition_mutableblockposition) != Blocks.FIRE) { + if (CraftEventFactory.callBlockIgniteEvent(world, blockposition_mutableblockposition, blockposition).isCancelled()) { + continue; + } + + CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition_mutableblockposition, (IBlockData) this.a((IBlockAccess) world, (BlockPosition) blockposition_mutableblockposition).set(BlockFire.AGE, j2), 3); // CraftBukkit + } + // CraftBukkit end + } + } + } + } + } + } + + } + } + } + + protected boolean a(World world, BlockPosition blockposition) { + return world.isRainingAt(blockposition) || world.isRainingAt(blockposition.west()) || world.isRainingAt(blockposition.east()) || world.isRainingAt(blockposition.north()) || world.isRainingAt(blockposition.south()); + } + + private int f(Block block) { + return this.t.getInt(block); + } + + private int g(Block block) { + return this.flameChances.getInt(block); + } + + private void a(World world, BlockPosition blockposition, int i, Random random, int j, BlockPosition sourceposition) { // CraftBukkit add sourceposition + // Paper start + final IBlockData iblockdata = world.getTypeIfLoaded(blockposition); + if (iblockdata == null) return; + int k = this.f(iblockdata.getBlock()); + // Paper end + + if (random.nextInt(i) < k) { + //IBlockData iblockdata = world.getType(blockposition); // Paper + + // CraftBukkit start + org.bukkit.block.Block theBlock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + org.bukkit.block.Block sourceBlock = world.getWorld().getBlockAt(sourceposition.getX(), sourceposition.getY(), sourceposition.getZ()); + + BlockBurnEvent event = new BlockBurnEvent(theBlock, sourceBlock); + world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + // CraftBukkit end + + if (random.nextInt(j + 10) < 5 && !world.isRainingAt(blockposition)) { + int l = Math.min(j + random.nextInt(5) / 4, 15); + + world.setTypeAndData(blockposition, (IBlockData) this.a((IBlockAccess) world, blockposition).set(BlockFire.AGE, l), 3); + } else { + if(iblockdata.getBlock() != Blocks.TNT) world.setAir(blockposition); // Paper - TNTPrimeEvent - We might be cancelling it below, move the setAir down + } + + Block block = iblockdata.getBlock(); + + if (block instanceof BlockTNT) { + // Paper start - TNTPrimeEvent + org.bukkit.block.Block tntBlock = MCUtil.toBukkitBlock(world, blockposition); + if (!new TNTPrimeEvent(tntBlock, TNTPrimeEvent.PrimeReason.FIRE, null).callEvent()) { + return; + } + world.setAir(blockposition); // setair after non cancelled event, it would usually be air by now + // Paper end + ((BlockTNT) block).a(world, blockposition); + } + } + + } + + private boolean d(IBlockAccess iblockaccess, BlockPosition blockposition) { + EnumDirection[] aenumdirection = EnumDirection.values(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + + if (this.k(iblockaccess.getType(blockposition.shift(enumdirection)))) { + return true; + } + } + + return false; + } + + private int a(IWorldReader iworldreader, BlockPosition blockposition) { + if (!iworldreader.isEmpty(blockposition)) { + return 0; + } else { + int i = 0; + EnumDirection[] aenumdirection = EnumDirection.values(); + int j = aenumdirection.length; + + for (int k = 0; k < j; ++k) { + EnumDirection enumdirection = aenumdirection[k]; + + // Paper start + final IBlockData type = ((World)iworldreader).getTypeIfLoaded(blockposition.shift(enumdirection)); + if (type == null) continue; + i = Math.max(this.g(type.getBlock()), i); + // Paper end + } + + return i; + } + } + + public boolean j() { + return false; + } + + public boolean k(IBlockData iblockdata) { + return this.g(iblockdata.getBlock()) > 0; + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + if (iblockdata1.getBlock() != iblockdata.getBlock()) { + if (world.worldProvider.getDimensionManager() != DimensionManager.OVERWORLD && world.worldProvider.getDimensionManager() != DimensionManager.NETHER || !((BlockPortal) Blocks.NETHER_PORTAL).a((GeneratorAccess) world, blockposition)) { + if (!iblockdata.canPlace(world, blockposition)) { + fireExtinguished(world, blockposition); // CraftBukkit - fuel block broke + } else { + world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world) + world.random.nextInt(10)); + } + } + } + } + + public TextureType c() { + return TextureType.CUTOUT; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockFire.AGE, BlockFire.NORTH, BlockFire.EAST, BlockFire.SOUTH, BlockFire.WEST, BlockFire.UPPER); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } + + public void a(Block block, int i, int j) { + this.flameChances.put(block, i); + this.t.put(block, j); + } + + public static void d() { + BlockFire blockfire = (BlockFire) Blocks.FIRE; + + blockfire.a(Blocks.OAK_PLANKS, 5, 20); + blockfire.a(Blocks.SPRUCE_PLANKS, 5, 20); + blockfire.a(Blocks.BIRCH_PLANKS, 5, 20); + blockfire.a(Blocks.JUNGLE_PLANKS, 5, 20); + blockfire.a(Blocks.ACACIA_PLANKS, 5, 20); + blockfire.a(Blocks.DARK_OAK_PLANKS, 5, 20); + blockfire.a(Blocks.OAK_SLAB, 5, 20); + blockfire.a(Blocks.SPRUCE_SLAB, 5, 20); + blockfire.a(Blocks.BIRCH_SLAB, 5, 20); + blockfire.a(Blocks.JUNGLE_SLAB, 5, 20); + blockfire.a(Blocks.ACACIA_SLAB, 5, 20); + blockfire.a(Blocks.DARK_OAK_SLAB, 5, 20); + blockfire.a(Blocks.OAK_FENCE_GATE, 5, 20); + blockfire.a(Blocks.SPRUCE_FENCE_GATE, 5, 20); + blockfire.a(Blocks.BIRCH_FENCE_GATE, 5, 20); + blockfire.a(Blocks.JUNGLE_FENCE_GATE, 5, 20); + blockfire.a(Blocks.DARK_OAK_FENCE_GATE, 5, 20); + blockfire.a(Blocks.ACACIA_FENCE_GATE, 5, 20); + blockfire.a(Blocks.OAK_FENCE, 5, 20); + blockfire.a(Blocks.SPRUCE_FENCE, 5, 20); + blockfire.a(Blocks.BIRCH_FENCE, 5, 20); + blockfire.a(Blocks.JUNGLE_FENCE, 5, 20); + blockfire.a(Blocks.DARK_OAK_FENCE, 5, 20); + blockfire.a(Blocks.ACACIA_FENCE, 5, 20); + blockfire.a(Blocks.OAK_STAIRS, 5, 20); + blockfire.a(Blocks.BIRCH_STAIRS, 5, 20); + blockfire.a(Blocks.SPRUCE_STAIRS, 5, 20); + blockfire.a(Blocks.JUNGLE_STAIRS, 5, 20); + blockfire.a(Blocks.ACACIA_STAIRS, 5, 20); + blockfire.a(Blocks.DARK_OAK_STAIRS, 5, 20); + blockfire.a(Blocks.OAK_LOG, 5, 5); + blockfire.a(Blocks.SPRUCE_LOG, 5, 5); + blockfire.a(Blocks.BIRCH_LOG, 5, 5); + blockfire.a(Blocks.JUNGLE_LOG, 5, 5); + blockfire.a(Blocks.ACACIA_LOG, 5, 5); + blockfire.a(Blocks.DARK_OAK_LOG, 5, 5); + blockfire.a(Blocks.STRIPPED_OAK_LOG, 5, 5); + blockfire.a(Blocks.STRIPPED_SPRUCE_LOG, 5, 5); + blockfire.a(Blocks.STRIPPED_BIRCH_LOG, 5, 5); + blockfire.a(Blocks.STRIPPED_JUNGLE_LOG, 5, 5); + blockfire.a(Blocks.STRIPPED_ACACIA_LOG, 5, 5); + blockfire.a(Blocks.STRIPPED_DARK_OAK_LOG, 5, 5); + blockfire.a(Blocks.STRIPPED_OAK_WOOD, 5, 5); + blockfire.a(Blocks.STRIPPED_SPRUCE_WOOD, 5, 5); + blockfire.a(Blocks.STRIPPED_BIRCH_WOOD, 5, 5); + blockfire.a(Blocks.STRIPPED_JUNGLE_WOOD, 5, 5); + blockfire.a(Blocks.STRIPPED_ACACIA_WOOD, 5, 5); + blockfire.a(Blocks.STRIPPED_DARK_OAK_WOOD, 5, 5); + blockfire.a(Blocks.OAK_WOOD, 5, 5); + blockfire.a(Blocks.SPRUCE_WOOD, 5, 5); + blockfire.a(Blocks.BIRCH_WOOD, 5, 5); + blockfire.a(Blocks.JUNGLE_WOOD, 5, 5); + blockfire.a(Blocks.ACACIA_WOOD, 5, 5); + blockfire.a(Blocks.DARK_OAK_WOOD, 5, 5); + blockfire.a(Blocks.OAK_LEAVES, 30, 60); + blockfire.a(Blocks.SPRUCE_LEAVES, 30, 60); + blockfire.a(Blocks.BIRCH_LEAVES, 30, 60); + blockfire.a(Blocks.JUNGLE_LEAVES, 30, 60); + blockfire.a(Blocks.ACACIA_LEAVES, 30, 60); + blockfire.a(Blocks.DARK_OAK_LEAVES, 30, 60); + blockfire.a(Blocks.BOOKSHELF, 30, 20); + blockfire.a(Blocks.TNT, 15, 100); + blockfire.a(Blocks.GRASS, 60, 100); + blockfire.a(Blocks.FERN, 60, 100); + blockfire.a(Blocks.DEAD_BUSH, 60, 100); + blockfire.a(Blocks.SUNFLOWER, 60, 100); + blockfire.a(Blocks.LILAC, 60, 100); + blockfire.a(Blocks.ROSE_BUSH, 60, 100); + blockfire.a(Blocks.PEONY, 60, 100); + blockfire.a(Blocks.TALL_GRASS, 60, 100); + blockfire.a(Blocks.LARGE_FERN, 60, 100); + blockfire.a(Blocks.DANDELION, 60, 100); + blockfire.a(Blocks.POPPY, 60, 100); + blockfire.a(Blocks.BLUE_ORCHID, 60, 100); + blockfire.a(Blocks.ALLIUM, 60, 100); + blockfire.a(Blocks.AZURE_BLUET, 60, 100); + blockfire.a(Blocks.RED_TULIP, 60, 100); + blockfire.a(Blocks.ORANGE_TULIP, 60, 100); + blockfire.a(Blocks.WHITE_TULIP, 60, 100); + blockfire.a(Blocks.PINK_TULIP, 60, 100); + blockfire.a(Blocks.OXEYE_DAISY, 60, 100); + blockfire.a(Blocks.WHITE_WOOL, 30, 60); + blockfire.a(Blocks.ORANGE_WOOL, 30, 60); + blockfire.a(Blocks.MAGENTA_WOOL, 30, 60); + blockfire.a(Blocks.LIGHT_BLUE_WOOL, 30, 60); + blockfire.a(Blocks.YELLOW_WOOL, 30, 60); + blockfire.a(Blocks.LIME_WOOL, 30, 60); + blockfire.a(Blocks.PINK_WOOL, 30, 60); + blockfire.a(Blocks.GRAY_WOOL, 30, 60); + blockfire.a(Blocks.LIGHT_GRAY_WOOL, 30, 60); + blockfire.a(Blocks.CYAN_WOOL, 30, 60); + blockfire.a(Blocks.PURPLE_WOOL, 30, 60); + blockfire.a(Blocks.BLUE_WOOL, 30, 60); + blockfire.a(Blocks.BROWN_WOOL, 30, 60); + blockfire.a(Blocks.GREEN_WOOL, 30, 60); + blockfire.a(Blocks.RED_WOOL, 30, 60); + blockfire.a(Blocks.BLACK_WOOL, 30, 60); + blockfire.a(Blocks.VINE, 15, 100); + blockfire.a(Blocks.COAL_BLOCK, 5, 5); + blockfire.a(Blocks.HAY_BLOCK, 60, 20); + blockfire.a(Blocks.WHITE_CARPET, 60, 20); + blockfire.a(Blocks.ORANGE_CARPET, 60, 20); + blockfire.a(Blocks.MAGENTA_CARPET, 60, 20); + blockfire.a(Blocks.LIGHT_BLUE_CARPET, 60, 20); + blockfire.a(Blocks.YELLOW_CARPET, 60, 20); + blockfire.a(Blocks.LIME_CARPET, 60, 20); + blockfire.a(Blocks.PINK_CARPET, 60, 20); + blockfire.a(Blocks.GRAY_CARPET, 60, 20); + blockfire.a(Blocks.LIGHT_GRAY_CARPET, 60, 20); + blockfire.a(Blocks.CYAN_CARPET, 60, 20); + blockfire.a(Blocks.PURPLE_CARPET, 60, 20); + blockfire.a(Blocks.BLUE_CARPET, 60, 20); + blockfire.a(Blocks.BROWN_CARPET, 60, 20); + blockfire.a(Blocks.GREEN_CARPET, 60, 20); + blockfire.a(Blocks.RED_CARPET, 60, 20); + blockfire.a(Blocks.BLACK_CARPET, 60, 20); + blockfire.a(Blocks.DRIED_KELP_BLOCK, 30, 60); + } + + // CraftBukkit start + private void fireExtinguished(GeneratorAccess world, BlockPosition position) { + if (!CraftEventFactory.callBlockFadeEvent(world, position, Blocks.AIR.getBlockData()).isCancelled()) { + world.setAir(position); + } + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/BlockFluids.java b/src/main/java/net/minecraft/server/BlockFluids.java new file mode 100644 index 000000000000..b53a88c33f57 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockFluids.java @@ -0,0 +1,186 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import java.util.List; +import java.util.Map; +import java.util.Random; + +public class BlockFluids extends Block implements IFluidSource { + + public static final BlockStateInteger LEVEL = BlockProperties.ah; + protected final FluidTypeFlowing b; + private final List c; + private final Map o = Maps.newIdentityHashMap(); + + protected BlockFluids(FluidTypeFlowing fluidtypeflowing, Block.Info block_info) { + super(block_info); + this.b = fluidtypeflowing; + this.c = Lists.newArrayList(); + this.c.add(fluidtypeflowing.a(false)); + + for (int i = 1; i < 8; ++i) { + this.c.add(fluidtypeflowing.a(8 - i, false)); + } + + this.c.add(fluidtypeflowing.a(8, true)); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockFluids.LEVEL, 0)); + } + + public void b(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + world.getFluid(blockposition).b(world, blockposition, random); + } + + public boolean a_(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return false; + } + + public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) { + return !this.b.a(TagsFluid.LAVA); + } + + public Fluid h(IBlockData iblockdata) { + int i = (Integer) iblockdata.get(BlockFluids.LEVEL); + + return (Fluid) this.c.get(Math.min(i, 8)); + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public boolean isCollidable(IBlockData iblockdata) { + return false; + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + Fluid fluid = iblockaccess.getFluid(blockposition.up()); + + return fluid.c().a((FluidType) this.b) ? VoxelShapes.b() : (VoxelShape) this.o.computeIfAbsent(iblockdata, (iblockdata1) -> { + Fluid fluid1 = iblockdata1.s(); + + return VoxelShapes.create(0.0D, 0.0D, 0.0D, 1.0D, (double) fluid1.getHeight(), 1.0D); + }); + } + + public EnumRenderType c(IBlockData iblockdata) { + return EnumRenderType.INVISIBLE; + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return Items.AIR; + } + + public int a(IWorldReader iworldreader) { + return this.b.a(iworldreader); + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + if (this.a(world, blockposition, iblockdata)) { + world.getFluidTickList().a(blockposition, iblockdata.s().c(), this.getFlowSpeed(world, blockposition)); // Paper + } + + } + + // Paper start - Get flow speed. Throttle if its water and flowing adjacent to lava + public int getFlowSpeed(World world, BlockPosition blockposition) { + if (this.material == Material.WATER) { + if ( + world.getMaterialIfLoaded(blockposition.north(1)) == Material.LAVA || + world.getMaterialIfLoaded(blockposition.south(1)) == Material.LAVA || + world.getMaterialIfLoaded(blockposition.west(1)) == Material.LAVA || + world.getMaterialIfLoaded(blockposition.east(1)) == Material.LAVA + ) { + return world.paperConfig.waterOverLavaFlowSpeed; + } + } + return this.a(world); + } + // Paper end + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + if (iblockdata.s().d() || iblockdata1.s().d()) { + generatoraccess.getFluidTickList().a(blockposition, iblockdata.s().c(), this.a((IWorldReader) generatoraccess)); + } + + return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + if (this.a(world, blockposition, iblockdata)) { + world.getFluidTickList().a(blockposition, iblockdata.s().c(), this.getFlowSpeed(world, blockposition)); // Paper + } + + } + + public boolean a(World world, BlockPosition blockposition, IBlockData iblockdata) { + if (this.b.a(TagsFluid.LAVA)) { + boolean flag = false; + EnumDirection[] aenumdirection = EnumDirection.values(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + + if (enumdirection != EnumDirection.DOWN && world.getFluid(blockposition.shift(enumdirection)).a(TagsFluid.WATER)) { + flag = true; + break; + } + } + + if (flag) { + Fluid fluid = world.getFluid(blockposition); + + if (fluid.d()) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(world, blockposition, Blocks.OBSIDIAN.getBlockData())) { + this.fizz(world, blockposition); + } + // CraftBukkit end + return false; + } + + if (fluid.getHeight() >= 0.44444445F) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(world, blockposition, Blocks.COBBLESTONE.getBlockData())) { + this.fizz(world, blockposition); + } + // CraftBukkit end + return false; + } + } + } + + return true; + } + + protected void fizz(GeneratorAccess generatoraccess, BlockPosition blockposition) { + double d0 = (double) blockposition.getX(); + double d1 = (double) blockposition.getY(); + double d2 = (double) blockposition.getZ(); + + generatoraccess.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_LAVA_EXTINGUISH, SoundCategory.BLOCKS, 0.5F, 2.6F + (generatoraccess.m().nextFloat() - generatoraccess.m().nextFloat()) * 0.8F); + + for (int i = 0; i < 8; ++i) { + generatoraccess.addParticle(Particles.F, d0 + Math.random(), d1 + 1.2D, d2 + Math.random(), 0.0D, 0.0D, 0.0D); + } + + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockFluids.LEVEL); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } + + public FluidType removeFluid(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata) { + if ((Integer) iblockdata.get(BlockFluids.LEVEL) == 0) { + generatoraccess.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 11); + return this.b; + } else { + return FluidTypes.EMPTY; + } + } +} diff --git a/src/main/java/net/minecraft/server/BlockGrass.java b/src/main/java/net/minecraft/server/BlockGrass.java new file mode 100644 index 000000000000..50936b8ba293 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockGrass.java @@ -0,0 +1,81 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.Random; + +public class BlockGrass extends BlockDirtSnowSpreadable implements IBlockFragilePlantElement { + + public BlockGrass(Block.Info block_info) { + super(block_info); + } + + public boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, boolean flag) { + return iblockaccess.getType(blockposition.up()).isAir(); + } + + public boolean a(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) { + return true; + } + + public void b(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) { + BlockPosition blockposition1 = blockposition.up(); + IBlockData iblockdata1 = Blocks.GRASS.getBlockData(); + int i = 0; + + while (i < 128) { + BlockPosition blockposition2 = blockposition1; + int j = 0; + + while (true) { + if (j < i / 16) { + blockposition2 = blockposition2.a(random.nextInt(3) - 1, (random.nextInt(3) - 1) * random.nextInt(3) / 2, random.nextInt(3) - 1); + if (world.getType(blockposition2.down()).getBlock() == this && !world.getType(blockposition2).k()) { + ++j; + continue; + } + } else { + IBlockData iblockdata2 = world.getType(blockposition2); + + if (iblockdata2.getBlock() == iblockdata1.getBlock() && random.nextInt(10) == 0) { + ((IBlockFragilePlantElement) iblockdata1.getBlock()).b(world, random, blockposition2, iblockdata2); + } + + if (iblockdata2.isAir()) { + label38: + { + IBlockData iblockdata3; + + if (random.nextInt(8) == 0) { + List> list = world.getBiome(blockposition2).f(); + + if (list.isEmpty()) { + break label38; + } + + iblockdata3 = ((WorldGenFeatureCompositeFlower) list.get(0)).a(random, blockposition2); + } else { + iblockdata3 = iblockdata1; + } + + if (iblockdata3.canPlace(world, blockposition2)) { + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, blockposition2, iblockdata3, 3); // CraftBukkit + } + } + } + } + + ++i; + break; + } + } + + } + + public boolean f(IBlockData iblockdata) { + return true; + } + + public TextureType c() { + return TextureType.CUTOUT_MIPPED; + } +} diff --git a/src/main/java/net/minecraft/server/BlockIce.java b/src/main/java/net/minecraft/server/BlockIce.java new file mode 100644 index 000000000000..35e2fe91871d --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockIce.java @@ -0,0 +1,72 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; + +public class BlockIce extends BlockHalfTransparent { + + public BlockIce(Block.Info block_info) { + super(block_info); + } + + public int j(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return Blocks.WATER.getBlockData().b(iblockaccess, blockposition); + } + + public TextureType c() { + return TextureType.TRANSLUCENT; + } + + public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) { + entityhuman.b(StatisticList.BLOCK_MINED.b(this)); + entityhuman.applyExhaustion(0.005F); + if (this.X_() && EnchantmentManager.getEnchantmentLevel(Enchantments.SILK_TOUCH, itemstack) > 0) { + a(world, blockposition, this.t(iblockdata)); + } else { + if (world.worldProvider.isNether()) { + world.setAir(blockposition); + return; + } + + int i = EnchantmentManager.getEnchantmentLevel(Enchantments.LOOT_BONUS_BLOCKS, itemstack); + + iblockdata.a(world, blockposition, i); + Material material = world.getType(blockposition.down()).getMaterial(); + + if (material.isSolid() || material.isLiquid()) { + world.setTypeUpdate(blockposition, Blocks.WATER.getBlockData()); + } + } + + } + + public int a(IBlockData iblockdata, Random random) { + return 0; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (world.getBrightness(EnumSkyBlock.BLOCK, blockposition) > 11 - iblockdata.b(world, blockposition)) { + this.b(iblockdata, world, blockposition); + } + + } + + protected void b(IBlockData iblockdata, World world, BlockPosition blockposition) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, world.worldProvider.isNether() ? Blocks.AIR.getBlockData() : Blocks.WATER.getBlockData()).isCancelled()) { + return; + } + // CraftBukkit end + if (world.worldProvider.isNether()) { + world.setAir(blockposition); + } else { + iblockdata.a(world, blockposition, 0); + world.setTypeUpdate(blockposition, Blocks.WATER.getBlockData()); + world.a(blockposition, Blocks.WATER, blockposition); + } + } + + public EnumPistonReaction getPushReaction(IBlockData iblockdata) { + return EnumPistonReaction.NORMAL; + } +} diff --git a/src/main/java/net/minecraft/server/BlockIceFrost.java b/src/main/java/net/minecraft/server/BlockIceFrost.java new file mode 100644 index 000000000000..fa7407ba660a --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockIceFrost.java @@ -0,0 +1,128 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockIceFrost extends BlockIce { + + public static final BlockStateInteger a = BlockProperties.U; + + public BlockIceFrost(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockIceFrost.a, 0)); + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!world.paperConfig.frostedIceEnabled) return; // Paper - add ability to disable frosted ice + if ((random.nextInt(3) == 0 || this.a(world, blockposition, 4)) && world.getLightLevel(blockposition) > 11 - (Integer) iblockdata.get(BlockIceFrost.a) - iblockdata.b(world, blockposition) && this.c(iblockdata, world, blockposition)) { + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + EnumDirection[] aenumdirection = EnumDirection.values(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + + blockposition_b.g(blockposition).c(enumdirection); + IBlockData iblockdata1 = world.getTypeIfLoaded(blockposition_b); // Paper - don't load chunks + if (iblockdata1 == null) continue; // Paper + + if (iblockdata1.getBlock() == this && !this.c(iblockdata1, world, blockposition_b)) { + world.getBlockTickList().a(blockposition_b, this, MathHelper.nextInt(random, world.paperConfig.frostedIceDelayMin, world.paperConfig.frostedIceDelayMax)); // Paper - use configurable min/max delay + } + } + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + + } else { + world.getBlockTickList().a(blockposition, this, MathHelper.nextInt(random, world.paperConfig.frostedIceDelayMin, world.paperConfig.frostedIceDelayMax)); // Paper - use configurable min/max delay + } + } + + private boolean c(IBlockData iblockdata, World world, BlockPosition blockposition) { + int i = (Integer) iblockdata.get(BlockIceFrost.a); + + if (i < 3) { + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockIceFrost.a, i + 1), 2); + return false; + } else { + this.b(iblockdata, world, blockposition); + return true; + } + } + + public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + if (block == this && this.a(world, blockposition, 2)) { + this.b(iblockdata, world, blockposition); + } + + super.doPhysics(iblockdata, world, blockposition, block, blockposition1); + } + + private boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, int i) { + int j = 0; + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + EnumDirection[] aenumdirection = EnumDirection.values(); + int k = aenumdirection.length; + + for (int l = 0; l < k; ++l) { + EnumDirection enumdirection = aenumdirection[l]; + + blockposition_b.g(blockposition).c(enumdirection); + if (((World) iblockaccess).getBlockIfLoaded(blockposition_b) == this) { // Paper - don't load chunks + ++j; + if (j >= i) { + boolean flag = false; + + return flag; + } + } + } + + return true; + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockIceFrost.a); + } + + public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) { + return ItemStack.a; + } +} diff --git a/src/main/java/net/minecraft/server/BlockJukeBox.java b/src/main/java/net/minecraft/server/BlockJukeBox.java new file mode 100644 index 000000000000..18cad5e901c7 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockJukeBox.java @@ -0,0 +1,106 @@ +package net.minecraft.server; + +public class BlockJukeBox extends BlockTileEntity { + + public static final BlockStateBoolean HAS_RECORD = BlockProperties.l; + + protected BlockJukeBox(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockJukeBox.HAS_RECORD, false)); + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + if ((Boolean) iblockdata.get(BlockJukeBox.HAS_RECORD)) { + this.dropRecord(world, blockposition); + iblockdata = (IBlockData) iblockdata.set(BlockJukeBox.HAS_RECORD, false); + world.setTypeAndData(blockposition, iblockdata, 2); + return true; + } else { + return false; + } + } + + public void a(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, ItemStack itemstack) { + TileEntity tileentity = generatoraccess.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityJukeBox) { + // CraftBukkit start - There can only be one + itemstack = itemstack.cloneItemStack(); + if (!itemstack.isEmpty()) { + itemstack.setCount(1); + } + ((TileEntityJukeBox) tileentity).setRecord(itemstack); + // CraftBukkit end + generatoraccess.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockJukeBox.HAS_RECORD, true), 2); + } + } + + public void dropRecord(World world, BlockPosition blockposition) { + if (!world.isClientSide) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityJukeBox) { + TileEntityJukeBox tileentityjukebox = (TileEntityJukeBox) tileentity; + ItemStack itemstack = tileentityjukebox.getRecord(); + + if (!itemstack.isEmpty()) { + world.triggerEffect(1010, blockposition, 0); + world.a(blockposition, (SoundEffect) null); + tileentityjukebox.setRecord(ItemStack.a); + float f = 0.7F; + double d0 = (double) (world.random.nextFloat() * 0.7F) + 0.15000000596046448D; + double d1 = (double) (world.random.nextFloat() * 0.7F) + 0.06000000238418579D + 0.6D; + double d2 = (double) (world.random.nextFloat() * 0.7F) + 0.15000000596046448D; + ItemStack itemstack1 = itemstack.cloneItemStack(); + EntityItem entityitem = new EntityItem(world, (double) blockposition.getX() + d0, (double) blockposition.getY() + d1, (double) blockposition.getZ() + d2, itemstack1); + + entityitem.n(); + world.addEntity(entityitem); + } + } + } + } + + public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) { + if (iblockdata.getBlock() != iblockdata1.getBlock()) { + this.dropRecord(world, blockposition); + super.remove(iblockdata, world, blockposition, iblockdata1, flag); + } + } + + public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) { + if (!world.isClientSide) { + super.dropNaturally(iblockdata, world, blockposition, f, 0); + } + } + + public TileEntity a(IBlockAccess iblockaccess) { + return new TileEntityJukeBox(); + } + + public boolean isComplexRedstone(IBlockData iblockdata) { + return true; + } + + public int a(IBlockData iblockdata, World world, BlockPosition blockposition) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityJukeBox) { + Item item = ((TileEntityJukeBox) tileentity).getRecord().getItem(); + + if (item instanceof ItemRecord) { + return ((ItemRecord) item).d(); + } + } + + return 0; + } + + public EnumRenderType c(IBlockData iblockdata) { + return EnumRenderType.MODEL; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockJukeBox.HAS_RECORD); + } +} diff --git a/src/main/java/net/minecraft/server/BlockKelp.java b/src/main/java/net/minecraft/server/BlockKelp.java new file mode 100644 index 000000000000..63155e23a2fc --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockKelp.java @@ -0,0 +1,97 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; + +public class BlockKelp extends Block implements IFluidContainer { + + public static final BlockStateInteger a = BlockProperties.Y; + protected static final VoxelShape b = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 9.0D, 16.0D); + + protected BlockKelp(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockKelp.a, 0)); + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockKelp.b; + } + + @Nullable + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + Fluid fluid = blockactioncontext.getWorld().getFluid(blockactioncontext.getClickPosition()); + + return fluid.a(TagsFluid.WATER) && fluid.g() == 8 ? this.a((GeneratorAccess) blockactioncontext.getWorld()) : null; + } + + public IBlockData a(GeneratorAccess generatoraccess) { + return (IBlockData) this.getBlockData().set(BlockKelp.a, generatoraccess.m().nextInt(25)); + } + + public TextureType c() { + return TextureType.CUTOUT; + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } + + public Fluid h(IBlockData iblockdata) { + return FluidTypes.WATER.a(false); + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!iblockdata.canPlace(world, blockposition)) { + world.setAir(blockposition, true); + } else { + BlockPosition blockposition1 = blockposition.up(); + IBlockData iblockdata1 = world.getType(blockposition1); + + if (iblockdata1.getBlock() == Blocks.WATER && (Integer) iblockdata.get(BlockKelp.a) < 25 && random.nextDouble() < 0.14D) { + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition1, (IBlockData) iblockdata.a((IBlockState) BlockKelp.a)); // CraftBukkit + } + + } + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + BlockPosition blockposition1 = blockposition.down(); + IBlockData iblockdata1 = iworldreader.getType(blockposition1); + Block block = iblockdata1.getBlock(); + + return block == Blocks.MAGMA_BLOCK ? false : block == this || block == Blocks.KELP_PLANT || Block.a(iblockdata1.getCollisionShape(iworldreader, blockposition1), EnumDirection.UP); + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + if (!iblockdata.canPlace(generatoraccess, blockposition)) { + if (enumdirection == EnumDirection.DOWN) { + return Blocks.AIR.getBlockData(); + } + + generatoraccess.getBlockTickList().a(blockposition, this, 1); + } + + if (enumdirection == EnumDirection.UP && iblockdata1.getBlock() == this) { + return Blocks.KELP_PLANT.getBlockData(); + } else { + generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess)); + return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockKelp.a); + } + + public boolean canPlace(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, FluidType fluidtype) { + return false; + } + + public boolean place(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, Fluid fluid) { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/BlockLeaves.java b/src/main/java/net/minecraft/server/BlockLeaves.java new file mode 100644 index 000000000000..cdaac983a32f --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockLeaves.java @@ -0,0 +1,174 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; + +import org.bukkit.event.block.LeavesDecayEvent; // CraftBukkit + +public class BlockLeaves extends Block { + + public static final BlockStateInteger DISTANCE = BlockProperties.ab; + public static final BlockStateBoolean PERSISTENT = BlockProperties.s; + protected static boolean c; + + public BlockLeaves(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockLeaves.DISTANCE, 7)).set(BlockLeaves.PERSISTENT, false)); + } + + public boolean isTicking(IBlockData iblockdata) { + return (Integer) iblockdata.get(BlockLeaves.DISTANCE) == 7 && !(Boolean) iblockdata.get(BlockLeaves.PERSISTENT); + } + + public void b(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!(Boolean) iblockdata.get(BlockLeaves.PERSISTENT) && (Integer) iblockdata.get(BlockLeaves.DISTANCE) == 7) { + // CraftBukkit start + LeavesDecayEvent event = new LeavesDecayEvent(world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ())); + world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled() || world.getType(blockposition).getBlock() != this) { + return; + } + // CraftBukkit end + iblockdata.a(world, blockposition, 0); + world.setAir(blockposition); + } + + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + world.setTypeAndData(blockposition, a(iblockdata, (GeneratorAccess) world, blockposition), 3); + } + + public int j(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return 1; + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + int i = w(iblockdata1) + 1; + + if (i != 1 || (Integer) iblockdata.get(BlockLeaves.DISTANCE) != i) { + generatoraccess.getBlockTickList().a(blockposition, this, 1); + } + + return iblockdata; + } + + private static IBlockData a(IBlockData iblockdata, GeneratorAccess generatoraccess, BlockPosition blockposition) { + int i = 7; + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + EnumDirection[] aenumdirection = EnumDirection.values(); + int j = aenumdirection.length; + + for (int k = 0; k < j; ++k) { + EnumDirection enumdirection = aenumdirection[k]; + + blockposition_b.g(blockposition).c(enumdirection); + i = Math.min(i, w(generatoraccess.getType(blockposition_b)) + 1); + if (i == 1) { + break; + } + } + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + + return (IBlockData) iblockdata.set(BlockLeaves.DISTANCE, i); + } + + private static int w(IBlockData iblockdata) { + return TagsBlock.LOGS.isTagged(iblockdata.getBlock()) ? 0 : (iblockdata.getBlock() instanceof BlockLeaves ? (Integer) iblockdata.get(BlockLeaves.DISTANCE) : 7); + } + + public int a(IBlockData iblockdata, Random random) { + return random.nextInt(20) == 0 ? 1 : 0; + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + Block block = iblockdata.getBlock(); + + return block == Blocks.OAK_LEAVES ? Blocks.OAK_SAPLING : (block == Blocks.SPRUCE_LEAVES ? Blocks.SPRUCE_SAPLING : (block == Blocks.BIRCH_LEAVES ? Blocks.BIRCH_SAPLING : (block == Blocks.JUNGLE_LEAVES ? Blocks.JUNGLE_SAPLING : (block == Blocks.ACACIA_LEAVES ? Blocks.ACACIA_SAPLING : (block == Blocks.DARK_OAK_LEAVES ? Blocks.DARK_OAK_SAPLING : Blocks.OAK_SAPLING))))); + } + + public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) { + if (!world.isClientSide) { + int j = this.k(iblockdata); + + if (i > 0) { + j -= 2 << i; + if (j < 10) { + j = 10; + } + } + + if (world.random.nextInt(j) == 0) { + a(world, blockposition, new ItemStack(this.getDropType(iblockdata, world, blockposition, i))); + } + + j = 200; + if (i > 0) { + j -= 10 << i; + if (j < 40) { + j = 40; + } + } + + this.a(world, blockposition, iblockdata, j); + } + + } + + protected void a(World world, BlockPosition blockposition, IBlockData iblockdata, int i) { + if ((iblockdata.getBlock() == Blocks.OAK_LEAVES || iblockdata.getBlock() == Blocks.DARK_OAK_LEAVES) && world.random.nextInt(i) == 0) { + a(world, blockposition, new ItemStack(Items.APPLE)); + } + + } + + protected int k(IBlockData iblockdata) { + return iblockdata.getBlock() == Blocks.JUNGLE_LEAVES ? 40 : 20; + } + + public TextureType c() { + return BlockLeaves.c ? TextureType.CUTOUT_MIPPED : TextureType.SOLID; + } + + public boolean q(IBlockData iblockdata) { + return false; + } + + public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) { + if (!world.isClientSide && itemstack.getItem() == Items.SHEARS) { + entityhuman.b(StatisticList.BLOCK_MINED.b(this)); + entityhuman.applyExhaustion(0.005F); + a(world, blockposition, new ItemStack(this)); + } else { + super.a(world, entityhuman, blockposition, iblockdata, tileentity, itemstack); + } + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockLeaves.DISTANCE, BlockLeaves.PERSISTENT); + } + + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + return a((IBlockData) this.getBlockData().set(BlockLeaves.PERSISTENT, true), (GeneratorAccess) blockactioncontext.getWorld(), blockactioncontext.getClickPosition()); + } +} diff --git a/src/main/java/net/minecraft/server/BlockLever.java b/src/main/java/net/minecraft/server/BlockLever.java new file mode 100644 index 000000000000..e998b70f5be5 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockLever.java @@ -0,0 +1,138 @@ +package net.minecraft.server; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public class BlockLever extends BlockAttachable { + + public static final BlockStateBoolean POWERED = BlockProperties.t; + protected static final VoxelShape b = Block.a(5.0D, 4.0D, 10.0D, 11.0D, 12.0D, 16.0D); + protected static final VoxelShape c = Block.a(5.0D, 4.0D, 0.0D, 11.0D, 12.0D, 6.0D); + protected static final VoxelShape o = Block.a(10.0D, 4.0D, 5.0D, 16.0D, 12.0D, 11.0D); + protected static final VoxelShape p = Block.a(0.0D, 4.0D, 5.0D, 6.0D, 12.0D, 11.0D); + protected static final VoxelShape q = Block.a(5.0D, 0.0D, 4.0D, 11.0D, 6.0D, 12.0D); + protected static final VoxelShape r = Block.a(4.0D, 0.0D, 5.0D, 12.0D, 6.0D, 11.0D); + protected static final VoxelShape s = Block.a(5.0D, 10.0D, 4.0D, 11.0D, 16.0D, 12.0D); + protected static final VoxelShape t = Block.a(4.0D, 10.0D, 5.0D, 12.0D, 16.0D, 11.0D); + + protected BlockLever(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockLever.FACING, EnumDirection.NORTH)).set(BlockLever.POWERED, false)).set(BlockLever.FACE, BlockPropertyAttachPosition.WALL)); + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + switch ((BlockPropertyAttachPosition) iblockdata.get(BlockLever.FACE)) { + case FLOOR: + switch (((EnumDirection) iblockdata.get(BlockLever.FACING)).k()) { + case X: + return BlockLever.r; + case Z: + default: + return BlockLever.q; + } + case WALL: + switch ((EnumDirection) iblockdata.get(BlockLever.FACING)) { + case EAST: + return BlockLever.p; + case WEST: + return BlockLever.o; + case SOUTH: + return BlockLever.c; + case NORTH: + default: + return BlockLever.b; + } + case CEILING: + default: + switch (((EnumDirection) iblockdata.get(BlockLever.FACING)).k()) { + case X: + return BlockLever.t; + case Z: + default: + return BlockLever.s; + } + } + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + iblockdata = (IBlockData) iblockdata.a((IBlockState) BlockLever.POWERED); + boolean flag = (Boolean) iblockdata.get(BlockLever.POWERED); + + if (world.isClientSide) { + if (flag) { + a(iblockdata, world, blockposition, 1.0F); + } + + return true; + } else { + // CraftBukkit start - Interact Lever + boolean powered = !flag; // Old powered state + org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + int old = (powered) ? 15 : 0; + int current = (!powered) ? 15 : 0; + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current); + world.getServer().getPluginManager().callEvent(eventRedstone); + + if ((eventRedstone.getNewCurrent() > 0) != (!powered)) { + return true; + } + // CraftBukkit end + + world.setTypeAndData(blockposition, iblockdata, 3); + float f3 = flag ? 0.6F : 0.5F; + + world.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_LEVER_CLICK, SoundCategory.BLOCKS, 0.3F, f3); + this.b(iblockdata, world, blockposition); + return true; + } + } + + private static void a(IBlockData iblockdata, GeneratorAccess generatoraccess, BlockPosition blockposition, float f) { + EnumDirection enumdirection = ((EnumDirection) iblockdata.get(BlockLever.FACING)).opposite(); + EnumDirection enumdirection1 = k(iblockdata).opposite(); + double d0 = (double) blockposition.getX() + 0.5D + 0.1D * (double) enumdirection.getAdjacentX() + 0.2D * (double) enumdirection1.getAdjacentX(); + double d1 = (double) blockposition.getY() + 0.5D + 0.1D * (double) enumdirection.getAdjacentY() + 0.2D * (double) enumdirection1.getAdjacentY(); + double d2 = (double) blockposition.getZ() + 0.5D + 0.1D * (double) enumdirection.getAdjacentZ() + 0.2D * (double) enumdirection1.getAdjacentZ(); + + generatoraccess.addParticle(new ParticleParamRedstone(1.0F, 0.0F, 0.0F, f), d0, d1, d2, 0.0D, 0.0D, 0.0D); + } + + public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) { + if (!flag && iblockdata.getBlock() != iblockdata1.getBlock()) { + if ((Boolean) iblockdata.get(BlockLever.POWERED)) { + this.b(iblockdata, world, blockposition); + } + + super.remove(iblockdata, world, blockposition, iblockdata1, flag); + } + } + + public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return (Boolean) iblockdata.get(BlockLever.POWERED) ? 15 : 0; + } + + public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return (Boolean) iblockdata.get(BlockLever.POWERED) && k(iblockdata) == enumdirection ? 15 : 0; + } + + public boolean isPowerSource(IBlockData iblockdata) { + return true; + } + + private void b(IBlockData iblockdata, World world, BlockPosition blockposition) { + world.applyPhysics(blockposition, this); + world.applyPhysics(blockposition.shift(k(iblockdata).opposite()), this); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockLever.FACE, BlockLever.FACING, BlockLever.POWERED); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } +} diff --git a/src/main/java/net/minecraft/server/BlockMagma.java b/src/main/java/net/minecraft/server/BlockMagma.java new file mode 100644 index 000000000000..44dfe0e6406f --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockMagma.java @@ -0,0 +1,60 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockMagma extends Block { + + public BlockMagma(Block.Info block_info) { + super(block_info); + } + + public void stepOn(World world, BlockPosition blockposition, Entity entity) { + if (!entity.isFireProof() && entity instanceof EntityLiving && !EnchantmentManager.i((EntityLiving) entity)) { + org.bukkit.craftbukkit.event.CraftEventFactory.blockDamage = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); // CraftBukkit + entity.damageEntity(DamageSource.HOT_FLOOR, 1.0F); + org.bukkit.craftbukkit.event.CraftEventFactory.blockDamage = null; // CraftBukkit + } + + super.stepOn(world, blockposition, entity); + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + BlockBubbleColumn.a(world, blockposition.up(), true); + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + if (enumdirection == EnumDirection.UP && iblockdata1.getBlock() == Blocks.WATER) { + generatoraccess.getBlockTickList().a(blockposition, this, this.a((IWorldReader) generatoraccess)); + } + + return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + public void b(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + BlockPosition blockposition1 = blockposition.up(); + + if (world.getFluid(blockposition).a(TagsFluid.WATER)) { + world.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_FIRE_EXTINGUISH, SoundCategory.BLOCKS, 0.5F, 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F); + if (world instanceof WorldServer) { + ((WorldServer) world).a(Particles.F, (double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.25D, (double) blockposition1.getZ() + 0.5D, 8, 0.5D, 0.25D, 0.5D, 0.0D); + } + } + + } + + public int a(IWorldReader iworldreader) { + return 20; + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world)); + } + + public boolean a(IBlockData iblockdata, Entity entity) { + return entity.isFireProof(); + } + + public boolean e(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return true; + } +} diff --git a/src/main/java/net/minecraft/server/BlockMinecartDetector.java b/src/main/java/net/minecraft/server/BlockMinecartDetector.java new file mode 100644 index 000000000000..cc8f7382f0ec --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockMinecartDetector.java @@ -0,0 +1,271 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public class BlockMinecartDetector extends BlockMinecartTrackAbstract { + + public static final BlockStateEnum SHAPE = BlockProperties.S; + public static final BlockStateBoolean POWERED = BlockProperties.t; + + public BlockMinecartDetector(Block.Info block_info) { + super(true, block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockMinecartDetector.POWERED, false)).set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_SOUTH)); + } + + public int a(IWorldReader iworldreader) { + return 20; + } + + public boolean isPowerSource(IBlockData iblockdata) { + return true; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) { + if (!world.isClientSide) { + if (!(Boolean) iblockdata.get(BlockMinecartDetector.POWERED)) { + this.a(world, blockposition, iblockdata); + } + } + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!world.isClientSide && (Boolean) iblockdata.get(BlockMinecartDetector.POWERED)) { + this.a(world, blockposition, iblockdata); + } + } + + public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return (Boolean) iblockdata.get(BlockMinecartDetector.POWERED) ? 15 : 0; + } + + public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return !(Boolean) iblockdata.get(BlockMinecartDetector.POWERED) ? 0 : (enumdirection == EnumDirection.UP ? 15 : 0); + } + + private void a(World world, BlockPosition blockposition, IBlockData iblockdata) { + boolean flag = (Boolean) iblockdata.get(BlockMinecartDetector.POWERED); + boolean flag1 = false; + List list = this.a(world, blockposition, EntityMinecartAbstract.class, (Predicate) null); + + if (!list.isEmpty()) { + flag1 = true; + } + + // CraftBukkit start + if (flag != flag1) { + org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, flag ? 15 : 0, flag1 ? 15 : 0); + world.getServer().getPluginManager().callEvent(eventRedstone); + + flag1 = eventRedstone.getNewCurrent() > 0; + } + // CraftBukkit end + + if (flag1 && !flag) { + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockMinecartDetector.POWERED, true), 3); + this.b(world, blockposition, iblockdata, true); + world.applyPhysics(blockposition, this); + world.applyPhysics(blockposition.down(), this); + world.a(blockposition, blockposition); + } + + if (!flag1 && flag) { + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockMinecartDetector.POWERED, false), 3); + this.b(world, blockposition, iblockdata, false); + world.applyPhysics(blockposition, this); + world.applyPhysics(blockposition.down(), this); + world.a(blockposition, blockposition); + } + + if (flag1) { + world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world)); + } + + world.updateAdjacentComparators(blockposition, this); + } + + protected void b(World world, BlockPosition blockposition, IBlockData iblockdata, boolean flag) { + MinecartTrackLogic minecarttracklogic = new MinecartTrackLogic(world, blockposition, iblockdata); + List list = minecarttracklogic.a(); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + BlockPosition blockposition1 = (BlockPosition) iterator.next(); + IBlockData iblockdata1 = world.getType(blockposition1); + + iblockdata1.doPhysics(world, blockposition1, iblockdata1.getBlock(), blockposition); + } + + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + if (iblockdata1.getBlock() != iblockdata.getBlock()) { + super.onPlace(iblockdata, world, blockposition, iblockdata1); + this.a(world, blockposition, iblockdata); + } + } + + public IBlockState e() { + return BlockMinecartDetector.SHAPE; + } + + public boolean isComplexRedstone(IBlockData iblockdata) { + return true; + } + + public int a(IBlockData iblockdata, World world, BlockPosition blockposition) { + if ((Boolean) iblockdata.get(BlockMinecartDetector.POWERED)) { + List list = this.a(world, blockposition, EntityMinecartCommandBlock.class, (Predicate) null); + + if (!list.isEmpty()) { + return ((EntityMinecartCommandBlock) list.get(0)).getCommandBlock().i(); + } + + List list1 = this.a(world, blockposition, EntityMinecartAbstract.class, IEntitySelector.d); + + if (!list1.isEmpty()) { + return Container.b((IInventory) list1.get(0)); + } + } + + return 0; + } + + protected List a(World world, BlockPosition blockposition, Class oclass, @Nullable Predicate predicate) { + return world.a(oclass, this.a(blockposition), predicate); + } + + private AxisAlignedBB a(BlockPosition blockposition) { + float f = 0.2F; + + return new AxisAlignedBB((double) ((float) blockposition.getX() + 0.2F), (double) blockposition.getY(), (double) ((float) blockposition.getZ() + 0.2F), (double) ((float) (blockposition.getX() + 1) - 0.2F), (double) ((float) (blockposition.getY() + 1) - 0.2F), (double) ((float) (blockposition.getZ() + 1) - 0.2F)); + } + + public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) { + switch (enumblockrotation) { + case CLOCKWISE_180: + switch ((BlockPropertyTrackPosition) iblockdata.get(BlockMinecartDetector.SHAPE)) { + case ASCENDING_EAST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_WEST); + case ASCENDING_WEST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_EAST); + case ASCENDING_NORTH: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_SOUTH); + case ASCENDING_SOUTH: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_NORTH); + case SOUTH_EAST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_WEST); + case SOUTH_WEST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_EAST); + case NORTH_WEST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST); + case NORTH_EAST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST); + } + case COUNTERCLOCKWISE_90: + switch ((BlockPropertyTrackPosition) iblockdata.get(BlockMinecartDetector.SHAPE)) { + case ASCENDING_EAST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_NORTH); + case ASCENDING_WEST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_SOUTH); + case ASCENDING_NORTH: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_WEST); + case ASCENDING_SOUTH: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_EAST); + case SOUTH_EAST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_EAST); + case SOUTH_WEST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST); + case NORTH_WEST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST); + case NORTH_EAST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_WEST); + case NORTH_SOUTH: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.EAST_WEST); + case EAST_WEST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_SOUTH); + } + case CLOCKWISE_90: + switch ((BlockPropertyTrackPosition) iblockdata.get(BlockMinecartDetector.SHAPE)) { + case ASCENDING_EAST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_SOUTH); + case ASCENDING_WEST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_NORTH); + case ASCENDING_NORTH: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_EAST); + case ASCENDING_SOUTH: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_WEST); + case SOUTH_EAST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST); + case SOUTH_WEST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_WEST); + case NORTH_WEST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_EAST); + case NORTH_EAST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST); + case NORTH_SOUTH: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.EAST_WEST); + case EAST_WEST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_SOUTH); + } + default: + return iblockdata; + } + } + + public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) { + BlockPropertyTrackPosition blockpropertytrackposition = (BlockPropertyTrackPosition) iblockdata.get(BlockMinecartDetector.SHAPE); + + switch (enumblockmirror) { + case LEFT_RIGHT: + switch (blockpropertytrackposition) { + case ASCENDING_NORTH: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_SOUTH); + case ASCENDING_SOUTH: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_NORTH); + case SOUTH_EAST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_EAST); + case SOUTH_WEST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_WEST); + case NORTH_WEST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST); + case NORTH_EAST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST); + default: + return super.a(iblockdata, enumblockmirror); + } + case FRONT_BACK: + switch (blockpropertytrackposition) { + case ASCENDING_EAST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_WEST); + case ASCENDING_WEST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_EAST); + case ASCENDING_NORTH: + case ASCENDING_SOUTH: + default: + break; + case SOUTH_EAST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST); + case SOUTH_WEST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST); + case NORTH_WEST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_EAST); + case NORTH_EAST: + return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_WEST); + } + } + + return super.a(iblockdata, enumblockmirror); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockMinecartDetector.SHAPE, BlockMinecartDetector.POWERED); + } +} diff --git a/src/main/java/net/minecraft/server/BlockMobSpawner.java b/src/main/java/net/minecraft/server/BlockMobSpawner.java new file mode 100644 index 000000000000..4e26969c6ca1 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockMobSpawner.java @@ -0,0 +1,45 @@ +package net.minecraft.server; + +public class BlockMobSpawner extends BlockTileEntity { + + protected BlockMobSpawner(Block.Info block_info) { + super(block_info); + } + + public TileEntity a(IBlockAccess iblockaccess) { + return new TileEntityMobSpawner(); + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return Items.AIR; + } + + public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) { + super.dropNaturally(iblockdata, world, blockposition, f, i); + /* CraftBukkit start - Delegate to getExpDrop + int j = 15 + world.random.nextInt(15) + world.random.nextInt(15); + + this.dropExperience(world, blockposition, j); + */ + } + + @Override + public int getExpDrop(IBlockData iblockdata, World world, BlockPosition blockposition, int enchantmentLevel) { + int j = 15 + world.random.nextInt(15) + world.random.nextInt(15); + + return j; + // CraftBukkit end + } + + public EnumRenderType c(IBlockData iblockdata) { + return EnumRenderType.MODEL; + } + + public TextureType c() { + return TextureType.CUTOUT; + } + + public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) { + return ItemStack.a; + } +} diff --git a/src/main/java/net/minecraft/server/BlockMonsterEggs.java b/src/main/java/net/minecraft/server/BlockMonsterEggs.java new file mode 100644 index 000000000000..d385f647e7d2 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockMonsterEggs.java @@ -0,0 +1,50 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import java.util.Map; +import java.util.Random; + +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; // CraftBukkit + +public class BlockMonsterEggs extends Block { + + private final Block a; + private static final Map b = Maps.newIdentityHashMap(); + + public BlockMonsterEggs(Block block, Block.Info block_info) { + super(block_info); + this.a = block; + BlockMonsterEggs.b.put(block, this); + } + + public int a(IBlockData iblockdata, Random random) { + return 0; + } + + public Block d() { + return this.a; + } + + public static boolean k(IBlockData iblockdata) { + return BlockMonsterEggs.b.containsKey(iblockdata.getBlock()); + } + + protected ItemStack t(IBlockData iblockdata) { + return new ItemStack(this.a); + } + + public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) { + if (!world.isClientSide && world.getGameRules().getBoolean("doTileDrops")) { + EntitySilverfish entitysilverfish = EntityTypes.SILVERFISH.create(world); // Paper + + entitysilverfish.setPositionRotation((double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, 0.0F, 0.0F); + world.addEntity(entitysilverfish, SpawnReason.SILVERFISH_BLOCK); // CraftBukkit - add SpawnReason + entitysilverfish.doSpawnEffect(); + } + + } + + public static IBlockData f(Block block) { + return ((Block) BlockMonsterEggs.b.get(block)).getBlockData(); + } +} diff --git a/src/main/java/net/minecraft/server/BlockMushroom.java b/src/main/java/net/minecraft/server/BlockMushroom.java new file mode 100644 index 000000000000..7cc30a4f3d98 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockMushroom.java @@ -0,0 +1,103 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.Random; + +// CraftBukkit start +import org.bukkit.TreeType; +// CraftBukkit end + +public class BlockMushroom extends BlockPlant implements IBlockFragilePlantElement { + + protected static final VoxelShape a = Block.a(5.0D, 0.0D, 5.0D, 11.0D, 6.0D, 11.0D); + + public BlockMushroom(Block.Info block_info) { + super(block_info); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockMushroom.a; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (random.nextInt(Math.max(1, (int) (100.0F / world.spigotConfig.mushroomModifier) * 25)) == 0) { // Spigot + int i = 5; + boolean flag = true; + Iterator iterator = BlockPosition.b(blockposition.a(-4, -1, -4), blockposition.a(4, 1, 4)).iterator(); + + while (iterator.hasNext()) { + BlockPosition blockposition1 = (BlockPosition) iterator.next(); + + if (world.getType(blockposition1).getBlock() == this) { + --i; + if (i <= 0) { + return; + } + } + } + + BlockPosition blockposition2 = blockposition.a(random.nextInt(3) - 1, random.nextInt(2) - random.nextInt(2), random.nextInt(3) - 1); + + for (int j = 0; j < 4; ++j) { + if (world.isEmpty(blockposition2) && iblockdata.canPlace(world, blockposition2)) { + blockposition = blockposition2; + } + + blockposition2 = blockposition.a(random.nextInt(3) - 1, random.nextInt(2) - random.nextInt(2), random.nextInt(3) - 1); + } + + if (world.isEmpty(blockposition2) && iblockdata.canPlace(world, blockposition2)) { + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition2, iblockdata, 2); // CraftBukkit + } + } + + } + + protected boolean b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return iblockdata.f(iblockaccess, blockposition); + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + BlockPosition blockposition1 = blockposition.down(); + IBlockData iblockdata1 = iworldreader.getType(blockposition1); + Block block = iblockdata1.getBlock(); + + return block != Blocks.MYCELIUM && block != Blocks.PODZOL ? iworldreader.getLightLevel(blockposition, 0) < 13 && this.b(iblockdata1, (IBlockAccess) iworldreader, blockposition1) : true; + } + + public boolean a(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, Random random) { + generatoraccess.setAir(blockposition); + WorldGenerator worldgenerator = null; + + if (this == Blocks.BROWN_MUSHROOM) { + BlockSapling.treeType = TreeType.BROWN_MUSHROOM; // CraftBukkit + worldgenerator = WorldGenerator.U; + } else if (this == Blocks.RED_MUSHROOM) { + BlockSapling.treeType = TreeType.RED_MUSHROOM; // CraftBukkit + worldgenerator = WorldGenerator.T; + } + + if (worldgenerator != null && worldgenerator.generate(generatoraccess, generatoraccess.getChunkProvider().getChunkGenerator(), random, blockposition, WorldGenFeatureConfiguration.e)) { + return true; + } else { + generatoraccess.setTypeAndData(blockposition, iblockdata, 3); + return false; + } + } + + public boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, boolean flag) { + return true; + } + + public boolean a(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) { + return (double) random.nextFloat() < 0.4D; + } + + public void b(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) { + this.a((GeneratorAccess) world, blockposition, iblockdata, random); + } + + public boolean e(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return true; + } +} diff --git a/src/main/java/net/minecraft/server/BlockNetherWart.java b/src/main/java/net/minecraft/server/BlockNetherWart.java new file mode 100644 index 000000000000..3934230ac769 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockNetherWart.java @@ -0,0 +1,63 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockNetherWart extends BlockPlant { + + public static final BlockStateInteger AGE = BlockProperties.U; + private static final VoxelShape[] b = new VoxelShape[] { Block.a(0.0D, 0.0D, 0.0D, 16.0D, 5.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 11.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 14.0D, 16.0D)}; + + protected BlockNetherWart(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockNetherWart.AGE, 0)); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockNetherWart.b[(Integer) iblockdata.get(BlockNetherWart.AGE)]; + } + + protected boolean b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return iblockdata.getBlock() == Blocks.SOUL_SAND; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + int i = (Integer) iblockdata.get(BlockNetherWart.AGE); + + if (i < 3 && random.nextInt(Math.max(1, (int) (100.0F / world.spigotConfig.wartModifier) * 10)) == 0) { // Spigot + iblockdata = (IBlockData) iblockdata.set(BlockNetherWart.AGE, i + 1); + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, blockposition, iblockdata, 2); // CraftBukkit + } + + super.a(iblockdata, world, blockposition, random); + } + + public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) { + if (!world.isClientSide) { + int j = 1; + + if ((Integer) iblockdata.get(BlockNetherWart.AGE) >= 3) { + j = 2 + world.random.nextInt(3); + if (i > 0) { + j += world.random.nextInt(i + 1); + } + } + + for (int k = 0; k < j; ++k) { + a(world, blockposition, new ItemStack(Items.NETHER_WART)); + } + + } + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return Items.AIR; + } + + public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) { + return new ItemStack(Items.NETHER_WART); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockNetherWart.AGE); + } +} diff --git a/src/main/java/net/minecraft/server/BlockNote.java b/src/main/java/net/minecraft/server/BlockNote.java new file mode 100644 index 000000000000..631c79fa01cb --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockNote.java @@ -0,0 +1,78 @@ +package net.minecraft.server; + +public class BlockNote extends Block { + + public static final BlockStateEnum INSTRUMENT = BlockProperties.as; + public static final BlockStateBoolean POWERED = BlockProperties.t; + public static final BlockStateInteger NOTE = BlockProperties.aj; + + public BlockNote(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockNote.INSTRUMENT, BlockPropertyInstrument.HARP)).set(BlockNote.NOTE, 0)).set(BlockNote.POWERED, false)); + } + + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + return (IBlockData) this.getBlockData().set(BlockNote.INSTRUMENT, BlockPropertyInstrument.a(blockactioncontext.getWorld().getType(blockactioncontext.getClickPosition().down()))); + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + return enumdirection == EnumDirection.DOWN ? (IBlockData) iblockdata.set(BlockNote.INSTRUMENT, BlockPropertyInstrument.a(iblockdata1)) : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + boolean flag = world.isBlockIndirectlyPowered(blockposition); + + if (flag != (Boolean) iblockdata.get(BlockNote.POWERED)) { + if (flag) { + this.play(world, blockposition, iblockdata); // CraftBukkit + } + + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockNote.POWERED, flag), 3); + } + + } + + private void play(World world, BlockPosition blockposition, IBlockData data) { // CraftBukkit + if (world.getType(blockposition.up()).isAir()) { + // CraftBukkit start + org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(world, blockposition, data.get(BlockNote.INSTRUMENT), data.get(BlockNote.NOTE)); + if (!event.isCancelled()) { + world.playBlockAction(blockposition, this, 0, 0); + } + // CraftBukkit end + } + + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + if (world.isClientSide) { + return true; + } else { + iblockdata = (IBlockData) iblockdata.a((IBlockState) BlockNote.NOTE); + world.setTypeAndData(blockposition, iblockdata, 3); + this.play(world, blockposition, iblockdata); // CraftBukkit + entityhuman.a(StatisticList.TUNE_NOTEBLOCK); + return true; + } + } + + public void attack(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman) { + if (!world.isClientSide) { + this.play(world, blockposition, iblockdata); // CraftBukkit + entityhuman.a(StatisticList.PLAY_NOTEBLOCK); + } + } + + public boolean a(IBlockData iblockdata, World world, BlockPosition blockposition, int i, int j) { + int k = (Integer) iblockdata.get(BlockNote.NOTE); + float f = (float) Math.pow(2.0D, (double) (k - 12) / 12.0D); + + world.a((EntityHuman) null, blockposition, ((BlockPropertyInstrument) iblockdata.get(BlockNote.INSTRUMENT)).a(), SoundCategory.RECORDS, 3.0F, f); + world.addParticle(Particles.I, (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 1.2D, (double) blockposition.getZ() + 0.5D, (double) k / 24.0D, 0.0D, 0.0D); + return true; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockNote.INSTRUMENT, BlockNote.POWERED, BlockNote.NOTE); + } +} diff --git a/src/main/java/net/minecraft/server/BlockObserver.java b/src/main/java/net/minecraft/server/BlockObserver.java new file mode 100644 index 000000000000..958e268d40fd --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockObserver.java @@ -0,0 +1,108 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockObserver extends BlockDirectional { + + public static final BlockStateBoolean b = BlockProperties.t; + + public BlockObserver(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockObserver.FACING, EnumDirection.SOUTH)).set(BlockObserver.b, false)); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockObserver.FACING, BlockObserver.b); + } + + public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) { + return (IBlockData) iblockdata.set(BlockObserver.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockObserver.FACING))); + } + + public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) { + return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockObserver.FACING))); + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if ((Boolean) iblockdata.get(BlockObserver.b)) { + // CraftBukkit start + if (CraftEventFactory.callRedstoneChange(world, blockposition, 15, 0).getNewCurrent() != 0) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockObserver.b, false), 2); + } else { + // CraftBukkit start + if (CraftEventFactory.callRedstoneChange(world, blockposition, 0, 15).getNewCurrent() != 15) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockObserver.b, true), 2); + world.getBlockTickList().a(blockposition, this, 2); + } + + this.a(world, blockposition, iblockdata); + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + if (iblockdata.get(BlockObserver.FACING) == enumdirection && !(Boolean) iblockdata.get(BlockObserver.b)) { + this.a(generatoraccess, blockposition); + } + + return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + private void a(GeneratorAccess generatoraccess, BlockPosition blockposition) { + if (!generatoraccess.e() && !generatoraccess.getBlockTickList().a(blockposition, this)) { + generatoraccess.getBlockTickList().a(blockposition, this, 2); + } + + } + + protected void a(World world, BlockPosition blockposition, IBlockData iblockdata) { + EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockObserver.FACING); + BlockPosition blockposition1 = blockposition.shift(enumdirection.opposite()); + + world.a(blockposition1, (Block) this, blockposition); + world.a(blockposition1, (Block) this, enumdirection); + } + + public boolean isPowerSource(IBlockData iblockdata) { + return true; + } + + public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return iblockdata.a(iblockaccess, blockposition, enumdirection); + } + + public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return (Boolean) iblockdata.get(BlockObserver.b) && iblockdata.get(BlockObserver.FACING) == enumdirection ? 15 : 0; + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + if (iblockdata.getBlock() != iblockdata1.getBlock()) { + if (!world.e() && (Boolean) iblockdata.get(BlockObserver.b) && !world.getBlockTickList().a(blockposition, this)) { + IBlockData iblockdata2 = (IBlockData) iblockdata.set(BlockObserver.b, false); + + world.setTypeAndData(blockposition, iblockdata2, 18); + this.a(world, blockposition, iblockdata2); + } + + } + } + + public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) { + if (iblockdata.getBlock() != iblockdata1.getBlock()) { + if (!world.isClientSide && (Boolean) iblockdata.get(BlockObserver.b) && world.getBlockTickList().a(blockposition, this)) { + this.a(world, blockposition, (IBlockData) iblockdata.set(BlockObserver.b, false)); + } + + } + } + + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + return (IBlockData) this.getBlockData().set(BlockObserver.FACING, blockactioncontext.d().opposite().opposite()); + } +} diff --git a/src/main/java/net/minecraft/server/BlockOre.java b/src/main/java/net/minecraft/server/BlockOre.java new file mode 100644 index 000000000000..9c8780e43dba --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockOre.java @@ -0,0 +1,84 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockOre extends Block { + + public BlockOre(Block.Info block_info) { + super(block_info); + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return (IMaterial) (this == Blocks.COAL_ORE ? Items.COAL : (this == Blocks.DIAMOND_ORE ? Items.DIAMOND : (this == Blocks.LAPIS_ORE ? Items.LAPIS_LAZULI : (this == Blocks.EMERALD_ORE ? Items.EMERALD : (this == Blocks.NETHER_QUARTZ_ORE ? Items.QUARTZ : this))))); + } + + public int a(IBlockData iblockdata, Random random) { + return this == Blocks.LAPIS_ORE ? 4 + random.nextInt(5) : 1; + } + + public int getDropCount(IBlockData iblockdata, int i, World world, BlockPosition blockposition, Random random) { + if (i > 0 && this != this.getDropType((IBlockData) this.getStates().a().iterator().next(), world, blockposition, i)) { + int j = random.nextInt(i + 2) - 1; + + if (j < 0) { + j = 0; + } + + return this.a(iblockdata, random) * (j + 1); + } else { + return this.a(iblockdata, random); + } + } + + public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) { + super.dropNaturally(iblockdata, world, blockposition, f, i); + /* CraftBukkit start - Delegated to getExpDrop + if (this.getDropType(iblockdata, world, blockposition, i) != this) { + int j = 0; + + if (this == Blocks.COAL_ORE) { + j = MathHelper.nextInt(world.random, 0, 2); + } else if (this == Blocks.DIAMOND_ORE) { + j = MathHelper.nextInt(world.random, 3, 7); + } else if (this == Blocks.EMERALD_ORE) { + j = MathHelper.nextInt(world.random, 3, 7); + } else if (this == Blocks.LAPIS_ORE) { + j = MathHelper.nextInt(world.random, 2, 5); + } else if (this == Blocks.NETHER_QUARTZ_ORE) { + j = MathHelper.nextInt(world.random, 2, 5); + } + + this.dropExperience(world, blockposition, j); + } + // */ + + } + + @Override + public int getExpDrop(IBlockData iblockdata, World world, BlockPosition blockposition, int enchantmentLevel) { + if (this.getDropType(iblockdata, world, blockposition, enchantmentLevel) != this) { + int j = 0; + + if (this == Blocks.COAL_ORE) { + j = MathHelper.nextInt(world.random, 0, 2); + } else if (this == Blocks.DIAMOND_ORE) { + j = MathHelper.nextInt(world.random, 3, 7); + } else if (this == Blocks.EMERALD_ORE) { + j = MathHelper.nextInt(world.random, 3, 7); + } else if (this == Blocks.LAPIS_ORE) { + j = MathHelper.nextInt(world.random, 2, 5); + } else if (this == Blocks.NETHER_QUARTZ_ORE) { + j = MathHelper.nextInt(world.random, 2, 5); + } + + return j; + } + + return 0; + // CraftBukkit end + } + + public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) { + return new ItemStack(this); + } +} diff --git a/src/main/java/net/minecraft/server/BlockPiston.java b/src/main/java/net/minecraft/server/BlockPiston.java new file mode 100644 index 000000000000..0109484e0a68 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockPiston.java @@ -0,0 +1,433 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +// CraftBukkit start +import java.util.AbstractList; +import java.util.Collection; +import java.util.Iterator; +import java.util.ListIterator; + +import com.google.common.collect.ImmutableList; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.event.block.BlockPistonRetractEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +// CraftBukkit end + +public class BlockPiston extends BlockDirectional { + + public static final BlockStateBoolean EXTENDED = BlockProperties.f; + protected static final VoxelShape c = Block.a(0.0D, 0.0D, 0.0D, 12.0D, 16.0D, 16.0D); + protected static final VoxelShape o = Block.a(4.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D); + protected static final VoxelShape p = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 12.0D); + protected static final VoxelShape q = Block.a(0.0D, 0.0D, 4.0D, 16.0D, 16.0D, 16.0D); + protected static final VoxelShape r = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 12.0D, 16.0D); + protected static final VoxelShape s = Block.a(0.0D, 4.0D, 0.0D, 16.0D, 16.0D, 16.0D); + private final boolean sticky; + + public BlockPiston(boolean flag, Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockPiston.FACING, EnumDirection.NORTH)).set(BlockPiston.EXTENDED, false)); + this.sticky = flag; + } + + public boolean q(IBlockData iblockdata) { + return !(Boolean) iblockdata.get(BlockPiston.EXTENDED); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + if ((Boolean) iblockdata.get(BlockPiston.EXTENDED)) { + switch ((EnumDirection) iblockdata.get(BlockPiston.FACING)) { + case DOWN: + return BlockPiston.s; + case UP: + default: + return BlockPiston.r; + case NORTH: + return BlockPiston.q; + case SOUTH: + return BlockPiston.p; + case WEST: + return BlockPiston.o; + case EAST: + return BlockPiston.c; + } + } else { + return VoxelShapes.b(); + } + } + + public boolean r(IBlockData iblockdata) { + return !(Boolean) iblockdata.get(BlockPiston.EXTENDED) || iblockdata.get(BlockPiston.FACING) == EnumDirection.DOWN; + } + + public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) { + if (!world.isClientSide) { + this.a(world, blockposition, iblockdata); + } + + } + + public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + if (!world.isClientSide) { + this.a(world, blockposition, iblockdata); + } + + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + if (iblockdata1.getBlock() != iblockdata.getBlock()) { + if (!world.isClientSide && world.getTileEntity(blockposition) == null) { + this.a(world, blockposition, iblockdata); + } + + } + } + + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + return (IBlockData) ((IBlockData) this.getBlockData().set(BlockPiston.FACING, blockactioncontext.d().opposite())).set(BlockPiston.EXTENDED, false); + } + + private void a(World world, BlockPosition blockposition, IBlockData iblockdata) { + EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockPiston.FACING); + boolean flag = this.a(world, blockposition, enumdirection); + + if (flag && !(Boolean) iblockdata.get(BlockPiston.EXTENDED)) { + if ((new PistonExtendsChecker(world, blockposition, enumdirection, true)).a()) { + world.playBlockAction(blockposition, this, 0, enumdirection.a()); + } + } else if (!flag && (Boolean) iblockdata.get(BlockPiston.EXTENDED)) { + BlockPosition blockposition1 = blockposition.shift(enumdirection, 2); + IBlockData iblockdata1 = world.getType(blockposition1); + byte b0 = 1; + + if (iblockdata1.getBlock() == Blocks.MOVING_PISTON && iblockdata1.get(BlockPiston.FACING) == enumdirection) { + TileEntity tileentity = world.getTileEntity(blockposition1); + + if (tileentity instanceof TileEntityPiston) { + TileEntityPiston tileentitypiston = (TileEntityPiston) tileentity; + + if (tileentitypiston.c() && (tileentitypiston.a(0.0F) < 0.5F || world.getTime() == tileentitypiston.k() || ((WorldServer) world).j_())) { + b0 = 2; + } + } + } + + // CraftBukkit start + //if (!this.sticky) { // Paper - Prevents empty sticky pistons from firing retract - history behind is odd + org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + BlockPistonRetractEvent event = new BlockPistonRetractEvent(block, ImmutableList.of(), CraftBlock.notchToBlockFace(enumdirection)); + world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + //} // Paper + // PAIL: checkME - what happened to setTypeAndData? + // CraftBukkit end + world.playBlockAction(blockposition, this, b0, enumdirection.a()); + } + + } + + private boolean a(World world, BlockPosition blockposition, EnumDirection enumdirection) { + EnumDirection[] aenumdirection = EnumDirection.values(); + int i = aenumdirection.length; + + int j; + + for (j = 0; j < i; ++j) { + EnumDirection enumdirection1 = aenumdirection[j]; + + if (enumdirection1 != enumdirection && world.isBlockFacePowered(blockposition.shift(enumdirection1), enumdirection1)) { + return true; + } + } + + if (world.isBlockFacePowered(blockposition, EnumDirection.DOWN)) { + return true; + } else { + BlockPosition blockposition1 = blockposition.up(); + EnumDirection[] aenumdirection1 = EnumDirection.values(); + + j = aenumdirection1.length; + + for (int k = 0; k < j; ++k) { + EnumDirection enumdirection2 = aenumdirection1[k]; + + if (enumdirection2 != EnumDirection.DOWN && world.isBlockFacePowered(blockposition1.shift(enumdirection2), enumdirection2)) { + return true; + } + } + + return false; + } + } + + public boolean a(IBlockData iblockdata, World world, BlockPosition blockposition, int i, int j) { + EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockPiston.FACING); + + if (!world.isClientSide) { + boolean flag = this.a(world, blockposition, enumdirection); + + if (flag && (i == 1 || i == 2)) { + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockPiston.EXTENDED, true), 2); + return false; + } + + if (!flag && i == 0) { + return false; + } + } + + if (i == 0) { + if (!this.a(world, blockposition, enumdirection, true)) { + return false; + } + + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockPiston.EXTENDED, true), 67); + world.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_PISTON_EXTEND, SoundCategory.BLOCKS, 0.5F, world.random.nextFloat() * 0.25F + 0.6F); + } else if (i == 1 || i == 2) { + TileEntity tileentity = world.getTileEntity(blockposition.shift(enumdirection)); + + if (tileentity instanceof TileEntityPiston) { + ((TileEntityPiston) tileentity).j(); + } + + world.setTypeAndData(blockposition, (IBlockData) ((IBlockData) Blocks.MOVING_PISTON.getBlockData().set(BlockPistonMoving.a, enumdirection)).set(BlockPistonMoving.b, this.sticky ? BlockPropertyPistonType.STICKY : BlockPropertyPistonType.DEFAULT), 3); + world.setTileEntity(blockposition, BlockPistonMoving.a((IBlockData) this.getBlockData().set(BlockPiston.FACING, EnumDirection.fromType1(j & 7)), enumdirection, false, true)); + if (this.sticky) { + BlockPosition blockposition1 = blockposition.a(enumdirection.getAdjacentX() * 2, enumdirection.getAdjacentY() * 2, enumdirection.getAdjacentZ() * 2); + IBlockData iblockdata1 = world.getType(blockposition1); + Block block = iblockdata1.getBlock(); + boolean flag1 = false; + + if (block == Blocks.MOVING_PISTON) { + TileEntity tileentity1 = world.getTileEntity(blockposition1); + + if (tileentity1 instanceof TileEntityPiston) { + TileEntityPiston tileentitypiston = (TileEntityPiston) tileentity1; + + if (tileentitypiston.d() == enumdirection && tileentitypiston.c()) { + tileentitypiston.j(); + flag1 = true; + } + } + } + + if (!flag1) { + if (i == 1 && !iblockdata1.isAir() && a(iblockdata1, world, blockposition1, enumdirection.opposite(), false, enumdirection) && (iblockdata1.getPushReaction() == EnumPistonReaction.NORMAL || block == Blocks.PISTON || block == Blocks.STICKY_PISTON)) { + this.a(world, blockposition, enumdirection, false); + } else { + world.setAir(blockposition.shift(enumdirection)); + } + } + } else { + world.setAir(blockposition.shift(enumdirection)); + } + + world.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_PISTON_CONTRACT, SoundCategory.BLOCKS, 0.5F, world.random.nextFloat() * 0.15F + 0.6F); + } + + return true; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public static boolean a(IBlockData iblockdata, World world, BlockPosition blockposition, EnumDirection enumdirection, boolean flag, EnumDirection enumdirection1) { + Block block = iblockdata.getBlock(); + + if (block == Blocks.OBSIDIAN) { + return false; + } else if (!world.getWorldBorder().a(blockposition)) { + return false; + } else if (blockposition.getY() >= 0 && (enumdirection != EnumDirection.DOWN || blockposition.getY() != 0)) { + if (blockposition.getY() <= world.getHeight() - 1 && (enumdirection != EnumDirection.UP || blockposition.getY() != world.getHeight() - 1)) { + if (block != Blocks.PISTON && block != Blocks.STICKY_PISTON) { + if (iblockdata.e(world, blockposition) == -1.0F) { + return false; + } + + switch (iblockdata.getPushReaction()) { + case BLOCK: + return false; + case DESTROY: + return flag; + case PUSH_ONLY: + return enumdirection == enumdirection1; + } + } else if ((Boolean) iblockdata.get(BlockPiston.EXTENDED)) { + return false; + } + + return !block.isTileEntity(); + } else { + return false; + } + } else { + return false; + } + } + + private boolean a(World world, BlockPosition blockposition, EnumDirection enumdirection, boolean flag) { + BlockPosition blockposition1 = blockposition.shift(enumdirection); + + if (!flag && world.getType(blockposition1).getBlock() == Blocks.PISTON_HEAD) { + world.setTypeAndData(blockposition1, Blocks.AIR.getBlockData(), 20); + } + + PistonExtendsChecker pistonextendschecker = new PistonExtendsChecker(world, blockposition, enumdirection, flag); + + if (!pistonextendschecker.a()) { + return false; + } else { + List list = pistonextendschecker.getMovedBlocks(); + List list1 = Lists.newArrayList(); + + for (int i = 0; i < list.size(); ++i) { + BlockPosition blockposition2 = (BlockPosition) list.get(i); + + list1.add(world.getType(blockposition2)); + } + + List list2 = pistonextendschecker.getBrokenBlocks(); + int j = list.size() + list2.size(); + IBlockData[] aiblockdata = new IBlockData[j]; + EnumDirection enumdirection1 = flag ? enumdirection : enumdirection.opposite(); + Set set = Sets.newHashSet(list); + // CraftBukkit start + final org.bukkit.block.Block bblock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + + final List moved = pistonextendschecker.getMovedBlocks(); + final List broken = pistonextendschecker.getBrokenBlocks(); + + List blocks = new AbstractList() { + + @Override + public int size() { + return moved.size() + broken.size(); + } + + @Override + public org.bukkit.block.Block get(int index) { + if (index >= size() || index < 0) { + throw new ArrayIndexOutOfBoundsException(index); + } + BlockPosition pos = (BlockPosition) (index < moved.size() ? moved.get(index) : broken.get(index - moved.size())); + return bblock.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()); + } + }; + org.bukkit.event.block.BlockPistonEvent event; + if (flag) { + event = new BlockPistonExtendEvent(bblock, blocks, CraftBlock.notchToBlockFace(enumdirection1)); + } else { + event = new BlockPistonRetractEvent(bblock, blocks, CraftBlock.notchToBlockFace(enumdirection1)); + } + world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + for (BlockPosition b : broken) { + world.notify(b, Blocks.AIR.getBlockData(), world.getType(b), 3); + } + for (BlockPosition b : moved) { + world.notify(b, Blocks.AIR.getBlockData(), world.getType(b), 3); + b = b.shift(enumdirection1); + world.notify(b, Blocks.AIR.getBlockData(), world.getType(b), 3); + } + return false; + } + // CraftBukkit end + + BlockPosition blockposition3; + int k; + IBlockData iblockdata; + + for (k = list2.size() - 1; k >= 0; --k) { + blockposition3 = (BlockPosition) list2.get(k); + iblockdata = world.getType(blockposition3); + iblockdata.a(world, blockposition3, 0); + world.setTypeAndData(blockposition3, Blocks.AIR.getBlockData(), 18); + --j; + aiblockdata[j] = iblockdata; + } + + for (k = list.size() - 1; k >= 0; --k) { + blockposition3 = (BlockPosition) list.get(k); + iblockdata = world.getType(blockposition3); + blockposition3 = blockposition3.shift(enumdirection1); + set.remove(blockposition3); + world.setTypeAndData(blockposition3, (IBlockData) Blocks.MOVING_PISTON.getBlockData().set(BlockPiston.FACING, enumdirection), 68); + world.setTileEntity(blockposition3, BlockPistonMoving.a((IBlockData) list1.get(k), enumdirection, flag, false)); + --j; + aiblockdata[j] = iblockdata; + } + + IBlockData iblockdata1; + + if (flag) { + BlockPropertyPistonType blockpropertypistontype = this.sticky ? BlockPropertyPistonType.STICKY : BlockPropertyPistonType.DEFAULT; + + iblockdata1 = (IBlockData) ((IBlockData) Blocks.PISTON_HEAD.getBlockData().set(BlockPistonExtension.FACING, enumdirection)).set(BlockPistonExtension.TYPE, blockpropertypistontype); + iblockdata = (IBlockData) ((IBlockData) Blocks.MOVING_PISTON.getBlockData().set(BlockPistonMoving.a, enumdirection)).set(BlockPistonMoving.b, this.sticky ? BlockPropertyPistonType.STICKY : BlockPropertyPistonType.DEFAULT); + set.remove(blockposition1); + world.setTypeAndData(blockposition1, iblockdata, 68); + world.setTileEntity(blockposition1, BlockPistonMoving.a(iblockdata1, enumdirection, true, true)); + } + + Iterator iterator = set.iterator(); + + while (iterator.hasNext()) { + blockposition3 = (BlockPosition) iterator.next(); + world.setTypeAndData(blockposition3, Blocks.AIR.getBlockData(), 66); + } + + for (k = list2.size() - 1; k >= 0; --k) { + iblockdata1 = aiblockdata[j++]; + BlockPosition blockposition4 = (BlockPosition) list2.get(k); + + iblockdata1.b(world, blockposition4, 2); + world.applyPhysics(blockposition4, iblockdata1.getBlock()); + } + + for (k = list.size() - 1; k >= 0; --k) { + world.applyPhysics((BlockPosition) list.get(k), aiblockdata[j++].getBlock()); + } + + if (flag) { + world.applyPhysics(blockposition1, Blocks.PISTON_HEAD); + } + + return true; + } + } + + public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) { + return (IBlockData) iblockdata.set(BlockPiston.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockPiston.FACING))); + } + + public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) { + return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockPiston.FACING))); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockPiston.FACING, BlockPiston.EXTENDED); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return iblockdata.get(BlockPiston.FACING) != enumdirection.opposite() && (Boolean) iblockdata.get(BlockPiston.EXTENDED) ? EnumBlockFaceShape.UNDEFINED : EnumBlockFaceShape.SOLID; + } + + public int j(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return 0; + } + + public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/BlockPlant.java b/src/main/java/net/minecraft/server/BlockPlant.java new file mode 100644 index 000000000000..317ae1634725 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockPlant.java @@ -0,0 +1,47 @@ +package net.minecraft.server; + +public class BlockPlant extends Block { + + protected BlockPlant(Block.Info block_info) { + super(block_info); + } + + protected boolean b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + Block block = iblockdata.getBlock(); + + return block == Blocks.GRASS_BLOCK || block == Blocks.DIRT || block == Blocks.COARSE_DIRT || block == Blocks.PODZOL || block == Blocks.FARMLAND; + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + // CraftBukkit start + if (!iblockdata.canPlace(generatoraccess, blockposition)) { + if (!(generatoraccess instanceof WorldServer && ((WorldServer) generatoraccess).hasPhysicsEvent) || !org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPhysicsEvent(generatoraccess, blockposition).isCancelled()) { // Paper + return Blocks.AIR.getBlockData(); + } + } + return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + // CraftBukkit end + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + BlockPosition blockposition1 = blockposition.down(); + + return this.b(iworldreader.getType(blockposition1), (IBlockAccess) iworldreader, blockposition1); + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public TextureType c() { + return TextureType.CUTOUT; + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } + + public int j(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return 0; + } +} diff --git a/src/main/java/net/minecraft/server/BlockPortal.java b/src/main/java/net/minecraft/server/BlockPortal.java new file mode 100644 index 000000000000..dbefe069b3f9 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockPortal.java @@ -0,0 +1,381 @@ +package net.minecraft.server; + +import com.google.common.cache.LoadingCache; +import java.util.Random; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.event.entity.EntityPortalEnterEvent; +import org.bukkit.event.world.PortalCreateEvent; +// CraftBukkit end + +public class BlockPortal extends Block { + + public static final BlockStateEnum AXIS = BlockProperties.z; + protected static final VoxelShape b = Block.a(0.0D, 0.0D, 6.0D, 16.0D, 16.0D, 10.0D); + protected static final VoxelShape c = Block.a(6.0D, 0.0D, 0.0D, 10.0D, 16.0D, 16.0D); + + public BlockPortal(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockPortal.AXIS, EnumDirection.EnumAxis.X)); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + switch ((EnumDirection.EnumAxis) iblockdata.get(BlockPortal.AXIS)) { + case Z: + return BlockPortal.c; + case X: + default: + return BlockPortal.b; + } + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (world.spigotConfig.enableZombiePigmenPortalSpawns && world.worldProvider.isOverworld() && world.getGameRules().getBoolean("doMobSpawning") && random.nextInt(2000) < world.getDifficulty().a()) { // Spigot + int i = blockposition.getY(); + + BlockPosition blockposition1; + + for (blockposition1 = blockposition; !world.getType(blockposition1).q() && blockposition1.getY() > 0; blockposition1 = blockposition1.down()) { + ; + } + + if (i > 0 && !world.getType(blockposition1.up()).isOccluding()) { + // CraftBukkit - set spawn reason to NETHER_PORTAL + Entity entity = EntityTypes.ZOMBIE_PIGMAN.spawnCreature(world, (NBTTagCompound) null, (IChatBaseComponent) null, (EntityHuman) null, blockposition1.up(), false, false, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NETHER_PORTAL); + + if (entity != null) { + entity.portalCooldown = entity.aQ(); + } + } + } + + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public boolean a(GeneratorAccess generatoraccess, BlockPosition blockposition) { + BlockPortal.Shape blockportal_shape = this.b(generatoraccess, blockposition); + + if (blockportal_shape != null) { + // CraftBukkit start - return portalcreator + return blockportal_shape.createPortal(); + // return true; + // CraftBukkit end + } else { + return false; + } + } + + @Nullable + public BlockPortal.Shape b(GeneratorAccess generatoraccess, BlockPosition blockposition) { + BlockPortal.Shape blockportal_shape = new BlockPortal.Shape(generatoraccess, blockposition, EnumDirection.EnumAxis.X); + + if (blockportal_shape.d() && blockportal_shape.e == 0) { + return blockportal_shape; + } else { + BlockPortal.Shape blockportal_shape1 = new BlockPortal.Shape(generatoraccess, blockposition, EnumDirection.EnumAxis.Z); + + return blockportal_shape1.d() && blockportal_shape1.e == 0 ? blockportal_shape1 : null; + } + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + EnumDirection.EnumAxis enumdirection_enumaxis = enumdirection.k(); + EnumDirection.EnumAxis enumdirection_enumaxis1 = (EnumDirection.EnumAxis) iblockdata.get(BlockPortal.AXIS); + boolean flag = enumdirection_enumaxis1 != enumdirection_enumaxis && enumdirection_enumaxis.c(); + + return !flag && iblockdata1.getBlock() != this && !(new BlockPortal.Shape(generatoraccess, blockposition, enumdirection_enumaxis1)).f() ? Blocks.AIR.getBlockData() : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + public int a(IBlockData iblockdata, Random random) { + return 0; + } + + public TextureType c() { + return TextureType.TRANSLUCENT; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) { + if (!entity.isPassenger() && !entity.isVehicle() && entity.bm()) { + // CraftBukkit start - Entity in portal + EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ())); + world.getServer().getPluginManager().callEvent(event); + // CraftBukkit end + entity.e(blockposition); + } + + } + + public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) { + return ItemStack.a; + } + + public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) { + switch (enumblockrotation) { + case COUNTERCLOCKWISE_90: + case CLOCKWISE_90: + switch ((EnumDirection.EnumAxis) iblockdata.get(BlockPortal.AXIS)) { + case Z: + return (IBlockData) iblockdata.set(BlockPortal.AXIS, EnumDirection.EnumAxis.X); + case X: + return (IBlockData) iblockdata.set(BlockPortal.AXIS, EnumDirection.EnumAxis.Z); + default: + return iblockdata; + } + default: + return iblockdata; + } + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockPortal.AXIS); + } + + public ShapeDetector.ShapeDetectorCollection c(GeneratorAccess generatoraccess, BlockPosition blockposition) { + EnumDirection.EnumAxis enumdirection_enumaxis = EnumDirection.EnumAxis.Z; + BlockPortal.Shape blockportal_shape = new BlockPortal.Shape(generatoraccess, blockposition, EnumDirection.EnumAxis.X); + LoadingCache loadingcache = ShapeDetector.a(generatoraccess, true); + + if (!blockportal_shape.d()) { + enumdirection_enumaxis = EnumDirection.EnumAxis.X; + blockportal_shape = new BlockPortal.Shape(generatoraccess, blockposition, EnumDirection.EnumAxis.Z); + } + + if (!blockportal_shape.d()) { + return new ShapeDetector.ShapeDetectorCollection(blockposition, EnumDirection.NORTH, EnumDirection.UP, loadingcache, 1, 1, 1); + } else { + int[] aint = new int[EnumDirection.EnumAxisDirection.values().length]; + EnumDirection enumdirection = blockportal_shape.c.f(); + BlockPosition blockposition1 = blockportal_shape.position.up(blockportal_shape.a() - 1); + EnumDirection.EnumAxisDirection[] aenumdirection_enumaxisdirection = EnumDirection.EnumAxisDirection.values(); + int i = aenumdirection_enumaxisdirection.length; + + int j; + + for (j = 0; j < i; ++j) { + EnumDirection.EnumAxisDirection enumdirection_enumaxisdirection = aenumdirection_enumaxisdirection[j]; + ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = new ShapeDetector.ShapeDetectorCollection(enumdirection.c() == enumdirection_enumaxisdirection ? blockposition1 : blockposition1.shift(blockportal_shape.c, blockportal_shape.b() - 1), EnumDirection.a(enumdirection_enumaxisdirection, enumdirection_enumaxis), EnumDirection.UP, loadingcache, blockportal_shape.b(), blockportal_shape.a(), 1); + + for (int k = 0; k < blockportal_shape.b(); ++k) { + for (int l = 0; l < blockportal_shape.a(); ++l) { + ShapeDetectorBlock shapedetectorblock = shapedetector_shapedetectorcollection.a(k, l, 1); + + if (!shapedetectorblock.a().isAir()) { + ++aint[enumdirection_enumaxisdirection.ordinal()]; + } + } + } + } + + EnumDirection.EnumAxisDirection enumdirection_enumaxisdirection1 = EnumDirection.EnumAxisDirection.POSITIVE; + EnumDirection.EnumAxisDirection[] aenumdirection_enumaxisdirection1 = EnumDirection.EnumAxisDirection.values(); + + j = aenumdirection_enumaxisdirection1.length; + + for (int i1 = 0; i1 < j; ++i1) { + EnumDirection.EnumAxisDirection enumdirection_enumaxisdirection2 = aenumdirection_enumaxisdirection1[i1]; + + if (aint[enumdirection_enumaxisdirection2.ordinal()] < aint[enumdirection_enumaxisdirection1.ordinal()]) { + enumdirection_enumaxisdirection1 = enumdirection_enumaxisdirection2; + } + } + + return new ShapeDetector.ShapeDetectorCollection(enumdirection.c() == enumdirection_enumaxisdirection1 ? blockposition1 : blockposition1.shift(blockportal_shape.c, blockportal_shape.b() - 1), EnumDirection.a(enumdirection_enumaxisdirection1, enumdirection_enumaxis), EnumDirection.UP, loadingcache, blockportal_shape.b(), blockportal_shape.a(), 1); + } + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } + + public static class Shape { + + private final GeneratorAccess a; + private final EnumDirection.EnumAxis b; + private final EnumDirection c; + private final EnumDirection d; + private int e; + private BlockPosition position; + private int height; + private int width; + java.util.Collection blocks = new java.util.HashSet(); // CraftBukkit - add field + + public Shape(GeneratorAccess generatoraccess, BlockPosition blockposition, EnumDirection.EnumAxis enumdirection_enumaxis) { + this.a = generatoraccess; + this.b = enumdirection_enumaxis; + if (enumdirection_enumaxis == EnumDirection.EnumAxis.X) { + this.d = EnumDirection.EAST; + this.c = EnumDirection.WEST; + } else { + this.d = EnumDirection.NORTH; + this.c = EnumDirection.SOUTH; + } + + for (BlockPosition blockposition1 = blockposition; blockposition.getY() > blockposition1.getY() - 21 && blockposition.getY() > 0 && this.a(generatoraccess.getType(blockposition.down())); blockposition = blockposition.down()) { + ; + } + + int i = this.a(blockposition, this.d) - 1; + + if (i >= 0) { + this.position = blockposition.shift(this.d, i); + this.width = this.a(this.position, this.c); + if (this.width < 2 || this.width > 21) { + this.position = null; + this.width = 0; + } + } + + if (this.position != null) { + this.height = this.c(); + } + + } + + protected int a(BlockPosition blockposition, EnumDirection enumdirection) { + int i; + + for (i = 0; i < 22; ++i) { + BlockPosition blockposition1 = blockposition.shift(enumdirection, i); + + if (!this.a(this.a.getType(blockposition1)) || this.a.getType(blockposition1.down()).getBlock() != Blocks.OBSIDIAN) { + break; + } + } + + Block block = this.a.getType(blockposition.shift(enumdirection, i)).getBlock(); + + return block == Blocks.OBSIDIAN ? i : 0; + } + + public int a() { + return this.height; + } + + public int b() { + return this.width; + } + + protected int c() { + // CraftBukkit start + this.blocks.clear(); + // CraftBukkit end + int i; + + label56: + for (this.height = 0; this.height < 21; ++this.height) { + for (i = 0; i < this.width; ++i) { + BlockPosition blockposition = this.position.shift(this.c, i).up(this.height); + IBlockData iblockdata = this.a.getType(blockposition); + + if (!this.a(iblockdata)) { + break label56; + } + + Block block = iblockdata.getBlock(); + + if (block == Blocks.NETHER_PORTAL) { + ++this.e; + } + + if (i == 0) { + block = this.a.getType(blockposition.shift(this.d)).getBlock(); + if (block != Blocks.OBSIDIAN) { + break label56; + // CraftBukkit start - add the block to our list + } else { + BlockPosition pos = blockposition.shift(this.d); + blocks.add(CraftBlock.at(this.a, pos)); + // CraftBukkit end + } + } else if (i == this.width - 1) { + block = this.a.getType(blockposition.shift(this.c)).getBlock(); + if (block != Blocks.OBSIDIAN) { + break label56; + // CraftBukkit start - add the block to our list + } else { + BlockPosition pos = blockposition.shift(this.c); + blocks.add(CraftBlock.at(this.a, pos)); + // CraftBukkit end + } + } + } + } + + for (i = 0; i < this.width; ++i) { + if (this.a.getType(this.position.shift(this.c, i).up(this.height)).getBlock() != Blocks.OBSIDIAN) { + this.height = 0; + break; + // CraftBukkit start - add the block to our list + } else { + BlockPosition pos = this.position.shift(this.c, i).up(this.height); + blocks.add(CraftBlock.at(this.a, pos)); + // CraftBukkit end + } + } + + if (this.height <= 21 && this.height >= 3) { + return this.height; + } else { + this.position = null; + this.width = 0; + this.height = 0; + return 0; + } + } + + protected boolean a(IBlockData iblockdata) { + Block block = iblockdata.getBlock(); + + return iblockdata.isAir() || block == Blocks.FIRE || block == Blocks.NETHER_PORTAL; + } + + public boolean d() { + return this.position != null && this.width >= 2 && this.width <= 21 && this.height >= 3 && this.height <= 21; + } + + // CraftBukkit start - return boolean + public boolean createPortal() { + org.bukkit.World bworld = this.a.getMinecraftWorld().getWorld(); + + // Copy below for loop + for (int i = 0; i < this.width; ++i) { + BlockPosition blockposition = this.position.shift(this.c, i); + + for (int j = 0; j < this.height; ++j) { + BlockPosition pos = blockposition.up(j); + blocks.add(bworld.getBlockAt(pos.getX(), pos.getY(), pos.getZ())); + } + } + + PortalCreateEvent event = new PortalCreateEvent(blocks, bworld, PortalCreateEvent.CreateReason.FIRE); + this.a.getMinecraftWorld().getMinecraftServer().server.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return false; + } + // CraftBukkit end + for (int i = 0; i < this.width; ++i) { + BlockPosition blockposition = this.position.shift(this.c, i); + + for (int j = 0; j < this.height; ++j) { + this.a.setTypeAndData(blockposition.up(j), (IBlockData) Blocks.NETHER_PORTAL.getBlockData().set(BlockPortal.AXIS, this.b), 18); + } + } + + return true; // CraftBukkit + } + + private boolean g() { + return this.e >= this.width * this.height; + } + + public boolean f() { + return this.d() && this.g(); + } + } +} diff --git a/src/main/java/net/minecraft/server/BlockPosition.java b/src/main/java/net/minecraft/server/BlockPosition.java new file mode 100644 index 000000000000..01e05a1dba42 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockPosition.java @@ -0,0 +1,422 @@ +package net.minecraft.server; + +import com.google.common.collect.AbstractIterator; +import com.google.common.collect.Lists; +import java.util.List; +import javax.annotation.concurrent.Immutable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@Immutable +public class BlockPosition extends BaseBlockPosition { + + //private static final Logger b = LogManager.getLogger(); // Paper - variable name conflict, logger isn't used + public static final BlockPosition ZERO = new BlockPosition(0, 0, 0); + private static final int c = 1 + MathHelper.e(MathHelper.c(30000000)); + private static final int d = BlockPosition.c; + private static final int f = 64 - BlockPosition.c - BlockPosition.d; + private static final int g = 0 + BlockPosition.d; + private static final int h = BlockPosition.g + BlockPosition.f; + private static final long i = (1L << BlockPosition.c) - 1L; + private static final long j = (1L << BlockPosition.f) - 1L; + private static final long k = (1L << BlockPosition.d) - 1L; + + public BlockPosition(int i, int j, int k) { + super(i, j, k); + } + + public BlockPosition(double d0, double d1, double d2) { + super(d0, d1, d2); + } + + public BlockPosition(Entity entity) { + this(entity.locX, entity.locY, entity.locZ); + } + + public BlockPosition(Vec3D vec3d) { + this(vec3d.x, vec3d.y, vec3d.z); + } + + public BlockPosition(BaseBlockPosition baseblockposition) { + this(baseblockposition.getX(), baseblockposition.getY(), baseblockposition.getZ()); + } + + public BlockPosition a(double d0, double d1, double d2) { + return d0 == 0.0D && d1 == 0.0D && d2 == 0.0D ? this : new BlockPosition((double) this.getX() + d0, (double) this.getY() + d1, (double) this.getZ() + d2); + } + + public BlockPosition add(int i, int j, int k) {return a(i, j, k);} // Paper - OBFHELPER + public BlockPosition a(int i, int j, int k) { + return i == 0 && j == 0 && k == 0 ? this : new BlockPosition(this.getX() + i, this.getY() + j, this.getZ() + k); + } + + public BlockPosition a(BaseBlockPosition baseblockposition) { + return this.a(baseblockposition.getX(), baseblockposition.getY(), baseblockposition.getZ()); + } + + public BlockPosition b(BaseBlockPosition baseblockposition) { + return this.a(-baseblockposition.getX(), -baseblockposition.getY(), -baseblockposition.getZ()); + } + + public BlockPosition up() { + return new BlockPosition(this.getX(), this.getY() + 1, this.getZ()); // Paper - Optimize BlockPosition + } + + public BlockPosition up(int i) { + return i == 0 ? this : new BlockPosition(this.getX(), this.getY() + i, this.getZ()); // Paper - Optimize BlockPosition + } + + public BlockPosition down() { + return new BlockPosition(this.getX(), this.getY() - 1, this.getZ()); // Paper - Optimize BlockPosition + } + + public BlockPosition down(int i) { + return i == 0 ? this : new BlockPosition(this.getX(), this.getY() - i, this.getZ()); // Paper - Optimize BlockPosition + } + + public BlockPosition north() { + return new BlockPosition(this.getX(), this.getY(), this.getZ() - 1); // Paper - Optimize BlockPosition + } + + public BlockPosition north(int i) { + return i == 0 ? this : new BlockPosition(this.getX(), this.getY(), this.getZ() - i); // Paper - Optimize BlockPosition + } + + public BlockPosition south() { + return new BlockPosition(this.getX(), this.getY(), this.getZ() + 1); // Paper - Optimize BlockPosition + } + + public BlockPosition south(int i) { + return i == 0 ? this : new BlockPosition(this.getX(), this.getY(), this.getZ() + i); // Paper - Optimize BlockPosition + } + + public BlockPosition west() { + return new BlockPosition(this.getX() - 1, this.getY(), this.getZ()); // Paper - Optimize BlockPosition + } + + public BlockPosition west(int i) { + return i == 0 ? this : new BlockPosition(this.getX() - i, this.getY(), this.getZ()); // Paper - Optimize BlockPosition + } + + public BlockPosition east() { + return new BlockPosition(this.getX() + 1, this.getY(), this.getZ()); // Paper - Optimize BlockPosition + } + + public BlockPosition east(int i) { + return i == 0 ? this : new BlockPosition(this.getX() + i, this.getY(), this.getZ()); // Paper - Optimize BlockPosition + } + + public BlockPosition shift(EnumDirection enumdirection) { + // Paper Start - Optimize BlockPosition + switch(enumdirection) { + case UP: + return new BlockPosition(this.getX(), this.getY() + 1, this.getZ()); + case DOWN: + return new BlockPosition(this.getX(), this.getY() - 1, this.getZ()); + case NORTH: + return new BlockPosition(this.getX(), this.getY(), this.getZ() - 1); + case SOUTH: + return new BlockPosition(this.getX(), this.getY(), this.getZ() + 1); + case WEST: + return new BlockPosition(this.getX() - 1, this.getY(), this.getZ()); + case EAST: + return new BlockPosition(this.getX() + 1, this.getY(), this.getZ()); + default: + return new BlockPosition(this.getX() + enumdirection.getAdjacentX(), this.getY() + enumdirection.getAdjacentY(), this.getZ() + enumdirection.getAdjacentZ()); + } + // Paper End + } + + public BlockPosition shift(EnumDirection enumdirection, int i) { + return i == 0 ? this : new BlockPosition(this.getX() + enumdirection.getAdjacentX() * i, this.getY() + enumdirection.getAdjacentY() * i, this.getZ() + enumdirection.getAdjacentZ() * i); + } + + public BlockPosition a(EnumBlockRotation enumblockrotation) { + switch (enumblockrotation) { + case NONE: + default: + return this; + case CLOCKWISE_90: + return new BlockPosition(-this.getZ(), this.getY(), this.getX()); + case CLOCKWISE_180: + return new BlockPosition(-this.getX(), this.getY(), -this.getZ()); + case COUNTERCLOCKWISE_90: + return new BlockPosition(this.getZ(), this.getY(), -this.getX()); + } + } + + public BlockPosition d(BaseBlockPosition baseblockposition) { + return new BlockPosition(this.getY() * baseblockposition.getZ() - this.getZ() * baseblockposition.getY(), this.getZ() * baseblockposition.getX() - this.getX() * baseblockposition.getZ(), this.getX() * baseblockposition.getY() - this.getY() * baseblockposition.getX()); + } + + public long asLong() { + return ((long) this.getX() & BlockPosition.i) << BlockPosition.h | ((long) this.getY() & BlockPosition.j) << BlockPosition.g | ((long) this.getZ() & BlockPosition.k) << 0; + } + + public static BlockPosition fromLong(long i) { + int j = (int) (i << 64 - BlockPosition.h - BlockPosition.c >> 64 - BlockPosition.c); + int k = (int) (i << 64 - BlockPosition.g - BlockPosition.f >> 64 - BlockPosition.f); + int l = (int) (i << 64 - BlockPosition.d >> 64 - BlockPosition.d); + + return new BlockPosition(j, k, l); + } + + public static Iterable a(BlockPosition blockposition, BlockPosition blockposition1) { + return a(Math.min(blockposition.getX(), blockposition1.getX()), Math.min(blockposition.getY(), blockposition1.getY()), Math.min(blockposition.getZ(), blockposition1.getZ()), Math.max(blockposition.getX(), blockposition1.getX()), Math.max(blockposition.getY(), blockposition1.getY()), Math.max(blockposition.getZ(), blockposition1.getZ())); + } + + public static Iterable a(int ix, int jx, int kx, int l, int i1, int j1) { // Paper - decompile fix + return () -> { + return new AbstractIterator() { + private boolean g = true; + private int h; + private int i; + private int j; + + protected BlockPosition computeNext() { + if (this.g) { + this.g = false; + this.h = ix; // Paper - decompile fix + this.i = jx; // Paper - decompile fix + this.j = kx; // Paper - decompile fix + return new BlockPosition(ix, jx, kx); + } else if (this.h == l && this.i == i1 && this.j == j1) { + return (BlockPosition) this.endOfData(); + } else { + if (this.h < l) { + ++this.h; + } else if (this.i < i1) { + this.h = ix; // Paper - decompile fix + ++this.i; + } else if (this.j < j1) { + this.h = ix; // Paper - decompile fix + this.i = jx; // Paper - decompile fix + ++this.j; + } + + return new BlockPosition(this.h, this.i, this.j); + } + } + }; + }; + } + + public BlockPosition asImmutable() { return h(); } // Paper - OBFHELPER + public BlockPosition h() { + return this; + } + + public static Iterable b(BlockPosition blockposition, BlockPosition blockposition1) { + return b(Math.min(blockposition.getX(), blockposition1.getX()), Math.min(blockposition.getY(), blockposition1.getY()), Math.min(blockposition.getZ(), blockposition1.getZ()), Math.max(blockposition.getX(), blockposition1.getX()), Math.max(blockposition.getY(), blockposition1.getY()), Math.max(blockposition.getZ(), blockposition1.getZ())); + } + + public static Iterable b(int i, int j, int k, int l, int i1, int j1) { + return () -> { + return new AbstractIterator() { + private BlockPosition.MutableBlockPosition g; + + protected BlockPosition.MutableBlockPosition computeNext() { + if (this.g == null) { + this.g = new BlockPosition.MutableBlockPosition(i, j, k); + return this.g; + } else if (this.g.x == l && this.g.y == i1 && this.g.z == j1) { + return (BlockPosition.MutableBlockPosition) this.endOfData(); + } else { + if (this.g.x < l) { + ++this.g.x; + } else if (this.g.y < i1) { + this.g.x = i; // Paper - decompile fix Readd line removed by the decompiler + ++this.g.y; + } else if (this.g.z < j1) { + this.g.x = i; // Paper - decompile fix Readd line removed by the decompiler + this.g.y = j; // Paper - decompile fix Readd line removed by the decompiler + ++this.g.z; + } + + return this.g; + } + } + }; + }; + } + + public static final class b extends BlockPosition.MutableBlockPosition implements AutoCloseable { + + private boolean f; + private static final List g = Lists.newArrayList(); + + private b(int i, int j, int k) { + super(i, j, k); + } + + public static BlockPosition.b r() { + return e(0, 0, 0); + } + + public static BlockPosition.b b(Entity entity) { + return d(entity.locX, entity.locY, entity.locZ); + } + + public static BlockPosition.b d(double d0, double d1, double d2) { + return e(MathHelper.floor(d0), MathHelper.floor(d1), MathHelper.floor(d2)); + } + + public static BlockPosition.b e(int i, int j, int k) { + synchronized (BlockPosition.b.g) { + if (!BlockPosition.b.g.isEmpty()) { + BlockPosition.b blockposition_b = (BlockPosition.b) BlockPosition.b.g.remove(BlockPosition.b.g.size() - 1); + + if (blockposition_b != null && blockposition_b.f) { + blockposition_b.f = false; + blockposition_b.c(i, j, k); + return blockposition_b; + } + } + } + + return new BlockPosition.b(i, j, k); + } + + public BlockPosition.b c(int i, int j, int k) { + return (BlockPosition.b) super.c(i, j, k); + } + + public BlockPosition.b c(double d0, double d1, double d2) { + return (BlockPosition.b) super.c(d0, d1, d2); + } + + public BlockPosition.b g(BaseBlockPosition baseblockposition) { + return (BlockPosition.b) super.g(baseblockposition); + } + + public BlockPosition.b c(EnumDirection enumdirection) { + return (BlockPosition.b) super.c(enumdirection); + } + + public BlockPosition.b c(EnumDirection enumdirection, int i) { + return (BlockPosition.b) super.c(enumdirection, i); + } + + public BlockPosition.b d(int i, int j, int k) { + return (BlockPosition.b) super.d(i, j, k); + } + + public void close() { + synchronized (BlockPosition.b.g) { + if (BlockPosition.b.g.size() < 100) { + BlockPosition.b.g.add(this); + } + + this.f = true; + } + } + } + + public static class MutableBlockPosition extends BlockPosition { + // Paper start - comment out + /* + protected int b; + protected int c; + protected int d; + + @Override + public boolean isValidLocation() { + return b >= -30000000 && d >= -30000000 && b < 30000000 && d < 30000000 && c >= 0 && c < 256; + } + @Override + public boolean isInvalidYLocation() { + return c < 0 || c >= 256; + } + */ + // Paper end + + public MutableBlockPosition() { + this(0, 0, 0); + } + + public MutableBlockPosition(BlockPosition blockposition) { + this(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + } + + public MutableBlockPosition(int i, int j, int k) { + // Paper start + super(i, j, k); + /* + this.b = i; + this.c = j; + this.d = k;*/ + // Paper end + } + + public BlockPosition a(double d0, double d1, double d2) { + return super.a(d0, d1, d2).h(); + } + + public BlockPosition a(int i, int j, int k) { + return super.a(i, j, k).h(); + } + + public BlockPosition shift(EnumDirection enumdirection, int i) { + return super.shift(enumdirection, i).h(); + } + + public BlockPosition a(EnumBlockRotation enumblockrotation) { + return super.a(enumblockrotation).h(); + } + + /* + // Paper start - use parent getters + public int getX() { + return this.b; + } + + public int getY() { + return this.c; + } + + public int getZ() { + return this.d; + }*/ + // Paper end + + public BlockPosition.MutableBlockPosition setValues(int i, int j, int k) { return c(i, j, k);} // Paper - OBFHELPER + public BlockPosition.MutableBlockPosition c(int i, int j, int k) { + // Paper start - use xyz + this.x = i; + this.y = j; + this.z = k; + // Paper end + return this; + } + + public BlockPosition.MutableBlockPosition setValues(double d0, double d1, double d2) { return c(d0, d1, d2);} // Paper - OBFHELPER + public BlockPosition.MutableBlockPosition c(double d0, double d1, double d2) { + return this.c(MathHelper.floor(d0), MathHelper.floor(d1), MathHelper.floor(d2)); + } + + public BlockPosition.MutableBlockPosition g(BaseBlockPosition baseblockposition) { + return this.c(baseblockposition.getX(), baseblockposition.getY(), baseblockposition.getZ()); + } + + public BlockPosition.MutableBlockPosition c(EnumDirection enumdirection) { + return this.c(enumdirection, 1); + } + + public BlockPosition.MutableBlockPosition c(EnumDirection enumdirection, int i) { + return this.c(x + enumdirection.getAdjacentX() * i, y + enumdirection.getAdjacentY() * i, z + enumdirection.getAdjacentZ() * i); // Paper - use xyz + } + + public BlockPosition.MutableBlockPosition d(int i, int j, int k) { + return this.c(x + i, y + j, z + k); // Paper - use xyz + } + + public void p(int i) { + this.y = i; // Paper change to y + } + + public BlockPosition toBlockPosition() { return h(); } // Paper - OBFHELPER + public BlockPosition h() { + return new BlockPosition(this); + } + } +} diff --git a/src/main/java/net/minecraft/server/BlockPoweredRail.java b/src/main/java/net/minecraft/server/BlockPoweredRail.java new file mode 100644 index 000000000000..6e85aebd43a7 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockPoweredRail.java @@ -0,0 +1,246 @@ +package net.minecraft.server; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockPoweredRail extends BlockMinecartTrackAbstract { + + public static final BlockStateEnum SHAPE = BlockProperties.S; + public static final BlockStateBoolean POWERED = BlockProperties.t; + + protected BlockPoweredRail(Block.Info block_info) { + super(true, block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.NORTH_SOUTH)).set(BlockPoweredRail.POWERED, false)); + } + + protected boolean a(World world, BlockPosition blockposition, IBlockData iblockdata, boolean flag, int i) { + if (i >= 8) { + return false; + } else { + int j = blockposition.getX(); + int k = blockposition.getY(); + int l = blockposition.getZ(); + boolean flag1 = true; + BlockPropertyTrackPosition blockpropertytrackposition = (BlockPropertyTrackPosition) iblockdata.get(BlockPoweredRail.SHAPE); + + switch (blockpropertytrackposition) { + case NORTH_SOUTH: + if (flag) { + ++l; + } else { + --l; + } + break; + case EAST_WEST: + if (flag) { + --j; + } else { + ++j; + } + break; + case ASCENDING_EAST: + if (flag) { + --j; + } else { + ++j; + ++k; + flag1 = false; + } + + blockpropertytrackposition = BlockPropertyTrackPosition.EAST_WEST; + break; + case ASCENDING_WEST: + if (flag) { + --j; + ++k; + flag1 = false; + } else { + ++j; + } + + blockpropertytrackposition = BlockPropertyTrackPosition.EAST_WEST; + break; + case ASCENDING_NORTH: + if (flag) { + ++l; + } else { + --l; + ++k; + flag1 = false; + } + + blockpropertytrackposition = BlockPropertyTrackPosition.NORTH_SOUTH; + break; + case ASCENDING_SOUTH: + if (flag) { + ++l; + ++k; + flag1 = false; + } else { + --l; + } + + blockpropertytrackposition = BlockPropertyTrackPosition.NORTH_SOUTH; + } + + return this.a(world, new BlockPosition(j, k, l), flag, i, blockpropertytrackposition) ? true : flag1 && this.a(world, new BlockPosition(j, k - 1, l), flag, i, blockpropertytrackposition); + } + } + + protected boolean a(World world, BlockPosition blockposition, boolean flag, int i, BlockPropertyTrackPosition blockpropertytrackposition) { + IBlockData iblockdata = world.getType(blockposition); + + if (iblockdata.getBlock() != this) { + return false; + } else { + BlockPropertyTrackPosition blockpropertytrackposition1 = (BlockPropertyTrackPosition) iblockdata.get(BlockPoweredRail.SHAPE); + + return blockpropertytrackposition == BlockPropertyTrackPosition.EAST_WEST && (blockpropertytrackposition1 == BlockPropertyTrackPosition.NORTH_SOUTH || blockpropertytrackposition1 == BlockPropertyTrackPosition.ASCENDING_NORTH || blockpropertytrackposition1 == BlockPropertyTrackPosition.ASCENDING_SOUTH) ? false : (blockpropertytrackposition == BlockPropertyTrackPosition.NORTH_SOUTH && (blockpropertytrackposition1 == BlockPropertyTrackPosition.EAST_WEST || blockpropertytrackposition1 == BlockPropertyTrackPosition.ASCENDING_EAST || blockpropertytrackposition1 == BlockPropertyTrackPosition.ASCENDING_WEST) ? false : ((Boolean) iblockdata.get(BlockPoweredRail.POWERED) ? (world.isBlockIndirectlyPowered(blockposition) ? true : this.a(world, blockposition, iblockdata, flag, i + 1)) : false)); + } + } + + protected void a(IBlockData iblockdata, World world, BlockPosition blockposition, Block block) { + boolean flag = (Boolean) iblockdata.get(BlockPoweredRail.POWERED); + boolean flag1 = world.isBlockIndirectlyPowered(blockposition) || this.a(world, blockposition, iblockdata, true, 0) || this.a(world, blockposition, iblockdata, false, 0); + + if (flag1 != flag) { + // CraftBukkit start + int power = flag ? 15 : 0; + int newPower = CraftEventFactory.callRedstoneChange(world, blockposition, power, 15 - power).getNewCurrent(); + if (newPower == power) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockPoweredRail.POWERED, flag1), 3); + world.applyPhysics(blockposition.down(), this); + if (((BlockPropertyTrackPosition) iblockdata.get(BlockPoweredRail.SHAPE)).c()) { + world.applyPhysics(blockposition.up(), this); + } + } + + } + + public IBlockState e() { + return BlockPoweredRail.SHAPE; + } + + public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) { + switch (enumblockrotation) { + case CLOCKWISE_180: + switch ((BlockPropertyTrackPosition) iblockdata.get(BlockPoweredRail.SHAPE)) { + case ASCENDING_EAST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.ASCENDING_WEST); + case ASCENDING_WEST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.ASCENDING_EAST); + case ASCENDING_NORTH: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.ASCENDING_SOUTH); + case ASCENDING_SOUTH: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.ASCENDING_NORTH); + case SOUTH_EAST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.NORTH_WEST); + case SOUTH_WEST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.NORTH_EAST); + case NORTH_WEST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST); + case NORTH_EAST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST); + } + case COUNTERCLOCKWISE_90: + switch ((BlockPropertyTrackPosition) iblockdata.get(BlockPoweredRail.SHAPE)) { + case NORTH_SOUTH: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.EAST_WEST); + case EAST_WEST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.NORTH_SOUTH); + case ASCENDING_EAST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.ASCENDING_NORTH); + case ASCENDING_WEST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.ASCENDING_SOUTH); + case ASCENDING_NORTH: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.ASCENDING_WEST); + case ASCENDING_SOUTH: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.ASCENDING_EAST); + case SOUTH_EAST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.NORTH_EAST); + case SOUTH_WEST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST); + case NORTH_WEST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST); + case NORTH_EAST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.NORTH_WEST); + } + case CLOCKWISE_90: + switch ((BlockPropertyTrackPosition) iblockdata.get(BlockPoweredRail.SHAPE)) { + case NORTH_SOUTH: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.EAST_WEST); + case EAST_WEST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.NORTH_SOUTH); + case ASCENDING_EAST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.ASCENDING_SOUTH); + case ASCENDING_WEST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.ASCENDING_NORTH); + case ASCENDING_NORTH: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.ASCENDING_EAST); + case ASCENDING_SOUTH: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.ASCENDING_WEST); + case SOUTH_EAST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST); + case SOUTH_WEST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.NORTH_WEST); + case NORTH_WEST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.NORTH_EAST); + case NORTH_EAST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST); + } + default: + return iblockdata; + } + } + + public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) { + BlockPropertyTrackPosition blockpropertytrackposition = (BlockPropertyTrackPosition) iblockdata.get(BlockPoweredRail.SHAPE); + + switch (enumblockmirror) { + case LEFT_RIGHT: + switch (blockpropertytrackposition) { + case ASCENDING_NORTH: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.ASCENDING_SOUTH); + case ASCENDING_SOUTH: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.ASCENDING_NORTH); + case SOUTH_EAST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.NORTH_EAST); + case SOUTH_WEST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.NORTH_WEST); + case NORTH_WEST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST); + case NORTH_EAST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST); + default: + return super.a(iblockdata, enumblockmirror); + } + case FRONT_BACK: + switch (blockpropertytrackposition) { + case ASCENDING_EAST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.ASCENDING_WEST); + case ASCENDING_WEST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.ASCENDING_EAST); + case ASCENDING_NORTH: + case ASCENDING_SOUTH: + default: + break; + case SOUTH_EAST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST); + case SOUTH_WEST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST); + case NORTH_WEST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.NORTH_EAST); + case NORTH_EAST: + return (IBlockData) iblockdata.set(BlockPoweredRail.SHAPE, BlockPropertyTrackPosition.NORTH_WEST); + } + } + + return super.a(iblockdata, enumblockmirror); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockPoweredRail.SHAPE, BlockPoweredRail.POWERED); + } +} diff --git a/src/main/java/net/minecraft/server/BlockPressurePlateAbstract.java b/src/main/java/net/minecraft/server/BlockPressurePlateAbstract.java new file mode 100644 index 000000000000..1b0f42fb9c5b --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockPressurePlateAbstract.java @@ -0,0 +1,146 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public abstract class BlockPressurePlateAbstract extends Block { + + protected static final VoxelShape a = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 0.5D, 15.0D); + protected static final VoxelShape b = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 1.0D, 15.0D); + protected static final AxisAlignedBB c = new AxisAlignedBB(0.125D, 0.0D, 0.125D, 0.875D, 0.25D, 0.875D); + + protected BlockPressurePlateAbstract(Block.Info block_info) { + super(block_info); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return this.getPower(iblockdata) > 0 ? BlockPressurePlateAbstract.a : BlockPressurePlateAbstract.b; + } + + public int a(IWorldReader iworldreader) { + return 20; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public boolean a() { + return true; + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + return enumdirection == EnumDirection.DOWN && !iblockdata.canPlace(generatoraccess, blockposition) ? Blocks.AIR.getBlockData() : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + IBlockData iblockdata1 = iworldreader.getType(blockposition.down()); + + return iblockdata1.q() || iblockdata1.getBlock() instanceof BlockFence; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!world.isClientSide) { + int i = this.getPower(iblockdata); + + if (i > 0) { + this.a(world, blockposition, iblockdata, i); + } + + } + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) { + if (!world.isClientSide) { + int i = this.getPower(iblockdata); + + if (i == 0) { + this.a(world, blockposition, iblockdata, i); + } + + } + } + + protected void a(World world, BlockPosition blockposition, IBlockData iblockdata, int i) { + int j = this.b(world, blockposition); + boolean flag = i > 0; + boolean flag1 = j > 0; + + // CraftBukkit start - Interact Pressure Plate + org.bukkit.World bworld = world.getWorld(); + org.bukkit.plugin.PluginManager manager = world.getServer().getPluginManager(); + + if (flag != flag1) { + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), i, j); + manager.callEvent(eventRedstone); + + flag1 = eventRedstone.getNewCurrent() > 0; + j = eventRedstone.getNewCurrent(); + } + // CraftBukkit end + + if (i != j) { + iblockdata = this.a(iblockdata, j); + world.setTypeAndData(blockposition, iblockdata, 2); + this.a(world, blockposition); + world.a(blockposition, blockposition); + } + + if (!flag1 && flag) { + this.b((GeneratorAccess) world, blockposition); + } else if (flag1 && !flag) { + this.a((GeneratorAccess) world, blockposition); + } + + if (flag1) { + world.getBlockTickList().a(new BlockPosition(blockposition), this, this.a((IWorldReader) world)); + } + + } + + protected abstract void a(GeneratorAccess generatoraccess, BlockPosition blockposition); + + protected abstract void b(GeneratorAccess generatoraccess, BlockPosition blockposition); + + public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) { + if (!flag && iblockdata.getBlock() != iblockdata1.getBlock()) { + if (this.getPower(iblockdata) > 0) { + this.a(world, blockposition); + } + + super.remove(iblockdata, world, blockposition, iblockdata1, flag); + } + } + + protected void a(World world, BlockPosition blockposition) { + world.applyPhysics(blockposition, this); + world.applyPhysics(blockposition.down(), this); + } + + public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return this.getPower(iblockdata); + } + + public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return enumdirection == EnumDirection.UP ? this.getPower(iblockdata) : 0; + } + + public boolean isPowerSource(IBlockData iblockdata) { + return true; + } + + public EnumPistonReaction getPushReaction(IBlockData iblockdata) { + return EnumPistonReaction.DESTROY; + } + + protected abstract int b(World world, BlockPosition blockposition); + + protected abstract int getPower(IBlockData iblockdata); + + protected abstract IBlockData a(IBlockData iblockdata, int i); + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } +} diff --git a/src/main/java/net/minecraft/server/BlockPressurePlateBinary.java b/src/main/java/net/minecraft/server/BlockPressurePlateBinary.java new file mode 100644 index 000000000000..1a9acd2f5b3b --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockPressurePlateBinary.java @@ -0,0 +1,105 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +import org.bukkit.event.entity.EntityInteractEvent; // CraftBukkit + +public class BlockPressurePlateBinary extends BlockPressurePlateAbstract { + + public static final BlockStateBoolean POWERED = BlockProperties.t; + private final BlockPressurePlateBinary.EnumMobType p; + + protected BlockPressurePlateBinary(BlockPressurePlateBinary.EnumMobType blockpressureplatebinary_enummobtype, Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockPressurePlateBinary.POWERED, false)); + this.p = blockpressureplatebinary_enummobtype; + } + + protected int getPower(IBlockData iblockdata) { + return (Boolean) iblockdata.get(BlockPressurePlateBinary.POWERED) ? 15 : 0; + } + + protected IBlockData a(IBlockData iblockdata, int i) { + return (IBlockData) iblockdata.set(BlockPressurePlateBinary.POWERED, i > 0); + } + + protected void a(GeneratorAccess generatoraccess, BlockPosition blockposition) { + if (this.material == Material.WOOD) { + generatoraccess.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_WOODEN_PRESSURE_PLATE_CLICK_ON, SoundCategory.BLOCKS, 0.3F, 0.8F); + } else { + generatoraccess.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_STONE_PRESSURE_PLATE_CLICK_ON, SoundCategory.BLOCKS, 0.3F, 0.6F); + } + + } + + protected void b(GeneratorAccess generatoraccess, BlockPosition blockposition) { + if (this.material == Material.WOOD) { + generatoraccess.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_WOODEN_PRESSURE_PLATE_CLICK_OFF, SoundCategory.BLOCKS, 0.3F, 0.7F); + } else { + generatoraccess.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_STONE_PRESSURE_PLATE_CLICK_OFF, SoundCategory.BLOCKS, 0.3F, 0.5F); + } + + } + + protected int b(World world, BlockPosition blockposition) { + AxisAlignedBB axisalignedbb = BlockPressurePlateBinary.c.a(blockposition); + List list; + + switch (this.p) { + case EVERYTHING: + list = world.getEntities((Entity) null, axisalignedbb); + break; + case MOBS: + list = world.a(EntityLiving.class, axisalignedbb); + break; + default: + return 0; + } + + if (!list.isEmpty()) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + // CraftBukkit start - Call interact event when turning on a pressure plate + if (this.getPower(world.getType(blockposition)) == 0) { + org.bukkit.World bworld = world.getWorld(); + org.bukkit.plugin.PluginManager manager = world.getServer().getPluginManager(); + org.bukkit.event.Cancellable cancellable; + + if (entity instanceof EntityHuman) { + cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((EntityHuman) entity, org.bukkit.event.block.Action.PHYSICAL, blockposition, null, null, null); + } else { + cancellable = new EntityInteractEvent(entity.getBukkitEntity(), bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ())); + manager.callEvent((EntityInteractEvent) cancellable); + } + + // We only want to block turning the plate on if all events are cancelled + if (cancellable.isCancelled()) { + continue; + } + } + // CraftBukkit end + + if (!entity.isIgnoreBlockTrigger()) { + return 15; + } + } + } + + return 0; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockPressurePlateBinary.POWERED); + } + + public static enum EnumMobType { + + EVERYTHING, MOBS; + + private EnumMobType() {} + } +} diff --git a/src/main/java/net/minecraft/server/BlockPressurePlateWeighted.java b/src/main/java/net/minecraft/server/BlockPressurePlateWeighted.java new file mode 100644 index 000000000000..7570236edf48 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockPressurePlateWeighted.java @@ -0,0 +1,75 @@ +package net.minecraft.server; + +import org.bukkit.event.entity.EntityInteractEvent; // CraftBukkit + +public class BlockPressurePlateWeighted extends BlockPressurePlateAbstract { + + public static final BlockStateInteger POWER = BlockProperties.al; + private final int weight; + + protected BlockPressurePlateWeighted(int i, Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockPressurePlateWeighted.POWER, 0)); + this.weight = i; + } + + protected int b(World world, BlockPosition blockposition) { + // CraftBukkit start + // int i = Math.min(world.a(Entity.class, BlockPressurePlateWeighted.c.a(blockposition)).size(), this.weight); + int i = 0; + java.util.Iterator iterator = world.a(Entity.class, BlockPressurePlateWeighted.c.a(blockposition)).iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + org.bukkit.event.Cancellable cancellable; + + if (entity instanceof EntityHuman) { + cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((EntityHuman) entity, org.bukkit.event.block.Action.PHYSICAL, blockposition, null, null, null); + } else { + cancellable = new EntityInteractEvent(entity.getBukkitEntity(), world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ())); + world.getServer().getPluginManager().callEvent((EntityInteractEvent) cancellable); + } + + // We only want to block turning the plate on if all events are cancelled + if (!cancellable.isCancelled()) { + i++; + } + } + + i = Math.min(i, this.weight); + // CraftBukkit end + + if (i > 0) { + float f = (float) Math.min(this.weight, i) / (float) this.weight; + + return MathHelper.f(f * 15.0F); + } else { + return 0; + } + } + + protected void a(GeneratorAccess generatoraccess, BlockPosition blockposition) { + generatoraccess.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_METAL_PRESSURE_PLATE_CLICK_ON, SoundCategory.BLOCKS, 0.3F, 0.90000004F); + } + + protected void b(GeneratorAccess generatoraccess, BlockPosition blockposition) { + generatoraccess.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_METAL_PRESSURE_PLATE_CLICK_OFF, SoundCategory.BLOCKS, 0.3F, 0.75F); + } + + protected int getPower(IBlockData iblockdata) { + return (Integer) iblockdata.get(BlockPressurePlateWeighted.POWER); + } + + protected IBlockData a(IBlockData iblockdata, int i) { + return (IBlockData) iblockdata.set(BlockPressurePlateWeighted.POWER, i); + } + + public int a(IWorldReader iworldreader) { + return 10; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockPressurePlateWeighted.POWER); + } +} diff --git a/src/main/java/net/minecraft/server/BlockPumpkinCarved.java b/src/main/java/net/minecraft/server/BlockPumpkinCarved.java new file mode 100644 index 000000000000..265369984087 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockPumpkinCarved.java @@ -0,0 +1,161 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.function.Predicate; + +// CraftBukkit start +import org.bukkit.craftbukkit.util.BlockStateListPopulator; +import org.bukkit.event.block.BlockRedstoneEvent; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +// CraftBukkit end + +public class BlockPumpkinCarved extends BlockFacingHorizontal { + + public static final BlockStateDirection a = BlockFacingHorizontal.FACING; + private ShapeDetector b; + private ShapeDetector c; + private ShapeDetector o; + private ShapeDetector p; + private static final Predicate q = (iblockdata) -> { + return iblockdata != null && (iblockdata.getBlock() == Blocks.CARVED_PUMPKIN || iblockdata.getBlock() == Blocks.JACK_O_LANTERN); + }; + + protected BlockPumpkinCarved(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockPumpkinCarved.a, EnumDirection.NORTH)); + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + if (iblockdata1.getBlock() != iblockdata.getBlock()) { + this.a(world, blockposition); + } + } + + public boolean a(IWorldReader iworldreader, BlockPosition blockposition) { + return this.d().a(iworldreader, blockposition) != null || this.f().a(iworldreader, blockposition) != null; + } + + private void a(World world, BlockPosition blockposition) { + ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = this.e().a(world, blockposition); + int i; + Iterator iterator; + EntityPlayer entityplayer; + int j; + ShapeDetectorBlock shapedetectorblock; + int k; + + BlockStateListPopulator blockList = new BlockStateListPopulator(world); // CraftBukkit - Use BlockStateListPopulator + if (shapedetector_shapedetectorcollection != null) { + for (i = 0; i < this.e().b(); ++i) { + ShapeDetectorBlock shapedetectorblock1 = shapedetector_shapedetectorcollection.a(0, i, 0); + + blockList.setTypeAndData(shapedetectorblock1.getPosition(), Blocks.AIR.getBlockData(), 2); // CraftBukkit + } + + EntitySnowman entitysnowman = EntityTypes.SNOW_GOLEM.create(world); // Paper + BlockPosition blockposition1 = shapedetector_shapedetectorcollection.a(0, 2, 0).getPosition(); + + entitysnowman.setPositionRotation((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.05D, (double) blockposition1.getZ() + 0.5D, 0.0F, 0.0F); + // CraftBukkit start + if (!world.addEntity(entitysnowman, SpawnReason.BUILD_SNOWMAN)) { + return; + } + blockList.updateList(); + // CraftBukkit end + iterator = world.a(EntityPlayer.class, entitysnowman.getBoundingBox().g(5.0D)).iterator(); + + while (iterator.hasNext()) { + entityplayer = (EntityPlayer) iterator.next(); + CriterionTriggers.n.a(entityplayer, (Entity) entitysnowman); + } + + j = Block.getCombinedId(Blocks.SNOW_BLOCK.getBlockData()); + world.triggerEffect(2001, blockposition1, j); + world.triggerEffect(2001, blockposition1.up(), j); + + for (k = 0; k < this.e().b(); ++k) { + shapedetectorblock = shapedetector_shapedetectorcollection.a(0, k, 0); + world.update(shapedetectorblock.getPosition(), Blocks.AIR); + } + } else { + shapedetector_shapedetectorcollection = this.g().a(world, blockposition); + if (shapedetector_shapedetectorcollection != null) { + for (i = 0; i < this.g().c(); ++i) { + for (int l = 0; l < this.g().b(); ++l) { + blockList.setTypeAndData(shapedetector_shapedetectorcollection.a(i, l, 0).getPosition(), Blocks.AIR.getBlockData(), 2); // CraftBukkit + } + } + + BlockPosition blockposition2 = shapedetector_shapedetectorcollection.a(1, 2, 0).getPosition(); + EntityIronGolem entityirongolem = EntityTypes.IRON_GOLEM.create(world); // Paper + + entityirongolem.setPlayerCreated(true); + entityirongolem.setPositionRotation((double) blockposition2.getX() + 0.5D, (double) blockposition2.getY() + 0.05D, (double) blockposition2.getZ() + 0.5D, 0.0F, 0.0F); + // CraftBukkit start + if (!world.addEntity(entityirongolem, SpawnReason.BUILD_IRONGOLEM)) { + return; + } + blockList.updateList(); + // CraftBukkit end + iterator = world.a(EntityPlayer.class, entityirongolem.getBoundingBox().g(5.0D)).iterator(); + + while (iterator.hasNext()) { + entityplayer = (EntityPlayer) iterator.next(); + CriterionTriggers.n.a(entityplayer, (Entity) entityirongolem); + } + + for (j = 0; j < 120; ++j) { + world.addParticle(Particles.E, (double) blockposition2.getX() + world.random.nextDouble(), (double) blockposition2.getY() + world.random.nextDouble() * 3.9D, (double) blockposition2.getZ() + world.random.nextDouble(), 0.0D, 0.0D, 0.0D); + } + + for (j = 0; j < this.g().c(); ++j) { + for (k = 0; k < this.g().b(); ++k) { + shapedetectorblock = shapedetector_shapedetectorcollection.a(j, k, 0); + world.update(shapedetectorblock.getPosition(), Blocks.AIR); + } + } + } + } + + } + + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + return (IBlockData) this.getBlockData().set(BlockPumpkinCarved.a, blockactioncontext.f().opposite()); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockPumpkinCarved.a); + } + + protected ShapeDetector d() { + if (this.b == null) { + this.b = ShapeDetectorBuilder.a().a(" ", "#", "#").a('#', ShapeDetectorBlock.a(BlockStatePredicate.a(Blocks.SNOW_BLOCK))).b(); + } + + return this.b; + } + + protected ShapeDetector e() { + if (this.c == null) { + this.c = ShapeDetectorBuilder.a().a("^", "#", "#").a('^', ShapeDetectorBlock.a(BlockPumpkinCarved.q)).a('#', ShapeDetectorBlock.a(BlockStatePredicate.a(Blocks.SNOW_BLOCK))).b(); + } + + return this.c; + } + + protected ShapeDetector f() { + if (this.o == null) { + this.o = ShapeDetectorBuilder.a().a("~ ~", "###", "~#~").a('#', ShapeDetectorBlock.a(BlockStatePredicate.a(Blocks.IRON_BLOCK))).a('~', ShapeDetectorBlock.a(MaterialPredicate.a(Material.AIR))).b(); + } + + return this.o; + } + + protected ShapeDetector g() { + if (this.p == null) { + this.p = ShapeDetectorBuilder.a().a("~^~", "###", "~#~").a('^', ShapeDetectorBlock.a(BlockPumpkinCarved.q)).a('#', ShapeDetectorBlock.a(BlockStatePredicate.a(Blocks.IRON_BLOCK))).a('~', ShapeDetectorBlock.a(MaterialPredicate.a(Material.AIR))).b(); + } + + return this.p; + } +} diff --git a/src/main/java/net/minecraft/server/BlockRedstoneComparator.java b/src/main/java/net/minecraft/server/BlockRedstoneComparator.java new file mode 100644 index 000000000000..58d58bd146ca --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockRedstoneComparator.java @@ -0,0 +1,161 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.Random; +import javax.annotation.Nullable; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockRedstoneComparator extends BlockDiodeAbstract implements ITileEntity { + + public static final BlockStateEnum MODE = BlockProperties.aq; + + public BlockRedstoneComparator(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockRedstoneComparator.FACING, EnumDirection.NORTH)).set(BlockRedstoneComparator.c, false)).set(BlockRedstoneComparator.MODE, BlockPropertyComparatorMode.COMPARE)); + } + + protected int k(IBlockData iblockdata) { + return 2; + } + + protected int b(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) { + TileEntity tileentity = iblockaccess.getTileEntity(blockposition); + + return tileentity instanceof TileEntityComparator ? ((TileEntityComparator) tileentity).c() : 0; + } + + private int e(World world, BlockPosition blockposition, IBlockData iblockdata) { + return iblockdata.get(BlockRedstoneComparator.MODE) == BlockPropertyComparatorMode.SUBTRACT ? Math.max(this.b(world, blockposition, iblockdata) - this.b((IWorldReader) world, blockposition, iblockdata), 0) : this.b(world, blockposition, iblockdata); + } + + protected boolean a(World world, BlockPosition blockposition, IBlockData iblockdata) { + int i = this.b(world, blockposition, iblockdata); + + return i >= 15 ? true : (i == 0 ? false : i >= this.b((IWorldReader) world, blockposition, iblockdata)); + } + + protected void a(World world, BlockPosition blockposition) { + world.n(blockposition); + } + + protected int b(World world, BlockPosition blockposition, IBlockData iblockdata) { + int i = super.b(world, blockposition, iblockdata); + EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockRedstoneComparator.FACING); + BlockPosition blockposition1 = blockposition.shift(enumdirection); + IBlockData iblockdata1 = world.getType(blockposition1); + + if (iblockdata1.isComplexRedstone()) { + i = iblockdata1.a(world, blockposition1); + } else if (i < 15 && iblockdata1.isOccluding()) { + blockposition1 = blockposition1.shift(enumdirection); + iblockdata1 = world.getType(blockposition1); + if (iblockdata1.isComplexRedstone()) { + i = iblockdata1.a(world, blockposition1); + } else if (iblockdata1.isAir()) { + EntityItemFrame entityitemframe = this.a(world, enumdirection, blockposition1); + + if (entityitemframe != null) { + i = entityitemframe.q(); + } + } + } + + return i; + } + + @Nullable + private EntityItemFrame a(World world, EnumDirection enumdirection, BlockPosition blockposition) { + // CraftBukkit - decompile error + List list = world.a(EntityItemFrame.class, new AxisAlignedBB((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), (double) (blockposition.getX() + 1), (double) (blockposition.getY() + 1), (double) (blockposition.getZ() + 1)), (java.util.function.Predicate) (entityitemframe) -> { + return entityitemframe != null && entityitemframe.getDirection() == enumdirection; + }); + + return list.size() == 1 ? (EntityItemFrame) list.get(0) : null; + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + if (!entityhuman.abilities.mayBuild) { + return false; + } else { + iblockdata = (IBlockData) iblockdata.a((IBlockState) BlockRedstoneComparator.MODE); + float f3 = iblockdata.get(BlockRedstoneComparator.MODE) == BlockPropertyComparatorMode.SUBTRACT ? 0.55F : 0.5F; + + world.a(entityhuman, blockposition, SoundEffects.BLOCK_COMPARATOR_CLICK, SoundCategory.BLOCKS, 0.3F, f3); + world.setTypeAndData(blockposition, iblockdata, 2); + this.f(world, blockposition, iblockdata); + return true; + } + } + + protected void c(World world, BlockPosition blockposition, IBlockData iblockdata) { + if (!world.getBlockTickList().b(blockposition, this)) { + int i = this.e(world, blockposition, iblockdata); + TileEntity tileentity = world.getTileEntity(blockposition); + int j = tileentity instanceof TileEntityComparator ? ((TileEntityComparator) tileentity).c() : 0; + + if (i != j || (Boolean) iblockdata.get(BlockRedstoneComparator.c) != this.a(world, blockposition, iblockdata)) { + TickListPriority ticklistpriority = this.c((IBlockAccess) world, blockposition, iblockdata) ? TickListPriority.HIGH : TickListPriority.NORMAL; + + world.getBlockTickList().a(blockposition, this, 2, ticklistpriority); + } + + } + } + + private void f(World world, BlockPosition blockposition, IBlockData iblockdata) { + int i = this.e(world, blockposition, iblockdata); + TileEntity tileentity = world.getTileEntity(blockposition); + int j = 0; + + if (tileentity instanceof TileEntityComparator) { + TileEntityComparator tileentitycomparator = (TileEntityComparator) tileentity; + + j = tileentitycomparator.c(); + tileentitycomparator.a(i); + } + + if (j != i || iblockdata.get(BlockRedstoneComparator.MODE) == BlockPropertyComparatorMode.COMPARE) { + boolean flag = this.a(world, blockposition, iblockdata); + boolean flag1 = (Boolean) iblockdata.get(BlockRedstoneComparator.c); + + if (flag1 && !flag) { + // CraftBukkit start + if (CraftEventFactory.callRedstoneChange(world, blockposition, 15, 0).getNewCurrent() != 0) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockRedstoneComparator.c, false), 2); + } else if (!flag1 && flag) { + // CraftBukkit start + if (CraftEventFactory.callRedstoneChange(world, blockposition, 0, 15).getNewCurrent() != 15) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockRedstoneComparator.c, true), 2); + } + + this.d(world, blockposition, iblockdata); + } + + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + this.f(world, blockposition, iblockdata); + } + + public boolean a(IBlockData iblockdata, World world, BlockPosition blockposition, int i, int j) { + super.a(iblockdata, world, blockposition, i, j); + TileEntity tileentity = world.getTileEntity(blockposition); + + return tileentity != null && tileentity.c(i, j); + } + + public TileEntity a(IBlockAccess iblockaccess) { + return new TileEntityComparator(); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockRedstoneComparator.FACING, BlockRedstoneComparator.MODE, BlockRedstoneComparator.c); + } +} diff --git a/src/main/java/net/minecraft/server/BlockRedstoneLamp.java b/src/main/java/net/minecraft/server/BlockRedstoneLamp.java new file mode 100644 index 000000000000..8fdbbd74e6d2 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockRedstoneLamp.java @@ -0,0 +1,67 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockRedstoneLamp extends Block { + + public static final BlockStateBoolean a = BlockRedstoneTorch.LIT; + + public BlockRedstoneLamp(Block.Info block_info) { + super(block_info); + this.v((IBlockData) this.getBlockData().set(BlockRedstoneLamp.a, false)); + } + + public int m(IBlockData iblockdata) { + return (Boolean) iblockdata.get(BlockRedstoneLamp.a) ? super.m(iblockdata) : 0; + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + super.onPlace(iblockdata, world, blockposition, iblockdata1); + } + + @Nullable + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + return (IBlockData) this.getBlockData().set(BlockRedstoneLamp.a, blockactioncontext.getWorld().isBlockIndirectlyPowered(blockactioncontext.getClickPosition())); + } + + public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + if (!world.isClientSide) { + boolean flag = (Boolean) iblockdata.get(BlockRedstoneLamp.a); + + if (flag != world.isBlockIndirectlyPowered(blockposition)) { + if (flag) { + world.getBlockTickList().a(blockposition, this, 4); + } else { + // CraftBukkit start + if (CraftEventFactory.callRedstoneChange(world, blockposition, 0, 15).getNewCurrent() != 15) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.a((IBlockState) BlockRedstoneLamp.a), 2); + } + } + + } + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!world.isClientSide) { + if ((Boolean) iblockdata.get(BlockRedstoneLamp.a) && !world.isBlockIndirectlyPowered(blockposition)) { + // CraftBukkit start + if (CraftEventFactory.callRedstoneChange(world, blockposition, 15, 0).getNewCurrent() != 0) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.a((IBlockState) BlockRedstoneLamp.a), 2); + } + + } + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockRedstoneLamp.a); + } +} diff --git a/src/main/java/net/minecraft/server/BlockRedstoneOre.java b/src/main/java/net/minecraft/server/BlockRedstoneOre.java new file mode 100644 index 000000000000..7f2dcefa0a94 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockRedstoneOre.java @@ -0,0 +1,139 @@ +package net.minecraft.server; + +import java.util.Random; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityInteractEvent; +// CraftBukkit end + +public class BlockRedstoneOre extends Block { + + public static final BlockStateBoolean a = BlockRedstoneTorch.LIT; + + public BlockRedstoneOre(Block.Info block_info) { + super(block_info); + this.v((IBlockData) this.getBlockData().set(BlockRedstoneOre.a, false)); + } + + public int m(IBlockData iblockdata) { + return (Boolean) iblockdata.get(BlockRedstoneOre.a) ? super.m(iblockdata) : 0; + } + + public void attack(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman) { + interact(iblockdata, world, blockposition, entityhuman); // CraftBukkit - add entityhuman + super.attack(iblockdata, world, blockposition, entityhuman); + } + + public void stepOn(World world, BlockPosition blockposition, Entity entity) { + // CraftBukkit start + // interact(world.getType(blockposition), world, blockposition); + // super.stepOn(world, blockposition, entity); + if (entity instanceof EntityHuman) { + org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((EntityHuman) entity, org.bukkit.event.block.Action.PHYSICAL, blockposition, null, null, null); + if (!event.isCancelled()) { + interact(world.getType(blockposition), world, blockposition, entity); // add entity + super.stepOn(world, blockposition, entity); + } + } else { + EntityInteractEvent event = new EntityInteractEvent(entity.getBukkitEntity(), world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ())); + world.getServer().getPluginManager().callEvent(event); + if (!event.isCancelled()) { + interact(world.getType(blockposition), world, blockposition, entity); // add entity + super.stepOn(world, blockposition, entity); + } + } + // CraftBukkit end + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + interact(iblockdata, world, blockposition, entityhuman); // CraftBukkit - add entityhuman + return super.interact(iblockdata, world, blockposition, entityhuman, enumhand, enumdirection, f, f1, f2); + } + + private static void interact(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) { // CraftBukkit - add Entity + playEffect(world, blockposition); + if (!(Boolean) iblockdata.get(BlockRedstoneOre.a)) { + // CraftBukkit start + if (CraftEventFactory.callEntityChangeBlockEvent(entity, blockposition, iblockdata.set(BlockRedstoneOre.a, true)).isCancelled()) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockRedstoneOre.a, true), 3); + } + + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if ((Boolean) iblockdata.get(BlockRedstoneOre.a)) { + // CraftBukkit start + if (CraftEventFactory.callBlockFadeEvent(world, blockposition, iblockdata.set(BlockRedstoneOre.a, false)).isCancelled()) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockRedstoneOre.a, false), 3); + } + + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return Items.REDSTONE; + } + + public int getDropCount(IBlockData iblockdata, int i, World world, BlockPosition blockposition, Random random) { + return this.a(iblockdata, random) + random.nextInt(i + 1); + } + + public int a(IBlockData iblockdata, Random random) { + return 4 + random.nextInt(2); + } + + public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) { + super.dropNaturally(iblockdata, world, blockposition, f, i); + /* CraftBukkit start - Delegated to getExpDrop + if (this.getDropType(iblockdata, world, blockposition, i) != this) { + int j = 1 + world.random.nextInt(5); + + this.dropExperience(world, blockposition, j); + } + // */ + + } + + @Override + public int getExpDrop(IBlockData iblockdata, World world, BlockPosition blockposition, int enchantmentLevel) { + if (this.getDropType(iblockdata, world, blockposition, enchantmentLevel) != this) { + int j = 1 + world.random.nextInt(5); + + return j; + } + return 0; + // CraftBukkit end + } + + private static void playEffect(World world, BlockPosition blockposition) { + double d0 = 0.5625D; + Random random = world.random; + EnumDirection[] aenumdirection = EnumDirection.values(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + BlockPosition blockposition1 = blockposition.shift(enumdirection); + + if (!world.getType(blockposition1).f(world, blockposition1)) { + EnumDirection.EnumAxis enumdirection_enumaxis = enumdirection.k(); + double d1 = enumdirection_enumaxis == EnumDirection.EnumAxis.X ? 0.5D + 0.5625D * (double) enumdirection.getAdjacentX() : (double) random.nextFloat(); + double d2 = enumdirection_enumaxis == EnumDirection.EnumAxis.Y ? 0.5D + 0.5625D * (double) enumdirection.getAdjacentY() : (double) random.nextFloat(); + double d3 = enumdirection_enumaxis == EnumDirection.EnumAxis.Z ? 0.5D + 0.5625D * (double) enumdirection.getAdjacentZ() : (double) random.nextFloat(); + + world.addParticle(ParticleParamRedstone.a, (double) blockposition.getX() + d1, (double) blockposition.getY() + d2, (double) blockposition.getZ() + d3, 0.0D, 0.0D, 0.0D); + } + } + + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockRedstoneOre.a); + } +} diff --git a/src/main/java/net/minecraft/server/BlockRedstoneTorch.java b/src/main/java/net/minecraft/server/BlockRedstoneTorch.java new file mode 100644 index 000000000000..74b63fe1d19f --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockRedstoneTorch.java @@ -0,0 +1,187 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public class BlockRedstoneTorch extends BlockTorch { + + public static final BlockStateBoolean LIT = BlockProperties.o; + private static final Map> b = new java.util.WeakHashMap(); // Spigot + + protected BlockRedstoneTorch(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockRedstoneTorch.LIT, true)); + } + + public int a(IWorldReader iworldreader) { + return 2; + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + EnumDirection[] aenumdirection = EnumDirection.values(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + + world.applyPhysics(blockposition.shift(enumdirection), this); + } + + } + + public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) { + if (!flag) { + EnumDirection[] aenumdirection = EnumDirection.values(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + + world.applyPhysics(blockposition.shift(enumdirection), this); + } + + } + } + + public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return (Boolean) iblockdata.get(BlockRedstoneTorch.LIT) && EnumDirection.UP != enumdirection ? 15 : 0; + } + + protected boolean a(World world, BlockPosition blockposition, IBlockData iblockdata) { + return world.isBlockFacePowered(blockposition.down(), EnumDirection.DOWN); + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + a(iblockdata, world, blockposition, random, this.a(world, blockposition, iblockdata)); + } + + public static void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random, boolean flag) { + List list = (List) BlockRedstoneTorch.b.get(world); + + // Paper start + if (list != null) { + int index = 0; + while (index < list.size() && world.getTime() - ((BlockRedstoneTorch.RedstoneUpdateInfo) list.get(index)).getTime() > 60L) { + index++; + } + if (index > 0) { + list.subList(0, index).clear(); + } + } + // Paper end + + // CraftBukkit start + org.bukkit.plugin.PluginManager manager = world.getServer().getPluginManager(); + org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + int oldCurrent = ((Boolean) iblockdata.get(BlockRedstoneTorch.LIT)).booleanValue() ? 15 : 0; + + BlockRedstoneEvent event = new BlockRedstoneEvent(block, oldCurrent, oldCurrent); + // CraftBukkit end + if ((Boolean) iblockdata.get(BlockRedstoneTorch.LIT)) { + if (flag) { + // CraftBukkit start + if (oldCurrent != 0) { + event.setNewCurrent(0); + manager.callEvent(event); + if (event.getNewCurrent() != 0) { + return; + } + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockRedstoneTorch.LIT, false), 3); + if (a(world, blockposition, true)) { + world.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_REDSTONE_TORCH_BURNOUT, SoundCategory.BLOCKS, 0.5F, 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F); + + for (int i = 0; i < 5; ++i) { + double d0 = (double) blockposition.getX() + random.nextDouble() * 0.6D + 0.2D; + double d1 = (double) blockposition.getY() + random.nextDouble() * 0.6D + 0.2D; + double d2 = (double) blockposition.getZ() + random.nextDouble() * 0.6D + 0.2D; + + world.addParticle(Particles.M, d0, d1, d2, 0.0D, 0.0D, 0.0D); + } + + world.getBlockTickList().a(blockposition, world.getType(blockposition).getBlock(), 160); + } + } + } else if (!flag && !a(world, blockposition, false)) { + // CraftBukkit start + if (oldCurrent != 15) { + event.setNewCurrent(15); + manager.callEvent(event); + if (event.getNewCurrent() != 15) { + return; + } + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockRedstoneTorch.LIT, true), 3); + } + + } + + public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + if ((Boolean) iblockdata.get(BlockRedstoneTorch.LIT) == this.a(world, blockposition, iblockdata) && !world.getBlockTickList().b(blockposition, this)) { + world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world)); + } + + } + + public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return enumdirection == EnumDirection.DOWN ? iblockdata.a(iblockaccess, blockposition, enumdirection) : 0; + } + + public boolean isPowerSource(IBlockData iblockdata) { + return true; + } + + public int m(IBlockData iblockdata) { + return (Boolean) iblockdata.get(BlockRedstoneTorch.LIT) ? super.m(iblockdata) : 0; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockRedstoneTorch.LIT); + } + + private static boolean a(World world, BlockPosition blockposition, boolean flag) { + List list = (List) BlockRedstoneTorch.b.get(world); + + if (list == null) { + list = Lists.newArrayList(); + BlockRedstoneTorch.b.put(world, list); + } + + if (flag) { + ((List) list).add(new BlockRedstoneTorch.RedstoneUpdateInfo(blockposition.h(), world.getTime())); + } + + int i = 0; + + for (int j = 0; j < ((List) list).size(); ++j) { + BlockRedstoneTorch.RedstoneUpdateInfo blockredstonetorch_redstoneupdateinfo = (BlockRedstoneTorch.RedstoneUpdateInfo) ((List) list).get(j); + + if (blockredstonetorch_redstoneupdateinfo.a.equals(blockposition)) { + ++i; + if (i >= 8) { + return true; + } + } + } + + return false; + } + + public static class RedstoneUpdateInfo { + + private final BlockPosition a; + private final long b; final long getTime() { return this.b; } // Paper - OBFHELPER + + public RedstoneUpdateInfo(BlockPosition blockposition, long i) { + this.a = blockposition; + this.b = i; + } + } +} diff --git a/src/main/java/net/minecraft/server/BlockRedstoneWire.java b/src/main/java/net/minecraft/server/BlockRedstoneWire.java new file mode 100644 index 000000000000..a09aa69444c0 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockRedstoneWire.java @@ -0,0 +1,585 @@ +package net.minecraft.server; + +import com.destroystokyo.paper.PaperConfig; +import com.destroystokyo.paper.util.RedstoneWireTurbo; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nullable; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public class BlockRedstoneWire extends Block { + + public static final BlockStateEnum NORTH = BlockProperties.M; + public static final BlockStateEnum EAST = BlockProperties.L; + public static final BlockStateEnum SOUTH = BlockProperties.N; + public static final BlockStateEnum WEST = BlockProperties.O; + public static final BlockStateInteger POWER = BlockProperties.al; + public static final Map> q = Maps.newEnumMap(ImmutableMap.of(EnumDirection.NORTH, BlockRedstoneWire.NORTH, EnumDirection.EAST, BlockRedstoneWire.EAST, EnumDirection.SOUTH, BlockRedstoneWire.SOUTH, EnumDirection.WEST, BlockRedstoneWire.WEST)); + protected static final VoxelShape[] r = new VoxelShape[] { Block.a(3.0D, 0.0D, 3.0D, 13.0D, 1.0D, 13.0D), Block.a(3.0D, 0.0D, 3.0D, 13.0D, 1.0D, 16.0D), Block.a(0.0D, 0.0D, 3.0D, 13.0D, 1.0D, 13.0D), Block.a(0.0D, 0.0D, 3.0D, 13.0D, 1.0D, 16.0D), Block.a(3.0D, 0.0D, 0.0D, 13.0D, 1.0D, 13.0D), Block.a(3.0D, 0.0D, 0.0D, 13.0D, 1.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 13.0D, 1.0D, 13.0D), Block.a(0.0D, 0.0D, 0.0D, 13.0D, 1.0D, 16.0D), Block.a(3.0D, 0.0D, 3.0D, 16.0D, 1.0D, 13.0D), Block.a(3.0D, 0.0D, 3.0D, 16.0D, 1.0D, 16.0D), Block.a(0.0D, 0.0D, 3.0D, 16.0D, 1.0D, 13.0D), Block.a(0.0D, 0.0D, 3.0D, 16.0D, 1.0D, 16.0D), Block.a(3.0D, 0.0D, 0.0D, 16.0D, 1.0D, 13.0D), Block.a(3.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 13.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D)}; + public boolean canProvidePower() { return this.s; } // Paper - OBFHELPER + public void setCanProvidePower(boolean value) { this.s = value; } // Paper - OBFHELPER + private boolean s = true; + private Set getBlocksNeedingUpdate() { return this.t; } // Paper - OBFHELPER + private final Set t = Sets.newHashSet(); + + public BlockRedstoneWire(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockRedstoneWire.NORTH, BlockPropertyRedstoneSide.NONE)).set(BlockRedstoneWire.EAST, BlockPropertyRedstoneSide.NONE)).set(BlockRedstoneWire.SOUTH, BlockPropertyRedstoneSide.NONE)).set(BlockRedstoneWire.WEST, BlockPropertyRedstoneSide.NONE)).set(BlockRedstoneWire.POWER, 0)); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockRedstoneWire.r[w(iblockdata)]; + } + + private static int w(IBlockData iblockdata) { + int i = 0; + boolean flag = iblockdata.get(BlockRedstoneWire.NORTH) != BlockPropertyRedstoneSide.NONE; + boolean flag1 = iblockdata.get(BlockRedstoneWire.EAST) != BlockPropertyRedstoneSide.NONE; + boolean flag2 = iblockdata.get(BlockRedstoneWire.SOUTH) != BlockPropertyRedstoneSide.NONE; + boolean flag3 = iblockdata.get(BlockRedstoneWire.WEST) != BlockPropertyRedstoneSide.NONE; + + if (flag || flag2 && !flag && !flag1 && !flag3) { + i |= 1 << EnumDirection.NORTH.get2DRotationValue(); + } + + if (flag1 || flag3 && !flag && !flag1 && !flag2) { + i |= 1 << EnumDirection.EAST.get2DRotationValue(); + } + + if (flag2 || flag && !flag1 && !flag2 && !flag3) { + i |= 1 << EnumDirection.SOUTH.get2DRotationValue(); + } + + if (flag3 || flag1 && !flag && !flag2 && !flag3) { + i |= 1 << EnumDirection.WEST.get2DRotationValue(); + } + + return i; + } + + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + World world = blockactioncontext.getWorld(); + BlockPosition blockposition = blockactioncontext.getClickPosition(); + + return (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.getBlockData().set(BlockRedstoneWire.WEST, this.a((IBlockAccess) world, blockposition, EnumDirection.WEST))).set(BlockRedstoneWire.EAST, this.a((IBlockAccess) world, blockposition, EnumDirection.EAST))).set(BlockRedstoneWire.NORTH, this.a((IBlockAccess) world, blockposition, EnumDirection.NORTH))).set(BlockRedstoneWire.SOUTH, this.a((IBlockAccess) world, blockposition, EnumDirection.SOUTH)); + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + return enumdirection == EnumDirection.DOWN ? iblockdata : (enumdirection == EnumDirection.UP ? (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) iblockdata.set(BlockRedstoneWire.WEST, this.a((IBlockAccess) generatoraccess, blockposition, EnumDirection.WEST))).set(BlockRedstoneWire.EAST, this.a((IBlockAccess) generatoraccess, blockposition, EnumDirection.EAST))).set(BlockRedstoneWire.NORTH, this.a((IBlockAccess) generatoraccess, blockposition, EnumDirection.NORTH))).set(BlockRedstoneWire.SOUTH, this.a((IBlockAccess) generatoraccess, blockposition, EnumDirection.SOUTH)) : (IBlockData) iblockdata.set((IBlockState) BlockRedstoneWire.q.get(enumdirection), this.a((IBlockAccess) generatoraccess, blockposition, enumdirection))); + } + + public void b(IBlockData iblockdata, GeneratorAccess generatoraccess, BlockPosition blockposition, int i) { + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + BlockPropertyRedstoneSide blockpropertyredstoneside = (BlockPropertyRedstoneSide) iblockdata.get((IBlockState) BlockRedstoneWire.q.get(enumdirection)); + + if (blockpropertyredstoneside != BlockPropertyRedstoneSide.NONE && generatoraccess.getType(blockposition_b.g(blockposition).c(enumdirection)).getBlock() != this) { + blockposition_b.c(EnumDirection.DOWN); + IBlockData iblockdata1 = generatoraccess.getType(blockposition_b); + + if (iblockdata1.getBlock() != Blocks.OBSERVER) { + BlockPosition blockposition1 = blockposition_b.shift(enumdirection.opposite()); + IBlockData iblockdata2 = iblockdata1.updateState(enumdirection.opposite(), generatoraccess.getType(blockposition1), generatoraccess, blockposition_b, blockposition1); + + a(iblockdata1, iblockdata2, generatoraccess, blockposition_b, i); + } + + blockposition_b.g(blockposition).c(enumdirection).c(EnumDirection.UP); + IBlockData iblockdata3 = generatoraccess.getType(blockposition_b); + + if (iblockdata3.getBlock() != Blocks.OBSERVER) { + BlockPosition blockposition2 = blockposition_b.shift(enumdirection.opposite()); + IBlockData iblockdata4 = iblockdata3.updateState(enumdirection.opposite(), generatoraccess.getType(blockposition2), generatoraccess, blockposition_b, blockposition2); + + a(iblockdata3, iblockdata4, generatoraccess, blockposition_b, i); + } + } + } + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + + } + + private BlockPropertyRedstoneSide a(IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + BlockPosition blockposition1 = blockposition.shift(enumdirection); + IBlockData iblockdata = iblockaccess.getType(blockposition.shift(enumdirection)); + IBlockData iblockdata1 = iblockaccess.getType(blockposition.up()); + + if (!iblockdata1.isOccluding()) { + boolean flag = iblockaccess.getType(blockposition1).q() || iblockaccess.getType(blockposition1).getBlock() == Blocks.GLOWSTONE; + + if (flag && k(iblockaccess.getType(blockposition1.up()))) { + if (iblockdata.k()) { + return BlockPropertyRedstoneSide.UP; + } + + return BlockPropertyRedstoneSide.SIDE; + } + } + + return !a(iblockaccess.getType(blockposition1), enumdirection) && (iblockdata.isOccluding() || !k(iblockaccess.getType(blockposition1.down()))) ? BlockPropertyRedstoneSide.NONE : BlockPropertyRedstoneSide.SIDE; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + IBlockData iblockdata1 = iworldreader.getType(blockposition.down()); + + return iblockdata1.q() || iblockdata1.getBlock() == Blocks.GLOWSTONE; + } + + // Paper start - Optimize redstone + // The bulk of the new functionality is found in RedstoneWireTurbo.java + RedstoneWireTurbo turbo = new RedstoneWireTurbo(this); + + /* + * Modified version of pre-existing updateSurroundingRedstone, which is called from + * this.neighborChanged and a few other methods in this class. + * Note: Added 'source' argument so as to help determine direction of information flow + */ + private IBlockData updateSurroundingRedstone(World worldIn, BlockPosition pos, IBlockData state, BlockPosition source) { + if (worldIn.paperConfig.useEigencraftRedstone) { + return turbo.updateSurroundingRedstone(worldIn, pos, state, source); + } + return a(worldIn, pos, state); + } + + /* + * Slightly modified method to compute redstone wire power levels from neighboring blocks. + * Modifications cut the number of power level changes by about 45% from vanilla, and this + * optimization synergizes well with the breadth-first search implemented in + * RedstoneWireTurbo. + * Note: RedstoneWireTurbo contains a faster version of this code. + * Note: Made this public so that RedstoneWireTurbo can access it. + */ + public IBlockData calculateCurrentChanges(World worldIn, BlockPosition pos1, BlockPosition pos2, IBlockData state) { + IBlockData iblockstate = state; + int i = state.get(POWER).intValue(); + int j = 0; + j = this.getPower(j, worldIn.getType(pos2)); + this.setCanProvidePower(false); + int k = worldIn.isBlockIndirectlyGettingPowered(pos1); + this.setCanProvidePower(true); + + if (!worldIn.paperConfig.useEigencraftRedstone) { + // This code is totally redundant to if statements just below the loop. + if (k > 0 && k > j - 1) { + j = k; + } + } + + int l = 0; + + // The variable 'k' holds the maximum redstone power value of any adjacent blocks. + // If 'k' has the highest level of all neighbors, then the power level of this + // redstone wire will be set to 'k'. If 'k' is already 15, then nothing inside the + // following loop can affect the power level of the wire. Therefore, the loop is + // skipped if k is already 15. + if (!worldIn.paperConfig.useEigencraftRedstone || k < 15) { + for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) { + BlockPosition blockpos = pos1.shift(enumfacing); + boolean flag = blockpos.getX() != pos2.getX() || blockpos.getZ() != pos2.getZ(); + + if (flag) { + l = this.getPower(l, worldIn.getType(blockpos)); + } + + if (worldIn.getType(blockpos).isOccluding() && !worldIn.getType(pos1.up()).isOccluding()) { + if (flag && pos1.getY() >= pos2.getY()) { + l = this.getPower(l, worldIn.getType(blockpos.up())); + } + } else if (!worldIn.getType(blockpos).isOccluding() && flag && pos1.getY() <= pos2.getY()) { + l = this.getPower(l, worldIn.getType(blockpos.down())); + } + } + } + + if (!worldIn.paperConfig.useEigencraftRedstone) { + // The old code would decrement the wire value only by 1 at a time. + if (l > j) { + j = l - 1; + } else if (j > 0) { + --j; + } else { + j = 0; + } + + if (k > j - 1) { + j = k; + } + } else { + // The new code sets this RedstoneWire block's power level to the highest neighbor + // minus 1. This usually results in wire power levels dropping by 2 at a time. + // This optimization alone has no impact on update order, only the number of updates. + j = l - 1; + + // If 'l' turns out to be zero, then j will be set to -1, but then since 'k' will + // always be in the range of 0 to 15, the following if will correct that. + if (k > j) j = k; + } + + if (i != j) { + state = state.set(POWER, Integer.valueOf(j)); + + if (worldIn.getType(pos1) == iblockstate) { + worldIn.setTypeAndData(pos1, state, 2); + } + + if (!worldIn.paperConfig.useEigencraftRedstone) { + // The new search algorithm keeps track of blocks needing updates in its own data structures, + // so only add anything to blocksNeedingUpdate if we're using the vanilla update algorithm. + this.getBlocksNeedingUpdate().add(pos1); + + for (EnumDirection enumfacing1 : EnumDirection.values()) { + this.getBlocksNeedingUpdate().add(pos1.shift(enumfacing1)); + } + } + } + + return state; + } + // Paper end + private IBlockData a(World world, BlockPosition blockposition, IBlockData iblockdata) { + iblockdata = this.b(world, blockposition, iblockdata); + List list = Lists.newArrayList(this.t); + + this.t.clear(); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + BlockPosition blockposition1 = (BlockPosition) iterator.next(); + + world.applyPhysics(blockposition1, this); + } + + return iblockdata; + } + + private IBlockData b(World world, BlockPosition blockposition, IBlockData iblockdata) { + IBlockData iblockdata1 = iblockdata; + int i = (Integer) iblockdata.get(BlockRedstoneWire.POWER); + byte b0 = 0; + int j = this.getPower(b0, iblockdata); + + this.s = false; + int k = world.u(blockposition); + + this.s = true; + if (k > 0 && k > j - 1) { + j = k; + } + + int l = 0; + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + BlockPosition blockposition1 = blockposition.shift(enumdirection); + boolean flag = blockposition1.getX() != blockposition.getX() || blockposition1.getZ() != blockposition.getZ(); + IBlockData iblockdata2 = world.getType(blockposition1); + + if (flag) { + l = this.getPower(l, iblockdata2); + } + + if (iblockdata2.isOccluding() && !world.getType(blockposition.up()).isOccluding()) { + if (flag && blockposition.getY() >= blockposition.getY()) { + l = this.getPower(l, world.getType(blockposition1.up())); + } + } else if (!iblockdata2.isOccluding() && flag && blockposition.getY() <= blockposition.getY()) { + l = this.getPower(l, world.getType(blockposition1.down())); + } + } + + if (l > j) { + j = l - 1; + } else if (j > 0) { + --j; + } else { + j = 0; + } + + if (k > j - 1) { + j = k; + } + + // CraftBukkit start + if (i != j) { + BlockRedstoneEvent event = new BlockRedstoneEvent(world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), i, j); + world.getServer().getPluginManager().callEvent(event); + + j = event.getNewCurrent(); + } + // CraftBukkit end + + if (i != j) { + iblockdata = (IBlockData) iblockdata.set(BlockRedstoneWire.POWER, j); + if (world.getType(blockposition) == iblockdata1) { + world.setTypeAndData(blockposition, iblockdata, 2); + } + + this.t.add(blockposition); + EnumDirection[] aenumdirection = EnumDirection.values(); + int i1 = aenumdirection.length; + + for (int j1 = 0; j1 < i1; ++j1) { + EnumDirection enumdirection1 = aenumdirection[j1]; + + this.t.add(blockposition.shift(enumdirection1)); + } + } + + return iblockdata; + } + + private void a(World world, BlockPosition blockposition) { + if (world.getType(blockposition).getBlock() == this) { + world.applyPhysics(blockposition, this); + EnumDirection[] aenumdirection = EnumDirection.values(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + + world.applyPhysics(blockposition.shift(enumdirection), this); + } + + } + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + if (iblockdata1.getBlock() != iblockdata.getBlock() && !world.isClientSide) { + this.updateSurroundingRedstone(world, blockposition, iblockdata, null); // Paper - Optimize redstone + Iterator iterator = EnumDirection.EnumDirectionLimit.VERTICAL.iterator(); + + EnumDirection enumdirection; + + while (iterator.hasNext()) { + enumdirection = (EnumDirection) iterator.next(); + world.applyPhysics(blockposition.shift(enumdirection), this); + } + + iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + enumdirection = (EnumDirection) iterator.next(); + this.a(world, blockposition.shift(enumdirection)); + } + + iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + enumdirection = (EnumDirection) iterator.next(); + BlockPosition blockposition1 = blockposition.shift(enumdirection); + + if (world.getType(blockposition1).isOccluding()) { + this.a(world, blockposition1.up()); + } else { + this.a(world, blockposition1.down()); + } + } + + } + } + + public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) { + if (!flag && iblockdata.getBlock() != iblockdata1.getBlock()) { + super.remove(iblockdata, world, blockposition, iblockdata1, flag); + if (!world.isClientSide) { + EnumDirection[] aenumdirection = EnumDirection.values(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + + world.applyPhysics(blockposition.shift(enumdirection), this); + } + + this.updateSurroundingRedstone(world, blockposition, iblockdata, null); // Paper - Optimize redstone + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + EnumDirection enumdirection1; + + while (iterator.hasNext()) { + enumdirection1 = (EnumDirection) iterator.next(); + this.a(world, blockposition.shift(enumdirection1)); + } + + iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + enumdirection1 = (EnumDirection) iterator.next(); + BlockPosition blockposition1 = blockposition.shift(enumdirection1); + + if (world.getType(blockposition1).isOccluding()) { + this.a(world, blockposition1.up()); + } else { + this.a(world, blockposition1.down()); + } + } + + } + } + } + + public int getPower(int i, IBlockData iblockdata) { + if (iblockdata.getBlock() != this) { + return i; + } else { + int j = (Integer) iblockdata.get(BlockRedstoneWire.POWER); + + return j > i ? j : i; + } + } + + public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + if (!world.isClientSide) { + if (iblockdata.canPlace(world, blockposition)) { + this.updateSurroundingRedstone(world, blockposition, iblockdata, blockposition1); // Paper - Optimize redstone + } else { + iblockdata.a(world, blockposition, 0); + world.setAir(blockposition); + } + + } + } + + public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return !this.s ? 0 : iblockdata.a(iblockaccess, blockposition, enumdirection); + } + + public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + if (!this.s) { + return 0; + } else { + int i = (Integer) iblockdata.get(BlockRedstoneWire.POWER); + + if (i == 0) { + return 0; + } else if (enumdirection == EnumDirection.UP) { + return i; + } else { + EnumSet enumset = EnumSet.noneOf(EnumDirection.class); + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection1 = (EnumDirection) iterator.next(); + + if (this.b(iblockaccess, blockposition, enumdirection1)) { + enumset.add(enumdirection1); + } + } + + if (enumdirection.k().c() && enumset.isEmpty()) { + return i; + } else if (enumset.contains(enumdirection) && !enumset.contains(enumdirection.f()) && !enumset.contains(enumdirection.e())) { + return i; + } else { + return 0; + } + } + } + } + + private boolean b(IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + BlockPosition blockposition1 = blockposition.shift(enumdirection); + IBlockData iblockdata = iblockaccess.getType(blockposition1); + boolean flag = iblockdata.isOccluding(); + boolean flag1 = iblockaccess.getType(blockposition.up()).isOccluding(); + + return !flag1 && flag && a(iblockaccess, blockposition1.up()) ? true : (a(iblockdata, enumdirection) ? true : (iblockdata.getBlock() == Blocks.REPEATER && (Boolean) iblockdata.get(BlockDiodeAbstract.c) && iblockdata.get(BlockDiodeAbstract.FACING) == enumdirection ? true : !flag && a(iblockaccess, blockposition1.down()))); + } + + protected static boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) { + return k(iblockaccess.getType(blockposition)); + } + + protected static boolean k(IBlockData iblockdata) { + return a(iblockdata, (EnumDirection) null); + } + + protected static boolean a(IBlockData iblockdata, @Nullable EnumDirection enumdirection) { + Block block = iblockdata.getBlock(); + + if (block == Blocks.REDSTONE_WIRE) { + return true; + } else if (iblockdata.getBlock() == Blocks.REPEATER) { + EnumDirection enumdirection1 = (EnumDirection) iblockdata.get(BlockRepeater.FACING); + + return enumdirection1 == enumdirection || enumdirection1.opposite() == enumdirection; + } else { + return Blocks.OBSERVER == iblockdata.getBlock() ? enumdirection == iblockdata.get(BlockObserver.FACING) : iblockdata.isPowerSource() && enumdirection != null; + } + } + + public boolean isPowerSource(IBlockData iblockdata) { + return this.s; + } + + public TextureType c() { + return TextureType.CUTOUT; + } + + public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) { + switch (enumblockrotation) { + case CLOCKWISE_180: + return (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) iblockdata.set(BlockRedstoneWire.NORTH, iblockdata.get(BlockRedstoneWire.SOUTH))).set(BlockRedstoneWire.EAST, iblockdata.get(BlockRedstoneWire.WEST))).set(BlockRedstoneWire.SOUTH, iblockdata.get(BlockRedstoneWire.NORTH))).set(BlockRedstoneWire.WEST, iblockdata.get(BlockRedstoneWire.EAST)); + case COUNTERCLOCKWISE_90: + return (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) iblockdata.set(BlockRedstoneWire.NORTH, iblockdata.get(BlockRedstoneWire.EAST))).set(BlockRedstoneWire.EAST, iblockdata.get(BlockRedstoneWire.SOUTH))).set(BlockRedstoneWire.SOUTH, iblockdata.get(BlockRedstoneWire.WEST))).set(BlockRedstoneWire.WEST, iblockdata.get(BlockRedstoneWire.NORTH)); + case CLOCKWISE_90: + return (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) iblockdata.set(BlockRedstoneWire.NORTH, iblockdata.get(BlockRedstoneWire.WEST))).set(BlockRedstoneWire.EAST, iblockdata.get(BlockRedstoneWire.NORTH))).set(BlockRedstoneWire.SOUTH, iblockdata.get(BlockRedstoneWire.EAST))).set(BlockRedstoneWire.WEST, iblockdata.get(BlockRedstoneWire.SOUTH)); + default: + return iblockdata; + } + } + + public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) { + switch (enumblockmirror) { + case LEFT_RIGHT: + return (IBlockData) ((IBlockData) iblockdata.set(BlockRedstoneWire.NORTH, iblockdata.get(BlockRedstoneWire.SOUTH))).set(BlockRedstoneWire.SOUTH, iblockdata.get(BlockRedstoneWire.NORTH)); + case FRONT_BACK: + return (IBlockData) ((IBlockData) iblockdata.set(BlockRedstoneWire.EAST, iblockdata.get(BlockRedstoneWire.WEST))).set(BlockRedstoneWire.WEST, iblockdata.get(BlockRedstoneWire.EAST)); + default: + return super.a(iblockdata, enumblockmirror); + } + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockRedstoneWire.NORTH, BlockRedstoneWire.EAST, BlockRedstoneWire.SOUTH, BlockRedstoneWire.WEST, BlockRedstoneWire.POWER); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } +} diff --git a/src/main/java/net/minecraft/server/BlockReed.java b/src/main/java/net/minecraft/server/BlockReed.java new file mode 100644 index 000000000000..c7017c58e703 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockReed.java @@ -0,0 +1,86 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.Random; + +public class BlockReed extends Block { + + public static final BlockStateInteger AGE = BlockProperties.X; + protected static final VoxelShape b = Block.a(2.0D, 0.0D, 2.0D, 14.0D, 16.0D, 14.0D); + + protected BlockReed(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockReed.AGE, 0)); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockReed.b; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (iblockdata.canPlace(world, blockposition) && world.isEmpty(blockposition.up())) { + int i; + + for (i = 1; world.getType(blockposition.down(i)).getBlock() == this; ++i) { + ; + } + + if (i < world.paperConfig.reedMaxHeight) { // Paper - Configurable growth height + int j = (Integer) iblockdata.get(BlockReed.AGE); + + if (j >= (byte) range(3, ((100.0F / world.spigotConfig.caneModifier) * 15) + 0.5F, 15)) { // Spigot + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, blockposition.up(), this.getBlockData()); // CraftBukkit + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockReed.AGE, 0), 4); + } else { + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockReed.AGE, j + 1), 4); + } + } + } + + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + return !iblockdata.canPlace(generatoraccess, blockposition) ? Blocks.AIR.getBlockData() : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + Block block = iworldreader.getType(blockposition.down()).getBlock(); + + if (block == this) { + return true; + } else { + if (block == Blocks.GRASS_BLOCK || block == Blocks.DIRT || block == Blocks.COARSE_DIRT || block == Blocks.PODZOL || block == Blocks.SAND || block == Blocks.RED_SAND) { + BlockPosition blockposition1 = blockposition.down(); + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + IBlockData iblockdata1 = iworldreader.getType(blockposition1.shift(enumdirection)); + Fluid fluid = iworldreader.getFluid(blockposition1.shift(enumdirection)); + + if (fluid.a(TagsFluid.WATER) || iblockdata1.getBlock() == Blocks.FROSTED_ICE) { + return true; + } + } + } + + return false; + } + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public TextureType c() { + return TextureType.CUTOUT; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockReed.AGE); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } +} diff --git a/src/main/java/net/minecraft/server/BlockSapling.java b/src/main/java/net/minecraft/server/BlockSapling.java new file mode 100644 index 000000000000..291cc9a39841 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockSapling.java @@ -0,0 +1,86 @@ +package net.minecraft.server; + +import java.util.Random; + +// CraftBukkit start +import java.util.List; + +import org.bukkit.Location; +import org.bukkit.TreeType; +import org.bukkit.block.BlockState; +import org.bukkit.event.world.StructureGrowEvent; +// CraftBukkit end + +public class BlockSapling extends BlockPlant implements IBlockFragilePlantElement { + + public static final BlockStateInteger STAGE = BlockProperties.am; + protected static final VoxelShape b = Block.a(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D); + private final WorldGenTreeProvider c; + public static TreeType treeType; // CraftBukkit + + protected BlockSapling(WorldGenTreeProvider worldgentreeprovider, Block.Info block_info) { + super(block_info); + this.c = worldgentreeprovider; + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockSapling.STAGE, 0)); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockSapling.b; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + super.a(iblockdata, world, blockposition, random); + if (world.isLightLevel(blockposition.up(), 9) && random.nextInt(Math.max(2, (int) (((100.0F / world.spigotConfig.saplingModifier) * 7) + 0.5F))) == 0) { // Spigot // Paper + // CraftBukkit start + world.captureTreeGeneration = true; + // CraftBukkit end + this.grow(world, blockposition, iblockdata, random); + // CraftBukkit start + world.captureTreeGeneration = false; + if (world.capturedBlockStates.size() > 0) { + TreeType treeType = BlockSapling.treeType; + BlockSapling.treeType = null; + Location location = new Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ()); + List blocks = (List) world.capturedBlockStates.clone(); + world.capturedBlockStates.clear(); + StructureGrowEvent event = null; + if (treeType != null) { + event = new StructureGrowEvent(location, treeType, false, null, blocks); + org.bukkit.Bukkit.getPluginManager().callEvent(event); + } + if (event == null || !event.isCancelled()) { + for (BlockState blockstate : blocks) { + blockstate.update(true); + } + } + } + // CraftBukkit end + } + + } + + public void grow(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, Random random) { + if ((Integer) iblockdata.get(BlockSapling.STAGE) == 0) { + generatoraccess.setTypeAndData(blockposition, (IBlockData) iblockdata.a((IBlockState) BlockSapling.STAGE), 4); + } else { + this.c.a(generatoraccess, blockposition, iblockdata, random); + } + + } + + public boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, boolean flag) { + return true; + } + + public boolean a(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) { + return (double) world.random.nextFloat() < 0.45D; + } + + public void b(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) { + this.grow(world, blockposition, iblockdata, random); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockSapling.STAGE); + } +} diff --git a/src/main/java/net/minecraft/server/BlockShulkerBox.java b/src/main/java/net/minecraft/server/BlockShulkerBox.java new file mode 100644 index 000000000000..997ed795b1ce --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockShulkerBox.java @@ -0,0 +1,242 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class BlockShulkerBox extends BlockTileEntity { + + public static final BlockStateEnum a = BlockDirectional.FACING; + @Nullable + public final EnumColor color; + + public BlockShulkerBox(@Nullable EnumColor enumcolor, Block.Info block_info) { + super(block_info); + this.color = enumcolor; + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockShulkerBox.a, EnumDirection.UP)); + } + + public TileEntity a(IBlockAccess iblockaccess) { + return new TileEntityShulkerBox(this.color); + } + + public boolean q(IBlockData iblockdata) { + return true; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public EnumRenderType c(IBlockData iblockdata) { + return EnumRenderType.ENTITYBLOCK_ANIMATED; + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + if (world.isClientSide) { + return true; + } else if (entityhuman.isSpectator()) { + return true; + } else { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityShulkerBox) { + EnumDirection enumdirection1 = (EnumDirection) iblockdata.get(BlockShulkerBox.a); + boolean flag; + + if (((TileEntityShulkerBox) tileentity).r() == TileEntityShulkerBox.AnimationPhase.CLOSED) { + AxisAlignedBB axisalignedbb = VoxelShapes.b().getBoundingBox().b((double) (0.5F * (float) enumdirection1.getAdjacentX()), (double) (0.5F * (float) enumdirection1.getAdjacentY()), (double) (0.5F * (float) enumdirection1.getAdjacentZ())).a((double) enumdirection1.getAdjacentX(), (double) enumdirection1.getAdjacentY(), (double) enumdirection1.getAdjacentZ()); + + flag = world.getCubes((Entity) null, axisalignedbb.a(blockposition.shift(enumdirection1))); + } else { + flag = true; + } + + if (flag) { + entityhuman.a(StatisticList.OPEN_SHULKER_BOX); + entityhuman.openContainer((IInventory) tileentity); + } + + return true; + } else { + return false; + } + } + } + + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + return (IBlockData) this.getBlockData().set(BlockShulkerBox.a, blockactioncontext.getClickedFace()); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockShulkerBox.a); + } + + public void a(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) { + if (world.getTileEntity(blockposition) instanceof TileEntityShulkerBox) { + TileEntityShulkerBox tileentityshulkerbox = (TileEntityShulkerBox) world.getTileEntity(blockposition); + + tileentityshulkerbox.a(entityhuman.abilities.canInstantlyBuild); + tileentityshulkerbox.d(entityhuman); + } + + super.a(world, blockposition, iblockdata, entityhuman); + } + + // CraftBukkit start - override to prevent duplication when dropping + @Override + public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) { + if (true) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityShulkerBox) { + TileEntityShulkerBox tileentityshulkerbox = (TileEntityShulkerBox) tileentity; + + if (!tileentityshulkerbox.s() && tileentityshulkerbox.G()) { + ItemStack itemstack = new ItemStack(this); + + itemstack.getOrCreateTag().set("BlockEntityTag", ((TileEntityShulkerBox) tileentity).g(new NBTTagCompound())); + if (tileentityshulkerbox.hasCustomName()) { + itemstack.a(tileentityshulkerbox.getCustomName()); + tileentityshulkerbox.setCustomName((IChatBaseComponent) null); + } + + a(world, blockposition, itemstack); + tileentityshulkerbox.clear(); // Paper - This was intended to be called in Vanilla (is checked in the if statement above if has been called) - Fixes dupe issues + } + } + world.updateAdjacentComparators(blockposition, iblockdata.getBlock()); + } + } + // CraftBukkit end + + public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) { + if (itemstack.hasName()) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityShulkerBox) { + ((TileEntityShulkerBox) tileentity).setCustomName(itemstack.getName()); + } + } + + } + + public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) { + if (iblockdata.getBlock() != iblockdata1.getBlock()) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (false && tileentity instanceof TileEntityShulkerBox) { // CraftBukkit - moved up + TileEntityShulkerBox tileentityshulkerbox = (TileEntityShulkerBox) tileentity; + + if (!tileentityshulkerbox.s() && tileentityshulkerbox.G()) { + ItemStack itemstack = new ItemStack(this); + + itemstack.getOrCreateTag().set("BlockEntityTag", ((TileEntityShulkerBox) tileentity).g(new NBTTagCompound())); + if (tileentityshulkerbox.hasCustomName()) { + itemstack.a(tileentityshulkerbox.getCustomName()); + tileentityshulkerbox.setCustomName((IChatBaseComponent) null); + } + + a(world, blockposition, itemstack); + } + + } + world.updateAdjacentComparators(blockposition, iblockdata.getBlock()); // CraftBukkit - moved down + + super.remove(iblockdata, world, blockposition, iblockdata1, flag); + } + } + + public EnumPistonReaction getPushReaction(IBlockData iblockdata) { + return EnumPistonReaction.DESTROY; + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + TileEntity tileentity = iblockaccess.getTileEntity(blockposition); + + return tileentity instanceof TileEntityShulkerBox ? VoxelShapes.a(((TileEntityShulkerBox) tileentity).a(iblockdata)) : VoxelShapes.b(); + } + + public boolean f(IBlockData iblockdata) { + return false; + } + + public boolean isComplexRedstone(IBlockData iblockdata) { + return true; + } + + public int a(IBlockData iblockdata, World world, BlockPosition blockposition) { + return Container.b((IInventory) world.getTileEntity(blockposition)); + } + + public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) { + ItemStack itemstack = super.a(iblockaccess, blockposition, iblockdata); + TileEntityShulkerBox tileentityshulkerbox = (TileEntityShulkerBox) iblockaccess.getTileEntity(blockposition); + NBTTagCompound nbttagcompound = tileentityshulkerbox.g(new NBTTagCompound()); + + if (!nbttagcompound.isEmpty()) { + itemstack.a("BlockEntityTag", (NBTBase) nbttagcompound); + } + + return itemstack; + } + + public static Block a(EnumColor enumcolor) { + if (enumcolor == null) { + return Blocks.SHULKER_BOX; + } else { + switch (enumcolor) { + case WHITE: + return Blocks.WHITE_SHULKER_BOX; + case ORANGE: + return Blocks.ORANGE_SHULKER_BOX; + case MAGENTA: + return Blocks.MAGENTA_SHULKER_BOX; + case LIGHT_BLUE: + return Blocks.LIGHT_BLUE_SHULKER_BOX; + case YELLOW: + return Blocks.YELLOW_SHULKER_BOX; + case LIME: + return Blocks.LIME_SHULKER_BOX; + case PINK: + return Blocks.PINK_SHULKER_BOX; + case GRAY: + return Blocks.GRAY_SHULKER_BOX; + case LIGHT_GRAY: + return Blocks.LIGHT_GRAY_SHULKER_BOX; + case CYAN: + return Blocks.CYAN_SHULKER_BOX; + case PURPLE: + default: + return Blocks.PURPLE_SHULKER_BOX; + case BLUE: + return Blocks.BLUE_SHULKER_BOX; + case BROWN: + return Blocks.BROWN_SHULKER_BOX; + case GREEN: + return Blocks.GREEN_SHULKER_BOX; + case RED: + return Blocks.RED_SHULKER_BOX; + case BLACK: + return Blocks.BLACK_SHULKER_BOX; + } + } + } + + public static ItemStack b(EnumColor enumcolor) { + return new ItemStack(a(enumcolor)); + } + + public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) { + return (IBlockData) iblockdata.set(BlockShulkerBox.a, enumblockrotation.a((EnumDirection) iblockdata.get(BlockShulkerBox.a))); + } + + public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) { + return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockShulkerBox.a))); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + EnumDirection enumdirection1 = (EnumDirection) iblockdata.get(BlockShulkerBox.a); + TileEntityShulkerBox.AnimationPhase tileentityshulkerbox_animationphase = ((TileEntityShulkerBox) iblockaccess.getTileEntity(blockposition)).r(); + + return tileentityshulkerbox_animationphase != TileEntityShulkerBox.AnimationPhase.CLOSED && (tileentityshulkerbox_animationphase != TileEntityShulkerBox.AnimationPhase.OPENED || enumdirection1 != enumdirection.opposite() && enumdirection1 != enumdirection) ? EnumBlockFaceShape.UNDEFINED : EnumBlockFaceShape.SOLID; + } +} diff --git a/src/main/java/net/minecraft/server/BlockSkullAbstract.java b/src/main/java/net/minecraft/server/BlockSkullAbstract.java new file mode 100644 index 000000000000..b82f68e2aacd --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockSkullAbstract.java @@ -0,0 +1,84 @@ +package net.minecraft.server; + +public abstract class BlockSkullAbstract extends BlockTileEntity { + + private final BlockSkull.a a; + + public BlockSkullAbstract(BlockSkull.a blockskull_a, Block.Info block_info) { + super(block_info); + this.a = blockskull_a; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public TileEntity a(IBlockAccess iblockaccess) { + return new TileEntitySkull(); + } + + // CraftBukkit start - Special case dropping so we can get info from the tile entity + @Override + public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) { + if (world.random.nextFloat() < f) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntitySkull) { + TileEntitySkull tileentityskull = (TileEntitySkull) tileentity; + + if (tileentityskull.shouldDrop()) { + ItemStack itemstack = this.a((IBlockAccess) world, blockposition, iblockdata); + Block block = tileentityskull.getBlock().getBlock(); + + if ((block == Blocks.PLAYER_HEAD || block == Blocks.PLAYER_WALL_HEAD) && tileentityskull.getGameProfile() != null) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + GameProfileSerializer.serialize(nbttagcompound, tileentityskull.getGameProfile()); + itemstack.getOrCreateTag().set("SkullOwner", nbttagcompound); + } + + a(world, blockposition, itemstack); + } + } + } + } + // CraftBukkit end + + public void a(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) { + if (!world.isClientSide && entityhuman.abilities.canInstantlyBuild) { + TileEntitySkull.a(world, blockposition); + } + + super.a(world, blockposition, iblockdata, entityhuman); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } + + public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) { + if (iblockdata.getBlock() != iblockdata1.getBlock() && !world.isClientSide) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (false && tileentity instanceof TileEntitySkull) { // CraftBukkit - Drop item in code above, not here + TileEntitySkull tileentityskull = (TileEntitySkull) tileentity; + + if (tileentityskull.shouldDrop()) { + ItemStack itemstack = this.a((IBlockAccess) world, blockposition, iblockdata); + Block block = tileentityskull.getBlock().getBlock(); + + if ((block == Blocks.PLAYER_HEAD || block == Blocks.PLAYER_WALL_HEAD) && tileentityskull.getGameProfile() != null) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + GameProfileSerializer.serialize(nbttagcompound, tileentityskull.getGameProfile()); + itemstack.getOrCreateTag().set("SkullOwner", nbttagcompound); + } + + a(world, blockposition, itemstack); + } + } + + super.remove(iblockdata, world, blockposition, iblockdata1, flag); + } + } +} diff --git a/src/main/java/net/minecraft/server/BlockSnow.java b/src/main/java/net/minecraft/server/BlockSnow.java new file mode 100644 index 000000000000..9c22d2ec7463 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockSnow.java @@ -0,0 +1,125 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; + +public class BlockSnow extends Block { + + public static final BlockStateInteger LAYERS = BlockProperties.ae; + protected static final VoxelShape[] b = new VoxelShape[] { VoxelShapes.a(), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 4.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 6.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 10.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 12.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 14.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D)}; + + protected BlockSnow(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockSnow.LAYERS, 1)); + } + + public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) { + switch (pathmode) { + case LAND: + return (Integer) iblockdata.get(BlockSnow.LAYERS) < 5; + case WATER: + return false; + case AIR: + return false; + default: + return false; + } + } + + public boolean a(IBlockData iblockdata) { + return (Integer) iblockdata.get(BlockSnow.LAYERS) == 8; + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return enumdirection == EnumDirection.DOWN ? EnumBlockFaceShape.SOLID : EnumBlockFaceShape.UNDEFINED; + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockSnow.b[(Integer) iblockdata.get(BlockSnow.LAYERS)]; + } + + public VoxelShape f(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockSnow.b[(Integer) iblockdata.get(BlockSnow.LAYERS) - 1]; + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + IBlockData iblockdata1 = iworldreader.getType(blockposition.down()); + Block block = iblockdata1.getBlock(); + + if (block != Blocks.ICE && block != Blocks.PACKED_ICE && block != Blocks.BARRIER) { + EnumBlockFaceShape enumblockfaceshape = iblockdata1.c(iworldreader, blockposition.down(), EnumDirection.UP); + + return enumblockfaceshape == EnumBlockFaceShape.SOLID || iblockdata1.a(TagsBlock.LEAVES) || block == this && (Integer) iblockdata1.get(BlockSnow.LAYERS) == 8; + } else { + return false; + } + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + return !iblockdata.canPlace(generatoraccess, blockposition) ? Blocks.AIR.getBlockData() : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) { + Integer integer = (Integer) iblockdata.get(BlockSnow.LAYERS); + + if (this.X_() && EnchantmentManager.getEnchantmentLevel(Enchantments.SILK_TOUCH, itemstack) > 0) { + if (integer == 8) { + a(world, blockposition, new ItemStack(Blocks.SNOW_BLOCK)); + } else { + for (int i = 0; i < integer; ++i) { + a(world, blockposition, this.t(iblockdata)); + } + } + } else { + a(world, blockposition, new ItemStack(Items.SNOWBALL, integer)); + } + + world.setAir(blockposition); + entityhuman.b(StatisticList.BLOCK_MINED.b(this)); + entityhuman.applyExhaustion(0.005F); + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return Items.AIR; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (world.getBrightness(EnumSkyBlock.BLOCK, blockposition) > 11) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, Blocks.AIR.getBlockData()).isCancelled()) { + return; + } + // CraftBukkit end + iblockdata.a(world, blockposition, 0); + world.setAir(blockposition); + } + + } + + public boolean a(IBlockData iblockdata, BlockActionContext blockactioncontext) { + int i = (Integer) iblockdata.get(BlockSnow.LAYERS); + + return blockactioncontext.getItemStack().getItem() == this.getItem() && i < 8 ? (blockactioncontext.c() ? blockactioncontext.getClickedFace() == EnumDirection.UP : true) : i == 1; + } + + @Nullable + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + IBlockData iblockdata = blockactioncontext.getWorld().getType(blockactioncontext.getClickPosition()); + + if (iblockdata.getBlock() == this) { + int i = (Integer) iblockdata.get(BlockSnow.LAYERS); + + return (IBlockData) iblockdata.set(BlockSnow.LAYERS, Math.min(8, i + 1)); + } else { + return super.getPlacedState(blockactioncontext); + } + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockSnow.LAYERS); + } + + protected boolean X_() { + return true; + } +} diff --git a/src/main/java/net/minecraft/server/BlockSnowBlock.java b/src/main/java/net/minecraft/server/BlockSnowBlock.java new file mode 100644 index 000000000000..44ed656263fa --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockSnowBlock.java @@ -0,0 +1,35 @@ +package net.minecraft.server; + +import java.util.Random; + +public class BlockSnowBlock extends Block { + + protected BlockSnowBlock(Block.Info block_info) { + super(block_info); + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return Items.SNOWBALL; + } + + public int a(IBlockData iblockdata, Random random) { + return 4; + } + + // Paper start - snow blocks don't need to tick + /* + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (world.getBrightness(EnumSkyBlock.BLOCK, blockposition) > 11) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, Blocks.AIR.getBlockData()).isCancelled()) { + return; + } + // CraftBukkit end + iblockdata.a(world, blockposition, 0); + world.setAir(blockposition); + } + + } + */ + //Paper end +} diff --git a/src/main/java/net/minecraft/server/BlockSoil.java b/src/main/java/net/minecraft/server/BlockSoil.java new file mode 100644 index 000000000000..228ca966673d --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockSoil.java @@ -0,0 +1,142 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.Random; + +// CraftBukkit start +import org.bukkit.event.entity.EntityInteractEvent; +import org.bukkit.craftbukkit.event.CraftEventFactory; +// CraftBukkit end + +public class BlockSoil extends Block { + + public static final BlockStateInteger MOISTURE = BlockProperties.ai; + protected static final VoxelShape b = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 15.0D, 16.0D); + + protected BlockSoil(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockSoil.MOISTURE, 0)); + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + if (enumdirection == EnumDirection.UP && !iblockdata.canPlace(generatoraccess, blockposition)) { + generatoraccess.getBlockTickList().a(blockposition, this, 1); + } + + return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + IBlockData iblockdata1 = iworldreader.getType(blockposition.up()); + + return !iblockdata1.getMaterial().isBuildable() || iblockdata1.getBlock() instanceof BlockFenceGate; + } + + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + return !this.getBlockData().canPlace(blockactioncontext.getWorld(), blockactioncontext.getClickPosition()) ? Blocks.DIRT.getBlockData() : super.getPlacedState(blockactioncontext); + } + + public int j(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return iblockaccess.K(); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockSoil.b; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!iblockdata.canPlace(world, blockposition)) { + b(iblockdata, world, blockposition); + } else { + int i = (Integer) iblockdata.get(BlockSoil.MOISTURE); + + if (!a((IWorldReader) world, blockposition) && !world.isRainingAt(blockposition.up())) { + if (i > 0) { + org.bukkit.craftbukkit.event.CraftEventFactory.handleMoistureChangeEvent(world, blockposition, (IBlockData) iblockdata.set(BlockSoil.MOISTURE, i - 1), 2); // CraftBukkit + } else if (!a((IBlockAccess) world, blockposition)) { + b(iblockdata, world, blockposition); + } + } else if (i < 7) { + org.bukkit.craftbukkit.event.CraftEventFactory.handleMoistureChangeEvent(world, blockposition, (IBlockData) iblockdata.set(BlockSoil.MOISTURE, 7), 2); // CraftBukkit + } + + } + } + + public void fallOn(World world, BlockPosition blockposition, Entity entity, float f) { + super.fallOn(world, blockposition, entity, f); // CraftBukkit - moved here as game rules / events shouldn't affect fall damage. + if (!world.isClientSide && world.random.nextFloat() < f - 0.5F && entity instanceof EntityLiving && (entity instanceof EntityHuman || world.getGameRules().getBoolean("mobGriefing")) && entity.width * entity.width * entity.length > 0.512F) { + // CraftBukkit start - Interact soil + org.bukkit.event.Cancellable cancellable; + if (entity instanceof EntityHuman) { + cancellable = CraftEventFactory.callPlayerInteractEvent((EntityHuman) entity, org.bukkit.event.block.Action.PHYSICAL, blockposition, null, null, null); + } else { + cancellable = new EntityInteractEvent(entity.getBukkitEntity(), world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ())); + world.getServer().getPluginManager().callEvent((EntityInteractEvent) cancellable); + } + + if (cancellable.isCancelled()) { + return; + } + + if (CraftEventFactory.callEntityChangeBlockEvent(entity, blockposition, Blocks.DIRT.getBlockData()).isCancelled()) { + return; + } + // CraftBukkit end + b(world.getType(blockposition), world, blockposition); + } + + // super.fallOn(world, blockposition, entity, f); // CraftBukkit - moved up + } + + public static void b(IBlockData iblockdata, World world, BlockPosition blockposition) { + // CraftBukkit start + if (CraftEventFactory.callBlockFadeEvent(world, blockposition, Blocks.DIRT.getBlockData()).isCancelled()) { + return; + } + // CraftBukkit end + world.setTypeUpdate(blockposition, a(iblockdata, Blocks.DIRT.getBlockData(), world, blockposition)); + } + + private static boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) { + Block block = iblockaccess.getType(blockposition.up()).getBlock(); + + return block instanceof BlockCrops || block instanceof BlockStem || block instanceof BlockStemAttached; + } + + private static boolean a(IWorldReader iworldreader, BlockPosition blockposition) { + Iterator iterator = BlockPosition.b(blockposition.a(-4, 0, -4), blockposition.a(4, 1, 4)).iterator(); + + BlockPosition.MutableBlockPosition blockposition_mutableblockposition; + + do { + if (!iterator.hasNext()) { + return false; + } + + blockposition_mutableblockposition = (BlockPosition.MutableBlockPosition) iterator.next(); + } while (!iworldreader.getFluid(blockposition_mutableblockposition).a(TagsFluid.WATER)); + + return true; + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return Blocks.DIRT; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockSoil.MOISTURE); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return enumdirection == EnumDirection.DOWN ? EnumBlockFaceShape.SOLID : EnumBlockFaceShape.UNDEFINED; + } + + public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/BlockSponge.java b/src/main/java/net/minecraft/server/BlockSponge.java new file mode 100644 index 000000000000..cda6f2b732a3 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockSponge.java @@ -0,0 +1,124 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Queue; +// CraftBukkit start +import java.util.List; +import org.bukkit.craftbukkit.block.CraftBlockState; +import org.bukkit.craftbukkit.util.BlockStateListPopulator; +import org.bukkit.event.block.SpongeAbsorbEvent; +// CraftBukkit end + +public class BlockSponge extends Block { + + protected BlockSponge(Block.Info block_info) { + super(block_info); + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + if (iblockdata1.getBlock() != iblockdata.getBlock()) { + this.a(world, blockposition); + } + } + + public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + this.a(world, blockposition); + super.doPhysics(iblockdata, world, blockposition, block, blockposition1); + } + + protected void a(World world, BlockPosition blockposition) { + if (this.b(world, blockposition)) { + world.setTypeAndData(blockposition, Blocks.WET_SPONGE.getBlockData(), 2); + world.triggerEffect(2001, blockposition, Block.getCombinedId(Blocks.WATER.getBlockData())); + } + + } + + private boolean b(World world, BlockPosition blockposition) { + Queue> queue = Lists.newLinkedList(); + + queue.add(new Tuple<>(blockposition, 0)); + int i = 0; + BlockStateListPopulator blockList = new BlockStateListPopulator(world); // CraftBukkit - Use BlockStateListPopulator + + while (!queue.isEmpty()) { + Tuple tuple = (Tuple) queue.poll(); + BlockPosition blockposition1 = (BlockPosition) tuple.a(); + int j = (Integer) tuple.b(); + EnumDirection[] aenumdirection = EnumDirection.values(); + int k = aenumdirection.length; + + for (int l = 0; l < k; ++l) { + EnumDirection enumdirection = aenumdirection[l]; + BlockPosition blockposition2 = blockposition1.shift(enumdirection); + IBlockData iblockdata = world.getType(blockposition2); + Fluid fluid = world.getFluid(blockposition2); + Material material = iblockdata.getMaterial(); + + if (fluid.a(TagsFluid.WATER)) { + if (iblockdata.getBlock() instanceof IFluidSource && ((IFluidSource) iblockdata.getBlock()).removeFluid(blockList, blockposition2, iblockdata) != FluidTypes.EMPTY) { // CraftBukkit + ++i; + if (j < 6) { + queue.add(new Tuple<>(blockposition2, j + 1)); + } + } else if (iblockdata.getBlock() instanceof BlockFluids) { + blockList.setTypeAndData(blockposition2, Blocks.AIR.getBlockData(), 3); // CraftBukkit + ++i; + if (j < 6) { + queue.add(new Tuple<>(blockposition2, j + 1)); + } + } else if (material == Material.WATER_PLANT || material == Material.REPLACEABLE_WATER_PLANT) { + // iblockdata.a(world, blockposition2, 0); + blockList.setTypeAndData(blockposition2, Blocks.AIR.getBlockData(), 3); // CraftBukkit + ++i; + if (j < 6) { + queue.add(new Tuple<>(blockposition2, j + 1)); + } + } + } + } + + if (i > 64) { + break; + } + } + // CraftBukkit start + List blocks = blockList.getList(); // Is a clone + if (!blocks.isEmpty()) { + final org.bukkit.block.Block bblock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + + SpongeAbsorbEvent event = new SpongeAbsorbEvent(bblock, (List) (List) blocks); + world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return false; + } + + for (CraftBlockState block : blocks) { + BlockPosition blockposition2 = block.getPosition(); + IBlockData iblockdata = world.getType(blockposition2); + Fluid fluid = world.getFluid(blockposition2); + Material material = iblockdata.getMaterial(); + + if (fluid.a(TagsFluid.WATER)) { + if (iblockdata.getBlock() instanceof IFluidSource && ((IFluidSource) iblockdata.getBlock()).removeFluid(blockList, blockposition2, iblockdata) != FluidTypes.EMPTY) { + // NOP + } else if (iblockdata.getBlock() instanceof BlockFluids) { + // NOP + } else if (material == Material.WATER_PLANT || material == Material.REPLACEABLE_WATER_PLANT) { + // Paper start + if (block.getHandle().getMaterial() == Material.AIR) { + iblockdata.dropNaturally(world, blockposition2, 0); + } + // Paper end + } + } + world.setTypeAndData(blockposition2, block.getHandle(), block.getFlag()); + } + } + // CraftBukkit end + + return i > 0; + } +} + diff --git a/src/main/java/net/minecraft/server/BlockState.java b/src/main/java/net/minecraft/server/BlockState.java new file mode 100644 index 000000000000..ea0e0ff4fe7e --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockState.java @@ -0,0 +1,41 @@ +package net.minecraft.server; + +import com.google.common.base.MoreObjects; + +public abstract class BlockState> implements IBlockState { + + private final Class a; + private final String b; + private Integer c; + + protected BlockState(String s, Class oclass) { + this.a = oclass; + this.b = s; + } + + public String a() { + return this.b; + } + + public Class b() { + return this.a; + } + + public String toString() { + return MoreObjects.toStringHelper(this).add("name", this.b).add("clazz", this.a).add("values", this.d()).toString(); + } + + public boolean equals(Object object) { + return this == object; // Paper - only one instance per configuration + } + + private static java.util.concurrent.atomic.AtomicInteger hashId = new java.util.concurrent.atomic.AtomicInteger(1);// Paper - only one instance per configuration + private final int hashCode = 92821 * hashId.getAndIncrement(); // Paper - only one instance per configuration + public final int hashCode() { + return hashCode; // Paper - only one instance per configuration + } + + public int c() { + return 31 * this.a.hashCode() + this.b.hashCode(); + } +} diff --git a/src/main/java/net/minecraft/server/BlockStateBoolean.java b/src/main/java/net/minecraft/server/BlockStateBoolean.java new file mode 100644 index 000000000000..71d2ad97037b --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockStateBoolean.java @@ -0,0 +1,46 @@ +package net.minecraft.server; + +import com.google.common.collect.ImmutableSet; +import java.util.Collection; +import java.util.Optional; + +public class BlockStateBoolean extends BlockState { + + private final ImmutableSet a = ImmutableSet.of(true, false); + + protected BlockStateBoolean(String s) { + super(s, Boolean.class); + } + + public Collection d() { + return this.a; + } + + public static BlockStateBoolean of(String s) { + return new BlockStateBoolean(s); + } + + public Optional b(String s) { + return !"true".equals(s) && !"false".equals(s) ? Optional.empty() : Optional.of(Boolean.valueOf(s)); + } + + public String a(Boolean obool) { + return obool.toString(); + } + + public boolean equals_unused(Object object) { // Paper + if (this == object) { + return true; + } else if (object instanceof BlockStateBoolean && super.equals(object)) { + BlockStateBoolean blockstateboolean = (BlockStateBoolean) object; + + return this.a.equals(blockstateboolean.a); + } else { + return false; + } + } + + public int c() { + return 31 * super.c() + this.a.hashCode(); + } +} diff --git a/src/main/java/net/minecraft/server/BlockStateEnum.java b/src/main/java/net/minecraft/server/BlockStateEnum.java new file mode 100644 index 000000000000..facbf30b4e2e --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockStateEnum.java @@ -0,0 +1,85 @@ +package net.minecraft.server; + +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public class BlockStateEnum & INamable> extends BlockState { + + private final ImmutableSet a; + private final Map b = Maps.newHashMap(); + + protected BlockStateEnum(String s, Class oclass, Collection collection) { + super(s, oclass); + this.a = ImmutableSet.copyOf(collection); + Iterator iterator = collection.iterator(); // Paper - decompile fix + + while (iterator.hasNext()) { + T t0 = iterator.next(); // Paper - Decompile fix + String s1 = ((INamable) t0).getName(); + + if (this.b.containsKey(s1)) { + throw new IllegalArgumentException("Multiple values have the same name '" + s1 + "'"); + } + + this.b.put(s1, t0); + } + + } + + public Collection d() { + return this.a; + } + + public Optional b(String s) { + return Optional.ofNullable(this.b.get(s)); + } + + public String a(T t0) { + return ((INamable) t0).getName(); + } + + public boolean equals_unused(Object object) { // Paper + if (this == object) { + return true; + } else if (object instanceof BlockStateEnum && super.equals(object)) { + BlockStateEnum blockstateenum = (BlockStateEnum) object; + + return this.a.equals(blockstateenum.a) && this.b.equals(blockstateenum.b); + } else { + return false; + } + } + + public int c() { + int i = super.c(); + + i = 31 * i + this.a.hashCode(); + i = 31 * i + this.b.hashCode(); + return i; + } + + public static & INamable> BlockStateEnum of(String s, Class oclass) { + return a(s, oclass, (Predicate) Predicates.alwaysTrue()); + } + + public static & INamable> BlockStateEnum a(String s, Class oclass, Predicate predicate) { + return a(s, oclass, (Collection) Arrays.stream(oclass.getEnumConstants()).filter(predicate).collect(Collectors.toList())); + } + + public static & INamable> BlockStateEnum of(String s, Class oclass, T... at) { + return a(s, oclass, (Collection) Lists.newArrayList(at)); + } + + public static & INamable> BlockStateEnum a(String s, Class oclass, Collection collection) { + return new BlockStateEnum<>(s, oclass, collection); + } +} diff --git a/src/main/java/net/minecraft/server/BlockStateInteger.java b/src/main/java/net/minecraft/server/BlockStateInteger.java new file mode 100644 index 000000000000..613cd0bce215 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockStateInteger.java @@ -0,0 +1,73 @@ +package net.minecraft.server; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import java.util.Collection; +import java.util.Optional; +import java.util.Set; + +public class BlockStateInteger extends BlockState { + + private final ImmutableSet a; + // CraftBukkit start + public final int min; + public final int max; + + protected BlockStateInteger(String s, int i, int j) { + super(s, Integer.class); + this.min = i; + this.max = j; + // CraftBukkit end + if (i < 0) { + throw new IllegalArgumentException("Min value of " + s + " must be 0 or greater"); + } else if (j <= i) { + throw new IllegalArgumentException("Max value of " + s + " must be greater than min (" + i + ")"); + } else { + Set set = Sets.newHashSet(); + + for (int k = i; k <= j; ++k) { + set.add(k); + } + + this.a = ImmutableSet.copyOf(set); + } + } + + public Collection d() { + return this.a; + } + + public boolean equals_unused(Object object) { // Paper + if (this == object) { + return true; + } else if (object instanceof BlockStateInteger && super.equals(object)) { + BlockStateInteger blockstateinteger = (BlockStateInteger) object; + + return this.a.equals(blockstateinteger.a); + } else { + return false; + } + } + + public int c() { + return 31 * super.c() + this.a.hashCode(); + } + + public static BlockStateInteger of(String s, int i, int j) { + return new BlockStateInteger(s, i, j); + } + + public Optional b(String s) { + try { + Integer integer = Integer.valueOf(s); + + return this.a.contains(integer) ? Optional.of(integer) : Optional.empty(); + } catch (NumberFormatException numberformatexception) { + return Optional.empty(); + } + } + + public String a(Integer integer) { + return integer.toString(); + } +} diff --git a/src/main/java/net/minecraft/server/BlockStem.java b/src/main/java/net/minecraft/server/BlockStem.java new file mode 100644 index 000000000000..f8dda1b7a11d --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockStem.java @@ -0,0 +1,117 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockStem extends BlockPlant implements IBlockFragilePlantElement { + + public static final BlockStateInteger AGE = BlockProperties.W; + protected static final VoxelShape[] b = new VoxelShape[] { Block.a(7.0D, 0.0D, 7.0D, 9.0D, 2.0D, 9.0D), Block.a(7.0D, 0.0D, 7.0D, 9.0D, 4.0D, 9.0D), Block.a(7.0D, 0.0D, 7.0D, 9.0D, 6.0D, 9.0D), Block.a(7.0D, 0.0D, 7.0D, 9.0D, 8.0D, 9.0D), Block.a(7.0D, 0.0D, 7.0D, 9.0D, 10.0D, 9.0D), Block.a(7.0D, 0.0D, 7.0D, 9.0D, 12.0D, 9.0D), Block.a(7.0D, 0.0D, 7.0D, 9.0D, 14.0D, 9.0D), Block.a(7.0D, 0.0D, 7.0D, 9.0D, 16.0D, 9.0D)}; + private final BlockStemmed blockFruit; + + protected BlockStem(BlockStemmed blockstemmed, Block.Info block_info) { + super(block_info); + this.blockFruit = blockstemmed; + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockStem.AGE, 0)); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockStem.b[(Integer) iblockdata.get(BlockStem.AGE)]; + } + + protected boolean b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return iblockdata.getBlock() == Blocks.FARMLAND; + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + super.a(iblockdata, world, blockposition, random); + if (world.isLightLevel(blockposition.up(), 9)) { // Paper + float f = BlockCrops.a((Block) this, (IBlockAccess) world, blockposition); + + if (random.nextInt((int) ((100.0F / (this == Blocks.PUMPKIN_STEM ? world.spigotConfig.pumpkinModifier : world.spigotConfig.melonModifier)) * (25.0F / f)) + 1) == 0) { // Spigot + int i = (Integer) iblockdata.get(BlockStem.AGE); + + if (i < 7) { + iblockdata = (IBlockData) iblockdata.set(BlockStem.AGE, i + 1); + CraftEventFactory.handleBlockGrowEvent(world, blockposition, iblockdata, 2); // CraftBukkit + } else { + EnumDirection enumdirection = EnumDirection.EnumDirectionLimit.HORIZONTAL.a(random); + BlockPosition blockposition1 = blockposition.shift(enumdirection); + Block block = world.getType(blockposition1.down()).getBlock(); + + if (world.getType(blockposition1).isAir() && (block == Blocks.FARMLAND || block == Blocks.DIRT || block == Blocks.COARSE_DIRT || block == Blocks.PODZOL || block == Blocks.GRASS_BLOCK)) { + // CraftBukkit start + if (!CraftEventFactory.handleBlockGrowEvent(world, blockposition1, this.blockFruit.getBlockData())) { + return; + } + // CraftBukkit end + world.setTypeUpdate(blockposition, (IBlockData) this.blockFruit.e().getBlockData().set(BlockFacingHorizontal.FACING, enumdirection)); + } + } + } + + } + } + + public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) { + super.dropNaturally(iblockdata, world, blockposition, f, i); + if (!world.isClientSide) { + Item item = this.d(); + + if (item != null) { + int j = (Integer) iblockdata.get(BlockStem.AGE); + + for (int k = 0; k < 3; ++k) { + if (world.random.nextInt(15) <= j) { + a(world, blockposition, new ItemStack(item)); + } + } + + } + } + } + + @Nullable + protected Item d() { + return this.blockFruit == Blocks.PUMPKIN ? Items.PUMPKIN_SEEDS : (this.blockFruit == Blocks.MELON ? Items.MELON_SEEDS : null); + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return Items.AIR; + } + + public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) { + Item item = this.d(); + + return item == null ? ItemStack.a : new ItemStack(item); + } + + public boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, boolean flag) { + return (Integer) iblockdata.get(BlockStem.AGE) != 7; + } + + public boolean a(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) { + return true; + } + + public void b(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) { + int i = Math.min(7, (Integer) iblockdata.get(BlockStem.AGE) + MathHelper.nextInt(world.random, 2, 5)); + IBlockData iblockdata1 = (IBlockData) iblockdata.set(BlockStem.AGE, i); + + CraftEventFactory.handleBlockGrowEvent(world, blockposition, iblockdata1, 2); // CraftBukkit + if (i == 7) { + iblockdata1.a(world, blockposition, world.random); + } + + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockStem.AGE); + } + + public BlockStemmed e() { + return this.blockFruit; + } +} diff --git a/src/main/java/net/minecraft/server/BlockTNT.java b/src/main/java/net/minecraft/server/BlockTNT.java new file mode 100644 index 000000000000..c821e6b3b90a --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockTNT.java @@ -0,0 +1,140 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; +import com.destroystokyo.paper.event.block.TNTPrimeEvent; // Paper - TNTPrimeEvent + +public class BlockTNT extends Block { + + public static final BlockStateBoolean a = BlockProperties.x; + + public BlockTNT(Block.Info block_info) { + super(block_info); + this.v((IBlockData) this.getBlockData().set(BlockTNT.a, false)); + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + if (iblockdata1.getBlock() != iblockdata.getBlock()) { + if (world.isBlockIndirectlyPowered(blockposition)) { + // Paper start - TNTPrimeEvent + org.bukkit.block.Block tntBlock = MCUtil.toBukkitBlock(world, blockposition);; + if(!new TNTPrimeEvent(tntBlock, TNTPrimeEvent.PrimeReason.REDSTONE, null).callEvent()) + return; + // Paper end + this.a(world, blockposition); + world.setAir(blockposition); + } + + } + } + + public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + if (world.isBlockIndirectlyPowered(blockposition)) { + // Paper start - TNTPrimeEvent + org.bukkit.block.Block tntBlock = MCUtil.toBukkitBlock(world, blockposition);; + if(!new TNTPrimeEvent(tntBlock, TNTPrimeEvent.PrimeReason.REDSTONE, null).callEvent()) + return; + // Paper end + this.a(world, blockposition); + world.setAir(blockposition); + } + + } + + public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) { + if (!(Boolean) iblockdata.get(BlockTNT.a)) { + super.dropNaturally(iblockdata, world, blockposition, f, i); + } + } + + public void a(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) { + if (!world.e() && !entityhuman.u() && (Boolean) iblockdata.get(BlockTNT.a)) { + this.a(world, blockposition); + } + + super.a(world, blockposition, iblockdata, entityhuman); + } + + public void wasExploded(World world, BlockPosition blockposition, Explosion explosion) { + if (!world.isClientSide) { + // Paper start - TNTPrimeEvent + org.bukkit.block.Block tntBlock = MCUtil.toBukkitBlock(world, blockposition);; + org.bukkit.entity.Entity source = explosion.source != null ? explosion.source.bukkitEntity : null; + if(!new TNTPrimeEvent(tntBlock, TNTPrimeEvent.PrimeReason.EXPLOSION, source).callEvent()) + return; + // Paper end + EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(world, (double) ((float) blockposition.getX() + 0.5F), (double) blockposition.getY(), (double) ((float) blockposition.getZ() + 0.5F), explosion.getSource()); + + entitytntprimed.setFuseTicks((short) (world.random.nextInt(entitytntprimed.getFuseTicks() / 4) + entitytntprimed.getFuseTicks() / 8)); + world.addEntity(entitytntprimed); + } + } + + public void a(World world, BlockPosition blockposition) { + this.a(world, blockposition, (EntityLiving) null); + } + + private void a(World world, BlockPosition blockposition, @Nullable EntityLiving entityliving) { + if (!world.isClientSide) { + EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(world, (double) ((float) blockposition.getX() + 0.5F), (double) blockposition.getY(), (double) ((float) blockposition.getZ() + 0.5F), entityliving); + + world.addEntity(entitytntprimed); + world.a((EntityHuman) null, entitytntprimed.locX, entitytntprimed.locY, entitytntprimed.locZ, SoundEffects.ENTITY_TNT_PRIMED, SoundCategory.BLOCKS, 1.0F, 1.0F); + } + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + ItemStack itemstack = entityhuman.b(enumhand); + Item item = itemstack.getItem(); + + if (item != Items.FLINT_AND_STEEL && item != Items.FIRE_CHARGE) { + return super.interact(iblockdata, world, blockposition, entityhuman, enumhand, enumdirection, f, f1, f2); + } else { + // Paper start - TNTPrimeEvent + org.bukkit.block.Block tntBlock = MCUtil.toBukkitBlock(world, blockposition); + if(!new TNTPrimeEvent(tntBlock, TNTPrimeEvent.PrimeReason.ITEM, entityhuman.bukkitEntity).callEvent()) + return true; + // Paper end + this.a(world, blockposition, (EntityLiving) entityhuman); + world.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 11); + if (item == Items.FLINT_AND_STEEL) { + itemstack.damage(1, entityhuman); + } else { + itemstack.subtract(1); + } + + return true; + } + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) { + if (!world.isClientSide && entity instanceof EntityArrow) { + EntityArrow entityarrow = (EntityArrow) entity; + Entity entity1 = entityarrow.getShooter(); + + if (entityarrow.isBurning()) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entityarrow, blockposition, Blocks.AIR.getBlockData()).isCancelled()) { + return; + } + // CraftBukkit end + // Paper start - TNTPrimeEvent + org.bukkit.block.Block tntBlock = MCUtil.toBukkitBlock(world, blockposition); + if (!new TNTPrimeEvent(tntBlock, TNTPrimeEvent.PrimeReason.PROJECTILE, entityarrow.bukkitEntity).callEvent()) { + return; + } + // Paper end + this.a(world, blockposition, entity1 instanceof EntityLiving ? (EntityLiving) entity1 : null); + world.setAir(blockposition); + } + } + + } + + public boolean a(Explosion explosion) { + return false; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockTNT.a); + } +} diff --git a/src/main/java/net/minecraft/server/BlockTallPlant.java b/src/main/java/net/minecraft/server/BlockTallPlant.java new file mode 100644 index 000000000000..39d449c28e7e --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockTallPlant.java @@ -0,0 +1,91 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class BlockTallPlant extends BlockPlant { + + public static final BlockStateEnum HALF = BlockProperties.P; + + public BlockTallPlant(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockTallPlant.HALF, BlockPropertyDoubleBlockHalf.LOWER)); + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + BlockPropertyDoubleBlockHalf blockpropertydoubleblockhalf = (BlockPropertyDoubleBlockHalf) iblockdata.get(BlockTallPlant.HALF); + + return enumdirection.k() == EnumDirection.EnumAxis.Y && blockpropertydoubleblockhalf == BlockPropertyDoubleBlockHalf.LOWER == (enumdirection == EnumDirection.UP) && (iblockdata1.getBlock() != this || iblockdata1.get(BlockTallPlant.HALF) == blockpropertydoubleblockhalf) ? Blocks.AIR.getBlockData() : (blockpropertydoubleblockhalf == BlockPropertyDoubleBlockHalf.LOWER && enumdirection == EnumDirection.DOWN && !iblockdata.canPlace(generatoraccess, blockposition) ? Blocks.AIR.getBlockData() : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1)); + } + + @Nullable + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + BlockPosition blockposition = blockactioncontext.getClickPosition(); + + return blockposition.getY() < 255 && blockactioncontext.getWorld().getType(blockposition.up()).a(blockactioncontext) ? super.getPlacedState(blockactioncontext) : null; + } + + public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) { + world.setTypeAndData(blockposition.up(), (IBlockData) this.getBlockData().set(BlockTallPlant.HALF, BlockPropertyDoubleBlockHalf.UPPER), 3); + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + if (iblockdata.get(BlockTallPlant.HALF) != BlockPropertyDoubleBlockHalf.UPPER) { + return super.canPlace(iblockdata, iworldreader, blockposition); + } else { + IBlockData iblockdata1 = iworldreader.getType(blockposition.down()); + + return iblockdata1.getBlock() == this && iblockdata1.get(BlockTallPlant.HALF) == BlockPropertyDoubleBlockHalf.LOWER; + } + } + + public void a(GeneratorAccess generatoraccess, BlockPosition blockposition, int i) { + generatoraccess.setTypeAndData(blockposition, (IBlockData) this.getBlockData().set(BlockTallPlant.HALF, BlockPropertyDoubleBlockHalf.LOWER), i); + generatoraccess.setTypeAndData(blockposition.up(), (IBlockData) this.getBlockData().set(BlockTallPlant.HALF, BlockPropertyDoubleBlockHalf.UPPER), i); + } + + public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) { + super.a(world, entityhuman, blockposition, Blocks.AIR.getBlockData(), tileentity, itemstack); + } + + public void a(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) { + // CraftBukkit start + if (((WorldServer)world).hasPhysicsEvent && org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPhysicsEvent(world, blockposition).isCancelled()) { // Paper + return; + } + // CraftBukkit end + BlockPropertyDoubleBlockHalf blockpropertydoubleblockhalf = (BlockPropertyDoubleBlockHalf) iblockdata.get(BlockTallPlant.HALF); + boolean flag = blockpropertydoubleblockhalf == BlockPropertyDoubleBlockHalf.LOWER; + BlockPosition blockposition1 = flag ? blockposition.up() : blockposition.down(); + IBlockData iblockdata1 = world.getType(blockposition1); + + if (iblockdata1.getBlock() == this && iblockdata1.get(BlockTallPlant.HALF) != blockpropertydoubleblockhalf) { + world.setTypeAndData(blockposition1, Blocks.AIR.getBlockData(), 35); + world.a(entityhuman, 2001, blockposition1, Block.getCombinedId(iblockdata1)); + if (!world.isClientSide && !entityhuman.u()) { + if (flag) { + this.a(iblockdata, world, blockposition, entityhuman.getItemInMainHand()); + } else { + this.a(iblockdata1, world, blockposition1, entityhuman.getItemInMainHand()); + } + } + } + + super.a(world, blockposition, iblockdata, entityhuman); + } + + protected void a(IBlockData iblockdata, World world, BlockPosition blockposition, ItemStack itemstack) { + iblockdata.a(world, blockposition, 0); + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return (IMaterial) (iblockdata.get(BlockTallPlant.HALF) == BlockPropertyDoubleBlockHalf.LOWER ? super.getDropType(iblockdata, world, blockposition, i) : Items.AIR); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockTallPlant.HALF); + } + + public Block.EnumRandomOffset q() { + return Block.EnumRandomOffset.XZ; + } +} diff --git a/src/main/java/net/minecraft/server/BlockTrapdoor.java b/src/main/java/net/minecraft/server/BlockTrapdoor.java new file mode 100644 index 000000000000..fc7ecd37f377 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockTrapdoor.java @@ -0,0 +1,186 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public class BlockTrapdoor extends BlockFacingHorizontal implements IFluidSource, IFluidContainer { + + public static final BlockStateBoolean OPEN = BlockProperties.r; + public static final BlockStateEnum HALF = BlockProperties.Q; + public static final BlockStateBoolean c = BlockProperties.t; + public static final BlockStateBoolean o = BlockProperties.y; + protected static final VoxelShape p = Block.a(0.0D, 0.0D, 0.0D, 3.0D, 16.0D, 16.0D); + protected static final VoxelShape q = Block.a(13.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D); + protected static final VoxelShape r = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 3.0D); + protected static final VoxelShape s = Block.a(0.0D, 0.0D, 13.0D, 16.0D, 16.0D, 16.0D); + protected static final VoxelShape t = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 3.0D, 16.0D); + protected static final VoxelShape u = Block.a(0.0D, 13.0D, 0.0D, 16.0D, 16.0D, 16.0D); + + protected BlockTrapdoor(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockTrapdoor.FACING, EnumDirection.NORTH)).set(BlockTrapdoor.OPEN, false)).set(BlockTrapdoor.HALF, BlockPropertyHalf.BOTTOM)).set(BlockTrapdoor.c, false)).set(BlockTrapdoor.o, false)); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + if (!(Boolean) iblockdata.get(BlockTrapdoor.OPEN)) { + return iblockdata.get(BlockTrapdoor.HALF) == BlockPropertyHalf.TOP ? BlockTrapdoor.u : BlockTrapdoor.t; + } else { + switch ((EnumDirection) iblockdata.get(BlockTrapdoor.FACING)) { + case NORTH: + default: + return BlockTrapdoor.s; + case SOUTH: + return BlockTrapdoor.r; + case WEST: + return BlockTrapdoor.q; + case EAST: + return BlockTrapdoor.p; + } + } + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) { + switch (pathmode) { + case LAND: + return (Boolean) iblockdata.get(BlockTrapdoor.OPEN); + case WATER: + return (Boolean) iblockdata.get(BlockTrapdoor.o); + case AIR: + return (Boolean) iblockdata.get(BlockTrapdoor.OPEN); + default: + return false; + } + } + + public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + if (this.material == Material.ORE) { + return false; + } else { + iblockdata = (IBlockData) iblockdata.a((IBlockState) BlockTrapdoor.OPEN); + world.setTypeAndData(blockposition, iblockdata, 2); + if ((Boolean) iblockdata.get(BlockTrapdoor.o)) { + world.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) world)); + } + + this.a(entityhuman, world, blockposition, (Boolean) iblockdata.get(BlockTrapdoor.OPEN)); + return true; + } + } + + protected void a(@Nullable EntityHuman entityhuman, World world, BlockPosition blockposition, boolean flag) { + int i; + + if (flag) { + i = this.material == Material.ORE ? 1037 : 1007; + world.a(entityhuman, i, blockposition, 0); + } else { + i = this.material == Material.ORE ? 1036 : 1013; + world.a(entityhuman, i, blockposition, 0); + } + + } + + public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + if (!world.isClientSide) { + boolean flag = world.isBlockIndirectlyPowered(blockposition); + + if (flag != (Boolean) iblockdata.get(BlockTrapdoor.c)) { + // CraftBukkit start + org.bukkit.World bworld = world.getWorld(); + org.bukkit.block.Block bblock = bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + + int power = bblock.getBlockPower(); + int oldPower = (Boolean) iblockdata.get(OPEN) ? 15 : 0; + + if (oldPower == 0 ^ power == 0 || block.getBlockData().isPowerSource()) { + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bblock, oldPower, power); + world.getServer().getPluginManager().callEvent(eventRedstone); + flag = eventRedstone.getNewCurrent() > 0; + } + // CraftBukkit end + if ((Boolean) iblockdata.get(BlockTrapdoor.OPEN) != flag) { + iblockdata = (IBlockData) iblockdata.set(BlockTrapdoor.OPEN, flag); + this.a((EntityHuman) null, world, blockposition, flag); + } + + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockTrapdoor.c, flag), 2); + if ((Boolean) iblockdata.get(BlockTrapdoor.o)) { + world.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) world)); + } + } + + } + } + + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + IBlockData iblockdata = this.getBlockData(); + Fluid fluid = blockactioncontext.getWorld().getFluid(blockactioncontext.getClickPosition()); + EnumDirection enumdirection = blockactioncontext.getClickedFace(); + + if (!blockactioncontext.c() && enumdirection.k().c()) { + iblockdata = (IBlockData) ((IBlockData) iblockdata.set(BlockTrapdoor.FACING, enumdirection)).set(BlockTrapdoor.HALF, blockactioncontext.n() > 0.5F ? BlockPropertyHalf.TOP : BlockPropertyHalf.BOTTOM); + } else { + iblockdata = (IBlockData) ((IBlockData) iblockdata.set(BlockTrapdoor.FACING, blockactioncontext.f().opposite())).set(BlockTrapdoor.HALF, enumdirection == EnumDirection.UP ? BlockPropertyHalf.BOTTOM : BlockPropertyHalf.TOP); + } + + if (blockactioncontext.getWorld().isBlockIndirectlyPowered(blockactioncontext.getClickPosition())) { + iblockdata = (IBlockData) ((IBlockData) iblockdata.set(BlockTrapdoor.OPEN, true)).set(BlockTrapdoor.c, true); + } + + return (IBlockData) iblockdata.set(BlockTrapdoor.o, fluid.c() == FluidTypes.WATER); + } + + public TextureType c() { + return TextureType.CUTOUT; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockTrapdoor.FACING, BlockTrapdoor.OPEN, BlockTrapdoor.HALF, BlockTrapdoor.c, BlockTrapdoor.o); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return (enumdirection == EnumDirection.UP && iblockdata.get(BlockTrapdoor.HALF) == BlockPropertyHalf.TOP || enumdirection == EnumDirection.DOWN && iblockdata.get(BlockTrapdoor.HALF) == BlockPropertyHalf.BOTTOM) && !(Boolean) iblockdata.get(BlockTrapdoor.OPEN) ? EnumBlockFaceShape.SOLID : EnumBlockFaceShape.UNDEFINED; + } + + public FluidType removeFluid(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata) { + if ((Boolean) iblockdata.get(BlockTrapdoor.o)) { + generatoraccess.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockTrapdoor.o, false), 3); + return FluidTypes.WATER; + } else { + return FluidTypes.EMPTY; + } + } + + public Fluid h(IBlockData iblockdata) { + return (Boolean) iblockdata.get(BlockTrapdoor.o) ? FluidTypes.WATER.a(false) : super.h(iblockdata); + } + + public boolean canPlace(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, FluidType fluidtype) { + return !(Boolean) iblockdata.get(BlockTrapdoor.o) && fluidtype == FluidTypes.WATER; + } + + public boolean place(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, Fluid fluid) { + if (!(Boolean) iblockdata.get(BlockTrapdoor.o) && fluid.c() == FluidTypes.WATER) { + if (!generatoraccess.e()) { + generatoraccess.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockTrapdoor.o, true), 3); + generatoraccess.getFluidTickList().a(blockposition, fluid.c(), fluid.c().a((IWorldReader) generatoraccess)); + } + + return true; + } else { + return false; + } + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + if ((Boolean) iblockdata.get(BlockTrapdoor.o)) { + generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess)); + } + + return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } +} diff --git a/src/main/java/net/minecraft/server/BlockTripwire.java b/src/main/java/net/minecraft/server/BlockTripwire.java new file mode 100644 index 000000000000..c8b82feef83c --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockTripwire.java @@ -0,0 +1,222 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import org.bukkit.event.entity.EntityInteractEvent; // CraftBukkit + +public class BlockTripwire extends Block { + + public static final BlockStateBoolean POWERED = BlockProperties.t; + public static final BlockStateBoolean ATTACHED = BlockProperties.a; + public static final BlockStateBoolean DISARMED = BlockProperties.c; + public static final BlockStateBoolean NORTH = BlockSprawling.a; + public static final BlockStateBoolean EAST = BlockSprawling.b; + public static final BlockStateBoolean SOUTH = BlockSprawling.c; + public static final BlockStateBoolean WEST = BlockSprawling.o; + private static final Map u = BlockTall.q; + protected static final VoxelShape s = Block.a(0.0D, 1.0D, 0.0D, 16.0D, 2.5D, 16.0D); + protected static final VoxelShape t = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D); + private final BlockTripwireHook v; + + public BlockTripwire(BlockTripwireHook blocktripwirehook, Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockTripwire.POWERED, false)).set(BlockTripwire.ATTACHED, false)).set(BlockTripwire.DISARMED, false)).set(BlockTripwire.NORTH, false)).set(BlockTripwire.EAST, false)).set(BlockTripwire.SOUTH, false)).set(BlockTripwire.WEST, false)); + this.v = blocktripwirehook; + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return (Boolean) iblockdata.get(BlockTripwire.ATTACHED) ? BlockTripwire.s : BlockTripwire.t; + } + + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + World world = blockactioncontext.getWorld(); + BlockPosition blockposition = blockactioncontext.getClickPosition(); + + return (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.getBlockData().set(BlockTripwire.NORTH, this.a(world.getType(blockposition.north()), EnumDirection.NORTH))).set(BlockTripwire.EAST, this.a(world.getType(blockposition.east()), EnumDirection.EAST))).set(BlockTripwire.SOUTH, this.a(world.getType(blockposition.south()), EnumDirection.SOUTH))).set(BlockTripwire.WEST, this.a(world.getType(blockposition.west()), EnumDirection.WEST)); + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + return enumdirection.k().c() ? (IBlockData) iblockdata.set((IBlockState) BlockTripwire.u.get(enumdirection), this.a(iblockdata1, enumdirection)) : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public TextureType c() { + return TextureType.TRANSLUCENT; + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + if (iblockdata1.getBlock() != iblockdata.getBlock()) { + this.a(world, blockposition, iblockdata); + } + } + + public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) { + if (!flag && iblockdata.getBlock() != iblockdata1.getBlock()) { + this.a(world, blockposition, (IBlockData) iblockdata.set(BlockTripwire.POWERED, true)); + } + } + + public void a(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) { + if (!world.isClientSide && !entityhuman.getItemInMainHand().isEmpty() && entityhuman.getItemInMainHand().getItem() == Items.SHEARS) { + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockTripwire.DISARMED, true), 4); + } + + super.a(world, blockposition, iblockdata, entityhuman); + } + + private void a(World world, BlockPosition blockposition, IBlockData iblockdata) { + EnumDirection[] aenumdirection = new EnumDirection[] { EnumDirection.SOUTH, EnumDirection.WEST}; + int i = aenumdirection.length; + int j = 0; + + while (j < i) { + EnumDirection enumdirection = aenumdirection[j]; + int k = 1; + + while (true) { + if (k < 42) { + BlockPosition blockposition1 = blockposition.shift(enumdirection, k); + IBlockData iblockdata1 = world.getType(blockposition1); + + if (iblockdata1.getBlock() == this.v) { + if (iblockdata1.get(BlockTripwireHook.FACING) == enumdirection.opposite()) { + this.v.a(world, blockposition1, iblockdata1, false, true, k, iblockdata); + } + } else if (iblockdata1.getBlock() == this) { + ++k; + continue; + } + } + + ++j; + break; + } + } + + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) { + if (!world.isClientSide) { + if (!(Boolean) iblockdata.get(BlockTripwire.POWERED)) { + this.a(world, blockposition); + } + } + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!world.isClientSide) { + if ((Boolean) world.getType(blockposition).get(BlockTripwire.POWERED)) { + this.a(world, blockposition); + } + } + } + + private void a(World world, BlockPosition blockposition) { + IBlockData iblockdata = world.getType(blockposition); + boolean flag = (Boolean) iblockdata.get(BlockTripwire.POWERED); + boolean flag1 = false; + List list = world.getEntities((Entity) null, iblockdata.getShape(world, blockposition).getBoundingBox().a(blockposition)); + + if (!list.isEmpty()) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + if (!entity.isIgnoreBlockTrigger()) { + flag1 = true; + break; + } + } + } + + // CraftBukkit start - Call interact even when triggering connected tripwire + if (flag != flag1 && flag1 && (Boolean)iblockdata.get(ATTACHED)) { + org.bukkit.World bworld = world.getWorld(); + org.bukkit.plugin.PluginManager manager = world.getServer().getPluginManager(); + org.bukkit.block.Block block = bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + boolean allowed = false; + + // If all of the events are cancelled block the tripwire trigger, else allow + for (Object object : list) { + if (object != null) { + org.bukkit.event.Cancellable cancellable; + + if (object instanceof EntityHuman) { + cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((EntityHuman) object, org.bukkit.event.block.Action.PHYSICAL, blockposition, null, null, null); + } else if (object instanceof Entity) { + cancellable = new EntityInteractEvent(((Entity) object).getBukkitEntity(), block); + manager.callEvent((EntityInteractEvent) cancellable); + } else { + continue; + } + + if (!cancellable.isCancelled()) { + allowed = true; + break; + } + } + } + + if (!allowed) { + return; + } + } + // CraftBukkit end + + if (flag1 != flag) { + iblockdata = (IBlockData) iblockdata.set(BlockTripwire.POWERED, flag1); + world.setTypeAndData(blockposition, iblockdata, 3); + this.a(world, blockposition, iblockdata); + } + + if (flag1) { + world.getBlockTickList().a(new BlockPosition(blockposition), this, this.a((IWorldReader) world)); + } + + } + + public boolean a(IBlockData iblockdata, EnumDirection enumdirection) { + Block block = iblockdata.getBlock(); + + return block == this.v ? iblockdata.get(BlockTripwireHook.FACING) == enumdirection.opposite() : block == this; + } + + public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) { + switch (enumblockrotation) { + case CLOCKWISE_180: + return (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) iblockdata.set(BlockTripwire.NORTH, iblockdata.get(BlockTripwire.SOUTH))).set(BlockTripwire.EAST, iblockdata.get(BlockTripwire.WEST))).set(BlockTripwire.SOUTH, iblockdata.get(BlockTripwire.NORTH))).set(BlockTripwire.WEST, iblockdata.get(BlockTripwire.EAST)); + case COUNTERCLOCKWISE_90: + return (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) iblockdata.set(BlockTripwire.NORTH, iblockdata.get(BlockTripwire.EAST))).set(BlockTripwire.EAST, iblockdata.get(BlockTripwire.SOUTH))).set(BlockTripwire.SOUTH, iblockdata.get(BlockTripwire.WEST))).set(BlockTripwire.WEST, iblockdata.get(BlockTripwire.NORTH)); + case CLOCKWISE_90: + return (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) iblockdata.set(BlockTripwire.NORTH, iblockdata.get(BlockTripwire.WEST))).set(BlockTripwire.EAST, iblockdata.get(BlockTripwire.NORTH))).set(BlockTripwire.SOUTH, iblockdata.get(BlockTripwire.EAST))).set(BlockTripwire.WEST, iblockdata.get(BlockTripwire.SOUTH)); + default: + return iblockdata; + } + } + + public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) { + switch (enumblockmirror) { + case LEFT_RIGHT: + return (IBlockData) ((IBlockData) iblockdata.set(BlockTripwire.NORTH, iblockdata.get(BlockTripwire.SOUTH))).set(BlockTripwire.SOUTH, iblockdata.get(BlockTripwire.NORTH)); + case FRONT_BACK: + return (IBlockData) ((IBlockData) iblockdata.set(BlockTripwire.EAST, iblockdata.get(BlockTripwire.WEST))).set(BlockTripwire.WEST, iblockdata.get(BlockTripwire.EAST)); + default: + return super.a(iblockdata, enumblockmirror); + } + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockTripwire.POWERED, BlockTripwire.ATTACHED, BlockTripwire.DISARMED, BlockTripwire.NORTH, BlockTripwire.EAST, BlockTripwire.WEST, BlockTripwire.SOUTH); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } +} diff --git a/src/main/java/net/minecraft/server/BlockTripwireHook.java b/src/main/java/net/minecraft/server/BlockTripwireHook.java new file mode 100644 index 000000000000..636e6202ab78 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockTripwireHook.java @@ -0,0 +1,245 @@ +package net.minecraft.server; + +import com.google.common.base.MoreObjects; +import java.util.Random; +import javax.annotation.Nullable; + +import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit + +public class BlockTripwireHook extends Block { + + public static final BlockStateDirection FACING = BlockFacingHorizontal.FACING; + public static final BlockStateBoolean POWERED = BlockProperties.t; + public static final BlockStateBoolean ATTACHED = BlockProperties.a; + protected static final VoxelShape o = Block.a(5.0D, 0.0D, 10.0D, 11.0D, 10.0D, 16.0D); + protected static final VoxelShape p = Block.a(5.0D, 0.0D, 0.0D, 11.0D, 10.0D, 6.0D); + protected static final VoxelShape q = Block.a(10.0D, 0.0D, 5.0D, 16.0D, 10.0D, 11.0D); + protected static final VoxelShape r = Block.a(0.0D, 0.0D, 5.0D, 6.0D, 10.0D, 11.0D); + + public BlockTripwireHook(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockTripwireHook.FACING, EnumDirection.NORTH)).set(BlockTripwireHook.POWERED, false)).set(BlockTripwireHook.ATTACHED, false)); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + switch ((EnumDirection) iblockdata.get(BlockTripwireHook.FACING)) { + case EAST: + default: + return BlockTripwireHook.r; + case WEST: + return BlockTripwireHook.q; + case SOUTH: + return BlockTripwireHook.p; + case NORTH: + return BlockTripwireHook.o; + } + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockTripwireHook.FACING); + BlockPosition blockposition1 = blockposition.shift(enumdirection.opposite()); + IBlockData iblockdata1 = iworldreader.getType(blockposition1); + boolean flag = b(iblockdata1.getBlock()); + + return !flag && enumdirection.k().c() && iblockdata1.c(iworldreader, blockposition1, enumdirection) == EnumBlockFaceShape.SOLID && !iblockdata1.isPowerSource(); + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + return enumdirection.opposite() == iblockdata.get(BlockTripwireHook.FACING) && !iblockdata.canPlace(generatoraccess, blockposition) ? Blocks.AIR.getBlockData() : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } + + @Nullable + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + IBlockData iblockdata = (IBlockData) ((IBlockData) this.getBlockData().set(BlockTripwireHook.POWERED, false)).set(BlockTripwireHook.ATTACHED, false); + World world = blockactioncontext.getWorld(); + BlockPosition blockposition = blockactioncontext.getClickPosition(); + EnumDirection[] aenumdirection = blockactioncontext.e(); + EnumDirection[] aenumdirection1 = aenumdirection; + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection1[j]; + + if (enumdirection.k().c()) { + EnumDirection enumdirection1 = enumdirection.opposite(); + + iblockdata = (IBlockData) iblockdata.set(BlockTripwireHook.FACING, enumdirection1); + if (iblockdata.canPlace(world, blockposition)) { + return iblockdata; + } + } + } + + return null; + } + + public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) { + this.a(world, blockposition, iblockdata, false, false, -1, (IBlockData) null); + } + + public void a(World world, BlockPosition blockposition, IBlockData iblockdata, boolean flag, boolean flag1, int i, @Nullable IBlockData iblockdata1) { + EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockTripwireHook.FACING); + boolean flag2 = (Boolean) iblockdata.get(BlockTripwireHook.ATTACHED); + boolean flag3 = (Boolean) iblockdata.get(BlockTripwireHook.POWERED); + boolean flag4 = !flag; + boolean flag5 = false; + int j = 0; + IBlockData[] aiblockdata = new IBlockData[42]; + + BlockPosition blockposition1; + + for (int k = 1; k < 42; ++k) { + blockposition1 = blockposition.shift(enumdirection, k); + IBlockData iblockdata2 = world.getType(blockposition1); + + if (iblockdata2.getBlock() == Blocks.TRIPWIRE_HOOK) { + if (iblockdata2.get(BlockTripwireHook.FACING) == enumdirection.opposite()) { + j = k; + } + break; + } + + if (iblockdata2.getBlock() != Blocks.TRIPWIRE && k != i) { + aiblockdata[k] = null; + flag4 = false; + } else { + if (k == i) { + iblockdata2 = (IBlockData) MoreObjects.firstNonNull(iblockdata1, iblockdata2); + } + + boolean flag6 = !(Boolean) iblockdata2.get(BlockTripwire.DISARMED); + boolean flag7 = (Boolean) iblockdata2.get(BlockTripwire.POWERED); + + flag5 |= flag6 && flag7; + aiblockdata[k] = iblockdata2; + if (k == i) { + world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world)); + flag4 &= flag6; + } + } + } + + flag4 &= j > 1; + flag5 &= flag4; + IBlockData iblockdata3 = (IBlockData) ((IBlockData) this.getBlockData().set(BlockTripwireHook.ATTACHED, flag4)).set(BlockTripwireHook.POWERED, flag5); + + if (j > 0) { + blockposition1 = blockposition.shift(enumdirection, j); + EnumDirection enumdirection1 = enumdirection.opposite(); + + world.setTypeAndData(blockposition1, (IBlockData) iblockdata3.set(BlockTripwireHook.FACING, enumdirection1), 3); + this.a(world, blockposition1, enumdirection1); + this.a(world, blockposition1, flag4, flag5, flag2, flag3); + } + + // CraftBukkit start + org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + + BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, 15, 0); + world.getServer().getPluginManager().callEvent(eventRedstone); + + if (eventRedstone.getNewCurrent() > 0) { + return; + } + // CraftBukkit end + + this.a(world, blockposition, flag4, flag5, flag2, flag3); + if (!flag) { + world.setTypeAndData(blockposition, (IBlockData) iblockdata3.set(BlockTripwireHook.FACING, enumdirection), 3); + if (flag1) { + this.a(world, blockposition, enumdirection); + } + } + + if (flag2 != flag4) { + for (int l = 1; l < j; ++l) { + BlockPosition blockposition2 = blockposition.shift(enumdirection, l); + IBlockData iblockdata4 = aiblockdata[l]; + + if (iblockdata4 != null) { + world.setTypeAndData(blockposition2, (IBlockData) iblockdata4.set(BlockTripwireHook.ATTACHED, flag4), 3); + if (!world.getType(blockposition2).isAir()) { + ; + } + } + } + } + + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + this.a(world, blockposition, iblockdata, false, true, -1, (IBlockData) null); + } + + private void a(World world, BlockPosition blockposition, boolean flag, boolean flag1, boolean flag2, boolean flag3) { + if (flag1 && !flag3) { + world.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_TRIPWIRE_CLICK_ON, SoundCategory.BLOCKS, 0.4F, 0.6F); + } else if (!flag1 && flag3) { + world.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_TRIPWIRE_CLICK_OFF, SoundCategory.BLOCKS, 0.4F, 0.5F); + } else if (flag && !flag2) { + world.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_TRIPWIRE_ATTACH, SoundCategory.BLOCKS, 0.4F, 0.7F); + } else if (!flag && flag2) { + world.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_TRIPWIRE_DETACH, SoundCategory.BLOCKS, 0.4F, 1.2F / (world.random.nextFloat() * 0.2F + 0.9F)); + } + + } + + private void a(World world, BlockPosition blockposition, EnumDirection enumdirection) { + world.applyPhysics(blockposition, this); + world.applyPhysics(blockposition.shift(enumdirection.opposite()), this); + } + + public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) { + if (!flag && iblockdata.getBlock() != iblockdata1.getBlock()) { + boolean flag1 = (Boolean) iblockdata.get(BlockTripwireHook.ATTACHED); + boolean flag2 = (Boolean) iblockdata.get(BlockTripwireHook.POWERED); + + if (flag1 || flag2) { + this.a(world, blockposition, iblockdata, true, false, -1, (IBlockData) null); + } + + if (flag2) { + world.applyPhysics(blockposition, this); + world.applyPhysics(blockposition.shift(((EnumDirection) iblockdata.get(BlockTripwireHook.FACING)).opposite()), this); + } + + super.remove(iblockdata, world, blockposition, iblockdata1, flag); + } + } + + public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return (Boolean) iblockdata.get(BlockTripwireHook.POWERED) ? 15 : 0; + } + + public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return !(Boolean) iblockdata.get(BlockTripwireHook.POWERED) ? 0 : (iblockdata.get(BlockTripwireHook.FACING) == enumdirection ? 15 : 0); + } + + public boolean isPowerSource(IBlockData iblockdata) { + return true; + } + + public TextureType c() { + return TextureType.CUTOUT_MIPPED; + } + + public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) { + return (IBlockData) iblockdata.set(BlockTripwireHook.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockTripwireHook.FACING))); + } + + public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) { + return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockTripwireHook.FACING))); + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockTripwireHook.FACING, BlockTripwireHook.POWERED, BlockTripwireHook.ATTACHED); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } +} diff --git a/src/main/java/net/minecraft/server/BlockTurtleEgg.java b/src/main/java/net/minecraft/server/BlockTurtleEgg.java new file mode 100644 index 000000000000..1c1bf85a0efd --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockTurtleEgg.java @@ -0,0 +1,174 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; +import org.bukkit.craftbukkit.block.CraftBlock; + +// CraftBukkit start +import org.bukkit.event.entity.EntityInteractEvent; +import org.bukkit.craftbukkit.event.CraftEventFactory; +// CraftBukkit end + +public class BlockTurtleEgg extends Block { + + private static final VoxelShape c = Block.a(3.0D, 0.0D, 3.0D, 12.0D, 7.0D, 12.0D); + private static final VoxelShape o = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 7.0D, 15.0D); + public static final BlockStateInteger a = BlockProperties.ad; + public static final BlockStateInteger b = BlockProperties.ac; + + public BlockTurtleEgg(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockTurtleEgg.a, 0)).set(BlockTurtleEgg.b, 1)); + } + + public void stepOn(World world, BlockPosition blockposition, Entity entity) { + this.a(world, blockposition, entity, 100); + super.stepOn(world, blockposition, entity); + } + + public void fallOn(World world, BlockPosition blockposition, Entity entity, float f) { + if (!(entity instanceof EntityZombie)) { + this.a(world, blockposition, entity, 3); + } + + super.fallOn(world, blockposition, entity, f); + } + + private void a(World world, BlockPosition blockposition, Entity entity, int i) { + if (!this.a(world, entity)) { + super.stepOn(world, blockposition, entity); + } else { + if (!world.isClientSide && world.random.nextInt(i) == 0) { + // CraftBukkit start - Step on eggs + org.bukkit.event.Cancellable cancellable; + if (entity instanceof EntityHuman) { + cancellable = CraftEventFactory.callPlayerInteractEvent((EntityHuman) entity, org.bukkit.event.block.Action.PHYSICAL, blockposition, null, null, null); + } else { + cancellable = new EntityInteractEvent(entity.getBukkitEntity(), CraftBlock.at(world, blockposition)); + world.getServer().getPluginManager().callEvent((EntityInteractEvent) cancellable); + } + + if (cancellable.isCancelled()) { + return; + } + // CraftBukkit end + this.a(world, blockposition, world.getType(blockposition)); + } + + } + } + + private void a(World world, BlockPosition blockposition, IBlockData iblockdata) { + world.a((EntityHuman) null, blockposition, SoundEffects.ENTITY_TURTLE_EGG_BREAK, SoundCategory.BLOCKS, 0.7F, 0.9F + world.random.nextFloat() * 0.2F); + int i = (Integer) iblockdata.get(BlockTurtleEgg.b); + + if (i <= 1) { + world.setAir(blockposition, false); + } else { + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockTurtleEgg.b, i - 1), 2); + world.triggerEffect(2001, blockposition, Block.getCombinedId(iblockdata)); + } + + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (this.a(world) && this.a((IBlockAccess) world, blockposition)) { + int i = (Integer) iblockdata.get(BlockTurtleEgg.a); + + if (i < 2) { + // CraftBukkit start - Call BlockGrowEvent + if (!CraftEventFactory.handleBlockGrowEvent(world, blockposition, iblockdata.set(BlockTurtleEgg.a, i + 1), 2)) { + return; + } + // CraftBukkit end + world.a((EntityHuman) null, blockposition, SoundEffects.ENTITY_TURTLE_EGG_CRACK, SoundCategory.BLOCKS, 0.7F, 0.9F + random.nextFloat() * 0.2F); + // world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockTurtleEgg.a, i + 1), 2); // CraftBukkit - handled above + } else { + // CraftBukkit start - Call BlockFadeEvent + if (CraftEventFactory.callBlockFadeEvent(world, blockposition, Blocks.AIR.getBlockData()).isCancelled()) { + return; + } + // CraftBukkit end + world.a((EntityHuman) null, blockposition, SoundEffects.ENTITY_TURTLE_EGG_HATCH, SoundCategory.BLOCKS, 0.7F, 0.9F + random.nextFloat() * 0.2F); + world.setAir(blockposition); + if (!world.isClientSide) { + for (int j = 0; j < (Integer) iblockdata.get(BlockTurtleEgg.b); ++j) { + world.triggerEffect(2001, blockposition, Block.getCombinedId(iblockdata)); + EntityTurtle entityturtle = EntityTypes.TURTLE.create(world); // Paper + + entityturtle.setAgeRaw(-24000); + entityturtle.g(blockposition); + entityturtle.setPositionRotation((double) blockposition.getX() + 0.3D + (double) j * 0.2D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.3D, 0.0F, 0.0F); + world.addEntity(entityturtle, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.EGG); // CraftBukkit + } + } + } + } + + } + + private boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) { + return iblockaccess.getType(blockposition.down()).getBlock() == Blocks.SAND; + } + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + if (this.a((IBlockAccess) world, blockposition) && !world.isClientSide) { + world.triggerEffect(2005, blockposition, 0); + } + + } + + private boolean a(World world) { + float f = world.k(1.0F); + + return (double) f < 0.69D && (double) f > 0.65D ? true : world.random.nextInt(500) == 0; + } + + protected boolean X_() { + return true; + } + + public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) { + super.a(world, entityhuman, blockposition, iblockdata, tileentity, itemstack); + this.a(world, blockposition, iblockdata); + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return Items.AIR; + } + + public boolean a(IBlockData iblockdata, BlockActionContext blockactioncontext) { + return blockactioncontext.getItemStack().getItem() == this.getItem() && (Integer) iblockdata.get(BlockTurtleEgg.b) < 4 ? true : super.a(iblockdata, blockactioncontext); + } + + @Nullable + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + IBlockData iblockdata = blockactioncontext.getWorld().getType(blockactioncontext.getClickPosition()); + + return iblockdata.getBlock() == this ? (IBlockData) iblockdata.set(BlockTurtleEgg.b, Math.min(4, (Integer) iblockdata.get(BlockTurtleEgg.b) + 1)) : super.getPlacedState(blockactioncontext); + } + + public TextureType c() { + return TextureType.CUTOUT; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return (Integer) iblockdata.get(BlockTurtleEgg.b) > 1 ? BlockTurtleEgg.o : BlockTurtleEgg.c; + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockTurtleEgg.a, BlockTurtleEgg.b); + } + + private boolean a(World world, Entity entity) { + return entity instanceof EntityTurtle ? false : (entity instanceof EntityLiving && !(entity instanceof EntityHuman) ? world.getGameRules().getBoolean("mobGriefing") : true); + } +} diff --git a/src/main/java/net/minecraft/server/BlockVine.java b/src/main/java/net/minecraft/server/BlockVine.java new file mode 100644 index 000000000000..7006619b2202 --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockVine.java @@ -0,0 +1,385 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.Map; +import java.util.Random; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockVine extends Block { + + public static final BlockStateBoolean UP = BlockSprawling.p; + public static final BlockStateBoolean NORTH = BlockSprawling.a; + public static final BlockStateBoolean EAST = BlockSprawling.b; + public static final BlockStateBoolean SOUTH = BlockSprawling.c; + public static final BlockStateBoolean WEST = BlockSprawling.o; + public static final Map q = (Map) BlockSprawling.r.entrySet().stream().filter((entry) -> { + return entry.getKey() != EnumDirection.DOWN; + }).collect(SystemUtils.a()); + protected static final VoxelShape r = Block.a(0.0D, 15.0D, 0.0D, 16.0D, 16.0D, 16.0D); + protected static final VoxelShape s = Block.a(0.0D, 0.0D, 0.0D, 1.0D, 16.0D, 16.0D); + protected static final VoxelShape t = Block.a(15.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D); + protected static final VoxelShape u = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 1.0D); + protected static final VoxelShape v = Block.a(0.0D, 0.0D, 15.0D, 16.0D, 16.0D, 16.0D); + + public BlockVine(Block.Info block_info) { + super(block_info); + this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockVine.UP, false)).set(BlockVine.NORTH, false)).set(BlockVine.EAST, false)).set(BlockVine.SOUTH, false)).set(BlockVine.WEST, false)); + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + VoxelShape voxelshape = VoxelShapes.a(); + + if ((Boolean) iblockdata.get(BlockVine.UP)) { + voxelshape = VoxelShapes.a(voxelshape, BlockVine.r); + } + + if ((Boolean) iblockdata.get(BlockVine.NORTH)) { + voxelshape = VoxelShapes.a(voxelshape, BlockVine.u); + } + + if ((Boolean) iblockdata.get(BlockVine.EAST)) { + voxelshape = VoxelShapes.a(voxelshape, BlockVine.t); + } + + if ((Boolean) iblockdata.get(BlockVine.SOUTH)) { + voxelshape = VoxelShapes.a(voxelshape, BlockVine.v); + } + + if ((Boolean) iblockdata.get(BlockVine.WEST)) { + voxelshape = VoxelShapes.a(voxelshape, BlockVine.s); + } + + return voxelshape; + } + + public boolean a(IBlockData iblockdata) { + return false; + } + + public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) { + return this.k(this.m(iblockdata, iworldreader, blockposition)); + } + + private boolean k(IBlockData iblockdata) { + return this.w(iblockdata) > 0; + } + + private int w(IBlockData iblockdata) { + int i = 0; + Iterator iterator = BlockVine.q.values().iterator(); + + while (iterator.hasNext()) { + BlockStateBoolean blockstateboolean = (BlockStateBoolean) iterator.next(); + + if ((Boolean) iblockdata.get(blockstateboolean)) { + ++i; + } + } + + return i; + } + + private boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + if (enumdirection == EnumDirection.DOWN) { + return false; + } else { + BlockPosition blockposition1 = blockposition.shift(enumdirection); + + if (this.b(iblockaccess, blockposition1, enumdirection)) { + return true; + } else if (enumdirection.k() == EnumDirection.EnumAxis.Y) { + return false; + } else { + BlockStateBoolean blockstateboolean = (BlockStateBoolean) BlockVine.q.get(enumdirection); + IBlockData iblockdata = iblockaccess.getType(blockposition.up()); + + return iblockdata.getBlock() == this && (Boolean) iblockdata.get(blockstateboolean); + } + } + } + + private boolean b(IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + IBlockData iblockdata = iblockaccess.getType(blockposition); + + return iblockdata.c(iblockaccess, blockposition, enumdirection.opposite()) == EnumBlockFaceShape.SOLID && !f(iblockdata.getBlock()); + } + + protected static boolean f(Block block) { + return block instanceof BlockShulkerBox || block instanceof BlockStainedGlass || block == Blocks.BEACON || block == Blocks.CAULDRON || block == Blocks.GLASS || block == Blocks.PISTON || block == Blocks.STICKY_PISTON || block == Blocks.PISTON_HEAD || block.a(TagsBlock.WOODEN_TRAPDOORS); + } + + private IBlockData m(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + BlockPosition blockposition1 = blockposition.up(); + + if ((Boolean) iblockdata.get(BlockVine.UP)) { + iblockdata = (IBlockData) iblockdata.set(BlockVine.UP, this.b(iblockaccess, blockposition1, EnumDirection.DOWN)); + } + + IBlockData iblockdata1 = null; + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + BlockStateBoolean blockstateboolean = getDirection(enumdirection); + + if ((Boolean) iblockdata.get(blockstateboolean)) { + boolean flag = this.a(iblockaccess, blockposition, enumdirection); + + if (!flag) { + if (iblockdata1 == null) { + iblockdata1 = iblockaccess.getType(blockposition1); + } + + flag = iblockdata1.getBlock() == this && (Boolean) iblockdata1.get(blockstateboolean); + } + + iblockdata = (IBlockData) iblockdata.set(blockstateboolean, flag); + } + } + + return iblockdata; + } + + public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + if (enumdirection == EnumDirection.DOWN) { + return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1); + } else { + IBlockData iblockdata2 = this.m(iblockdata, generatoraccess, blockposition); + + return !this.k(iblockdata2) ? Blocks.AIR.getBlockData() : iblockdata2; + } + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) { + if (!world.isClientSide) { + IBlockData iblockdata1 = this.m(iblockdata, world, blockposition); + + if (iblockdata1 != iblockdata) { + if (this.k(iblockdata1)) { + world.setTypeAndData(blockposition, iblockdata1, 2); + } else { + iblockdata.a(world, blockposition, 0); + world.setAir(blockposition); + } + + } else if (world.random.nextInt(Math.max(1, (int) (100.0F / world.spigotConfig.vineModifier) * 4)) == 0) { // Spigot + EnumDirection enumdirection = EnumDirection.a(random); + BlockPosition blockposition1 = blockposition.up(); + BlockPosition blockposition2; + IBlockData iblockdata2; + EnumDirection enumdirection1; + + if (enumdirection.k().c() && !(Boolean) iblockdata.get(getDirection(enumdirection))) { + if (this.a((IBlockAccess) world, blockposition)) { + blockposition2 = blockposition.shift(enumdirection); + iblockdata2 = world.getType(blockposition2); + if (iblockdata2.isAir()) { + enumdirection1 = enumdirection.e(); + EnumDirection enumdirection2 = enumdirection.f(); + boolean flag = (Boolean) iblockdata.get(getDirection(enumdirection1)); + boolean flag1 = (Boolean) iblockdata.get(getDirection(enumdirection2)); + BlockPosition blockposition3 = blockposition2.shift(enumdirection1); + BlockPosition blockposition4 = blockposition2.shift(enumdirection2); + + // CraftBukkit start - Call BlockSpreadEvent + BlockPosition source = blockposition; + + if (flag && this.b((IBlockAccess) world, blockposition3, enumdirection1)) { + CraftEventFactory.handleBlockSpreadEvent(world, source, blockposition2, (IBlockData) this.getBlockData().set(getDirection(enumdirection1), true), 2); + } else if (flag1 && this.b((IBlockAccess) world, blockposition4, enumdirection2)) { + CraftEventFactory.handleBlockSpreadEvent(world, source, blockposition2, (IBlockData) this.getBlockData().set(getDirection(enumdirection2), true), 2); + } else { + EnumDirection enumdirection3 = enumdirection.opposite(); + + if (flag && world.isEmpty(blockposition3) && this.b((IBlockAccess) world, blockposition.shift(enumdirection1), enumdirection3)) { + CraftEventFactory.handleBlockSpreadEvent(world, source, blockposition3, (IBlockData) this.getBlockData().set(getDirection(enumdirection3), true), 2); + } else if (flag1 && world.isEmpty(blockposition4) && this.b((IBlockAccess) world, blockposition.shift(enumdirection2), enumdirection3)) { + CraftEventFactory.handleBlockSpreadEvent(world, source, blockposition4, (IBlockData) this.getBlockData().set(getDirection(enumdirection3), true), 2); + } else if ((double) world.random.nextFloat() < 0.05D && this.b((IBlockAccess) world, blockposition2.up(), EnumDirection.UP)) { + CraftEventFactory.handleBlockSpreadEvent(world, source, blockposition2, (IBlockData) this.getBlockData().set(BlockVine.UP, true), 2); + } + // CraftBukkit end + } + } else if (this.b((IBlockAccess) world, blockposition2, enumdirection)) { + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(getDirection(enumdirection), true), 2); + } + + } + } else { + if (enumdirection == EnumDirection.UP && blockposition.getY() < 255) { + if (this.a((IBlockAccess) world, blockposition, enumdirection)) { + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockVine.UP, true), 2); + return; + } + + if (world.isEmpty(blockposition1)) { + if (!this.a((IBlockAccess) world, blockposition)) { + return; + } + + IBlockData iblockdata3 = iblockdata; + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + enumdirection1 = (EnumDirection) iterator.next(); + if (random.nextBoolean() || !this.b((IBlockAccess) world, blockposition1.shift(enumdirection1), EnumDirection.UP)) { + iblockdata3 = (IBlockData) iblockdata3.set(getDirection(enumdirection1), false); + } + } + + if (this.x(iblockdata3)) { + CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition1, iblockdata3, 2); // CraftBukkit + } + + return; + } + } + + if (blockposition.getY() > 0) { + blockposition2 = blockposition.down(); + iblockdata2 = world.getType(blockposition2); + if (iblockdata2.isAir() || iblockdata2.getBlock() == this) { + IBlockData iblockdata4 = iblockdata2.isAir() ? this.getBlockData() : iblockdata2; + IBlockData iblockdata5 = this.a(iblockdata, iblockdata4, random); + + if (iblockdata4 != iblockdata5 && this.x(iblockdata5)) { + CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition2, iblockdata5, 2); // CraftBukkit + } + } + } + + } + } + } + } + + private IBlockData a(IBlockData iblockdata, IBlockData iblockdata1, Random random) { + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + + if (random.nextBoolean()) { + BlockStateBoolean blockstateboolean = getDirection(enumdirection); + + if ((Boolean) iblockdata.get(blockstateboolean)) { + iblockdata1 = (IBlockData) iblockdata1.set(blockstateboolean, true); + } + } + } + + return iblockdata1; + } + + private boolean x(IBlockData iblockdata) { + return (Boolean) iblockdata.get(BlockVine.NORTH) || (Boolean) iblockdata.get(BlockVine.EAST) || (Boolean) iblockdata.get(BlockVine.SOUTH) || (Boolean) iblockdata.get(BlockVine.WEST); + } + + private boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) { + boolean flag = true; + Iterable iterable = BlockPosition.MutableBlockPosition.b(blockposition.getX() - 4, blockposition.getY() - 1, blockposition.getZ() - 4, blockposition.getX() + 4, blockposition.getY() + 1, blockposition.getZ() + 4); + int i = 5; + Iterator iterator = iterable.iterator(); + + while (iterator.hasNext()) { + BlockPosition blockposition1 = (BlockPosition) iterator.next(); + + if (iblockaccess.getType(blockposition1).getBlock() == this) { + --i; + if (i <= 0) { + return false; + } + } + } + + return true; + } + + public boolean a(IBlockData iblockdata, BlockActionContext blockactioncontext) { + IBlockData iblockdata1 = blockactioncontext.getWorld().getType(blockactioncontext.getClickPosition()); + + return iblockdata1.getBlock() == this ? this.w(iblockdata1) < BlockVine.q.size() : super.a(iblockdata, blockactioncontext); + } + + @Nullable + public IBlockData getPlacedState(BlockActionContext blockactioncontext) { + IBlockData iblockdata = blockactioncontext.getWorld().getType(blockactioncontext.getClickPosition()); + boolean flag = iblockdata.getBlock() == this; + IBlockData iblockdata1 = flag ? iblockdata : this.getBlockData(); + EnumDirection[] aenumdirection = blockactioncontext.e(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + + if (enumdirection != EnumDirection.DOWN) { + BlockStateBoolean blockstateboolean = getDirection(enumdirection); + boolean flag1 = flag && (Boolean) iblockdata.get(blockstateboolean); + + if (!flag1 && this.a((IBlockAccess) blockactioncontext.getWorld(), blockactioncontext.getClickPosition(), enumdirection)) { + return (IBlockData) iblockdata1.set(blockstateboolean, true); + } + } + } + + return flag ? iblockdata1 : null; + } + + public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) { + return Items.AIR; + } + + public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) { + if (!world.isClientSide && itemstack.getItem() == Items.SHEARS) { + entityhuman.b(StatisticList.BLOCK_MINED.b(this)); + entityhuman.applyExhaustion(0.005F); + a(world, blockposition, new ItemStack(Blocks.VINE)); + } else { + super.a(world, entityhuman, blockposition, iblockdata, tileentity, itemstack); + } + + } + + public TextureType c() { + return TextureType.CUTOUT; + } + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(BlockVine.UP, BlockVine.NORTH, BlockVine.EAST, BlockVine.SOUTH, BlockVine.WEST); + } + + public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) { + switch (enumblockrotation) { + case CLOCKWISE_180: + return (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) iblockdata.set(BlockVine.NORTH, iblockdata.get(BlockVine.SOUTH))).set(BlockVine.EAST, iblockdata.get(BlockVine.WEST))).set(BlockVine.SOUTH, iblockdata.get(BlockVine.NORTH))).set(BlockVine.WEST, iblockdata.get(BlockVine.EAST)); + case COUNTERCLOCKWISE_90: + return (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) iblockdata.set(BlockVine.NORTH, iblockdata.get(BlockVine.EAST))).set(BlockVine.EAST, iblockdata.get(BlockVine.SOUTH))).set(BlockVine.SOUTH, iblockdata.get(BlockVine.WEST))).set(BlockVine.WEST, iblockdata.get(BlockVine.NORTH)); + case CLOCKWISE_90: + return (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) iblockdata.set(BlockVine.NORTH, iblockdata.get(BlockVine.WEST))).set(BlockVine.EAST, iblockdata.get(BlockVine.NORTH))).set(BlockVine.SOUTH, iblockdata.get(BlockVine.EAST))).set(BlockVine.WEST, iblockdata.get(BlockVine.SOUTH)); + default: + return iblockdata; + } + } + + public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) { + switch (enumblockmirror) { + case LEFT_RIGHT: + return (IBlockData) ((IBlockData) iblockdata.set(BlockVine.NORTH, iblockdata.get(BlockVine.SOUTH))).set(BlockVine.SOUTH, iblockdata.get(BlockVine.NORTH)); + case FRONT_BACK: + return (IBlockData) ((IBlockData) iblockdata.set(BlockVine.EAST, iblockdata.get(BlockVine.WEST))).set(BlockVine.WEST, iblockdata.get(BlockVine.EAST)); + default: + return super.a(iblockdata, enumblockmirror); + } + } + + public static BlockStateBoolean getDirection(EnumDirection enumdirection) { + return (BlockStateBoolean) BlockVine.q.get(enumdirection); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } +} diff --git a/src/main/java/net/minecraft/server/BlockWaterLily.java b/src/main/java/net/minecraft/server/BlockWaterLily.java new file mode 100644 index 000000000000..8ec343f578ce --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockWaterLily.java @@ -0,0 +1,28 @@ +package net.minecraft.server; + +public class BlockWaterLily extends BlockPlant { + + protected static final VoxelShape a = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 1.5D, 15.0D); + + protected BlockWaterLily(Block.Info block_info) { + super(block_info); + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) { + super.a(iblockdata, world, blockposition, entity); + if (entity instanceof EntityBoat && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, blockposition, Blocks.AIR.getBlockData()).isCancelled()) { // CraftBukkit + world.setAir(new BlockPosition(blockposition), true); + } + + } + + public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return BlockWaterLily.a; + } + + protected boolean b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + Fluid fluid = iblockaccess.getFluid(blockposition); + + return fluid.c() == FluidTypes.WATER || iblockdata.getMaterial() == Material.ICE; + } +} diff --git a/src/main/java/net/minecraft/server/BlockWitherSkull.java b/src/main/java/net/minecraft/server/BlockWitherSkull.java new file mode 100644 index 000000000000..e6063bb4626e --- /dev/null +++ b/src/main/java/net/minecraft/server/BlockWitherSkull.java @@ -0,0 +1,116 @@ +package net.minecraft.server; + +import java.util.Iterator; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.craftbukkit.util.BlockStateListPopulator; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +// CraftBukkit end + +public class BlockWitherSkull extends BlockSkull { + + private static ShapeDetector c; + private static ShapeDetector o; + + protected BlockWitherSkull(Block.Info block_info) { + super(BlockSkull.Type.WITHER_SKELETON, block_info); + } + + public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, @Nullable EntityLiving entityliving, ItemStack itemstack) { + super.postPlace(world, blockposition, iblockdata, entityliving, itemstack); + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntitySkull) { + a(world, blockposition, (TileEntitySkull) tileentity); + } + + } + + public static void a(World world, BlockPosition blockposition, TileEntitySkull tileentityskull) { + if (world.captureBlockStates) return; // CraftBukkit + Block block = tileentityskull.getBlock().getBlock(); + boolean flag = block == Blocks.WITHER_SKELETON_SKULL || block == Blocks.WITHER_SKELETON_WALL_SKULL; + + if (flag && blockposition.getY() >= 2 && world.getDifficulty() != EnumDifficulty.PEACEFUL && !world.isClientSide) { + ShapeDetector shapedetector = d(); + ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = shapedetector.a(world, blockposition); + + if (shapedetector_shapedetectorcollection != null) { + // CraftBukkit start - Use BlockStateListPopulator + BlockStateListPopulator blockList = new BlockStateListPopulator(world); + int i; + + for (i = 0; i < 3; ++i) { + TileEntitySkull.a(world, shapedetector_shapedetectorcollection.a(i, 0, 0).getPosition()); + } + + for (i = 0; i < shapedetector.c(); ++i) { + for (int j = 0; j < shapedetector.b(); ++j) { + blockList.setTypeAndData(shapedetector_shapedetectorcollection.a(i, j, 0).getPosition(), Blocks.AIR.getBlockData(), 2); // CraftBukkit + } + } + + BlockPosition blockposition1 = shapedetector_shapedetectorcollection.a(1, 0, 0).getPosition(); + EntityWither entitywither = EntityTypes.WITHER.create(world); // Paper + BlockPosition blockposition2 = shapedetector_shapedetectorcollection.a(1, 2, 0).getPosition(); + + entitywither.setPositionRotation((double) blockposition2.getX() + 0.5D, (double) blockposition2.getY() + 0.55D, (double) blockposition2.getZ() + 0.5D, shapedetector_shapedetectorcollection.getFacing().k() == EnumDirection.EnumAxis.X ? 0.0F : 90.0F, 0.0F); + entitywither.aQ = shapedetector_shapedetectorcollection.getFacing().k() == EnumDirection.EnumAxis.X ? 0.0F : 90.0F; + entitywither.l(); + // CraftBukkit start + if (!world.addEntity(entitywither, SpawnReason.BUILD_WITHER)) { + // Restore drop status from above + for (i = 0; i < 3; ++i) { + TileEntitySkull.setShouldDrop(world, shapedetector_shapedetectorcollection.a(i, 0, 0).getPosition(), true); + } + return; + } + blockList.updateList(); + // CraftBukkit end + Iterator iterator = world.a(EntityPlayer.class, entitywither.getBoundingBox().g(50.0D)).iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + CriterionTriggers.n.a(entityplayer, (Entity) entitywither); + } + + // world.addEntity(entitywither); // CraftBukkit - moved up + + int k; + + for (k = 0; k < 120; ++k) { + world.addParticle(Particles.E, (double) blockposition1.getX() + world.random.nextDouble(), (double) (blockposition1.getY() - 2) + world.random.nextDouble() * 3.9D, (double) blockposition1.getZ() + world.random.nextDouble(), 0.0D, 0.0D, 0.0D); + } + + for (k = 0; k < shapedetector.c(); ++k) { + for (int l = 0; l < shapedetector.b(); ++l) { + world.update(shapedetector_shapedetectorcollection.a(k, l, 0).getPosition(), Blocks.AIR); + } + } + + } + } + } + + public static boolean b(World world, BlockPosition blockposition, ItemStack itemstack) { + return itemstack.getItem() == Items.WITHER_SKELETON_SKULL && blockposition.getY() >= 2 && world.getDifficulty() != EnumDifficulty.PEACEFUL && !world.isClientSide ? e().a(world, blockposition) != null : false; + } + + protected static ShapeDetector d() { + if (BlockWitherSkull.c == null) { + BlockWitherSkull.c = ShapeDetectorBuilder.a().a("^^^", "###", "~#~").a('#', ShapeDetectorBlock.a(BlockStatePredicate.a(Blocks.SOUL_SAND))).a('^', ShapeDetectorBlock.a(BlockStatePredicate.a(Blocks.WITHER_SKELETON_SKULL).or(BlockStatePredicate.a(Blocks.WITHER_SKELETON_WALL_SKULL)))).a('~', ShapeDetectorBlock.a(MaterialPredicate.a(Material.AIR))).b(); + } + + return BlockWitherSkull.c; + } + + protected static ShapeDetector e() { + if (BlockWitherSkull.o == null) { + BlockWitherSkull.o = ShapeDetectorBuilder.a().a(" ", "###", "~#~").a('#', ShapeDetectorBlock.a(BlockStatePredicate.a(Blocks.SOUL_SAND))).a('~', ShapeDetectorBlock.a(MaterialPredicate.a(Material.AIR))).b(); + } + + return BlockWitherSkull.o; + } +} diff --git a/src/main/java/net/minecraft/server/BossBattleCustom.java b/src/main/java/net/minecraft/server/BossBattleCustom.java new file mode 100644 index 000000000000..bc460f8ef31f --- /dev/null +++ b/src/main/java/net/minecraft/server/BossBattleCustom.java @@ -0,0 +1,230 @@ +package net.minecraft.server; + +import com.google.common.collect.Sets; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; +import java.util.UUID; + +// CraftBukkit start +import org.bukkit.boss.KeyedBossBar; +import org.bukkit.craftbukkit.boss.CraftKeyedBossbar; +// CraftBukkit end + +public class BossBattleCustom extends BossBattleServer { + + private final MinecraftKey h; + private final Set i = Sets.newHashSet(); + private int j; + private int k = 100; + // CraftBukkit start + private KeyedBossBar bossBar; + + public KeyedBossBar getBukkitEntity() { + if (bossBar == null) { + bossBar = new CraftKeyedBossbar(this); + } + return bossBar; + } + // CraftBukkit end + + public BossBattleCustom(MinecraftKey minecraftkey, IChatBaseComponent ichatbasecomponent) { + super(ichatbasecomponent, BossBattle.BarColor.WHITE, BossBattle.BarStyle.PROGRESS); + this.h = minecraftkey; + this.setProgress(0.0F); + } + + public MinecraftKey getKey() { + return this.h; + } + + public void addPlayer(EntityPlayer entityplayer) { + super.addPlayer(entityplayer); + this.i.add(entityplayer.getUniqueID()); + } + + public void a(UUID uuid) { + this.i.add(uuid); + } + + public void removePlayer(EntityPlayer entityplayer) { + super.removePlayer(entityplayer); + this.i.remove(entityplayer.getUniqueID()); + } + + public void b() { + super.b(); + this.i.clear(); + } + + public int c() { + return this.j; + } + + public int d() { + return this.k; + } + + public void a(int i) { + this.j = i; + this.setProgress(MathHelper.a((float) i / (float) this.k, 0.0F, 1.0F)); + } + + public void b(int i) { + this.k = i; + this.setProgress(MathHelper.a((float) this.j / (float) i, 0.0F, 1.0F)); + } + + public final IChatBaseComponent e() { + return ChatComponentUtils.a(this.j()).a((chatmodifier) -> { + chatmodifier.setColor(this.l().a()).setChatHoverable(new ChatHoverable(ChatHoverable.EnumHoverAction.SHOW_TEXT, new ChatComponentText(this.getKey().toString()))).setInsertion(this.getKey().toString()); + }); + } + + public boolean a(Collection collection) { + Set set = Sets.newHashSet(); + Set set1 = Sets.newHashSet(); + Iterator iterator = this.i.iterator(); + + UUID uuid; + boolean flag; + Iterator iterator1; + + while (iterator.hasNext()) { + uuid = (UUID) iterator.next(); + flag = false; + iterator1 = collection.iterator(); + + while (true) { + if (iterator1.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator1.next(); + + if (!entityplayer.getUniqueID().equals(uuid)) { + continue; + } + + flag = true; + } + + if (!flag) { + set.add(uuid); + } + break; + } + } + + iterator = collection.iterator(); + + EntityPlayer entityplayer1; + + while (iterator.hasNext()) { + entityplayer1 = (EntityPlayer) iterator.next(); + flag = false; + iterator1 = this.i.iterator(); + + while (true) { + if (iterator1.hasNext()) { + UUID uuid1 = (UUID) iterator1.next(); + + if (!entityplayer1.getUniqueID().equals(uuid1)) { + continue; + } + + flag = true; + } + + if (!flag) { + set1.add(entityplayer1); + } + break; + } + } + + iterator = set.iterator(); + + while (iterator.hasNext()) { + uuid = (UUID) iterator.next(); + Iterator iterator2 = this.getPlayers().iterator(); + + while (true) { + if (iterator2.hasNext()) { + EntityPlayer entityplayer2 = (EntityPlayer) iterator2.next(); + + if (!entityplayer2.getUniqueID().equals(uuid)) { + continue; + } + + this.removePlayer(entityplayer2); + } + + this.i.remove(uuid); + break; + } + } + + iterator = set1.iterator(); + + while (iterator.hasNext()) { + entityplayer1 = (EntityPlayer) iterator.next(); + this.addPlayer(entityplayer1); + } + + return !set.isEmpty() || !set1.isEmpty(); + } + + public NBTTagCompound f() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setString("Name", IChatBaseComponent.ChatSerializer.a(this.title)); + nbttagcompound.setBoolean("Visible", this.g()); + nbttagcompound.setInt("Value", this.j); + nbttagcompound.setInt("Max", this.k); + nbttagcompound.setString("Color", this.l().b()); + nbttagcompound.setString("Overlay", this.m().a()); + nbttagcompound.setBoolean("DarkenScreen", this.isDarkenSky()); + nbttagcompound.setBoolean("PlayBossMusic", this.isPlayMusic()); + nbttagcompound.setBoolean("CreateWorldFog", this.isCreateFog()); + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = this.i.iterator(); + + while (iterator.hasNext()) { + UUID uuid = (UUID) iterator.next(); + + nbttaglist.add((NBTBase) GameProfileSerializer.a(uuid)); + } + + nbttagcompound.set("Players", nbttaglist); + return nbttagcompound; + } + + public static BossBattleCustom a(NBTTagCompound nbttagcompound, MinecraftKey minecraftkey) { + BossBattleCustom bossbattlecustom = new BossBattleCustom(minecraftkey, IChatBaseComponent.ChatSerializer.a(nbttagcompound.getString("Name"))); + + bossbattlecustom.setVisible(nbttagcompound.getBoolean("Visible")); + bossbattlecustom.a(nbttagcompound.getInt("Value")); + bossbattlecustom.b(nbttagcompound.getInt("Max")); + bossbattlecustom.a(BossBattle.BarColor.a(nbttagcompound.getString("Color"))); + bossbattlecustom.a(BossBattle.BarStyle.a(nbttagcompound.getString("Overlay"))); + bossbattlecustom.setDarkenSky(nbttagcompound.getBoolean("DarkenScreen")); + bossbattlecustom.setPlayMusic(nbttagcompound.getBoolean("PlayBossMusic")); + bossbattlecustom.setCreateFog(nbttagcompound.getBoolean("CreateWorldFog")); + NBTTagList nbttaglist = nbttagcompound.getList("Players", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + bossbattlecustom.a(GameProfileSerializer.b(nbttaglist.getCompound(i))); + } + + return bossbattlecustom; + } + + public void c(EntityPlayer entityplayer) { + if (this.i.contains(entityplayer.getUniqueID())) { + this.addPlayer(entityplayer); + } + + } + + public void d(EntityPlayer entityplayer) { + super.removePlayer(entityplayer); + } +} diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java new file mode 100644 index 000000000000..2bcbf8902b14 --- /dev/null +++ b/src/main/java/net/minecraft/server/Chunk.java @@ -0,0 +1,1530 @@ +package net.minecraft.server; + +// Paper start +import com.destroystokyo.paper.PaperWorldConfig.DuplicateUUIDMode; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.UUID; +// Paper end +import com.destroystokyo.paper.exception.ServerInternalException; +import com.google.common.collect.Maps; +import com.google.common.collect.Queues; +import com.google.common.collect.Sets; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSet; +import it.unimi.dsi.fastutil.shorts.ShortList; +import it.unimi.dsi.fastutil.shorts.ShortListIterator; +import java.util.BitSet; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import com.google.common.collect.Lists; +import java.util.LinkedList; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.CreatureSpawnEvent; +// CraftBukkit end + +public class Chunk implements IChunkAccess { + + private static final Logger d = LogManager.getLogger(); + public static final ChunkSection a = null; public static final ChunkSection EMPTY_CHUNK_SECTION = Chunk.a; // Paper - OBFHELPER + private final ChunkSection[] sections; + private final BiomeBase[] f; + private final boolean[] g; + private final Map h; + private boolean i;public boolean isLoaded() { return i; } // Paper - OBFHELPER + public final World world; + public final Map heightMap; + public Long scheduledForUnload; // Paper - delay chunk unloads + private static final Logger logger = LogManager.getLogger(); // Paper + public final int locX; + public final int locZ; + private boolean l; + private final ChunkConverter m; + public final Map tileEntities; + public final List[] entitySlices; // Spigot + private final Map p; + private final Map q; + private final ShortList[] r; + private final TickList s; + private final TickList t; + private boolean u; + private boolean v;public boolean hasEntities() { return v; } // Paper - OBFHELPER + private long lastSaved; + private boolean x; public boolean isModified() { return x; } // Paper - OBFHELPER + private int y; + private long z; + private int A; + private final ConcurrentLinkedQueue B; + private ChunkStatus C; + private int D; + private final AtomicInteger E; + private final ChunkCoordIntPair F; + + // CraftBukkit start - Neighbor loaded cache for chunk lighting and entity ticking + private int neighbors = 0x1 << 12; + public long chunkKey; + // Paper start + public final co.aikar.util.Counter entityCounts = new co.aikar.util.Counter<>(); + public final co.aikar.util.Counter tileEntityCounts = new co.aikar.util.Counter<>(); + private class TileEntityHashMap extends java.util.HashMap { + @Override + public TileEntity put(BlockPosition key, TileEntity value) { + TileEntity replaced = super.put(key, value); + if (replaced != null) { + replaced.setCurrentChunk(null); + tileEntityCounts.decrement(replaced.getMinecraftKeyString()); + } + if (value != null) { + value.setCurrentChunk(Chunk.this); + tileEntityCounts.increment(value.getMinecraftKeyString()); + } + return replaced; + } + + @Override + public TileEntity remove(Object key) { + TileEntity removed = super.remove(key); + if (removed != null) { + removed.setCurrentChunk(null); + tileEntityCounts.decrement(removed.getMinecraftKeyString()); + } + return removed; + } + } + final PaperLightingQueue.LightingQueue lightingQueue = new PaperLightingQueue.LightingQueue(this); + // Track the number of minecarts and items + // Keep this synced with entitySlices.add() and entitySlices.remove() + private final int[] itemCounts = new int[16]; + private final int[] inventoryEntityCounts = new int[16]; + // Paper end + public boolean areNeighborsLoaded(final int radius) { + switch (radius) { + case 2: + return this.neighbors == Integer.MAX_VALUE >> 6; + case 1: + final int mask = + // x z offset x z offset x z offset + (0x1 << (1 * 5 + 1 + 12)) | (0x1 << (0 * 5 + 1 + 12)) | (0x1 << (-1 * 5 + 1 + 12)) | + (0x1 << (1 * 5 + 0 + 12)) | (0x1 << (0 * 5 + 0 + 12)) | (0x1 << (-1 * 5 + 0 + 12)) | + (0x1 << (1 * 5 + -1 + 12)) | (0x1 << (0 * 5 + -1 + 12)) | (0x1 << (-1 * 5 + -1 + 12)); + return (this.neighbors & mask) == mask; + default: + throw new UnsupportedOperationException(String.valueOf(radius)); + } + } + + public void setNeighborLoaded(final int x, final int z) { + this.neighbors |= 0x1 << (x * 5 + 12 + z); + } + + public void setNeighborUnloaded(final int x, final int z) { + this.neighbors &= ~(0x1 << (x * 5 + 12 + z)); + } + // CraftBukkit end + + public Chunk(World world, int i, int j, BiomeBase[] abiomebase, ChunkConverter chunkconverter, TickList ticklist, TickList ticklist1, long k) { + this.sections = new ChunkSection[16]; + this.g = new boolean[256]; + this.h = Maps.newHashMap(); + this.heightMap = Maps.newEnumMap(HeightMap.Type.class); + this.tileEntities = new TileEntityHashMap(); // Paper + this.p = Maps.newHashMap(); + this.q = Maps.newHashMap(); + this.r = new ShortList[16]; + this.A = 4096; + this.B = Queues.newConcurrentLinkedQueue(); + this.C = ChunkStatus.EMPTY; + this.E = new AtomicInteger(); + this.entitySlices = (List[]) (new List[16]); // Spigot + this.world = world; + this.locX = i; + this.locZ = j; + this.F = new ChunkCoordIntPair(i, j); + this.m = chunkconverter; + HeightMap.Type[] aheightmap_type = HeightMap.Type.values(); + int l = aheightmap_type.length; + + for (int i1 = 0; i1 < l; ++i1) { + HeightMap.Type heightmap_type = aheightmap_type[i1]; + + if (heightmap_type.c() == HeightMap.Use.LIVE_WORLD) { + this.heightMap.put(heightmap_type, new HeightMap(this, heightmap_type)); + } + } + + for (int j1 = 0; j1 < this.entitySlices.length; ++j1) { + this.entitySlices[j1] = new org.bukkit.craftbukkit.util.UnsafeList(); // Spigot + } + + this.f = abiomebase; + this.s = ticklist; + this.t = ticklist1; + this.z = k; + // CraftBukkit start + this.bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this); + this.chunkKey = ChunkCoordIntPair.a(this.locX, this.locZ); + } + + public org.bukkit.Chunk bukkitChunk; + public boolean mustSave; + private boolean needsDecoration; + // CraftBukkit end + + public Chunk(World world, ProtoChunk protochunk, int i, int j) { + this(world, i, j, protochunk.getBiomeIndex(), protochunk.v(), protochunk.k(), protochunk.l(), protochunk.m()); + + int k; + + for (k = 0; k < this.sections.length; ++k) { + this.sections[k] = protochunk.getSections()[k]; + if (this.sections[k] != null) this.sections[k].disableLocks(); // Paper - Async Chunks - disable locks used during world gen + } + + Iterator iterator = protochunk.s().iterator(); + + while (iterator.hasNext()) { + NBTTagCompound nbttagcompound = (NBTTagCompound) iterator.next(); + + ChunkRegionLoader.a(nbttagcompound, world, this); + } + + iterator = protochunk.r().values().iterator(); + + while (iterator.hasNext()) { + TileEntity tileentity = (TileEntity) iterator.next(); + + this.a(tileentity); + } + + this.h.putAll(protochunk.w()); + + for (k = 0; k < protochunk.u().length; ++k) { + this.r[k] = protochunk.u()[k]; + } + + this.a(protochunk.e()); + this.b(protochunk.f()); + iterator = protochunk.t().iterator(); + + while (iterator.hasNext()) { + HeightMap.Type heightmap_type = (HeightMap.Type) iterator.next(); + + if (heightmap_type.c() == HeightMap.Use.LIVE_WORLD) { + ((HeightMap) this.heightMap.computeIfAbsent(heightmap_type, (heightmap_type1) -> { + return new HeightMap(this, heightmap_type1); + })).a(protochunk.b(heightmap_type).b()); + } + } + + this.x = true; + this.a(ChunkStatus.FULLCHUNK); + this.needsDecoration = true; // CraftBukkit + } + + public Set t() { + Set set = Sets.newHashSet(this.h.keySet()); + + set.addAll(this.tileEntities.keySet()); + return set; + } + + public boolean a(int i, int j) { + return i == this.locX && j == this.locZ; + } + + public ChunkSection[] getSections() { + return this.sections; + } + + public void initLighting() { + int i = this.b(); + + this.y = Integer.MAX_VALUE; + Iterator iterator = this.heightMap.values().iterator(); + + while (iterator.hasNext()) { + HeightMap heightmap = (HeightMap) iterator.next(); + + heightmap.a(); + } + + for (int j = 0; j < 16; ++j) { + for (int k = 0; k < 16; ++k) { + if (this.world.worldProvider.g()) { + int l = 15; + int i1 = i + 16 - 1; + + do { + int j1 = this.d(j, i1, k); + + if (j1 == 0 && l != 15) { + j1 = 1; + } + + l -= j1; + if (l > 0) { + ChunkSection chunksection = this.sections[i1 >> 4]; + + if (chunksection != Chunk.a) { + chunksection.a(j, i1 & 15, k, l); + this.world.m(new BlockPosition((this.locX << 4) + j, i1, (this.locZ << 4) + k)); + } + } + + --i1; + } while (i1 > 0 && l > 0); + } + } + } + + this.x = true; + } + + private void c(int i, int j) { + this.g[i + j * 16] = true; + this.l = true; + } + + private void g(boolean flag) { + this.world.methodProfiler.enter("recheckGaps"); + if (this.areNeighborsLoaded(1)) { // Paper + for (int i = 0; i < 16; ++i) { + for (int j = 0; j < 16; ++j) { + if (this.g[i + j * 16]) { + this.g[i + j * 16] = false; + int k = this.a(HeightMap.Type.LIGHT_BLOCKING, i, j); + int l = this.locX * 16 + i; + int i1 = this.locZ * 16 + j; + int j1 = Integer.MAX_VALUE; + + EnumDirection enumdirection; + Iterator iterator; + + for (iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); iterator.hasNext(); j1 = Math.min(j1, this.world.d(l + enumdirection.getAdjacentX(), i1 + enumdirection.getAdjacentZ()))) { + enumdirection = (EnumDirection) iterator.next(); + } + + this.c(l, i1, j1); + iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + enumdirection = (EnumDirection) iterator.next(); + this.c(l + enumdirection.getAdjacentX(), i1 + enumdirection.getAdjacentZ(), k); + } + + if (flag) { + this.world.methodProfiler.exit(); + return; + } + } + } + } + + this.l = false; + } + + this.world.methodProfiler.exit(); + } + + private void c(int i, int j, int k) { + int l = this.world.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING, new BlockPosition(i, 0, j)).getY(); + + if (l > k) { + this.a(i, j, k, l + 1); + } else if (l < k) { + this.a(i, j, l, k + 1); + } + + } + + private void a(int i, int j, int k, int l) { + if (l > k && this.areNeighborsLoaded(1)) { // Paper + for (int i1 = k; i1 < l; ++i1) { + this.world.updateBrightness(EnumSkyBlock.SKY, new BlockPosition(i, i1, j), this); // Paper + } + + this.x = true; + } + + } + + private void a(int i, int j, int k, IBlockData iblockdata) { + HeightMap heightmap = (HeightMap) this.heightMap.get(HeightMap.Type.LIGHT_BLOCKING); + int l = heightmap.a(i & 15, k & 15) & 255; + + if (heightmap.a(i, j, k, iblockdata)) { + int i1 = heightmap.a(i & 15, k & 15); + int j1 = this.locX * 16 + i; + int k1 = this.locZ * 16 + k; + + this.world.a(j1, k1, i1, l); + int l1; + int i2; + int j2; + + if (this.world.worldProvider.g()) { + l1 = Math.min(l, i1); + i2 = Math.max(l, i1); + j2 = i1 < l ? 15 : 0; + + int k2; + + for (k2 = l1; k2 < i2; ++k2) { + ChunkSection chunksection = this.sections[k2 >> 4]; + + if (chunksection != Chunk.a) { + chunksection.a(i, k2 & 15, k, j2); + this.world.m(new BlockPosition((this.locX << 4) + i, k2, (this.locZ << 4) + k)); + } + } + + k2 = 15; + + while (i1 > 0 && k2 > 0) { + --i1; + int l2 = this.d(i, i1, k); + + l2 = l2 == 0 ? 1 : l2; + k2 -= l2; + k2 = Math.max(0, k2); + ChunkSection chunksection1 = this.sections[i1 >> 4]; + + if (chunksection1 != Chunk.a) { + chunksection1.a(i, i1 & 15, k, k2); + } + } + } + + if (i1 < this.y) { + this.y = i1; + } + + if (this.world.worldProvider.g()) { + l1 = heightmap.a(i & 15, k & 15); + i2 = Math.min(l, l1); + j2 = Math.max(l, l1); + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + + this.a(j1 + enumdirection.getAdjacentX(), k1 + enumdirection.getAdjacentZ(), i2, j2); + } + + this.a(j1, k1, i2, j2); + } + + this.x = true; + } + } + + private int d(int i, int j, int k) { + return this.getBlockData(i, j, k).b(this.world, new BlockPosition(i, j, k)); + } + + // Paper start - Optimize getBlockData to reduce instructions + public final IBlockData getBlockData(BlockPosition pos) { return getBlockData(pos.getX(), pos.getY(), pos.getZ()); } // Paper + public final IBlockData getType(BlockPosition blockposition) { + return this.getBlockData(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + } + + public final IBlockData getBlockData(final int x, final int y, final int z) { + // Method body / logic copied from below + final int i = y >> 4; + if (y >= 0 && i < this.sections.length && this.sections[i] != null) { + // Inlined ChunkSection.getType() and DataPaletteBlock.a(int,int,int) + return this.sections[i].blockIds.a((y & 15) << 8 | (z & 15) << 4 | x & 15); + } + return Blocks.AIR.getBlockData(); + } + + public IBlockData getBlockData_unused(int i, int j, int k) { + // Paper end + if (this.world.S() == WorldType.DEBUG_ALL_BLOCK_STATES) { + IBlockData iblockdata = null; + + if (j == 60) { + iblockdata = Blocks.BARRIER.getBlockData(); + } + + if (j == 70) { + iblockdata = ChunkProviderDebug.b(i, k); + } + + return iblockdata == null ? Blocks.AIR.getBlockData() : iblockdata; + } else { + try { + if (j >= 0 && j >> 4 < this.sections.length) { + ChunkSection chunksection = this.sections[j >> 4]; + + if (chunksection != Chunk.a) { + return chunksection.getType(i & 15, j & 15, k & 15); + } + } + + return Blocks.AIR.getBlockData(); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Getting block state"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Block being got"); + + crashreportsystemdetails.a("Location", () -> { + return CrashReportSystemDetails.a(i, j, k); + }); + throw new ReportedException(crashreport); + } + } + } + + public Fluid getFluid(BlockPosition blockposition) { + return this.b(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + } + + public Fluid b(int i, int j, int k) { + try { + if (j >= 0 && j >> 4 < this.sections.length) { + ChunkSection chunksection = this.sections[j >> 4]; + + if (chunksection != Chunk.a) { + return chunksection.b(i & 15, j & 15, k & 15); + } + } + + return FluidTypes.EMPTY.i(); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Getting fluid state"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Block being got"); + + crashreportsystemdetails.a("Location", () -> { + return CrashReportSystemDetails.a(i, j, k); + }); + throw new ReportedException(crashreport); + } + } + + // CraftBukkit start + @Nullable + public IBlockData setType(BlockPosition blockposition, IBlockData iblockdata, boolean flag) { + return this.setType(blockposition, iblockdata, flag, true); + } + + @Nullable + public IBlockData setType(BlockPosition blockposition, IBlockData iblockdata, boolean flag, boolean doPlace) { + // CraftBukkit end + int i = blockposition.getX() & 15; + int j = blockposition.getY(); + int k = blockposition.getZ() & 15; + int l = ((HeightMap) this.heightMap.get(HeightMap.Type.LIGHT_BLOCKING)).a(i, k); + IBlockData iblockdata1 = this.getType(blockposition); + + if (iblockdata1 == iblockdata) { + return null; + } else { + Block block = iblockdata.getBlock(); + Block block1 = iblockdata1.getBlock(); + ChunkSection chunksection = this.sections[j >> 4]; + boolean flag1 = false; + + if (chunksection == Chunk.a) { + if (iblockdata.isAir()) { + return null; + } + + chunksection = new ChunkSection(j >> 4 << 4, this.world.worldProvider.g(), this, this.world, true); // Paper - Anti-Xray + this.sections[j >> 4] = chunksection; + flag1 = j >= l; + } + + chunksection.setType(i, j & 15, k, iblockdata); + ((HeightMap) this.heightMap.get(HeightMap.Type.MOTION_BLOCKING)).a(i, j, k, iblockdata); + ((HeightMap) this.heightMap.get(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES)).a(i, j, k, iblockdata); + ((HeightMap) this.heightMap.get(HeightMap.Type.OCEAN_FLOOR)).a(i, j, k, iblockdata); + ((HeightMap) this.heightMap.get(HeightMap.Type.WORLD_SURFACE)).a(i, j, k, iblockdata); + if (!this.world.isClientSide) { + iblockdata1.remove(this.world, blockposition, iblockdata, flag); + } else if (block1 != block && block1 instanceof ITileEntity) { + this.world.n(blockposition); + } + + if (false && chunksection.getType(i, j & 15, k).getBlock() != block) { // Paper - don't need to recheck this - this would only fail due to non main thread writes which are not supported + return null; + } else { + if (flag1) { + this.initLighting(); + } else if (!world.paperConfig.optimizeLight || block != block1) { // Paper - Optimize light recalculations + this.runOrQueueLightUpdate(() -> { // Paper - Queue light update + int i1 = iblockdata.b(this.world, blockposition); + int j1 = iblockdata1.b(this.world, blockposition); + + this.a(i, j, k, iblockdata); + if (i1 != j1 && (i1 < j1 || this.getBrightness(EnumSkyBlock.SKY, blockposition) > 0 || this.getBrightness(EnumSkyBlock.BLOCK, blockposition) > 0)) { + this.c(i, k); + } + }); // Paper + } + + TileEntity tileentity; + + if (block1 instanceof ITileEntity) { + tileentity = this.a(blockposition, Chunk.EnumTileEntityState.CHECK); + if (tileentity != null) { + tileentity.invalidateBlockCache(); + } + } + + // CraftBukkit - Don't place while processing the BlockPlaceEvent, unless it's a BlockContainer. Prevents blocks such as TNT from activating when cancelled. + if (!this.world.isClientSide && doPlace && (!this.world.captureBlockStates || block instanceof BlockTileEntity)) { + iblockdata.onPlace(this.world, blockposition, iblockdata1); + } + + if (block instanceof ITileEntity) { + tileentity = this.a(blockposition, Chunk.EnumTileEntityState.CHECK); + if (tileentity == null) { + tileentity = ((ITileEntity) block).a(this.world); + this.world.setTileEntity(blockposition, tileentity); + } else { + tileentity.invalidateBlockCache(); + } + } + + this.x = true; + return iblockdata1; + } + } + } + + public int getBrightness(EnumSkyBlock enumskyblock, BlockPosition blockposition) { + return this.a(enumskyblock, blockposition, this.world.o().g()); + } + + public int a(EnumSkyBlock enumskyblock, BlockPosition blockposition, boolean flag) { + int i = blockposition.getX() & 15; + int j = blockposition.getY(); + int k = blockposition.getZ() & 15; + int l = j >> 4; + + if (l >= 0 && l <= this.sections.length - 1) { + ChunkSection chunksection = this.sections[l]; + + return chunksection == Chunk.a ? (this.c(blockposition) ? enumskyblock.c : 0) : (enumskyblock == EnumSkyBlock.SKY ? (!flag ? 0 : chunksection.c(i, j & 15, k)) : (enumskyblock == EnumSkyBlock.BLOCK ? chunksection.d(i, j & 15, k) : enumskyblock.c)); + } else { + return (enumskyblock != EnumSkyBlock.SKY || !flag) && enumskyblock != EnumSkyBlock.BLOCK ? 0 : enumskyblock.c; + } + } + + public void a(EnumSkyBlock enumskyblock, BlockPosition blockposition, int i) { + this.a(enumskyblock, this.world.o().g(), blockposition, i); + } + + public void a(EnumSkyBlock enumskyblock, boolean flag, BlockPosition blockposition, int i) { + int j = blockposition.getX() & 15; + int k = blockposition.getY(); + int l = blockposition.getZ() & 15; + int i1 = k >> 4; + + if (i1 < 16 && i1 >= 0) { + ChunkSection chunksection = this.sections[i1]; + + if (chunksection == Chunk.a) { + if (i == enumskyblock.c) { + return; + } + + chunksection = new ChunkSection(i1 << 4, flag, this, this.world, true); // Paper - Anti-Xray + this.sections[i1] = chunksection; + this.initLighting(); + } + + if (enumskyblock == EnumSkyBlock.SKY) { + if (this.world.worldProvider.g()) { + chunksection.a(j, k & 15, l, i); + } + } else if (enumskyblock == EnumSkyBlock.BLOCK) { + chunksection.b(j, k & 15, l, i); + } + + this.x = true; + } + } + + public final int getLightSubtracted(BlockPosition blockposition, int i) { return this.a(blockposition, i); } // Paper - OBFHELPER + public int a(BlockPosition blockposition, int i) { + return this.a(blockposition, i, this.world.o().g()); + } + + public int a(BlockPosition blockposition, int i, boolean flag) { + int j = blockposition.getX() & 15; + int k = blockposition.getY(); + int l = blockposition.getZ() & 15; + int i1 = k >> 4; + + if (i1 >= 0 && i1 <= this.sections.length - 1) { + ChunkSection chunksection = this.sections[i1]; + + if (chunksection == Chunk.a) { + return flag && i < EnumSkyBlock.SKY.c ? EnumSkyBlock.SKY.c - i : 0; + } else { + int j1 = flag ? chunksection.c(j, k & 15, l) : 0; + + j1 -= i; + int k1 = chunksection.d(j, k & 15, l); + + if (k1 > j1) { + j1 = k1; + } + + return j1; + } + } else { + return 0; + } + } + + public void a(Entity entity) { + this.v = true; + int i = MathHelper.floor(entity.locX / 16.0D); + int j = MathHelper.floor(entity.locZ / 16.0D); + + if (i != this.locX || j != this.locZ) { + Chunk.d.warn("Wrong location! ({}, {}) should be ({}, {}), {}", i, j, this.locX, this.locZ, entity); + entity.die(); + return; // Paper + } + + int k = MathHelper.floor(entity.locY / 16.0D); + + if (k < 0) { + k = 0; + } + + if (k >= this.entitySlices.length) { + k = this.entitySlices.length - 1; + } + + entity.inChunk = true; + entity.chunkX = this.locX; + entity.chunkY = k; + entity.chunkZ = this.locZ; + + // Paper start + List entitySlice = this.entitySlices[k]; + boolean inThis = entitySlice.contains(entity); + List currentSlice = entity.entitySlice; + if (inThis || (currentSlice != null && currentSlice.contains(entity))) { + if (currentSlice == entitySlice || inThis) { + return; + } else { + Chunk chunk = entity.getCurrentChunk(); + if (chunk != null) { + chunk.removeEntity(entity); + } else { + removeEntity(entity); + } + currentSlice.remove(entity); // Just incase the above did not remove from this target slice + } + } + entity.entitySlice = entitySlice; + entitySlice.add(entity); + + this.markDirty(); + if (entity instanceof EntityItem) { + itemCounts[k]++; + } else if (entity instanceof IInventory) { + inventoryEntityCounts[k]++; + } + entity.setCurrentChunk(this); + entityCounts.increment(entity.getMinecraftKeyString()); + // Paper end + } + + public void a(HeightMap.Type heightmap_type, long[] along) { + ((HeightMap) this.heightMap.get(heightmap_type)).a(along); + } + + public void removeEntity(Entity entity) { b(entity); } // Paper - OBFHELPER + public void b(Entity entity) { + this.a(entity, entity.chunkY); + } + + public void a(Entity entity, int i) { + if (i < 0) { + i = 0; + } + + if (i >= this.entitySlices.length) { + i = this.entitySlices.length - 1; + } + // Paper start + if (entity.entitySlice == null || !entity.entitySlice.contains(entity) || entitySlices[i] == entity.entitySlice) { + entity.entitySlice = null; + } + if (!this.entitySlices[i].remove(entity)) { return; } + this.markDirty(); + if (entity instanceof EntityItem) { + itemCounts[i]--; + } else if (entity instanceof IInventory) { + inventoryEntityCounts[i]--; + } + entity.setCurrentChunk(null); + entityCounts.decrement(entity.getMinecraftKeyString()); + // Paper end + } + + public boolean c(BlockPosition blockposition) { + int i = blockposition.getX() & 15; + int j = blockposition.getY(); + int k = blockposition.getZ() & 15; + + return j >= ((HeightMap) this.heightMap.get(HeightMap.Type.LIGHT_BLOCKING)).a(i, k); + } + + public int a(HeightMap.Type heightmap_type, int i, int j) { + return ((HeightMap) this.heightMap.get(heightmap_type)).a(i & 15, j & 15) - 1; + } + + @Nullable + private TileEntity j(BlockPosition blockposition) { + IBlockData iblockdata = this.getType(blockposition); + Block block = iblockdata.getBlock(); + + return !block.isTileEntity() ? null : ((ITileEntity) block).a(this.world); + } + + @Nullable + public TileEntity getTileEntity(BlockPosition blockposition) { + return this.a(blockposition, Chunk.EnumTileEntityState.CHECK); + } + + @Nullable public final TileEntity getTileEntityImmediately(BlockPosition pos) { return this.a(pos, EnumTileEntityState.IMMEDIATE); } // Paper - OBFHELPER + @Nullable + public TileEntity a(BlockPosition blockposition, Chunk.EnumTileEntityState chunk_enumtileentitystate) { + // CraftBukkit start + TileEntity tileentity = world.capturedTileEntities.get(blockposition); + if (tileentity == null) { + tileentity = (TileEntity) this.tileEntities.get(blockposition); + } + // CraftBukkit end + + if (tileentity == null) { + NBTTagCompound nbttagcompound = (NBTTagCompound) this.h.remove(blockposition); + + if (nbttagcompound != null) { + TileEntity tileentity1 = this.a(blockposition, nbttagcompound); + + if (tileentity1 != null) { + return tileentity1; + } + } + } + + if (tileentity == null) { + if (chunk_enumtileentitystate == Chunk.EnumTileEntityState.IMMEDIATE) { + tileentity = this.j(blockposition); + this.world.setTileEntity(blockposition, tileentity); + } else if (chunk_enumtileentitystate == Chunk.EnumTileEntityState.QUEUED) { + this.B.add(blockposition); + } + } else if (tileentity.x()) { + this.tileEntities.remove(blockposition); + return null; + } + + return tileentity; + } + + public void a(TileEntity tileentity) { + this.a(tileentity.getPosition(), tileentity); + if (this.i) { + this.world.a(tileentity); + } + + } + + public void a(BlockPosition blockposition, TileEntity tileentity) { + tileentity.setWorld(this.world); + tileentity.setPosition(blockposition); + if (this.getType(blockposition).getBlock() instanceof ITileEntity) { + if (this.tileEntities.containsKey(blockposition)) { + ((TileEntity) this.tileEntities.get(blockposition)).y(); + } + + tileentity.z(); + this.tileEntities.put(blockposition.h(), tileentity); + // CraftBukkit start + // Paper start - Remove invalid mob spawner tile entities + } else if (tileentity instanceof TileEntityMobSpawner && !(getBlockData(blockposition.getX(), blockposition.getY(), blockposition.getZ()).getBlock() instanceof BlockMobSpawner)) { + this.tileEntities.remove(blockposition); + // Paper end + } else { + // Paper start + ServerInternalException e = new ServerInternalException( + "Attempted to place a tile entity (" + tileentity + ") at " + tileentity.position.getX() + "," + + tileentity.position.getY() + "," + tileentity.position.getZ() + + " (" + getBlockData(blockposition) + ") where there was no entity tile!\n" + + "Chunk coordinates: " + (this.locX * 16) + "," + (this.locZ * 16)); + e.printStackTrace(); + ServerInternalException.reportInternalException(e); + + if (this.world.paperConfig.removeCorruptTEs) { + this.removeTileEntity(tileentity.getPosition()); + this.markDirty(); + org.bukkit.Bukkit.getLogger().info("Removing corrupt tile entity"); + } + // Paper end + // CraftBukkit end + } + } + + public void a(NBTTagCompound nbttagcompound) { + this.h.put(new BlockPosition(nbttagcompound.getInt("x"), nbttagcompound.getInt("y"), nbttagcompound.getInt("z")), nbttagcompound); + } + + public void removeTileEntity(BlockPosition blockposition) { this.d(blockposition); } // Paper - OBFHELPER + public void d(BlockPosition blockposition) { + if (this.i) { + TileEntity tileentity = (TileEntity) this.tileEntities.remove(blockposition); + + if (tileentity != null) { + tileentity.y(); + } + } + + } + + public void addEntities() { + this.i = true; + this.world.a(this.tileEntities.values()); + List[] aentityslice = this.entitySlices; // Spigot + int i = aentityslice.length; + + for (int j = 0; j < i; ++j) { + List entityslice = aentityslice[j]; // Spigot + // Paper start + DuplicateUUIDMode mode = world.paperConfig.duplicateUUIDMode; + if (mode == DuplicateUUIDMode.WARN || mode == DuplicateUUIDMode.DELETE || mode == DuplicateUUIDMode.SAFE_REGEN) { + Map thisChunk = new HashMap<>(); + for (Iterator iterator = ((List) entityslice).iterator(); iterator.hasNext(); ) { + Entity entity = iterator.next(); + if (entity.dead || entity.valid) continue; + Entity other = ((WorldServer) world).entitiesByUUID.get(entity.uniqueID); + if (other == null || other.dead || world.getEntityUnloadQueue().contains(other)) { + other = thisChunk.get(entity.uniqueID); + } + + if (mode == DuplicateUUIDMode.SAFE_REGEN && other != null && !other.dead && + !world.getEntityUnloadQueue().contains(other) + && java.util.Objects.equals(other.getSaveID(), entity.getSaveID()) + && entity.getBukkitEntity().getLocation().distance(other.getBukkitEntity().getLocation()) < world.paperConfig.duplicateUUIDDeleteRange + ) { + if (World.DEBUG_ENTITIES) logger.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + " because it was near the duplicate and likely an actual duplicate. See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about."); + entity.die(); + iterator.remove(); + continue; + } + if (other != null && !other.dead) { + switch (mode) { + case SAFE_REGEN: { + entity.setUUID(UUID.randomUUID()); + if (World.DEBUG_ENTITIES) logger.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", regenerated UUID for " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about."); + break; + } + case DELETE: { + if (World.DEBUG_ENTITIES) logger.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about."); + entity.die(); + iterator.remove(); + break; + } + default: + if (World.DEBUG_ENTITIES) logger.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", doing nothing to " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about."); + break; + } + } + thisChunk.put(entity.uniqueID, entity); + } + } + // Paper end + + // CraftBukkit start + this.world.addChunkEntities(entityslice.stream() // Paper - add all at same time to avoid entities adding to world modifying slice state, skip already added entities (not normal, but can happen) + // Paper start - Inline event into stream + .filter((entity) -> { + if (!this.needsDecoration) { + return true; + } + return CraftEventFactory.doEntityAddEventCalling(this.world, entity, CreatureSpawnEvent.SpawnReason.CHUNK_GEN); + }) + // Paper end - Inline event into stream + .filter((entity) -> !(entity instanceof EntityHuman || entity.valid))); // Paper - add all at same time to avoid entities adding to world modifying slice state, skip already added entities (not normal, but can happen) + // CraftBukkit end + } + + // CraftBukkit start + org.bukkit.Server server = this.world.getServer(); + if (server != null) { + /* + * If it's a new world, the first few chunks are generated inside + * the World constructor. We can't reliably alter that, so we have + * no way of creating a CraftWorld/CraftServer at that point. + */ + server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(this.bukkitChunk, this.needsDecoration)); + + if (this.needsDecoration) { + this.world.timings.syncChunkLoadPopulateTimer.startTiming(); // Paper + BlockSand.instaFall = true; + java.util.Random random = new java.util.Random(); + random.setSeed(world.getSeed()); + long xRand = random.nextLong() / 2L * 2L + 1L; + long zRand = random.nextLong() / 2L * 2L + 1L; + random.setSeed((long) locX * xRand + (long) locZ * zRand ^ world.getSeed()); + + org.bukkit.World world = this.world.getWorld(); + if (world != null) { + this.world.populating = true; + try { + for (org.bukkit.generator.BlockPopulator populator : world.getPopulators()) { + populator.populate(world, random, bukkitChunk); + } + } finally { + this.world.populating = false; + } + } + BlockSand.instaFall = false; + server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkPopulateEvent(bukkitChunk)); + this.world.timings.syncChunkLoadPopulateTimer.stopTiming(); // Paper + } + } + // CraftBukkit end + } + + public void removeEntities() { + this.i = false; + Iterator iterator = this.tileEntities.values().iterator(); + + while (iterator.hasNext()) { + TileEntity tileentity = (TileEntity) iterator.next(); + // Spigot Start + if ( tileentity instanceof IInventory ) + { + for ( org.bukkit.entity.HumanEntity h : Lists.newArrayList((List) ( (IInventory) tileentity ).getViewers() ) ) + { + if ( h instanceof org.bukkit.craftbukkit.entity.CraftHumanEntity ) + { + ( (org.bukkit.craftbukkit.entity.CraftHumanEntity) h).getHandle().closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); // Paper + } + } + } + // Spigot End + + this.world.b(tileentity); + } + + List[] aentityslice = this.entitySlices; // Spigot + int i = aentityslice.length; + + for (int j = 0; j < i; ++j) { + // CraftBukkit start + List newList = Lists.newArrayList(aentityslice[j]); + java.util.Iterator iter = newList.iterator(); + while (iter.hasNext()) { + Entity entity = iter.next(); + // Spigot Start + if ( entity instanceof IInventory ) + { + for ( org.bukkit.entity.HumanEntity h : Lists.newArrayList( (List) ( (IInventory) entity ).getViewers() ) ) + { + if ( h instanceof org.bukkit.craftbukkit.entity.CraftHumanEntity ) + { + ( (org.bukkit.craftbukkit.entity.CraftHumanEntity) h).getHandle().closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); // Paper + } + } + } + // Spigot End + entity.setCurrentChunk(null); // Paper + entity.entitySlice = null; // Paper + + // Do not pass along players, as doing so can get them stuck outside of time. + // (which for example disables inventory icon updates and prevents block breaking) + if (entity instanceof EntityPlayer) { + iter.remove(); + } + } + + this.world.b((Collection) newList); + // CraftBukkit end + } + + } + + public void markDirty() { + this.x = true; + } + + public void a(@Nullable Entity entity, AxisAlignedBB axisalignedbb, List list, Predicate predicate) { + int i = MathHelper.floor((axisalignedbb.minY - 2.0D) / 16.0D); + int j = MathHelper.floor((axisalignedbb.maxY + 2.0D) / 16.0D); + + i = MathHelper.clamp(i, 0, this.entitySlices.length - 1); + j = MathHelper.clamp(j, 0, this.entitySlices.length - 1); + + for (int k = i; k <= j; ++k) { + if (!this.entitySlices[k].isEmpty()) { + Iterator iterator = this.entitySlices[k].iterator(); + + // Paper start - Don't search for inventories if we have none, and that is all we want + /* + * We check if they want inventories by seeing if it is the static `IEntitySelector.c` + * + * Make sure the inventory selector stays in sync. + * It should be the one that checks `var1 instanceof IInventory && var1.isAlive()` + */ + if (predicate == IEntitySelector.isInventory() && inventoryEntityCounts[k] <= 0) continue; + // Paper end + while (iterator.hasNext()) { + Entity entity1 = (Entity) iterator.next(); + + if (entity1.getBoundingBox().c(axisalignedbb) && entity1 != entity) { + if (predicate == null || predicate.test(entity1)) { + list.add(entity1); + } + + Entity[] aentity = entity1.bi(); + + if (aentity != null) { + Entity[] aentity1 = aentity; + int l = aentity.length; + + for (int i1 = 0; i1 < l; ++i1) { + Entity entity2 = aentity1[i1]; + + if (entity2 != entity && entity2.getBoundingBox().c(axisalignedbb) && (predicate == null || predicate.test(entity2))) { + list.add(entity2); + } + } + } + } + } + } + } + + } + + public void a(Class oclass, AxisAlignedBB axisalignedbb, List list, @Nullable Predicate predicate) { + int i = MathHelper.floor((axisalignedbb.minY - 2.0D) / 16.0D); + int j = MathHelper.floor((axisalignedbb.maxY + 2.0D) / 16.0D); + + i = MathHelper.clamp(i, 0, this.entitySlices.length - 1); + j = MathHelper.clamp(j, 0, this.entitySlices.length - 1); + + // Paper start + int[] counts; + if (EntityItem.class.isAssignableFrom(oclass)) { + counts = itemCounts; + } else if (IInventory.class.isAssignableFrom(oclass)) { + counts = inventoryEntityCounts; + } else { + counts = null; + } + // Paper end + for (int k = i; k <= j; ++k) { + if (counts != null && counts[k] <= 0) continue; // Paper - Don't check a chunk if it doesn't have the type we are looking for + Iterator iterator = this.entitySlices[k].iterator(); // Spigot + + while (iterator.hasNext()) { + T t0 = (T) iterator.next(); // CraftBukkit - decompile error + + if (oclass.isInstance(t0) && t0.getBoundingBox().c(axisalignedbb) && (predicate == null || predicate.test(t0))) { // Spigot - instance check + list.add(t0); + } + } + } + + } + + public boolean c(boolean flag) { + if (flag) { + if (this.v && this.world.getTime() != this.lastSaved || this.x) { + return true; + } + } + // Paper start - Make world configurable and incremental + // This !flag section should say if isModified or hasEntities, then check auto save + return ((isModified() || hasEntities()) && this.world.getTime() >= this.lastSaved + world.paperConfig.autoSavePeriod); + // Paper end + } + + public boolean isEmpty() { + return false; + } + + public void d(boolean flag) { + if (this.l && this.world.worldProvider.g() && !flag) { + this.g(this.world.isClientSide); + } + + this.u = true; + + while (!this.B.isEmpty()) { + BlockPosition blockposition = (BlockPosition) this.B.poll(); + + if (this.a(blockposition, Chunk.EnumTileEntityState.CHECK) == null && this.getType(blockposition).getBlock().isTileEntity()) { + TileEntity tileentity = this.j(blockposition); + + this.world.setTileEntity(blockposition, tileentity); + this.world.a(blockposition, blockposition); + } + } + + } + + public boolean isReady() { + return true; // Paper - Always send chunks + } + + public boolean v() { + return this.u; + } + + public ChunkCoordIntPair getPos() { + return this.F; + } + + public boolean b(int i, int j) { + if (i < 0) { + i = 0; + } + + if (j >= 256) { + j = 255; + } + + for (int k = i; k <= j; k += 16) { + ChunkSection chunksection = this.sections[k >> 4]; + + if (chunksection != Chunk.a && !chunksection.a()) { + return false; + } + } + + return true; + } + + public void a(ChunkSection[] achunksection) { + if (this.sections.length != achunksection.length) { + Chunk.d.warn("Could not set level chunk sections, array length is {} instead of {}", achunksection.length, this.sections.length); + } else { + System.arraycopy(achunksection, 0, this.sections, 0, this.sections.length); + } + } + + public BiomeBase getBiome(BlockPosition blockposition) { + int i = blockposition.getX() & 15; + int j = blockposition.getZ() & 15; + + return this.f[j << 4 | i]; + } + + public BiomeBase[] getBiomeIndex() { + return this.f; + } + + public void x() { + if (this.A < 4096) { + BlockPosition blockposition = new BlockPosition(this.locX << 4, 0, this.locZ << 4); + + for (int i = 0; i < 8; ++i) { + if (this.A >= 4096) { + return; + } + + int j = this.A % 16; + int k = this.A / 16 % 16; + int l = this.A / 256; + + ++this.A; + + for (int i1 = 0; i1 < 16; ++i1) { + BlockPosition blockposition1 = blockposition.a(k, (j << 4) + i1, l); + boolean flag = i1 == 0 || i1 == 15 || k == 0 || k == 15 || l == 0 || l == 15; + + if (this.sections[j] == Chunk.a && flag || this.sections[j] != Chunk.a && this.sections[j].getType(k, i1, l).isAir()) { + EnumDirection[] aenumdirection = EnumDirection.values(); + int j1 = aenumdirection.length; + + for (int k1 = 0; k1 < j1; ++k1) { + EnumDirection enumdirection = aenumdirection[k1]; + BlockPosition blockposition2 = blockposition1.shift(enumdirection); + + if (this.world.getType(blockposition2).e() > 0) { + this.world.r(blockposition2); + } + } + + this.world.r(blockposition1); + } + } + } + + } + } + + public boolean y() { + return this.i; + } + + public World getWorld() { + return this.world; + } + + public Set A() { + return this.heightMap.keySet(); + } + + public HeightMap b(HeightMap.Type heightmap_type) { + return (HeightMap) this.heightMap.get(heightmap_type); + } + + public Map getTileEntities() { + return this.tileEntities; + } + + public List[] getEntitySlices() { // Spigot + return this.entitySlices; + } + + public NBTTagCompound g(BlockPosition blockposition) { + return (NBTTagCompound) this.h.get(blockposition); + } + + public TickList k() { + return this.s; + } + + public TickList l() { + return this.t; + } + + public BitSet a(WorldGenStage.Features worldgenstage_features) { + throw new RuntimeException("Not yet implemented"); + } + + public void a(boolean flag) { + this.x = flag; + } + + public void f(boolean flag) { + this.v = flag; + } + + public void setLastSaved(long i) { + this.lastSaved = i; + } + + @Nullable + public StructureStart a(String s) { + return (StructureStart) this.p.get(s); + } + + public void a(String s, StructureStart structurestart) { + this.p.put(s, structurestart); + } + + public Map e() { + return this.p; + } + + public void a(Map map) { + this.p.clear(); + this.p.putAll(map); + } + + @Nullable + public LongSet b(String s) { + return (LongSet) this.q.computeIfAbsent(s, (s1) -> { + return new LongOpenHashSet(); + }); + } + + public void a(String s, long i) { + ((LongSet) this.q.computeIfAbsent(s, (s1) -> { + return new LongOpenHashSet(); + })).add(i); + } + + public Map f() { + return this.q; + } + + public void b(Map map) { + this.q.clear(); + this.q.putAll(map); + } + + public int D() { + return this.y; + } + + public long m() { + return world.paperConfig.fixedInhabitedTime < 0 ? this.z : world.paperConfig.fixedInhabitedTime; // Paper + } + + public void b(long i) { + this.z = i; + } + + public void E() { + if (!this.C.a(ChunkStatus.POSTPROCESSED) && this.D == 8) { + ChunkCoordIntPair chunkcoordintpair = this.getPos(); + + for (int i = 0; i < this.r.length; ++i) { + if (this.r[i] != null) { + ShortListIterator shortlistiterator = this.r[i].iterator(); + + while (shortlistiterator.hasNext()) { + Short oshort = (Short) shortlistiterator.next(); + BlockPosition blockposition = ProtoChunk.a(oshort, i, chunkcoordintpair); + IBlockData iblockdata = this.world.getType(blockposition); + IBlockData iblockdata1 = Block.b(iblockdata, this.world, blockposition); + + this.world.setTypeAndData(blockposition, iblockdata1, 20); + } + + this.r[i].clear(); + } + } + + if (this.s instanceof ProtoChunkTickList) { + ((ProtoChunkTickList) this.s).a(this.world.getBlockTickList(), (blockposition1) -> { // CraftBukkit - decompile error + return this.world.getType(blockposition1).getBlock(); + }); + } + + if (this.t instanceof ProtoChunkTickList) { + ((ProtoChunkTickList) this.t).a(this.world.getFluidTickList(), (blockposition1) -> { // CraftBukkit - decompile error + return this.world.getFluid(blockposition1).c(); + }); + } + + Iterator iterator = (new HashSet(this.h.keySet())).iterator(); + + while (iterator.hasNext()) { + BlockPosition blockposition1 = (BlockPosition) iterator.next(); + + this.getTileEntity(blockposition1); + } + + this.h.clear(); + this.a(ChunkStatus.POSTPROCESSED); + this.m.a(this); + // Paper start - resend chunk after post process + PlayerChunk playerChunk = ((WorldServer) world).getPlayerChunkMap().getChunk(locX, locZ); + if (playerChunk != null) { + playerChunk.done = false; + playerChunk.sendAll(); + } + // Paper end + } + } + + @Nullable + private TileEntity a(BlockPosition blockposition, NBTTagCompound nbttagcompound) { + TileEntity tileentity; + + if ("DUMMY".equals(nbttagcompound.getString("id"))) { + Block block = this.getType(blockposition).getBlock(); + + if (block instanceof ITileEntity) { + tileentity = ((ITileEntity) block).a(this.world); + } else { + tileentity = null; + Chunk.d.warn("Tried to load a DUMMY block entity @ {} but found not block entity block {} at location", blockposition, this.getType(blockposition)); + } + } else { + tileentity = TileEntity.create(nbttagcompound); + } + + if (tileentity != null) { + tileentity.setPosition(blockposition); + this.a(tileentity); + } else { + Chunk.d.warn("Tried to load a block entity for block {} but failed at location {}", this.getType(blockposition), blockposition); + } + + return tileentity; + } + + public ChunkConverter F() { + return this.m; + } + + public ShortList[] G() { + return this.r; + } + + public void a(short short0, int i) { + ProtoChunk.a(this.r, i).add(short0); + } + + public ChunkStatus i() { + return this.C; + } + + public void a(ChunkStatus chunkstatus) { + this.C = chunkstatus; + } + + public void c(String s) { + this.a(ChunkStatus.a(s)); + } + + public void H() { + ++this.D; + if (this.D > 8) { + throw new RuntimeException("Error while adding chunk to cache. Too many neighbors"); + } else { + if (this.J()) { + ((IAsyncTaskHandler) this.world).postToMainThread(this::E); + } + + } + } + + public void I() { + --this.D; + if (this.D < 0) { + throw new RuntimeException("Error while removing chunk from cache. Not enough neighbors"); + } + } + + public boolean J() { + return this.D == 8; + } + + // Paper start + public void runOrQueueLightUpdate(Runnable runnable) { + if (this.world.paperConfig.queueLightUpdates) { + lightingQueue.add(runnable); + } else { + runnable.run(); + } + } + // Paper end + + public static enum EnumTileEntityState { + + IMMEDIATE, QUEUED, CHECK; + + private EnumTileEntityState() {} + } +} diff --git a/src/main/java/net/minecraft/server/ChunkCache.java b/src/main/java/net/minecraft/server/ChunkCache.java new file mode 100644 index 000000000000..eef3ab73fc0d --- /dev/null +++ b/src/main/java/net/minecraft/server/ChunkCache.java @@ -0,0 +1,220 @@ +package net.minecraft.server; + +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public class ChunkCache implements IIBlockAccess { + + protected int a; + protected int b; + protected Chunk[][] c; + protected boolean d; + protected World e; + + public ChunkCache(World world, BlockPosition blockposition, BlockPosition blockposition1, int i) { + this.e = world; + this.a = blockposition.getX() - i >> 4; + this.b = blockposition.getZ() - i >> 4; + int j = blockposition1.getX() + i >> 4; + int k = blockposition1.getZ() + i >> 4; + + this.c = new Chunk[j - this.a + 1][k - this.b + 1]; + this.d = true; + + int l; + int i1; + + for (l = this.a; l <= j; ++l) { + for (i1 = this.b; i1 <= k; ++i1) { + this.c[l - this.a][i1 - this.b] = world.getChunkIfLoaded(l, i1); // Paper + } + } + + for (l = blockposition.getX() >> 4; l <= blockposition1.getX() >> 4; ++l) { + for (i1 = blockposition.getZ() >> 4; i1 <= blockposition1.getZ() >> 4; ++i1) { + Chunk chunk = this.c[l - this.a][i1 - this.b]; + + if (chunk != null && !chunk.b(blockposition.getY(), blockposition1.getY())) { + this.d = false; + } + } + } + + } + + @Nullable + public TileEntity getTileEntity(BlockPosition blockposition) { + return this.a(blockposition, Chunk.EnumTileEntityState.IMMEDIATE); + } + + @Nullable + public TileEntity a(BlockPosition blockposition, Chunk.EnumTileEntityState chunk_enumtileentitystate) { + int i = (blockposition.getX() >> 4) - this.a; + int j = (blockposition.getZ() >> 4) - this.b; + + return this.c[i][j].a(blockposition, chunk_enumtileentitystate); + } + + public float A(BlockPosition blockposition) { + return this.e.worldProvider.i()[this.getLightLevel(blockposition)]; + } + + public int d(BlockPosition blockposition, int i) { + if (this.getType(blockposition).c(this, blockposition)) { + int j = 0; + EnumDirection[] aenumdirection = EnumDirection.values(); + int k = aenumdirection.length; + + for (int l = 0; l < k; ++l) { + EnumDirection enumdirection = aenumdirection[l]; + int i1 = this.getLightLevel(blockposition.shift(enumdirection), i); + + if (i1 > j) { + j = i1; + } + + if (j >= 15) { + return j; + } + } + + return j; + } else { + return this.getLightLevel(blockposition, i); + } + } + + public WorldProvider o() { + return this.e.o(); + } + + public int getLightLevel(BlockPosition blockposition, int i) { + if (blockposition.getX() >= -30000000 && blockposition.getZ() >= -30000000 && blockposition.getX() < 30000000 && blockposition.getZ() <= 30000000) { + if (blockposition.getY() < 0) { + return 0; + } else { + int j; + + if (blockposition.getY() >= 256) { + j = 15 - i; + if (j < 0) { + j = 0; + } + + return j; + } else { + j = (blockposition.getX() >> 4) - this.a; + int k = (blockposition.getZ() >> 4) - this.b; + + return this.c[j][k].a(blockposition, i); + } + } + } else { + return 15; + } + } + + public boolean isChunkLoaded(int i, int j, boolean flag) { + return this.a(i, j); + } + + public boolean e(BlockPosition blockposition) { + return false; + } + + public boolean a(int i, int j) { + int k = i - this.a; + int l = j - this.b; + + return k >= 0 && k < this.c.length && l >= 0 && l < this.c[k].length; + } + + public int a(HeightMap.Type heightmap_type, int i, int j) { + throw new RuntimeException("NOT IMPLEMENTED!"); + } + + public WorldBorder getWorldBorder() { + return this.e.getWorldBorder(); + } + + public boolean a(@Nullable Entity entity, VoxelShape voxelshape) { + throw new RuntimeException("This method should never be called here. No entity logic inside Region"); + } + + @Nullable + public EntityHuman a(double d0, double d1, double d2, double d3, Predicate predicate) { + throw new RuntimeException("This method should never be called here. No entity logic inside Region"); + } + + public IBlockData getType(BlockPosition blockposition) { + if (blockposition.getY() >= 0 && blockposition.getY() < 256) { + int i = (blockposition.getX() >> 4) - this.a; + int j = (blockposition.getZ() >> 4) - this.b; + + if (i >= 0 && i < this.c.length && j >= 0 && j < this.c[i].length) { + Chunk chunk = this.c[i][j]; + + if (chunk != null) { + return chunk.getType(blockposition); + } + } + } + + return Blocks.AIR.getBlockData(); + } + + public Fluid getFluid(BlockPosition blockposition) { + if (blockposition.getY() >= 0 && blockposition.getY() < 256) { + int i = (blockposition.getX() >> 4) - this.a; + int j = (blockposition.getZ() >> 4) - this.b; + + if (i >= 0 && i < this.c.length && j >= 0 && j < this.c[i].length) { + Chunk chunk = this.c[i][j]; + + if (chunk != null) { + return chunk.getFluid(blockposition); + } + } + } + + return FluidTypes.EMPTY.i(); + } + + public int c() { + return 0; + } + + public BiomeBase getBiome(BlockPosition blockposition) { + int i = (blockposition.getX() >> 4) - this.a; + int j = (blockposition.getZ() >> 4) - this.b; + + return this.c[i][j].getBiome(blockposition); + } + + public boolean isEmpty(BlockPosition blockposition) { + return this.getType(blockposition).isAir(); + } + + public int getBrightness(EnumSkyBlock enumskyblock, BlockPosition blockposition) { + if (blockposition.getY() >= 0 && blockposition.getY() < 256) { + int i = (blockposition.getX() >> 4) - this.a; + int j = (blockposition.getZ() >> 4) - this.b; + + return this.c[i][j].getBrightness(enumskyblock, blockposition); + } else { + return enumskyblock.c; + } + } + + public int a(BlockPosition blockposition, EnumDirection enumdirection) { + return this.getType(blockposition).b((IBlockAccess) this, blockposition, enumdirection); + } + + public boolean e() { + throw new RuntimeException("Not yet implemented"); + } + + public int getSeaLevel() { + throw new RuntimeException("Not yet implemented"); + } +} diff --git a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java new file mode 100644 index 000000000000..d2cece265159 --- /dev/null +++ b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java @@ -0,0 +1,97 @@ +package net.minecraft.server; + +public class ChunkCoordIntPair { + + public final int x; + public final int z; + + public ChunkCoordIntPair(int i, int j) { + this.x = i; + this.z = j; + } + + public ChunkCoordIntPair(BlockPosition blockposition) { + this.x = blockposition.getX() >> 4; + this.z = blockposition.getZ() >> 4; + } + + public ChunkCoordIntPair(long i) { + this.x = (int) i; + this.z = (int) (i >> 32); + } + + public long asLong() { return a(); } // Paper + public long a() { + return a(this.x, this.z); + } + + public static long asLong(final BlockPosition pos) { return a(pos.getX() >> 4, pos.getZ() >> 4); } // Paper - OBFHELPER + public static long asLong(int x, int z) { return a(x, z); } // Paper - OBFHELPER + public static long a(int i, int j) { + return (long) i & 4294967295L | ((long) j & 4294967295L) << 32; + } + + public static int a(long i) { + return (int) (i & 4294967295L); + } + + public static int b(long i) { + return (int) (i >>> 32 & 4294967295L); + } + + public int hashCode() { + int i = 1664525 * this.x + 1013904223; + int j = 1664525 * (this.z ^ -559038737) + 1013904223; + + return i ^ j; + } + + public boolean equals(Object object) { + if (this == object) { + return true; + } else if (!(object instanceof ChunkCoordIntPair)) { + return false; + } else { + ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair) object; + + return this.x == chunkcoordintpair.x && this.z == chunkcoordintpair.z; + } + } + + public double a(Entity entity) { + double d0 = (double) (this.x * 16 + 8); + double d1 = (double) (this.z * 16 + 8); + double d2 = d0 - entity.locX; + double d3 = d1 - entity.locZ; + + return d2 * d2 + d3 * d3; + } + + public int d() { + return this.x << 4; + } + + public int e() { + return this.z << 4; + } + + public int f() { + return (this.x << 4) + 15; + } + + public int g() { + return (this.z << 4) + 15; + } + + public BlockPosition a(int i, int j, int k) { + return new BlockPosition((this.x << 4) + i, j, (this.z << 4) + k); + } + + public String toString() { + return "[" + this.x + ", " + this.z + "]"; + } + + public BlockPosition h() { + return new BlockPosition(this.x << 4, 0, this.z << 4); + } +} diff --git a/src/main/java/net/minecraft/server/ChunkGenerator.java b/src/main/java/net/minecraft/server/ChunkGenerator.java new file mode 100644 index 000000000000..0c9583834271 --- /dev/null +++ b/src/main/java/net/minecraft/server/ChunkGenerator.java @@ -0,0 +1,45 @@ +package net.minecraft.server; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.LongSet; +import java.util.List; +import javax.annotation.Nullable; + +public interface ChunkGenerator { + + void createChunk(IChunkAccess ichunkaccess); + + void addFeatures(RegionLimitedWorldAccess regionlimitedworldaccess, WorldGenStage.Features worldgenstage_features); + + void addDecorations(RegionLimitedWorldAccess regionlimitedworldaccess); + + void addMobs(RegionLimitedWorldAccess regionlimitedworldaccess); + + List getMobsFor(EnumCreatureType enumcreaturetype, BlockPosition blockposition); + + @Nullable + BlockPosition findNearestMapFeature(World world, String s, BlockPosition blockposition, int i, boolean flag); + + C getSettings(); + + int a(World world, boolean flag, boolean flag1); + + boolean canSpawnStructure(BiomeBase biomebase, StructureGenerator structuregenerator); + + @Nullable + WorldGenFeatureConfiguration getFeatureConfiguration(BiomeBase biomebase, StructureGenerator structuregenerator); + + Long2ObjectMap getStructureStartCache(StructureGenerator structuregenerator); + + Long2ObjectMap getStructureCache(StructureGenerator structuregenerator); + + WorldChunkManager getWorldChunkManager(); + + long getSeed(); + + int getSpawnHeight(); + + int getGenerationDepth(); + + World getWorld(); // Spigot +} diff --git a/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java b/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java new file mode 100644 index 000000000000..835a2aae40ae --- /dev/null +++ b/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java @@ -0,0 +1,165 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; +import it.unimi.dsi.fastutil.longs.LongSet; +import java.util.BitSet; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Locale; +import java.util.Map; +import java.util.Random; +import javax.annotation.Nullable; + +public abstract class ChunkGeneratorAbstract implements ChunkGenerator { + + protected final GeneratorAccess a; + protected final long b; + protected final WorldChunkManager c; + protected final Map, Long2ObjectMap> d = Maps.newHashMap(); + protected final Map, Long2ObjectMap> e = Maps.newHashMap(); + + public ChunkGeneratorAbstract(GeneratorAccess generatoraccess, WorldChunkManager worldchunkmanager) { + this.a = generatoraccess; + this.b = generatoraccess.getSeed(); + this.c = worldchunkmanager; + } + + public void addFeatures(RegionLimitedWorldAccess regionlimitedworldaccess, WorldGenStage.Features worldgenstage_features) { + SeededRandom seededrandom = new SeededRandom(this.b); + boolean flag = true; + int i = regionlimitedworldaccess.a(); + int j = regionlimitedworldaccess.b(); + BitSet bitset = regionlimitedworldaccess.getChunkAt(i, j).a(worldgenstage_features); + + for (int k = i - 8; k <= i + 8; ++k) { + for (int l = j - 8; l <= j + 8; ++l) { + List> list = regionlimitedworldaccess.getChunkProvider().getChunkGenerator().getWorldChunkManager().getBiome(new BlockPosition(k * 16, 0, l * 16), (BiomeBase) null).a(worldgenstage_features); + ListIterator listiterator = list.listIterator(); + + while (listiterator.hasNext()) { + int i1 = listiterator.nextIndex(); + WorldGenCarverWrapper worldgencarverwrapper = (WorldGenCarverWrapper) listiterator.next(); + + seededrandom.c(regionlimitedworldaccess.getMinecraftWorld().getSeed() + (long) i1, k, l); + if (worldgencarverwrapper.a(regionlimitedworldaccess, seededrandom, k, l, WorldGenFeatureConfiguration.e)) { + worldgencarverwrapper.a(regionlimitedworldaccess, seededrandom, k, l, i, j, bitset, WorldGenFeatureConfiguration.e); + } + } + } + } + + } + + @Nullable + public BlockPosition findNearestMapFeature(World world, String s, BlockPosition blockposition, int i, boolean flag) { + StructureGenerator structuregenerator = (StructureGenerator) WorldGenerator.aF.get(s.toLowerCase(Locale.ROOT)); + + return structuregenerator != null ? structuregenerator.getNearestGeneratedFeature(world, this, blockposition, i, flag) : null; + } + + protected void a(IChunkAccess ichunkaccess, Random random) { + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + int i = ichunkaccess.getPos().d(); + int j = ichunkaccess.getPos().e(); + Iterator iterator = BlockPosition.a(i, 0, j, i + 16, 0, j + 16).iterator(); + + while (iterator.hasNext()) { + BlockPosition blockposition = (BlockPosition) iterator.next(); + + for (int k = 4; k >= 0; --k) { + if (k <= random.nextInt(5)) { + ichunkaccess.setType(blockposition_mutableblockposition.c(blockposition.getX(), k, blockposition.getZ()), Blocks.BEDROCK.getBlockData(), false); + } + } + } + + } + + public void addDecorations(RegionLimitedWorldAccess regionlimitedworldaccess) { + BlockFalling.instaFall = true; + int i = regionlimitedworldaccess.a(); + int j = regionlimitedworldaccess.b(); + int k = i * 16; + int l = j * 16; + BlockPosition blockposition = new BlockPosition(k, 0, l); + BiomeBase biomebase = regionlimitedworldaccess.getChunkAt(i + 1, j + 1).getBiomeIndex()[0]; + SeededRandom seededrandom = new SeededRandom(); + long i1 = seededrandom.a(regionlimitedworldaccess.getSeed(), k, l); + WorldGenStage.Decoration[] aworldgenstage_decoration = WorldGenStage.Decoration.values(); + int j1 = aworldgenstage_decoration.length; + + for (int k1 = 0; k1 < j1; ++k1) { + WorldGenStage.Decoration worldgenstage_decoration = aworldgenstage_decoration[k1]; + + biomebase.a(worldgenstage_decoration, this, regionlimitedworldaccess, i1, seededrandom, blockposition); + } + + BlockFalling.instaFall = false; + } + + public void a(IChunkAccess ichunkaccess, BiomeBase[] abiomebase, SeededRandom seededrandom, int i) { + double d0 = 0.03125D; + ChunkCoordIntPair chunkcoordintpair = ichunkaccess.getPos(); + int j = chunkcoordintpair.d(); + int k = chunkcoordintpair.e(); + double[] adouble = this.a(chunkcoordintpair.x, chunkcoordintpair.z); + + for (int l = 0; l < 16; ++l) { + for (int i1 = 0; i1 < 16; ++i1) { + int j1 = j + l; + int k1 = k + i1; + int l1 = ichunkaccess.a(HeightMap.Type.WORLD_SURFACE_WG, l, i1) + 1; + + abiomebase[i1 * 16 + l].a(seededrandom, ichunkaccess, j1, k1, l1, adouble[i1 * 16 + l], this.getSettings().r(), this.getSettings().s(), i, this.a.getSeed()); + } + } + + } + + public abstract C getSettings(); + + public abstract double[] a(int i, int j); + + public boolean canSpawnStructure(BiomeBase biomebase, StructureGenerator structuregenerator) { + return biomebase.a(structuregenerator); + } + + @Nullable + public WorldGenFeatureConfiguration getFeatureConfiguration(BiomeBase biomebase, StructureGenerator structuregenerator) { + return biomebase.b(structuregenerator); + } + + public WorldChunkManager getWorldChunkManager() { + return this.c; + } + + public long getSeed() { + return this.b; + } + + public Long2ObjectMap getStructureStartCache(StructureGenerator structuregenerator) { + return (Long2ObjectMap) this.d.computeIfAbsent(structuregenerator, (structuregenerator1) -> { + return new ExpiringMap<>(8192, 10000); // Paper - already synchronized + }); + } + + public Long2ObjectMap getStructureCache(StructureGenerator structuregenerator) { + return (Long2ObjectMap) this.e.computeIfAbsent(structuregenerator, (structuregenerator1) -> { + return new ExpiringMap<>(8192, 10000); // Paper - already synchronized + }); + } + + public int getGenerationDepth() { + return 256; + } + + // Spigot start + @Override + public World getWorld() { + return this.a.getMinecraftWorld(); + } + // Spigot end +} diff --git a/src/main/java/net/minecraft/server/ChunkMap.java b/src/main/java/net/minecraft/server/ChunkMap.java new file mode 100644 index 000000000000..154ab09e0c6c --- /dev/null +++ b/src/main/java/net/minecraft/server/ChunkMap.java @@ -0,0 +1,142 @@ +package net.minecraft.server; + +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ChunkMap extends Long2ObjectOpenHashMap { + + private static final Logger a = LogManager.getLogger(); + + public ChunkMap(int i) { + super(i); + } + + public Chunk put(long i, Chunk chunk) { + org.spigotmc.AsyncCatcher.catchOp("Async Chunk put"); // Paper + chunk.world.timings.syncChunkLoadPostTimer.startTiming(); // Paper + lastChunkByPos = chunk; // Paper + // Paper start + Chunk chunk1; + synchronized (this) { + // synchronize so any async gets are safe + chunk1 = (Chunk) super.put(i, chunk); + } + if (chunk1 == null) { // Paper - we should never be overwriting chunks + // Paper end + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i); + + for (int j = chunkcoordintpair.x - 1; j <= chunkcoordintpair.x + 1; ++j) { + for (int k = chunkcoordintpair.z - 1; k <= chunkcoordintpair.z + 1; ++k) { + if (j != chunkcoordintpair.x || k != chunkcoordintpair.z) { + long l = ChunkCoordIntPair.a(j, k); + Chunk chunk2 = (Chunk) super.get(l); // Paper - use super to avoid polluting last access cache + + if (chunk2 != null) { + chunk.H(); + chunk2.H(); + } + } + } + } + + // CraftBukkit start + // Update neighbor counts + for (int x = -2; x < 3; x++) { + for (int z = -2; z < 3; z++) { + if (x == 0 && z == 0) { + continue; + } + + Chunk neighbor = super.get(ChunkCoordIntPair.a(chunkcoordintpair.x + x, chunkcoordintpair.z + z)); // Paper - use super to avoid polluting last access cache + if (neighbor != null) { + neighbor.setNeighborLoaded(-x, -z); + chunk.setNeighborLoaded(x, z); + } + } + // Paper start + } } else { + a.error("Overwrote existing chunk! (" + chunk.world.getWorld().getName() + ":" + chunk.locX+"," + chunk.locZ + ")", new IllegalStateException()); + } + // Paper end + // Paper start - if this is a spare chunk (not part of any players view distance), go ahead and queue it for unload. + if (!((WorldServer)chunk.world).getPlayerChunkMap().isChunkInUse(chunk.locX, chunk.locZ)) { + if (chunk.world.paperConfig.delayChunkUnloadsBy > 0) { + chunk.scheduledForUnload = System.currentTimeMillis(); + } else { + ((WorldServer) chunk.world).getChunkProvider().unload(chunk); + } + } + // Paper end + chunk.world.timings.syncChunkLoadPostTimer.stopTiming(); // Paper + // CraftBukkit end + + return chunk1; + } + + public Chunk put(Long olong, Chunk chunk) { + return MCUtil.ensureMain("Chunk Put", () -> this.put(olong.longValue(), chunk)); // Paper + } + + public Chunk remove(long i) { + // Paper start + org.spigotmc.AsyncCatcher.catchOp("Async Chunk remove"); + Chunk chunk; + synchronized (this) { + // synchronize so any async gets are safe + chunk = super.remove(i); + } + if (chunk != null) { // Paper - don't decrement if we didn't remove anything + // Paper end + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i); + + for (int j = chunkcoordintpair.x - 1; j <= chunkcoordintpair.x + 1; ++j) { + for (int k = chunkcoordintpair.z - 1; k <= chunkcoordintpair.z + 1; ++k) { + if (j != chunkcoordintpair.x || k != chunkcoordintpair.z) { + Chunk chunk1 = (Chunk) super.get(ChunkCoordIntPair.a(j, k)); // Paper - use super to avoid polluting last access cache + + if (chunk1 != null) { + chunk1.I(); + } + } + } + } + + // Paper start + } // close if (chunk != null) + if (lastChunkByPos != null && i == lastChunkByPos.chunkKey) { + lastChunkByPos = null; + } + return chunk; + } + private Chunk lastChunkByPos = null; + + @Override + public Chunk get(long l) { + if (MCUtil.isMainThread()) { + if (lastChunkByPos != null && l == lastChunkByPos.chunkKey) { + return lastChunkByPos; + } + final Chunk chunk = super.get(l); + return chunk != null ? (lastChunkByPos = chunk) : null; + } else { + synchronized (this) { + return super.get(l); + } + } + } + // Paper end + + public Chunk remove(Object object) { + return MCUtil.ensureMain("Chunk Remove", () -> this.remove(((Long) object).longValue())); // Paper + } + + public void putAll(Map map) { + throw new RuntimeException("Not yet implemented"); + } + + public boolean remove(Object object, Object object1) { + throw new RuntimeException("Not yet implemented"); + } +} diff --git a/src/main/java/net/minecraft/server/ChunkProviderGenerate.java b/src/main/java/net/minecraft/server/ChunkProviderGenerate.java new file mode 100644 index 000000000000..275e6a1774bc --- /dev/null +++ b/src/main/java/net/minecraft/server/ChunkProviderGenerate.java @@ -0,0 +1,274 @@ +package net.minecraft.server; + +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ChunkProviderGenerate extends ChunkGeneratorAbstract { + + private static final Logger f = LogManager.getLogger(); + private final NoiseGeneratorOctaves g; + private final NoiseGeneratorOctaves h; + private final NoiseGeneratorOctaves i; + private final NoiseGenerator3 j; + private final GeneratorSettingsOverworld k; + private final NoiseGeneratorOctaves l; + private final NoiseGeneratorOctaves m; + private final WorldType n; + private final float[] o; + private final MobSpawnerPhantom p = new MobSpawnerPhantom(); + private final IBlockData q; + private final IBlockData r; + + public ChunkProviderGenerate(GeneratorAccess generatoraccess, WorldChunkManager worldchunkmanager, GeneratorSettingsOverworld generatorsettingsoverworld) { + super(generatoraccess, worldchunkmanager); + this.n = generatoraccess.getWorldData().getType(); + SeededRandom seededrandom = new SeededRandom(this.b); + + this.g = new NoiseGeneratorOctaves(seededrandom, 16); + this.h = new NoiseGeneratorOctaves(seededrandom, 16); + this.i = new NoiseGeneratorOctaves(seededrandom, 8); + this.j = new NoiseGenerator3(seededrandom, 4); + this.l = new NoiseGeneratorOctaves(seededrandom, 10); + this.m = new NoiseGeneratorOctaves(seededrandom, 16); + this.o = new float[25]; + + for (int i = -2; i <= 2; ++i) { + for (int j = -2; j <= 2; ++j) { + float f = 10.0F / MathHelper.c((float) (i * i + j * j) + 0.2F); + + this.o[i + 2 + (j + 2) * 5] = f; + } + } + + this.k = generatorsettingsoverworld; + this.q = this.k.r(); + this.r = this.k.s(); + } + + public void createChunk(IChunkAccess ichunkaccess) { + ChunkCoordIntPair chunkcoordintpair = ichunkaccess.getPos(); + int i = chunkcoordintpair.x; + int j = chunkcoordintpair.z; + SeededRandom seededrandom = new SeededRandom(); + + seededrandom.a(i, j); + BiomeBase[] abiomebase = this.c.getBiomeBlock(i * 16, j * 16, 16, 16); + + ichunkaccess.a(abiomebase); + this.a(i, j, ichunkaccess); + ichunkaccess.a(HeightMap.Type.WORLD_SURFACE_WG, HeightMap.Type.OCEAN_FLOOR_WG); + this.a(ichunkaccess, abiomebase, seededrandom, this.a.getSeaLevel()); + this.a(ichunkaccess, seededrandom); + ichunkaccess.a(HeightMap.Type.WORLD_SURFACE_WG, HeightMap.Type.OCEAN_FLOOR_WG); + ichunkaccess.a(ChunkStatus.BASE); + } + + public void addMobs(RegionLimitedWorldAccess regionlimitedworldaccess) { + int i = regionlimitedworldaccess.a(); + int j = regionlimitedworldaccess.b(); + BiomeBase biomebase = regionlimitedworldaccess.getChunkAt(i, j).getBiomeIndex()[0]; + SeededRandom seededrandom = new SeededRandom(); + + seededrandom.a(regionlimitedworldaccess.getSeed(), i << 4, j << 4); + SpawnerCreature.a(regionlimitedworldaccess, biomebase, i, j, seededrandom); + } + + public void a(int i, int j, IChunkAccess ichunkaccess) { + BiomeBase[] abiomebase = this.c.getBiomes(ichunkaccess.getPos().x * 4 - 2, ichunkaccess.getPos().z * 4 - 2, 10, 10); + double[] adouble = new double[825]; + + this.a(abiomebase, ichunkaccess.getPos().x * 4, 0, ichunkaccess.getPos().z * 4, adouble); + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + for (int k = 0; k < 4; ++k) { + int l = k * 5; + int i1 = (k + 1) * 5; + + for (int j1 = 0; j1 < 4; ++j1) { + int k1 = (l + j1) * 33; + int l1 = (l + j1 + 1) * 33; + int i2 = (i1 + j1) * 33; + int j2 = (i1 + j1 + 1) * 33; + + for (int k2 = 0; k2 < 32; ++k2) { + double d0 = 0.125D; + double d1 = adouble[k1 + k2]; + double d2 = adouble[l1 + k2]; + double d3 = adouble[i2 + k2]; + double d4 = adouble[j2 + k2]; + double d5 = (adouble[k1 + k2 + 1] - d1) * 0.125D; + double d6 = (adouble[l1 + k2 + 1] - d2) * 0.125D; + double d7 = (adouble[i2 + k2 + 1] - d3) * 0.125D; + double d8 = (adouble[j2 + k2 + 1] - d4) * 0.125D; + + for (int l2 = 0; l2 < 8; ++l2) { + double d9 = 0.25D; + double d10 = d1; + double d11 = d2; + double d12 = (d3 - d1) * 0.25D; + double d13 = (d4 - d2) * 0.25D; + + for (int i3 = 0; i3 < 4; ++i3) { + double d14 = 0.25D; + double d15 = (d11 - d10) * 0.25D; + double d16 = d10 - d15; + + for (int j3 = 0; j3 < 4; ++j3) { + blockposition_mutableblockposition.c(k * 4 + i3, k2 * 8 + l2, j1 * 4 + j3); + if ((d16 += d15) > 0.0D) { + ichunkaccess.setType(blockposition_mutableblockposition, this.q, false); + } else if (k2 * 8 + l2 < this.k.w()) { + ichunkaccess.setType(blockposition_mutableblockposition, this.r, false); + } + } + + d10 += d12; + d11 += d13; + } + + d1 += d5; + d2 += d6; + d3 += d7; + d4 += d8; + } + } + } + } + + } + + private void a(BiomeBase[] abiomebase, int i, int j, int k, double[] adouble) { + double[] adouble1 = this.m.a(i, k, 5, 5, this.k.x(), this.k.y(), this.k.z()); + float f = this.k.A(); + float f1 = this.k.B(); + double[] adouble2 = this.i.a(i, j, k, 5, 33, 5, (double) (f / this.k.C()), (double) (f1 / this.k.D()), (double) (f / this.k.E())); + double[] adouble3 = this.g.a(i, j, k, 5, 33, 5, (double) f, (double) f1, (double) f); + double[] adouble4 = this.h.a(i, j, k, 5, 33, 5, (double) f, (double) f1, (double) f); + int l = 0; + int i1 = 0; + + for (int j1 = 0; j1 < 5; ++j1) { + for (int k1 = 0; k1 < 5; ++k1) { + float f2 = 0.0F; + float f3 = 0.0F; + float f4 = 0.0F; + boolean flag = true; + BiomeBase biomebase = abiomebase[j1 + 2 + (k1 + 2) * 10]; + + for (int l1 = -2; l1 <= 2; ++l1) { + for (int i2 = -2; i2 <= 2; ++i2) { + BiomeBase biomebase1 = abiomebase[j1 + l1 + 2 + (k1 + i2 + 2) * 10]; + float f5 = this.k.F() + biomebase1.h() * this.k.G(); + float f6 = this.k.H() + biomebase1.l() * this.k.I(); + + if (this.n == WorldType.AMPLIFIED && f5 > 0.0F) { + f5 = 1.0F + f5 * 2.0F; + f6 = 1.0F + f6 * 4.0F; + } + // CraftBukkit start - fix MC-54738 + if (f5 < -1.8F) { + f5 = -1.8F; + } + // CraftBukkit end + + float f7 = this.o[l1 + 2 + (i2 + 2) * 5] / (f5 + 2.0F); + + if (biomebase1.h() > biomebase.h()) { + f7 /= 2.0F; + } + + f2 += f6 * f7; + f3 += f5 * f7; + f4 += f7; + } + } + + f2 /= f4; + f3 /= f4; + f2 = f2 * 0.9F + 0.1F; + f3 = (f3 * 4.0F - 1.0F) / 8.0F; + double d0 = adouble1[i1] / 8000.0D; + + if (d0 < 0.0D) { + d0 = -d0 * 0.3D; + } + + d0 = d0 * 3.0D - 2.0D; + if (d0 < 0.0D) { + d0 /= 2.0D; + if (d0 < -1.0D) { + d0 = -1.0D; + } + + d0 /= 1.4D; + d0 /= 2.0D; + } else { + if (d0 > 1.0D) { + d0 = 1.0D; + } + + d0 /= 8.0D; + } + + ++i1; + double d1 = (double) f3; + double d2 = (double) f2; + + d1 += d0 * 0.2D; + d1 = d1 * this.k.J() / 8.0D; + double d3 = this.k.J() + d1 * 4.0D; + + for (int j2 = 0; j2 < 33; ++j2) { + double d4 = ((double) j2 - d3) * this.k.K() * 128.0D / 256.0D / d2; + + if (d4 < 0.0D) { + d4 *= 4.0D; + } + + double d5 = adouble3[l] / this.k.L(); + double d6 = adouble4[l] / this.k.M(); + double d7 = (adouble2[l] / 10.0D + 1.0D) / 2.0D; + double d8 = MathHelper.b(d5, d6, d7) - d4; + + if (j2 > 29) { + double d9 = (double) ((float) (j2 - 29) / 3.0F); + + d8 = d8 * (1.0D - d9) - 10.0D * d9; + } + + adouble[l] = d8; + ++l; + } + } + } + + } + + public List getMobsFor(EnumCreatureType enumcreaturetype, BlockPosition blockposition) { + BiomeBase biomebase = this.a.getBiome(blockposition); + + return enumcreaturetype == EnumCreatureType.MONSTER && ((WorldGenFeatureSwampHut) WorldGenerator.l).d(this.a, blockposition) ? WorldGenerator.l.d() : (enumcreaturetype == EnumCreatureType.MONSTER && WorldGenerator.n.b(this.a, blockposition) ? WorldGenerator.n.d() : biomebase.getMobs(enumcreaturetype)); + } + + public int a(World world, boolean flag, boolean flag1) { + byte b0 = 0; + int i = b0 + this.p.a(world, flag, flag1); + + return i; + } + + public GeneratorSettingsOverworld getSettings() { + return this.k; + } + + public double[] a(int i, int j) { + double d0 = 0.03125D; + + return this.j.a((double) (i << 4), (double) (j << 4), 16, 16, 0.0625D, 0.0625D, 1.0D); + } + + public int getSpawnHeight() { + return this.a.getSeaLevel() + 1; + } +} diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java new file mode 100644 index 000000000000..781e0687702a --- /dev/null +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -0,0 +1,507 @@ +package net.minecraft.server; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSet; +import it.unimi.dsi.fastutil.objects.ObjectIterator; +import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; +import javax.annotation.Nullable; +import com.destroystokyo.paper.exception.ServerInternalException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor; +import org.bukkit.event.world.ChunkUnloadEvent; +// CraftBukkit end + +public class ChunkProviderServer implements IChunkProvider { + + private static final Logger a = LogManager.getLogger(); + public final LongSet unloadQueue = new LongOpenHashSet(); + public final ChunkGenerator chunkGenerator; + public final IChunkLoader chunkLoader; + // Paper start - chunk save stats + private long lastQueuedSaves = 0L; // Paper + private long lastProcessedSaves = 0L; // Paper + private long lastSaveStatPrinted = System.currentTimeMillis(); + // Paper end + public final Long2ObjectMap chunks = new ChunkMap(8192); // Paper - remove synchronize - we keep everything on main for manip + private Chunk lastChunk; + private final ChunkTaskScheduler chunkScheduler; + final SchedulerBatch batchScheduler; // Paper + public final WorldServer world; + final IAsyncTaskHandler asyncTaskHandler; // Paper + + public ChunkProviderServer(WorldServer worldserver, IChunkLoader ichunkloader, ChunkGenerator chunkgenerator, IAsyncTaskHandler iasynctaskhandler) { + this.world = worldserver; + this.chunkLoader = ichunkloader; + this.chunkGenerator = chunkgenerator; + this.asyncTaskHandler = iasynctaskhandler; + this.chunkScheduler = new ChunkTaskScheduler(0, worldserver, chunkgenerator, ichunkloader, iasynctaskhandler); // CraftBukkit - very buggy, broken in lots of __subtle__ ways. Same goes for async chunk loading. Also Bukkit API / plugins can't handle async events at all anyway. + this.batchScheduler = new SchedulerBatch<>(this.chunkScheduler); + } + + public Collection a() { + return this.chunks.values(); + } + + public void unload(Chunk chunk) { + if (this.world.worldProvider.a(chunk.locX, chunk.locZ)) { + this.unloadQueue.add(ChunkCoordIntPair.a(chunk.locX, chunk.locZ)); + } + + } + + public void b() { + ObjectIterator objectiterator = this.chunks.values().iterator(); + + while (objectiterator.hasNext()) { + Chunk chunk = (Chunk) objectiterator.next(); + + this.unload(chunk); + } + + } + + public void a(int i, int j) { + this.unloadQueue.remove(ChunkCoordIntPair.a(i, j)); + } + + // Paper start - defaults if Async Chunks is not enabled + boolean chunkGoingToExists(int x, int z) { + final long k = ChunkCoordIntPair.asLong(x, z); + return chunkScheduler.progressCache.containsKey(k); + } + public void bumpPriority(ChunkCoordIntPair coords) { + // do nothing, override in async + } + + public List getSpiralOutChunks(BlockPosition blockposition, int radius) { + List list = com.google.common.collect.Lists.newArrayList(); + + list.add(new ChunkCoordIntPair(blockposition.getX() >> 4, blockposition.getZ() >> 4)); + for (int r = 1; r <= radius; r++) { + int x = -r; + int z = r; + + // Iterates the edge of half of the box; then negates for other half. + while (x <= r && z > -r) { + list.add(new ChunkCoordIntPair((blockposition.getX() + (x << 4)) >> 4, (blockposition.getZ() + (z << 4)) >> 4)); + list.add(new ChunkCoordIntPair((blockposition.getX() - (x << 4)) >> 4, (blockposition.getZ() - (z << 4)) >> 4)); + + if (x < r) { + x++; + } else { + z--; + } + } + } + return list; + } + + public Chunk getChunkAt(int x, int z, boolean load, boolean gen, Consumer consumer) { + return getChunkAt(x, z, load, gen, false, consumer); + } + public Chunk getChunkAt(int x, int z, boolean load, boolean gen, boolean priority, Consumer consumer) { + Chunk chunk = getChunkAt(x, z, load, gen); + if (consumer != null) { + consumer.accept(chunk); + } + return chunk; + } + + PaperAsyncChunkProvider.CancellableChunkRequest requestChunk(int x, int z, boolean gen, boolean priority, Consumer consumer) { + Chunk chunk = getChunkAt(x, z, gen, priority, consumer); + return new PaperAsyncChunkProvider.CancellableChunkRequest() { + @Override + public void cancel() { + + } + + @Override + public Chunk getChunk() { + return chunk; + } + }; + } + // Paper end + + @Nullable + public Chunk getChunkAt(int i, int j, boolean flag, boolean flag1) { + IChunkLoader ichunkloader = this.chunkLoader; + Chunk chunk; + // Paper start - do already loaded checks before synchronize + long k = ChunkCoordIntPair.a(i, j); + chunk = (Chunk) this.chunks.get(k); + if (chunk != null) { + //this.lastChunk = chunk; // Paper remove vanilla lastChunk + return chunk; + } + // Paper end + + synchronized (this.chunkLoader) { + // Paper start - remove vanilla lastChunk, we do it more accurately + /* if (this.lastChunk != null && this.lastChunk.locX == i && this.lastChunk.locZ == j) { + return this.lastChunk; + }*/ // Paper end + + // Paper start - move up + //long k = ChunkCoordIntPair.a(i, j); + + /*chunk = (Chunk) this.chunks.get(k); + if (chunk != null) { + //this.lastChunk = chunk; // Paper remove vanilla lastChunk + return chunk; + }*/ + // Paper end + + if (flag) { + try (co.aikar.timings.Timing timing = world.timings.syncChunkLoadTimer.startTiming()) { // Paper + chunk = this.chunkLoader.a(this.world, i, j, (chunk1) -> { + chunk1.setLastSaved(this.world.getTime()); + this.chunks.put(ChunkCoordIntPair.a(i, j), chunk1); + }); + } catch (Exception exception) { + ChunkProviderServer.a.error("Couldn't load chunk", exception); + } + } + } + + if (chunk != null) { + this.asyncTaskHandler.postToMainThread(chunk::addEntities); + return chunk; + } else if (flag1) { + try (co.aikar.timings.Timing timing = world.timings.chunkGeneration.startTiming()) { // Paper + this.batchScheduler.b(); + this.batchScheduler.a(new ChunkCoordIntPair(i, j)); + CompletableFuture completablefuture = this.batchScheduler.c(); + + return (Chunk) completablefuture.thenApply(this::a).join(); + } catch (RuntimeException runtimeexception) { + throw this.a(i, j, (Throwable) runtimeexception); + } + // finally { world.timings.syncChunkLoadTimer.stopTiming(); } // Spigot // Paper + } else { + return null; + } + } + + // CraftBukkit start + public Chunk generateChunk(int x, int z) { + try { + this.batchScheduler.b(); + ChunkCoordIntPair pos = new ChunkCoordIntPair(x, z); + this.chunkScheduler.forcePolluteCache(pos); + ((ChunkRegionLoader) this.chunkLoader).blacklist.add(pos.a()); + this.batchScheduler.a(pos); + CompletableFuture completablefuture = this.batchScheduler.c(); + + Chunk chunk = (Chunk) completablefuture.thenApply(this::a).join(); + ((ChunkRegionLoader) this.chunkLoader).blacklist.remove(pos.a()); + return chunk; + } catch (RuntimeException runtimeexception) { + throw this.a(x, z, (Throwable) runtimeexception); + } + } + // CraftBukkit end + + public IChunkAccess a(int i, int j, boolean flag) { + Chunk chunk = this.getChunkAt(i, j, true, false); + + return (IChunkAccess) (chunk != null ? chunk : (IChunkAccess) this.chunkScheduler.b(new ChunkCoordIntPair(i, j), flag)); + } + + public CompletableFuture loadAllChunks(Iterable iterable, Consumer consumer) { return a(iterable, consumer).thenCompose(protoChunk -> null); } // Paper - overriden in async chunk provider + private CompletableFuture a(Iterable iterable, Consumer consumer) { // Paper - mark private, use above method + this.batchScheduler.b(); + Iterator iterator = iterable.iterator(); + + while (iterator.hasNext()) { + ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair) iterator.next(); + Chunk chunk = this.getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z, true, false); + + if (chunk != null) { + consumer.accept(chunk); + } else { + this.batchScheduler.a(chunkcoordintpair).thenApply(this::a).thenAccept(consumer); + } + } + + return this.batchScheduler.c(); + } + + ReportedException generateChunkError(int i, int j, Throwable throwable) { return a(i, j, throwable); } // Paper - OBFHELPER + private ReportedException a(int i, int j, Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Exception generating new chunk"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Chunk to be generated"); + + crashreportsystemdetails.a("Location", (Object) String.format("%d,%d", i, j)); + crashreportsystemdetails.a("Position hash", (Object) ChunkCoordIntPair.a(i, j)); + crashreportsystemdetails.a("Generator", (Object) this.chunkGenerator); + return new ReportedException(crashreport); + } + + private Chunk a(IChunkAccess ichunkaccess) { + ChunkCoordIntPair chunkcoordintpair = ichunkaccess.getPos(); + int i = chunkcoordintpair.x; + int j = chunkcoordintpair.z; + long k = ChunkCoordIntPair.a(i, j); + Long2ObjectMap long2objectmap = this.chunks; + Chunk chunk; + + synchronized (this.chunks) { + Chunk chunk1 = (Chunk) this.chunks.get(k); + + if (chunk1 != null) { + return chunk1; + } + + if (ichunkaccess instanceof Chunk) { + chunk = (Chunk) ichunkaccess; + } else { + if (!(ichunkaccess instanceof ProtoChunk)) { + throw new IllegalStateException(); + } + + chunk = new Chunk(this.world, (ProtoChunk) ichunkaccess, i, j); + } + + this.chunks.put(k, chunk); + //this.lastChunk = chunk; // Paper + } + + this.asyncTaskHandler.postToMainThread(chunk::addEntities); + return chunk; + } + + public void saveChunk(IChunkAccess ichunkaccess, boolean unloaded) { // Spigot + try (co.aikar.timings.Timing timed = world.timings.chunkSaveData.startTiming()) { // Paper - Timings + ichunkaccess.setLastSaved(this.world.getTime()); + this.chunkLoader.saveChunk(this.world, ichunkaccess, unloaded); // Spigot + } catch (IOException ioexception) { + // Paper start + String msg = "Couldn\'t save chunk"; + ChunkProviderServer.a.error(msg, ioexception); + ServerInternalException.reportInternalException(ioexception); + } catch (ExceptionWorldConflict exceptionworldconflict) { + ChunkProviderServer.a.error("Couldn\'t save chunk; already in use by another instance of Minecraft?", exceptionworldconflict); + String msg = "Couldn\'t save chunk; already in use by another instance of Minecraft?"; + ChunkProviderServer.a.error(msg, exceptionworldconflict); + ServerInternalException.reportInternalException(exceptionworldconflict); + // Paper end + } + + } + + public boolean a(boolean flag) { + int i = 0; + + this.chunkScheduler.a(() -> { + return true; + }); + IChunkLoader ichunkloader = this.chunkLoader; + + synchronized (this.chunkLoader) { + ObjectIterator objectiterator = this.chunks.values().iterator(); + + // Paper start + final ChunkRegionLoader chunkLoader = (ChunkRegionLoader) world.getChunkProvider().chunkLoader; + final int queueSize = chunkLoader.getQueueSize(); + + final long now = System.currentTimeMillis(); + final long timeSince = (now - lastSaveStatPrinted) / 1000; + final Integer printRateSecs = Integer.getInteger("printSaveStats"); + if (printRateSecs != null && timeSince >= printRateSecs) { + final String timeStr = "/" + timeSince +"s"; + final long queuedSaves = chunkLoader.getQueuedSaves(); + long queuedDiff = queuedSaves - lastQueuedSaves; + lastQueuedSaves = queuedSaves; + + final long processedSaves = chunkLoader.getProcessedSaves(); + long processedDiff = processedSaves - lastProcessedSaves; + lastProcessedSaves = processedSaves; + + lastSaveStatPrinted = now; + if (processedDiff > 0 || queueSize > 0 || queuedDiff > 0) { + System.out.println("[Chunk Save Stats] " + world.worldData.getName() + + " - Current: " + queueSize + + " - Queued: " + queuedDiff + timeStr + + " - Processed: " +processedDiff + timeStr + ); + } + } + if (!flag && queueSize > world.paperConfig.queueSizeAutoSaveThreshold){ + return false; + } + // Paper end + while (objectiterator.hasNext()) { + Chunk chunk = (Chunk) objectiterator.next(); + + if (chunk.c(flag)) { + this.saveChunk(chunk, false); // Spigot + chunk.a(false); + ++i; + if (!flag && i >= world.paperConfig.maxAutoSaveChunksPerTick) { // Spigot - // Paper - Incremental Auto Save - cap max + return false; + } + } + } + + return true; + } + } + + public void close() { + // Paper start - we do not need to wait for chunk generations to finish on close + /*try { + this.batchScheduler.a(); + } catch (InterruptedException interruptedexception) { + ChunkProviderServer.a.error("Couldn't stop taskManager", interruptedexception); + }*/ + // Paper end + + } + + public void c() { + IChunkLoader ichunkloader = this.chunkLoader; + + synchronized (this.chunkLoader) { + this.chunkLoader.b(); + } + } + + private static final double UNLOAD_QUEUE_RESIZE_FACTOR = 0.96; // Spigot + + public boolean unloadChunks(BooleanSupplier booleansupplier) { + if (!this.world.savingDisabled) { + if (!this.unloadQueue.isEmpty()) { + // Spigot start + org.spigotmc.SlackActivityAccountant activityAccountant = this.world.getMinecraftServer().slackActivityAccountant; + activityAccountant.startActivity(0.5); + int targetSize = Math.min(this.unloadQueue.size() - 100, (int) (this.unloadQueue.size() * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive + // Spigot end + Iterator iterator = this.unloadQueue.iterator(); + + while (iterator.hasNext()) { // Spigot + Long olong = (Long) iterator.next(); + iterator.remove(); // Spigot + IChunkLoader ichunkloader = this.chunkLoader; + + synchronized (this.chunkLoader) { + Chunk chunk = (Chunk) this.chunks.get(olong); + + if (chunk != null) { + // CraftBukkit start - move unload logic to own method + if (!unloadChunk(chunk, true)) { + continue; + } + // CraftBukkit end + + // Spigot start + if (!booleansupplier.getAsBoolean() && this.unloadQueue.size() <= targetSize && activityAccountant.activityTimeIsExhausted()) { + break; + } + // Spigot end + } + } + } + activityAccountant.endActivity(); // Spigot + } + // Paper start - delayed chunk unloads + long now = System.currentTimeMillis(); + long unloadAfter = world.paperConfig.delayChunkUnloadsBy; + if (unloadAfter > 0) { + //noinspection Convert2streamapi + for (Chunk chunk : chunks.values()) { + if (chunk.scheduledForUnload != null && now - chunk.scheduledForUnload > unloadAfter) { + chunk.scheduledForUnload = null; + unload(chunk); + } + } + } + // Paper end + + this.chunkScheduler.a(booleansupplier); + } + + return false; + } + + // CraftBukkit start + public boolean unloadChunk(Chunk chunk, boolean save) { + ChunkUnloadEvent event = new ChunkUnloadEvent(chunk.bukkitChunk, save); + this.world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + save = event.isSaveChunk(); + chunk.lightingQueue.processUnload(); // Paper + + // Update neighbor counts + for (int x = -2; x < 3; x++) { + for (int z = -2; z < 3; z++) { + if (x == 0 && z == 0) { + continue; + } + + Chunk neighbor = this.chunks.get(chunk.chunkKey); // Paper + if (neighbor != null) { + neighbor.setNeighborUnloaded(-x, -z); + chunk.setNeighborUnloaded(x, z); + } + } + } + // Moved from unloadChunks above + synchronized (this.chunkLoader) { + chunk.removeEntities(); + if (save) { + this.saveChunk(chunk, true); // Spigot + } + this.chunks.remove(chunk.chunkKey); + // this.lastChunk = null; // Paper + } + return true; + } + // CraftBukkit end + + public boolean d() { + return !this.world.savingDisabled; + } + + public String getName() { + return "ServerChunkCache: " + this.chunks.size() + " Drop: " + this.unloadQueue.size(); + } + + public List a(EnumCreatureType enumcreaturetype, BlockPosition blockposition) { + return this.chunkGenerator.getMobsFor(enumcreaturetype, blockposition); + } + + public int a(World world, boolean flag, boolean flag1) { + return this.chunkGenerator.a(world, flag, flag1); + } + + @Nullable + public BlockPosition a(World world, String s, BlockPosition blockposition, int i, boolean flag) { + return this.chunkGenerator.findNearestMapFeature(world, s, blockposition, i, flag); + } + + public ChunkGenerator getChunkGenerator() { + return this.chunkGenerator; + } + + public int g() { + return this.chunks.size(); + } + + public boolean isLoaded(int i, int j) { + return this.chunks.get(ChunkCoordIntPair.asLong(i, j)) != null; // Paper - use get for last access + } +} diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java new file mode 100644 index 000000000000..51df075b44ac --- /dev/null +++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java @@ -0,0 +1,1155 @@ +package net.minecraft.server; + +import co.aikar.timings.Timings; +import com.google.common.collect.Maps; +import com.mojang.datafixers.DataFixTypes; +import com.mojang.datafixers.DataFixer; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSet; +import it.unimi.dsi.fastutil.shorts.ShortList; +import it.unimi.dsi.fastutil.shorts.ShortListIterator; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.BitSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.function.Consumer; +import java.util.function.Function; +import javax.annotation.Nullable; +import java.util.concurrent.ConcurrentLinkedQueue; // Paper +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +// Spigot start +import java.util.function.Supplier; +import org.spigotmc.SupplierUtils; +// Spigot end + +public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { + + // Paper start - Chunk queue improvements + private static class QueuedChunk { + public ChunkCoordIntPair coords; + public Supplier compoundSupplier; + public Runnable onSave; + + public QueuedChunk(Runnable run) { + this.coords = null; + this.compoundSupplier = null; + this.onSave = run; + } + + public QueuedChunk(ChunkCoordIntPair coords, Supplier compoundSupplier) { + this.coords = coords; + this.compoundSupplier = compoundSupplier; + } + } + final private ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue<>(); + // Paper end + + private static final Logger a = LogManager.getLogger(); + private final it.unimi.dsi.fastutil.longs.Long2ObjectMap> saveMap = it.unimi.dsi.fastutil.longs.Long2ObjectMaps.synchronize(new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>()); // Paper + private final File c; + private final DataFixer d; + private PersistentStructureLegacy e; + // private boolean f; // CraftBukkit + public final LongSet blacklist = new LongOpenHashSet(); + private static final double SAVE_QUEUE_TARGET_SIZE = 625; // Spigot + // Paper start - support saving to an alternate directory + private final File templateWorld; + private final File actualWorld; + private final boolean useAltWorld; + + private void copyIfNeeded(int x, int z) { + if (!useAltWorld) { + return; + } + synchronized (RegionFileCache.class) { + if (RegionFileCache.hasRegionFile(this.actualWorld, x, z)) { + return; + } + File actual = RegionFileCache.getRegionFileName(this.actualWorld, x, z); + File template = RegionFileCache.getRegionFileName(this.templateWorld, x, z); + if (!actual.exists() && template.exists()) { + try { + //a.info("Copying" + template + " to " + actual); + java.nio.file.Files.copy(template.toPath(), actual.toPath(), java.nio.file.StandardCopyOption.COPY_ATTRIBUTES); + } catch (IOException e1) { + LogManager.getLogger().error("Error copying " + template + " to " + actual, e1); + MinecraftServer.getServer().safeShutdown(); + com.destroystokyo.paper.util.SneakyThrow.sneaky(e1); + } + } + } + } + // Paper end + + public ChunkRegionLoader(File file, DataFixer datafixer) { + // Paper start + this.actualWorld = file; + if (com.destroystokyo.paper.PaperConfig.useVersionedWorld) { + this.useAltWorld = true; + String name = file.getName(); + File container = file.getParentFile().getParentFile(); + if (name.equals("DIM-1") || name.equals("DIM1")) { + container = container.getParentFile(); + } + this.templateWorld = new File(container, name); + File region = new File(file, "region"); + if (!region.exists()) { + region.mkdirs(); + } + } else { + this.useAltWorld = false; + this.templateWorld = file; + } + // Paper end + this.c = file; + this.d = datafixer; + } + + @Nullable + private NBTTagCompound a(GeneratorAccess generatoraccess, int i, int j) throws IOException { + return this.a(generatoraccess.o().getDimensionManager(), generatoraccess.h(), i, j, generatoraccess); // CraftBukkit + } + + // CraftBukkit start + private boolean check(ChunkProviderServer cps, int x, int z) throws IOException { + if (cps != null) { + //com.google.common.base.Preconditions.checkState(org.bukkit.Bukkit.isPrimaryThread(), "primary thread"); // Paper - this is safe + if (cps.isLoaded(x, z)) { + return true; + } + } + + if (this.chunkExists(x, z)) { + NBTTagCompound nbt = RegionFileCache.read(this.c, x, z); + if (nbt != null) { + NBTTagCompound level = nbt.getCompound("Level"); + if (level.getBoolean("TerrainPopulated")) { + return true; + } + + ChunkStatus status = ChunkStatus.a(level.getString("Status")); + if (status != null && status.a(ChunkStatus.DECORATED)) { + return true; + } + } + } + + return false; + } + + public boolean chunkExists(int x, int z) { + // Paper start + if (this.saveMap.containsKey(ChunkCoordIntPair.asLong(x, z))) { + return true; + } + copyIfNeeded(x, z); + return RegionFileCache.chunkExists(this.actualWorld, x, z); + // Paper end + } + + @Nullable + private NBTTagCompound a(DimensionManager dimensionmanager, @Nullable PersistentCollection persistentcollection, int i, int j, @Nullable GeneratorAccess generatoraccess) throws IOException { + // CraftBukkit start + if (blacklist.contains(ChunkCoordIntPair.a(i, j))) { + return null; + } + // CraftBukkit end + copyIfNeeded(i, j); // Paper + + NBTTagCompound nbttagcompound = SupplierUtils.getIfExists(this.saveMap.get(ChunkCoordIntPair.asLong(i, j))); // Spigot // Paper + + if (nbttagcompound != null) { + return nbttagcompound; + } else { + NBTTagCompound nbttagcompound1 = RegionFileCache.read(this.c, i, j); + + if (nbttagcompound1 == null) { + return null; + } else { + int k = nbttagcompound1.hasKeyOfType("DataVersion", 99) ? nbttagcompound1.getInt("DataVersion") : -1; + // CraftBukkit start + if (k < 1466) { + NBTTagCompound level = nbttagcompound1.getCompound("Level"); + if (level.getBoolean("TerrainPopulated") && !level.getBoolean("LightPopulated")) { + ChunkProviderServer cps = (generatoraccess == null) ? null : ((WorldServer) generatoraccess).getChunkProvider(); + if (check(cps, i - 1, j) && check(cps, i - 1, j - 1) && check(cps, i, j - 1)) { + level.setBoolean("LightPopulated", true); + } + } + } + // CraftBukkit end + + if (k < 1493) { + nbttagcompound1 = GameProfileSerializer.a(this.d, DataFixTypes.CHUNK, nbttagcompound1, k, 1493); + if (nbttagcompound1.getCompound("Level").getBoolean("hasLegacyStructureData")) { + this.a(dimensionmanager, persistentcollection); + nbttagcompound1 = this.e.a(nbttagcompound1); + } + } + + nbttagcompound1 = GameProfileSerializer.a(this.d, DataFixTypes.CHUNK, nbttagcompound1, Math.max(1493, k)); + if (k < 1631) { + nbttagcompound1.setInt("DataVersion", 1631); + this.a(new ChunkCoordIntPair(i, j), new SupplierUtils.ValueSupplier<>(nbttagcompound1)); // Spigot + } + + return nbttagcompound1; + } + } + } + + public void a(DimensionManager dimensionmanager, @Nullable PersistentCollection persistentcollection) { + if (this.e == null) { + this.e = PersistentStructureLegacy.a(dimensionmanager, persistentcollection); + } + + } + + // Paper start + private long queuedSaves = 0; + private final java.util.concurrent.atomic.AtomicLong processedSaves = new java.util.concurrent.atomic.AtomicLong(0L); + public int getQueueSize() { return queue.size(); } + public long getQueuedSaves() { return queuedSaves; } + public long getProcessedSaves() { return processedSaves.longValue(); } + // Paper end + + // CraftBukkit start - Add async variant, provide compatibility + @Nullable + public Chunk a(GeneratorAccess generatoraccess, int i, int j, Consumer consumer) throws IOException { + generatoraccess.getMinecraftWorld().timings.syncChunkLoadDataTimer.startTiming(); // Spigot + Object[] data = loadChunk(generatoraccess, i, j, consumer); + generatoraccess.getMinecraftWorld().timings.syncChunkLoadDataTimer.stopTiming(); // Spigot + if (data != null) { + Chunk chunk = (Chunk) data[0]; + NBTTagCompound nbttagcompound = (NBTTagCompound) data[1]; + consumer.accept(chunk); + this.loadEntities(nbttagcompound.getCompound("Level"), chunk); + return chunk; + } + + return null; + } + + public Object[] loadChunk(GeneratorAccess generatoraccess, int i, int j, Consumer consumer) throws IOException { + // CraftBukkit end + NBTTagCompound nbttagcompound = this.a(generatoraccess, i, j); + + if (nbttagcompound == null) { + return null; + } else { + /* + Chunk chunk = this.a(generatoraccess, i, j, nbttagcompound); + + if (chunk != null) { + consumer.accept(chunk); + this.loadEntities(nbttagcompound.getCompound("Level"), chunk); + } + + return chunk; + */ + + return this.a(generatoraccess, i, j, nbttagcompound); + } + } + + @Nullable + public ProtoChunk b(GeneratorAccess generatoraccess, int i, int j, Consumer consumer) throws IOException { + NBTTagCompound nbttagcompound; + + try { + nbttagcompound = this.a(generatoraccess, i, j); + } catch (ReportedException reportedexception) { + if (reportedexception.getCause() instanceof IOException) { + throw (IOException) reportedexception.getCause(); + } + + throw reportedexception; + } + + if (nbttagcompound == null) { + return null; + } else { + ProtoChunk protochunk = this.b(generatoraccess, i, j, nbttagcompound); + + if (protochunk != null) { + consumer.accept(protochunk); + } + + return protochunk; + } + } + + @Nullable + protected Object[] a(GeneratorAccess generatoraccess, int i, int j, NBTTagCompound nbttagcompound) { // CraftBukkit - return Chunk -> Object[] + if (nbttagcompound.hasKeyOfType("Level", 10) && nbttagcompound.getCompound("Level").hasKeyOfType("Status", 8)) { + ChunkStatus.Type chunkstatus_type = this.a(nbttagcompound); + + if (chunkstatus_type != ChunkStatus.Type.LEVELCHUNK) { + return null; + } else { + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Level"); + + if (!nbttagcompound1.hasKeyOfType("Sections", 9)) { + ChunkRegionLoader.a.error("Chunk file at {},{} is missing block data, skipping", i, j); + return null; + } else { + Chunk chunk = this.a(generatoraccess, nbttagcompound1); + + if (!chunk.a(i, j)) { + ChunkRegionLoader.a.error("Chunk file at {},{} is in the wrong location; relocating. (Expected {}, {}, got {}, {})", i, j, i, j, chunk.locX, chunk.locZ); + nbttagcompound1.setInt("xPos", i); + nbttagcompound1.setInt("zPos", j); + + // CraftBukkit start - Have to move tile entities since we don't load them at this stage + NBTTagList tileEntities = nbttagcompound.getCompound("Level").getList("TileEntities", 10); + if (tileEntities != null) { + for (int te = 0; te < tileEntities.size(); te++) { + NBTTagCompound tileEntity = (NBTTagCompound) tileEntities.get(te); + int x = tileEntity.getInt("x") - chunk.locX * 16; + int z = tileEntity.getInt("z") - chunk.locZ * 16; + tileEntity.setInt("x", i * 16 + x); + tileEntity.setInt("z", j * 16 + z); + } + } + // CraftBukkit end + chunk = this.a(generatoraccess, nbttagcompound1); + } + + // CraftBukkit start + Object[] data = new Object[2]; + data[0] = chunk; + data[1] = nbttagcompound; + return data; + // CraftBukkit end + } + } + } else { + ChunkRegionLoader.a.error("Chunk file at {},{} is missing level data, skipping", i, j); + return null; + } + } + + @Nullable + protected ProtoChunk b(GeneratorAccess generatoraccess, int i, int j, NBTTagCompound nbttagcompound) { + if (nbttagcompound.hasKeyOfType("Level", 10) && nbttagcompound.getCompound("Level").hasKeyOfType("Status", 8)) { + ChunkStatus.Type chunkstatus_type = this.a(nbttagcompound); + + if (chunkstatus_type == ChunkStatus.Type.LEVELCHUNK) { + return new ProtoChunkExtension((IChunkAccess) this.a(generatoraccess, i, j, nbttagcompound)[0]); // CraftBukkit - fix up access + } else { + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Level"); + + return this.b(generatoraccess, nbttagcompound1); + } + } else { + ChunkRegionLoader.a.error("Chunk file at {},{} is missing level data, skipping", i, j); + return null; + } + } + + // Spigot start + public void saveChunk(World world, IChunkAccess ichunkaccess) throws IOException, ExceptionWorldConflict { + saveChunk(world, ichunkaccess, false); // Ideally we shouldn't use this, but easier than decompile errors + } + + public void saveChunk(World world, IChunkAccess ichunkaccess, boolean unloaded) throws IOException, ExceptionWorldConflict { + if (ichunkaccess.i().d() == ChunkStatus.Type.PROTOCHUNK) { return; } // Paper - don't save proto chunks + // Spigot end + world.checkSession(); + + try { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound.setInt("DataVersion", 1631); + ChunkCoordIntPair chunkcoordintpair = ichunkaccess.getPos(); + + nbttagcompound.set("Level", nbttagcompound1); + // Spigot start + Supplier completion; + final long worldTime = world.getTime(); + final boolean worldHasSkyLight = world.worldProvider.g(); + if (ichunkaccess.i().d() == ChunkStatus.Type.LEVELCHUNK) { + final Chunk chunk = (Chunk) ichunkaccess; + saveEntities(nbttagcompound1, chunk, world); + completion = new Supplier() { + public NBTTagCompound get() { + saveBody(chunk, world, nbttagcompound1, worldTime, worldHasSkyLight); + return nbttagcompound; + } + }; + } else { + /* // Paper start - we will never invoke this in an unsafe way + NBTTagCompound nbttagcompound2 = this.a(world, chunkcoordintpair.x, chunkcoordintpair.z); + + if (nbttagcompound2 != null && this.a(nbttagcompound2) == ChunkStatus.Type.LEVELCHUNK) { + return; + }*/ // Paper end + + completion = new Supplier() { + public NBTTagCompound get() { + a((ProtoChunk) ichunkaccess, world, nbttagcompound1, worldTime, worldHasSkyLight); + return nbttagcompound; + } + }; + } + + this.a(chunkcoordintpair, SupplierUtils.createUnivaluedSupplier(completion, unloaded)); // Paper - Remove save queue target size + // Spigot end + } catch (Exception exception) { + ChunkRegionLoader.a.error("Failed to save chunk", exception); + } + + } + + protected void a(ChunkCoordIntPair chunkcoordintpair, Supplier nbttagcompound) { // Spigot + this.saveMap.put(chunkcoordintpair.asLong(), nbttagcompound); // Paper + queue.add(new QueuedChunk(chunkcoordintpair, nbttagcompound)); // Paper - Chunk queue improvements + queuedSaves++; // Paper + FileIOThread.a().a(this); + } + + public boolean a() { + // CraftBukkit start + return this.processSaveQueueEntry(false); + } + + private boolean processSaveQueueEntry(boolean logCompletion) { + // Paper start - Chunk queue improvements + QueuedChunk chunk = queue.poll(); + if (chunk == null) { + // Paper - end + if (logCompletion) { // CraftBukkit + ChunkRegionLoader.a.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", this.c.getName()); + } + + return false; + } else { + // Paper start + if (chunk.onSave != null) { + chunk.onSave.run(); + return true; + } + // Paper end + ChunkCoordIntPair chunkcoordintpair = chunk.coords; // Paper - Chunk queue improvements + Supplier nbttagcompound = chunk.compoundSupplier; // Spigot // Paper + processedSaves.incrementAndGet(); // Paper + + if (nbttagcompound == null) { + return true; + } else { + try { + // CraftBukkit start + RegionFileCache.write(this.c, chunkcoordintpair.x, chunkcoordintpair.z, SupplierUtils.getIfExists(nbttagcompound)); // Spigot + + // Paper start remove from map only if this was the latest version of the chunk + synchronized (this.saveMap) { + long k = chunkcoordintpair.asLong(); + // This will not equal if a newer version is still pending - wait until newest is saved to remove + if (this.saveMap.get(k) == chunk.compoundSupplier) { + this.saveMap.remove(k); + } + } + // Paper end + /* + NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) dataoutputstream); + dataoutputstream.close(); + */ + // CraftBukkit end + if (this.e != null) { + this.e.a(chunkcoordintpair.a()); + } + } catch (Exception exception) { + ChunkRegionLoader.a.error("Failed to save chunk", exception); + } + + return true; + } + } + } + + private ChunkStatus.Type a(@Nullable NBTTagCompound nbttagcompound) { + if (nbttagcompound != null) { + ChunkStatus chunkstatus = ChunkStatus.a(nbttagcompound.getCompound("Level").getString("Status")); + + if (chunkstatus != null) { + return chunkstatus.d(); + } + } + + return ChunkStatus.Type.PROTOCHUNK; + } + + public void b() { + try { + // this.f = true; // CraftBukkit + + while (true) { + if (this.processSaveQueueEntry(true)) { // CraftBukkit + continue; + } + break; // CraftBukkit - Fix infinite loop when saving chunks + } + } finally { + // this.f = false; // CraftBukkit + } + + } + + private void a(ProtoChunk protochunk, World world, NBTTagCompound nbttagcompound, long worldTime, boolean worldHasSkyLight) { // Spigot + int i = protochunk.getPos().x; + int j = protochunk.getPos().z; + + nbttagcompound.setInt("xPos", i); + nbttagcompound.setInt("zPos", j); + nbttagcompound.setLong("LastUpdate", worldTime); // Spigot + nbttagcompound.setLong("InhabitedTime", protochunk.m()); + nbttagcompound.setString("Status", protochunk.i().b()); + ChunkConverter chunkconverter = protochunk.v(); + + if (!chunkconverter.a()) { + nbttagcompound.set("UpgradeData", chunkconverter.b()); + } + + ChunkSection[] achunksection = protochunk.getSections(); + NBTTagList nbttaglist = this.a(world, achunksection, worldHasSkyLight); // Spigot + + nbttagcompound.set("Sections", nbttaglist); + BiomeBase[] abiomebase = protochunk.getBiomeIndex(); + int[] aint = abiomebase != null ? new int[abiomebase.length] : new int[0]; + + if (abiomebase != null) { + for (int k = 0; k < abiomebase.length; ++k) { + aint[k] = IRegistry.BIOME.a(abiomebase[k]); // CraftBukkit - decompile error + } + } + + nbttagcompound.setIntArray("Biomes", aint); + NBTTagList nbttaglist1 = new NBTTagList(); + Iterator iterator = protochunk.s().iterator(); + + NBTTagCompound nbttagcompound1; + + while (iterator.hasNext()) { + nbttagcompound1 = (NBTTagCompound) iterator.next(); + nbttaglist1.add((NBTBase) nbttagcompound1); + } + + nbttagcompound.set("Entities", nbttaglist1); + NBTTagList nbttaglist2 = new NBTTagList(); + Iterator iterator1 = protochunk.q().iterator(); + + while (iterator1.hasNext()) { + BlockPosition blockposition = (BlockPosition) iterator1.next(); + TileEntity tileentity = protochunk.getTileEntity(blockposition); + + if (tileentity != null) { + NBTTagCompound nbttagcompound2 = new NBTTagCompound(); + + tileentity.save(nbttagcompound2); + nbttaglist2.add((NBTBase) nbttagcompound2); + } else { + nbttaglist2.add((NBTBase) protochunk.g(blockposition)); + } + } + + nbttagcompound.set("TileEntities", nbttaglist2); + nbttagcompound.set("Lights", a(protochunk.p())); + nbttagcompound.set("PostProcessing", a(protochunk.u())); + nbttagcompound.set("ToBeTicked", protochunk.k().a()); + nbttagcompound.set("LiquidsToBeTicked", protochunk.l().a()); + nbttagcompound1 = new NBTTagCompound(); + Iterator iterator2 = protochunk.t().iterator(); + + while (iterator2.hasNext()) { + HeightMap.Type heightmap_type = (HeightMap.Type) iterator2.next(); + + nbttagcompound1.set(heightmap_type.b(), new NBTTagLongArray(protochunk.b(heightmap_type).b())); + } + + nbttagcompound.set("Heightmaps", nbttagcompound1); + NBTTagCompound nbttagcompound3 = new NBTTagCompound(); + WorldGenStage.Features[] aworldgenstage_features = WorldGenStage.Features.values(); + int l = aworldgenstage_features.length; + + for (int i1 = 0; i1 < l; ++i1) { + WorldGenStage.Features worldgenstage_features = aworldgenstage_features[i1]; + + nbttagcompound3.setByteArray(worldgenstage_features.toString(), protochunk.a(worldgenstage_features).toByteArray()); + } + + nbttagcompound.set("CarvingMasks", nbttagcompound3); + nbttagcompound.set("Structures", this.a(i, j, protochunk.e(), protochunk.f())); + } + + private void saveBody(Chunk chunk, World world, NBTTagCompound nbttagcompound, long worldTime, boolean worldHasSkyLight) { // Spigot + nbttagcompound.setInt("xPos", chunk.locX); + nbttagcompound.setInt("zPos", chunk.locZ); + nbttagcompound.setLong("LastUpdate", worldTime); // Spigot + nbttagcompound.setLong("InhabitedTime", chunk.m()); + nbttagcompound.setString("Status", chunk.i().b()); + ChunkConverter chunkconverter = chunk.F(); + + if (!chunkconverter.a()) { + nbttagcompound.set("UpgradeData", chunkconverter.b()); + } + + ChunkSection[] achunksection = chunk.getSections(); + NBTTagList nbttaglist = this.a(world, achunksection, worldHasSkyLight); // Spigot + + nbttagcompound.set("Sections", nbttaglist); + BiomeBase[] abiomebase = chunk.getBiomeIndex(); + int[] aint = new int[abiomebase.length]; + + for (int i = 0; i < abiomebase.length; ++i) { + aint[i] = IRegistry.BIOME.a(abiomebase[i]); // CraftBukkit - decompile error + } + + nbttagcompound.setIntArray("Biomes", aint); + + // Spigot start - End this method here and split off entity saving to another method + } + + private void saveEntities(NBTTagCompound nbttagcompound, Chunk chunk, World world) { + // Spigot end + chunk.f(false); + NBTTagList nbttaglist1 = new NBTTagList(); + + Iterator iterator; + + java.util.List toUpdate = new java.util.ArrayList<>(); // Paper + for (int j = 0; j < chunk.getEntitySlices().length; ++j) { + iterator = chunk.getEntitySlices()[j].iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + // Paper start + if ((int)Math.floor(entity.locX) >> 4 != chunk.locX || (int)Math.floor(entity.locZ) >> 4 != chunk.locZ) { + LogManager.getLogger().warn(entity + " is not in this chunk, skipping save. This a bug fix to a vanilla bug. Do not report this to PaperMC please."); + toUpdate.add(entity); + continue; + } + if (entity.dead) { + continue; + } + // Paper end + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + if (entity.d(nbttagcompound1)) { + chunk.f(true); + nbttaglist1.add((NBTBase) nbttagcompound1); + } + } + } + // Paper start - move entities to the correct chunk + for (Entity entity : toUpdate) { + world.entityJoinedWorld(entity, false); + } + // Paper end + + nbttagcompound.set("Entities", nbttaglist1); + NBTTagList nbttaglist2 = new NBTTagList(); + + iterator = chunk.t().iterator(); + + while (iterator.hasNext()) { + BlockPosition blockposition = (BlockPosition) iterator.next(); + TileEntity tileentity = chunk.getTileEntity(blockposition); + NBTTagCompound nbttagcompound2; + + if (tileentity != null) { + nbttagcompound2 = new NBTTagCompound(); + tileentity.save(nbttagcompound2); + nbttagcompound2.setBoolean("keepPacked", false); + nbttaglist2.add((NBTBase) nbttagcompound2); + } else { + nbttagcompound2 = chunk.g(blockposition); + if (nbttagcompound2 != null) { + nbttagcompound2.setBoolean("keepPacked", true); + nbttaglist2.add((NBTBase) nbttagcompound2); + } + } + } + + nbttagcompound.set("TileEntities", nbttaglist2); + if (world.getBlockTickList() instanceof TickListServer) { + nbttagcompound.set("TileTicks", ((TickListServer) world.getBlockTickList()).a(chunk)); + } + + if (world.getFluidTickList() instanceof TickListServer) { + nbttagcompound.set("LiquidTicks", ((TickListServer) world.getFluidTickList()).a(chunk)); + } + + nbttagcompound.set("PostProcessing", a(chunk.G())); + if (chunk.k() instanceof ProtoChunkTickList) { + nbttagcompound.set("ToBeTicked", ((ProtoChunkTickList) chunk.k()).a()); + } + + if (chunk.l() instanceof ProtoChunkTickList) { + nbttagcompound.set("LiquidsToBeTicked", ((ProtoChunkTickList) chunk.l()).a()); + } + + NBTTagCompound nbttagcompound3 = new NBTTagCompound(); + Iterator iterator1 = chunk.A().iterator(); + + while (iterator1.hasNext()) { + HeightMap.Type heightmap_type = (HeightMap.Type) iterator1.next(); + + if (heightmap_type.c() == HeightMap.Use.LIVE_WORLD) { + nbttagcompound3.set(heightmap_type.b(), new NBTTagLongArray(chunk.b(heightmap_type).b())); + } + } + + nbttagcompound.set("Heightmaps", nbttagcompound3); + nbttagcompound.set("Structures", this.a(chunk.locX, chunk.locZ, chunk.e(), chunk.f())); + } + + private Chunk a(GeneratorAccess generatoraccess, NBTTagCompound nbttagcompound) { + int i = nbttagcompound.getInt("xPos"); + int j = nbttagcompound.getInt("zPos"); + BiomeBase[] abiomebase = new BiomeBase[256]; + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + if (nbttagcompound.hasKeyOfType("Biomes", 11)) { + int[] aint = nbttagcompound.getIntArray("Biomes"); + + for (int k = 0; k < aint.length; ++k) { + abiomebase[k] = (BiomeBase) IRegistry.BIOME.fromId(aint[k]); + if (abiomebase[k] == null) { + abiomebase[k] = generatoraccess.getChunkProvider().getChunkGenerator().getWorldChunkManager().getBiome(blockposition_mutableblockposition.c((k & 15) + (i << 4), 0, (k >> 4 & 15) + (j << 4)), Biomes.PLAINS); + } + } + } else { + for (int l = 0; l < abiomebase.length; ++l) { + abiomebase[l] = generatoraccess.getChunkProvider().getChunkGenerator().getWorldChunkManager().getBiome(blockposition_mutableblockposition.c((l & 15) + (i << 4), 0, (l >> 4 & 15) + (j << 4)), Biomes.PLAINS); + } + } + + ChunkConverter chunkconverter = nbttagcompound.hasKeyOfType("UpgradeData", 10) ? new ChunkConverter(nbttagcompound.getCompound("UpgradeData")) : ChunkConverter.a; + ProtoChunkTickList protochunkticklist = new ProtoChunkTickList<>((block) -> { + return block.getBlockData().isAir(); + }, IRegistry.BLOCK::getKey, IRegistry.BLOCK::getOrDefault, new ChunkCoordIntPair(i, j)); + ProtoChunkTickList protochunkticklist1 = new ProtoChunkTickList<>((fluidtype) -> { + return fluidtype == FluidTypes.EMPTY; + }, IRegistry.FLUID::getKey, IRegistry.FLUID::getOrDefault, new ChunkCoordIntPair(i, j)); + long i1 = nbttagcompound.getLong("InhabitedTime"); + Chunk chunk = new Chunk(generatoraccess.getMinecraftWorld(), i, j, abiomebase, chunkconverter, protochunkticklist, protochunkticklist1, i1); + + chunk.c(nbttagcompound.getString("Status")); + NBTTagList nbttaglist = nbttagcompound.getList("Sections", 10); + + chunk.a(this.a((IWorldReader) generatoraccess, nbttaglist)); + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Heightmaps"); + HeightMap.Type[] aheightmap_type = HeightMap.Type.values(); + int j1 = aheightmap_type.length; + + int k1; + + for (k1 = 0; k1 < j1; ++k1) { + HeightMap.Type heightmap_type = aheightmap_type[k1]; + + if (heightmap_type.c() == HeightMap.Use.LIVE_WORLD) { + String s = heightmap_type.b(); + + if (nbttagcompound1.hasKeyOfType(s, 12)) { + chunk.a(heightmap_type, nbttagcompound1.o(s)); + } else { + chunk.b(heightmap_type).a(); + } + } + } + + NBTTagCompound nbttagcompound2 = nbttagcompound.getCompound("Structures"); + + chunk.a(this.c(generatoraccess, nbttagcompound2)); + chunk.b(this.b(nbttagcompound2)); + NBTTagList nbttaglist1 = nbttagcompound.getList("PostProcessing", 9); + + for (k1 = 0; k1 < nbttaglist1.size(); ++k1) { + NBTTagList nbttaglist2 = nbttaglist1.f(k1); + + for (int l1 = 0; l1 < nbttaglist2.size(); ++l1) { + chunk.a(nbttaglist2.g(l1), k1); + } + } + + protochunkticklist.a(nbttagcompound.getList("ToBeTicked", 9)); + protochunkticklist1.a(nbttagcompound.getList("LiquidsToBeTicked", 9)); + if (nbttagcompound.getBoolean("shouldSave")) { + chunk.a(true); + } + + return chunk; + } + + public void loadEntities(NBTTagCompound nbttagcompound, Chunk chunk) { + NBTTagList nbttaglist = nbttagcompound.getList("Entities", 10); + World world = chunk.getWorld(); + world.timings.chunkLoadLevelTimer.startTiming(); // Spigot + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.getCompound(i); + + a(nbttagcompound1, world, chunk); + chunk.f(true); + } + + NBTTagList nbttaglist1 = nbttagcompound.getList("TileEntities", 10); + + for (int j = 0; j < nbttaglist1.size(); ++j) { + NBTTagCompound nbttagcompound2 = nbttaglist1.getCompound(j); + boolean flag = nbttagcompound2.getBoolean("keepPacked"); + + if (flag) { + chunk.a(nbttagcompound2); + } else { + TileEntity tileentity = TileEntity.create(nbttagcompound2); + + if (tileentity != null) { + chunk.a(tileentity); + } + } + } + + if (nbttagcompound.hasKeyOfType("TileTicks", 9) && world.getBlockTickList() instanceof TickListServer) { + ((TickListServer) world.getBlockTickList()).a(nbttagcompound.getList("TileTicks", 10)); + } + + if (nbttagcompound.hasKeyOfType("LiquidTicks", 9) && world.getFluidTickList() instanceof TickListServer) { + ((TickListServer) world.getFluidTickList()).a(nbttagcompound.getList("LiquidTicks", 10)); + } + world.timings.chunkLoadLevelTimer.stopTiming(); // Spigot + + } + + private ProtoChunk b(GeneratorAccess generatoraccess, NBTTagCompound nbttagcompound) { + int i = nbttagcompound.getInt("xPos"); + int j = nbttagcompound.getInt("zPos"); + BiomeBase[] abiomebase = new BiomeBase[256]; + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + if (nbttagcompound.hasKeyOfType("Biomes", 11)) { + int[] aint = nbttagcompound.getIntArray("Biomes"); + + for (int k = 0; k < aint.length; ++k) { + abiomebase[k] = (BiomeBase) IRegistry.BIOME.fromId(aint[k]); + if (abiomebase[k] == null) { + abiomebase[k] = generatoraccess.getChunkProvider().getChunkGenerator().getWorldChunkManager().getBiome(blockposition_mutableblockposition.c((k & 15) + (i << 4), 0, (k >> 4 & 15) + (j << 4)), Biomes.PLAINS); + } + } + } else { + for (int l = 0; l < abiomebase.length; ++l) { + abiomebase[l] = generatoraccess.getChunkProvider().getChunkGenerator().getWorldChunkManager().getBiome(blockposition_mutableblockposition.c((l & 15) + (i << 4), 0, (l >> 4 & 15) + (j << 4)), Biomes.PLAINS); + } + } + + ChunkConverter chunkconverter = nbttagcompound.hasKeyOfType("UpgradeData", 10) ? new ChunkConverter(nbttagcompound.getCompound("UpgradeData")) : ChunkConverter.a; + ProtoChunk protochunk = new ProtoChunk(i, j, chunkconverter, generatoraccess); // Paper - Anti-Xray + + protochunk.a(abiomebase); + protochunk.b(nbttagcompound.getLong("InhabitedTime")); + protochunk.c(nbttagcompound.getString("Status")); + NBTTagList nbttaglist = nbttagcompound.getList("Sections", 10); + + protochunk.a(this.a((IWorldReader) generatoraccess, nbttaglist)); + NBTTagList nbttaglist1 = nbttagcompound.getList("Entities", 10); + + for (int i1 = 0; i1 < nbttaglist1.size(); ++i1) { + protochunk.b(nbttaglist1.getCompound(i1)); + } + + NBTTagList nbttaglist2 = nbttagcompound.getList("TileEntities", 10); + + for (int j1 = 0; j1 < nbttaglist2.size(); ++j1) { + NBTTagCompound nbttagcompound1 = nbttaglist2.getCompound(j1); + + protochunk.a(nbttagcompound1); + } + + NBTTagList nbttaglist3 = nbttagcompound.getList("Lights", 9); + + for (int k1 = 0; k1 < nbttaglist3.size(); ++k1) { + NBTTagList nbttaglist4 = nbttaglist3.f(k1); + + for (int l1 = 0; l1 < nbttaglist4.size(); ++l1) { + protochunk.a(nbttaglist4.g(l1), k1); + } + } + + NBTTagList nbttaglist5 = nbttagcompound.getList("PostProcessing", 9); + + for (int i2 = 0; i2 < nbttaglist5.size(); ++i2) { + NBTTagList nbttaglist6 = nbttaglist5.f(i2); + + for (int j2 = 0; j2 < nbttaglist6.size(); ++j2) { + protochunk.b(nbttaglist6.g(j2), i2); + } + } + + protochunk.k().a(nbttagcompound.getList("ToBeTicked", 9)); + protochunk.l().a(nbttagcompound.getList("LiquidsToBeTicked", 9)); + NBTTagCompound nbttagcompound2 = nbttagcompound.getCompound("Heightmaps"); + Iterator iterator = nbttagcompound2.getKeys().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + protochunk.a(HeightMap.Type.a(s), nbttagcompound2.o(s)); + } + + NBTTagCompound nbttagcompound3 = nbttagcompound.getCompound("Structures"); + + protochunk.a(this.c(generatoraccess, nbttagcompound3)); + protochunk.b(this.b(nbttagcompound3)); + NBTTagCompound nbttagcompound4 = nbttagcompound.getCompound("CarvingMasks"); + Iterator iterator1 = nbttagcompound4.getKeys().iterator(); + + while (iterator1.hasNext()) { + String s1 = (String) iterator1.next(); + WorldGenStage.Features worldgenstage_features = WorldGenStage.Features.valueOf(s1); + + protochunk.a(worldgenstage_features, BitSet.valueOf(nbttagcompound4.getByteArray(s1))); + } + + return protochunk; + } + + private NBTTagList a(World world, ChunkSection[] achunksection, boolean worldHasSkyLight) { // Spigot + NBTTagList nbttaglist = new NBTTagList(); + boolean flag = worldHasSkyLight; // Spigot + ChunkSection[] achunksection1 = achunksection; + int i = achunksection.length; + + for (int j = 0; j < i; ++j) { + ChunkSection chunksection = achunksection1[j]; + + if (chunksection != Chunk.a) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setByte("Y", (byte) (chunksection.getYPosition() >> 4 & 255)); + chunksection.getBlocks().b(nbttagcompound, "Palette", "BlockStates"); + nbttagcompound.setByteArray("BlockLight", chunksection.getEmittedLightArray().asBytes()); + if (flag) { + nbttagcompound.setByteArray("SkyLight", chunksection.getSkyLightArray().asBytes()); + } else { + nbttagcompound.setByteArray("SkyLight", new byte[chunksection.getEmittedLightArray().asBytes().length]); + } + + nbttaglist.add((NBTBase) nbttagcompound); + } + } + + return nbttaglist; + } + + private ChunkSection[] a(IWorldReader iworldreader, NBTTagList nbttaglist) { + boolean flag = true; + ChunkSection[] achunksection = new ChunkSection[16]; + boolean flag1 = iworldreader.o().g(); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist.getCompound(i); + byte b0 = nbttagcompound.getByte("Y"); + ChunkSection chunksection = new ChunkSection(b0 << 4, flag1, null, iworldreader, false); // Paper - Anti-Xray + + chunksection.getBlocks().a(nbttagcompound, "Palette", "BlockStates"); + chunksection.a(new NibbleArray(nbttagcompound.getByteArray("BlockLight"))); + if (flag1) { + chunksection.b(new NibbleArray(nbttagcompound.getByteArray("SkyLight"))); + } + + chunksection.recalcBlockCounts(); + achunksection[b0] = chunksection; + } + + return achunksection; + } + + private NBTTagCompound a(int i, int j, Map map, Map map1) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + Iterator iterator = map.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + + nbttagcompound1.set((String) entry.getKey(), ((StructureStart) entry.getValue()).a(i, j)); + } + + nbttagcompound.set("Starts", nbttagcompound1); + NBTTagCompound nbttagcompound2 = new NBTTagCompound(); + Iterator iterator1 = map1.entrySet().iterator(); + + while (iterator1.hasNext()) { + Entry entry1 = (Entry) iterator1.next(); + + nbttagcompound2.set((String) entry1.getKey(), new NBTTagLongArray((LongSet) entry1.getValue())); + } + + nbttagcompound.set("References", nbttagcompound2); + return nbttagcompound; + } + + private Map c(GeneratorAccess generatoraccess, NBTTagCompound nbttagcompound) { + Map map = Maps.newHashMap(); + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Starts"); + Iterator iterator = nbttagcompound1.getKeys().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + map.put(s, WorldGenFactory.a(nbttagcompound1.getCompound(s), generatoraccess)); + } + + return map; + } + + private Map b(NBTTagCompound nbttagcompound) { + Map map = Maps.newHashMap(); + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("References"); + Iterator iterator = nbttagcompound1.getKeys().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + map.put(s, new LongOpenHashSet(nbttagcompound1.o(s))); + } + + return map; + } + + public static NBTTagList a(ShortList[] ashortlist) { + NBTTagList nbttaglist = new NBTTagList(); + ShortList[] ashortlist1 = ashortlist; + int i = ashortlist.length; + + for (int j = 0; j < i; ++j) { + ShortList shortlist = ashortlist1[j]; + NBTTagList nbttaglist1 = new NBTTagList(); + + if (shortlist != null) { + ShortListIterator shortlistiterator = shortlist.iterator(); + + while (shortlistiterator.hasNext()) { + Short oshort = (Short) shortlistiterator.next(); + + nbttaglist1.add((NBTBase) (new NBTTagShort(oshort))); + } + } + + nbttaglist.add((NBTBase) nbttaglist1); + } + + return nbttaglist; + } + + @Nullable + private static Entity a(NBTTagCompound nbttagcompound, World world, Function function) { + Entity entity = a(nbttagcompound, world); + + if (entity == null) { + return null; + } else { + entity = (Entity) function.apply(entity); + if (entity != null && nbttagcompound.hasKeyOfType("Passengers", 9)) { + NBTTagList nbttaglist = nbttagcompound.getList("Passengers", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + Entity entity1 = a(nbttaglist.getCompound(i), world, function); + + if (entity1 != null) { + entity1.a(entity, true); + } + } + } + + return entity; + } + } + + @Nullable + public static Entity a(NBTTagCompound nbttagcompound, World world, Chunk chunk) { + return a(nbttagcompound, world, (entity) -> { + chunk.a(entity); + return entity; + }); + } + + @Nullable + // CraftBukkit start + public static Entity a(NBTTagCompound nbttagcompound, World world, double d0, double d1, double d2, boolean flag) { + return spawnEntity(nbttagcompound, world, d0, d1, d2, flag, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT); + } + + public static Entity spawnEntity(NBTTagCompound nbttagcompound, World world, double d0, double d1, double d2, boolean flag, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) { + // CraftBukkit end + return a(nbttagcompound, world, (entity) -> { + entity.setPositionRotation(d0, d1, d2, entity.yaw, entity.pitch); + return flag && !world.addEntity(entity, spawnReason) ? null : entity; + }); + } + + @Nullable + // CraftBukkit start + public static Entity a(NBTTagCompound nbttagcompound, World world, boolean flag) { + return spawnEntity(nbttagcompound, world, flag, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT); + } + + public static Entity spawnEntity(NBTTagCompound nbttagcompound, World world, boolean flag, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) { + // CraftBukkit end + return a(nbttagcompound, world, (entity) -> { + return flag && !world.addEntity(entity, spawnReason) ? null : entity; // CraftBukkit + }); + } + + @Nullable + protected static Entity a(NBTTagCompound nbttagcompound, World world) { + try { + return EntityTypes.a(nbttagcompound, world); + } catch (RuntimeException runtimeexception) { + ChunkRegionLoader.a.warn("Exception loading entity: ", runtimeexception); + return null; + } + } + + // CraftBukkit start + public static void a(Entity entity, GeneratorAccess generatoraccess) { + a(entity, generatoraccess, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT); + } + + public static void a(Entity entity, GeneratorAccess generatoraccess, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) { + if (!entity.valid && generatoraccess.addEntity(entity, reason) && entity.isVehicle()) { // Paper + // CraftBukkit end + Iterator iterator = entity.bP().iterator(); + + while (iterator.hasNext()) { + Entity entity1 = (Entity) iterator.next(); + + a(entity1, generatoraccess); + } + } + + } + + public boolean a(ChunkCoordIntPair chunkcoordintpair, DimensionManager dimensionmanager, PersistentCollection persistentcollection) { + boolean flag = false; + + try { + this.a(dimensionmanager, persistentcollection, chunkcoordintpair.x, chunkcoordintpair.z, null); // CraftBukkit + + while (this.a()) { + flag = true; + } + } catch (IOException ioexception) { + ; + } + + return flag; + } +} diff --git a/src/main/java/net/minecraft/server/ChunkSection.java b/src/main/java/net/minecraft/server/ChunkSection.java new file mode 100644 index 000000000000..9c6844d44114 --- /dev/null +++ b/src/main/java/net/minecraft/server/ChunkSection.java @@ -0,0 +1,163 @@ +package net.minecraft.server; + +public class ChunkSection { + + public static final DataPalette GLOBAL_PALETTE = new DataPaletteGlobal<>(Block.REGISTRY_ID, Blocks.AIR.getBlockData()); + private final int yPos; + private int nonEmptyBlockCount; + private int tickingBlockCount; + private int e; + final DataPaletteBlock blockIds; // Paper - package + private NibbleArray emittedLight; + private NibbleArray skyLight; + + // Paper start - Anti-Xray - Support default constructor + public ChunkSection(int i, boolean flag) { + this(i, flag, null, null, true); + } + // Paper end + + public ChunkSection(int i, boolean flag, IChunkAccess chunk, IWorldReader world, boolean initializeBlocks) { // Paper - Anti-Xray + this.yPos = i; + this.blockIds = new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE, Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData(), world instanceof GeneratorAccess ? ((GeneratorAccess) world).getMinecraftWorld().chunkPacketBlockController.getPredefinedBlockData(world, chunk, this, flag, initializeBlocks) : null, initializeBlocks); // Paper - Anti-Xray - Add predefined block data + this.emittedLight = new NibbleArray(); + if (flag) { + this.skyLight = new NibbleArray(); + } + + // Paper start - Async Chunks - Lock during world gen + if (chunk instanceof ProtoChunk) { + this.blockIds.enableLocks(); + } else { + this.blockIds.disableLocks(); + } + } + void disableLocks() { + this.blockIds.disableLocks(); + } + // Paper end + + public IBlockData getType(int i, int j, int k) { + return (IBlockData) this.blockIds.a(i, j, k); + } + + public Fluid b(int i, int j, int k) { + return ((IBlockData) this.blockIds.a(i, j, k)).s(); + } + + public void setType(int i, int j, int k, IBlockData iblockdata) { + IBlockData iblockdata1 = this.getType(i, j, k); + Fluid fluid = this.b(i, j, k); + Fluid fluid1 = iblockdata.s(); + + if (!iblockdata1.isAir()) { + --this.nonEmptyBlockCount; + if (iblockdata1.t()) { + --this.tickingBlockCount; + } + } + + if (!fluid.e()) { + --this.e; + } + + if (!iblockdata.isAir()) { + ++this.nonEmptyBlockCount; + if (iblockdata.t()) { + ++this.tickingBlockCount; + } + } + + if (!fluid1.e()) { + --this.e; + } + + this.blockIds.setBlock(i, j, k, iblockdata); + } + + public boolean a() { + return this.nonEmptyBlockCount == 0; + } + + public boolean b() { + return this.shouldTick() || this.d(); + } + + public boolean shouldTick() { + return this.tickingBlockCount > 0; + } + + public boolean d() { + return this.e > 0; + } + + public int getYPosition() { + return this.yPos; + } + + public void a(int i, int j, int k, int l) { + this.skyLight.a(i, j, k, l); + } + + public int c(int i, int j, int k) { + return this.skyLight.a(i, j, k); + } + + public void b(int i, int j, int k, int l) { + this.emittedLight.a(i, j, k, l); + } + + public int d(int i, int j, int k) { + return this.emittedLight.a(i, j, k); + } + + public void recalcBlockCounts() { + this.nonEmptyBlockCount = 0; + this.tickingBlockCount = 0; + this.e = 0; + + for (int i = 0; i < 16; ++i) { + for (int j = 0; j < 16; ++j) { + for (int k = 0; k < 16; ++k) { + IBlockData iblockdata = this.getType(i, j, k); + Fluid fluid = this.b(i, j, k); + + if (!iblockdata.isAir()) { + ++this.nonEmptyBlockCount; + if (iblockdata.t()) { + ++this.tickingBlockCount; + } + } + + if (!fluid.e()) { + ++this.nonEmptyBlockCount; + if (fluid.h()) { + ++this.e; + } + } + } + } + } + + } + + public DataPaletteBlock getBlocks() { + return this.blockIds; + } + + public NibbleArray getEmittedLightArray() { + return this.emittedLight; + } + + public NibbleArray getSkyLightArray() { + return this.skyLight; + } + + public void a(NibbleArray nibblearray) { + this.emittedLight = nibblearray; + } + + public void b(NibbleArray nibblearray) { + this.skyLight = nibblearray; + } +} diff --git a/src/main/java/net/minecraft/server/ChunkTaskScheduler.java b/src/main/java/net/minecraft/server/ChunkTaskScheduler.java new file mode 100644 index 000000000000..8f061f5ca302 --- /dev/null +++ b/src/main/java/net/minecraft/server/ChunkTaskScheduler.java @@ -0,0 +1,148 @@ +package net.minecraft.server; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.objects.ObjectIterator; +import java.io.IOException; +import java.util.EnumMap; +import java.util.Map; +import java.util.function.BooleanSupplier; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ChunkTaskScheduler extends Scheduler { + + private static final Logger b = LogManager.getLogger(); + private final World c; private final World getWorld() { return this.c; } // Paper - OBFHELPER + private final ChunkGenerator d; + private final IChunkLoader e; + private final IAsyncTaskHandler f; + protected final Long2ObjectMap.a> progressCache = new ExpiringMap.a>(8192, 5000) { // Paper - protected + protected boolean a(Scheduler.a scheduler_a) { + ProtoChunk protochunk = (ProtoChunk) scheduler_a.a(); + + return !protochunk.ab_() /*&& !protochunk.h()*/; // Paper + } + }; + private final Long2ObjectMap> pendingSchedulers = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(); // Paper + + public ChunkTaskScheduler(int i, World world, ChunkGenerator chunkgenerator, IChunkLoader ichunkloader, IAsyncTaskHandler iasynctaskhandler) { + super("WorldGen", i, ChunkStatus.FINALIZED, () -> { + return new EnumMap(ChunkStatus.class); + }, () -> { + return new EnumMap(ChunkStatus.class); + }); + this.c = world; + this.d = chunkgenerator; + this.e = ichunkloader; + this.f = iasynctaskhandler; + } + + // CraftBukkit start + public void forcePolluteCache(ChunkCoordIntPair chunkcoordintpair) { + this.progressCache.put(chunkcoordintpair.a(), new Scheduler.a(chunkcoordintpair, new ProtoChunk(chunkcoordintpair, ChunkConverter.a, this.getWorld()), ChunkStatus.EMPTY)); // Paper - Anti-Xray + } + // CraftBukkit end + + @Nullable + protected Scheduler.a a(ChunkCoordIntPair chunkcoordintpair, boolean flag) { + IChunkLoader ichunkloader = this.e; + + // Paper start - refactor a lot of this - avoid generating a chunk while holding lock on expiring map + java.util.concurrent.CompletableFuture pending = null; + boolean created = false; + long key = chunkcoordintpair.a(); + synchronized (pendingSchedulers) { + Scheduler.a existing = this.progressCache.get(key); + if (existing != null) { + return existing; + } + pending = this.pendingSchedulers.get(key); + if (pending == null) { + if (!flag) { + return null; + } + created = true; + pending = new java.util.concurrent.CompletableFuture<>(); + pendingSchedulers.put(key, pending); + } + } + if (created) { + java.util.function.Function get = (i) -> { + // Paper end + ProtoChunk protochunk; + + try { + protochunk = this.e.b(this.c, chunkcoordintpair.x, chunkcoordintpair.z, (ichunkaccess) -> { + }); + } catch (ReportedException reportedexception) { + throw reportedexception; + } catch (Exception exception) { + ChunkTaskScheduler.b.error("Couldn't load protochunk", exception); + protochunk = null; + } + + if (protochunk != null) { + protochunk.setLastSaved(this.c.getTime()); + return new Scheduler.a(chunkcoordintpair, protochunk, protochunk.i()); + } else { + return new Scheduler.a(chunkcoordintpair, new ProtoChunk(chunkcoordintpair, ChunkConverter.a, this.getWorld()), ChunkStatus.EMPTY); // Paper - Anti-Xray + } + // Paper start + }; + Scheduler.a scheduler = get.apply(key); + progressCache.put(key, scheduler); + pending.complete(scheduler); + synchronized (pendingSchedulers) { + pendingSchedulers.remove(key); + } + return scheduler; + } + return pending.join(); + // Paper end + } + + protected ProtoChunk a(ChunkCoordIntPair chunkcoordintpair, ChunkStatus chunkstatus, Map map) { + return chunkstatus.a(this.c, this.d, map, chunkcoordintpair.x, chunkcoordintpair.z); + } + + protected Scheduler.a a(ChunkCoordIntPair chunkcoordintpair, Scheduler.a scheduler_a) { + ((ProtoChunk) scheduler_a.a()).a(1); + return scheduler_a; + } + + protected void b(ChunkCoordIntPair chunkcoordintpair, Scheduler.a scheduler_a) { + ((ProtoChunk) scheduler_a.a()).a(-1); + } + + public void a(BooleanSupplier booleansupplier) { + if (true) return; // Paper - we don't save proto chunks, and don't want to block thread + IChunkLoader ichunkloader = this.e; + + synchronized (this.e) { + ObjectIterator objectiterator = this.progressCache.values().iterator(); + + do { + if (!objectiterator.hasNext()) { + return; + } + + Scheduler.a scheduler_a = (Scheduler.a) objectiterator.next(); + ProtoChunk protochunk = (ProtoChunk) scheduler_a.a(); + + if (protochunk.h() && protochunk.i().d() == ChunkStatus.Type.PROTOCHUNK) { + try { + protochunk.setLastSaved(this.c.getTime()); + this.e.saveChunk(this.c, protochunk); + protochunk.a(false); + } catch (IOException ioexception) { + ChunkTaskScheduler.b.error("Couldn't save chunk", ioexception); + } catch (ExceptionWorldConflict exceptionworldconflict) { + ChunkTaskScheduler.b.error("Couldn't save chunk; already in use by another instance of Minecraft?", exceptionworldconflict); + } + } + } while (booleansupplier.getAsBoolean()); + + } + } +} diff --git a/src/main/java/net/minecraft/server/CombatTracker.java b/src/main/java/net/minecraft/server/CombatTracker.java new file mode 100644 index 000000000000..19750ceed198 --- /dev/null +++ b/src/main/java/net/minecraft/server/CombatTracker.java @@ -0,0 +1,200 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; + +public class CombatTracker { + + private final List a = Lists.newArrayList(); + private final EntityLiving b; + private int c; + private int d; + private int e; + private boolean f; + private boolean g; + private String h; + + public CombatTracker(EntityLiving entityliving) { + this.b = entityliving; + } + + public void a() { + this.k(); + if (this.b.z_()) { + Block block = this.b.world.getType(new BlockPosition(this.b.locX, this.b.getBoundingBox().minY, this.b.locZ)).getBlock(); + + if (block == Blocks.LADDER) { + this.h = "ladder"; + } else if (block == Blocks.VINE) { + this.h = "vines"; + } + } else if (this.b.isInWater()) { + this.h = "water"; + } + + } + + public void trackDamage(DamageSource damagesource, float f, float f1) { + this.g(); + this.a(); + CombatEntry combatentry = new CombatEntry(damagesource, this.b.ticksLived, f, f1, this.h, this.b.fallDistance); + + this.a.add(combatentry); + this.c = this.b.ticksLived; + this.g = true; + if (combatentry.f() && !this.f && this.b.isAlive()) { + this.f = true; + this.d = this.b.ticksLived; + this.e = this.d; + this.b.enterCombat(); + } + + } + + public IChatBaseComponent getDeathMessage() { + if (this.a.isEmpty()) { + return new ChatMessage("death.attack.generic", new Object[] { this.b.getScoreboardDisplayName()}); + } else { + CombatEntry combatentry = this.j(); + CombatEntry combatentry1 = (CombatEntry) this.a.get(this.a.size() - 1); + IChatBaseComponent ichatbasecomponent = combatentry1.h(); + Entity entity = combatentry1.a().getEntity(); + Object object; + + if (combatentry != null && combatentry1.a() == DamageSource.FALL) { + IChatBaseComponent ichatbasecomponent1 = combatentry.h(); + + if (combatentry.a() != DamageSource.FALL && combatentry.a() != DamageSource.OUT_OF_WORLD) { + if (ichatbasecomponent1 != null && (ichatbasecomponent == null || !ichatbasecomponent1.equals(ichatbasecomponent))) { + Entity entity1 = combatentry.a().getEntity(); + ItemStack itemstack = entity1 instanceof EntityLiving ? ((EntityLiving) entity1).getItemInMainHand() : ItemStack.a; + + if (!itemstack.isEmpty() && itemstack.hasName()) { + object = new ChatMessage("death.fell.assist.item", new Object[] { this.b.getScoreboardDisplayName(), ichatbasecomponent1, itemstack.A()}); + } else { + object = new ChatMessage("death.fell.assist", new Object[] { this.b.getScoreboardDisplayName(), ichatbasecomponent1}); + } + } else if (ichatbasecomponent != null) { + ItemStack itemstack1 = entity instanceof EntityLiving ? ((EntityLiving) entity).getItemInMainHand() : ItemStack.a; + + if (!itemstack1.isEmpty() && itemstack1.hasName()) { + object = new ChatMessage("death.fell.finish.item", new Object[] { this.b.getScoreboardDisplayName(), ichatbasecomponent, itemstack1.A()}); + } else { + object = new ChatMessage("death.fell.finish", new Object[] { this.b.getScoreboardDisplayName(), ichatbasecomponent}); + } + } else { + object = new ChatMessage("death.fell.killer", new Object[] { this.b.getScoreboardDisplayName()}); + } + } else { + object = new ChatMessage("death.fell.accident." + this.a(combatentry), new Object[] { this.b.getScoreboardDisplayName()}); + } + } else { + object = combatentry1.a().getLocalizedDeathMessage(this.b); + } + + return (IChatBaseComponent) object; + } + } + + @Nullable + public EntityLiving c() { + EntityLiving entityliving = null; + EntityHuman entityhuman = null; + float f = 0.0F; + float f1 = 0.0F; + Iterator iterator = this.a.iterator(); + + while (iterator.hasNext()) { + CombatEntry combatentry = (CombatEntry) iterator.next(); + + if (combatentry.a().getEntity() instanceof EntityHuman && (entityhuman == null || combatentry.c() > f1)) { + f1 = combatentry.c(); + entityhuman = (EntityHuman) combatentry.a().getEntity(); + } + + if (combatentry.a().getEntity() instanceof EntityLiving && (entityliving == null || combatentry.c() > f)) { + f = combatentry.c(); + entityliving = (EntityLiving) combatentry.a().getEntity(); + } + } + + if (entityhuman != null && f1 >= f / 3.0F) { + return entityhuman; + } else { + return entityliving; + } + } + + @Nullable + private CombatEntry j() { + CombatEntry combatentry = null; + CombatEntry combatentry1 = null; + float f = 0.0F; + float f1 = 0.0F; + + for (int i = 0; i < this.a.size(); ++i) { + CombatEntry combatentry2 = (CombatEntry) this.a.get(i); + CombatEntry combatentry3 = i > 0 ? (CombatEntry) this.a.get(i - 1) : null; + + if ((combatentry2.a() == DamageSource.FALL || combatentry2.a() == DamageSource.OUT_OF_WORLD) && combatentry2.j() > 0.0F && (combatentry == null || combatentry2.j() > f1)) { + if (i > 0) { + combatentry = combatentry3; + } else { + combatentry = combatentry2; + } + + f1 = combatentry2.j(); + } + + if (combatentry2.g() != null && (combatentry1 == null || combatentry2.c() > f)) { + combatentry1 = combatentry2; + f = combatentry2.c(); + } + } + + if (f1 > 5.0F && combatentry != null) { + return combatentry; + } else if (f > 5.0F && combatentry1 != null) { + return combatentry1; + } else { + return null; + } + } + + private String a(CombatEntry combatentry) { + return combatentry.g() == null ? "generic" : combatentry.g(); + } + + public int f() { + return this.f ? this.b.ticksLived - this.d : this.e - this.d; + } + + private void k() { + this.h = null; + } + + public void reset() { this.g(); } // Paper - OBFHELPER + public void g() { + int i = this.f ? 300 : 100; + + if (this.g && (!this.b.isAlive() || this.b.ticksLived - this.c > i)) { + boolean flag = this.f; + + this.g = false; + this.f = false; + this.e = this.b.ticksLived; + if (flag) { + this.b.exitCombat(); + } + + this.a.clear(); + } + + } + + public EntityLiving h() { + return this.b; + } +} diff --git a/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java b/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java new file mode 100644 index 000000000000..a245df1dceba --- /dev/null +++ b/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java @@ -0,0 +1,194 @@ +package net.minecraft.server; + +import com.mojang.brigadier.context.CommandContext; +import java.text.SimpleDateFormat; +import java.util.Date; +import javax.annotation.Nullable; +import org.bukkit.command.CommandSender; + +public abstract class CommandBlockListenerAbstract implements ICommandListener { + + private static final SimpleDateFormat a = new SimpleDateFormat("HH:mm:ss"); + private long b = -1L; + private boolean c = true; + private int d; + private boolean e = true; + private IChatBaseComponent f; + private String g = ""; + private IChatBaseComponent h = new ChatComponentText("@"); + // CraftBukkit start + @Override + public abstract CommandSender getBukkitSender(CommandListenerWrapper wrapper); + // CraftBukkit end + + public CommandBlockListenerAbstract() {} + + public int i() { + return this.d; + } + + public void a(int i) { + this.d = i; + } + + public IChatBaseComponent j() { + return (IChatBaseComponent) (this.f == null ? new ChatComponentText("") : this.f); + } + + public NBTTagCompound a(NBTTagCompound nbttagcompound) { + nbttagcompound.setString("Command", this.g); + nbttagcompound.setInt("SuccessCount", this.d); + nbttagcompound.setString("CustomName", IChatBaseComponent.ChatSerializer.a(this.h)); + nbttagcompound.setBoolean("TrackOutput", this.e); + if (this.f != null && this.e) { + nbttagcompound.setString("LastOutput", IChatBaseComponent.ChatSerializer.a(this.f)); + } + + nbttagcompound.setBoolean("UpdateLastExecution", this.c); + if (this.c && this.b > 0L) { + nbttagcompound.setLong("LastExecution", this.b); + } + + return nbttagcompound; + } + + public void b(NBTTagCompound nbttagcompound) { + this.g = nbttagcompound.getString("Command"); + this.d = nbttagcompound.getInt("SuccessCount"); + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.h = MCUtil.getBaseComponentFromNbt("CustomName", nbttagcompound); // Paper - Catch ParseException + } + + if (nbttagcompound.hasKeyOfType("TrackOutput", 1)) { + this.e = nbttagcompound.getBoolean("TrackOutput"); + } + + if (nbttagcompound.hasKeyOfType("LastOutput", 8) && this.e) { + try { + this.f = IChatBaseComponent.ChatSerializer.a(nbttagcompound.getString("LastOutput")); + } catch (Throwable throwable) { + this.f = new ChatComponentText(throwable.getMessage()); + } + } else { + this.f = null; + } + + if (nbttagcompound.hasKey("UpdateLastExecution")) { + this.c = nbttagcompound.getBoolean("UpdateLastExecution"); + } + + if (this.c && nbttagcompound.hasKey("LastExecution")) { + this.b = nbttagcompound.getLong("LastExecution"); + } else { + this.b = -1L; + } + + } + + public void setCommand(String s) { + this.g = s; + this.d = 0; + } + + public String getCommand() { + return this.g; + } + + public boolean a(World world) { + if (!world.isClientSide && world.getTime() != this.b) { + if ("Searge".equalsIgnoreCase(this.g)) { + this.f = new ChatComponentText("#itzlipofutzli"); + this.d = 1; + return true; + } else { + this.d = 0; + MinecraftServer minecraftserver = this.d().getMinecraftServer(); + + if (minecraftserver != null && minecraftserver.D() && minecraftserver.getEnableCommandBlock() && !UtilColor.b(this.g)) { + try { + this.f = null; + this.d = minecraftserver.getCommandDispatcher().dispatchServerCommand(this.getWrapper(), this.g); // CraftBukkit + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Executing command block"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Command to be executed"); + + crashreportsystemdetails.a("Command", this::getCommand); + crashreportsystemdetails.a("Name", () -> { + return this.getName().getString(); + }); + throw new ReportedException(crashreport); + } + } + + if (this.c) { + this.b = world.getTime(); + } else { + this.b = -1L; + } + + return true; + } + } else { + return false; + } + } + + public IChatBaseComponent getName() { + return this.h; + } + + public void setName(IChatBaseComponent ichatbasecomponent) { + // CraftBukkit start + if (ichatbasecomponent == null) { + ichatbasecomponent = new ChatComponentText("@"); + } + // CraftBukkit end + this.h = ichatbasecomponent; + } + + public void sendMessage(IChatBaseComponent ichatbasecomponent) { + if (this.e) { + this.f = (new ChatComponentText("[" + CommandBlockListenerAbstract.a.format(new Date()) + "] ")).addSibling(ichatbasecomponent); + this.e(); + } + + } + + public abstract WorldServer d(); + + public abstract void e(); + + public void c(@Nullable IChatBaseComponent ichatbasecomponent) { + this.f = ichatbasecomponent; + } + + public void a(boolean flag) { + this.e = flag; + } + + public boolean a(EntityHuman entityhuman) { + if (!entityhuman.isCreativeAndOp()) { + return false; + } else { + if (entityhuman.getWorld().isClientSide) { + entityhuman.a(this); + } + + return true; + } + } + + public abstract CommandListenerWrapper getWrapper(); + + public boolean a() { + return this.d().getGameRules().getBoolean("sendCommandFeedback") && this.e; + } + + public boolean b() { + return this.e; + } + + public boolean B_() { + return this.d().getGameRules().getBoolean("commandBlockOutput"); + } +} diff --git a/src/main/java/net/minecraft/server/CommandDebug.java b/src/main/java/net/minecraft/server/CommandDebug.java new file mode 100644 index 000000000000..5dd29094b475 --- /dev/null +++ b/src/main/java/net/minecraft/server/CommandDebug.java @@ -0,0 +1,149 @@ +package net.minecraft.server; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class CommandDebug { + + private static final Logger a = LogManager.getLogger(); + private static final SimpleCommandExceptionType b = new SimpleCommandExceptionType(new ChatMessage("commands.debug.notRunning", new Object[0])); + private static final SimpleCommandExceptionType c = new SimpleCommandExceptionType(new ChatMessage("commands.debug.alreadyRunning", new Object[0])); + + public static void a(com.mojang.brigadier.CommandDispatcher com_mojang_brigadier_commanddispatcher) { + com_mojang_brigadier_commanddispatcher.register((LiteralArgumentBuilder) ((LiteralArgumentBuilder) ((LiteralArgumentBuilder) CommandDispatcher.a("debug").requires((commandlistenerwrapper) -> { + return commandlistenerwrapper.hasPermission(3); + })).then(CommandDispatcher.a("start").executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource()); + }))).then(CommandDispatcher.a("stop").executes((commandcontext) -> { + return b((CommandListenerWrapper) commandcontext.getSource()); + }))); + } + + private static int a(CommandListenerWrapper commandlistenerwrapper) throws CommandSyntaxException { + // CraftBukkit start - only allow use when enabled (so that no blank profile results occur) + if (!commandlistenerwrapper.getServer().methodProfiler.ENABLED) { + commandlistenerwrapper.sendFailureMessage(new ChatComponentText("Vanilla debug profiling is disabled.")); + commandlistenerwrapper.sendFailureMessage(new ChatComponentText("To enable, restart the server with `-DenableDebugMethodProfiler=true' before `-jar'.")); + commandlistenerwrapper.sendFailureMessage(new ChatComponentText("Use `/timings' for plugin timings.")); + return 0; + } + // CraftBukkit end + MinecraftServer minecraftserver = commandlistenerwrapper.getServer(); + MethodProfiler methodprofiler = minecraftserver.methodProfiler; + + if (methodprofiler.a()) { + throw CommandDebug.c.create(); + } else { + minecraftserver.ai(); + commandlistenerwrapper.sendMessage(new ChatMessage("commands.debug.started", new Object[] { "Started the debug profiler. Type '/debug stop' to stop it."}), true); + return 0; + } + } + + private static int b(CommandListenerWrapper commandlistenerwrapper) throws CommandSyntaxException { + // CraftBukkit start - only allow use when enabled (so that no blank profile results occur) + if (!commandlistenerwrapper.getServer().methodProfiler.ENABLED) { + commandlistenerwrapper.sendFailureMessage(new ChatComponentText("Vanilla debug profiling is disabled.")); + commandlistenerwrapper.sendFailureMessage(new ChatComponentText("To enable, restart the server with `-DenableDebugMethodProfiler=true' before `-jar'.")); + commandlistenerwrapper.sendFailureMessage(new ChatComponentText("Use `/timings' for plugin timings.")); + return 0; + } + // CraftBukkit end + MinecraftServer minecraftserver = commandlistenerwrapper.getServer(); + MethodProfiler methodprofiler = minecraftserver.methodProfiler; + + if (!methodprofiler.a()) { + throw CommandDebug.b.create(); + } else { + long i = SystemUtils.getMonotonicNanos(); + int j = minecraftserver.ah(); + long k = i - methodprofiler.c(); + int l = j - methodprofiler.d(); + File file = new File(minecraftserver.c("debug"), "profile-results-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + ".txt"); + + file.getParentFile().mkdirs(); + OutputStreamWriter outputstreamwriter = null; + + try { + outputstreamwriter = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8); + outputstreamwriter.write(a(k, l, methodprofiler)); + } catch (Throwable throwable) { + CommandDebug.a.error("Could not save profiler results to {}", file, throwable); + } finally { + IOUtils.closeQuietly(outputstreamwriter); + } + + methodprofiler.b(); + float f = (float) k / 1.0E9F; + float f1 = (float) l / f; + + commandlistenerwrapper.sendMessage(new ChatMessage("commands.debug.stopped", new Object[] { String.format(Locale.ROOT, "%.2f", f), l, String.format("%.2f", f1)}), true); + return MathHelper.d(f1); + } + } + + private static String a(long i, int j, MethodProfiler methodprofiler) { + StringBuilder stringbuilder = new StringBuilder(); + + stringbuilder.append("---- Minecraft Profiler Results ----\n"); + stringbuilder.append("// "); + stringbuilder.append(a()); + stringbuilder.append("\n\n"); + stringbuilder.append("Time span: ").append(i).append(" ms\n"); + stringbuilder.append("Tick span: ").append(j).append(" ticks\n"); + stringbuilder.append("// This is approximately ").append(String.format(Locale.ROOT, "%.2f", (float) j / ((float) i / 1.0E9F))).append(" ticks per second. It should be ").append(20).append(" ticks per second\n\n"); + stringbuilder.append("--- BEGIN PROFILE DUMP ---\n\n"); + a(0, "root", stringbuilder, methodprofiler); + stringbuilder.append("--- END PROFILE DUMP ---\n\n"); + return stringbuilder.toString(); + } + + private static void a(int i, String s, StringBuilder stringbuilder, MethodProfiler methodprofiler) { + List list = methodprofiler.b(s); + + if (list != null && list.size() >= 3) { + for (int j = 1; j < list.size(); ++j) { + MethodProfiler.ProfilerInfo methodprofiler_profilerinfo = (MethodProfiler.ProfilerInfo) list.get(j); + + stringbuilder.append(String.format("[%02d] ", i)); + + for (int k = 0; k < i; ++k) { + stringbuilder.append("| "); + } + + stringbuilder.append(methodprofiler_profilerinfo.c).append(" - ").append(String.format(Locale.ROOT, "%.2f", methodprofiler_profilerinfo.a)).append("%/").append(String.format(Locale.ROOT, "%.2f", methodprofiler_profilerinfo.b)).append("%\n"); + if (!"unspecified".equals(methodprofiler_profilerinfo.c)) { + try { + a(i + 1, s + "." + methodprofiler_profilerinfo.c, stringbuilder, methodprofiler); + } catch (Exception exception) { + stringbuilder.append("[[ EXCEPTION ").append(exception).append(" ]]"); + } + } + } + + } + } + + private static String a() { + String[] astring = new String[] { "Shiny numbers!", "Am I not running fast enough? :(", "I'm working as hard as I can!", "Will I ever be good enough for you? :(", "Speedy. Zoooooom!", "Hello world", "40% better than a crash report.", "Now with extra numbers", "Now with less numbers", "Now with the same numbers", "You should add flames to things, it makes them go faster!", "Do you feel the need for... optimization?", "*cracks redstone whip*", "Maybe if you treated it better then it'll have more motivation to work faster! Poor server."}; + + try { + return astring[(int) (SystemUtils.getMonotonicNanos() % (long) astring.length)]; + } catch (Throwable throwable) { + return "Witty comment unavailable :("; + } + } +} diff --git a/src/main/java/net/minecraft/server/CommandDispatcher.java b/src/main/java/net/minecraft/server/CommandDispatcher.java new file mode 100644 index 000000000000..b1bd08e5c91b --- /dev/null +++ b/src/main/java/net/minecraft/server/CommandDispatcher.java @@ -0,0 +1,351 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import com.google.common.io.Files; +import com.google.gson.GsonBuilder; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.tree.RootCommandNode; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.function.Predicate; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import com.google.common.base.Joiner; +import java.util.LinkedHashSet; +import org.bukkit.craftbukkit.command.VanillaCommandWrapper; +import org.bukkit.event.player.PlayerCommandSendEvent; +import org.bukkit.event.server.ServerCommandEvent; +// CraftBukkit end + +public class CommandDispatcher { + + private static final Logger a = LogManager.getLogger(); + private final com.mojang.brigadier.CommandDispatcher b = new com.mojang.brigadier.CommandDispatcher(); + + // CraftBukkit start + public final CommandDispatcher init(boolean flag) { + CommandAdvancement.a(this.b); + CommandExecute.a(this.b); + CommandBossBar.a(this.b); + CommandClear.a(this.b); + CommandClone.a(this.b); + CommandData.a(this.b); + CommandDatapack.a(this.b); + CommandDebug.a(this.b); + CommandGamemodeDefault.a(this.b); + CommandDifficulty.a(this.b); + CommandEffect.a(this.b); + CommandMe.a(this.b); + CommandEnchant.a(this.b); + CommandXp.a(this.b); + CommandFill.a(this.b); + CommandFunction.a(this.b); + CommandGamemode.a(this.b); + CommandGamerule.a(this.b); + CommandGive.a(this.b); + CommandHelp.a(this.b); + CommandKick.a(this.b); + CommandKill.a(this.b); + CommandList.a(this.b); + CommandLocate.a(this.b); + CommandTell.a(this.b); + CommandParticle.a(this.b); + CommandPlaySound.a(this.b); + CommandPublish.a(this.b); + CommandReload.a(this.b); + CommandRecipe.a(this.b); + CommandReplaceItem.a(this.b); + CommandSay.a(this.b); + CommandScoreboard.a(this.b); + CommandSeed.a(this.b); + CommandSetBlock.a(this.b); + CommandSpawnpoint.a(this.b); + CommandSetWorldSpawn.a(this.b); + CommandSpreadPlayers.a(this.b); + CommandStopSound.a(this.b); + CommandSummon.a(this.b); + CommandTag.a(this.b); + CommandTeam.a(this.b); + CommandTeleport.a(this.b); + CommandTellRaw.a(this.b); + CommandForceload.a(this.b); + CommandTime.a(this.b); + CommandTitle.a(this.b); + CommandTrigger.a(this.b); + CommandWeather.a(this.b); + CommandWorldBorder.a(this.b); + if (flag) { + CommandBanIp.a(this.b); + CommandBanList.a(this.b); + CommandBan.a(this.b); + CommandDeop.a(this.b); + CommandOp.a(this.b); + CommandPardon.a(this.b); + CommandPardonIP.a(this.b); + CommandSaveAll.a(this.b); + CommandSaveOff.a(this.b); + CommandSaveOn.a(this.b); + CommandIdleTimeout.a(this.b); + CommandStop.a(this.b); + CommandWhitelist.a(this.b); + } + + this.b.findAmbiguities((commandnode, commandnode1, commandnode2, collection) -> { + // CommandDispatcher.a.warn("Ambiguity between arguments {} and {} with inputs: {}", this.b.getPath(commandnode1), this.b.getPath(commandnode2), collection); // CraftBukkit + }); + return this; + } + + public CommandDispatcher() { + // CraftBukkit end + this.b.setConsumer((commandcontext, flag1, i) -> { + ((CommandListenerWrapper) commandcontext.getSource()).a(commandcontext, flag1, i); + }); + } + + public void a(File file) { + try { + Files.write((new GsonBuilder()).setPrettyPrinting().create().toJson(ArgumentRegistry.a(this.b, (CommandNode) this.b.getRoot())), file, StandardCharsets.UTF_8); + } catch (IOException ioexception) { + CommandDispatcher.a.error("Couldn't write out command tree!", ioexception); + } + + } + + // CraftBukkit start + public int dispatchServerCommand(CommandListenerWrapper sender, String command) { + Joiner joiner = Joiner.on(" "); + if (command.startsWith("/")) { + command = command.substring(1); + } + + ServerCommandEvent event = new ServerCommandEvent(sender.getBukkitSender(), command); + org.bukkit.Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) { + return 0; + } + command = event.getCommand(); + + String[] args = command.split(" "); + + String cmd = args[0]; + if (cmd.startsWith("minecraft:")) cmd = cmd.substring("minecraft:".length()); + if (cmd.startsWith("bukkit:")) cmd = cmd.substring("bukkit:".length()); + + // Block disallowed commands + if (cmd.equalsIgnoreCase("stop") || cmd.equalsIgnoreCase("kick") || cmd.equalsIgnoreCase("op") + || cmd.equalsIgnoreCase("deop") || cmd.equalsIgnoreCase("ban") || cmd.equalsIgnoreCase("ban-ip") + || cmd.equalsIgnoreCase("pardon") || cmd.equalsIgnoreCase("pardon-ip") || cmd.equalsIgnoreCase("reload")) { + return 0; + } + + // Handle vanilla commands; + if (sender.getWorld().getServer().getCommandBlockOverride(args[0])) { + args[0] = "minecraft:" + args[0]; + } + + return this.a(sender, joiner.join(args)); + } + + public int a(CommandListenerWrapper commandlistenerwrapper, String s) { + return this.a(commandlistenerwrapper, s, s); + } + + public int a(CommandListenerWrapper commandlistenerwrapper, String s, String label) { + // CraftBukkit end + StringReader stringreader = new StringReader(s); + + if (stringreader.canRead() && stringreader.peek() == '/') { + stringreader.skip(); + } + + commandlistenerwrapper.getServer().methodProfiler.enter(s); + + byte b0; + + try { + ChatComponentText chatcomponenttext; + + try { + int i = this.b.execute(stringreader, commandlistenerwrapper); + + return i; + } catch (CommandException commandexception) { + commandlistenerwrapper.sendFailureMessage(commandexception.a()); + b0 = 0; + return b0; + } catch (CommandSyntaxException commandsyntaxexception) { + commandlistenerwrapper.sendFailureMessage(ChatComponentUtils.a(commandsyntaxexception.getRawMessage())); + if (commandsyntaxexception.getInput() != null && commandsyntaxexception.getCursor() >= 0) { + int j = Math.min(commandsyntaxexception.getInput().length(), commandsyntaxexception.getCursor()); + + chatcomponenttext = new ChatComponentText(""); + if (j > 10) { + chatcomponenttext.a("..."); + } + + chatcomponenttext.a(commandsyntaxexception.getInput().substring(Math.max(0, j - 10), j)); + if (j < commandsyntaxexception.getInput().length()) { + ChatComponentText chatcomponenttext1 = new ChatComponentText(commandsyntaxexception.getInput().substring(j)); + + chatcomponenttext1.getChatModifier().setColor(EnumChatFormat.RED); + chatcomponenttext1.getChatModifier().setUnderline(Boolean.valueOf(true)); + chatcomponenttext.addSibling(chatcomponenttext1); + } + + ChatMessage chatmessage = new ChatMessage("command.context.here", new Object[0]); + + chatmessage.getChatModifier().setItalic(Boolean.valueOf(true)); + chatmessage.getChatModifier().setColor(EnumChatFormat.RED); + chatcomponenttext.addSibling(chatmessage); + chatcomponenttext.getChatModifier().setColor(EnumChatFormat.GRAY); + chatcomponenttext.getChatModifier().setChatClickable(new ChatClickable(ChatClickable.EnumClickAction.SUGGEST_COMMAND, label)); // CraftBukkit + commandlistenerwrapper.sendFailureMessage(chatcomponenttext); + } + + b0 = 0; + } catch (Exception exception) { + ChatMessage chatmessage1 = new ChatMessage("command.failed", new Object[0]); + + chatcomponenttext = new ChatComponentText(exception.getMessage() == null ? exception.getClass().getName() : exception.getMessage()); + if (CommandDispatcher.a.isDebugEnabled()) { + StackTraceElement[] astacktraceelement = exception.getStackTrace(); + + for (int k = 0; k < Math.min(astacktraceelement.length, 3); ++k) { + chatcomponenttext.a("\n\n" + astacktraceelement[k].getMethodName() + "\n " + astacktraceelement[k].getFileName() + ":" + astacktraceelement[k].getLineNumber()); + } + } + + chatmessage1.getChatModifier().setChatHoverable(new ChatHoverable(ChatHoverable.EnumHoverAction.SHOW_TEXT, chatcomponenttext)); + commandlistenerwrapper.sendFailureMessage(chatmessage1); + byte b1 = 0; + + return b1; + } + } finally { + commandlistenerwrapper.getServer().methodProfiler.exit(); + } + + return b0; + } + + public void a(EntityPlayer entityplayer) { + if ( org.spigotmc.SpigotConfig.tabComplete < 0 ) return; // Spigot + // CraftBukkit start + // Register Vanilla commands into builtRoot as before + Map, CommandNode> map = Maps.newIdentityHashMap(); // Use identity to prevent aliasing issues + RootCommandNode vanillaRoot = new RootCommandNode(); + + RootCommandNode vanilla = entityplayer.server.vanillaCommandDispatcher.a().getRoot(); + map.put(vanilla, vanillaRoot); + this.a(vanilla, vanillaRoot, entityplayer.getCommandListener(), (Map) map); + + // Now build the global commands in a second pass + RootCommandNode rootcommandnode = new RootCommandNode(); + + map.put(this.b.getRoot(), rootcommandnode); + this.a(this.b.getRoot(), rootcommandnode, entityplayer.getCommandListener(), (Map) map); + + Collection bukkit = new LinkedHashSet<>(); + for (CommandNode node : rootcommandnode.getChildren()) { + bukkit.add(node.getName()); + } + + PlayerCommandSendEvent event = new PlayerCommandSendEvent(entityplayer.getBukkitEntity(), new LinkedHashSet<>(bukkit)); + event.getPlayer().getServer().getPluginManager().callEvent(event); + + // Remove labels that were removed during the event + for (String orig : bukkit) { + if (!event.getCommands().contains(orig)) { + rootcommandnode.removeCommand(orig); + } + } + // CraftBukkit end + entityplayer.playerConnection.sendPacket(new PacketPlayOutCommands(rootcommandnode)); + } + + private void a(CommandNode commandnode, CommandNode commandnode1, CommandListenerWrapper commandlistenerwrapper, Map, CommandNode> map) { + Iterator iterator = commandnode.getChildren().iterator(); + + while (iterator.hasNext()) { + CommandNode commandnode2 = (CommandNode) iterator.next(); + if ( !org.spigotmc.SpigotConfig.sendNamespaced && commandnode2.getName().contains( ":" ) ) continue; // Spigot + + if (commandnode2.canUse(commandlistenerwrapper)) { + ArgumentBuilder argumentbuilder = commandnode2.createBuilder(); // CraftBukkit - decompile error + + argumentbuilder.requires((icompletionprovider) -> { + return true; + }); + if (argumentbuilder.getCommand() != null) { + argumentbuilder.executes((commandcontext) -> { + return 0; + }); + } + + if (argumentbuilder instanceof RequiredArgumentBuilder) { + RequiredArgumentBuilder requiredargumentbuilder = (RequiredArgumentBuilder) argumentbuilder; + + if (requiredargumentbuilder.getSuggestionsProvider() != null) { + requiredargumentbuilder.suggests(CompletionProviders.b(requiredargumentbuilder.getSuggestionsProvider())); + } + } + + if (argumentbuilder.getRedirect() != null) { + argumentbuilder.redirect((CommandNode) map.get(argumentbuilder.getRedirect())); + } + + CommandNode commandnode3 = argumentbuilder.build(); // CraftBukkit - decompile error + + map.put(commandnode2, commandnode3); + commandnode1.addChild(commandnode3); + if (!commandnode2.getChildren().isEmpty()) { + this.a(commandnode2, commandnode3, commandlistenerwrapper, map); + } + } + } + + } + + public static LiteralArgumentBuilder a(String s) { + return LiteralArgumentBuilder.literal(s); + } + + public static RequiredArgumentBuilder a(String s, ArgumentType argumenttype) { + return RequiredArgumentBuilder.argument(s, argumenttype); + } + + public static Predicate a(CommandDispatcher.a commanddispatcher_a) { + return (s) -> { + try { + commanddispatcher_a.parse(new StringReader(s)); + return true; + } catch (CommandSyntaxException commandsyntaxexception) { + return false; + } + }; + } + + public com.mojang.brigadier.CommandDispatcher a() { + return this.b; + } + + @FunctionalInterface + public interface a { + + void parse(StringReader stringreader) throws CommandSyntaxException; + } +} diff --git a/src/main/java/net/minecraft/server/CommandEffect.java b/src/main/java/net/minecraft/server/CommandEffect.java new file mode 100644 index 000000000000..290cb03fce37 --- /dev/null +++ b/src/main/java/net/minecraft/server/CommandEffect.java @@ -0,0 +1,131 @@ +package net.minecraft.server; + +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import java.util.Collection; +import java.util.Iterator; +import javax.annotation.Nullable; + +public class CommandEffect { + + private static final SimpleCommandExceptionType a = new SimpleCommandExceptionType(new ChatMessage("commands.effect.give.failed", new Object[0])); + private static final SimpleCommandExceptionType b = new SimpleCommandExceptionType(new ChatMessage("commands.effect.clear.everything.failed", new Object[0])); + private static final SimpleCommandExceptionType c = new SimpleCommandExceptionType(new ChatMessage("commands.effect.clear.specific.failed", new Object[0])); + + public static void a(com.mojang.brigadier.CommandDispatcher com_mojang_brigadier_commanddispatcher) { + com_mojang_brigadier_commanddispatcher.register((LiteralArgumentBuilder) ((LiteralArgumentBuilder) ((LiteralArgumentBuilder) CommandDispatcher.a("effect").requires((commandlistenerwrapper) -> { + return commandlistenerwrapper.hasPermission(2); + })).then(CommandDispatcher.a("clear").then(((RequiredArgumentBuilder) CommandDispatcher.a("targets", (ArgumentType) ArgumentEntity.b()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentEntity.b(commandcontext, "targets")); + })).then(CommandDispatcher.a("effect", (ArgumentType) ArgumentMobEffect.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentEntity.b(commandcontext, "targets"), ArgumentMobEffect.a(commandcontext, "effect")); + }))))).then(CommandDispatcher.a("give").then(CommandDispatcher.a("targets", (ArgumentType) ArgumentEntity.b()).then(((RequiredArgumentBuilder) CommandDispatcher.a("effect", (ArgumentType) ArgumentMobEffect.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentEntity.b(commandcontext, "targets"), ArgumentMobEffect.a(commandcontext, "effect"), (Integer) null, 0, true); + })).then(((RequiredArgumentBuilder) CommandDispatcher.a("seconds", (ArgumentType) IntegerArgumentType.integer(1, 1000000)).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentEntity.b(commandcontext, "targets"), ArgumentMobEffect.a(commandcontext, "effect"), IntegerArgumentType.getInteger(commandcontext, "seconds"), 0, true); + })).then(((RequiredArgumentBuilder) CommandDispatcher.a("amplifier", (ArgumentType) IntegerArgumentType.integer(0, 255)).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentEntity.b(commandcontext, "targets"), ArgumentMobEffect.a(commandcontext, "effect"), IntegerArgumentType.getInteger(commandcontext, "seconds"), IntegerArgumentType.getInteger(commandcontext, "amplifier"), true); + })).then(CommandDispatcher.a("hideParticles", (ArgumentType) BoolArgumentType.bool()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentEntity.b(commandcontext, "targets"), ArgumentMobEffect.a(commandcontext, "effect"), IntegerArgumentType.getInteger(commandcontext, "seconds"), IntegerArgumentType.getInteger(commandcontext, "amplifier"), !BoolArgumentType.getBool(commandcontext, "hideParticles")); + })))))))); + } + + private static int a(CommandListenerWrapper commandlistenerwrapper, Collection collection, MobEffectList mobeffectlist, @Nullable Integer integer, int i, boolean flag) throws CommandSyntaxException { + int j = 0; + int k; + + if (integer != null) { + if (mobeffectlist.isInstant()) { + k = integer; + } else { + k = integer * 20; + } + } else if (mobeffectlist.isInstant()) { + k = 1; + } else { + k = 600; + } + + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + if (entity instanceof EntityLiving) { + MobEffect mobeffect = new MobEffect(mobeffectlist, k, i, false, flag); + + if (((EntityLiving) entity).addEffect(mobeffect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.COMMAND)) { // CraftBukkit + ++j; + } + } + } + + if (j == 0) { + throw CommandEffect.a.create(); + } else { + if (collection.size() == 1) { + commandlistenerwrapper.sendMessage(new ChatMessage("commands.effect.give.success.single", new Object[] { mobeffectlist.d(), ((Entity) collection.iterator().next()).getScoreboardDisplayName(), k / 20}), true); + } else { + commandlistenerwrapper.sendMessage(new ChatMessage("commands.effect.give.success.multiple", new Object[] { mobeffectlist.d(), collection.size(), k / 20}), true); + } + + return j; + } + } + + private static int a(CommandListenerWrapper commandlistenerwrapper, Collection collection) throws CommandSyntaxException { + int i = 0; + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + if (entity instanceof EntityLiving && ((EntityLiving) entity).removeAllEffects(org.bukkit.event.entity.EntityPotionEffectEvent.Cause.COMMAND)) { // CraftBukkit + ++i; + } + } + + if (i == 0) { + throw CommandEffect.b.create(); + } else { + if (collection.size() == 1) { + commandlistenerwrapper.sendMessage(new ChatMessage("commands.effect.clear.everything.success.single", new Object[] { ((Entity) collection.iterator().next()).getScoreboardDisplayName()}), true); + } else { + commandlistenerwrapper.sendMessage(new ChatMessage("commands.effect.clear.everything.success.multiple", new Object[] { collection.size()}), true); + } + + return i; + } + } + + private static int a(CommandListenerWrapper commandlistenerwrapper, Collection collection, MobEffectList mobeffectlist) throws CommandSyntaxException { + int i = 0; + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + if (entity instanceof EntityLiving && ((EntityLiving) entity).removeEffect(mobeffectlist, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.COMMAND)) { // CraftBukkit + ++i; + } + } + + if (i == 0) { + throw CommandEffect.c.create(); + } else { + if (collection.size() == 1) { + commandlistenerwrapper.sendMessage(new ChatMessage("commands.effect.clear.specific.success.single", new Object[] { mobeffectlist.d(), ((Entity) collection.iterator().next()).getScoreboardDisplayName()}), true); + } else { + commandlistenerwrapper.sendMessage(new ChatMessage("commands.effect.clear.specific.success.multiple", new Object[] { mobeffectlist.d(), collection.size()}), true); + } + + return i; + } + } +} diff --git a/src/main/java/net/minecraft/server/CommandForceload.java b/src/main/java/net/minecraft/server/CommandForceload.java new file mode 100644 index 000000000000..f0c30bf54128 --- /dev/null +++ b/src/main/java/net/minecraft/server/CommandForceload.java @@ -0,0 +1,142 @@ +package net.minecraft.server; + +import com.google.common.base.Joiner; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import it.unimi.dsi.fastutil.longs.LongSet; + +public class CommandForceload { + + private static final Dynamic2CommandExceptionType a = new Dynamic2CommandExceptionType((object, object1) -> { + return new ChatMessage("commands.forceload.toobig", new Object[] { object, object1}); + }); + private static final Dynamic2CommandExceptionType b = new Dynamic2CommandExceptionType((object, object1) -> { + return new ChatMessage("commands.forceload.query.failure", new Object[] { object, object1}); + }); + private static final SimpleCommandExceptionType c = new SimpleCommandExceptionType(new ChatMessage("commands.forceload.added.failure", new Object[0])); + private static final SimpleCommandExceptionType d = new SimpleCommandExceptionType(new ChatMessage("commands.forceload.removed.failure", new Object[0])); + + public static void a(com.mojang.brigadier.CommandDispatcher com_mojang_brigadier_commanddispatcher) { + com_mojang_brigadier_commanddispatcher.register((LiteralArgumentBuilder) ((LiteralArgumentBuilder) ((LiteralArgumentBuilder) ((LiteralArgumentBuilder) CommandDispatcher.a("forceload").requires((commandlistenerwrapper) -> { + return commandlistenerwrapper.hasPermission(4); + })).then(CommandDispatcher.a("add").then(((RequiredArgumentBuilder) CommandDispatcher.a("from", (ArgumentType) ArgumentVec2I.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentVec2I.a(commandcontext, "from"), ArgumentVec2I.a(commandcontext, "from"), true); + })).then(CommandDispatcher.a("to", (ArgumentType) ArgumentVec2I.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentVec2I.a(commandcontext, "from"), ArgumentVec2I.a(commandcontext, "to"), true); + }))))).then(((LiteralArgumentBuilder) CommandDispatcher.a("remove").then(((RequiredArgumentBuilder) CommandDispatcher.a("from", (ArgumentType) ArgumentVec2I.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentVec2I.a(commandcontext, "from"), ArgumentVec2I.a(commandcontext, "from"), false); + })).then(CommandDispatcher.a("to", (ArgumentType) ArgumentVec2I.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentVec2I.a(commandcontext, "from"), ArgumentVec2I.a(commandcontext, "to"), false); + })))).then(CommandDispatcher.a("all").executes((commandcontext) -> { + return b((CommandListenerWrapper) commandcontext.getSource()); + })))).then(((LiteralArgumentBuilder) CommandDispatcher.a("query").executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource()); + })).then(CommandDispatcher.a("pos", (ArgumentType) ArgumentVec2I.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentVec2I.a(commandcontext, "pos")); + })))); + } + + private static int a(CommandListenerWrapper commandlistenerwrapper, ArgumentVec2I.a argumentvec2i_a) throws CommandSyntaxException { + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(argumentvec2i_a.a >> 4, argumentvec2i_a.b >> 4); + DimensionManager dimensionmanager = commandlistenerwrapper.getWorld().dimension; // CraftBukkit + boolean flag = commandlistenerwrapper.getServer().getWorldServer(dimensionmanager).isForceLoaded(chunkcoordintpair.x, chunkcoordintpair.z); + + if (flag) { + commandlistenerwrapper.sendMessage(new ChatMessage("commands.forceload.query.success", new Object[] { chunkcoordintpair, dimensionmanager}), false); + return 1; + } else { + throw CommandForceload.b.create(chunkcoordintpair, dimensionmanager); + } + } + + private static int a(CommandListenerWrapper commandlistenerwrapper) { + DimensionManager dimensionmanager = commandlistenerwrapper.getWorld().dimension; // CraftBukkit + LongSet longset = commandlistenerwrapper.getServer().getWorldServer(dimensionmanager).ag(); + int i = longset.size(); + + if (i > 0) { + String s = Joiner.on(", ").join(longset.stream().sorted().map(ChunkCoordIntPair::new).map(ChunkCoordIntPair::toString).iterator()); + + if (i == 1) { + commandlistenerwrapper.sendMessage(new ChatMessage("commands.forceload.list.single", new Object[] { dimensionmanager, s}), false); + } else { + commandlistenerwrapper.sendMessage(new ChatMessage("commands.forceload.list.multiple", new Object[] { i, dimensionmanager, s}), false); + } + } else { + commandlistenerwrapper.sendFailureMessage(new ChatMessage("commands.forceload.added.none", new Object[] { dimensionmanager})); + } + + return i; + } + + private static int b(CommandListenerWrapper commandlistenerwrapper) { + DimensionManager dimensionmanager = commandlistenerwrapper.getWorld().dimension; // CraftBukkit + WorldServer worldserver = commandlistenerwrapper.getServer().getWorldServer(dimensionmanager); + LongSet longset = worldserver.ag(); + + longset.forEach((java.util.function.LongConsumer) (i) -> { // CraftBukkit - decompile error + worldserver.setForceLoaded(ChunkCoordIntPair.a(i), ChunkCoordIntPair.b(i), false); + }); + commandlistenerwrapper.sendMessage(new ChatMessage("commands.forceload.removed.all", new Object[] { dimensionmanager}), true); + return 0; + } + + private static int a(CommandListenerWrapper commandlistenerwrapper, ArgumentVec2I.a argumentvec2i_a, ArgumentVec2I.a argumentvec2i_a1, boolean flag) throws CommandSyntaxException { + int i = Math.min(argumentvec2i_a.a, argumentvec2i_a1.a); + int j = Math.min(argumentvec2i_a.b, argumentvec2i_a1.b); + int k = Math.max(argumentvec2i_a.a, argumentvec2i_a1.a); + int l = Math.max(argumentvec2i_a.b, argumentvec2i_a1.b); + + if (i >= -30000000 && j >= -30000000 && k < 30000000 && l < 30000000) { + int i1 = i >> 4; + int j1 = j >> 4; + int k1 = k >> 4; + int l1 = l >> 4; + long i2 = ((long) (k1 - i1) + 1L) * ((long) (l1 - j1) + 1L); + + if (i2 > 256L) { + throw CommandForceload.a.create(256, i2); + } else { + DimensionManager dimensionmanager = commandlistenerwrapper.getWorld().dimension; // CraftBukkit + WorldServer worldserver = commandlistenerwrapper.getServer().getWorldServer(dimensionmanager); + ChunkCoordIntPair chunkcoordintpair = null; + int j2 = 0; + + for (int k2 = i1; k2 <= k1; ++k2) { + for (int l2 = j1; l2 <= l1; ++l2) { + boolean flag1 = worldserver.setForceLoaded(k2, l2, flag); + + if (flag1) { + ++j2; + if (chunkcoordintpair == null) { + chunkcoordintpair = new ChunkCoordIntPair(k2, l2); + } + } + } + } + + if (j2 == 0) { + throw (flag ? CommandForceload.c : CommandForceload.d).create(); + } else { + if (j2 == 1) { + commandlistenerwrapper.sendMessage(new ChatMessage("commands.forceload." + (flag ? "added" : "removed") + ".single", new Object[] { chunkcoordintpair, dimensionmanager}), true); + } else { + ChunkCoordIntPair chunkcoordintpair1 = new ChunkCoordIntPair(i1, j1); + ChunkCoordIntPair chunkcoordintpair2 = new ChunkCoordIntPair(k1, l1); + + commandlistenerwrapper.sendMessage(new ChatMessage("commands.forceload." + (flag ? "added" : "removed") + ".multiple", new Object[] { j2, dimensionmanager, chunkcoordintpair1, chunkcoordintpair2}), true); + } + + return j2; + } + } + } else { + throw ArgumentPosition.b.create(); + } + } +} diff --git a/src/main/java/net/minecraft/server/CommandGamemode.java b/src/main/java/net/minecraft/server/CommandGamemode.java new file mode 100644 index 000000000000..0af2af091cef --- /dev/null +++ b/src/main/java/net/minecraft/server/CommandGamemode.java @@ -0,0 +1,71 @@ +package net.minecraft.server; + +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + +public class CommandGamemode { + + public static void a(com.mojang.brigadier.CommandDispatcher com_mojang_brigadier_commanddispatcher) { + LiteralArgumentBuilder literalargumentbuilder = (LiteralArgumentBuilder) CommandDispatcher.a("gamemode").requires((commandlistenerwrapper) -> { + return commandlistenerwrapper.hasPermission(2); + }); + EnumGamemode[] aenumgamemode = EnumGamemode.values(); + int i = aenumgamemode.length; + + for (int j = 0; j < i; ++j) { + EnumGamemode enumgamemode = aenumgamemode[j]; + + if (enumgamemode != EnumGamemode.NOT_SET) { + literalargumentbuilder.then(((LiteralArgumentBuilder) CommandDispatcher.a(enumgamemode.b()).executes((commandcontext) -> { + return a(commandcontext, (Collection) Collections.singleton(((CommandListenerWrapper) commandcontext.getSource()).h()), enumgamemode); + })).then(CommandDispatcher.a("target", (ArgumentType) ArgumentEntity.d()).executes((commandcontext) -> { + return a(commandcontext, ArgumentEntity.f(commandcontext, "target"), enumgamemode); + }))); + } + } + + com_mojang_brigadier_commanddispatcher.register(literalargumentbuilder); + } + + private static void a(CommandListenerWrapper commandlistenerwrapper, EntityPlayer entityplayer, EnumGamemode enumgamemode) { + ChatMessage chatmessage = new ChatMessage("gameMode." + enumgamemode.b(), new Object[0]); + + if (commandlistenerwrapper.getEntity() == entityplayer) { + commandlistenerwrapper.sendMessage(new ChatMessage("commands.gamemode.success.self", new Object[] { chatmessage}), true); + } else { + if (commandlistenerwrapper.getWorld().getGameRules().getBoolean("sendCommandFeedback")) { + entityplayer.sendMessage(new ChatMessage("gameMode.changed", new Object[] { chatmessage})); + } + + commandlistenerwrapper.sendMessage(new ChatMessage("commands.gamemode.success.other", new Object[] { entityplayer.getScoreboardDisplayName(), chatmessage}), true); + } + + } + + private static int a(CommandContext commandcontext, Collection collection, EnumGamemode enumgamemode) { + int i = 0; + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + if (entityplayer.playerInteractManager.getGameMode() != enumgamemode) { + entityplayer.a(enumgamemode); + // CraftBukkit start - handle event cancelling the change + if (entityplayer.playerInteractManager.getGameMode() != enumgamemode) { + commandcontext.getSource().sendFailureMessage(new ChatComponentText("Failed to set the gamemode of '" + entityplayer.getName() + "'")); + continue; + } + // CraftBukkit end + a((CommandListenerWrapper) commandcontext.getSource(), entityplayer, enumgamemode); + ++i; + } + } + + return i; + } +} diff --git a/src/main/java/net/minecraft/server/CommandGamerule.java b/src/main/java/net/minecraft/server/CommandGamerule.java new file mode 100644 index 000000000000..6f8d4887a012 --- /dev/null +++ b/src/main/java/net/minecraft/server/CommandGamerule.java @@ -0,0 +1,43 @@ +package net.minecraft.server; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import java.util.Iterator; +import java.util.Map.Entry; + +public class CommandGamerule { + + public static void a(com.mojang.brigadier.CommandDispatcher com_mojang_brigadier_commanddispatcher) { + LiteralArgumentBuilder literalargumentbuilder = (LiteralArgumentBuilder) CommandDispatcher.a("gamerule").requires((commandlistenerwrapper) -> { + return commandlistenerwrapper.hasPermission(2); + }); + Iterator iterator = GameRules.getGameRules().entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + + literalargumentbuilder.then(((LiteralArgumentBuilder) CommandDispatcher.a((String) entry.getKey()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), (String) entry.getKey()); + })).then(((GameRules.b) entry.getValue()).b().a("value").executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), (String) entry.getKey(), commandcontext); + }))); + } + + com_mojang_brigadier_commanddispatcher.register(literalargumentbuilder); + } + + private static int a(CommandListenerWrapper commandlistenerwrapper, String s, CommandContext commandcontext) { + GameRules.GameRuleValue gamerules_gamerulevalue = commandlistenerwrapper.getWorld().getGameRules().get(s); // CraftBukkit + + gamerules_gamerulevalue.getType().a(commandcontext, "value", gamerules_gamerulevalue); + commandlistenerwrapper.sendMessage(new ChatMessage("commands.gamerule.set", new Object[] { s, gamerules_gamerulevalue.a()}), true); + return gamerules_gamerulevalue.c(); + } + + private static int a(CommandListenerWrapper commandlistenerwrapper, String s) { + GameRules.GameRuleValue gamerules_gamerulevalue = commandlistenerwrapper.getWorld().getGameRules().get(s); // CraftBukkit + + commandlistenerwrapper.sendMessage(new ChatMessage("commands.gamerule.query", new Object[] { s, gamerules_gamerulevalue.a()}), false); + return gamerules_gamerulevalue.c(); + } +} diff --git a/src/main/java/net/minecraft/server/CommandListenerWrapper.java b/src/main/java/net/minecraft/server/CommandListenerWrapper.java new file mode 100644 index 000000000000..5aa3a93ceedb --- /dev/null +++ b/src/main/java/net/minecraft/server/CommandListenerWrapper.java @@ -0,0 +1,256 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.mojang.brigadier.ResultConsumer; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import com.mojang.brigadier.tree.CommandNode; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.concurrent.CompletableFuture; +import java.util.function.BinaryOperator; +import javax.annotation.Nullable; + +public class CommandListenerWrapper implements ICompletionProvider { + + public static final SimpleCommandExceptionType a = new SimpleCommandExceptionType(new ChatMessage("permissions.requires.player", new Object[0])); + public static final SimpleCommandExceptionType b = new SimpleCommandExceptionType(new ChatMessage("permissions.requires.entity", new Object[0])); + public final ICommandListener base; + private final Vec3D d; + private final WorldServer e; + private final int f; + private final String g; + private final IChatBaseComponent h; + private final MinecraftServer i; + private final boolean j; + @Nullable + private final Entity k; + private final ResultConsumer l; + private final ArgumentAnchor.Anchor m; + private final Vec2F n; + public CommandNode currentCommand; // CraftBukkit + + public CommandListenerWrapper(ICommandListener icommandlistener, Vec3D vec3d, Vec2F vec2f, WorldServer worldserver, int i, String s, IChatBaseComponent ichatbasecomponent, MinecraftServer minecraftserver, @Nullable Entity entity) { + this(icommandlistener, vec3d, vec2f, worldserver, i, s, ichatbasecomponent, minecraftserver, entity, false, (commandcontext, flag, j) -> { + }, ArgumentAnchor.Anchor.FEET); + } + + protected CommandListenerWrapper(ICommandListener icommandlistener, Vec3D vec3d, Vec2F vec2f, WorldServer worldserver, int i, String s, IChatBaseComponent ichatbasecomponent, MinecraftServer minecraftserver, @Nullable Entity entity, boolean flag, ResultConsumer resultconsumer, ArgumentAnchor.Anchor argumentanchor_anchor) { + this.base = icommandlistener; + this.d = vec3d; + this.e = worldserver; + this.j = flag; + this.k = entity; + this.f = i; + this.g = s; + this.h = ichatbasecomponent; + this.i = minecraftserver; + this.l = resultconsumer; + this.m = argumentanchor_anchor; + this.n = vec2f; + } + + public CommandListenerWrapper a(Entity entity) { + return this.k == entity ? this : new CommandListenerWrapper(this.base, this.d, this.n, this.e, this.f, entity.getDisplayName().getString(), entity.getScoreboardDisplayName(), this.i, entity, this.j, this.l, this.m); + } + + public CommandListenerWrapper a(Vec3D vec3d) { + return this.d.equals(vec3d) ? this : new CommandListenerWrapper(this.base, vec3d, this.n, this.e, this.f, this.g, this.h, this.i, this.k, this.j, this.l, this.m); + } + + public CommandListenerWrapper a(Vec2F vec2f) { + return this.n.c(vec2f) ? this : new CommandListenerWrapper(this.base, this.d, vec2f, this.e, this.f, this.g, this.h, this.i, this.k, this.j, this.l, this.m); + } + + public CommandListenerWrapper a(ResultConsumer resultconsumer) { + return this.l.equals(resultconsumer) ? this : new CommandListenerWrapper(this.base, this.d, this.n, this.e, this.f, this.g, this.h, this.i, this.k, this.j, resultconsumer, this.m); + } + + public CommandListenerWrapper a(ResultConsumer resultconsumer, BinaryOperator> binaryoperator) { + ResultConsumer resultconsumer1 = (ResultConsumer) binaryoperator.apply(this.l, resultconsumer); + + return this.a(resultconsumer1); + } + + public CommandListenerWrapper a() { + return this.j ? this : new CommandListenerWrapper(this.base, this.d, this.n, this.e, this.f, this.g, this.h, this.i, this.k, true, this.l, this.m); + } + + public CommandListenerWrapper a(int i) { + return i == this.f ? this : new CommandListenerWrapper(this.base, this.d, this.n, this.e, i, this.g, this.h, this.i, this.k, this.j, this.l, this.m); + } + + public CommandListenerWrapper b(int i) { + return i <= this.f ? this : new CommandListenerWrapper(this.base, this.d, this.n, this.e, i, this.g, this.h, this.i, this.k, this.j, this.l, this.m); + } + + public CommandListenerWrapper a(ArgumentAnchor.Anchor argumentanchor_anchor) { + return argumentanchor_anchor == this.m ? this : new CommandListenerWrapper(this.base, this.d, this.n, this.e, this.f, this.g, this.h, this.i, this.k, this.j, this.l, argumentanchor_anchor); + } + + public CommandListenerWrapper a(WorldServer worldserver) { + return worldserver == this.e ? this : new CommandListenerWrapper(this.base, this.d, this.n, worldserver, this.f, this.g, this.h, this.i, this.k, this.j, this.l, this.m); + } + + public CommandListenerWrapper a(Entity entity, ArgumentAnchor.Anchor argumentanchor_anchor) throws CommandSyntaxException { + return this.b(argumentanchor_anchor.a(entity)); + } + + public CommandListenerWrapper b(Vec3D vec3d) throws CommandSyntaxException { + Vec3D vec3d1 = this.m.a(this); + double d0 = vec3d.x - vec3d1.x; + double d1 = vec3d.y - vec3d1.y; + double d2 = vec3d.z - vec3d1.z; + double d3 = (double) MathHelper.sqrt(d0 * d0 + d2 * d2); + float f = MathHelper.g((float) (-(MathHelper.c(d1, d3) * 57.2957763671875D))); + float f1 = MathHelper.g((float) (MathHelper.c(d2, d0) * 57.2957763671875D) - 90.0F); + + return this.a(new Vec2F(f, f1)); + } + + public IChatBaseComponent getScoreboardDisplayName() { + return this.h; + } + + public String getName() { + return this.g; + } + + public boolean hasPermission(int i) { + // CraftBukkit start + if (currentCommand != null) { + return hasPermission(i, org.bukkit.craftbukkit.command.VanillaCommandWrapper.getPermission(currentCommand)); + } + // CraftBukkit end + + return this.f >= i; + } + + // CraftBukkit start + public boolean hasPermission(int i, String bukkitPermission) { + // World is null when loading functions + return ((getWorld() == null || !getWorld().getServer().ignoreVanillaPermissions) && this.f >= i) || getBukkitSender().hasPermission(bukkitPermission); + } + // CraftBukkit end + + public Vec3D getPosition() { + return this.d; + } + + public WorldServer getWorld() { + return this.e; + } + + @Nullable + public Entity getEntity() { + return this.k; + } + + public Entity g() throws CommandSyntaxException { + if (this.k == null) { + throw CommandListenerWrapper.b.create(); + } else { + return this.k; + } + } + + public EntityPlayer h() throws CommandSyntaxException { + if (!(this.k instanceof EntityPlayer)) { + throw CommandListenerWrapper.a.create(); + } else { + return (EntityPlayer) this.k; + } + } + + public Vec2F i() { + return this.n; + } + + public MinecraftServer getServer() { + return this.i; + } + + public ArgumentAnchor.Anchor k() { + return this.m; + } + + public void sendMessage(IChatBaseComponent ichatbasecomponent, boolean flag) { + if (this.base.a() && !this.j) { + this.base.sendMessage(ichatbasecomponent); + } + + if (flag && this.base.B_() && !this.j) { + this.sendAdminMessage(ichatbasecomponent); + } + + } + + private void sendAdminMessage(IChatBaseComponent ichatbasecomponent) { + IChatBaseComponent ichatbasecomponent1 = (new ChatMessage("chat.type.admin", new Object[] { this.getScoreboardDisplayName(), ichatbasecomponent})).a(new EnumChatFormat[] { EnumChatFormat.GRAY, EnumChatFormat.ITALIC}); + + if (this.i.getGameRules().getBoolean("sendCommandFeedback")) { + Iterator iterator = this.i.getPlayerList().v().iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + if (entityplayer != this.base && this.i.getPlayerList().isOp(entityplayer.getProfile())) { + entityplayer.sendMessage(ichatbasecomponent1); + } + } + } + + if (this.base != this.i && this.i.getGameRules().getBoolean("logAdminCommands") && !org.spigotmc.SpigotConfig.silentCommandBlocks) { // Spigot + this.i.sendMessage(ichatbasecomponent1); + } + + } + + public void sendFailureMessage(IChatBaseComponent ichatbasecomponent) { + if (this.base.b() && !this.j) { + this.base.sendMessage((new ChatComponentText("")).addSibling(ichatbasecomponent).a(EnumChatFormat.RED)); + } + + } + + public void a(CommandContext commandcontext, boolean flag, int i) { + if (this.l != null) { + this.l.onCommandComplete(commandcontext, flag, i); + } + + } + + public Collection l() { + return Lists.newArrayList(this.i.getPlayers()); + } + + public Collection m() { + return this.i.getScoreboard().f(); + } + + public Collection n() { + return IRegistry.SOUND_EVENT.keySet(); + } + + public Collection o() { + return this.i.getCraftingManager().c(); + } + + public CompletableFuture a(CommandContext commandcontext, SuggestionsBuilder suggestionsbuilder) { + return null; + } + + public Collection a(boolean flag) { + return Collections.singleton(ICompletionProvider.a.b); + } + + // CraftBukkit start + public org.bukkit.command.CommandSender getBukkitSender() { + return base.getBukkitSender(this); + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/CommandSpreadPlayers.java b/src/main/java/net/minecraft/server/CommandSpreadPlayers.java new file mode 100644 index 000000000000..5b6127e0dff4 --- /dev/null +++ b/src/main/java/net/minecraft/server/CommandSpreadPlayers.java @@ -0,0 +1,305 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.FloatArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.Dynamic4CommandExceptionType; +import java.util.Collection; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +public class CommandSpreadPlayers { + + private static final Dynamic4CommandExceptionType a = new Dynamic4CommandExceptionType((object, object1, object2, object3) -> { + return new ChatMessage("commands.spreadplayers.failed.teams", new Object[] { object, object1, object2, object3}); + }); + private static final Dynamic4CommandExceptionType b = new Dynamic4CommandExceptionType((object, object1, object2, object3) -> { + return new ChatMessage("commands.spreadplayers.failed.entities", new Object[] { object, object1, object2, object3}); + }); + + public static void a(com.mojang.brigadier.CommandDispatcher com_mojang_brigadier_commanddispatcher) { + com_mojang_brigadier_commanddispatcher.register((LiteralArgumentBuilder) ((LiteralArgumentBuilder) CommandDispatcher.a("spreadplayers").requires((commandlistenerwrapper) -> { + return commandlistenerwrapper.hasPermission(2); + })).then(CommandDispatcher.a("center", (ArgumentType) ArgumentVec2.a()).then(CommandDispatcher.a("spreadDistance", (ArgumentType) FloatArgumentType.floatArg(0.0F)).then(CommandDispatcher.a("maxRange", (ArgumentType) FloatArgumentType.floatArg(1.0F)).then(CommandDispatcher.a("respectTeams", (ArgumentType) BoolArgumentType.bool()).then(CommandDispatcher.a("targets", (ArgumentType) ArgumentEntity.b()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentVec2.a(commandcontext, "center"), FloatArgumentType.getFloat(commandcontext, "spreadDistance"), FloatArgumentType.getFloat(commandcontext, "maxRange"), BoolArgumentType.getBool(commandcontext, "respectTeams"), ArgumentEntity.b(commandcontext, "targets")); + }))))))); + } + + private static int a(CommandListenerWrapper commandlistenerwrapper, Vec2F vec2f, float f, float f1, boolean flag, Collection collection) throws CommandSyntaxException { + Random random = new Random(); + double d0 = (double) (vec2f.i - f1); + double d1 = (double) (vec2f.j - f1); + double d2 = (double) (vec2f.i + f1); + double d3 = (double) (vec2f.j + f1); + CommandSpreadPlayers.a[] acommandspreadplayers_a = a(random, flag ? a(collection) : collection.size(), d0, d1, d2, d3); + + a(vec2f, (double) f, commandlistenerwrapper.getWorld(), random, d0, d1, d2, d3, acommandspreadplayers_a, flag); + double d4 = a(collection, commandlistenerwrapper.getWorld(), acommandspreadplayers_a, flag); + + commandlistenerwrapper.sendMessage(new ChatMessage("commands.spreadplayers.success." + (flag ? "teams" : "entities"), new Object[] { acommandspreadplayers_a.length, vec2f.i, vec2f.j, String.format(Locale.ROOT, "%.2f", d4)}), true); + return acommandspreadplayers_a.length; + } + + private static int a(Collection collection) { + Set set = Sets.newHashSet(); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + if (entity instanceof EntityHuman) { + set.add(entity.getScoreboardTeam()); + } else { + set.add((ScoreboardTeamBase) null); // CraftBukkit - decompile error + } + } + + return set.size(); + } + + private static void a(Vec2F vec2f, double d0, WorldServer worldserver, Random random, double d1, double d2, double d3, double d4, CommandSpreadPlayers.a[] acommandspreadplayers_a, boolean flag) throws CommandSyntaxException { + boolean flag1 = true; + double d5 = 3.4028234663852886E38D; + + int i; + + for (i = 0; i < 10000 && flag1; ++i) { + flag1 = false; + d5 = 3.4028234663852886E38D; + + int j; + CommandSpreadPlayers.a commandspreadplayers_a; + + for (int k = 0; k < acommandspreadplayers_a.length; ++k) { + CommandSpreadPlayers.a commandspreadplayers_a1 = acommandspreadplayers_a[k]; + + j = 0; + commandspreadplayers_a = new CommandSpreadPlayers.a(); + + for (int l = 0; l < acommandspreadplayers_a.length; ++l) { + if (k != l) { + CommandSpreadPlayers.a commandspreadplayers_a2 = acommandspreadplayers_a[l]; + double d6 = commandspreadplayers_a1.a(commandspreadplayers_a2); + + d5 = Math.min(d6, d5); + if (d6 < d0) { + ++j; + commandspreadplayers_a.a = commandspreadplayers_a.a + (commandspreadplayers_a2.a - commandspreadplayers_a1.a); + commandspreadplayers_a.b = commandspreadplayers_a.b + (commandspreadplayers_a2.b - commandspreadplayers_a1.b); + } + } + } + + if (j > 0) { + commandspreadplayers_a.a = commandspreadplayers_a.a / (double) j; + commandspreadplayers_a.b = commandspreadplayers_a.b / (double) j; + double d7 = (double) commandspreadplayers_a.b(); + + if (d7 > 0.0D) { + commandspreadplayers_a.a(); + commandspreadplayers_a1.b(commandspreadplayers_a); + } else { + commandspreadplayers_a1.a(random, d1, d2, d3, d4); + } + + flag1 = true; + } + + if (commandspreadplayers_a1.a(d1, d2, d3, d4)) { + flag1 = true; + } + } + + if (!flag1) { + CommandSpreadPlayers.a[] acommandspreadplayers_a1 = acommandspreadplayers_a; + int i1 = acommandspreadplayers_a.length; + + for (j = 0; j < i1; ++j) { + commandspreadplayers_a = acommandspreadplayers_a1[j]; + if (!commandspreadplayers_a.b((IBlockAccess) worldserver)) { + commandspreadplayers_a.a(random, d1, d2, d3, d4); + flag1 = true; + } + } + } + } + + if (d5 == 3.4028234663852886E38D) { + d5 = 0.0D; + } + + if (i >= 10000) { + if (flag) { + throw CommandSpreadPlayers.a.create(acommandspreadplayers_a.length, vec2f.i, vec2f.j, String.format(Locale.ROOT, "%.2f", d5)); + } else { + throw CommandSpreadPlayers.b.create(acommandspreadplayers_a.length, vec2f.i, vec2f.j, String.format(Locale.ROOT, "%.2f", d5)); + } + } + } + + private static double a(Collection collection, WorldServer worldserver, CommandSpreadPlayers.a[] acommandspreadplayers_a, boolean flag) { + double d0 = 0.0D; + int i = 0; + Map map = Maps.newHashMap(); + + double d1; + + for (Iterator iterator = collection.iterator(); iterator.hasNext(); d0 += d1) { + Entity entity = (Entity) iterator.next(); + CommandSpreadPlayers.a commandspreadplayers_a; + + if (flag) { + ScoreboardTeamBase scoreboardteambase = entity instanceof EntityHuman ? entity.getScoreboardTeam() : null; + + if (!map.containsKey(scoreboardteambase)) { + map.put(scoreboardteambase, acommandspreadplayers_a[i++]); + } + + commandspreadplayers_a = (CommandSpreadPlayers.a) map.get(scoreboardteambase); + } else { + commandspreadplayers_a = acommandspreadplayers_a[i++]; + } + + entity.enderTeleportTo((double) ((float) MathHelper.floor(commandspreadplayers_a.a) + 0.5F), (double) commandspreadplayers_a.a((IBlockAccess) worldserver), (double) MathHelper.floor(commandspreadplayers_a.b) + 0.5D); + d1 = Double.MAX_VALUE; + CommandSpreadPlayers.a[] acommandspreadplayers_a1 = acommandspreadplayers_a; + int j = acommandspreadplayers_a.length; + + for (int k = 0; k < j; ++k) { + CommandSpreadPlayers.a commandspreadplayers_a1 = acommandspreadplayers_a1[k]; + + if (commandspreadplayers_a != commandspreadplayers_a1) { + double d2 = commandspreadplayers_a.a(commandspreadplayers_a1); + + d1 = Math.min(d2, d1); + } + } + } + + if (collection.size() < 2) { + return 0.0D; + } else { + d0 /= (double) collection.size(); + return d0; + } + } + + private static CommandSpreadPlayers.a[] a(Random random, int i, double d0, double d1, double d2, double d3) { + CommandSpreadPlayers.a[] acommandspreadplayers_a = new CommandSpreadPlayers.a[i]; + + for (int j = 0; j < acommandspreadplayers_a.length; ++j) { + CommandSpreadPlayers.a commandspreadplayers_a = new CommandSpreadPlayers.a(); + + commandspreadplayers_a.a(random, d0, d1, d2, d3); + acommandspreadplayers_a[j] = commandspreadplayers_a; + } + + return acommandspreadplayers_a; + } + + static class a { + + private double a; + private double b; + + a() {} + + double a(CommandSpreadPlayers.a commandspreadplayers_a) { + double d0 = this.a - commandspreadplayers_a.a; + double d1 = this.b - commandspreadplayers_a.b; + + return Math.sqrt(d0 * d0 + d1 * d1); + } + + void a() { + double d0 = (double) this.b(); + + this.a /= d0; + this.b /= d0; + } + + float b() { + return MathHelper.sqrt(this.a * this.a + this.b * this.b); + } + + public void b(CommandSpreadPlayers.a commandspreadplayers_a) { + this.a -= commandspreadplayers_a.a; + this.b -= commandspreadplayers_a.b; + } + + public boolean a(double d0, double d1, double d2, double d3) { + boolean flag = false; + + if (this.a < d0) { + this.a = d0; + flag = true; + } else if (this.a > d2) { + this.a = d2; + flag = true; + } + + if (this.b < d1) { + this.b = d1; + flag = true; + } else if (this.b > d3) { + this.b = d3; + flag = true; + } + + return flag; + } + + public int a(IBlockAccess iblockaccess) { + BlockPosition blockposition = new BlockPosition(this.a, 256.0D, this.b); + + do { + if (blockposition.getY() <= 0) { + return 257; + } + + blockposition = blockposition.down(); + } while (getType(iblockaccess, blockposition).isAir()); // CraftBukkit + + return blockposition.getY() + 1; + } + + public boolean b(IBlockAccess iblockaccess) { + BlockPosition blockposition = new BlockPosition(this.a, 256.0D, this.b); + + IBlockData iblockdata; + + do { + if (blockposition.getY() <= 0) { + return false; + } + + blockposition = blockposition.down(); + iblockdata = getType(iblockaccess, blockposition); // CraftBukkit + } while (iblockdata.isAir()); + + Material material = iblockdata.getMaterial(); + + return !material.isLiquid() && material != Material.FIRE; + } + + public void a(Random random, double d0, double d1, double d2, double d3) { + this.a = MathHelper.a(random, d0, d2); + this.b = MathHelper.a(random, d1, d3); + } + + // CraftBukkit start - add a version of getType which force loads chunks + private static IBlockData getType(IBlockAccess iblockaccess, BlockPosition position) { + ((ChunkProviderServer) ((World) iblockaccess).chunkProvider).getChunkAt(position.getX() >> 4, position.getZ() >> 4, true, true); + return iblockaccess.getType(position); + } + // CraftBukkit end + } +} diff --git a/src/main/java/net/minecraft/server/CommandSummon.java b/src/main/java/net/minecraft/server/CommandSummon.java new file mode 100644 index 000000000000..f97be3bf937d --- /dev/null +++ b/src/main/java/net/minecraft/server/CommandSummon.java @@ -0,0 +1,52 @@ +package net.minecraft.server; + +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; + +public class CommandSummon { + + private static final SimpleCommandExceptionType a = new SimpleCommandExceptionType(new ChatMessage("commands.summon.failed", new Object[0])); + + public static void a(com.mojang.brigadier.CommandDispatcher com_mojang_brigadier_commanddispatcher) { + com_mojang_brigadier_commanddispatcher.register((LiteralArgumentBuilder) ((LiteralArgumentBuilder) CommandDispatcher.a("summon").requires((commandlistenerwrapper) -> { + return commandlistenerwrapper.hasPermission(2); + })).then(((RequiredArgumentBuilder) CommandDispatcher.a("entity", (ArgumentType) ArgumentEntitySummon.a()).suggests(CompletionProviders.d).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentEntitySummon.a(commandcontext, "entity"), ((CommandListenerWrapper) commandcontext.getSource()).getPosition(), new NBTTagCompound(), true); + })).then(((RequiredArgumentBuilder) CommandDispatcher.a("pos", (ArgumentType) ArgumentVec3.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentEntitySummon.a(commandcontext, "entity"), ArgumentVec3.a(commandcontext, "pos"), new NBTTagCompound(), true); + })).then(CommandDispatcher.a("nbt", (ArgumentType) ArgumentNBTTag.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentEntitySummon.a(commandcontext, "entity"), ArgumentVec3.a(commandcontext, "pos"), ArgumentNBTTag.a(commandcontext, "nbt"), false); + }))))); + } + + private static int a(CommandListenerWrapper commandlistenerwrapper, MinecraftKey minecraftkey, Vec3D vec3d, NBTTagCompound nbttagcompound, boolean flag) throws CommandSyntaxException { + NBTTagCompound nbttagcompound1 = nbttagcompound.clone(); + + nbttagcompound1.setString("id", minecraftkey.toString()); + if (EntityTypes.getName(EntityTypes.LIGHTNING_BOLT).equals(minecraftkey)) { + EntityLightning entitylightning = new EntityLightning(commandlistenerwrapper.getWorld(), vec3d.x, vec3d.y, vec3d.z, false); + + commandlistenerwrapper.getWorld().strikeLightning(entitylightning, org.bukkit.event.weather.LightningStrikeEvent.Cause.COMMAND); // CraftBukkit + commandlistenerwrapper.sendMessage(new ChatMessage("commands.summon.success", new Object[] { entitylightning.getScoreboardDisplayName()}), true); + return 1; + } else { + Entity entity = ChunkRegionLoader.a(nbttagcompound1, commandlistenerwrapper.getWorld(), vec3d.x, vec3d.y, vec3d.z, true); + + if (entity == null) { + throw CommandSummon.a.create(); + } else { + entity.setPositionRotation(vec3d.x, vec3d.y, vec3d.z, entity.yaw, entity.pitch); + if (flag && entity instanceof EntityInsentient) { + ((EntityInsentient) entity).prepare(commandlistenerwrapper.getWorld().getDamageScaler(new BlockPosition(entity)), (GroupDataEntity) null, (NBTTagCompound) null); + } + + commandlistenerwrapper.sendMessage(new ChatMessage("commands.summon.success", new Object[] { entity.getScoreboardDisplayName()}), true); + return 1; + } + } + } +} diff --git a/src/main/java/net/minecraft/server/CommandTeleport.java b/src/main/java/net/minecraft/server/CommandTeleport.java new file mode 100644 index 000000000000..dafd3154048b --- /dev/null +++ b/src/main/java/net/minecraft/server/CommandTeleport.java @@ -0,0 +1,223 @@ +package net.minecraft.server; + +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.tree.LiteralCommandNode; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.Set; +import javax.annotation.Nullable; +// CraftBukkit start +import org.bukkit.Location; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.event.entity.EntityTeleportEvent; +// CraftBukkit end + +public class CommandTeleport { + + public static void a(com.mojang.brigadier.CommandDispatcher com_mojang_brigadier_commanddispatcher) { + LiteralCommandNode literalcommandnode = com_mojang_brigadier_commanddispatcher.register((LiteralArgumentBuilder) ((LiteralArgumentBuilder) ((LiteralArgumentBuilder) ((LiteralArgumentBuilder) CommandDispatcher.a("teleport").requires((commandlistenerwrapper) -> { + return commandlistenerwrapper.hasPermission(2); + })).then(((RequiredArgumentBuilder) CommandDispatcher.a("targets", (ArgumentType) ArgumentEntity.b()).then(((RequiredArgumentBuilder) ((RequiredArgumentBuilder) CommandDispatcher.a("location", (ArgumentType) ArgumentVec3.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentEntity.b(commandcontext, "targets"), ((CommandListenerWrapper) commandcontext.getSource()).getWorld(), ArgumentVec3.b(commandcontext, "location"), (IVectorPosition) null, (CommandTeleport.a) null); + })).then(CommandDispatcher.a("rotation", (ArgumentType) ArgumentRotation.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentEntity.b(commandcontext, "targets"), ((CommandListenerWrapper) commandcontext.getSource()).getWorld(), ArgumentVec3.b(commandcontext, "location"), ArgumentRotation.a(commandcontext, "rotation"), (CommandTeleport.a) null); + }))).then(((LiteralArgumentBuilder) CommandDispatcher.a("facing").then(CommandDispatcher.a("entity").then(((RequiredArgumentBuilder) CommandDispatcher.a("facingEntity", (ArgumentType) ArgumentEntity.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentEntity.b(commandcontext, "targets"), ((CommandListenerWrapper) commandcontext.getSource()).getWorld(), ArgumentVec3.b(commandcontext, "location"), (IVectorPosition) null, new CommandTeleport.a(ArgumentEntity.a(commandcontext, "facingEntity"), ArgumentAnchor.Anchor.FEET)); + })).then(CommandDispatcher.a("facingAnchor", (ArgumentType) ArgumentAnchor.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentEntity.b(commandcontext, "targets"), ((CommandListenerWrapper) commandcontext.getSource()).getWorld(), ArgumentVec3.b(commandcontext, "location"), (IVectorPosition) null, new CommandTeleport.a(ArgumentEntity.a(commandcontext, "facingEntity"), ArgumentAnchor.a(commandcontext, "facingAnchor"))); + }))))).then(CommandDispatcher.a("facingLocation", (ArgumentType) ArgumentVec3.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentEntity.b(commandcontext, "targets"), ((CommandListenerWrapper) commandcontext.getSource()).getWorld(), ArgumentVec3.b(commandcontext, "location"), (IVectorPosition) null, new CommandTeleport.a(ArgumentVec3.a(commandcontext, "facingLocation"))); + }))))).then(CommandDispatcher.a("destination", (ArgumentType) ArgumentEntity.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), ArgumentEntity.b(commandcontext, "targets"), ArgumentEntity.a(commandcontext, "destination")); + })))).then(CommandDispatcher.a("location", (ArgumentType) ArgumentVec3.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), Collections.singleton(((CommandListenerWrapper) commandcontext.getSource()).g()), ((CommandListenerWrapper) commandcontext.getSource()).getWorld(), ArgumentVec3.b(commandcontext, "location"), VectorPosition.d(), (CommandTeleport.a) null); + }))).then(CommandDispatcher.a("destination", (ArgumentType) ArgumentEntity.a()).executes((commandcontext) -> { + return a((CommandListenerWrapper) commandcontext.getSource(), Collections.singleton(((CommandListenerWrapper) commandcontext.getSource()).g()), ArgumentEntity.a(commandcontext, "destination")); + }))); + + com_mojang_brigadier_commanddispatcher.register((LiteralArgumentBuilder) ((LiteralArgumentBuilder) CommandDispatcher.a("tp").requires((commandlistenerwrapper) -> { + return commandlistenerwrapper.hasPermission(2); + })).redirect(literalcommandnode)); + } + + private static int a(CommandListenerWrapper commandlistenerwrapper, Collection collection, Entity entity) { + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + Entity entity1 = (Entity) iterator.next(); + + a(commandlistenerwrapper, entity1, (WorldServer) entity.world, entity.locX, entity.locY, entity.locZ, EnumSet.noneOf(PacketPlayOutPosition.EnumPlayerTeleportFlags.class), entity.yaw, entity.pitch, (CommandTeleport.a) null); // SPIGOT-4245, MC-128441 - use target world as destination + } + + if (collection.size() == 1) { + commandlistenerwrapper.sendMessage(new ChatMessage("commands.teleport.success.entity.single", new Object[] { ((Entity) collection.iterator().next()).getScoreboardDisplayName(), entity.getScoreboardDisplayName()}), true); + } else { + commandlistenerwrapper.sendMessage(new ChatMessage("commands.teleport.success.entity.multiple", new Object[] { collection.size(), entity.getScoreboardDisplayName()}), true); + } + + return collection.size(); + } + + private static int a(CommandListenerWrapper commandlistenerwrapper, Collection collection, WorldServer worldserver, IVectorPosition ivectorposition, @Nullable IVectorPosition ivectorposition1, @Nullable CommandTeleport.a commandteleport_a) throws CommandSyntaxException { + Vec3D vec3d = ivectorposition.a(commandlistenerwrapper); + Vec2F vec2f = ivectorposition1 == null ? null : ivectorposition1.b(commandlistenerwrapper); + Set set = EnumSet.noneOf(PacketPlayOutPosition.EnumPlayerTeleportFlags.class); + + if (ivectorposition.a()) { + set.add(PacketPlayOutPosition.EnumPlayerTeleportFlags.X); + } + + if (ivectorposition.b()) { + set.add(PacketPlayOutPosition.EnumPlayerTeleportFlags.Y); + } + + if (ivectorposition.c()) { + set.add(PacketPlayOutPosition.EnumPlayerTeleportFlags.Z); + } + + if (ivectorposition1 == null) { + set.add(PacketPlayOutPosition.EnumPlayerTeleportFlags.X_ROT); + set.add(PacketPlayOutPosition.EnumPlayerTeleportFlags.Y_ROT); + } else { + if (ivectorposition1.a()) { + set.add(PacketPlayOutPosition.EnumPlayerTeleportFlags.X_ROT); + } + + if (ivectorposition1.b()) { + set.add(PacketPlayOutPosition.EnumPlayerTeleportFlags.Y_ROT); + } + } + + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + if (ivectorposition1 == null) { + a(commandlistenerwrapper, entity, worldserver, vec3d.x, vec3d.y, vec3d.z, set, entity.yaw, entity.pitch, commandteleport_a); + } else { + a(commandlistenerwrapper, entity, worldserver, vec3d.x, vec3d.y, vec3d.z, set, vec2f.j, vec2f.i, commandteleport_a); + } + } + + if (collection.size() == 1) { + commandlistenerwrapper.sendMessage(new ChatMessage("commands.teleport.success.location.single", new Object[] { ((Entity) collection.iterator().next()).getScoreboardDisplayName(), vec3d.x, vec3d.y, vec3d.z}), true); + } else { + commandlistenerwrapper.sendMessage(new ChatMessage("commands.teleport.success.location.multiple", new Object[] { collection.size(), vec3d.x, vec3d.y, vec3d.z}), true); + } + + return collection.size(); + } + + private static void a(CommandListenerWrapper commandlistenerwrapper, Entity entity, WorldServer worldserver, double d0, double d1, double d2, Set set, float f, float f1, @Nullable CommandTeleport.a commandteleport_a) { + if (entity instanceof EntityPlayer) { + entity.stopRiding(); + if (((EntityPlayer) entity).isSleeping()) { + ((EntityPlayer) entity).a(true, true, false); + } + + if (worldserver == entity.world) { + ((EntityPlayer) entity).playerConnection.a(d0, d1, d2, f, f1, set, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.COMMAND); // CraftBukkit + } else { + ((EntityPlayer) entity).a(worldserver, d0, d1, d2, f, f1, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.COMMAND); // CraftBukkit + } + + entity.setHeadRotation(f); + } else { + float f2 = MathHelper.g(f); + float f3 = MathHelper.g(f1); + + f3 = MathHelper.a(f3, -90.0F, 90.0F); + // CraftBukkit start - Teleport event + Location to = new Location(worldserver.getWorld(), d0, d1, d2, f2, f3); + EntityTeleportEvent event = new EntityTeleportEvent(entity.getBukkitEntity(), entity.getBukkitEntity().getLocation(), to); + worldserver.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + + d0 = to.getX(); + d1 = to.getY(); + d2 = to.getZ(); + f2 = to.getYaw(); + f3 = to.getPitch(); + worldserver = ((CraftWorld) to.getWorld()).getHandle(); + // CraftBukkit end + if (worldserver == entity.world) { + entity.setPositionRotation(d0, d1, d2, f2, f3); + entity.setHeadRotation(f2); + } else { + WorldServer worldserver1 = (WorldServer) entity.world; + + worldserver1.kill(entity); + entity.dimension = worldserver.worldProvider.getDimensionManager(); + entity.dead = false; + Entity entity1 = entity; + + entity = entity.P().a((World) worldserver); + if (entity == null) { + return; + } + + entity.v(entity1); + entity.setPositionRotation(d0, d1, d2, f2, f3); + entity.setHeadRotation(f2); + boolean flag = entity.attachedToPlayer; + + entity.attachedToPlayer = true; + worldserver.addEntity(entity); + entity.attachedToPlayer = flag; + worldserver.entityJoinedWorld(entity, false); + entity1.dead = true; + } + } + + if (commandteleport_a != null) { + commandteleport_a.a(commandlistenerwrapper, entity); + } + + if (!(entity instanceof EntityLiving) || !((EntityLiving) entity).dc()) { + entity.motY = 0.0D; + entity.onGround = true; + } + + } + + static class a { + + private final Vec3D a; + private final Entity b; + private final ArgumentAnchor.Anchor c; + + public a(Entity entity, ArgumentAnchor.Anchor argumentanchor_anchor) { + this.b = entity; + this.c = argumentanchor_anchor; + this.a = argumentanchor_anchor.a(entity); + } + + public a(Vec3D vec3d) { + this.b = null; + this.a = vec3d; + this.c = null; + } + + public void a(CommandListenerWrapper commandlistenerwrapper, Entity entity) { + if (this.b != null) { + if (entity instanceof EntityPlayer) { + ((EntityPlayer) entity).a(commandlistenerwrapper.k(), this.b, this.c); + } else { + entity.a(commandlistenerwrapper.k(), this.a); + } + } else { + entity.a(commandlistenerwrapper.k(), this.a); + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/Container.java b/src/main/java/net/minecraft/server/Container.java new file mode 100644 index 000000000000..2f0f667ad6e5 --- /dev/null +++ b/src/main/java/net/minecraft/server/Container.java @@ -0,0 +1,649 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import javax.annotation.Nullable; + +// CraftBukkit start +import java.util.HashMap; +import java.util.Map; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.Event.Result; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.InventoryView; +// CraftBukkit end + +public abstract class Container { + + public NonNullList items = NonNullList.a(); + public List slots = Lists.newArrayList(); + public int windowId; + private int dragType = -1; + private int g; + private final Set h = Sets.newHashSet(); + protected List listeners = Lists.newArrayList(); + private final Set i = Sets.newHashSet(); + + // CraftBukkit start + public boolean checkReachable = true; + public abstract InventoryView getBukkitView(); + public void transferTo(Container other, org.bukkit.craftbukkit.entity.CraftHumanEntity player) { + InventoryView source = this.getBukkitView(), destination = other.getBukkitView(); + ((CraftInventory) source.getTopInventory()).getInventory().onClose(player); + ((CraftInventory) source.getBottomInventory()).getInventory().onClose(player); + ((CraftInventory) destination.getTopInventory()).getInventory().onOpen(player); + ((CraftInventory) destination.getBottomInventory()).getInventory().onOpen(player); + } + // CraftBukkit end + + public Container() {} + + protected Slot a(Slot slot) { + slot.rawSlotIndex = this.slots.size(); + this.slots.add(slot); + this.items.add(ItemStack.a); + return slot; + } + + public void addSlotListener(ICrafting icrafting) { + if (this.listeners.contains(icrafting)) { + throw new IllegalArgumentException("Listener already listening"); + } else { + this.listeners.add(icrafting); + icrafting.a(this, this.a()); + this.b(); + } + } + + public NonNullList a() { + NonNullList nonnulllist = NonNullList.a(); + + for (int i = 0; i < this.slots.size(); ++i) { + nonnulllist.add(((Slot) this.slots.get(i)).getItem()); + } + + return nonnulllist; + } + + public void b() { + for (int i = 0; i < this.slots.size(); ++i) { + ItemStack itemstack = ((Slot) this.slots.get(i)).getItem(); + ItemStack itemstack1 = (ItemStack) this.items.get(i); + + if (!ItemStack.matches(itemstack1, itemstack)) { + itemstack1 = itemstack.isEmpty() ? ItemStack.a : itemstack.cloneItemStack(); + this.items.set(i, itemstack1); + + for (int j = 0; j < this.listeners.size(); ++j) { + ((ICrafting) this.listeners.get(j)).a(this, i, itemstack1); + } + } + } + + } + + public boolean a(EntityHuman entityhuman, int i) { + return false; + } + + @Nullable + public Slot getSlot(IInventory iinventory, int i) { + for (int j = 0; j < this.slots.size(); ++j) { + Slot slot = (Slot) this.slots.get(j); + + if (slot.a(iinventory, i)) { + return slot; + } + } + + return null; + } + + public Slot getSlot(int i) { + return (Slot) this.slots.get(i); + } + + public ItemStack shiftClick(EntityHuman entityhuman, int i) { + Slot slot = (Slot) this.slots.get(i); + + return slot != null ? slot.getItem() : ItemStack.a; + } + + public ItemStack a(int i, int j, InventoryClickType inventoryclicktype, EntityHuman entityhuman) { + ItemStack itemstack = ItemStack.a; + PlayerInventory playerinventory = entityhuman.inventory; + ItemStack itemstack1; + ItemStack itemstack2; + int k; + int l; + + if (inventoryclicktype == InventoryClickType.QUICK_CRAFT) { + int i1 = this.g; + + this.g = c(j); + if ((i1 != 1 || this.g != 2) && i1 != this.g) { + this.c(); + } else if (playerinventory.getCarried().isEmpty()) { + this.c(); + } else if (this.g == 0) { + this.dragType = b(j); + if (a(this.dragType, entityhuman)) { + this.g = 1; + this.h.clear(); + } else { + this.c(); + } + } else if (this.g == 1) { + Slot slot = i < this.slots.size() ? this.slots.get(i) : null; // Paper - Ensure drag in bounds + + itemstack1 = playerinventory.getCarried(); + if (slot != null && a(slot, itemstack1, true) && slot.isAllowed(itemstack1) && (this.dragType == 2 || itemstack1.getCount() > this.h.size()) && this.b(slot)) { + this.h.add(slot); + } + } else if (this.g == 2) { + if (!this.h.isEmpty()) { + itemstack2 = playerinventory.getCarried().cloneItemStack(); + k = playerinventory.getCarried().getCount(); + Iterator iterator = this.h.iterator(); + + Map draggedSlots = new HashMap(); // CraftBukkit - Store slots from drag in map (raw slot id -> new stack) + while (iterator.hasNext()) { + Slot slot1 = (Slot) iterator.next(); + ItemStack itemstack3 = playerinventory.getCarried(); + + if (slot1 != null && a(slot1, itemstack3, true) && slot1.isAllowed(itemstack3) && (this.dragType == 2 || itemstack3.getCount() >= this.h.size()) && this.b(slot1)) { + ItemStack itemstack4 = itemstack2.cloneItemStack(); + int j1 = slot1.hasItem() ? slot1.getItem().getCount() : 0; + + a(this.h, this.dragType, itemstack4, j1); + l = Math.min(itemstack4.getMaxStackSize(), slot1.getMaxStackSize(itemstack4)); + if (itemstack4.getCount() > l) { + itemstack4.setCount(l); + } + + k -= itemstack4.getCount() - j1; + // slot1.set(itemstack4); + draggedSlots.put(slot1.rawSlotIndex, itemstack4); // CraftBukkit - Put in map instead of setting + } + } + + // CraftBukkit start - InventoryDragEvent + InventoryView view = getBukkitView(); + org.bukkit.inventory.ItemStack newcursor = CraftItemStack.asCraftMirror(itemstack2); + newcursor.setAmount(k); + Map eventmap = new HashMap(); + for (Map.Entry ditem : draggedSlots.entrySet()) { + eventmap.put(ditem.getKey(), CraftItemStack.asBukkitCopy(ditem.getValue())); + } + + // It's essential that we set the cursor to the new value here to prevent item duplication if a plugin closes the inventory. + ItemStack oldCursor = playerinventory.getCarried(); + playerinventory.setCarried(CraftItemStack.asNMSCopy(newcursor)); + + InventoryDragEvent event = new InventoryDragEvent(view, (newcursor.getType() != org.bukkit.Material.AIR ? newcursor : null), CraftItemStack.asBukkitCopy(oldCursor), this.dragType == 1, eventmap); + entityhuman.world.getServer().getPluginManager().callEvent(event); + + // Whether or not a change was made to the inventory that requires an update. + boolean needsUpdate = event.getResult() != Result.DEFAULT; + + if (event.getResult() != Result.DENY) { + for (Map.Entry dslot : draggedSlots.entrySet()) { + view.setItem(dslot.getKey(), CraftItemStack.asBukkitCopy(dslot.getValue())); + } + // The only time the carried item will be set to null is if the inventory is closed by the server. + // If the inventory is closed by the server, then the cursor items are dropped. This is why we change the cursor early. + if (playerinventory.getCarried() != null) { + playerinventory.setCarried(CraftItemStack.asNMSCopy(event.getCursor())); + needsUpdate = true; + } + } else { + playerinventory.setCarried(oldCursor); + } + + if (needsUpdate && entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).updateInventory(this); + } + // CraftBukkit end + } + + this.c(); + } else { + this.c(); + } + } else if (this.g != 0) { + this.c(); + } else { + Slot slot2; + int k1; + + if ((inventoryclicktype == InventoryClickType.PICKUP || inventoryclicktype == InventoryClickType.QUICK_MOVE) && (j == 0 || j == 1)) { + if (i == -999) { + if (!playerinventory.getCarried().isEmpty()) { + if (j == 0) { + // CraftBukkit start + ItemStack carried = playerinventory.getCarried(); + playerinventory.setCarried(ItemStack.a); + entityhuman.drop(carried, true); + // CraftBukkit start + } + + if (j == 1) { + entityhuman.drop(playerinventory.getCarried().cloneAndSubtract(1), true); + } + } + } else if (inventoryclicktype == InventoryClickType.QUICK_MOVE) { + if (i < 0) { + return ItemStack.a; + } + + slot2 = (Slot) this.slots.get(i); + if (slot2 == null || !slot2.isAllowed(entityhuman)) { + return ItemStack.a; + } + + for (itemstack2 = this.shiftClick(entityhuman, i); !itemstack2.isEmpty() && ItemStack.c(slot2.getItem(), itemstack2); itemstack2 = this.shiftClick(entityhuman, i)) { + itemstack = itemstack2.cloneItemStack(); + } + } else { + if (i < 0) { + return ItemStack.a; + } + + slot2 = (Slot) this.slots.get(i); + if (slot2 != null) { + itemstack2 = slot2.getItem(); + itemstack1 = playerinventory.getCarried(); + if (!itemstack2.isEmpty()) { + itemstack = itemstack2.cloneItemStack(); + } + + if (itemstack2.isEmpty()) { + if (!itemstack1.isEmpty() && slot2.isAllowed(itemstack1)) { + k1 = j == 0 ? itemstack1.getCount() : 1; + if (k1 > slot2.getMaxStackSize(itemstack1)) { + k1 = slot2.getMaxStackSize(itemstack1); + } + + slot2.set(itemstack1.cloneAndSubtract(k1)); + } + } else if (slot2.isAllowed(entityhuman)) { + if (itemstack1.isEmpty()) { + if (itemstack2.isEmpty()) { + slot2.set(ItemStack.a); + playerinventory.setCarried(ItemStack.a); + } else { + k1 = j == 0 ? itemstack2.getCount() : (itemstack2.getCount() + 1) / 2; + playerinventory.setCarried(slot2.a(k1)); + if (itemstack2.isEmpty()) { + slot2.set(ItemStack.a); + } + + slot2.a(entityhuman, playerinventory.getCarried()); + } + } else if (slot2.isAllowed(itemstack1)) { + if (a(itemstack2, itemstack1)) { + k1 = j == 0 ? itemstack1.getCount() : 1; + if (k1 > slot2.getMaxStackSize(itemstack1) - itemstack2.getCount()) { + k1 = slot2.getMaxStackSize(itemstack1) - itemstack2.getCount(); + } + + if (k1 > itemstack1.getMaxStackSize() - itemstack2.getCount()) { + k1 = itemstack1.getMaxStackSize() - itemstack2.getCount(); + } + + itemstack1.subtract(k1); + itemstack2.add(k1); + } else if (itemstack1.getCount() <= slot2.getMaxStackSize(itemstack1)) { + slot2.set(itemstack1); + playerinventory.setCarried(itemstack2); + } + } else if (itemstack1.getMaxStackSize() > 1 && a(itemstack2, itemstack1) && !itemstack2.isEmpty()) { + k1 = itemstack2.getCount(); + if (k1 + itemstack1.getCount() <= itemstack1.getMaxStackSize()) { + itemstack1.add(k1); + itemstack2 = slot2.a(k1); + if (itemstack2.isEmpty()) { + slot2.set(ItemStack.a); + } + + slot2.a(entityhuman, playerinventory.getCarried()); + } + } + } + + slot2.f(); + // CraftBukkit start - Make sure the client has the right slot contents + if (entityhuman instanceof EntityPlayer && slot2.getMaxStackSize() != 64) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutSetSlot(this.windowId, slot2.rawSlotIndex, slot2.getItem())); + // Updating a crafting inventory makes the client reset the result slot, have to send it again + if (this.getBukkitView().getType() == InventoryType.WORKBENCH || this.getBukkitView().getType() == InventoryType.CRAFTING) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutSetSlot(this.windowId, 0, this.getSlot(0).getItem())); + } + } + // CraftBukkit end + } + } + } else if (inventoryclicktype == InventoryClickType.SWAP && j >= 0 && j < 9) { + slot2 = (Slot) this.slots.get(i); + itemstack2 = playerinventory.getItem(j); + itemstack1 = slot2.getItem(); + if (!itemstack2.isEmpty() || !itemstack1.isEmpty()) { + if (itemstack2.isEmpty()) { + if (slot2.isAllowed(entityhuman)) { + playerinventory.setItem(j, itemstack1); + slot2.b(itemstack1.getCount()); + slot2.set(ItemStack.a); + slot2.a(entityhuman, itemstack1); + } + } else if (itemstack1.isEmpty()) { + if (slot2.isAllowed(itemstack2)) { + k1 = slot2.getMaxStackSize(itemstack2); + if (itemstack2.getCount() > k1) { + slot2.set(itemstack2.cloneAndSubtract(k1)); + } else { + slot2.set(itemstack2); + playerinventory.setItem(j, ItemStack.a); + } + } + } else if (slot2.isAllowed(entityhuman) && slot2.isAllowed(itemstack2)) { + k1 = slot2.getMaxStackSize(itemstack2); + if (itemstack2.getCount() > k1) { + slot2.set(itemstack2.cloneAndSubtract(k1)); + slot2.a(entityhuman, itemstack1); + if (!playerinventory.pickup(itemstack1)) { + entityhuman.drop(itemstack1, true); + } + } else { + slot2.set(itemstack2); + playerinventory.setItem(j, itemstack1); + slot2.a(entityhuman, itemstack1); + } + } + } + } else if (inventoryclicktype == InventoryClickType.CLONE && entityhuman.abilities.canInstantlyBuild && playerinventory.getCarried().isEmpty() && i >= 0) { + slot2 = (Slot) this.slots.get(i); + if (slot2 != null && slot2.hasItem()) { + itemstack2 = slot2.getItem().cloneItemStack(); + itemstack2.setCount(itemstack2.getMaxStackSize()); + playerinventory.setCarried(itemstack2); + } + } else if (inventoryclicktype == InventoryClickType.THROW && playerinventory.getCarried().isEmpty() && i >= 0) { + slot2 = (Slot) this.slots.get(i); + if (slot2 != null && slot2.hasItem() && slot2.isAllowed(entityhuman)) { + itemstack2 = slot2.a(j == 0 ? 1 : slot2.getItem().getCount()); + slot2.a(entityhuman, itemstack2); + entityhuman.drop(itemstack2, true); + } + } else if (inventoryclicktype == InventoryClickType.PICKUP_ALL && i >= 0) { + slot2 = (Slot) this.slots.get(i); + itemstack2 = playerinventory.getCarried(); + if (!itemstack2.isEmpty() && (slot2 == null || !slot2.hasItem() || !slot2.isAllowed(entityhuman))) { + k = j == 0 ? 0 : this.slots.size() - 1; + k1 = j == 0 ? 1 : -1; + + for (int l1 = 0; l1 < 2; ++l1) { + for (int i2 = k; i2 >= 0 && i2 < this.slots.size() && itemstack2.getCount() < itemstack2.getMaxStackSize(); i2 += k1) { + Slot slot3 = (Slot) this.slots.get(i2); + + if (slot3.hasItem() && a(slot3, itemstack2, true) && slot3.isAllowed(entityhuman) && this.a(itemstack2, slot3)) { + ItemStack itemstack5 = slot3.getItem(); + + if (l1 != 0 || itemstack5.getCount() != itemstack5.getMaxStackSize()) { + l = Math.min(itemstack2.getMaxStackSize() - itemstack2.getCount(), itemstack5.getCount()); + ItemStack itemstack6 = slot3.a(l); + + itemstack2.add(l); + if (itemstack6.isEmpty()) { + slot3.set(ItemStack.a); + } + + slot3.a(entityhuman, itemstack6); + } + } + } + } + } + + this.b(); + } + } + + return itemstack; + } + + public static boolean a(ItemStack itemstack, ItemStack itemstack1) { + return itemstack.getItem() == itemstack1.getItem() && ItemStack.equals(itemstack, itemstack1); + } + + public boolean a(ItemStack itemstack, Slot slot) { + return true; + } + + public void b(EntityHuman entityhuman) { + PlayerInventory playerinventory = entityhuman.inventory; + + if (!playerinventory.getCarried().isEmpty()) { + // CraftBukkit start - SPIGOT-4556 + ItemStack carried = playerinventory.getCarried(); + playerinventory.setCarried(ItemStack.a); + entityhuman.drop(carried, false); + // CraftBukkit end + } + + } + + protected void a(EntityHuman entityhuman, World world, IInventory iinventory) { + int i; + + if (entityhuman.isAlive() && (!(entityhuman instanceof EntityPlayer) || !((EntityPlayer) entityhuman).o())) { + for (i = 0; i < iinventory.getSize(); ++i) { + entityhuman.inventory.a(world, iinventory.splitWithoutUpdate(i)); + } + + } else { + for (i = 0; i < iinventory.getSize(); ++i) { + entityhuman.drop(iinventory.splitWithoutUpdate(i), false); + } + + } + } + + public void a(IInventory iinventory) { + this.b(); + } + + public void setItem(int i, ItemStack itemstack) { + this.getSlot(i).set(itemstack); + } + + public boolean c(EntityHuman entityhuman) { + return !this.i.contains(entityhuman); + } + + public void a(EntityHuman entityhuman, boolean flag) { + if (flag) { + this.i.remove(entityhuman); + } else { + this.i.add(entityhuman); + } + + } + + public abstract boolean canUse(EntityHuman entityhuman); + + protected boolean a(ItemStack itemstack, int i, int j, boolean flag) { + boolean flag1 = false; + int k = i; + + if (flag) { + k = j - 1; + } + + Slot slot; + ItemStack itemstack1; + + if (itemstack.isStackable()) { + while (!itemstack.isEmpty()) { + if (flag) { + if (k < i) { + break; + } + } else if (k >= j) { + break; + } + + slot = (Slot) this.slots.get(k); + itemstack1 = slot.getItem(); + if (!itemstack1.isEmpty() && a(itemstack, itemstack1)) { + int l = itemstack1.getCount() + itemstack.getCount(); + + if (l <= itemstack.getMaxStackSize()) { + itemstack.setCount(0); + itemstack1.setCount(l); + slot.f(); + flag1 = true; + } else if (itemstack1.getCount() < itemstack.getMaxStackSize()) { + itemstack.subtract(itemstack.getMaxStackSize() - itemstack1.getCount()); + itemstack1.setCount(itemstack.getMaxStackSize()); + slot.f(); + flag1 = true; + } + } + + if (flag) { + --k; + } else { + ++k; + } + } + } + + if (!itemstack.isEmpty()) { + if (flag) { + k = j - 1; + } else { + k = i; + } + + while (true) { + if (flag) { + if (k < i) { + break; + } + } else if (k >= j) { + break; + } + + slot = (Slot) this.slots.get(k); + itemstack1 = slot.getItem(); + if (itemstack1.isEmpty() && slot.isAllowed(itemstack)) { + if (itemstack.getCount() > slot.getMaxStackSize()) { + slot.set(itemstack.cloneAndSubtract(slot.getMaxStackSize())); + } else { + slot.set(itemstack.cloneAndSubtract(itemstack.getCount())); + } + + slot.f(); + flag1 = true; + break; + } + + if (flag) { + --k; + } else { + ++k; + } + } + } + + return flag1; + } + + public static int b(int i) { + return i >> 2 & 3; + } + + public static int c(int i) { + return i & 3; + } + + public static boolean a(int i, EntityHuman entityhuman) { + return i == 0 ? true : (i == 1 ? true : i == 2 && entityhuman.abilities.canInstantlyBuild); + } + + protected void c() { + this.g = 0; + this.h.clear(); + } + + public static boolean a(@Nullable Slot slot, ItemStack itemstack, boolean flag) { + boolean flag1 = slot == null || !slot.hasItem(); + + return !flag1 && itemstack.doMaterialsMatch(slot.getItem()) && ItemStack.equals(slot.getItem(), itemstack) ? slot.getItem().getCount() + (flag ? 0 : itemstack.getCount()) <= itemstack.getMaxStackSize() : flag1; + } + + public static void a(Set set, int i, ItemStack itemstack, int j) { + switch (i) { + case 0: + itemstack.setCount(MathHelper.d((float) itemstack.getCount() / (float) set.size())); + break; + case 1: + itemstack.setCount(1); + break; + case 2: + itemstack.setCount(itemstack.getItem().getMaxStackSize()); + } + + itemstack.add(j); + } + + public boolean b(Slot slot) { + return true; + } + + public static int a(@Nullable TileEntity tileentity) { + return tileentity instanceof IInventory ? b((IInventory) tileentity) : 0; + } + + public static int b(@Nullable IInventory iinventory) { + if (iinventory == null) { + return 0; + } else { + int i = 0; + float f = 0.0F; + + for (int j = 0; j < iinventory.getSize(); ++j) { + ItemStack itemstack = iinventory.getItem(j); + + if (!itemstack.isEmpty()) { + f += (float) itemstack.getCount() / (float) Math.min(iinventory.getMaxStackSize(), itemstack.getMaxStackSize()); + ++i; + } + } + + f /= (float) iinventory.getSize(); + return MathHelper.d(f * 14.0F) + (i > 0 ? 1 : 0); + } + } + + protected void a(World world, EntityHuman entityhuman, IInventory iinventory, InventoryCraftResult inventorycraftresult) { + if (!world.isClientSide) { + EntityPlayer entityplayer = (EntityPlayer) entityhuman; + ItemStack itemstack = ItemStack.a; + IRecipe irecipe = world.getMinecraftServer().getCraftingManager().b(iinventory, world); + + if (inventorycraftresult.a(world, entityplayer, irecipe) && irecipe != null) { + itemstack = irecipe.craftItem(iinventory); + } + itemstack = org.bukkit.craftbukkit.event.CraftEventFactory.callPreCraftEvent(iinventory, inventorycraftresult, itemstack, getBukkitView(), false); // CraftBukkit + + inventorycraftresult.setItem(0, itemstack); + entityplayer.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.windowId, 0, itemstack)); + } + } +} diff --git a/src/main/java/net/minecraft/server/ContainerAnvil.java b/src/main/java/net/minecraft/server/ContainerAnvil.java new file mode 100644 index 000000000000..d206e0465714 --- /dev/null +++ b/src/main/java/net/minecraft/server/ContainerAnvil.java @@ -0,0 +1,408 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +// CraftBukkit end + +public class ContainerAnvil extends Container { + + private static final Logger f = LogManager.getLogger(); + private final IInventory resultInventory = new InventoryCraftResult(); + private final IInventory repairInventory = new InventorySubcontainer(new ChatComponentText("Repair"), 2) { + public void update() { + super.update(); + ContainerAnvil.this.a((IInventory) this); + } + }; + private final World world; + private final BlockPosition position; + public int levelCost; + private int k; + public String renameText; + private final EntityHuman player; + // CraftBukkit start + public int maximumRepairCost = 40; + private int lastLevelCost; + private CraftInventoryView bukkitEntity; + private PlayerInventory playerInventory; + // CraftBukkit end + + public ContainerAnvil(PlayerInventory playerinventory, final World world, final BlockPosition blockposition, EntityHuman entityhuman) { + this.playerInventory = playerinventory; // CraftBukkit + this.position = blockposition; + this.world = world; + this.player = entityhuman; + this.a(new Slot(this.repairInventory, 0, 27, 47)); + this.a(new Slot(this.repairInventory, 1, 76, 47)); + this.a(new Slot(this.resultInventory, 2, 134, 47) { + public boolean isAllowed(ItemStack itemstack) { + return false; + } + + public boolean isAllowed(EntityHuman entityhuman1) { + return (entityhuman1.abilities.canInstantlyBuild || entityhuman1.expLevel >= ContainerAnvil.this.levelCost) && ContainerAnvil.this.levelCost > 0 && this.hasItem(); + } + + public ItemStack a(EntityHuman entityhuman1, ItemStack itemstack) { + if (!entityhuman1.abilities.canInstantlyBuild) { + entityhuman1.levelDown(-ContainerAnvil.this.levelCost); + } + + ContainerAnvil.this.repairInventory.setItem(0, ItemStack.a); + if (ContainerAnvil.this.k > 0) { + ItemStack itemstack1 = ContainerAnvil.this.repairInventory.getItem(1); + + if (!itemstack1.isEmpty() && itemstack1.getCount() > ContainerAnvil.this.k) { + itemstack1.subtract(ContainerAnvil.this.k); + ContainerAnvil.this.repairInventory.setItem(1, itemstack1); + } else { + ContainerAnvil.this.repairInventory.setItem(1, ItemStack.a); + } + } else { + ContainerAnvil.this.repairInventory.setItem(1, ItemStack.a); + } + + ContainerAnvil.this.levelCost = 0; + IBlockData iblockdata = world.getType(blockposition); + + if (!world.isClientSide) { + if (!entityhuman1.abilities.canInstantlyBuild && iblockdata.a(TagsBlock.ANVIL) && entityhuman1.getRandom().nextFloat() < 0.12F) { + IBlockData iblockdata1 = BlockAnvil.a_(iblockdata); + // Paper start + com.destroystokyo.paper.event.block.AnvilDamagedEvent event = new com.destroystokyo.paper.event.block.AnvilDamagedEvent(getBukkitView(), iblockdata1 != null ? org.bukkit.craftbukkit.block.data.CraftBlockData.fromData(iblockdata1) : null); + if (!event.callEvent()) { + return itemstack; + } else if (event.getDamageState() == com.destroystokyo.paper.event.block.AnvilDamagedEvent.DamageState.BROKEN) { + iblockdata1 = null; + } else { + iblockdata1 = ((org.bukkit.craftbukkit.block.data.CraftBlockData) event.getDamageState().getMaterial().createBlockData()).getState().set(BlockAnvil.FACING, iblockdata.get(BlockAnvil.FACING)); + } + // Paper end + + if (iblockdata1 == null) { + world.setAir(blockposition); + world.triggerEffect(1029, blockposition, 0); + } else { + world.setTypeAndData(blockposition, iblockdata1, 2); + world.triggerEffect(1030, blockposition, 0); + } + } else { + world.triggerEffect(1030, blockposition, 0); + } + } + + return itemstack; + } + }); + + int i; + + for (i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.a(new Slot(playerinventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(playerinventory, i, 8 + i * 18, 142)); + } + + } + + public void a(IInventory iinventory) { + super.a(iinventory); + if (iinventory == this.repairInventory) { + this.d(); + } + + } + + public void d() { + ItemStack itemstack = this.repairInventory.getItem(0); + + this.levelCost = 1; + int i = 0; + byte b0 = 0; + byte b1 = 0; + + if (itemstack.isEmpty()) { + org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(getBukkitView(), ItemStack.a); // CraftBukkit + this.levelCost = 0; + } else { + ItemStack itemstack1 = itemstack.cloneItemStack(); + ItemStack itemstack2 = this.repairInventory.getItem(1); + Map map = EnchantmentManager.a(itemstack1); + int j = b0 + itemstack.getRepairCost() + (itemstack2.isEmpty() ? 0 : itemstack2.getRepairCost()); + + this.k = 0; + if (!itemstack2.isEmpty()) { + boolean flag = itemstack2.getItem() == Items.ENCHANTED_BOOK && !ItemEnchantedBook.e(itemstack2).isEmpty(); + int k; + int l; + int i1; + + if (itemstack1.e() && itemstack1.getItem().a(itemstack, itemstack2)) { + k = Math.min(itemstack1.getDamage(), itemstack1.h() / 4); + if (k <= 0) { + org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(getBukkitView(), ItemStack.a); // CraftBukkit + this.levelCost = 0; + return; + } + + for (i1 = 0; k > 0 && i1 < itemstack2.getCount(); ++i1) { + l = itemstack1.getDamage() - k; + itemstack1.setDamage(l); + ++i; + k = Math.min(itemstack1.getDamage(), itemstack1.h() / 4); + } + + this.k = i1; + } else { + if (!flag && (itemstack1.getItem() != itemstack2.getItem() || !itemstack1.e())) { + org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(getBukkitView(), ItemStack.a); // CraftBukkit + this.levelCost = 0; + return; + } + + if (itemstack1.e() && !flag) { + k = itemstack.h() - itemstack.getDamage(); + i1 = itemstack2.h() - itemstack2.getDamage(); + l = i1 + itemstack1.h() * 12 / 100; + int j1 = k + l; + int k1 = itemstack1.h() - j1; + + if (k1 < 0) { + k1 = 0; + } + + if (k1 < itemstack1.getDamage()) { + itemstack1.setDamage(k1); + i += 2; + } + } + + Map map1 = EnchantmentManager.a(itemstack2); + boolean flag1 = false; + boolean flag2 = false; + Iterator iterator = map1.keySet().iterator(); + + while (iterator.hasNext()) { + Enchantment enchantment = (Enchantment) iterator.next(); + + if (enchantment != null) { + int l1 = map.containsKey(enchantment) ? (Integer) map.get(enchantment) : 0; + int i2 = (Integer) map1.get(enchantment); + + i2 = l1 == i2 ? i2 + 1 : Math.max(i2, l1); + boolean flag3 = enchantment.canEnchant(itemstack); + + if (this.player.abilities.canInstantlyBuild || itemstack.getItem() == Items.ENCHANTED_BOOK) { + flag3 = true; + } + + Iterator iterator1 = map.keySet().iterator(); + + while (iterator1.hasNext()) { + Enchantment enchantment1 = (Enchantment) iterator1.next(); + + if (enchantment1 != enchantment && !enchantment.b(enchantment1)) { + flag3 = false; + ++i; + } + } + + if (!flag3) { + flag2 = true; + } else { + flag1 = true; + if (i2 > enchantment.getMaxLevel()) { + i2 = enchantment.getMaxLevel(); + } + + map.put(enchantment, i2); + int j2 = 0; + + switch (enchantment.d()) { + case COMMON: + j2 = 1; + break; + case UNCOMMON: + j2 = 2; + break; + case RARE: + j2 = 4; + break; + case VERY_RARE: + j2 = 8; + } + + if (flag) { + j2 = Math.max(1, j2 / 2); + } + + i += j2 * i2; + if (itemstack.getCount() > 1) { + i = 40; + } + } + } + } + + if (flag2 && !flag1) { + org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(getBukkitView(), ItemStack.a); // CraftBukkit + this.levelCost = 0; + return; + } + } + } + + if (StringUtils.isBlank(this.renameText)) { + if (itemstack.hasName()) { + b1 = 1; + i += b1; + itemstack1.r(); + } + } else if (!this.renameText.equals(itemstack.getName().getString())) { + b1 = 1; + i += b1; + itemstack1.a((IChatBaseComponent) (new ChatComponentText(this.renameText))); + } + + this.levelCost = j + i; + if (i <= 0) { + itemstack1 = ItemStack.a; + } + + if (b1 == i && b1 > 0 && this.levelCost >= maximumRepairCost) { // CraftBukkit + this.levelCost = maximumRepairCost - 1; // CraftBukkit + } + + if (this.levelCost >= maximumRepairCost && !this.player.abilities.canInstantlyBuild) { // CraftBukkit + itemstack1 = ItemStack.a; + } + + if (!itemstack1.isEmpty()) { + int k2 = itemstack1.getRepairCost(); + + if (!itemstack2.isEmpty() && k2 < itemstack2.getRepairCost()) { + k2 = itemstack2.getRepairCost(); + } + + if (b1 != i || b1 == 0) { + k2 = k2 * 2 + 1; + } + + itemstack1.setRepairCost(k2); + EnchantmentManager.a(map, itemstack1); + } + + org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareAnvilEvent(getBukkitView(), itemstack1); // CraftBukkit + this.b(); + } + } + + public void addSlotListener(ICrafting icrafting) { + super.addSlotListener(icrafting); + icrafting.setContainerData(this, 0, this.levelCost); + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + if (!this.world.isClientSide) { + this.a(entityhuman, this.world, this.repairInventory); + } + } + + public boolean canUse(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return !this.world.getType(this.position).a(TagsBlock.ANVIL) ? false : entityhuman.d((double) this.position.getX() + 0.5D, (double) this.position.getY() + 0.5D, (double) this.position.getZ() + 0.5D) <= 64.0D; + } + + public ItemStack shiftClick(EntityHuman entityhuman, int i) { + ItemStack itemstack = ItemStack.a; + Slot slot = (Slot) this.slots.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i == 2) { + if (!this.a(itemstack1, 3, 39, true)) { + return ItemStack.a; + } + + slot.a(itemstack1, itemstack); + } else if (i != 0 && i != 1) { + if (i >= 3 && i < 39 && !this.a(itemstack1, 0, 2, false)) { + return ItemStack.a; + } + } else if (!this.a(itemstack1, 3, 39, false)) { + return ItemStack.a; + } + + if (itemstack1.isEmpty()) { + slot.set(ItemStack.a); + } else { + slot.f(); + } + + if (itemstack1.getCount() == itemstack.getCount()) { + return ItemStack.a; + } + + slot.a(entityhuman, itemstack1); + } + + return itemstack; + } + + public void a(String s) { + this.renameText = s; + if (this.getSlot(2).hasItem()) { + ItemStack itemstack = this.getSlot(2).getItem(); + + if (StringUtils.isBlank(s)) { + itemstack.r(); + } else { + itemstack.a((IChatBaseComponent) (new ChatComponentText(this.renameText))); + } + } + + this.d(); + } + + // CraftBukkit start + @Override + public void b() { + super.b(); + + for (int i = 0; i < this.listeners.size(); ++i) { + ICrafting icrafting = (ICrafting) this.listeners.get(i); + + //if (this.lastLevelCost != this.levelCost) { // Paper - this was the wrong solution to this, fixing it correctly in CraftPlayer + icrafting.setContainerData(this, 0, this.levelCost); + //} // Paper + } + + this.lastLevelCost = this.levelCost; + } + + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + org.bukkit.craftbukkit.inventory.CraftInventory inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryAnvil( + new org.bukkit.Location(world.getWorld(), position.getX(), position.getY(), position.getZ()), this.repairInventory, this.resultInventory, this); + bukkitEntity = new CraftInventoryView(this.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/ContainerBeacon.java b/src/main/java/net/minecraft/server/ContainerBeacon.java new file mode 100644 index 000000000000..5cec73391f3e --- /dev/null +++ b/src/main/java/net/minecraft/server/ContainerBeacon.java @@ -0,0 +1,137 @@ +package net.minecraft.server; + +import org.bukkit.craftbukkit.inventory.CraftInventoryView; // CraftBukkit + +public class ContainerBeacon extends Container { + + private final IInventory beacon; + private final ContainerBeacon.SlotBeacon f; + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + // CraftBukkit end + + public ContainerBeacon(IInventory iinventory, IInventory iinventory1) { + player = (PlayerInventory) iinventory; // CraftBukkit - TODO: check this + this.beacon = iinventory1; + this.f = new ContainerBeacon.SlotBeacon(iinventory1, 0, 136, 110); + this.a((Slot) this.f); + boolean flag = true; + boolean flag1 = true; + + int i; + + for (i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.a(new Slot(iinventory, j + i * 9 + 9, 36 + j * 18, 137 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(iinventory, i, 36 + i * 18, 195)); + } + + } + + public void addSlotListener(ICrafting icrafting) { + super.addSlotListener(icrafting); + icrafting.setContainerData(this, this.beacon); + } + + public IInventory d() { + return this.beacon; + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + if (!entityhuman.world.isClientSide) { + ItemStack itemstack = this.f.a(this.f.getMaxStackSize()); + + if (!itemstack.isEmpty()) { + entityhuman.drop(itemstack, false); + } + + } + } + + public boolean canUse(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.beacon.a(entityhuman); + } + + public ItemStack shiftClick(EntityHuman entityhuman, int i) { + ItemStack itemstack = ItemStack.a; + Slot slot = (Slot) this.slots.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i == 0) { + if (!this.a(itemstack1, 1, 37, true)) { + return ItemStack.a; + } + + slot.a(itemstack1, itemstack); + } else if (!this.f.hasItem() && this.f.isAllowed(itemstack1) && itemstack1.getCount() == 1) { + if (!this.a(itemstack1, 0, 1, false)) { + return ItemStack.a; + } + } else if (i >= 1 && i < 28) { + if (!this.a(itemstack1, 28, 37, false)) { + return ItemStack.a; + } + } else if (i >= 28 && i < 37) { + if (!this.a(itemstack1, 1, 28, false)) { + return ItemStack.a; + } + } else if (!this.a(itemstack1, 1, 37, false)) { + return ItemStack.a; + } + + if (itemstack1.isEmpty()) { + slot.set(ItemStack.a); + } else { + slot.f(); + } + + if (itemstack1.getCount() == itemstack.getCount()) { + return ItemStack.a; + } + + slot.a(entityhuman, itemstack1); + } + + return itemstack; + } + + class SlotBeacon extends Slot { + + public SlotBeacon(IInventory iinventory, int i, int j, int k) { + super(iinventory, i, j, k); + } + + public boolean isAllowed(ItemStack itemstack) { + Item item = itemstack.getItem(); + + return item == Items.EMERALD || item == Items.DIAMOND || item == Items.GOLD_INGOT || item == Items.IRON_INGOT; + } + + public int getMaxStackSize() { + return 1; + } + } + + // CraftBukkit start + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + org.bukkit.craftbukkit.inventory.CraftInventory inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryBeacon((TileEntityBeacon) this.beacon); // TODO - check this + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/ContainerBrewingStand.java b/src/main/java/net/minecraft/server/ContainerBrewingStand.java new file mode 100644 index 000000000000..b475578e6a09 --- /dev/null +++ b/src/main/java/net/minecraft/server/ContainerBrewingStand.java @@ -0,0 +1,206 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventoryBrewer; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +// CraftBukkit end + +public class ContainerBrewingStand extends Container { + + private final IInventory brewingStand; + private final Slot f; + private int g; + private int h; + + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + // CraftBukkit end + + public ContainerBrewingStand(PlayerInventory playerinventory, IInventory iinventory) { + player = playerinventory; // CraftBukkit + this.brewingStand = iinventory; + this.a((Slot) (new ContainerBrewingStand.SlotPotionBottle(iinventory, 0, 56, 51))); + this.a((Slot) (new ContainerBrewingStand.SlotPotionBottle(iinventory, 1, 79, 58))); + this.a((Slot) (new ContainerBrewingStand.SlotPotionBottle(iinventory, 2, 102, 51))); + this.f = this.a((Slot) (new ContainerBrewingStand.SlotBrewing(iinventory, 3, 79, 17))); + this.a((Slot) (new ContainerBrewingStand.a(iinventory, 4, 17, 17))); + + int i; + + for (i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.a(new Slot(playerinventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(playerinventory, i, 8 + i * 18, 142)); + } + + } + + public void addSlotListener(ICrafting icrafting) { + super.addSlotListener(icrafting); + icrafting.setContainerData(this, this.brewingStand); + } + + public void b() { + super.b(); + + for (int i = 0; i < this.listeners.size(); ++i) { + ICrafting icrafting = (ICrafting) this.listeners.get(i); + + if (this.g != this.brewingStand.getProperty(0)) { + icrafting.setContainerData(this, 0, this.brewingStand.getProperty(0)); + } + + if (this.h != this.brewingStand.getProperty(1)) { + icrafting.setContainerData(this, 1, this.brewingStand.getProperty(1)); + } + } + + this.g = this.brewingStand.getProperty(0); + this.h = this.brewingStand.getProperty(1); + } + + public boolean canUse(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.brewingStand.a(entityhuman); + } + + public ItemStack shiftClick(EntityHuman entityhuman, int i) { + ItemStack itemstack = ItemStack.a; + Slot slot = (Slot) this.slots.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if ((i < 0 || i > 2) && i != 3 && i != 4) { + if (this.f.isAllowed(itemstack1)) { + if (!this.a(itemstack1, 3, 4, false)) { + return ItemStack.a; + } + } else if (ContainerBrewingStand.SlotPotionBottle.c_(itemstack) && itemstack.getCount() == 1) { + if (!this.a(itemstack1, 0, 3, false)) { + return ItemStack.a; + } + } else if (ContainerBrewingStand.a.b_(itemstack)) { + if (!this.a(itemstack1, 4, 5, false)) { + return ItemStack.a; + } + } else if (i >= 5 && i < 32) { + if (!this.a(itemstack1, 32, 41, false)) { + return ItemStack.a; + } + } else if (i >= 32 && i < 41) { + if (!this.a(itemstack1, 5, 32, false)) { + return ItemStack.a; + } + } else if (!this.a(itemstack1, 5, 41, false)) { + return ItemStack.a; + } + } else { + if (!this.a(itemstack1, 5, 41, true)) { + return ItemStack.a; + } + + slot.a(itemstack1, itemstack); + } + + if (itemstack1.isEmpty()) { + slot.set(ItemStack.a); + } else { + slot.f(); + } + + if (itemstack1.getCount() == itemstack.getCount()) { + return ItemStack.a; + } + + slot.a(entityhuman, itemstack1); + } + + return itemstack; + } + + static class a extends Slot { + + public a(IInventory iinventory, int i, int j, int k) { + super(iinventory, i, j, k); + } + + public boolean isAllowed(ItemStack itemstack) { + return b_(itemstack); + } + + public static boolean b_(ItemStack itemstack) { + return itemstack.getItem() == Items.BLAZE_POWDER; + } + + public int getMaxStackSize() { + return 64; + } + } + + static class SlotBrewing extends Slot { + + public SlotBrewing(IInventory iinventory, int i, int j, int k) { + super(iinventory, i, j, k); + } + + public boolean isAllowed(ItemStack itemstack) { + return PotionBrewer.a(itemstack); + } + + public int getMaxStackSize() { + return 64; + } + } + + static class SlotPotionBottle extends Slot { + + public SlotPotionBottle(IInventory iinventory, int i, int j, int k) { + super(iinventory, i, j, k); + } + + public boolean isAllowed(ItemStack itemstack) { + return c_(itemstack); + } + + public int getMaxStackSize() { + return 1; + } + + public ItemStack a(EntityHuman entityhuman, ItemStack itemstack) { + PotionRegistry potionregistry = PotionUtil.d(itemstack); + + if (entityhuman instanceof EntityPlayer) { + CriterionTriggers.k.a((EntityPlayer) entityhuman, potionregistry); + } + + super.a(entityhuman, itemstack); + return itemstack; + } + + public static boolean c_(ItemStack itemstack) { + Item item = itemstack.getItem(); + + return item == Items.POTION || item == Items.SPLASH_POTION || item == Items.LINGERING_POTION || item == Items.GLASS_BOTTLE; + } + } + + // CraftBukkit start + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventoryBrewer inventory = new CraftInventoryBrewer(this.brewingStand); + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/ContainerChest.java b/src/main/java/net/minecraft/server/ContainerChest.java new file mode 100644 index 000000000000..8115991d9cea --- /dev/null +++ b/src/main/java/net/minecraft/server/ContainerChest.java @@ -0,0 +1,107 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +// CraftBukkit end + +public class ContainerChest extends Container { + + private final IInventory container; + private final int f; + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventory inventory; + if (this.container instanceof PlayerInventory) { + inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryPlayer((PlayerInventory) this.container); + } else if (this.container instanceof InventoryLargeChest) { + inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) this.container); + } else { + inventory = new CraftInventory(this.container); + } + + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end + + public ContainerChest(IInventory iinventory, IInventory iinventory1, EntityHuman entityhuman) { + this.container = iinventory1; + this.f = iinventory1.getSize() / 9; + iinventory1.startOpen(entityhuman); + int i = (this.f - 4) * 18; + + // CraftBukkit start - Save player + // TODO: Should we check to make sure it really is an InventoryPlayer? + this.player = (PlayerInventory) iinventory; + // CraftBukkit end + + int j; + int k; + + for (j = 0; j < this.f; ++j) { + for (k = 0; k < 9; ++k) { + this.a(new Slot(iinventory1, k + j * 9, 8 + k * 18, 18 + j * 18)); + } + } + + for (j = 0; j < 3; ++j) { + for (k = 0; k < 9; ++k) { + this.a(new Slot(iinventory, k + j * 9 + 9, 8 + k * 18, 103 + j * 18 + i)); + } + } + + for (j = 0; j < 9; ++j) { + this.a(new Slot(iinventory, j, 8 + j * 18, 161 + i)); + } + + } + + public boolean canUse(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.container.a(entityhuman); + } + + public ItemStack shiftClick(EntityHuman entityhuman, int i) { + ItemStack itemstack = ItemStack.a; + Slot slot = (Slot) this.slots.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i < this.f * 9) { + if (!this.a(itemstack1, this.f * 9, this.slots.size(), true)) { + return ItemStack.a; + } + } else if (!this.a(itemstack1, 0, this.f * 9, false)) { + return ItemStack.a; + } + + if (itemstack1.isEmpty()) { + slot.set(ItemStack.a); + } else { + slot.f(); + } + } + + return itemstack; + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + this.container.closeContainer(entityhuman); + } + + public IInventory d() { + return this.container; + } +} diff --git a/src/main/java/net/minecraft/server/ContainerDispenser.java b/src/main/java/net/minecraft/server/ContainerDispenser.java new file mode 100644 index 000000000000..8b6533d02256 --- /dev/null +++ b/src/main/java/net/minecraft/server/ContainerDispenser.java @@ -0,0 +1,93 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +// CraftBukkit end + +public class ContainerDispenser extends Container { + + public final IInventory items; + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + // CraftBukkit end + + public ContainerDispenser(IInventory iinventory, IInventory iinventory1) { + this.items = iinventory1; + // CraftBukkit start - Save player + // TODO: Should we check to make sure it really is an InventoryPlayer? + this.player = (PlayerInventory)iinventory; + // CraftBukkit end + + int i; + int j; + + for (i = 0; i < 3; ++i) { + for (j = 0; j < 3; ++j) { + this.a(new Slot(iinventory1, j + i * 3, 62 + j * 18, 17 + i * 18)); + } + } + + for (i = 0; i < 3; ++i) { + for (j = 0; j < 9; ++j) { + this.a(new Slot(iinventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(iinventory, i, 8 + i * 18, 142)); + } + + } + + public boolean canUse(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.items.a(entityhuman); + } + + public ItemStack shiftClick(EntityHuman entityhuman, int i) { + ItemStack itemstack = ItemStack.a; + Slot slot = (Slot) this.slots.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i < 9) { + if (!this.a(itemstack1, 9, 45, true)) { + return ItemStack.a; + } + } else if (!this.a(itemstack1, 0, 9, false)) { + return ItemStack.a; + } + + if (itemstack1.isEmpty()) { + slot.set(ItemStack.a); + } else { + slot.f(); + } + + if (itemstack1.getCount() == itemstack.getCount()) { + return ItemStack.a; + } + + slot.a(entityhuman, itemstack1); + } + + return itemstack; + } + + // CraftBukkit start + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventory inventory = new CraftInventory(this.items); + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/ContainerEnchantTable.java b/src/main/java/net/minecraft/server/ContainerEnchantTable.java new file mode 100644 index 000000000000..a00977b35d7a --- /dev/null +++ b/src/main/java/net/minecraft/server/ContainerEnchantTable.java @@ -0,0 +1,409 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.Random; + +// CraftBukkit start +import java.util.Collections; +import java.util.Map; +import org.bukkit.Location; +import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.inventory.CraftInventoryEnchanting; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.enchantments.EnchantmentOffer; +import org.bukkit.event.enchantment.EnchantItemEvent; +import org.bukkit.event.enchantment.PrepareItemEnchantEvent; +import org.bukkit.entity.Player; +// CraftBukkit end + +public class ContainerEnchantTable extends Container { + + public IInventory enchantSlots = new InventorySubcontainer(new ChatComponentText("Enchant"), 2) { + public int getMaxStackSize() { + return 64; + } + + public void update() { + super.update(); + ContainerEnchantTable.this.a((IInventory) this); + } + + // CraftBukkit start + @Override + public Location getLocation() { + return new org.bukkit.Location(world.getWorld(), position.getX(), position.getY(), position.getZ()); + } + // CraftBukkit end + }; + public World world; + private final BlockPosition position; + private final Random l = new Random(); + public int f; + public int[] costs = new int[3]; + public int[] h = new int[] { -1, -1, -1}; + public int[] i = new int[] { -1, -1, -1}; + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private Player player; + // CraftBukkit end + + public ContainerEnchantTable(PlayerInventory playerinventory, World world, BlockPosition blockposition) { + this.world = world; + this.position = blockposition; + this.f = playerinventory.player.du(); + this.a(new Slot(this.enchantSlots, 0, 15, 47) { + public boolean isAllowed(ItemStack itemstack) { + return true; + } + + public int getMaxStackSize() { + return 1; + } + }); + this.a(new Slot(this.enchantSlots, 1, 35, 47) { + public boolean isAllowed(ItemStack itemstack) { + return itemstack.getItem() == Items.LAPIS_LAZULI; + } + }); + + int i; + + for (i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.a(new Slot(playerinventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(playerinventory, i, 8 + i * 18, 142)); + } + + // CraftBukkit start + player = (Player) playerinventory.player.getBukkitEntity(); + // CraftBukkit end + } + + protected void c(ICrafting icrafting) { + icrafting.setContainerData(this, 0, this.costs[0]); + icrafting.setContainerData(this, 1, this.costs[1]); + icrafting.setContainerData(this, 2, this.costs[2]); + icrafting.setContainerData(this, 3, this.f & -16); + icrafting.setContainerData(this, 4, this.h[0]); + icrafting.setContainerData(this, 5, this.h[1]); + icrafting.setContainerData(this, 6, this.h[2]); + icrafting.setContainerData(this, 7, this.i[0]); + icrafting.setContainerData(this, 8, this.i[1]); + icrafting.setContainerData(this, 9, this.i[2]); + } + + public void addSlotListener(ICrafting icrafting) { + super.addSlotListener(icrafting); + this.c(icrafting); + } + + public void b() { + super.b(); + + for (int i = 0; i < this.listeners.size(); ++i) { + ICrafting icrafting = (ICrafting) this.listeners.get(i); + + this.c(icrafting); + } + + } + + public void a(IInventory iinventory) { + if (iinventory == this.enchantSlots) { + ItemStack itemstack = iinventory.getItem(0); + int i; + + if (!itemstack.isEmpty()) { // CraftBukkit - relax condition + if (!this.world.isClientSide) { + i = 0; + + int j; + + for (j = -1; j <= 1; ++j) { + for (int k = -1; k <= 1; ++k) { + if ((j != 0 || k != 0) && this.world.isEmpty(this.position.a(k, 0, j)) && this.world.isEmpty(this.position.a(k, 1, j))) { + if (this.world.getType(this.position.a(k * 2, 0, j * 2)).getBlock() == Blocks.BOOKSHELF) { + ++i; + } + + if (this.world.getType(this.position.a(k * 2, 1, j * 2)).getBlock() == Blocks.BOOKSHELF) { + ++i; + } + + if (k != 0 && j != 0) { + if (this.world.getType(this.position.a(k * 2, 0, j)).getBlock() == Blocks.BOOKSHELF) { + ++i; + } + + if (this.world.getType(this.position.a(k * 2, 1, j)).getBlock() == Blocks.BOOKSHELF) { + ++i; + } + + if (this.world.getType(this.position.a(k, 0, j * 2)).getBlock() == Blocks.BOOKSHELF) { + ++i; + } + + if (this.world.getType(this.position.a(k, 1, j * 2)).getBlock() == Blocks.BOOKSHELF) { + ++i; + } + } + } + } + } + + this.l.setSeed((long) this.f); + + for (j = 0; j < 3; ++j) { + this.costs[j] = EnchantmentManager.a(this.l, j, i, itemstack); + this.h[j] = -1; + this.i[j] = -1; + if (this.costs[j] < j + 1) { + this.costs[j] = 0; + } + } + + for (j = 0; j < 3; ++j) { + if (this.costs[j] > 0) { + List list = this.a(itemstack, j, this.costs[j]); + + if (list != null && !list.isEmpty()) { + WeightedRandomEnchant weightedrandomenchant = (WeightedRandomEnchant) list.get(this.l.nextInt(list.size())); + + this.h[j] = IRegistry.ENCHANTMENT.a(weightedrandomenchant.enchantment); // CraftBukkit - decompile error + this.i[j] = weightedrandomenchant.level; + } + } + } + + // CraftBukkit start + CraftItemStack item = CraftItemStack.asCraftMirror(itemstack); + org.bukkit.enchantments.EnchantmentOffer[] offers = new EnchantmentOffer[3]; + for (j = 0; j < 3; ++j) { + org.bukkit.enchantments.Enchantment enchantment = (this.h[j] >= 0) ? org.bukkit.enchantments.Enchantment.getByKey(CraftNamespacedKey.fromMinecraft(IRegistry.ENCHANTMENT.getKey(IRegistry.ENCHANTMENT.fromId(this.h[j])))) : null; + offers[j] = (enchantment != null) ? new EnchantmentOffer(enchantment, this.i[j], this.costs[j]) : null; + } + + PrepareItemEnchantEvent event = new PrepareItemEnchantEvent(player, this.getBukkitView(), this.world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()), item, offers, i); + event.setCancelled(!itemstack.canEnchant()); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + for (j = 0; j < 3; ++j) { + this.costs[j] = 0; + this.h[j] = -1; + this.i[j] = -1; + } + return; + } + + for (j = 0; j < 3; j++) { + EnchantmentOffer offer = event.getOffers()[j]; + if (offer != null) { + this.costs[j] = offer.getCost(); + this.h[j] = IRegistry.ENCHANTMENT.a(IRegistry.ENCHANTMENT.get(CraftNamespacedKey.toMinecraft(offer.getEnchantment().getKey()))); + this.i[j] = offer.getEnchantmentLevel(); + } else { + this.costs[j] = 0; + this.h[j] = -1; + this.i[j] = -1; + } + } + // CraftBukkit end + + this.b(); + } + } else { + for (i = 0; i < 3; ++i) { + this.costs[i] = 0; + this.h[i] = -1; + this.i[i] = -1; + } + } + } + + } + + public boolean a(EntityHuman entityhuman, int i) { + ItemStack itemstack = this.enchantSlots.getItem(0); + ItemStack itemstack1 = this.enchantSlots.getItem(1); + int j = i + 1; + + if ((itemstack1.isEmpty() || itemstack1.getCount() < j) && !entityhuman.abilities.canInstantlyBuild) { + return false; + } else if (this.costs[i] > 0 && !itemstack.isEmpty() && (entityhuman.expLevel >= j && entityhuman.expLevel >= this.costs[i] || entityhuman.abilities.canInstantlyBuild)) { + if (!this.world.isClientSide) { + List list = this.a(itemstack, i, this.costs[i]); + + // CraftBukkit start + if (true || !list.isEmpty()) { + // entityhuman.enchantDone(itemstack, j); // Moved down + boolean flag = itemstack.getItem() == Items.BOOK; + Map enchants = new java.util.HashMap(); + for (Object obj : list) { + WeightedRandomEnchant instance = (WeightedRandomEnchant) obj; + enchants.put(org.bukkit.enchantments.Enchantment.getByKey(CraftNamespacedKey.fromMinecraft(IRegistry.ENCHANTMENT.getKey(instance.enchantment))), instance.level); + } + CraftItemStack item = CraftItemStack.asCraftMirror(itemstack); + + EnchantItemEvent event = new EnchantItemEvent((Player) entityhuman.getBukkitEntity(), this.getBukkitView(), this.world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()), item, this.costs[i], enchants, i); + this.world.getServer().getPluginManager().callEvent(event); + + int level = event.getExpLevelCost(); + if (event.isCancelled() || (level > entityhuman.expLevel && !entityhuman.abilities.canInstantlyBuild) || event.getEnchantsToAdd().isEmpty()) { + return false; + } + + if (flag) { + itemstack = new ItemStack(Items.ENCHANTED_BOOK); + this.enchantSlots.setItem(0, itemstack); + } + + for (Map.Entry entry : event.getEnchantsToAdd().entrySet()) { + try { + if (flag) { + NamespacedKey enchantId = entry.getKey().getKey(); + Enchantment nms = IRegistry.ENCHANTMENT.get(CraftNamespacedKey.toMinecraft(enchantId)); + if (nms == null) { + continue; + } + + WeightedRandomEnchant weightedrandomenchant = new WeightedRandomEnchant(nms, entry.getValue()); + ItemEnchantedBook.a(itemstack, weightedrandomenchant); + } else { + item.addUnsafeEnchantment(entry.getKey(), entry.getValue()); + } + } catch (IllegalArgumentException e) { + /* Just swallow invalid enchantments */ + } + } + + entityhuman.enchantDone(itemstack, j); + // CraftBukkit end + + // CraftBukkit - TODO: let plugins change this + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack1.subtract(j); + if (itemstack1.isEmpty()) { + this.enchantSlots.setItem(1, ItemStack.a); + } + } + + entityhuman.a(StatisticList.ENCHANT_ITEM); + if (entityhuman instanceof EntityPlayer) { + CriterionTriggers.i.a((EntityPlayer) entityhuman, itemstack, j); + } + + this.enchantSlots.update(); + this.f = entityhuman.du(); + this.a(this.enchantSlots); + this.world.a((EntityHuman) null, this.position, SoundEffects.BLOCK_ENCHANTMENT_TABLE_USE, SoundCategory.BLOCKS, 1.0F, this.world.random.nextFloat() * 0.1F + 0.9F); + } + } + + return true; + } else { + return false; + } + } + + private List a(ItemStack itemstack, int i, int j) { + this.l.setSeed((long) (this.f + i)); + List list = EnchantmentManager.b(this.l, itemstack, j, false); + + if (itemstack.getItem() == Items.BOOK && list.size() > 1) { + list.remove(this.l.nextInt(list.size())); + } + + return list; + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + // CraftBukkit Start - If an enchantable was opened from a null location, set the world to the player's world, preventing a crash + if (this.world == null) { + this.world = entityhuman.getWorld(); + } + // CraftBukkit end + if (!this.world.isClientSide) { + this.a(entityhuman, entityhuman.world, this.enchantSlots); + } + } + + public boolean canUse(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.world.getType(this.position).getBlock() != Blocks.ENCHANTING_TABLE ? false : entityhuman.d((double) this.position.getX() + 0.5D, (double) this.position.getY() + 0.5D, (double) this.position.getZ() + 0.5D) <= 64.0D; + } + + public ItemStack shiftClick(EntityHuman entityhuman, int i) { + ItemStack itemstack = ItemStack.a; + Slot slot = (Slot) this.slots.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i == 0) { + if (!this.a(itemstack1, 2, 38, true)) { + return ItemStack.a; + } + } else if (i == 1) { + if (!this.a(itemstack1, 2, 38, true)) { + return ItemStack.a; + } + } else if (itemstack1.getItem() == Items.LAPIS_LAZULI) { + if (!this.a(itemstack1, 1, 2, true)) { + return ItemStack.a; + } + } else { + if (((Slot) this.slots.get(0)).hasItem() || !((Slot) this.slots.get(0)).isAllowed(itemstack1)) { + return ItemStack.a; + } + + if (itemstack1.hasTag() && itemstack1.getCount() == 1) { + ((Slot) this.slots.get(0)).set(itemstack1.cloneItemStack()); + itemstack1.setCount(0); + } else if (!itemstack1.isEmpty()) { + // Spigot start + ItemStack clone = itemstack1.cloneItemStack(); + clone.setCount(1); + ((Slot) this.slots.get(0)).set(clone); + // Spigot end + itemstack1.subtract(1); + } + } + + if (itemstack1.isEmpty()) { + slot.set(ItemStack.a); + } else { + slot.f(); + } + + if (itemstack1.getCount() == itemstack.getCount()) { + return ItemStack.a; + } + + slot.a(entityhuman, itemstack1); + } + + return itemstack; + } + + // CraftBukkit start + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventoryEnchanting inventory = new CraftInventoryEnchanting(this.enchantSlots); + bukkitEntity = new CraftInventoryView(this.player, inventory, this); + return bukkitEntity; + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/ContainerFurnace.java b/src/main/java/net/minecraft/server/ContainerFurnace.java new file mode 100644 index 000000000000..00bedffd8502 --- /dev/null +++ b/src/main/java/net/minecraft/server/ContainerFurnace.java @@ -0,0 +1,188 @@ +package net.minecraft.server; + +import java.util.Iterator; +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventoryFurnace; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +// CraftBukkit end + +public class ContainerFurnace extends ContainerRecipeBook { + + private final IInventory furnace; + private final World f; + private int g; + private int h; + private int i; + private int j; + + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventoryFurnace inventory = new CraftInventoryFurnace((TileEntityFurnace) this.furnace); + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end + + public ContainerFurnace(PlayerInventory playerinventory, IInventory iinventory) { + this.furnace = iinventory; + this.f = playerinventory.player.world; + this.a(new Slot(iinventory, 0, 56, 17)); + this.a((Slot) (new SlotFurnaceFuel(iinventory, 1, 56, 53))); + this.a((Slot) (new SlotFurnaceResult(playerinventory.player, iinventory, 2, 116, 35))); + this.player = playerinventory; // CraftBukkit - save player + + int i; + + for (i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.a(new Slot(playerinventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(playerinventory, i, 8 + i * 18, 142)); + } + + } + + public void addSlotListener(ICrafting icrafting) { + super.addSlotListener(icrafting); + icrafting.setContainerData(this, this.furnace); + } + + public void a(AutoRecipeStackManager autorecipestackmanager) { + if (this.furnace instanceof AutoRecipeOutput) { + ((AutoRecipeOutput) this.furnace).a(autorecipestackmanager); + } + + } + + public void d() { + this.furnace.clear(); + } + + public boolean a(IRecipe irecipe) { + return irecipe.a(this.furnace, this.f); + } + + public int e() { + return 2; + } + + public int f() { + return 1; + } + + public int g() { + return 1; + } + + public void b() { + super.b(); + Iterator iterator = this.listeners.iterator(); + + while (iterator.hasNext()) { + ICrafting icrafting = (ICrafting) iterator.next(); + + if (this.g != this.furnace.getProperty(2)) { + icrafting.setContainerData(this, 2, this.furnace.getProperty(2)); + } + + if (this.i != this.furnace.getProperty(0)) { + icrafting.setContainerData(this, 0, this.furnace.getProperty(0)); + } + + if (this.j != this.furnace.getProperty(1)) { + icrafting.setContainerData(this, 1, this.furnace.getProperty(1)); + } + + if (this.h != this.furnace.getProperty(3)) { + icrafting.setContainerData(this, 3, this.furnace.getProperty(3)); + } + } + + this.g = this.furnace.getProperty(2); + this.i = this.furnace.getProperty(0); + this.j = this.furnace.getProperty(1); + this.h = this.furnace.getProperty(3); + } + + public boolean canUse(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.furnace.a(entityhuman); + } + + public ItemStack shiftClick(EntityHuman entityhuman, int i) { + ItemStack itemstack = ItemStack.a; + Slot slot = (Slot) this.slots.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i == 2) { + if (!this.a(itemstack1, 3, 39, true)) { + return ItemStack.a; + } + + slot.a(itemstack1, itemstack); + } else if (i != 1 && i != 0) { + if (this.a(itemstack1)) { + if (!this.a(itemstack1, 0, 1, false)) { + return ItemStack.a; + } + } else if (TileEntityFurnace.isFuel(itemstack1)) { + if (!this.a(itemstack1, 1, 2, false)) { + return ItemStack.a; + } + } else if (i >= 3 && i < 30) { + if (!this.a(itemstack1, 30, 39, false)) { + return ItemStack.a; + } + } else if (i >= 30 && i < 39 && !this.a(itemstack1, 3, 30, false)) { + return ItemStack.a; + } + } else if (!this.a(itemstack1, 3, 39, false)) { + return ItemStack.a; + } + + if (itemstack1.isEmpty()) { + slot.set(ItemStack.a); + } else { + slot.f(); + } + + if (itemstack1.getCount() == itemstack.getCount()) { + return ItemStack.a; + } + + slot.a(entityhuman, itemstack1); + } + + return itemstack; + } + + private boolean a(ItemStack itemstack) { + Iterator iterator = this.f.getCraftingManager().b().iterator(); + + IRecipe irecipe; + + do { + if (!iterator.hasNext()) { + return false; + } + + irecipe = (IRecipe) iterator.next(); + } while (!(irecipe instanceof FurnaceRecipe) || !((RecipeItemStack) irecipe.e().get(0)).test(itemstack)); + + return true; + } +} diff --git a/src/main/java/net/minecraft/server/ContainerHopper.java b/src/main/java/net/minecraft/server/ContainerHopper.java new file mode 100644 index 000000000000..1d7a383236f6 --- /dev/null +++ b/src/main/java/net/minecraft/server/ContainerHopper.java @@ -0,0 +1,87 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +// CraftBukkit end + +public class ContainerHopper extends Container { + + private final IInventory hopper; + + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventory inventory = new CraftInventory(this.hopper); + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end + + public ContainerHopper(PlayerInventory playerinventory, IInventory iinventory, EntityHuman entityhuman) { + this.hopper = iinventory; + this.player = playerinventory; // CraftBukkit - save player + iinventory.startOpen(entityhuman); + boolean flag = true; + + int i; + + for (i = 0; i < iinventory.getSize(); ++i) { + this.a(new Slot(iinventory, i, 44 + i * 18, 20)); + } + + for (i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.a(new Slot(playerinventory, j + i * 9 + 9, 8 + j * 18, i * 18 + 51)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(playerinventory, i, 8 + i * 18, 109)); + } + + } + + public boolean canUse(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.hopper.a(entityhuman); + } + + public ItemStack shiftClick(EntityHuman entityhuman, int i) { + ItemStack itemstack = ItemStack.a; + Slot slot = (Slot) this.slots.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i < this.hopper.getSize()) { + if (!this.a(itemstack1, this.hopper.getSize(), this.slots.size(), true)) { + return ItemStack.a; + } + } else if (!this.a(itemstack1, 0, this.hopper.getSize(), false)) { + return ItemStack.a; + } + + if (itemstack1.isEmpty()) { + slot.set(ItemStack.a); + } else { + slot.f(); + } + } + + return itemstack; + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + this.hopper.closeContainer(entityhuman); + } +} diff --git a/src/main/java/net/minecraft/server/ContainerHorse.java b/src/main/java/net/minecraft/server/ContainerHorse.java new file mode 100644 index 000000000000..e5c7ca73e5ed --- /dev/null +++ b/src/main/java/net/minecraft/server/ContainerHorse.java @@ -0,0 +1,115 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +import org.bukkit.inventory.InventoryView; +// CraftBukkit end + +public class ContainerHorse extends Container { + + private final IInventory a; + private final EntityHorseAbstract f; + + // CraftBukkit start + org.bukkit.craftbukkit.inventory.CraftInventoryView bukkitEntity; + PlayerInventory player; + + @Override + public InventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + return bukkitEntity = new CraftInventoryView(player.player.getBukkitEntity(), a.getOwner().getInventory(), this); + } + + public ContainerHorse(IInventory iinventory, IInventory iinventory1, final EntityHorseAbstract entityhorseabstract, EntityHuman entityhuman) { + player = (PlayerInventory) iinventory; + // CraftBukkit end + this.a = iinventory1; + this.f = entityhorseabstract; + boolean flag = true; + + iinventory1.startOpen(entityhuman); + boolean flag1 = true; + + this.a(new Slot(iinventory1, 0, 8, 18) { + public boolean isAllowed(ItemStack itemstack) { + return itemstack.getItem() == Items.SADDLE && !this.hasItem() && entityhorseabstract.dU(); + } + }); + this.a(new Slot(iinventory1, 1, 8, 36) { + public boolean isAllowed(ItemStack itemstack) { + return entityhorseabstract.g(itemstack); + } + + public int getMaxStackSize() { + return 1; + } + }); + int i; + int j; + + if (entityhorseabstract instanceof EntityHorseChestedAbstract && ((EntityHorseChestedAbstract) entityhorseabstract).isCarryingChest()) { + for (i = 0; i < 3; ++i) { + for (j = 0; j < ((EntityHorseChestedAbstract) entityhorseabstract).dH(); ++j) { + this.a(new Slot(iinventory1, 2 + j + i * ((EntityHorseChestedAbstract) entityhorseabstract).dH(), 80 + j * 18, 18 + i * 18)); + } + } + } + + for (i = 0; i < 3; ++i) { + for (j = 0; j < 9; ++j) { + this.a(new Slot(iinventory, j + i * 9 + 9, 8 + j * 18, 102 + i * 18 + -18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(iinventory, i, 8 + i * 18, 142)); + } + + } + + public boolean canUse(EntityHuman entityhuman) { + return this.a.a(entityhuman) && this.f.isAlive() && this.f.g((Entity) entityhuman) < 8.0F; + } + + public ItemStack shiftClick(EntityHuman entityhuman, int i) { + ItemStack itemstack = ItemStack.a; + Slot slot = (Slot) this.slots.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i < this.a.getSize()) { + if (!this.a(itemstack1, this.a.getSize(), this.slots.size(), true)) { + return ItemStack.a; + } + } else if (this.getSlot(1).isAllowed(itemstack1) && !this.getSlot(1).hasItem()) { + if (!this.a(itemstack1, 1, 2, false)) { + return ItemStack.a; + } + } else if (this.getSlot(0).isAllowed(itemstack1)) { + if (!this.a(itemstack1, 0, 1, false)) { + return ItemStack.a; + } + } else if (this.a.getSize() <= 2 || !this.a(itemstack1, 2, this.a.getSize(), false)) { + return ItemStack.a; + } + + if (itemstack1.isEmpty()) { + slot.set(ItemStack.a); + } else { + slot.f(); + } + } + + return itemstack; + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + this.a.closeContainer(entityhuman); + } +} diff --git a/src/main/java/net/minecraft/server/ContainerMerchant.java b/src/main/java/net/minecraft/server/ContainerMerchant.java new file mode 100644 index 000000000000..c8e07d8aeb9f --- /dev/null +++ b/src/main/java/net/minecraft/server/ContainerMerchant.java @@ -0,0 +1,124 @@ +package net.minecraft.server; + +import org.bukkit.craftbukkit.inventory.CraftInventoryView; // CraftBukkit + +public class ContainerMerchant extends Container { + + private final IMerchant merchant; + private final InventoryMerchant f; + private final World g; + + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity == null) { + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), new org.bukkit.craftbukkit.inventory.CraftInventoryMerchant((InventoryMerchant) f), this); + } + return bukkitEntity; + } + // CraftBukkit end + + public ContainerMerchant(PlayerInventory playerinventory, IMerchant imerchant, World world) { + this.merchant = imerchant; + this.g = world; + this.f = new InventoryMerchant(playerinventory.player, imerchant); + this.a(new Slot(this.f, 0, 36, 53)); + this.a(new Slot(this.f, 1, 62, 53)); + this.a((Slot) (new SlotMerchantResult(playerinventory.player, imerchant, this.f, 2, 120, 53))); + this.player = playerinventory; // CraftBukkit - save player + + int i; + + for (i = 0; i < 3; ++i) { + for (int j = 0; j < 9; ++j) { + this.a(new Slot(playerinventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(playerinventory, i, 8 + i * 18, 142)); + } + + } + + public InventoryMerchant d() { + return this.f; + } + + public void a(IInventory iinventory) { + this.f.i(); + super.a(iinventory); + } + + public void d(int i) { + this.f.d(i); + } + + public boolean canUse(EntityHuman entityhuman) { + return this.merchant.getTrader() == entityhuman; + } + + public ItemStack shiftClick(EntityHuman entityhuman, int i) { + ItemStack itemstack = ItemStack.a; + Slot slot = (Slot) this.slots.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i == 2) { + if (!this.a(itemstack1, 3, 39, true)) { + return ItemStack.a; + } + + slot.a(itemstack1, itemstack); + } else if (i != 0 && i != 1) { + if (i >= 3 && i < 30) { + if (!this.a(itemstack1, 30, 39, false)) { + return ItemStack.a; + } + } else if (i >= 30 && i < 39 && !this.a(itemstack1, 3, 30, false)) { + return ItemStack.a; + } + } else if (!this.a(itemstack1, 3, 39, false)) { + return ItemStack.a; + } + + if (itemstack1.isEmpty()) { + slot.set(ItemStack.a); + } else { + slot.f(); + } + + if (itemstack1.getCount() == itemstack.getCount()) { + return ItemStack.a; + } + + slot.a(entityhuman, itemstack1); + } + + return itemstack; + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + this.merchant.setTradingPlayer((EntityHuman) null); + super.b(entityhuman); + if (!this.g.isClientSide) { + ItemStack itemstack = this.f.splitWithoutUpdate(0); + + if (!itemstack.isEmpty()) { + entityhuman.drop(itemstack, false); + } + + itemstack = this.f.splitWithoutUpdate(1); + if (!itemstack.isEmpty()) { + entityhuman.drop(itemstack, false); + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/ContainerPlayer.java b/src/main/java/net/minecraft/server/ContainerPlayer.java new file mode 100644 index 000000000000..7cc91ee40dd5 --- /dev/null +++ b/src/main/java/net/minecraft/server/ContainerPlayer.java @@ -0,0 +1,198 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventoryCrafting; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +// CraftBukkit end + +public class ContainerPlayer extends ContainerRecipeBook { + + private static final String[] h = new String[] { "item/empty_armor_slot_boots", "item/empty_armor_slot_leggings", "item/empty_armor_slot_chestplate", "item/empty_armor_slot_helmet"}; + private static final EnumItemSlot[] i = new EnumItemSlot[] { EnumItemSlot.HEAD, EnumItemSlot.CHEST, EnumItemSlot.LEGS, EnumItemSlot.FEET}; + public InventoryCrafting craftInventory = new InventoryCrafting(this, 2, 2); + public InventoryCraftResult resultInventory = new InventoryCraftResult(); + public boolean g; + private final EntityHuman owner; + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + // CraftBukkit end + + public ContainerPlayer(PlayerInventory playerinventory, boolean flag, EntityHuman entityhuman) { + this.g = flag; + this.owner = entityhuman; + // CraftBukkit start + this.resultInventory = new InventoryCraftResult(); // CraftBukkit - moved to before InventoryCrafting construction + this.craftInventory = new InventoryCrafting(this, 2, 2, playerinventory.player); // CraftBukkit - pass player + this.craftInventory.resultInventory = this.resultInventory; // CraftBukkit - let InventoryCrafting know about its result slot + this.player = playerinventory; // CraftBukkit - save player + // CraftBukkit end + this.a((Slot) (new SlotResult(playerinventory.player, this.craftInventory, this.resultInventory, 0, 154, 28))); + + int i; + int j; + + for (i = 0; i < 2; ++i) { + for (j = 0; j < 2; ++j) { + this.a(new Slot(this.craftInventory, j + i * 2, 98 + j * 18, 18 + i * 18)); + } + } + + for (i = 0; i < 4; ++i) { + final EnumItemSlot enumitemslot = ContainerPlayer.i[i]; + + this.a(new Slot(playerinventory, 39 - i, 8, 8 + i * 18) { + public int getMaxStackSize() { + return 1; + } + + public boolean isAllowed(ItemStack itemstack) { + return enumitemslot == EntityInsentient.e(itemstack); + } + + public boolean isAllowed(EntityHuman entityhuman1) { + ItemStack itemstack = this.getItem(); + + return !itemstack.isEmpty() && !entityhuman1.u() && EnchantmentManager.d(itemstack) ? false : super.isAllowed(entityhuman1); + } + }); + } + + for (i = 0; i < 3; ++i) { + for (j = 0; j < 9; ++j) { + this.a(new Slot(playerinventory, j + (i + 1) * 9, 8 + j * 18, 84 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(playerinventory, i, 8 + i * 18, 142)); + } + + this.a(new Slot(playerinventory, 40, 77, 62) { + }); + } + + public void a(AutoRecipeStackManager autorecipestackmanager) { + this.craftInventory.a(autorecipestackmanager); + } + + public void d() { + this.resultInventory.clear(); + this.craftInventory.clear(); + } + + public boolean a(IRecipe irecipe) { + return irecipe.a(this.craftInventory, this.owner.world); + } + + public void a(IInventory iinventory) { + this.a(this.owner.world, this.owner, this.craftInventory, this.resultInventory); + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + this.resultInventory.clear(); + if (!entityhuman.world.isClientSide) { + this.a(entityhuman, entityhuman.world, this.craftInventory); + } + } + + public boolean canUse(EntityHuman entityhuman) { + return true; + } + + public ItemStack shiftClick(EntityHuman entityhuman, int i) { + ItemStack itemstack = ItemStack.a; + Slot slot = (Slot) this.slots.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + EnumItemSlot enumitemslot = EntityInsentient.e(itemstack); + + if (i == 0) { + if (!this.a(itemstack1, 9, 45, true)) { + return ItemStack.a; + } + + slot.a(itemstack1, itemstack); + } else if (i >= 1 && i < 5) { + if (!this.a(itemstack1, 9, 45, false)) { + return ItemStack.a; + } + } else if (i >= 5 && i < 9) { + if (!this.a(itemstack1, 9, 45, false)) { + return ItemStack.a; + } + } else if (enumitemslot.a() == EnumItemSlot.Function.ARMOR && !((Slot) this.slots.get(8 - enumitemslot.b())).hasItem()) { + int j = 8 - enumitemslot.b(); + + if (!this.a(itemstack1, j, j + 1, false)) { + return ItemStack.a; + } + } else if (enumitemslot == EnumItemSlot.OFFHAND && !((Slot) this.slots.get(45)).hasItem()) { + if (!this.a(itemstack1, 45, 46, false)) { + return ItemStack.a; + } + } else if (i >= 9 && i < 36) { + if (!this.a(itemstack1, 36, 45, false)) { + return ItemStack.a; + } + } else if (i >= 36 && i < 45) { + if (!this.a(itemstack1, 9, 36, false)) { + return ItemStack.a; + } + } else if (!this.a(itemstack1, 9, 45, false)) { + return ItemStack.a; + } + + if (itemstack1.isEmpty()) { + slot.set(ItemStack.a); + } else { + slot.f(); + } + + if (itemstack1.getCount() == itemstack.getCount()) { + return ItemStack.a; + } + + ItemStack itemstack2 = slot.a(entityhuman, itemstack1); + + if (i == 0) { + entityhuman.drop(itemstack2, false); + } + } + + return itemstack; + } + + public boolean a(ItemStack itemstack, Slot slot) { + return slot.inventory != this.resultInventory && super.a(itemstack, slot); + } + + public int e() { + return 0; + } + + public int f() { + return this.craftInventory.U_(); + } + + public int g() { + return this.craftInventory.n(); + } + + // CraftBukkit start + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventoryCrafting inventory = new CraftInventoryCrafting(this.craftInventory, this.resultInventory); + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/ContainerShulkerBox.java b/src/main/java/net/minecraft/server/ContainerShulkerBox.java new file mode 100644 index 000000000000..5f217978312f --- /dev/null +++ b/src/main/java/net/minecraft/server/ContainerShulkerBox.java @@ -0,0 +1,88 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +// CraftBukkit end + +public class ContainerShulkerBox extends Container { + + private final IInventory a; + // CraftBukkit start + private CraftInventoryView bukkitEntity; + private PlayerInventory player; + + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), new CraftInventory(this.a), this); + return bukkitEntity; + } + // CraftBukkit end + + public ContainerShulkerBox(PlayerInventory playerinventory, IInventory iinventory, EntityHuman entityhuman) { + this.a = iinventory; + this.player = playerinventory; // CraftBukkit - save player + iinventory.startOpen(entityhuman); + boolean flag = true; + boolean flag1 = true; + + int i; + int j; + + for (i = 0; i < 3; ++i) { + for (j = 0; j < 9; ++j) { + this.a((Slot) (new SlotShulkerBox(iinventory, j + i * 9, 8 + j * 18, 18 + i * 18))); + } + } + + for (i = 0; i < 3; ++i) { + for (j = 0; j < 9; ++j) { + this.a(new Slot(playerinventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(playerinventory, i, 8 + i * 18, 142)); + } + + } + + public boolean canUse(EntityHuman entityhuman) { + return this.a.a(entityhuman); + } + + public ItemStack shiftClick(EntityHuman entityhuman, int i) { + ItemStack itemstack = ItemStack.a; + Slot slot = (Slot) this.slots.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i < this.a.getSize()) { + if (!this.a(itemstack1, this.a.getSize(), this.slots.size(), true)) { + return ItemStack.a; + } + } else if (!this.a(itemstack1, 0, this.a.getSize(), false)) { + return ItemStack.a; + } + + if (itemstack1.isEmpty()) { + slot.set(ItemStack.a); + } else { + slot.f(); + } + } + + return itemstack; + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + this.a.closeContainer(entityhuman); + } +} diff --git a/src/main/java/net/minecraft/server/ContainerWorkbench.java b/src/main/java/net/minecraft/server/ContainerWorkbench.java new file mode 100644 index 000000000000..707685e8bbe3 --- /dev/null +++ b/src/main/java/net/minecraft/server/ContainerWorkbench.java @@ -0,0 +1,157 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventoryCrafting; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +// CraftBukkit end + +public class ContainerWorkbench extends ContainerRecipeBook { + + public InventoryCrafting craftInventory; // CraftBukkit - move initialization into constructor + public InventoryCraftResult resultInventory; // CraftBukkit - move initialization into constructor + private final World g; + private final BlockPosition h; + private final EntityHuman i; + // CraftBukkit start + private CraftInventoryView bukkitEntity = null; + private PlayerInventory player; + // CraftBukkit end + + public ContainerWorkbench(PlayerInventory playerinventory, World world, BlockPosition blockposition) { + // CraftBukkit start - Switched order of IInventory construction and stored player + this.resultInventory = new InventoryCraftResult(); + this.craftInventory = new InventoryCrafting(this, 3, 3, playerinventory.player); // CraftBukkit - pass player + this.craftInventory.resultInventory = this.resultInventory; + this.player = playerinventory; + // CraftBukkit end + this.g = world; + this.h = blockposition; + this.i = playerinventory.player; + this.a((Slot) (new SlotResult(playerinventory.player, this.craftInventory, this.resultInventory, 0, 124, 35))); + + int i; + int j; + + for (i = 0; i < 3; ++i) { + for (j = 0; j < 3; ++j) { + this.a(new Slot(this.craftInventory, j + i * 3, 30 + j * 18, 17 + i * 18)); + } + } + + for (i = 0; i < 3; ++i) { + for (j = 0; j < 9; ++j) { + this.a(new Slot(playerinventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(playerinventory, i, 8 + i * 18, 142)); + } + + } + + public void a(IInventory iinventory) { + this.a(this.g, this.i, this.craftInventory, this.resultInventory); + } + + public void a(AutoRecipeStackManager autorecipestackmanager) { + this.craftInventory.a(autorecipestackmanager); + } + + public void d() { + this.craftInventory.clear(); + this.resultInventory.clear(); + } + + public boolean a(IRecipe irecipe) { + return irecipe.a(this.craftInventory, this.i.world); + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + if (!this.g.isClientSide) { + this.a(entityhuman, this.g, this.craftInventory); + } + } + + public boolean canUse(EntityHuman entityhuman) { + if (!this.checkReachable) return true; // CraftBukkit + return this.g.getType(this.h).getBlock() != Blocks.CRAFTING_TABLE ? false : entityhuman.d((double) this.h.getX() + 0.5D, (double) this.h.getY() + 0.5D, (double) this.h.getZ() + 0.5D) <= 64.0D; + } + + public ItemStack shiftClick(EntityHuman entityhuman, int i) { + ItemStack itemstack = ItemStack.a; + Slot slot = (Slot) this.slots.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i == 0) { + itemstack1.getItem().b(itemstack1, this.g, entityhuman); + if (!this.a(itemstack1, 10, 46, true)) { + return ItemStack.a; + } + + slot.a(itemstack1, itemstack); + } else if (i >= 10 && i < 37) { + if (!this.a(itemstack1, 37, 46, false)) { + return ItemStack.a; + } + } else if (i >= 37 && i < 46) { + if (!this.a(itemstack1, 10, 37, false)) { + return ItemStack.a; + } + } else if (!this.a(itemstack1, 10, 46, false)) { + return ItemStack.a; + } + + if (itemstack1.isEmpty()) { + slot.set(ItemStack.a); + } else { + slot.f(); + } + + if (itemstack1.getCount() == itemstack.getCount()) { + return ItemStack.a; + } + + ItemStack itemstack2 = slot.a(entityhuman, itemstack1); + + if (i == 0) { + entityhuman.drop(itemstack2, false); + } + } + + return itemstack; + } + + public boolean a(ItemStack itemstack, Slot slot) { + return slot.inventory != this.resultInventory && super.a(itemstack, slot); + } + + public int e() { + return 0; + } + + public int f() { + return this.craftInventory.U_(); + } + + public int g() { + return this.craftInventory.n(); + } + + // CraftBukkit start + @Override + public CraftInventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + CraftInventoryCrafting inventory = new CraftInventoryCrafting(this.craftInventory, this.resultInventory); + bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); + return bukkitEntity; + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/ControllerJump.java b/src/main/java/net/minecraft/server/ControllerJump.java new file mode 100644 index 000000000000..489beed26983 --- /dev/null +++ b/src/main/java/net/minecraft/server/ControllerJump.java @@ -0,0 +1,21 @@ +package net.minecraft.server; + +public class ControllerJump { + + private final EntityInsentient b; + protected boolean a; + + public ControllerJump(EntityInsentient entityinsentient) { + this.b = entityinsentient; + } + + public void a() { + this.a = true; + } + + public void jumpIfSet() { this.b(); } // Paper - OBFHELPER + public void b() { + this.b.o(this.a); + this.a = false; + } +} diff --git a/src/main/java/net/minecraft/server/CraftingManager.java b/src/main/java/net/minecraft/server/CraftingManager.java new file mode 100644 index 000000000000..3186209f3060 --- /dev/null +++ b/src/main/java/net/minecraft/server/CraftingManager.java @@ -0,0 +1,161 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import javax.annotation.Nullable; +import org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class CraftingManager implements IResourcePackListener { + + private static final Logger c = LogManager.getLogger(); + public static final int a = "recipes/".length(); + public static final int b = ".json".length(); + public it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap recipes = new it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap<>(); // CraftBukkit + private boolean e; + + public CraftingManager() {} + + public void a(IResourceManager iresourcemanager) { + Gson gson = (new GsonBuilder()).setPrettyPrinting().disableHtmlEscaping().create(); + + this.e = false; + this.recipes.clear(); + Iterator iterator = iresourcemanager.a("recipes", (s) -> { + return s.endsWith(".json"); + }).iterator(); + + while (iterator.hasNext()) { + MinecraftKey minecraftkey = (MinecraftKey) iterator.next(); + String s = minecraftkey.getKey(); + MinecraftKey minecraftkey1 = new MinecraftKey(minecraftkey.b(), s.substring(CraftingManager.a, s.length() - CraftingManager.b)); + + try { + IResource iresource = iresourcemanager.a(minecraftkey); + Throwable throwable = null; + + try { + JsonObject jsonobject = (JsonObject) ChatDeserializer.a(gson, IOUtils.toString(iresource.b(), StandardCharsets.UTF_8), JsonObject.class); + + if (jsonobject == null) { + CraftingManager.c.error("Couldn't load recipe {} as it's null or empty", minecraftkey1); + } else { + this.a(RecipeSerializers.a(minecraftkey1, jsonobject)); + } + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (iresource != null) { + if (throwable != null) { + try { + iresource.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + iresource.close(); + } + } + + } + } catch (IllegalArgumentException | JsonParseException jsonparseexception) { + CraftingManager.c.error("Parsing error loading recipe {}", minecraftkey1, jsonparseexception); + this.e = true; + } catch (IOException ioexception) { + CraftingManager.c.error("Couldn't read custom advancement {} from {}", minecraftkey1, minecraftkey, ioexception); + this.e = true; + } + } + + CraftingManager.c.info("Loaded {} recipes", this.recipes.size()); + } + + public void a(IRecipe irecipe) { + org.spigotmc.AsyncCatcher.catchOp("Recipe Add"); // Spigot + if (this.recipes.containsKey(irecipe.getKey())) { + throw new IllegalStateException("Duplicate recipe ignored with ID " + irecipe.getKey()); + } else { + this.recipes.putAndMoveToFirst(irecipe.getKey(), irecipe); // CraftBukkit - SPIGOT-4638: last recipe gets priority + } + } + + public ItemStack craft(IInventory iinventory, World world) { + Iterator iterator = this.recipes.values().iterator(); + + IRecipe irecipe; + + do { + if (!iterator.hasNext()) { + iinventory.setCurrentRecipe(null); // CraftBukkit - Clear recipe when no recipe is found + return ItemStack.a; + } + + irecipe = (IRecipe) iterator.next(); + } while (!irecipe.a(iinventory, world)); + + iinventory.setCurrentRecipe(irecipe); // CraftBukkit + return irecipe.craftItem(iinventory); + } + + @Nullable + public IRecipe b(IInventory iinventory, World world) { + Iterator iterator = this.recipes.values().iterator(); + + IRecipe irecipe; + + do { + if (!iterator.hasNext()) { + iinventory.setCurrentRecipe(null); // CraftBukkit - Clear recipe when no recipe is found + return null; + } + + irecipe = (IRecipe) iterator.next(); + } while (!irecipe.a(iinventory, world)); + + iinventory.setCurrentRecipe(irecipe); // CraftBukkit + return irecipe; + } + + public NonNullList c(IInventory iinventory, World world) { + Iterator iterator = this.recipes.values().iterator(); + + while (iterator.hasNext()) { + IRecipe irecipe = (IRecipe) iterator.next(); + + if (irecipe.a(iinventory, world)) { + return irecipe.b(iinventory); + } + } + + NonNullList nonnulllist = NonNullList.a(iinventory.getSize(), ItemStack.a); + + for (int i = 0; i < nonnulllist.size(); ++i) { + nonnulllist.set(i, iinventory.getItem(i)); + } + + return nonnulllist; + } + + @Nullable + public IRecipe a(MinecraftKey minecraftkey) { + return (IRecipe) this.recipes.get(minecraftkey); + } + + public Collection b() { + return this.recipes.values(); + } + + public Collection c() { + return this.recipes.keySet(); + } +} diff --git a/src/main/java/net/minecraft/server/CrashReport.java b/src/main/java/net/minecraft/server/CrashReport.java new file mode 100644 index 000000000000..4e158a7b7310 --- /dev/null +++ b/src/main/java/net/minecraft/server/CrashReport.java @@ -0,0 +1,265 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class CrashReport { + + private static final Logger a = LogManager.getLogger(); + private final String b; + private final Throwable c; + private final CrashReportSystemDetails d = new CrashReportSystemDetails(this, "System Details"); + private final List e = Lists.newArrayList(); + private File f; + private boolean g = true; + private StackTraceElement[] h = new StackTraceElement[0]; + + public CrashReport(String s, Throwable throwable) { + this.b = s; + this.c = throwable; + this.h(); + } + + private void h() { + this.d.a("Minecraft Version", () -> { + return "1.13.2"; + }); + this.d.a("Operating System", () -> { + return System.getProperty("os.name") + " (" + System.getProperty("os.arch") + ") version " + System.getProperty("os.version"); + }); + this.d.a("Java Version", () -> { + return System.getProperty("java.version") + ", " + System.getProperty("java.vendor"); + }); + this.d.a("Java VM Version", () -> { + return System.getProperty("java.vm.name") + " (" + System.getProperty("java.vm.info") + "), " + System.getProperty("java.vm.vendor"); + }); + this.d.a("Memory", () -> { + Runtime runtime = Runtime.getRuntime(); + long i = runtime.maxMemory(); + long j = runtime.totalMemory(); + long k = runtime.freeMemory(); + long l = i / 1024L / 1024L; + long i1 = j / 1024L / 1024L; + long j1 = k / 1024L / 1024L; + + return k + " bytes (" + j1 + " MB) / " + j + " bytes (" + i1 + " MB) up to " + i + " bytes (" + l + " MB)"; + }); + this.d.a("JVM Flags", () -> { + List list = (List) SystemUtils.f().collect(Collectors.toList()); + + return String.format("%d total; %s", list.size(), list.stream().collect(Collectors.joining(" "))); + }); + this.d.a("CraftBukkit Information", (CrashReportCallable) new org.bukkit.craftbukkit.CraftCrashReport()); // CraftBukkit + } + + public String a() { + return this.b; + } + + public Throwable b() { + return this.c; + } + + public void a(StringBuilder stringbuilder) { + if ((this.h == null || this.h.length <= 0) && !this.e.isEmpty()) { + this.h = (StackTraceElement[]) ArrayUtils.subarray(((CrashReportSystemDetails) this.e.get(0)).a(), 0, 1); + } + + if (this.h != null && this.h.length > 0) { + stringbuilder.append("-- Head --\n"); + stringbuilder.append("Thread: ").append(Thread.currentThread().getName()).append("\n"); + stringbuilder.append("Stacktrace:\n"); + StackTraceElement[] astacktraceelement = this.h; + int i = astacktraceelement.length; + + for (int j = 0; j < i; ++j) { + StackTraceElement stacktraceelement = astacktraceelement[j]; + + stringbuilder.append("\t").append("at ").append(stacktraceelement); + stringbuilder.append("\n"); + } + + stringbuilder.append("\n"); + } + + Iterator iterator = this.e.iterator(); + + while (iterator.hasNext()) { + CrashReportSystemDetails crashreportsystemdetails = (CrashReportSystemDetails) iterator.next(); + + crashreportsystemdetails.a(stringbuilder); + stringbuilder.append("\n\n"); + } + + this.d.a(stringbuilder); + } + + public String d() { + StringWriter stringwriter = null; + PrintWriter printwriter = null; + Object object = this.c; + + if (((Throwable) object).getMessage() == null) { + if (object instanceof NullPointerException) { + object = new NullPointerException(this.b); + } else if (object instanceof StackOverflowError) { + object = new StackOverflowError(this.b); + } else if (object instanceof OutOfMemoryError) { + object = new OutOfMemoryError(this.b); + } + + ((Throwable) object).setStackTrace(this.c.getStackTrace()); + } + + String s; + + try { + stringwriter = new StringWriter(); + printwriter = new PrintWriter(stringwriter); + ((Throwable) object).printStackTrace(printwriter); + s = stringwriter.toString(); + } finally { + IOUtils.closeQuietly(stringwriter); + IOUtils.closeQuietly(printwriter); + } + + return s; + } + + public String e() { + StringBuilder stringbuilder = new StringBuilder(); + + stringbuilder.append("---- Minecraft Crash Report ----\n"); + stringbuilder.append("// "); + stringbuilder.append(i()); + stringbuilder.append("\n\n"); + stringbuilder.append("Time: "); + stringbuilder.append((new SimpleDateFormat()).format(new Date())); + stringbuilder.append("\n"); + stringbuilder.append("Description: "); + stringbuilder.append(this.b); + stringbuilder.append("\n\n"); + stringbuilder.append(this.d()); + stringbuilder.append("\n\nA detailed walkthrough of the error, its code path and all known details is as follows:\n"); + + for (int i = 0; i < 87; ++i) { + stringbuilder.append("-"); + } + + stringbuilder.append("\n\n"); + this.a(stringbuilder); + return stringbuilder.toString(); + } + + public boolean a(File file) { + if (this.f != null) { + return false; + } else { + if (file.getParentFile() != null) { + file.getParentFile().mkdirs(); + } + + OutputStreamWriter outputstreamwriter = null; + + boolean flag; + + try { + outputstreamwriter = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8); + outputstreamwriter.write(this.e()); + this.f = file; + boolean flag1 = true; + + return flag1; + } catch (Throwable throwable) { + CrashReport.a.error("Could not save crash report to {}", file, throwable); + flag = false; + } finally { + IOUtils.closeQuietly(outputstreamwriter); + } + + return flag; + } + } + + public CrashReportSystemDetails g() { + return this.d; + } + + public CrashReportSystemDetails a(String s) { + return this.a(s, 1); + } + + public CrashReportSystemDetails a(String s, int i) { + CrashReportSystemDetails crashreportsystemdetails = new CrashReportSystemDetails(this, s); + + if (this.g) { + int j = crashreportsystemdetails.a(i); + StackTraceElement[] astacktraceelement = this.c.getStackTrace(); + StackTraceElement stacktraceelement = null; + StackTraceElement stacktraceelement1 = null; + int k = astacktraceelement.length - j; + + if (k < 0) { + System.out.println("Negative index in crash report handler (" + astacktraceelement.length + "/" + j + ")"); + } + + if (astacktraceelement != null && 0 <= k && k < astacktraceelement.length) { + stacktraceelement = astacktraceelement[k]; + if (astacktraceelement.length + 1 - j < astacktraceelement.length) { + stacktraceelement1 = astacktraceelement[astacktraceelement.length + 1 - j]; + } + } + + this.g = crashreportsystemdetails.a(stacktraceelement, stacktraceelement1); + if (j > 0 && !this.e.isEmpty()) { + CrashReportSystemDetails crashreportsystemdetails1 = (CrashReportSystemDetails) this.e.get(this.e.size() - 1); + + crashreportsystemdetails1.b(j); + } else if (astacktraceelement != null && astacktraceelement.length >= j && 0 <= k && k < astacktraceelement.length) { + this.h = new StackTraceElement[k]; + System.arraycopy(astacktraceelement, 0, this.h, 0, this.h.length); + } else { + this.g = false; + } + } + + this.e.add(crashreportsystemdetails); + return crashreportsystemdetails; + } + + private static String i() { + String[] astring = new String[] { "Who set us up the TNT?", "Everything's going to plan. No, really, that was supposed to happen.", "Uh... Did I do that?", "Oops.", "Why did you do that?", "I feel sad now :(", "My bad.", "I'm sorry, Dave.", "I let you down. Sorry :(", "On the bright side, I bought you a teddy bear!", "Daisy, daisy...", "Oh - I know what I did wrong!", "Hey, that tickles! Hehehe!", "I blame Dinnerbone.", "You should try our sister game, Minceraft!", "Don't be sad. I'll do better next time, I promise!", "Don't be sad, have a hug! <3", "I just don't know what went wrong :(", "Shall we play a game?", "Quite honestly, I wouldn't worry myself about that.", "I bet Cylons wouldn't have this problem.", "Sorry :(", "Surprise! Haha. Well, this is awkward.", "Would you like a cupcake?", "Hi. I'm Minecraft, and I'm a crashaholic.", "Ooh. Shiny.", "This doesn't make any sense!", "Why is it breaking :(", "Don't do that.", "Ouch. That hurt :(", "You're mean.", "This is a token for 1 free hug. Redeem at your nearest Mojangsta: [~~HUG~~]", "There are four lights!", "But it works on my machine."}; + + try { + return astring[(int) (SystemUtils.getMonotonicNanos() % (long) astring.length)]; + } catch (Throwable throwable) { + return "Witty comment unavailable :("; + } + } + + public static CrashReport a(Throwable throwable, String s) { + CrashReport crashreport; + + if (throwable instanceof ReportedException) { + crashreport = ((ReportedException) throwable).a(); + } else { + crashreport = new CrashReport(s, throwable); + } + + return crashreport; + } +} diff --git a/src/main/java/net/minecraft/server/CustomFunction.java b/src/main/java/net/minecraft/server/CustomFunction.java new file mode 100644 index 000000000000..bc87cfc4b670 --- /dev/null +++ b/src/main/java/net/minecraft/server/CustomFunction.java @@ -0,0 +1,169 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.mojang.brigadier.ParseResults; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import java.util.ArrayDeque; +import java.util.List; +import javax.annotation.Nullable; + +public class CustomFunction { + + private final CustomFunction.c[] a; + private final MinecraftKey b; + // Paper start + public co.aikar.timings.Timing timing; + public co.aikar.timings.Timing getTiming() { + if (timing == null) { + timing = co.aikar.timings.MinecraftTimings.getCommandFunctionTiming(this); + } + return timing; + } + // Paper end + + public CustomFunction(MinecraftKey minecraftkey, CustomFunction.c[] acustomfunction_c) { + this.b = minecraftkey; + this.a = acustomfunction_c; + } + + public MinecraftKey getMinecraftKey() { return this.a(); } // Paper - OBFHELPER + public MinecraftKey a() { + return this.b; + } + + public CustomFunction.c[] b() { + return this.a; + } + + public static CustomFunction a(MinecraftKey minecraftkey, CustomFunctionData customfunctiondata, List list) { + List list1 = Lists.newArrayListWithCapacity(list.size()); + + for (int i = 0; i < list.size(); ++i) { + int j = i + 1; + String s = ((String) list.get(i)).trim(); + StringReader stringreader = new StringReader(s); + + if (stringreader.canRead() && stringreader.peek() != '#') { + if (stringreader.peek() == '/') { + stringreader.skip(); + if (stringreader.peek() == '/') { + throw new IllegalArgumentException("Unknown or invalid command '" + s + "' on line " + j + " (if you intended to make a comment, use '#' not '//')"); + } + + String s1 = stringreader.readUnquotedString(); + + throw new IllegalArgumentException("Unknown or invalid command '" + s + "' on line " + j + " (did you mean '" + s1 + "'? Do not use a preceding forwards slash.)"); + } + + try { + ParseResults parseresults = customfunctiondata.d().parse(stringreader, customfunctiondata.f()); // CraftBukkit + + if (parseresults.getReader().canRead()) { + if (parseresults.getExceptions().size() == 1) { + throw (CommandSyntaxException) parseresults.getExceptions().values().iterator().next(); + } + + if (parseresults.getContext().getRange().isEmpty()) { + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().createWithContext(parseresults.getReader()); + } + + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(parseresults.getReader()); + } + + list1.add(new CustomFunction.b(parseresults)); + } catch (CommandSyntaxException commandsyntaxexception) { + throw new IllegalArgumentException("Whilst parsing command on line " + j + ": " + commandsyntaxexception.getMessage()); + } + } + } + + return new CustomFunction(minecraftkey, (CustomFunction.c[]) list1.toArray(new CustomFunction.c[0])); + } + + public static class a { + + public static final CustomFunction.a a = new CustomFunction.a((MinecraftKey) null); + @Nullable + private final MinecraftKey b; + private boolean c; + private CustomFunction d; + + public a(@Nullable MinecraftKey minecraftkey) { + this.b = minecraftkey; + } + + public a(CustomFunction customfunction) { + this.b = null; + this.d = customfunction; + } + + @Nullable + public CustomFunction a(CustomFunctionData customfunctiondata) { + if (!this.c) { + if (this.b != null) { + this.d = customfunctiondata.a(this.b); + } + + this.c = true; + } + + return this.d; + } + + @Nullable + public MinecraftKey a() { + return this.d != null ? this.d.b : this.b; + } + } + + public static class d implements CustomFunction.c { + + private final CustomFunction.a a; + + public d(CustomFunction customfunction) { + this.a = new CustomFunction.a(customfunction); + } + + public void a(CustomFunctionData customfunctiondata, CommandListenerWrapper commandlistenerwrapper, ArrayDeque arraydeque, int i) { + CustomFunction customfunction = this.a.a(customfunctiondata); + + if (customfunction != null) { + CustomFunction.c[] acustomfunction_c = customfunction.b(); + int j = i - arraydeque.size(); + int k = Math.min(acustomfunction_c.length, j); + + for (int l = k - 1; l >= 0; --l) { + arraydeque.addFirst(new CustomFunctionData.a(customfunctiondata, commandlistenerwrapper, acustomfunction_c[l])); + } + } + + } + + public String toString() { + return "function " + this.a.a(); + } + } + + public static class b implements CustomFunction.c { + + private final ParseResults a; + + public b(ParseResults parseresults) { + this.a = parseresults; + } + + public void a(CustomFunctionData customfunctiondata, CommandListenerWrapper commandlistenerwrapper, ArrayDeque arraydeque, int i) throws CommandSyntaxException { + customfunctiondata.d().execute(new ParseResults(this.a.getContext().withSource(commandlistenerwrapper), this.a.getStartIndex(), this.a.getReader(), this.a.getExceptions())); + } + + public String toString() { + return this.a.getReader().getString(); + } + } + + public interface c { + + void a(CustomFunctionData customfunctiondata, CommandListenerWrapper commandlistenerwrapper, ArrayDeque arraydeque, int i) throws CommandSyntaxException; + } +} diff --git a/src/main/java/net/minecraft/server/CustomFunctionData.java b/src/main/java/net/minecraft/server/CustomFunctionData.java new file mode 100644 index 000000000000..6b417be1ddfe --- /dev/null +++ b/src/main/java/net/minecraft/server/CustomFunctionData.java @@ -0,0 +1,255 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import javax.annotation.Nullable; +import org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class CustomFunctionData implements ITickable, IResourcePackListener { + + private static final Logger c = LogManager.getLogger(); + private static final MinecraftKey d = new MinecraftKey("tick"); + private static final MinecraftKey e = new MinecraftKey("load"); + public static final int a = "functions/".length(); + public static final int b = ".mcfunction".length(); + private final MinecraftServer server; + private final Map g = Maps.newHashMap(); + private final ArrayDeque h = new ArrayDeque(); + private boolean i; + private final Tags j = new Tags<>((minecraftkey) -> { + return this.a(minecraftkey) != null; + }, this::a, "tags/functions", true, "function"); + private final List k = Lists.newArrayList(); + private boolean l; + + public CustomFunctionData(MinecraftServer minecraftserver) { + this.server = minecraftserver; + } + + @Nullable + public CustomFunction a(MinecraftKey minecraftkey) { + return (CustomFunction) this.g.get(minecraftkey); + } + + public MinecraftServer a() { + return this.server; + } + + public int b() { + return this.server.getGameRules().c("maxCommandChainLength"); + } + + public Map c() { + return this.g; + } + + public com.mojang.brigadier.CommandDispatcher d() { + return this.server.vanillaCommandDispatcher.a(); // CraftBukkit + } + + public void tick() { + MinecraftKey minecraftkey = CustomFunctionData.d; + + this.server.methodProfiler.a(minecraftkey::toString); + Iterator iterator = this.k.iterator(); + + while (iterator.hasNext()) { + CustomFunction customfunction = (CustomFunction) iterator.next(); + + this.a(customfunction, this.f()); + } + + this.server.methodProfiler.exit(); + if (this.l) { + this.l = false; + Collection collection = this.g().b(CustomFunctionData.e).a(); + + minecraftkey = CustomFunctionData.e; + this.server.methodProfiler.a(minecraftkey::toString); + Iterator iterator1 = collection.iterator(); + + while (iterator1.hasNext()) { + CustomFunction customfunction1 = (CustomFunction) iterator1.next(); + + this.a(customfunction1, this.f()); + } + + this.server.methodProfiler.exit(); + } + + } + + public int a(CustomFunction customfunction, CommandListenerWrapper commandlistenerwrapper) { + int i = this.b(); + + if (this.i) { + if (this.h.size() < i) { + this.h.addFirst(new CustomFunctionData.a(this, commandlistenerwrapper, new CustomFunction.d(customfunction))); + } + + return 0; + } else { + try (co.aikar.timings.Timing timing = customfunction.getTiming().startTiming()) { // Paper + this.i = true; + int j = 0; + CustomFunction.c[] acustomfunction_c = customfunction.b(); + + int k; + + for (k = acustomfunction_c.length - 1; k >= 0; --k) { + this.h.push(new CustomFunctionData.a(this, commandlistenerwrapper, acustomfunction_c[k])); + } + + while (!this.h.isEmpty()) { + try { + CustomFunctionData.a customfunctiondata_a = (CustomFunctionData.a) this.h.removeFirst(); + + this.server.methodProfiler.a(customfunctiondata_a::toString); + customfunctiondata_a.a(this.h, i); + } finally { + this.server.methodProfiler.exit(); + } + + ++j; + if (j >= i) { + k = j; + return k; + } + } + + k = j; + return k; + } finally { + this.h.clear(); + this.i = false; + } + } + } + + public void a(IResourceManager iresourcemanager) { + this.g.clear(); + this.k.clear(); + this.j.b(); + Collection collection = iresourcemanager.a("functions", (s) -> { + return s.endsWith(".mcfunction"); + }); + List> list = Lists.newArrayList(); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + MinecraftKey minecraftkey = (MinecraftKey) iterator.next(); + String s = minecraftkey.getKey(); + MinecraftKey minecraftkey1 = new MinecraftKey(minecraftkey.b(), s.substring(CustomFunctionData.a, s.length() - CustomFunctionData.b)); + + list.add(CompletableFuture.supplyAsync(() -> { + return a(iresourcemanager, minecraftkey); + }, Resource.a).thenApplyAsync((list1) -> { + return CustomFunction.a(minecraftkey1, this, list1); + }).handle((customfunction, throwable) -> { + return this.a(customfunction, throwable, minecraftkey); + })); + } + + CompletableFuture.allOf((CompletableFuture[]) list.toArray(new CompletableFuture[0])).join(); + if (!this.g.isEmpty()) { + CustomFunctionData.c.info("Loaded {} custom command functions", this.g.size()); + } + + this.j.a(iresourcemanager); + this.k.addAll(this.j.b(CustomFunctionData.d).a()); + this.l = true; + } + + @Nullable + private CustomFunction a(CustomFunction customfunction, @Nullable Throwable throwable, MinecraftKey minecraftkey) { + if (throwable != null) { + CustomFunctionData.c.error("Couldn't load function at {}", minecraftkey, throwable); + return null; + } else { + Map map = this.g; + + synchronized (this.g) { + this.g.put(customfunction.a(), customfunction); + return customfunction; + } + } + } + + private static List a(IResourceManager iresourcemanager, MinecraftKey minecraftkey) { + try { + IResource iresource = iresourcemanager.a(minecraftkey); + Throwable throwable = null; + + List list; + + try { + list = IOUtils.readLines(iresource.b(), StandardCharsets.UTF_8); + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (iresource != null) { + if (throwable != null) { + try { + iresource.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + iresource.close(); + } + } + + } + + return list; + } catch (IOException ioexception) { + throw new CompletionException(ioexception); + } + } + + public CommandListenerWrapper f() { + return this.server.getServerCommandListener().a(2).a(); + } + + public Tags g() { + return this.j; + } + + public static class a { + + private final CustomFunctionData a; + private final CommandListenerWrapper b; + private final CustomFunction.c c; + + public a(CustomFunctionData customfunctiondata, CommandListenerWrapper commandlistenerwrapper, CustomFunction.c customfunction_c) { + this.a = customfunctiondata; + this.b = commandlistenerwrapper; + this.c = customfunction_c; + } + + public void a(ArrayDeque arraydeque, int i) { + try { + this.c.a(this.a, this.b, arraydeque, i); + } catch (Throwable throwable) { + ; + } + + } + + public String toString() { + return this.c.toString(); + } + } +} diff --git a/src/main/java/net/minecraft/server/DamageSource.java b/src/main/java/net/minecraft/server/DamageSource.java new file mode 100644 index 000000000000..71a6d858e7d1 --- /dev/null +++ b/src/main/java/net/minecraft/server/DamageSource.java @@ -0,0 +1,213 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class DamageSource { + + public static final DamageSource FIRE = (new DamageSource("inFire")).setExplosion(); + public static final DamageSource LIGHTNING = new DamageSource("lightningBolt"); + public static final DamageSource BURN = (new DamageSource("onFire")).setIgnoreArmor().setExplosion(); + public static final DamageSource LAVA = (new DamageSource("lava")).setExplosion(); + public static final DamageSource HOT_FLOOR = (new DamageSource("hotFloor")).setExplosion(); + public static final DamageSource STUCK = (new DamageSource("inWall")).setIgnoreArmor(); + public static final DamageSource CRAMMING = (new DamageSource("cramming")).setIgnoreArmor(); + public static final DamageSource DROWN = (new DamageSource("drown")).setIgnoreArmor(); + public static final DamageSource STARVE = (new DamageSource("starve")).setIgnoreArmor().n(); + public static final DamageSource CACTUS = new DamageSource("cactus"); + public static final DamageSource FALL = (new DamageSource("fall")).setIgnoreArmor(); + public static final DamageSource FLY_INTO_WALL = (new DamageSource("flyIntoWall")).setIgnoreArmor(); + public static final DamageSource OUT_OF_WORLD = (new DamageSource("outOfWorld")).setIgnoreArmor().m(); + public static final DamageSource GENERIC = (new DamageSource("generic")).setIgnoreArmor(); + public static final DamageSource MAGIC = (new DamageSource("magic")).setIgnoreArmor().setMagic(); + public static final DamageSource WITHER = (new DamageSource("wither")).setIgnoreArmor(); + public static final DamageSource ANVIL = new DamageSource("anvil"); + public static final DamageSource FALLING_BLOCK = new DamageSource("fallingBlock"); + public static final DamageSource DRAGON_BREATH = (new DamageSource("dragonBreath")).setIgnoreArmor(); + public static final DamageSource FIREWORKS = (new DamageSource("fireworks")).e(); + public static final DamageSource DRYOUT = new DamageSource("dryout"); + private boolean w; + private boolean x; + private boolean y; + private float z = 0.1F; + private boolean A; + private boolean B; + private boolean C; + private boolean D; + private boolean E; + public final String translationIndex; + // CraftBukkit start + private boolean sweep; + + public boolean isSweep() { + return sweep; + } + + public DamageSource sweep() { + this.sweep = true; + return this; + } + // CraftBukkit end + + public static DamageSource mobAttack(EntityLiving entityliving) { + return new EntityDamageSource("mob", entityliving); + } + + public static DamageSource a(Entity entity, EntityLiving entityliving) { + return new EntityDamageSourceIndirect("mob", entity, entityliving); + } + + public static DamageSource playerAttack(EntityHuman entityhuman) { + return new EntityDamageSource("player", entityhuman); + } + + public static DamageSource arrow(EntityArrow entityarrow, @Nullable Entity entity) { + return (new EntityDamageSourceIndirect("arrow", entityarrow, entity)).c(); + } + + public static DamageSource a(Entity entity, @Nullable Entity entity1) { + return (new EntityDamageSourceIndirect("trident", entity, entity1)).c(); + } + + public static DamageSource fireball(EntityFireball entityfireball, @Nullable Entity entity) { + return entity == null ? (new EntityDamageSourceIndirect("onFire", entityfireball, entityfireball)).setExplosion().c() : (new EntityDamageSourceIndirect("fireball", entityfireball, entity)).setExplosion().c(); + } + + public static DamageSource projectile(Entity entity, @Nullable Entity entity1) { + return (new EntityDamageSourceIndirect("thrown", entity, entity1)).c(); + } + + public static DamageSource c(Entity entity, @Nullable Entity entity1) { + return (new EntityDamageSourceIndirect("indirectMagic", entity, entity1)).setIgnoreArmor().setMagic(); + } + + public static DamageSource a(Entity entity) { + return (new EntityDamageSource("thorns", entity)).x().setMagic(); + } + + public static DamageSource explosion(@Nullable Explosion explosion) { + return explosion != null && explosion.getSource() != null ? (new EntityDamageSource("explosion.player", explosion.getSource())).r().e() : (new DamageSource("explosion")).r().e(); + } + + public static DamageSource b(@Nullable EntityLiving entityliving) { + return entityliving != null ? (new EntityDamageSource("explosion.player", entityliving)).r().e() : (new DamageSource("explosion")).r().e(); + } + + public static DamageSource a() { + return new DamageSourceNetherBed(); + } + + public boolean b() { + return this.B; + } + + public DamageSource c() { + this.B = true; + return this; + } + + public boolean isExplosion() { + return this.E; + } + + public DamageSource e() { + this.E = true; + return this; + } + + public boolean ignoresArmor() { + return this.w; + } + + public float getExhaustionCost() { + return this.z; + } + + public boolean ignoresInvulnerability() { + return this.x; + } + + public boolean isStarvation() { + return this.y; + } + + protected DamageSource(String s) { + this.translationIndex = s; + } + + @Nullable + public Entity j() { + return this.getEntity(); + } + + @Nullable + public Entity getEntity() { + return null; + } + + protected DamageSource setIgnoreArmor() { + this.w = true; + this.z = 0.0F; + return this; + } + + protected DamageSource m() { + this.x = true; + return this; + } + + protected DamageSource n() { + this.y = true; + this.z = 0.0F; + return this; + } + + protected DamageSource setExplosion() { + this.A = true; + return this; + } + + public IChatBaseComponent getLocalizedDeathMessage(EntityLiving entityliving) { + EntityLiving entityliving1 = entityliving.cv(); + String s = "death.attack." + this.translationIndex; + String s1 = s + ".player"; + + return entityliving1 != null ? new ChatMessage(s1, new Object[] { entityliving.getScoreboardDisplayName(), entityliving1.getScoreboardDisplayName()}) : new ChatMessage(s, new Object[] { entityliving.getScoreboardDisplayName()}); + } + + public boolean p() { + return this.A; + } + + public String q() { + return this.translationIndex; + } + + public DamageSource r() { + this.C = true; + return this; + } + + public boolean s() { + return this.C; + } + + public boolean isMagic() { + return this.D; + } + + public DamageSource setMagic() { + this.D = true; + return this; + } + + public boolean v() { + Entity entity = this.getEntity(); + + return entity instanceof EntityHuman && ((EntityHuman) entity).abilities.canInstantlyBuild; + } + + @Nullable + public Vec3D w() { + return null; + } +} diff --git a/src/main/java/net/minecraft/server/DataBits.java b/src/main/java/net/minecraft/server/DataBits.java new file mode 100644 index 000000000000..fe5947b6ccac --- /dev/null +++ b/src/main/java/net/minecraft/server/DataBits.java @@ -0,0 +1,75 @@ +package net.minecraft.server; + +import org.apache.commons.lang3.Validate; + +public class DataBits { + + private final long[] a; + private final int b; + private final long c; + private final int d; + + public DataBits(int i, int j) { + this(i, j, new long[MathHelper.c(j * i, 64) / 64]); + } + + public DataBits(int i, int j, long[] along) { + //Validate.inclusiveBetween(1L, 32L, (long) i); // Paper + this.d = j; + this.b = i; + this.a = along; + this.c = (1L << i) - 1L; + int k = MathHelper.c(j * i, 64) / 64; + + if (along.length != k) { + throw new RuntimeException("Invalid length given for storage, got: " + along.length + " but expected: " + k); + } + } + + public void a(int i, int j) { + //Validate.inclusiveBetween(0L, (long) (this.d - 1), (long) i); // Paper + //Validate.inclusiveBetween(0L, this.c, (long) j); // Paper + int k = i * this.b; + int l = k / 64; + int i1 = ((i + 1) * this.b - 1) / 64; + int j1 = k % 64; + + this.a[l] = this.a[l] & ~(this.c << j1) | ((long) j & this.c) << j1; + if (l != i1) { + int k1 = 64 - j1; + int l1 = this.b - k1; + + this.a[i1] = this.a[i1] >>> l1 << l1 | ((long) j & this.c) >> k1; + } + + } + + public int a(int i) { + //Validate.inclusiveBetween(0L, (long) (this.d - 1), (long) i); // Paper + int j = i * this.b; + int k = j / 64; + int l = ((i + 1) * this.b - 1) / 64; + int i1 = j % 64; + + if (k == l) { + return (int) (this.a[k] >>> i1 & this.c); + } else { + int j1 = 64 - i1; + + return (int) ((this.a[k] >>> i1 | this.a[l] << j1) & this.c); + } + } + + public long[] getDataBits() { return this.a(); } // Paper - OBFHELPER + public long[] a() { + return this.a; + } + + public int b() { + return this.d; + } + + public int c() { + return this.b; + } +} diff --git a/src/main/java/net/minecraft/server/DataConverterFlatten.java b/src/main/java/net/minecraft/server/DataConverterFlatten.java new file mode 100644 index 000000000000..36aa61415f86 --- /dev/null +++ b/src/main/java/net/minecraft/server/DataConverterFlatten.java @@ -0,0 +1,399 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.DataFix; +import com.mojang.datafixers.DataFixUtils; +import com.mojang.datafixers.Dynamic; +import com.mojang.datafixers.OpticFinder; +import com.mojang.datafixers.TypeRewriteRule; +import com.mojang.datafixers.Typed; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.Type; +import com.mojang.datafixers.util.Pair; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nullable; + +public class DataConverterFlatten extends DataFix { + + private static final Map a = (Map) DataFixUtils.make(Maps.newHashMap(), (hashmap) -> { + hashmap.put("minecraft:stone.0", "minecraft:stone"); + hashmap.put("minecraft:stone.1", "minecraft:granite"); + hashmap.put("minecraft:stone.2", "minecraft:polished_granite"); + hashmap.put("minecraft:stone.3", "minecraft:diorite"); + hashmap.put("minecraft:stone.4", "minecraft:polished_diorite"); + hashmap.put("minecraft:stone.5", "minecraft:andesite"); + hashmap.put("minecraft:stone.6", "minecraft:polished_andesite"); + hashmap.put("minecraft:dirt.0", "minecraft:dirt"); + hashmap.put("minecraft:dirt.1", "minecraft:coarse_dirt"); + hashmap.put("minecraft:dirt.2", "minecraft:podzol"); + hashmap.put("minecraft:leaves.0", "minecraft:oak_leaves"); + hashmap.put("minecraft:leaves.1", "minecraft:spruce_leaves"); + hashmap.put("minecraft:leaves.2", "minecraft:birch_leaves"); + hashmap.put("minecraft:leaves.3", "minecraft:jungle_leaves"); + hashmap.put("minecraft:leaves2.0", "minecraft:acacia_leaves"); + hashmap.put("minecraft:leaves2.1", "minecraft:dark_oak_leaves"); + hashmap.put("minecraft:log.0", "minecraft:oak_log"); + hashmap.put("minecraft:log.1", "minecraft:spruce_log"); + hashmap.put("minecraft:log.2", "minecraft:birch_log"); + hashmap.put("minecraft:log.3", "minecraft:jungle_log"); + hashmap.put("minecraft:log2.0", "minecraft:acacia_log"); + hashmap.put("minecraft:log2.1", "minecraft:dark_oak_log"); + hashmap.put("minecraft:sapling.0", "minecraft:oak_sapling"); + hashmap.put("minecraft:sapling.1", "minecraft:spruce_sapling"); + hashmap.put("minecraft:sapling.2", "minecraft:birch_sapling"); + hashmap.put("minecraft:sapling.3", "minecraft:jungle_sapling"); + hashmap.put("minecraft:sapling.4", "minecraft:acacia_sapling"); + hashmap.put("minecraft:sapling.5", "minecraft:dark_oak_sapling"); + hashmap.put("minecraft:planks.0", "minecraft:oak_planks"); + hashmap.put("minecraft:planks.1", "minecraft:spruce_planks"); + hashmap.put("minecraft:planks.2", "minecraft:birch_planks"); + hashmap.put("minecraft:planks.3", "minecraft:jungle_planks"); + hashmap.put("minecraft:planks.4", "minecraft:acacia_planks"); + hashmap.put("minecraft:planks.5", "minecraft:dark_oak_planks"); + hashmap.put("minecraft:sand.0", "minecraft:sand"); + hashmap.put("minecraft:sand.1", "minecraft:red_sand"); + hashmap.put("minecraft:quartz_block.0", "minecraft:quartz_block"); + hashmap.put("minecraft:quartz_block.1", "minecraft:chiseled_quartz_block"); + hashmap.put("minecraft:quartz_block.2", "minecraft:quartz_pillar"); + hashmap.put("minecraft:anvil.0", "minecraft:anvil"); + hashmap.put("minecraft:anvil.1", "minecraft:chipped_anvil"); + hashmap.put("minecraft:anvil.2", "minecraft:damaged_anvil"); + hashmap.put("minecraft:wool.0", "minecraft:white_wool"); + hashmap.put("minecraft:wool.1", "minecraft:orange_wool"); + hashmap.put("minecraft:wool.2", "minecraft:magenta_wool"); + hashmap.put("minecraft:wool.3", "minecraft:light_blue_wool"); + hashmap.put("minecraft:wool.4", "minecraft:yellow_wool"); + hashmap.put("minecraft:wool.5", "minecraft:lime_wool"); + hashmap.put("minecraft:wool.6", "minecraft:pink_wool"); + hashmap.put("minecraft:wool.7", "minecraft:gray_wool"); + hashmap.put("minecraft:wool.8", "minecraft:light_gray_wool"); + hashmap.put("minecraft:wool.9", "minecraft:cyan_wool"); + hashmap.put("minecraft:wool.10", "minecraft:purple_wool"); + hashmap.put("minecraft:wool.11", "minecraft:blue_wool"); + hashmap.put("minecraft:wool.12", "minecraft:brown_wool"); + hashmap.put("minecraft:wool.13", "minecraft:green_wool"); + hashmap.put("minecraft:wool.14", "minecraft:red_wool"); + hashmap.put("minecraft:wool.15", "minecraft:black_wool"); + hashmap.put("minecraft:carpet.0", "minecraft:white_carpet"); + hashmap.put("minecraft:carpet.1", "minecraft:orange_carpet"); + hashmap.put("minecraft:carpet.2", "minecraft:magenta_carpet"); + hashmap.put("minecraft:carpet.3", "minecraft:light_blue_carpet"); + hashmap.put("minecraft:carpet.4", "minecraft:yellow_carpet"); + hashmap.put("minecraft:carpet.5", "minecraft:lime_carpet"); + hashmap.put("minecraft:carpet.6", "minecraft:pink_carpet"); + hashmap.put("minecraft:carpet.7", "minecraft:gray_carpet"); + hashmap.put("minecraft:carpet.8", "minecraft:light_gray_carpet"); + hashmap.put("minecraft:carpet.9", "minecraft:cyan_carpet"); + hashmap.put("minecraft:carpet.10", "minecraft:purple_carpet"); + hashmap.put("minecraft:carpet.11", "minecraft:blue_carpet"); + hashmap.put("minecraft:carpet.12", "minecraft:brown_carpet"); + hashmap.put("minecraft:carpet.13", "minecraft:green_carpet"); + hashmap.put("minecraft:carpet.14", "minecraft:red_carpet"); + hashmap.put("minecraft:carpet.15", "minecraft:black_carpet"); + hashmap.put("minecraft:hardened_clay.0", "minecraft:terracotta"); + hashmap.put("minecraft:stained_hardened_clay.0", "minecraft:white_terracotta"); + hashmap.put("minecraft:stained_hardened_clay.1", "minecraft:orange_terracotta"); + hashmap.put("minecraft:stained_hardened_clay.2", "minecraft:magenta_terracotta"); + hashmap.put("minecraft:stained_hardened_clay.3", "minecraft:light_blue_terracotta"); + hashmap.put("minecraft:stained_hardened_clay.4", "minecraft:yellow_terracotta"); + hashmap.put("minecraft:stained_hardened_clay.5", "minecraft:lime_terracotta"); + hashmap.put("minecraft:stained_hardened_clay.6", "minecraft:pink_terracotta"); + hashmap.put("minecraft:stained_hardened_clay.7", "minecraft:gray_terracotta"); + hashmap.put("minecraft:stained_hardened_clay.8", "minecraft:light_gray_terracotta"); + hashmap.put("minecraft:stained_hardened_clay.9", "minecraft:cyan_terracotta"); + hashmap.put("minecraft:stained_hardened_clay.10", "minecraft:purple_terracotta"); + hashmap.put("minecraft:stained_hardened_clay.11", "minecraft:blue_terracotta"); + hashmap.put("minecraft:stained_hardened_clay.12", "minecraft:brown_terracotta"); + hashmap.put("minecraft:stained_hardened_clay.13", "minecraft:green_terracotta"); + hashmap.put("minecraft:stained_hardened_clay.14", "minecraft:red_terracotta"); + hashmap.put("minecraft:stained_hardened_clay.15", "minecraft:black_terracotta"); + hashmap.put("minecraft:silver_glazed_terracotta.0", "minecraft:light_gray_glazed_terracotta"); + hashmap.put("minecraft:stained_glass.0", "minecraft:white_stained_glass"); + hashmap.put("minecraft:stained_glass.1", "minecraft:orange_stained_glass"); + hashmap.put("minecraft:stained_glass.2", "minecraft:magenta_stained_glass"); + hashmap.put("minecraft:stained_glass.3", "minecraft:light_blue_stained_glass"); + hashmap.put("minecraft:stained_glass.4", "minecraft:yellow_stained_glass"); + hashmap.put("minecraft:stained_glass.5", "minecraft:lime_stained_glass"); + hashmap.put("minecraft:stained_glass.6", "minecraft:pink_stained_glass"); + hashmap.put("minecraft:stained_glass.7", "minecraft:gray_stained_glass"); + hashmap.put("minecraft:stained_glass.8", "minecraft:light_gray_stained_glass"); + hashmap.put("minecraft:stained_glass.9", "minecraft:cyan_stained_glass"); + hashmap.put("minecraft:stained_glass.10", "minecraft:purple_stained_glass"); + hashmap.put("minecraft:stained_glass.11", "minecraft:blue_stained_glass"); + hashmap.put("minecraft:stained_glass.12", "minecraft:brown_stained_glass"); + hashmap.put("minecraft:stained_glass.13", "minecraft:green_stained_glass"); + hashmap.put("minecraft:stained_glass.14", "minecraft:red_stained_glass"); + hashmap.put("minecraft:stained_glass.15", "minecraft:black_stained_glass"); + hashmap.put("minecraft:stained_glass_pane.0", "minecraft:white_stained_glass_pane"); + hashmap.put("minecraft:stained_glass_pane.1", "minecraft:orange_stained_glass_pane"); + hashmap.put("minecraft:stained_glass_pane.2", "minecraft:magenta_stained_glass_pane"); + hashmap.put("minecraft:stained_glass_pane.3", "minecraft:light_blue_stained_glass_pane"); + hashmap.put("minecraft:stained_glass_pane.4", "minecraft:yellow_stained_glass_pane"); + hashmap.put("minecraft:stained_glass_pane.5", "minecraft:lime_stained_glass_pane"); + hashmap.put("minecraft:stained_glass_pane.6", "minecraft:pink_stained_glass_pane"); + hashmap.put("minecraft:stained_glass_pane.7", "minecraft:gray_stained_glass_pane"); + hashmap.put("minecraft:stained_glass_pane.8", "minecraft:light_gray_stained_glass_pane"); + hashmap.put("minecraft:stained_glass_pane.9", "minecraft:cyan_stained_glass_pane"); + hashmap.put("minecraft:stained_glass_pane.10", "minecraft:purple_stained_glass_pane"); + hashmap.put("minecraft:stained_glass_pane.11", "minecraft:blue_stained_glass_pane"); + hashmap.put("minecraft:stained_glass_pane.12", "minecraft:brown_stained_glass_pane"); + hashmap.put("minecraft:stained_glass_pane.13", "minecraft:green_stained_glass_pane"); + hashmap.put("minecraft:stained_glass_pane.14", "minecraft:red_stained_glass_pane"); + hashmap.put("minecraft:stained_glass_pane.15", "minecraft:black_stained_glass_pane"); + hashmap.put("minecraft:prismarine.0", "minecraft:prismarine"); + hashmap.put("minecraft:prismarine.1", "minecraft:prismarine_bricks"); + hashmap.put("minecraft:prismarine.2", "minecraft:dark_prismarine"); + hashmap.put("minecraft:concrete.0", "minecraft:white_concrete"); + hashmap.put("minecraft:concrete.1", "minecraft:orange_concrete"); + hashmap.put("minecraft:concrete.2", "minecraft:magenta_concrete"); + hashmap.put("minecraft:concrete.3", "minecraft:light_blue_concrete"); + hashmap.put("minecraft:concrete.4", "minecraft:yellow_concrete"); + hashmap.put("minecraft:concrete.5", "minecraft:lime_concrete"); + hashmap.put("minecraft:concrete.6", "minecraft:pink_concrete"); + hashmap.put("minecraft:concrete.7", "minecraft:gray_concrete"); + hashmap.put("minecraft:concrete.8", "minecraft:light_gray_concrete"); + hashmap.put("minecraft:concrete.9", "minecraft:cyan_concrete"); + hashmap.put("minecraft:concrete.10", "minecraft:purple_concrete"); + hashmap.put("minecraft:concrete.11", "minecraft:blue_concrete"); + hashmap.put("minecraft:concrete.12", "minecraft:brown_concrete"); + hashmap.put("minecraft:concrete.13", "minecraft:green_concrete"); + hashmap.put("minecraft:concrete.14", "minecraft:red_concrete"); + hashmap.put("minecraft:concrete.15", "minecraft:black_concrete"); + hashmap.put("minecraft:concrete_powder.0", "minecraft:white_concrete_powder"); + hashmap.put("minecraft:concrete_powder.1", "minecraft:orange_concrete_powder"); + hashmap.put("minecraft:concrete_powder.2", "minecraft:magenta_concrete_powder"); + hashmap.put("minecraft:concrete_powder.3", "minecraft:light_blue_concrete_powder"); + hashmap.put("minecraft:concrete_powder.4", "minecraft:yellow_concrete_powder"); + hashmap.put("minecraft:concrete_powder.5", "minecraft:lime_concrete_powder"); + hashmap.put("minecraft:concrete_powder.6", "minecraft:pink_concrete_powder"); + hashmap.put("minecraft:concrete_powder.7", "minecraft:gray_concrete_powder"); + hashmap.put("minecraft:concrete_powder.8", "minecraft:light_gray_concrete_powder"); + hashmap.put("minecraft:concrete_powder.9", "minecraft:cyan_concrete_powder"); + hashmap.put("minecraft:concrete_powder.10", "minecraft:purple_concrete_powder"); + hashmap.put("minecraft:concrete_powder.11", "minecraft:blue_concrete_powder"); + hashmap.put("minecraft:concrete_powder.12", "minecraft:brown_concrete_powder"); + hashmap.put("minecraft:concrete_powder.13", "minecraft:green_concrete_powder"); + hashmap.put("minecraft:concrete_powder.14", "minecraft:red_concrete_powder"); + hashmap.put("minecraft:concrete_powder.15", "minecraft:black_concrete_powder"); + hashmap.put("minecraft:cobblestone_wall.0", "minecraft:cobblestone_wall"); + hashmap.put("minecraft:cobblestone_wall.1", "minecraft:mossy_cobblestone_wall"); + hashmap.put("minecraft:sandstone.0", "minecraft:sandstone"); + hashmap.put("minecraft:sandstone.1", "minecraft:chiseled_sandstone"); + hashmap.put("minecraft:sandstone.2", "minecraft:cut_sandstone"); + hashmap.put("minecraft:red_sandstone.0", "minecraft:red_sandstone"); + hashmap.put("minecraft:red_sandstone.1", "minecraft:chiseled_red_sandstone"); + hashmap.put("minecraft:red_sandstone.2", "minecraft:cut_red_sandstone"); + hashmap.put("minecraft:stonebrick.0", "minecraft:stone_bricks"); + hashmap.put("minecraft:stonebrick.1", "minecraft:mossy_stone_bricks"); + hashmap.put("minecraft:stonebrick.2", "minecraft:cracked_stone_bricks"); + hashmap.put("minecraft:stonebrick.3", "minecraft:chiseled_stone_bricks"); + hashmap.put("minecraft:monster_egg.0", "minecraft:infested_stone"); + hashmap.put("minecraft:monster_egg.1", "minecraft:infested_cobblestone"); + hashmap.put("minecraft:monster_egg.2", "minecraft:infested_stone_bricks"); + hashmap.put("minecraft:monster_egg.3", "minecraft:infested_mossy_stone_bricks"); + hashmap.put("minecraft:monster_egg.4", "minecraft:infested_cracked_stone_bricks"); + hashmap.put("minecraft:monster_egg.5", "minecraft:infested_chiseled_stone_bricks"); + hashmap.put("minecraft:yellow_flower.0", "minecraft:dandelion"); + hashmap.put("minecraft:red_flower.0", "minecraft:poppy"); + hashmap.put("minecraft:red_flower.1", "minecraft:blue_orchid"); + hashmap.put("minecraft:red_flower.2", "minecraft:allium"); + hashmap.put("minecraft:red_flower.3", "minecraft:azure_bluet"); + hashmap.put("minecraft:red_flower.4", "minecraft:red_tulip"); + hashmap.put("minecraft:red_flower.5", "minecraft:orange_tulip"); + hashmap.put("minecraft:red_flower.6", "minecraft:white_tulip"); + hashmap.put("minecraft:red_flower.7", "minecraft:pink_tulip"); + hashmap.put("minecraft:red_flower.8", "minecraft:oxeye_daisy"); + hashmap.put("minecraft:double_plant.0", "minecraft:sunflower"); + hashmap.put("minecraft:double_plant.1", "minecraft:lilac"); + hashmap.put("minecraft:double_plant.2", "minecraft:tall_grass"); + hashmap.put("minecraft:double_plant.3", "minecraft:large_fern"); + hashmap.put("minecraft:double_plant.4", "minecraft:rose_bush"); + hashmap.put("minecraft:double_plant.5", "minecraft:peony"); + hashmap.put("minecraft:deadbush.0", "minecraft:dead_bush"); + hashmap.put("minecraft:tallgrass.0", "minecraft:dead_bush"); + hashmap.put("minecraft:tallgrass.1", "minecraft:grass"); + hashmap.put("minecraft:tallgrass.2", "minecraft:fern"); + hashmap.put("minecraft:sponge.0", "minecraft:sponge"); + hashmap.put("minecraft:sponge.1", "minecraft:wet_sponge"); + hashmap.put("minecraft:purpur_slab.0", "minecraft:purpur_slab"); + hashmap.put("minecraft:stone_slab.0", "minecraft:stone_slab"); + hashmap.put("minecraft:stone_slab.1", "minecraft:sandstone_slab"); + hashmap.put("minecraft:stone_slab.2", "minecraft:petrified_oak_slab"); + hashmap.put("minecraft:stone_slab.3", "minecraft:cobblestone_slab"); + hashmap.put("minecraft:stone_slab.4", "minecraft:brick_slab"); + hashmap.put("minecraft:stone_slab.5", "minecraft:stone_brick_slab"); + hashmap.put("minecraft:stone_slab.6", "minecraft:nether_brick_slab"); + hashmap.put("minecraft:stone_slab.7", "minecraft:quartz_slab"); + hashmap.put("minecraft:stone_slab2.0", "minecraft:red_sandstone_slab"); + hashmap.put("minecraft:wooden_slab.0", "minecraft:oak_slab"); + hashmap.put("minecraft:wooden_slab.1", "minecraft:spruce_slab"); + hashmap.put("minecraft:wooden_slab.2", "minecraft:birch_slab"); + hashmap.put("minecraft:wooden_slab.3", "minecraft:jungle_slab"); + hashmap.put("minecraft:wooden_slab.4", "minecraft:acacia_slab"); + hashmap.put("minecraft:wooden_slab.5", "minecraft:dark_oak_slab"); + hashmap.put("minecraft:coal.0", "minecraft:coal"); + hashmap.put("minecraft:coal.1", "minecraft:charcoal"); + hashmap.put("minecraft:fish.0", "minecraft:cod"); + hashmap.put("minecraft:fish.1", "minecraft:salmon"); + hashmap.put("minecraft:fish.2", "minecraft:clownfish"); + hashmap.put("minecraft:fish.3", "minecraft:pufferfish"); + hashmap.put("minecraft:cooked_fish.0", "minecraft:cooked_cod"); + hashmap.put("minecraft:cooked_fish.1", "minecraft:cooked_salmon"); + hashmap.put("minecraft:skull.0", "minecraft:skeleton_skull"); + hashmap.put("minecraft:skull.1", "minecraft:wither_skeleton_skull"); + hashmap.put("minecraft:skull.2", "minecraft:zombie_head"); + hashmap.put("minecraft:skull.3", "minecraft:player_head"); + hashmap.put("minecraft:skull.4", "minecraft:creeper_head"); + hashmap.put("minecraft:skull.5", "minecraft:dragon_head"); + hashmap.put("minecraft:golden_apple.0", "minecraft:golden_apple"); + hashmap.put("minecraft:golden_apple.1", "minecraft:enchanted_golden_apple"); + hashmap.put("minecraft:fireworks.0", "minecraft:firework_rocket"); + hashmap.put("minecraft:firework_charge.0", "minecraft:firework_star"); + hashmap.put("minecraft:dye.0", "minecraft:ink_sac"); + hashmap.put("minecraft:dye.1", "minecraft:rose_red"); + hashmap.put("minecraft:dye.2", "minecraft:cactus_green"); + hashmap.put("minecraft:dye.3", "minecraft:cocoa_beans"); + hashmap.put("minecraft:dye.4", "minecraft:lapis_lazuli"); + hashmap.put("minecraft:dye.5", "minecraft:purple_dye"); + hashmap.put("minecraft:dye.6", "minecraft:cyan_dye"); + hashmap.put("minecraft:dye.7", "minecraft:light_gray_dye"); + hashmap.put("minecraft:dye.8", "minecraft:gray_dye"); + hashmap.put("minecraft:dye.9", "minecraft:pink_dye"); + hashmap.put("minecraft:dye.10", "minecraft:lime_dye"); + hashmap.put("minecraft:dye.11", "minecraft:dandelion_yellow"); + hashmap.put("minecraft:dye.12", "minecraft:light_blue_dye"); + hashmap.put("minecraft:dye.13", "minecraft:magenta_dye"); + hashmap.put("minecraft:dye.14", "minecraft:orange_dye"); + hashmap.put("minecraft:dye.15", "minecraft:bone_meal"); + hashmap.put("minecraft:silver_shulker_box.0", "minecraft:light_gray_shulker_box"); + hashmap.put("minecraft:fence.0", "minecraft:oak_fence"); + hashmap.put("minecraft:fence_gate.0", "minecraft:oak_fence_gate"); + hashmap.put("minecraft:wooden_door.0", "minecraft:oak_door"); + hashmap.put("minecraft:boat.0", "minecraft:oak_boat"); + hashmap.put("minecraft:lit_pumpkin.0", "minecraft:jack_o_lantern"); + hashmap.put("minecraft:pumpkin.0", "minecraft:carved_pumpkin"); + hashmap.put("minecraft:trapdoor.0", "minecraft:oak_trapdoor"); + hashmap.put("minecraft:nether_brick.0", "minecraft:nether_bricks"); + hashmap.put("minecraft:red_nether_brick.0", "minecraft:red_nether_bricks"); + hashmap.put("minecraft:netherbrick.0", "minecraft:nether_brick"); + hashmap.put("minecraft:wooden_button.0", "minecraft:oak_button"); + hashmap.put("minecraft:wooden_pressure_plate.0", "minecraft:oak_pressure_plate"); + hashmap.put("minecraft:noteblock.0", "minecraft:note_block"); + hashmap.put("minecraft:bed.0", "minecraft:white_bed"); + hashmap.put("minecraft:bed.1", "minecraft:orange_bed"); + hashmap.put("minecraft:bed.2", "minecraft:magenta_bed"); + hashmap.put("minecraft:bed.3", "minecraft:light_blue_bed"); + hashmap.put("minecraft:bed.4", "minecraft:yellow_bed"); + hashmap.put("minecraft:bed.5", "minecraft:lime_bed"); + hashmap.put("minecraft:bed.6", "minecraft:pink_bed"); + hashmap.put("minecraft:bed.7", "minecraft:gray_bed"); + hashmap.put("minecraft:bed.8", "minecraft:light_gray_bed"); + hashmap.put("minecraft:bed.9", "minecraft:cyan_bed"); + hashmap.put("minecraft:bed.10", "minecraft:purple_bed"); + hashmap.put("minecraft:bed.11", "minecraft:blue_bed"); + hashmap.put("minecraft:bed.12", "minecraft:brown_bed"); + hashmap.put("minecraft:bed.13", "minecraft:green_bed"); + hashmap.put("minecraft:bed.14", "minecraft:red_bed"); + hashmap.put("minecraft:bed.15", "minecraft:black_bed"); + hashmap.put("minecraft:banner.15", "minecraft:white_banner"); + hashmap.put("minecraft:banner.14", "minecraft:orange_banner"); + hashmap.put("minecraft:banner.13", "minecraft:magenta_banner"); + hashmap.put("minecraft:banner.12", "minecraft:light_blue_banner"); + hashmap.put("minecraft:banner.11", "minecraft:yellow_banner"); + hashmap.put("minecraft:banner.10", "minecraft:lime_banner"); + hashmap.put("minecraft:banner.9", "minecraft:pink_banner"); + hashmap.put("minecraft:banner.8", "minecraft:gray_banner"); + hashmap.put("minecraft:banner.7", "minecraft:light_gray_banner"); + hashmap.put("minecraft:banner.6", "minecraft:cyan_banner"); + hashmap.put("minecraft:banner.5", "minecraft:purple_banner"); + hashmap.put("minecraft:banner.4", "minecraft:blue_banner"); + hashmap.put("minecraft:banner.3", "minecraft:brown_banner"); + hashmap.put("minecraft:banner.2", "minecraft:green_banner"); + hashmap.put("minecraft:banner.1", "minecraft:red_banner"); + hashmap.put("minecraft:banner.0", "minecraft:black_banner"); + hashmap.put("minecraft:grass.0", "minecraft:grass_block"); + hashmap.put("minecraft:brick_block.0", "minecraft:bricks"); + hashmap.put("minecraft:end_bricks.0", "minecraft:end_stone_bricks"); + hashmap.put("minecraft:golden_rail.0", "minecraft:powered_rail"); + hashmap.put("minecraft:magma.0", "minecraft:magma_block"); + hashmap.put("minecraft:quartz_ore.0", "minecraft:nether_quartz_ore"); + hashmap.put("minecraft:reeds.0", "minecraft:sugar_cane"); + hashmap.put("minecraft:slime.0", "minecraft:slime_block"); + hashmap.put("minecraft:stone_stairs.0", "minecraft:cobblestone_stairs"); + hashmap.put("minecraft:waterlily.0", "minecraft:lily_pad"); + hashmap.put("minecraft:web.0", "minecraft:cobweb"); + hashmap.put("minecraft:snow.0", "minecraft:snow_block"); + hashmap.put("minecraft:snow_layer.0", "minecraft:snow"); + hashmap.put("minecraft:record_11.0", "minecraft:music_disc_11"); + hashmap.put("minecraft:record_13.0", "minecraft:music_disc_13"); + hashmap.put("minecraft:record_blocks.0", "minecraft:music_disc_blocks"); + hashmap.put("minecraft:record_cat.0", "minecraft:music_disc_cat"); + hashmap.put("minecraft:record_chirp.0", "minecraft:music_disc_chirp"); + hashmap.put("minecraft:record_far.0", "minecraft:music_disc_far"); + hashmap.put("minecraft:record_mall.0", "minecraft:music_disc_mall"); + hashmap.put("minecraft:record_mellohi.0", "minecraft:music_disc_mellohi"); + hashmap.put("minecraft:record_stal.0", "minecraft:music_disc_stal"); + hashmap.put("minecraft:record_strad.0", "minecraft:music_disc_strad"); + hashmap.put("minecraft:record_wait.0", "minecraft:music_disc_wait"); + hashmap.put("minecraft:record_ward.0", "minecraft:music_disc_ward"); + }); + private static final Set b = (Set) DataConverterFlatten.a.keySet().stream().map((s) -> { + return s.substring(0, s.indexOf(46)); + }).collect(Collectors.toSet()); + private static final Set c = Sets.newHashSet(new String[] { "minecraft:bow", "minecraft:carrot_on_a_stick", "minecraft:chainmail_boots", "minecraft:chainmail_chestplate", "minecraft:chainmail_helmet", "minecraft:chainmail_leggings", "minecraft:diamond_axe", "minecraft:diamond_boots", "minecraft:diamond_chestplate", "minecraft:diamond_helmet", "minecraft:diamond_hoe", "minecraft:diamond_leggings", "minecraft:diamond_pickaxe", "minecraft:diamond_shovel", "minecraft:diamond_sword", "minecraft:elytra", "minecraft:fishing_rod", "minecraft:flint_and_steel", "minecraft:golden_axe", "minecraft:golden_boots", "minecraft:golden_chestplate", "minecraft:golden_helmet", "minecraft:golden_hoe", "minecraft:golden_leggings", "minecraft:golden_pickaxe", "minecraft:golden_shovel", "minecraft:golden_sword", "minecraft:iron_axe", "minecraft:iron_boots", "minecraft:iron_chestplate", "minecraft:iron_helmet", "minecraft:iron_hoe", "minecraft:iron_leggings", "minecraft:iron_pickaxe", "minecraft:iron_shovel", "minecraft:iron_sword", "minecraft:leather_boots", "minecraft:leather_chestplate", "minecraft:leather_helmet", "minecraft:leather_leggings", "minecraft:shears", "minecraft:shield", "minecraft:stone_axe", "minecraft:stone_hoe", "minecraft:stone_pickaxe", "minecraft:stone_shovel", "minecraft:stone_sword", "minecraft:wooden_axe", "minecraft:wooden_hoe", "minecraft:wooden_pickaxe", "minecraft:wooden_shovel", "minecraft:wooden_sword"}); + + public DataConverterFlatten(Schema schema, boolean flag) { + super(schema, flag); + } + + public TypeRewriteRule makeRule() { + Type type = this.getInputSchema().getType(DataConverterTypes.ITEM_STACK); + OpticFinder> opticfinder = DSL.fieldFinder("id", DSL.named(DataConverterTypes.q.typeName(), DSL.namespacedString())); + OpticFinder opticfinder1 = type.findField("tag"); + + return this.fixTypeEverywhereTyped("ItemInstanceTheFlatteningFix", type, (typed) -> { + Optional> optional = typed.getOptional(opticfinder); + + if (!optional.isPresent()) { + return typed; + } else { + Typed typed1 = typed; + Dynamic dynamic = (Dynamic) typed.get(DSL.remainderFinder()); + int i = dynamic.getInt("Damage"); + String s = a((String) ((Pair) optional.get()).getSecond(), i); + + if (s != null) { + typed1 = typed.set(opticfinder, Pair.of(DataConverterTypes.q.typeName(), s)); + } + + if (DataConverterFlatten.c.contains(((Pair) optional.get()).getSecond())) { + Typed typed2 = typed.getOrCreateTyped(opticfinder1); + Dynamic dynamic1 = (Dynamic) typed2.get(DSL.remainderFinder()); + + if (i != 0) dynamic1 = dynamic1.set("Damage", dynamic1.createInt(i)); // CraftBukkit + typed1 = typed1.set(opticfinder1, typed2.set(DSL.remainderFinder(), dynamic1)); + } + + typed1 = typed1.set(DSL.remainderFinder(), dynamic.remove("Damage")); + return typed1; + } + }); + } + + @Nullable + public static String a(@Nullable String s, int i) { + if (DataConverterFlatten.b.contains(s)) { + String s1 = (String) DataConverterFlatten.a.get(s + '.' + i); + + return s1 == null ? (String) DataConverterFlatten.a.get(s + ".0") : s1; + } else { + return null; + } + } +} diff --git a/src/main/java/net/minecraft/server/DataConverterMap.java b/src/main/java/net/minecraft/server/DataConverterMap.java new file mode 100644 index 000000000000..d21d2a1d7b6b --- /dev/null +++ b/src/main/java/net/minecraft/server/DataConverterMap.java @@ -0,0 +1,41 @@ +package net.minecraft.server; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.DataFix; +import com.mojang.datafixers.Dynamic; +import com.mojang.datafixers.OpticFinder; +import com.mojang.datafixers.TypeRewriteRule; +import com.mojang.datafixers.Typed; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.Type; +import com.mojang.datafixers.util.Pair; +import java.util.Objects; +import java.util.Optional; + +public class DataConverterMap extends DataFix { + + public DataConverterMap(Schema schema, boolean flag) { + super(schema, flag); + } + + public TypeRewriteRule makeRule() { + Type type = this.getInputSchema().getType(DataConverterTypes.ITEM_STACK); + OpticFinder> opticfinder = DSL.fieldFinder("id", DSL.named(DataConverterTypes.q.typeName(), DSL.namespacedString())); + OpticFinder opticfinder1 = type.findField("tag"); + + return this.fixTypeEverywhereTyped("ItemInstanceMapIdFix", type, (typed) -> { + Optional> optional = typed.getOptional(opticfinder); + + if (optional.isPresent() && Objects.equals(((Pair) optional.get()).getSecond(), "minecraft:filled_map")) { + Dynamic dynamic = (Dynamic) typed.get(DSL.remainderFinder()); + Typed typed1 = typed.getOrCreateTyped(opticfinder1); + Dynamic dynamic1 = (Dynamic) typed1.get(DSL.remainderFinder()); + + if (!dynamic1.get("map").isPresent()) dynamic1 = dynamic1.set("map", dynamic1.createInt(dynamic.getInt("Damage"))); // CraftBukkit + return typed.set(opticfinder1, typed1.set(DSL.remainderFinder(), dynamic1)); + } else { + return typed; + } + }); + } +} diff --git a/src/main/java/net/minecraft/server/DataConverterRegistry.java b/src/main/java/net/minecraft/server/DataConverterRegistry.java new file mode 100644 index 000000000000..db9af361d6a4 --- /dev/null +++ b/src/main/java/net/minecraft/server/DataConverterRegistry.java @@ -0,0 +1,346 @@ +package net.minecraft.server; + +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.DataFixer; +import com.mojang.datafixers.DataFixerBuilder; +import com.mojang.datafixers.Typed; +import com.mojang.datafixers.schemas.Schema; +import java.util.Objects; +import java.util.concurrent.ForkJoinPool; +import java.util.function.BiFunction; + +public class DataConverterRegistry { + + private static final BiFunction a = Schema::new; + private static final BiFunction b = DataConverterSchemaNamed::new; + private static final DataFixer c = b(); + + private static DataFixer b() { + DataFixerBuilder datafixerbuilder = new DataFixerBuilder(1631); + + a(datafixerbuilder); + // CraftBukkit start + ForkJoinPool pool = new ForkJoinPool(Integer.getInteger("net.minecraft.server.DataConverterRegistry.bootstrapThreads", Math.min(6, Math.max(Runtime.getRuntime().availableProcessors() - 2, 2)))); // Paper - use more reasonable default - 2 is hard minimum to avoid using unlimited threads + DataFixer fixer = datafixerbuilder.build(pool); + pool.shutdown(); + return fixer; + // CraftBukkit end + } + + public static DataFixer a() { + return DataConverterRegistry.c; + } + + private static void a(DataFixerBuilder datafixerbuilder) { + Schema schema = datafixerbuilder.addSchema(99, DataConverterSchemaV99::new); + Schema schema1 = datafixerbuilder.addSchema(100, DataConverterSchemaV100::new); + + datafixerbuilder.addFixer(new DataConverterEquipment(schema1, true)); + Schema schema2 = datafixerbuilder.addSchema(101, DataConverterRegistry.a); + + datafixerbuilder.addFixer(new DataConverterSignText(schema2, false)); + Schema schema3 = datafixerbuilder.addSchema(102, DataConverterSchemaV102::new); + + datafixerbuilder.addFixer(new DataConverterMaterialId(schema3, true)); + datafixerbuilder.addFixer(new DataConverterPotionId(schema3, false)); + Schema schema4 = datafixerbuilder.addSchema(105, DataConverterRegistry.a); + + datafixerbuilder.addFixer(new DataConverterSpawnEgg(schema4, true)); + Schema schema5 = datafixerbuilder.addSchema(106, DataConverterSchemaV106::new); + + datafixerbuilder.addFixer(new DataConverterMobSpawner(schema5, true)); + Schema schema6 = datafixerbuilder.addSchema(107, DataConverterSchemaV107::new); + + datafixerbuilder.addFixer(new DataConverterMinecart(schema6, true)); + Schema schema7 = datafixerbuilder.addSchema(108, DataConverterRegistry.a); + + datafixerbuilder.addFixer(new DataConverterUUID(schema7, true)); + Schema schema8 = datafixerbuilder.addSchema(109, DataConverterRegistry.a); + + datafixerbuilder.addFixer(new DataConverterHealth(schema8, true)); + Schema schema9 = datafixerbuilder.addSchema(110, DataConverterRegistry.a); + + datafixerbuilder.addFixer(new DataConverterSaddle(schema9, true)); + Schema schema10 = datafixerbuilder.addSchema(111, DataConverterRegistry.a); + + datafixerbuilder.addFixer(new DataConverterHanging(schema10, true)); + Schema schema11 = datafixerbuilder.addSchema(113, DataConverterRegistry.a); + + datafixerbuilder.addFixer(new DataConverterDropChances(schema11, true)); + Schema schema12 = datafixerbuilder.addSchema(135, DataConverterSchemaV135::new); + + datafixerbuilder.addFixer(new DataConverterRiding(schema12, true)); + Schema schema13 = datafixerbuilder.addSchema(143, DataConverterSchemaV143::new); + + datafixerbuilder.addFixer(new DataConverterEntityTippedArrow(schema13, true)); + Schema schema14 = datafixerbuilder.addSchema(147, DataConverterRegistry.a); + + datafixerbuilder.addFixer(new DataConverterArmorStand(schema14, true)); + Schema schema15 = datafixerbuilder.addSchema(165, DataConverterRegistry.a); + + datafixerbuilder.addFixer(new DataConverterBook(schema15, true)); + Schema schema16 = datafixerbuilder.addSchema(501, DataConverterSchemaV501::new); + + datafixerbuilder.addFixer(new DataConverterAddChoices(schema16, "Add 1.10 entities fix", DataConverterTypes.ENTITY)); + Schema schema17 = datafixerbuilder.addSchema(502, DataConverterRegistry.a); + + datafixerbuilder.addFixer(DataConverterItemName.a(schema17, "cooked_fished item renamer", (s) -> { + return Objects.equals(DataConverterSchemaNamed.a(s), "minecraft:cooked_fished") ? "minecraft:cooked_fish" : s; + })); + datafixerbuilder.addFixer(new DataConverterZombie(schema17, false)); + Schema schema18 = datafixerbuilder.addSchema(505, DataConverterRegistry.a); + + datafixerbuilder.addFixer(new DataConverterVBO(schema18, false)); + Schema schema19 = datafixerbuilder.addSchema(700, DataConverterSchemaV700::new); + + datafixerbuilder.addFixer(new DataConverterGuardian(schema19, true)); + Schema schema20 = datafixerbuilder.addSchema(701, DataConverterSchemaV701::new); + + datafixerbuilder.addFixer(new DataConverterSkeleton(schema20, true)); + Schema schema21 = datafixerbuilder.addSchema(702, DataConverterSchemaV702::new); + + datafixerbuilder.addFixer(new DataConverterZombieType(schema21, true)); + Schema schema22 = datafixerbuilder.addSchema(703, DataConverterSchemaV703::new); + + datafixerbuilder.addFixer(new DataConverterHorse(schema22, true)); + Schema schema23 = datafixerbuilder.addSchema(704, DataConverterSchemaV704::new); + + datafixerbuilder.addFixer(new DataConverterTileEntity(schema23, true)); + Schema schema24 = datafixerbuilder.addSchema(705, DataConverterSchemaV705::new); + + datafixerbuilder.addFixer(new DataConverterEntity(schema24, true)); + Schema schema25 = datafixerbuilder.addSchema(804, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterBanner(schema25, true)); + Schema schema26 = datafixerbuilder.addSchema(806, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterPotionWater(schema26, false)); + Schema schema27 = datafixerbuilder.addSchema(808, DataConverterSchemaV808::new); + + datafixerbuilder.addFixer(new DataConverterAddChoices(schema27, "added shulker box", DataConverterTypes.j)); + Schema schema28 = datafixerbuilder.addSchema(808, 1, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterShulker(schema28, false)); + Schema schema29 = datafixerbuilder.addSchema(813, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterShulkerBoxItem(schema29, false)); + datafixerbuilder.addFixer(new DataConverterShulkerBoxBlock(schema29, false)); + Schema schema30 = datafixerbuilder.addSchema(816, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterLang(schema30, false)); + Schema schema31 = datafixerbuilder.addSchema(820, DataConverterRegistry.b); + + datafixerbuilder.addFixer(DataConverterItemName.a(schema31, "totem item renamer", (s) -> { + return Objects.equals(s, "minecraft:totem") ? "minecraft:totem_of_undying" : s; + })); + Schema schema32 = datafixerbuilder.addSchema(1022, DataConverterSchemaV1022::new); + + datafixerbuilder.addFixer(new DataConverterShoulderEntity(schema32, "added shoulder entities to players", DataConverterTypes.PLAYER)); + Schema schema33 = datafixerbuilder.addSchema(1125, DataConverterSchemaV1125::new); + + datafixerbuilder.addFixer(new DataConverterBedBlock(schema33, true)); + datafixerbuilder.addFixer(new DataConverterBedItem(schema33, false)); + Schema schema34 = datafixerbuilder.addSchema(1344, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterKeybind(schema34, false)); + Schema schema35 = datafixerbuilder.addSchema(1446, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterKeybind2(schema35, false)); + Schema schema36 = datafixerbuilder.addSchema(1450, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterFlattenState(schema36, false)); + Schema schema37 = datafixerbuilder.addSchema(1451, DataConverterSchemaV1451::new); + + datafixerbuilder.addFixer(new DataConverterAddChoices(schema37, "AddTrappedChestFix", DataConverterTypes.j)); + Schema schema38 = datafixerbuilder.addSchema(1451, 1, DataConverterSchemaV1451_1::new); + + datafixerbuilder.addFixer(new ChunkConverterPalette(schema38, true)); + Schema schema39 = datafixerbuilder.addSchema(1451, 2, DataConverterSchemaV1451_2::new); + + datafixerbuilder.addFixer(new DataConverterPiston(schema39, true)); + Schema schema40 = datafixerbuilder.addSchema(1451, 3, DataConverterSchemaV1451_3::new); + + datafixerbuilder.addFixer(new DataConverterEntityBlockState(schema40, true)); + datafixerbuilder.addFixer(new DataConverterMap(schema40, false)); + Schema schema41 = datafixerbuilder.addSchema(1451, 4, DataConverterSchemaV1451_4::new); + + datafixerbuilder.addFixer(new DataConverterBlockName(schema41, true)); + datafixerbuilder.addFixer(new DataConverterFlatten(schema41, false)); + Schema schema42 = datafixerbuilder.addSchema(1451, 5, DataConverterSchemaV1451_5::new); + + datafixerbuilder.addFixer(new DataConverterAddChoices(schema42, "RemoveNoteBlockFlowerPotFix", DataConverterTypes.j)); + datafixerbuilder.addFixer(new DataConverterFlattenSpawnEgg(schema42, false)); + datafixerbuilder.addFixer(new DataConverterWolf(schema42, false)); + datafixerbuilder.addFixer(new DataConverterBannerColour(schema42, false)); + datafixerbuilder.addFixer(new DataConverterWorldGenSettings(schema42, false)); + Schema schema43 = datafixerbuilder.addSchema(1451, 6, DataConverterSchemaV1451_6::new); + + datafixerbuilder.addFixer(new DataConverterStatistic(schema43, true)); + datafixerbuilder.addFixer(new DataConverterJukeBox(schema43, false)); + Schema schema44 = datafixerbuilder.addSchema(1451, 7, DataConverterSchemaV1451_7::new); + + datafixerbuilder.addFixer(new DataConverterVillage(schema44, true)); + Schema schema45 = datafixerbuilder.addSchema(1451, 7, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterVillagerTrade(schema45, false)); + Schema schema46 = datafixerbuilder.addSchema(1456, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterItemFrame(schema46, false)); + Schema schema47 = datafixerbuilder.addSchema(1458, DataConverterRegistry.b); + + // CraftBukkit start + datafixerbuilder.addFixer(new com.mojang.datafixers.DataFix(schema47, false) { + @Override + protected com.mojang.datafixers.TypeRewriteRule makeRule() { + return this.fixTypeEverywhereTyped("Player CustomName", this.getInputSchema().getType(DataConverterTypes.PLAYER), (typed) -> { + return typed.update(DSL.remainderFinder(), (dynamic) -> { + return DataConverterCustomNameEntity.a(dynamic); + }); + }); + } + }); + // CraftBukkit end + datafixerbuilder.addFixer(new DataConverterCustomNameEntity(schema47, false)); + datafixerbuilder.addFixer(new DataConverterCustomNameItem(schema47, false)); + datafixerbuilder.addFixer(new DataConverterCustomNameTile(schema47, false)); + Schema schema48 = datafixerbuilder.addSchema(1460, DataConverterSchemaV1460::new); + + datafixerbuilder.addFixer(new DataConverterPainting(schema48, false)); + Schema schema49 = datafixerbuilder.addSchema(1466, DataConverterSchemaV1466::new); + + datafixerbuilder.addFixer(new DataConverterProtoChunk(schema49, true)); + Schema schema50 = datafixerbuilder.addSchema(1470, DataConverterSchemaV1470::new); + + datafixerbuilder.addFixer(new DataConverterAddChoices(schema50, "Add 1.13 entities fix", DataConverterTypes.ENTITY)); + Schema schema51 = datafixerbuilder.addSchema(1474, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterColorlessShulkerEntity(schema51, false)); + datafixerbuilder.addFixer(DataConverterBlockRename.a(schema51, "Colorless shulker block fixer", (s) -> { + return Objects.equals(DataConverterSchemaNamed.a(s), "minecraft:purple_shulker_box") ? "minecraft:shulker_box" : s; + })); + datafixerbuilder.addFixer(DataConverterItemName.a(schema51, "Colorless shulker item fixer", (s) -> { + return Objects.equals(DataConverterSchemaNamed.a(s), "minecraft:purple_shulker_box") ? "minecraft:shulker_box" : s; + })); + Schema schema52 = datafixerbuilder.addSchema(1475, DataConverterRegistry.b); + + datafixerbuilder.addFixer(DataConverterBlockRename.a(schema52, "Flowing fixer", (s) -> { + return (String) ImmutableMap.of("minecraft:flowing_water", "minecraft:water", "minecraft:flowing_lava", "minecraft:lava").getOrDefault(s, s); + })); + Schema schema53 = datafixerbuilder.addSchema(1480, DataConverterRegistry.b); + + datafixerbuilder.addFixer(DataConverterBlockRename.a(schema53, "Rename coral blocks", (s) -> { + return (String) DataConverterCoral.a.getOrDefault(s, s); + })); + datafixerbuilder.addFixer(DataConverterItemName.a(schema53, "Rename coral items", (s) -> { + return (String) DataConverterCoral.a.getOrDefault(s, s); + })); + Schema schema54 = datafixerbuilder.addSchema(1481, DataConverterSchemaV1481::new); + + datafixerbuilder.addFixer(new DataConverterAddChoices(schema54, "Add conduit", DataConverterTypes.j)); + Schema schema55 = datafixerbuilder.addSchema(1483, DataConverterSchemaV1483::new); + + datafixerbuilder.addFixer(new DataConverterEntityPufferfish(schema55, true)); + datafixerbuilder.addFixer(DataConverterItemName.a(schema55, "Rename pufferfish egg item", (s) -> { + return (String) DataConverterEntityPufferfish.a.getOrDefault(s, s); + })); + Schema schema56 = datafixerbuilder.addSchema(1484, DataConverterRegistry.b); + + datafixerbuilder.addFixer(DataConverterItemName.a(schema56, "Rename seagrass items", (s) -> { + return (String) ImmutableMap.of("minecraft:sea_grass", "minecraft:seagrass", "minecraft:tall_sea_grass", "minecraft:tall_seagrass").getOrDefault(s, s); + })); + datafixerbuilder.addFixer(DataConverterBlockRename.a(schema56, "Rename seagrass blocks", (s) -> { + return (String) ImmutableMap.of("minecraft:sea_grass", "minecraft:seagrass", "minecraft:tall_sea_grass", "minecraft:tall_seagrass").getOrDefault(s, s); + })); + datafixerbuilder.addFixer(new DataConverterHeightmapRenaming(schema56, false)); + Schema schema57 = datafixerbuilder.addSchema(1486, DataConverterSchemaV1486::new); + + datafixerbuilder.addFixer(new DataConverterEntityCodSalmon(schema57, true)); + datafixerbuilder.addFixer(DataConverterItemName.a(schema57, "Rename cod/salmon egg items", (s) -> { + return (String) DataConverterEntityCodSalmon.b.getOrDefault(s, s); + })); + Schema schema58 = datafixerbuilder.addSchema(1487, DataConverterRegistry.b); + + datafixerbuilder.addFixer(DataConverterItemName.a(schema58, "Rename prismarine_brick(s)_* blocks", (s) -> { + return (String) ImmutableMap.of("minecraft:prismarine_bricks_slab", "minecraft:prismarine_brick_slab", "minecraft:prismarine_bricks_stairs", "minecraft:prismarine_brick_stairs").getOrDefault(s, s); + })); + datafixerbuilder.addFixer(DataConverterBlockRename.a(schema58, "Rename prismarine_brick(s)_* items", (s) -> { + return (String) ImmutableMap.of("minecraft:prismarine_bricks_slab", "minecraft:prismarine_brick_slab", "minecraft:prismarine_bricks_stairs", "minecraft:prismarine_brick_stairs").getOrDefault(s, s); + })); + Schema schema59 = datafixerbuilder.addSchema(1488, DataConverterRegistry.b); + + datafixerbuilder.addFixer(DataConverterBlockRename.a(schema59, "Rename kelp/kelptop", (s) -> { + return (String) ImmutableMap.of("minecraft:kelp_top", "minecraft:kelp", "minecraft:kelp", "minecraft:kelp_plant").getOrDefault(s, s); + })); + datafixerbuilder.addFixer(DataConverterItemName.a(schema59, "Rename kelptop", (s) -> { + return Objects.equals(s, "minecraft:kelp_top") ? "minecraft:kelp" : s; + })); + datafixerbuilder.addFixer(new DataConverterNamedEntity(schema59, false, "Command block block entity custom name fix", DataConverterTypes.j, "minecraft:command_block") { + protected Typed a(Typed typed) { + return typed.update(DSL.remainderFinder(), DataConverterCustomNameEntity::a); + } + }); + datafixerbuilder.addFixer(new DataConverterNamedEntity(schema59, false, "Command block minecart custom name fix", DataConverterTypes.ENTITY, "minecraft:commandblock_minecart") { + protected Typed a(Typed typed) { + return typed.update(DSL.remainderFinder(), DataConverterCustomNameEntity::a); + } + }); + datafixerbuilder.addFixer(new DataConverterIglooMetadataRemoval(schema59, false)); + Schema schema60 = datafixerbuilder.addSchema(1490, DataConverterRegistry.b); + + datafixerbuilder.addFixer(DataConverterBlockRename.a(schema60, "Rename melon_block", (s) -> { + return Objects.equals(s, "minecraft:melon_block") ? "minecraft:melon" : s; + })); + datafixerbuilder.addFixer(DataConverterItemName.a(schema60, "Rename melon_block/melon/speckled_melon", (s) -> { + return (String) ImmutableMap.of("minecraft:melon_block", "minecraft:melon", "minecraft:melon", "minecraft:melon_slice", "minecraft:speckled_melon", "minecraft:glistering_melon_slice").getOrDefault(s, s); + })); + Schema schema61 = datafixerbuilder.addSchema(1492, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterChunkStructuresTemplateRename(schema61, false)); + Schema schema62 = datafixerbuilder.addSchema(1494, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterItemStackEnchantment(schema62, false)); + Schema schema63 = datafixerbuilder.addSchema(1496, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterLeaves(schema63, false)); + Schema schema64 = datafixerbuilder.addSchema(1500, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterBlockEntityKeepPacked(schema64, false)); + Schema schema65 = datafixerbuilder.addSchema(1501, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterAdvancement(schema65, false)); + Schema schema66 = datafixerbuilder.addSchema(1502, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterRecipes(schema66, false)); + Schema schema67 = datafixerbuilder.addSchema(1506, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterLevelDataGeneratorOptions(schema67, false)); + Schema schema68 = datafixerbuilder.addSchema(1508, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterBiome(schema68, false)); + Schema schema69 = datafixerbuilder.addSchema(1510, DataConverterSchemaV1510::new); + + datafixerbuilder.addFixer(DataConverterBlockRename.a(schema69, "Block renamening fix", (s) -> { + return (String) DataConverterEntityRename.b.getOrDefault(s, s); + })); + datafixerbuilder.addFixer(DataConverterItemName.a(schema69, "Item renamening fix", (s) -> { + return (String) DataConverterEntityRename.c.getOrDefault(s, s); + })); + datafixerbuilder.addFixer(new DataConverterRecipeRename(schema69, false)); + datafixerbuilder.addFixer(new DataConverterEntityRename(schema69, true)); + datafixerbuilder.addFixer(new DataConverterSwimStats(schema69, false)); + Schema schema70 = datafixerbuilder.addSchema(1514, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterObjectiveDisplayName(schema70, false)); + datafixerbuilder.addFixer(new DataConverterTeamDisplayName(schema70, false)); + datafixerbuilder.addFixer(new DataConverterObjectiveRenderType(schema70, false)); + Schema schema71 = datafixerbuilder.addSchema(1515, DataConverterRegistry.b); + + datafixerbuilder.addFixer(DataConverterBlockRename.a(schema71, "Rename coral fan blocks", (s) -> { + return (String) DataConverterCoralFan.a.getOrDefault(s, s); + })); + Schema schema72 = datafixerbuilder.addSchema(1624, DataConverterRegistry.b); + + datafixerbuilder.addFixer(new DataConverterTrappedChest(schema72, false)); + } +} diff --git a/src/main/java/net/minecraft/server/DataPalette.java b/src/main/java/net/minecraft/server/DataPalette.java new file mode 100644 index 000000000000..2ee8791963c9 --- /dev/null +++ b/src/main/java/net/minecraft/server/DataPalette.java @@ -0,0 +1,19 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public interface DataPalette { + + default int getOrCreateIdFor(T object) { return this.a(object); } // Paper - OBFHELPER + int a(T t0); + + @Nullable default T getObject(int dataBits) { return this.a(dataBits); } // Paper - OBFHELPER + @Nullable + T a(int i); + + void b(PacketDataSerializer packetdataserializer); + + int a(); + + void a(NBTTagList nbttaglist); +} diff --git a/src/main/java/net/minecraft/server/DataPaletteBlock.java b/src/main/java/net/minecraft/server/DataPaletteBlock.java new file mode 100644 index 000000000000..dcbcb655c553 --- /dev/null +++ b/src/main/java/net/minecraft/server/DataPaletteBlock.java @@ -0,0 +1,276 @@ +package net.minecraft.server; + +import com.destroystokyo.paper.antixray.ChunkPacketInfo; // Paper - Anti-Xray +import java.util.Arrays; +import java.util.Objects; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class DataPaletteBlock implements DataPaletteExpandable { + + private final DataPalette b; private final DataPalette getDataPaletteGlobal() { return this.b; } // Paper - OBFHELPER + private final DataPaletteExpandable c = (i, object) -> { + return 0; + }; + private final RegistryBlockID d; + private final Function e; + private final Function f; + private final T g; + private final T[] predefinedObjects; // Paper - Anti-Xray - Add predefined objects + protected DataBits a; protected DataBits getDataBits() { return this.a; } // Paper - OBFHELPER + private DataPalette h; private DataPalette getDataPalette() { return this.h; } // Paper - OBFHELPER + private int i; private int getBitsPerObject() { return this.i; } // Paper - OBFHELPER + // Paper start - use read write locks only during generation, disable once back on main thread + private static final NoopLock NOOP_LOCK = new NoopLock(); + private java.util.concurrent.locks.Lock readLock = NOOP_LOCK; + private java.util.concurrent.locks.Lock writeLock = NOOP_LOCK; + + private static class NoopLock extends ReentrantReadWriteLock.WriteLock { + private NoopLock() { + super(new ReentrantReadWriteLock()); + } + + @Override + public final void lock() { + } + + @Override + public final void unlock() { + + } + } + + synchronized void enableLocks() { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + readLock = lock.readLock(); + writeLock = lock.writeLock(); + } + synchronized void disableLocks() { + readLock = NOOP_LOCK; + writeLock = NOOP_LOCK; + } + + private void b() { + writeLock.lock(); + } + private void c() { + writeLock.unlock(); + } + // Paper end + + public DataPaletteBlock(DataPalette datapalette, RegistryBlockID registryblockid, Function function, Function function1, T t0) { + // Paper start - Anti-Xray - Support default constructor + this(datapalette, registryblockid, function, function1, t0, null, true); + } + + public DataPaletteBlock(DataPalette datapalette, RegistryBlockID registryblockid, Function function, Function function1, T t0, T[] predefinedObjects, boolean initialize) { + // Paper end - Anti-Xray - Add predefined objects + this.b = datapalette; + this.d = registryblockid; + this.e = function; + this.f = function1; + this.g = t0; + // Paper start - Anti-Xray - Add predefined objects + this.predefinedObjects = predefinedObjects; + + if (initialize) { + if (predefinedObjects == null) { + // Default + this.initialize(4); + } else { + // TODO: MathHelper.d(int i) can be used here instead (see DataPaletteBlock#a(NBTTagCompound nbttagcompound, String s, String s1)) but I don't understand the implementation + // Count the bits of the maximum array index to initialize a data palette with enough space from the beginning + // The length of the array is used because air is also added to the data palette from the beginning + // Start with at least 4 + int maxIndex = predefinedObjects.length >> 4; + int bitCount = 4; + + while (maxIndex != 0) { + maxIndex >>= 1; + bitCount++; + } + + // Initialize with at least 15 free indixes + this.initialize((1 << bitCount) - predefinedObjects.length < 16 ? bitCount + 1 : bitCount); + this.addPredefinedObjects(); + } + } + // Paper end + } + + private static int b(int i, int j, int k) { + return j << 8 | k << 4 | i; + } + + private void initialize(int bitsPerObject) { this.b(bitsPerObject); } // Paper - OBFHELPER + private void b(int i) { + if (i != this.i) { + this.i = i; + if (this.i <= 4) { + this.i = 4; + this.h = new DataPaletteLinear<>(this.d, this.i, this, this.e); + } else if (this.i < 9) { + this.h = new DataPaletteHash<>(this.d, this.i, this, this.e, this.f); + } else { + this.h = this.b; + this.i = MathHelper.d(this.d.a()); + } + + this.h.a(this.g); + this.a = new DataBits(this.i, 4096); + } + } + + // Paper start - Anti-Xray - Add predefined objects + private void addPredefinedObjects() { + if (this.predefinedObjects != null && this.getDataPalette() != this.getDataPaletteGlobal()) { + for (int i = 0; i < this.predefinedObjects.length; i++) { + this.getDataPalette().getOrCreateIdFor(this.predefinedObjects[i]); + } + } + } + // Paper end + + public int onResize(int i, T t0) { + this.b(); + DataBits databits = this.a; + DataPalette datapalette = this.h; + + this.b(i); + + int j; + + this.addPredefinedObjects(); // Paper - Anti-Xray - Add predefined objects + for (j = 0; j < databits.b(); ++j) { + T t1 = datapalette.a(databits.a(j)); + + if (t1 != null) { + this.setBlockIndex(j, t1); + } + } + + j = this.h.a(t0); + this.c(); + return j; + } + + public void setBlock(int i, int j, int k, T t0) { + this.b(); + this.setBlockIndex(b(i, j, k), t0); + this.c(); + } + + protected void setBlockIndex(int i, T t0) { + int j = this.h.a(t0); + + this.a.a(i, j); + } + + public T a(int i, int j, int k) { + return this.a(b(i, j, k)); + } + + protected T a(int i) { + try { // Paper start - read lock + readLock.lock(); + T object = this.h.a(this.a.a(i)); // Paper - decompile fix + return (T)(object == null ? this.g : object); + } finally { + readLock.unlock(); + } // Paper end + } + + // Paper start - Anti-Xray - Support default methods + public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer) { this.b(packetDataSerializer); } + public void b(PacketDataSerializer packetdataserializer) { + this.b(packetdataserializer, null, 0); + } + // Paper end + + public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer, ChunkPacketInfo chunkPacketInfo, int chunkSectionIndex) { this.b(packetDataSerializer, chunkPacketInfo, chunkSectionIndex); } // Paper - OBFHELPER // Paper - Anti-Xray - Add chunk packet info + public void b(PacketDataSerializer packetdataserializer, ChunkPacketInfo chunkPacketInfo, int chunkSectionIndex) { // Paper - Anti-Xray - Add chunk packet info + this.b(); + packetdataserializer.writeByte(this.i); + this.h.b(packetdataserializer); + + // Paper start - Anti-Xray - Add chunk packet info + if (chunkPacketInfo != null) { + chunkPacketInfo.setBitsPerObject(chunkSectionIndex, this.getBitsPerObject()); + chunkPacketInfo.setDataPalette(chunkSectionIndex, this.getDataPalette()); + chunkPacketInfo.setDataBitsIndex(chunkSectionIndex, packetdataserializer.writerIndex() + PacketDataSerializer.countBytes(this.getDataBits().getDataBits().length)); + chunkPacketInfo.setPredefinedObjects(chunkSectionIndex, this.predefinedObjects); + } + // Paper end + + packetdataserializer.a(this.a.a()); + this.c(); + } + + public void a(NBTTagCompound nbttagcompound, String s, String s1) { + this.b(); + NBTTagList nbttaglist = nbttagcompound.getList(s, 10); + // Paper - Anti-Xray - TODO: Should this.predefinedObjects.length just be added here (faster) or should the contents be compared to calculate the size (less RAM)? + int i = Math.max(4, MathHelper.d(nbttaglist.size() + (this.predefinedObjects == null ? 0 : this.predefinedObjects.length))); // Paper - Anti-Xray - Calculate the size with predefined objects + + if (true || i != this.i) { // Paper - Anti-Xray - Not initialized yet + this.b(i); + } + + this.h.a(nbttaglist); + this.addPredefinedObjects(); // Paper - Anti-Xray - Add predefined objects + long[] along = nbttagcompound.o(s1); + int j = along.length * 64 / 4096; + + if (this.h == this.b) { + DataPalette datapalette = new DataPaletteHash<>(this.d, i, this.c, this.e, this.f); + + datapalette.a(nbttaglist); + DataBits databits = new DataBits(i, 4096, along); + + for (int k = 0; k < 4096; ++k) { + this.a.a(k, this.b.a(datapalette.a(databits.a(k)))); + } + } else if (j == this.i) { + System.arraycopy(along, 0, this.a.a(), 0, along.length); + } else { + DataBits databits1 = new DataBits(j, 4096, along); + + for (int l = 0; l < 4096; ++l) { + this.a.a(l, databits1.a(l)); + } + } + + this.c(); + } + + public void b(NBTTagCompound nbttagcompound, String s, String s1) { + this.b(); + DataPaletteHash datapalettehash = new DataPaletteHash<>(this.d, this.i, this.c, this.e, this.f); + + datapalettehash.a(this.g); + int[] aint = new int[4096]; + + for (int i = 0; i < 4096; ++i) { + aint[i] = datapalettehash.a(this.a(i)); + } + + NBTTagList nbttaglist = new NBTTagList(); + + datapalettehash.b(nbttaglist); + nbttagcompound.set(s, nbttaglist); + int j = Math.max(4, MathHelper.d(nbttaglist.size())); + DataBits databits = new DataBits(j, 4096); + + for (int k = 0; k < aint.length; ++k) { + databits.a(k, aint[k]); + } + + nbttagcompound.a(s1, databits.a()); + this.c(); + } + + public int a() { + return 1 + this.h.a() + PacketDataSerializer.a(this.a.b()) + this.a.a().length * 8; + } +} diff --git a/src/main/java/net/minecraft/server/DataWatcher.java b/src/main/java/net/minecraft/server/DataWatcher.java new file mode 100644 index 000000000000..51a02edf8352 --- /dev/null +++ b/src/main/java/net/minecraft/server/DataWatcher.java @@ -0,0 +1,311 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import io.netty.handler.codec.DecoderException; +import io.netty.handler.codec.EncoderException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import javax.annotation.Nullable; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; // Paper +import org.apache.commons.lang3.ObjectUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class DataWatcher { + + private static final Logger a = LogManager.getLogger(); + private static final Map, Integer> b = Maps.newHashMap(); + private final Entity c; + private final Map> d = new Int2ObjectOpenHashMap<>(); // Paper + private final ReadWriteLock e = new ReentrantReadWriteLock(); + private boolean f = true; + private boolean g; + + public DataWatcher(Entity entity) { + this.c = entity; + } + + public static DataWatcherObject a(Class oclass, DataWatcherSerializer datawatcherserializer) { + if (DataWatcher.a.isDebugEnabled()) { + try { + Class oclass1 = Class.forName(Thread.currentThread().getStackTrace()[2].getClassName()); + + if (!oclass1.equals(oclass)) { + DataWatcher.a.debug("defineId called for: {} from {}", oclass, oclass1, new RuntimeException()); + } + } catch (ClassNotFoundException classnotfoundexception) { + ; + } + } + + int i; + + if (DataWatcher.b.containsKey(oclass)) { + i = (Integer) DataWatcher.b.get(oclass) + 1; + } else { + int j = 0; + Class oclass2 = oclass; + + while (oclass2 != Entity.class) { + oclass2 = oclass2.getSuperclass(); + if (DataWatcher.b.containsKey(oclass2)) { + j = (Integer) DataWatcher.b.get(oclass2) + 1; + break; + } + } + + i = j; + } + + if (i > 254) { + throw new IllegalArgumentException("Data value id is too big with " + i + "! (Max is " + 254 + ")"); + } else { + DataWatcher.b.put(oclass, i); + return datawatcherserializer.a(i); + } + } + + public void register(DataWatcherObject datawatcherobject, T t0) { + int i = datawatcherobject.a(); + + if (i > 254) { + throw new IllegalArgumentException("Data value id is too big with " + i + "! (Max is " + 254 + ")"); + } else if (this.d.containsKey(i)) { + throw new IllegalArgumentException("Duplicate id value for " + i + "!"); + } else if (DataWatcherRegistry.b(datawatcherobject.b()) < 0) { + throw new IllegalArgumentException("Unregistered serializer " + datawatcherobject.b() + " for " + i + "!"); + } else { + this.registerObject(datawatcherobject, t0); + } + } + + private void registerObject(DataWatcherObject datawatcherobject, T t0) { + DataWatcher.Item datawatcher_item = new DataWatcher.Item<>(datawatcherobject, t0); + + this.e.writeLock().lock(); + this.d.put(datawatcherobject.a(), datawatcher_item); + this.f = false; + this.e.writeLock().unlock(); + } + + private DataWatcher.Item b(DataWatcherObject datawatcherobject) { + this.e.readLock().lock(); + + DataWatcher.Item datawatcher_item; + + try { + datawatcher_item = (DataWatcher.Item) this.d.get(datawatcherobject.a()); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Getting synched entity data"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Synched entity data"); + + crashreportsystemdetails.a("Data ID", (Object) datawatcherobject); + throw new ReportedException(crashreport); + } + + this.e.readLock().unlock(); + return datawatcher_item; + } + + public T get(DataWatcherObject datawatcherobject) { + return this.b(datawatcherobject).b(); + } + + public void set(DataWatcherObject datawatcherobject, T t0) { + DataWatcher.Item datawatcher_item = this.b(datawatcherobject); + + if (ObjectUtils.notEqual(t0, datawatcher_item.b())) { + datawatcher_item.a(t0); + this.c.a(datawatcherobject); + datawatcher_item.a(true); + this.g = true; + } + + } + + // CraftBukkit start - add method from above + public void markDirty(DataWatcherObject datawatcherobject) { + this.b(datawatcherobject).a(true); + this.g = true; + } + // CraftBukkit end + + public boolean a() { + return this.g; + } + + public static void a(List> list, PacketDataSerializer packetdataserializer) throws IOException { + if (list != null) { + int i = 0; + + for (int j = list.size(); i < j; ++i) { + a(packetdataserializer, (DataWatcher.Item) list.get(i)); + } + } + + packetdataserializer.writeByte(255); + } + + @Nullable + public List> b() { + List> list = null; + + if (this.g) { + this.e.readLock().lock(); + Iterator iterator = this.d.values().iterator(); + + while (iterator.hasNext()) { + DataWatcher.Item datawatcher_item = (DataWatcher.Item) iterator.next(); + + if (datawatcher_item.c()) { + datawatcher_item.a(false); + if (list == null) { + list = Lists.newArrayList(); + } + + list.add(datawatcher_item.d()); + } + } + + this.e.readLock().unlock(); + } + + this.g = false; + return list; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.e.readLock().lock(); + Iterator iterator = this.d.values().iterator(); + + while (iterator.hasNext()) { + DataWatcher.Item datawatcher_item = (DataWatcher.Item) iterator.next(); + + a(packetdataserializer, datawatcher_item); + } + + this.e.readLock().unlock(); + packetdataserializer.writeByte(255); + } + + @Nullable + public List> c() { + List> list = null; + + this.e.readLock().lock(); + + DataWatcher.Item datawatcher_item; + + for (Iterator iterator = this.d.values().iterator(); iterator.hasNext(); list.add(datawatcher_item.d())) { + datawatcher_item = (DataWatcher.Item) iterator.next(); + if (list == null) { + list = Lists.newArrayList(); + } + } + + this.e.readLock().unlock(); + return list; + } + + private static void a(PacketDataSerializer packetdataserializer, DataWatcher.Item datawatcher_item) throws IOException { + DataWatcherObject datawatcherobject = datawatcher_item.a(); + int i = DataWatcherRegistry.b(datawatcherobject.b()); + + if (i < 0) { + throw new EncoderException("Unknown serializer type " + datawatcherobject.b()); + } else { + packetdataserializer.writeByte(datawatcherobject.a()); + packetdataserializer.d(i); + datawatcherobject.b().a(packetdataserializer, datawatcher_item.b()); + } + } + + @Nullable + public static List> b(PacketDataSerializer packetdataserializer) throws IOException { + ArrayList arraylist = null; + + short short0; + + while ((short0 = packetdataserializer.readUnsignedByte()) != 255) { + if (arraylist == null) { + arraylist = Lists.newArrayList(); + } + + int i = packetdataserializer.g(); + DataWatcherSerializer datawatcherserializer = DataWatcherRegistry.a(i); + + if (datawatcherserializer == null) { + throw new DecoderException("Unknown serializer type " + i); + } + + arraylist.add(a(packetdataserializer, short0, datawatcherserializer)); + } + + return arraylist; + } + + private static DataWatcher.Item a(PacketDataSerializer packetdataserializer, int i, DataWatcherSerializer datawatcherserializer) { + return new DataWatcher.Item<>(datawatcherserializer.a(i), datawatcherserializer.a(packetdataserializer)); + } + + public boolean d() { + return this.f; + } + + public void e() { + this.g = false; + this.e.readLock().lock(); + Iterator iterator = this.d.values().iterator(); + + while (iterator.hasNext()) { + DataWatcher.Item datawatcher_item = (DataWatcher.Item) iterator.next(); + + datawatcher_item.a(false); + } + + this.e.readLock().unlock(); + } + + public static class Item { + + private final DataWatcherObject a; + private T b; + private boolean c; + + public Item(DataWatcherObject datawatcherobject, T t0) { + this.a = datawatcherobject; + this.b = t0; + this.c = true; + } + + public DataWatcherObject a() { + return this.a; + } + + public void a(T t0) { + this.b = t0; + } + + public T b() { + return this.b; + } + + public boolean c() { + return this.c; + } + + public void a(boolean flag) { + this.c = flag; + } + + public DataWatcher.Item d() { + return new DataWatcher.Item<>(this.a, this.a.b().a(this.b)); + } + } +} diff --git a/src/main/java/net/minecraft/server/DedicatedServer.java b/src/main/java/net/minecraft/server/DedicatedServer.java new file mode 100644 index 000000000000..21a05b2b218b --- /dev/null +++ b/src/main/java/net/minecraft/server/DedicatedServer.java @@ -0,0 +1,772 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.gson.JsonObject; +import com.mojang.authlib.GameProfileRepository; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import com.mojang.datafixers.DataFixer; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.InetAddress; +import java.net.Proxy; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.function.BooleanSupplier; +import java.util.regex.Pattern; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import java.io.PrintStream; +import org.apache.logging.log4j.Level; + +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.LoggerOutputStream; +import co.aikar.timings.MinecraftTimings; // Paper +import org.bukkit.event.server.ServerCommandEvent; +import org.bukkit.craftbukkit.util.Waitable; +import org.bukkit.event.server.RemoteServerCommandEvent; +// CraftBukkit end + +public class DedicatedServer extends MinecraftServer implements IMinecraftServer { + + private static final Logger LOGGER = LogManager.getLogger(); + private static final Pattern h = Pattern.compile("^[a-fA-F0-9]{40}$"); + private final java.util.Queue serverCommandQueue = new java.util.concurrent.ConcurrentLinkedQueue(); // Paper - use a proper queue + private RemoteStatusListener j; + public final RemoteControlCommandListener remoteControlCommandListener = new RemoteControlCommandListener(this); + private RemoteControlListener l; + public PropertyManager propertyManager; + private EULA eula; + private boolean generateStructures; + private EnumGamemode p; + private boolean q; + + // CraftBukkit start - Signature changed + public DedicatedServer(joptsimple.OptionSet options, DataFixer datafixer, YggdrasilAuthenticationService yggdrasilauthenticationservice, MinecraftSessionService minecraftsessionservice, GameProfileRepository gameprofilerepository, UserCache usercache) { + super(options, Proxy.NO_PROXY, datafixer, new CommandDispatcher().init(true), yggdrasilauthenticationservice, minecraftsessionservice, gameprofilerepository, usercache); + // CraftBukkit end + Thread thread = new Thread("Server Infinisleeper") { + { + this.setDaemon(true); + this.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(DedicatedServer.LOGGER)); + this.start(); + } + + public void run() { + while (true) { + try { + Thread.sleep(2147483647L); + } catch (InterruptedException interruptedexception) { + ; + } + } + } + }; + } + + public boolean init() throws IOException { // CraftBukkit - decompile error + Thread thread = new Thread("Server console handler") { + public void run() { + // CraftBukkit start + if (!org.bukkit.craftbukkit.Main.useConsole) { + return; + } + // Paper start - Use TerminalConsoleAppender + new com.destroystokyo.paper.console.PaperConsole(DedicatedServer.this).start(); + /* + jline.console.ConsoleReader bufferedreader = reader; + // CraftBukkit end + + String s; + + try { + // CraftBukkit start - JLine disabling compatibility + while (!DedicatedServer.this.isStopped() && DedicatedServer.this.isRunning()) { + if (org.bukkit.craftbukkit.Main.useJline) { + s = bufferedreader.readLine(">", null); + } else { + s = bufferedreader.readLine(); + } + if (s != null && s.trim().length() > 0) { // Trim to filter lines which are just spaces + DedicatedServer.this.issueCommand(s, DedicatedServer.this.getServerCommandListener()); + } + // CraftBukkit end + } + } catch (IOException ioexception) { + DedicatedServer.LOGGER.error("Exception handling console input", ioexception); + } + + */ + // Paper end + } + }; + + // CraftBukkit start - TODO: handle command-line logging arguments + java.util.logging.Logger global = java.util.logging.Logger.getLogger(""); + global.setUseParentHandlers(false); + for (java.util.logging.Handler handler : global.getHandlers()) { + global.removeHandler(handler); + } + global.addHandler(new org.bukkit.craftbukkit.util.ForwardLogHandler()); + + // Paper start - Not needed with TerminalConsoleAppender + final org.apache.logging.log4j.Logger logger = LogManager.getRootLogger(); + /* + final org.apache.logging.log4j.core.Logger logger = ((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()); + for (org.apache.logging.log4j.core.Appender appender : logger.getAppenders().values()) { + if (appender instanceof org.apache.logging.log4j.core.appender.ConsoleAppender) { + logger.removeAppender(appender); + } + } + + new Thread(new org.bukkit.craftbukkit.util.TerminalConsoleWriterThread(System.out, this.reader)).start(); + */ + // Paper end + + // Paper start - Use Log4j IOStreams + System.setOut(org.apache.logging.log4j.io.IoBuilder.forLogger(logger).setLevel(Level.INFO).buildPrintStream()); + System.setErr(org.apache.logging.log4j.io.IoBuilder.forLogger(logger).setLevel(Level.WARN).buildPrintStream()); + // Paper end + // CraftBukkit end + + thread.setDaemon(true); + thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(DedicatedServer.LOGGER)); + thread.start(); + DedicatedServer.LOGGER.info("Starting minecraft server version 1.13.2"); + if (Runtime.getRuntime().maxMemory() / 1024L / 1024L < 512L) { + DedicatedServer.LOGGER.warn("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\""); + } + + DedicatedServer.LOGGER.info("Loading properties"); + this.propertyManager = new PropertyManager(this.options); // CraftBukkit - CLI argument support + this.eula = new EULA(new File("eula.txt")); + // Spigot Start + boolean eulaAgreed = Boolean.getBoolean( "com.mojang.eula.agree" ); + if ( eulaAgreed ) + { + System.err.println( "You have used the Spigot command line EULA agreement flag." ); + System.err.println( "By using this setting you are indicating your agreement to Mojang's EULA (https://account.mojang.com/documents/minecraft_eula)." ); + System.err.println( "If you do not agree to the above EULA please stop your server and remove this flag immediately." ); + } + // Spigot End + if (!this.eula.a() && !eulaAgreed) { // Spigot + DedicatedServer.LOGGER.info("You need to agree to the EULA in order to run the server. Go to eula.txt for more info."); + this.eula.b(); + return false; + } else { + if (this.H()) { + this.b("127.0.0.1"); + } else { + this.setOnlineMode(this.propertyManager.getBoolean("online-mode", true)); + this.f(this.propertyManager.getBoolean("prevent-proxy-connections", false)); + this.b(this.propertyManager.getString("server-ip", "")); + } + + this.setSpawnAnimals(this.propertyManager.getBoolean("spawn-animals", true)); + this.setSpawnNPCs(this.propertyManager.getBoolean("spawn-npcs", true)); + this.setPVP(this.propertyManager.getBoolean("pvp", true)); + this.setAllowFlight(this.propertyManager.getBoolean("allow-flight", false)); + this.setResourcePack(this.propertyManager.getString("resource-pack", ""), this.aT()); + this.setMotd(this.propertyManager.getString("motd", "A Minecraft Server")); + this.setForceGamemode(this.propertyManager.getBoolean("force-gamemode", false)); + this.setIdleTimeout(this.propertyManager.getInt("player-idle-timeout", 0)); + this.l(this.propertyManager.getBoolean("enforce-whitelist", false)); + if (this.propertyManager.getInt("difficulty", 1) < 0) { + this.propertyManager.setProperty("difficulty", 0); + } else if (this.propertyManager.getInt("difficulty", 1) > 3) { + this.propertyManager.setProperty("difficulty", 3); + } + + this.generateStructures = this.propertyManager.getBoolean("generate-structures", true); + int i = this.propertyManager.getInt("gamemode", EnumGamemode.SURVIVAL.getId()); + + this.p = WorldSettings.a(i); + DedicatedServer.LOGGER.info("Default game type: {}", this.p); + InetAddress inetaddress = null; + + if (!this.getServerIp().isEmpty()) { + inetaddress = InetAddress.getByName(this.getServerIp()); + } + + if (this.getPort() < 0) { + this.setPort(this.propertyManager.getInt("server-port", 25565)); + } + // Spigot start + this.a((PlayerList) (new DedicatedPlayerList(this))); + org.spigotmc.SpigotConfig.init((File) options.valueOf("spigot-settings")); + org.spigotmc.SpigotConfig.registerCommands(); + // Spigot end + // Paper start + try { + com.destroystokyo.paper.PaperConfig.init((File) options.valueOf("paper-settings")); + } catch (Exception e) { + DedicatedServer.LOGGER.error("Unable to load server configuration", e); + return false; + } + com.destroystokyo.paper.PaperConfig.registerCommands(); + com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // load version history now + // Paper end + + DedicatedServer.LOGGER.info("Generating keypair"); + this.a(MinecraftEncryption.b()); + DedicatedServer.LOGGER.info("Starting Minecraft server on {}:{}", this.getServerIp().isEmpty() ? "*" : this.getServerIp(), this.getPort()); + + if (!org.spigotmc.SpigotConfig.lateBind) { + try { + this.getServerConnection().a(inetaddress, this.getPort()); + } catch (IOException ioexception) { + DedicatedServer.LOGGER.warn("**** FAILED TO BIND TO PORT!"); + DedicatedServer.LOGGER.warn("The exception was: {}", ioexception.toString()); + DedicatedServer.LOGGER.warn("Perhaps a server is already running on that port?"); + return false; + } + } + + // CraftBukkit start + // this.a((PlayerList) (new DedicatedPlayerList(this))); // Spigot - moved up + server.loadPlugins(); + server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.STARTUP); + // CraftBukkit end + + if (!this.getOnlineMode()) { + DedicatedServer.LOGGER.warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!"); + DedicatedServer.LOGGER.warn("The server will make no attempt to authenticate usernames. Beware."); + // Spigot start + if (org.spigotmc.SpigotConfig.bungee) { + DedicatedServer.LOGGER.warn("Whilst this makes it possible to use BungeeCord, unless access to your server is properly restricted, it also opens up the ability for hackers to connect with any username they choose."); + DedicatedServer.LOGGER.warn("Please see http://www.spigotmc.org/wiki/firewall-guide/ for further information."); + } else { + DedicatedServer.LOGGER.warn("While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose."); + } + // Spigot end + DedicatedServer.LOGGER.warn("To change this, set \"online-mode\" to \"true\" in the server.properties file."); + } + + if (this.aX()) { + this.getUserCache().c(); + } + + if (!NameReferencingFileConverter.a(this.propertyManager)) { + return false; + } else { + this.convertable = new WorldLoaderServer(server.getWorldContainer().toPath(), server.getWorldContainer().toPath().resolve("../backups"), this.dataConverterManager); // CraftBukkit - moved from MinecraftServer constructor + long j = SystemUtils.getMonotonicNanos(); + + if (this.getWorld() == null) { + this.setWorld(this.propertyManager.getString("level-name", "world")); + } + + String s = this.propertyManager.getString("level-seed", ""); + String s1 = this.propertyManager.getString("level-type", "DEFAULT"); + String s2 = this.propertyManager.getString("generator-settings", ""); + long k = (new Random()).nextLong(); + + if (!s.isEmpty()) { + try { + long l = Long.parseLong(s); + + if (l != 0L) { + k = l; + } + } catch (NumberFormatException numberformatexception) { + k = (long) s.hashCode(); + } + } + + WorldType worldtype = WorldType.getType(s1); + + if (worldtype == null) { + worldtype = WorldType.NORMAL; + } + + this.getEnableCommandBlock(); + this.j(); + this.getSnooperEnabled(); + this.aw(); + this.b(this.propertyManager.getInt("max-build-height", 256)); + this.b((this.getMaxBuildHeight() + 8) / 16 * 16); + this.b(MathHelper.clamp(this.getMaxBuildHeight(), 64, 256)); + this.propertyManager.setProperty("max-build-height", this.getMaxBuildHeight()); + TileEntitySkull.a(this.getUserCache()); + TileEntitySkull.a(this.ap()); + UserCache.a(this.getOnlineMode()); + DedicatedServer.LOGGER.info("Preparing level \"{}\"", this.getWorld()); + JsonObject jsonobject = new JsonObject(); + + if (worldtype == WorldType.FLAT) { + jsonobject.addProperty("flat_world_options", s2); + } else if (!s2.isEmpty()) { + // CraftBukkit start + try { + jsonobject = ChatDeserializer.a(s2); + } catch (Exception ex) { + DedicatedServer.LOGGER.warn("Invalid generator-settings, ignoring", ex); + } + // CraftBukkit end + } + + this.a(this.getWorld(), this.getWorld(), k, worldtype, jsonobject); + long i1 = SystemUtils.getMonotonicNanos() - j; + String s3 = String.format(Locale.ROOT, "%.3fs", (double) i1 / 1.0E9D); + + DedicatedServer.LOGGER.info("Done ({})! For help, type \"help\"", s3); + if (this.propertyManager.a("announce-player-achievements")) { + this.getGameRules().set("announceAdvancements", this.propertyManager.getBoolean("announce-player-achievements", true) ? "true" : "false", this); + this.propertyManager.b("announce-player-achievements"); + this.propertyManager.savePropertiesFile(); + } + + if (this.propertyManager.getBoolean("enable-query", false)) { + DedicatedServer.LOGGER.info("Starting GS4 status listener"); + this.j = new RemoteStatusListener(this); + this.j.a(); + } + + if (this.propertyManager.getBoolean("enable-rcon", false)) { + DedicatedServer.LOGGER.info("Starting remote control listener"); + this.l = new RemoteControlListener(this); + this.l.a(); + this.remoteConsole = new org.bukkit.craftbukkit.command.CraftRemoteConsoleCommandSender(this.remoteControlCommandListener); // CraftBukkit + } + + // CraftBukkit start + if (this.server.getBukkitSpawnRadius() > -1) { + DedicatedServer.LOGGER.info("'settings.spawn-radius' in bukkit.yml has been moved to 'spawn-protection' in server.properties. I will move your config for you."); + this.propertyManager.properties.remove("spawn-protection"); + this.propertyManager.getInt("spawn-protection", this.server.getBukkitSpawnRadius()); + this.server.removeBukkitSpawnRadius(); + this.propertyManager.savePropertiesFile(); + } + // CraftBukkit end + + if (org.spigotmc.SpigotConfig.lateBind) { + try { + this.getServerConnection().a(inetaddress, this.getPort()); + } catch (IOException ioexception) { + DedicatedServer.LOGGER.warn("**** FAILED TO BIND TO PORT!"); + DedicatedServer.LOGGER.warn("The exception was: {}", ioexception.toString()); + DedicatedServer.LOGGER.warn("Perhaps a server is already running on that port?"); + return false; + } + } + + if (false && this.getMaxTickTime() > 0L) { // Spigot - disable + Thread thread1 = new Thread(new ThreadWatchdog(this)); + + thread1.setUncaughtExceptionHandler(new ThreadNamedUncaughtExceptionHandler(DedicatedServer.LOGGER)); + thread1.setName("Server Watchdog"); + thread1.setDaemon(true); + thread1.start(); + } + + Items.AIR.a(CreativeModeTab.g, NonNullList.a()); + return true; + } + } + } + + public String aT() { + if (this.propertyManager.a("resource-pack-hash")) { + if (this.propertyManager.a("resource-pack-sha1")) { + DedicatedServer.LOGGER.warn("resource-pack-hash is deprecated and found along side resource-pack-sha1. resource-pack-hash will be ignored."); + } else { + DedicatedServer.LOGGER.warn("resource-pack-hash is deprecated. Please use resource-pack-sha1 instead."); + this.propertyManager.getString("resource-pack-sha1", this.propertyManager.getString("resource-pack-hash", "")); + this.propertyManager.b("resource-pack-hash"); + } + } + + String s = this.propertyManager.getString("resource-pack-sha1", ""); + + if (!s.isEmpty() && !DedicatedServer.h.matcher(s).matches()) { + DedicatedServer.LOGGER.warn("Invalid sha1 for ressource-pack-sha1"); + } + + if (!this.propertyManager.getString("resource-pack", "").isEmpty() && s.isEmpty()) { + DedicatedServer.LOGGER.warn("You specified a resource pack without providing a sha1 hash. Pack will be updated on the client only if you change the name of the pack."); + } + + return s; + } + + public void setGamemode(EnumGamemode enumgamemode) { + super.setGamemode(enumgamemode); + this.p = enumgamemode; + } + + public boolean getGenerateStructures() { + return this.generateStructures; + } + + public EnumGamemode getGamemode() { + return this.p; + } + + public EnumDifficulty getDifficulty() { + return EnumDifficulty.getById(this.propertyManager.getInt("difficulty", EnumDifficulty.NORMAL.a())); + } + + public boolean isHardcore() { + return this.propertyManager.getBoolean("hardcore", false); + } + + public CrashReport b(CrashReport crashreport) { + crashreport = super.b(crashreport); + crashreport.g().a("Is Modded", () -> { + String s = this.getServerModName(); + + return !"vanilla".equals(s) ? "Definitely; Server brand changed to '" + s + "'" : "Unknown (can't tell)"; + }); + crashreport.g().a("Type", () -> { + return "Dedicated Server (map_server.txt)"; + }); + return crashreport; + } + + public void t() { // CraftBukkit - decompile error + System.exit(0); + } + + public void b(BooleanSupplier booleansupplier) { // CraftBukkit - fix decompile error + super.b(booleansupplier); + this.handleCommandQueue(); + } + + public boolean getAllowNether() { + return this.propertyManager.getBoolean("allow-nether", true); + } + + public boolean getSpawnMonsters() { + return this.propertyManager.getBoolean("spawn-monsters", true); + } + + public void a(MojangStatisticsGenerator mojangstatisticsgenerator) { + mojangstatisticsgenerator.a("whitelist_enabled", this.getPlayerList().getHasWhitelist()); + mojangstatisticsgenerator.a("whitelist_count", this.getPlayerList().getWhitelisted().length); + super.a(mojangstatisticsgenerator); + } + + public boolean getSnooperEnabled() { + if (this.propertyManager.getBoolean("snooper-enabled", true)) { + ; + } + + return false; + } + + public void issueCommand(String s, CommandListenerWrapper commandlistenerwrapper) { + this.serverCommandQueue.add(new ServerCommand(s, commandlistenerwrapper)); + } + + public void handleCommandQueue() { + MinecraftTimings.serverCommandTimer.startTiming(); // Spigot + // Paper start - use proper queue + ServerCommand servercommand; + while ((servercommand = this.serverCommandQueue.poll()) != null) { + // Paper end + + // CraftBukkit start - ServerCommand for preprocessing + ServerCommandEvent event = new ServerCommandEvent(console, servercommand.command); + server.getPluginManager().callEvent(event); + if (event.isCancelled()) continue; + servercommand = new ServerCommand(event.getCommand(), servercommand.source); + + // this.getCommandDispatcher().a(servercommand.source, servercommand.command); // Called in dispatchServerCommand + server.dispatchServerCommand(console, servercommand); + // CraftBukkit end + } + + MinecraftTimings.serverCommandTimer.stopTiming(); // Spigot + } + + public boolean Q() { + return true; + } + + public boolean V() { + return this.propertyManager.getBoolean("use-native-transport", true); + } + + public DedicatedPlayerList getPlayerList() { + return (DedicatedPlayerList) super.getPlayerList(); + } + + public boolean ad() { + return true; + } + + public int a(String s, int i) { + return this.propertyManager.getInt(s, i); + } + + public String a(String s, String s1) { + return this.propertyManager.getString(s, s1); + } + + public boolean a(String s, boolean flag) { + return this.propertyManager.getBoolean(s, flag); + } + + public void a(String s, Object object) { + this.propertyManager.setProperty(s, object); + } + + public void c_() { + this.propertyManager.savePropertiesFile(); + } + + public String d_() { + File file = this.propertyManager.c(); + + return file != null ? file.getAbsolutePath() : "No settings file"; + } + + public String e() { + return this.getServerIp(); + } + + public int e_() { + return this.getPort(); + } + + public String m() { + return this.getMotd(); + } + + public void aW() { + ServerGUI.a(this); + this.q = true; + } + + public boolean ag() { + return this.q; + } + + public boolean a(EnumGamemode enumgamemode, boolean flag, int i) { + return false; + } + + public boolean getEnableCommandBlock() { + return this.propertyManager.getBoolean("enable-command-block", false); + } + + public int getSpawnProtection() { + return this.propertyManager.getInt("spawn-protection", super.getSpawnProtection()); + } + + public boolean a(World world, BlockPosition blockposition, EntityHuman entityhuman) { + if (world.worldProvider.getDimensionManager() != DimensionManager.OVERWORLD) { + return false; + } else if (this.getPlayerList().getOPs().isEmpty()) { + return false; + } else if (this.getPlayerList().isOp(entityhuman.getProfile())) { + return false; + } else if (this.getSpawnProtection() <= 0) { + return false; + } else { + BlockPosition blockposition1 = world.getSpawn(); + int i = MathHelper.a(blockposition.getX() - blockposition1.getX()); + int j = MathHelper.a(blockposition.getZ() - blockposition1.getZ()); + int k = Math.max(i, j); + + return k <= this.getSpawnProtection(); + } + } + + public int j() { + return this.propertyManager.getInt("op-permission-level", 4); + } + + public void setIdleTimeout(int i) { + super.setIdleTimeout(i); + this.propertyManager.setProperty("player-idle-timeout", i); + this.c_(); + } + + public boolean k() { + return this.propertyManager.getBoolean("broadcast-rcon-to-ops", true); + } + + public boolean B_() { + return this.propertyManager.getBoolean("broadcast-console-to-ops", true); + } + + public int au() { + int i = this.propertyManager.getInt("max-world-size", super.au()); + + if (i < 1) { + i = 1; + } else if (i > super.au()) { + i = super.au(); + } + + return i; + } + + public int aw() { + return this.propertyManager.getInt("network-compression-threshold", super.aw()); + } + + protected boolean aX() { + boolean flag = false; + + int i; + + for (i = 0; !flag && i <= 2; ++i) { + if (i > 0) { + DedicatedServer.LOGGER.warn("Encountered a problem while converting the user banlist, retrying in a few seconds"); + this.ba(); + } + + flag = NameReferencingFileConverter.a((MinecraftServer) this); + } + + boolean flag1 = false; + + for (i = 0; !flag1 && i <= 2; ++i) { + if (i > 0) { + DedicatedServer.LOGGER.warn("Encountered a problem while converting the ip banlist, retrying in a few seconds"); + this.ba(); + } + + flag1 = NameReferencingFileConverter.b((MinecraftServer) this); + } + + boolean flag2 = false; + + for (i = 0; !flag2 && i <= 2; ++i) { + if (i > 0) { + DedicatedServer.LOGGER.warn("Encountered a problem while converting the op list, retrying in a few seconds"); + this.ba(); + } + + flag2 = NameReferencingFileConverter.c((MinecraftServer) this); + } + + boolean flag3 = false; + + for (i = 0; !flag3 && i <= 2; ++i) { + if (i > 0) { + DedicatedServer.LOGGER.warn("Encountered a problem while converting the whitelist, retrying in a few seconds"); + this.ba(); + } + + flag3 = NameReferencingFileConverter.d((MinecraftServer) this); + } + + boolean flag4 = false; + + for (i = 0; !flag4 && i <= 2; ++i) { + if (i > 0) { + DedicatedServer.LOGGER.warn("Encountered a problem while converting the player save files, retrying in a few seconds"); + this.ba(); + } + + flag4 = NameReferencingFileConverter.a(this, this.propertyManager); + } + + return flag || flag1 || flag2 || flag3 || flag4; + } + + private void ba() { + try { + Thread.sleep(5000L); + } catch (InterruptedException interruptedexception) { + ; + } + } + + public long getMaxTickTime() { + return this.propertyManager.getLong("max-tick-time", TimeUnit.MINUTES.toMillis(1L)); + } + + public String getPlugins() { + // CraftBukkit start - Whole method + StringBuilder result = new StringBuilder(); + org.bukkit.plugin.Plugin[] plugins = server.getPluginManager().getPlugins(); + + result.append(server.getName()); + result.append(" on Bukkit "); + result.append(server.getBukkitVersion()); + + if (plugins.length > 0 && server.getQueryPlugins()) { + result.append(": "); + + for (int i = 0; i < plugins.length; i++) { + if (i > 0) { + result.append("; "); + } + + result.append(plugins[i].getDescription().getName()); + result.append(" "); + result.append(plugins[i].getDescription().getVersion().replaceAll(";", ",")); + } + } + + return result.toString(); + // CraftBukkit end + } + + // CraftBukkit start - fire RemoteServerCommandEvent + public String executeRemoteCommand(final String s) { + Waitable waitable = new Waitable() { + @Override + protected String evaluate() { + remoteControlCommandListener.clearMessages(); + // Event changes start + RemoteServerCommandEvent event = new RemoteServerCommandEvent(remoteConsole, s); + server.getPluginManager().callEvent(event); + if (event.isCancelled()) { + return ""; + } + // Event change end + ServerCommand serverCommand = new ServerCommand(event.getCommand(), remoteControlCommandListener.f()); + server.dispatchServerCommand(remoteConsole, serverCommand); + return remoteControlCommandListener.getMessages(); + } + }; + // Paper start + if (s.toLowerCase().startsWith("timings") && s.toLowerCase().matches("timings (report|paste|get|merged|seperate)")) { + org.bukkit.command.BufferedCommandSender sender = new org.bukkit.command.BufferedCommandSender(); + waitable = new Waitable() { + @Override + protected String evaluate() { + return sender.getBuffer(); + } + }; + co.aikar.timings.Timings.generateReport(new co.aikar.timings.TimingsReportListener(sender, waitable)); + } else { + processQueue.add(waitable); + } + // Paper end + try { + return waitable.get(); + } catch (java.util.concurrent.ExecutionException e) { + throw new RuntimeException("Exception processing rcon command " + s, e.getCause()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // Maintain interrupted state + throw new RuntimeException("Interrupted processing rcon command " + s, e); + } + // CraftBukkit end + } + + // CraftBukkit start + @Override + public PropertyManager getPropertyManager() { + return this.propertyManager; + } + + @Override + public CommandSender getBukkitSender(CommandListenerWrapper wrapper) { + return console; + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/DefinedStructure.java b/src/main/java/net/minecraft/server/DefinedStructure.java new file mode 100644 index 000000000000..dd635292eca4 --- /dev/null +++ b/src/main/java/net/minecraft/server/DefinedStructure.java @@ -0,0 +1,733 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mojang.datafixers.util.Pair; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import javax.annotation.Nullable; + +public class DefinedStructure { + + private final List> a = Lists.newArrayList(); + private final List b = Lists.newArrayList(); + private BlockPosition c; + private String d; + + public DefinedStructure() { + this.c = BlockPosition.ZERO; + this.d = "?"; + } + + public BlockPosition a() { + return this.c; + } + + public void a(String s) { + this.d = s; + } + + public String b() { + return this.d; + } + + public void a(World world, BlockPosition blockposition, BlockPosition blockposition1, boolean flag, @Nullable Block block) { + if (blockposition1.getX() >= 1 && blockposition1.getY() >= 1 && blockposition1.getZ() >= 1) { + BlockPosition blockposition2 = blockposition.a((BaseBlockPosition) blockposition1).a(-1, -1, -1); + List list = Lists.newArrayList(); + List list1 = Lists.newArrayList(); + List list2 = Lists.newArrayList(); + BlockPosition blockposition3 = new BlockPosition(Math.min(blockposition.getX(), blockposition2.getX()), Math.min(blockposition.getY(), blockposition2.getY()), Math.min(blockposition.getZ(), blockposition2.getZ())); + BlockPosition blockposition4 = new BlockPosition(Math.max(blockposition.getX(), blockposition2.getX()), Math.max(blockposition.getY(), blockposition2.getY()), Math.max(blockposition.getZ(), blockposition2.getZ())); + + this.c = blockposition1; + Iterator iterator = BlockPosition.b(blockposition3, blockposition4).iterator(); + + while (iterator.hasNext()) { + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = (BlockPosition.MutableBlockPosition) iterator.next(); + BlockPosition blockposition5 = blockposition_mutableblockposition.b(blockposition3); + IBlockData iblockdata = world.getType(blockposition_mutableblockposition); + + if (block == null || block != iblockdata.getBlock()) { + TileEntity tileentity = world.getTileEntity(blockposition_mutableblockposition); + + if (tileentity != null) { + NBTTagCompound nbttagcompound = tileentity.save(new NBTTagCompound()); + + nbttagcompound.remove("x"); + nbttagcompound.remove("y"); + nbttagcompound.remove("z"); + list1.add(new DefinedStructure.BlockInfo(blockposition5, iblockdata, nbttagcompound)); + } else if (!iblockdata.f(world, blockposition_mutableblockposition) && !iblockdata.g()) { + list2.add(new DefinedStructure.BlockInfo(blockposition5, iblockdata, (NBTTagCompound) null)); + } else { + list.add(new DefinedStructure.BlockInfo(blockposition5, iblockdata, (NBTTagCompound) null)); + } + } + } + + List list3 = Lists.newArrayList(); + + list3.addAll(list); + list3.addAll(list1); + list3.addAll(list2); + this.a.clear(); + this.a.add(list3); + if (flag) { + this.a(world, blockposition3, blockposition4.a(1, 1, 1)); + } else { + this.b.clear(); + } + + } + } + + private void a(World world, BlockPosition blockposition, BlockPosition blockposition1) { + List list = world.a(Entity.class, new AxisAlignedBB(blockposition, blockposition1), (java.util.function.Predicate) (entity) -> { // Paper - decompile fix + return !(entity instanceof EntityHuman); + }); + + this.b.clear(); + + Vec3D vec3d; + NBTTagCompound nbttagcompound; + BlockPosition blockposition2; + + for (Iterator iterator = list.iterator(); iterator.hasNext(); this.b.add(new DefinedStructure.EntityInfo(vec3d, blockposition2, nbttagcompound))) { + Entity entity = (Entity) iterator.next(); + + vec3d = new Vec3D(entity.locX - (double) blockposition.getX(), entity.locY - (double) blockposition.getY(), entity.locZ - (double) blockposition.getZ()); + nbttagcompound = new NBTTagCompound(); + entity.d(nbttagcompound); + if (entity instanceof EntityPainting) { + blockposition2 = ((EntityPainting) entity).getBlockPosition().b(blockposition); + } else { + blockposition2 = new BlockPosition(vec3d); + } + } + + } + + public Map a(BlockPosition blockposition, DefinedStructureInfo definedstructureinfo) { + Map map = Maps.newHashMap(); + StructureBoundingBox structureboundingbox = definedstructureinfo.j(); + Iterator iterator = definedstructureinfo.a(this.a, blockposition).iterator(); + + while (iterator.hasNext()) { + DefinedStructure.BlockInfo definedstructure_blockinfo = (DefinedStructure.BlockInfo) iterator.next(); + BlockPosition blockposition1 = a(definedstructureinfo, definedstructure_blockinfo.a).a((BaseBlockPosition) blockposition); + + if (structureboundingbox == null || structureboundingbox.b((BaseBlockPosition) blockposition1)) { + IBlockData iblockdata = definedstructure_blockinfo.b; + + if (iblockdata.getBlock() == Blocks.STRUCTURE_BLOCK && definedstructure_blockinfo.c != null) { + BlockPropertyStructureMode blockpropertystructuremode = BlockPropertyStructureMode.valueOf(definedstructure_blockinfo.c.getString("mode")); + + if (blockpropertystructuremode == BlockPropertyStructureMode.DATA) { + map.put(blockposition1, definedstructure_blockinfo.c.getString("metadata")); + } + } + } + } + + return map; + } + + public BlockPosition a(DefinedStructureInfo definedstructureinfo, BlockPosition blockposition, DefinedStructureInfo definedstructureinfo1, BlockPosition blockposition1) { + BlockPosition blockposition2 = a(definedstructureinfo, blockposition); + BlockPosition blockposition3 = a(definedstructureinfo1, blockposition1); + + return blockposition2.b(blockposition3); + } + + public static BlockPosition a(DefinedStructureInfo definedstructureinfo, BlockPosition blockposition) { + return a(blockposition, definedstructureinfo.b(), definedstructureinfo.c(), definedstructureinfo.d()); + } + + public void a(GeneratorAccess generatoraccess, BlockPosition blockposition, DefinedStructureInfo definedstructureinfo) { + definedstructureinfo.l(); + this.b(generatoraccess, blockposition, definedstructureinfo); + } + + public void b(GeneratorAccess generatoraccess, BlockPosition blockposition, DefinedStructureInfo definedstructureinfo) { + this.a(generatoraccess, blockposition, new DefinedStructureProcessorRotation(blockposition, definedstructureinfo), definedstructureinfo, 2); + } + + public boolean a(GeneratorAccess generatoraccess, BlockPosition blockposition, DefinedStructureInfo definedstructureinfo, int i) { + return this.a(generatoraccess, blockposition, new DefinedStructureProcessorRotation(blockposition, definedstructureinfo), definedstructureinfo, i); + } + + public boolean a(GeneratorAccess generatoraccess, BlockPosition blockposition, @Nullable DefinedStructureProcessor definedstructureprocessor, DefinedStructureInfo definedstructureinfo, int i) { + if (this.a.isEmpty()) { + return false; + } else { + List list = definedstructureinfo.a(this.a, blockposition); + + if ((!list.isEmpty() || !definedstructureinfo.h() && !this.b.isEmpty()) && this.c.getX() >= 1 && this.c.getY() >= 1 && this.c.getZ() >= 1) { + Block block = definedstructureinfo.i(); + StructureBoundingBox structureboundingbox = definedstructureinfo.j(); + List list1 = Lists.newArrayListWithCapacity(definedstructureinfo.m() ? list.size() : 0); + List> list2 = Lists.newArrayListWithCapacity(list.size()); + int j = Integer.MAX_VALUE; + int k = Integer.MAX_VALUE; + int l = Integer.MAX_VALUE; + int i1 = Integer.MIN_VALUE; + int j1 = Integer.MIN_VALUE; + int k1 = Integer.MIN_VALUE; + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + DefinedStructure.BlockInfo definedstructure_blockinfo = (DefinedStructure.BlockInfo) iterator.next(); + BlockPosition blockposition1 = a(definedstructureinfo, definedstructure_blockinfo.a).a((BaseBlockPosition) blockposition); + DefinedStructure.BlockInfo definedstructure_blockinfo1 = definedstructureprocessor != null ? definedstructureprocessor.a(generatoraccess, blockposition1, definedstructure_blockinfo) : definedstructure_blockinfo; + + if (definedstructure_blockinfo1 != null) { + Block block1 = definedstructure_blockinfo1.b.getBlock(); + + if ((block == null || block != block1) && (!definedstructureinfo.k() || block1 != Blocks.STRUCTURE_BLOCK) && (structureboundingbox == null || structureboundingbox.b((BaseBlockPosition) blockposition1))) { + Fluid fluid = definedstructureinfo.m() ? generatoraccess.getFluid(blockposition1) : null; + IBlockData iblockdata = definedstructure_blockinfo1.b.a(definedstructureinfo.b()); + IBlockData iblockdata1 = iblockdata.a(definedstructureinfo.c()); + TileEntity tileentity; + + if (definedstructure_blockinfo1.c != null) { + tileentity = generatoraccess.getTileEntity(blockposition1); + if (tileentity instanceof IInventory) { + ((IInventory) tileentity).clear(); + } + + generatoraccess.setTypeAndData(blockposition1, Blocks.BARRIER.getBlockData(), 4); + } + + if (generatoraccess.setTypeAndData(blockposition1, iblockdata1, i)) { + j = Math.min(j, blockposition1.getX()); + k = Math.min(k, blockposition1.getY()); + l = Math.min(l, blockposition1.getZ()); + i1 = Math.max(i1, blockposition1.getX()); + j1 = Math.max(j1, blockposition1.getY()); + k1 = Math.max(k1, blockposition1.getZ()); + list2.add(Pair.of(blockposition1, definedstructure_blockinfo.c)); + if (definedstructure_blockinfo1.c != null) { + tileentity = generatoraccess.getTileEntity(blockposition1); + if (tileentity != null) { + definedstructure_blockinfo1.c.setInt("x", blockposition1.getX()); + definedstructure_blockinfo1.c.setInt("y", blockposition1.getY()); + definedstructure_blockinfo1.c.setInt("z", blockposition1.getZ()); + tileentity.isLoadingStructure = true; // Paper + tileentity.load(definedstructure_blockinfo1.c); + tileentity.a(definedstructureinfo.b()); + tileentity.a(definedstructureinfo.c()); + tileentity.isLoadingStructure = false; // Paper + } + } + + if (fluid != null && iblockdata1.getBlock() instanceof IFluidContainer) { + ((IFluidContainer) iblockdata1.getBlock()).place(generatoraccess, blockposition1, iblockdata1, fluid); + if (!fluid.d()) { + list1.add(blockposition1); + } + } + } + } + } + } + + boolean flag = true; + EnumDirection[] aenumdirection = new EnumDirection[] { EnumDirection.UP, EnumDirection.NORTH, EnumDirection.EAST, EnumDirection.SOUTH, EnumDirection.WEST}; + + //int l1; // Paper - decompile fix + + while (flag && !list1.isEmpty()) { + flag = false; + Iterator iterator1 = list1.iterator(); + + while (iterator1.hasNext()) { + BlockPosition blockposition2 = (BlockPosition) iterator1.next(); + Fluid fluid1 = generatoraccess.getFluid(blockposition2); + + for (int l1 = 0; l1 < aenumdirection.length && !fluid1.d(); ++l1) { // Paper - decompile fix + Fluid fluid2 = generatoraccess.getFluid(blockposition2.shift(aenumdirection[l1])); + + if (fluid2.getHeight() > fluid1.getHeight() || fluid2.d() && !fluid1.d()) { + fluid1 = fluid2; + } + } + + if (fluid1.d()) { + IBlockData iblockdata2 = generatoraccess.getType(blockposition2); + + if (iblockdata2.getBlock() instanceof IFluidContainer) { + ((IFluidContainer) iblockdata2.getBlock()).place(generatoraccess, blockposition2, iblockdata2, fluid1); + flag = true; + iterator1.remove(); + } + } + } + } + + if (j <= i1) { + VoxelShapeBitSet voxelshapebitset = new VoxelShapeBitSet(i1 - j + 1, j1 - k + 1, k1 - l + 1); + int i2 = j; + int j2 = k; + + int l1 = l; // Paper - decompile fix + Iterator iterator2 = list2.iterator(); + + Pair pair; + BlockPosition blockposition3; + + while (iterator2.hasNext()) { + pair = (Pair) iterator2.next(); + blockposition3 = (BlockPosition) pair.getFirst(); + voxelshapebitset.a(blockposition3.getX() - i2, blockposition3.getY() - j2, blockposition3.getZ() - l1, true, true); + } + + voxelshapebitset.a((enumdirection, k2, l2, i3) -> { + BlockPosition blockposition4 = new BlockPosition(i2 + k2, j2 + l2, l1 + i3); + BlockPosition blockposition5 = blockposition4.shift(enumdirection); + IBlockData iblockdata3 = generatoraccess.getType(blockposition4); + IBlockData iblockdata4 = generatoraccess.getType(blockposition5); + IBlockData iblockdata5 = iblockdata3.updateState(enumdirection, iblockdata4, generatoraccess, blockposition4, blockposition5); + + if (iblockdata3 != iblockdata5) { + generatoraccess.setTypeAndData(blockposition4, iblockdata5, i & -2 | 16); + } + + IBlockData iblockdata6 = iblockdata4.updateState(enumdirection.opposite(), iblockdata5, generatoraccess, blockposition5, blockposition4); + + if (iblockdata4 != iblockdata6) { + generatoraccess.setTypeAndData(blockposition5, iblockdata6, i & -2 | 16); + } + + }); + iterator2 = list2.iterator(); + + while (iterator2.hasNext()) { + pair = (Pair) iterator2.next(); + blockposition3 = (BlockPosition) pair.getFirst(); + IBlockData iblockdata3 = generatoraccess.getType(blockposition3); + IBlockData iblockdata4 = Block.b(iblockdata3, generatoraccess, blockposition3); + + if (iblockdata3 != iblockdata4) { + generatoraccess.setTypeAndData(blockposition3, iblockdata4, i & -2 | 16); + } + + generatoraccess.update(blockposition3, iblockdata4.getBlock()); + if (pair.getSecond() != null) { + TileEntity tileentity1 = generatoraccess.getTileEntity(blockposition3); + + if (tileentity1 != null) { + tileentity1.update(); + } + } + } + } + + if (!definedstructureinfo.h()) { + this.a(generatoraccess, blockposition, definedstructureinfo.b(), definedstructureinfo.c(), definedstructureinfo.d(), structureboundingbox); + } + + return true; + } else { + return false; + } + } + } + + private void a(GeneratorAccess generatoraccess, BlockPosition blockposition, EnumBlockMirror enumblockmirror, EnumBlockRotation enumblockrotation, BlockPosition blockposition1, @Nullable StructureBoundingBox structureboundingbox) { + Iterator iterator = this.b.iterator(); + + while (iterator.hasNext()) { + DefinedStructure.EntityInfo definedstructure_entityinfo = (DefinedStructure.EntityInfo) iterator.next(); + BlockPosition blockposition2 = a(definedstructure_entityinfo.b, enumblockmirror, enumblockrotation, blockposition1).a((BaseBlockPosition) blockposition); + + if (structureboundingbox == null || structureboundingbox.b((BaseBlockPosition) blockposition2)) { + NBTTagCompound nbttagcompound = definedstructure_entityinfo.c; + Vec3D vec3d = a(definedstructure_entityinfo.a, enumblockmirror, enumblockrotation, blockposition1); + Vec3D vec3d1 = vec3d.add((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ()); + NBTTagList nbttaglist = new NBTTagList(); + + nbttaglist.add((NBTBase) (new NBTTagDouble(vec3d1.x))); + nbttaglist.add((NBTBase) (new NBTTagDouble(vec3d1.y))); + nbttaglist.add((NBTBase) (new NBTTagDouble(vec3d1.z))); + nbttagcompound.set("Pos", nbttaglist); + nbttagcompound.a("UUID", UUID.randomUUID()); + + Entity entity; + + try { + entity = EntityTypes.a(nbttagcompound, generatoraccess.getMinecraftWorld()); + } catch (Exception exception) { + entity = null; + } + + if (entity != null) { + float f = entity.a(enumblockmirror); + + f += entity.yaw - entity.a(enumblockrotation); + entity.setPositionRotation(vec3d1.x, vec3d1.y, vec3d1.z, f, entity.pitch); + generatoraccess.addEntity(entity); + } + } + } + + } + + public BlockPosition a(EnumBlockRotation enumblockrotation) { + switch (enumblockrotation) { + case COUNTERCLOCKWISE_90: + case CLOCKWISE_90: + return new BlockPosition(this.c.getZ(), this.c.getY(), this.c.getX()); + default: + return this.c; + } + } + + public static BlockPosition a(BlockPosition blockposition, EnumBlockMirror enumblockmirror, EnumBlockRotation enumblockrotation, BlockPosition blockposition1) { + int i = blockposition.getX(); + int j = blockposition.getY(); + int k = blockposition.getZ(); + boolean flag = true; + + switch (enumblockmirror) { + case LEFT_RIGHT: + k = -k; + break; + case FRONT_BACK: + i = -i; + break; + default: + flag = false; + } + + int l = blockposition1.getX(); + int i1 = blockposition1.getZ(); + + switch (enumblockrotation) { + case COUNTERCLOCKWISE_90: + return new BlockPosition(l - i1 + k, j, l + i1 - i); + case CLOCKWISE_90: + return new BlockPosition(l + i1 - k, j, i1 - l + i); + case CLOCKWISE_180: + return new BlockPosition(l + l - i, j, i1 + i1 - k); + default: + return flag ? new BlockPosition(i, j, k) : blockposition; + } + } + + private static Vec3D a(Vec3D vec3d, EnumBlockMirror enumblockmirror, EnumBlockRotation enumblockrotation, BlockPosition blockposition) { + double d0 = vec3d.x; + double d1 = vec3d.y; + double d2 = vec3d.z; + boolean flag = true; + + switch (enumblockmirror) { + case LEFT_RIGHT: + d2 = 1.0D - d2; + break; + case FRONT_BACK: + d0 = 1.0D - d0; + break; + default: + flag = false; + } + + int i = blockposition.getX(); + int j = blockposition.getZ(); + + switch (enumblockrotation) { + case COUNTERCLOCKWISE_90: + return new Vec3D((double) (i - j) + d2, d1, (double) (i + j + 1) - d0); + case CLOCKWISE_90: + return new Vec3D((double) (i + j + 1) - d2, d1, (double) (j - i) + d0); + case CLOCKWISE_180: + return new Vec3D((double) (i + i + 1) - d0, d1, (double) (j + j + 1) - d2); + default: + return flag ? new Vec3D(d0, d1, d2) : vec3d; + } + } + + public BlockPosition a(BlockPosition blockposition, EnumBlockMirror enumblockmirror, EnumBlockRotation enumblockrotation) { + return a(blockposition, enumblockmirror, enumblockrotation, this.a().getX(), this.a().getZ()); + } + + public static BlockPosition a(BlockPosition blockposition, EnumBlockMirror enumblockmirror, EnumBlockRotation enumblockrotation, int i, int j) { + --i; + --j; + int k = enumblockmirror == EnumBlockMirror.FRONT_BACK ? i : 0; + int l = enumblockmirror == EnumBlockMirror.LEFT_RIGHT ? j : 0; + BlockPosition blockposition1 = blockposition; + + switch (enumblockrotation) { + case COUNTERCLOCKWISE_90: + blockposition1 = blockposition.a(l, 0, i - k); + break; + case CLOCKWISE_90: + blockposition1 = blockposition.a(j - l, 0, k); + break; + case CLOCKWISE_180: + blockposition1 = blockposition.a(i - k, 0, j - l); + break; + case NONE: + blockposition1 = blockposition.a(k, 0, l); + } + + return blockposition1; + } + + public NBTTagCompound a(NBTTagCompound nbttagcompound) { + if (this.a.isEmpty()) { + nbttagcompound.set("blocks", new NBTTagList()); + nbttagcompound.set("palette", new NBTTagList()); + } else { + List list = Lists.newArrayList(); + DefinedStructure.a definedstructure_a = new DefinedStructure.a(); + + list.add(definedstructure_a); + + for (int i = 1; i < this.a.size(); ++i) { + list.add(new DefinedStructure.a()); + } + + NBTTagList nbttaglist = new NBTTagList(); + List list1 = (List) this.a.get(0); + + for (int j = 0; j < list1.size(); ++j) { + DefinedStructure.BlockInfo definedstructure_blockinfo = (DefinedStructure.BlockInfo) list1.get(j); + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.set("pos", this.a(definedstructure_blockinfo.a.getX(), definedstructure_blockinfo.a.getY(), definedstructure_blockinfo.a.getZ())); + int k = definedstructure_a.a(definedstructure_blockinfo.b); + + nbttagcompound1.setInt("state", k); + if (definedstructure_blockinfo.c != null) { + nbttagcompound1.set("nbt", definedstructure_blockinfo.c); + } + + nbttaglist.add((NBTBase) nbttagcompound1); + + for (int l = 1; l < this.a.size(); ++l) { + DefinedStructure.a definedstructure_a1 = (DefinedStructure.a) list.get(l); + + definedstructure_a1.a(((DefinedStructure.BlockInfo) ((List) this.a.get(j)).get(j)).b, k); + } + } + + nbttagcompound.set("blocks", nbttaglist); + NBTTagList nbttaglist1; + Iterator iterator; + + if (list.size() == 1) { + nbttaglist1 = new NBTTagList(); + iterator = definedstructure_a.iterator(); + + while (iterator.hasNext()) { + IBlockData iblockdata = (IBlockData) iterator.next(); + + nbttaglist1.add((NBTBase) GameProfileSerializer.a(iblockdata)); + } + + nbttagcompound.set("palette", nbttaglist1); + } else { + nbttaglist1 = new NBTTagList(); + iterator = list.iterator(); + + while (iterator.hasNext()) { + DefinedStructure.a definedstructure_a2 = (DefinedStructure.a) iterator.next(); + NBTTagList nbttaglist2 = new NBTTagList(); + Iterator iterator1 = definedstructure_a2.iterator(); + + while (iterator1.hasNext()) { + IBlockData iblockdata1 = (IBlockData) iterator1.next(); + + nbttaglist2.add((NBTBase) GameProfileSerializer.a(iblockdata1)); + } + + nbttaglist1.add((NBTBase) nbttaglist2); + } + + nbttagcompound.set("palettes", nbttaglist1); + } + } + + NBTTagList nbttaglist3 = new NBTTagList(); + + NBTTagCompound nbttagcompound2; + + for (Iterator iterator2 = this.b.iterator(); iterator2.hasNext(); nbttaglist3.add((NBTBase) nbttagcompound2)) { + DefinedStructure.EntityInfo definedstructure_entityinfo = (DefinedStructure.EntityInfo) iterator2.next(); + + nbttagcompound2 = new NBTTagCompound(); + nbttagcompound2.set("pos", this.a(definedstructure_entityinfo.a.x, definedstructure_entityinfo.a.y, definedstructure_entityinfo.a.z)); + nbttagcompound2.set("blockPos", this.a(definedstructure_entityinfo.b.getX(), definedstructure_entityinfo.b.getY(), definedstructure_entityinfo.b.getZ())); + if (definedstructure_entityinfo.c != null) { + nbttagcompound2.set("nbt", definedstructure_entityinfo.c); + } + } + + nbttagcompound.set("entities", nbttaglist3); + nbttagcompound.set("size", this.a(this.c.getX(), this.c.getY(), this.c.getZ())); + nbttagcompound.setInt("DataVersion", 1631); + return nbttagcompound; + } + + public void b(NBTTagCompound nbttagcompound) { + this.a.clear(); + this.b.clear(); + NBTTagList nbttaglist = nbttagcompound.getList("size", 3); + + this.c = new BlockPosition(nbttaglist.h(0), nbttaglist.h(1), nbttaglist.h(2)); + NBTTagList nbttaglist1 = nbttagcompound.getList("blocks", 10); + NBTTagList nbttaglist2; + int i; + + if (nbttagcompound.hasKeyOfType("palettes", 9)) { + nbttaglist2 = nbttagcompound.getList("palettes", 9); + + for (i = 0; i < nbttaglist2.size(); ++i) { + this.a(nbttaglist2.f(i), nbttaglist1); + } + } else { + this.a(nbttagcompound.getList("palette", 10), nbttaglist1); + } + + nbttaglist2 = nbttagcompound.getList("entities", 10); + + for (i = 0; i < nbttaglist2.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist2.getCompound(i); + NBTTagList nbttaglist3 = nbttagcompound1.getList("pos", 6); + Vec3D vec3d = new Vec3D(nbttaglist3.k(0), nbttaglist3.k(1), nbttaglist3.k(2)); + NBTTagList nbttaglist4 = nbttagcompound1.getList("blockPos", 3); + BlockPosition blockposition = new BlockPosition(nbttaglist4.h(0), nbttaglist4.h(1), nbttaglist4.h(2)); + + if (nbttagcompound1.hasKey("nbt")) { + NBTTagCompound nbttagcompound2 = nbttagcompound1.getCompound("nbt"); + + this.b.add(new DefinedStructure.EntityInfo(vec3d, blockposition, nbttagcompound2)); + } + } + + } + + private void a(NBTTagList nbttaglist, NBTTagList nbttaglist1) { + DefinedStructure.a definedstructure_a = new DefinedStructure.a(); + List list = Lists.newArrayList(); + + int i; + + for (i = 0; i < nbttaglist.size(); ++i) { + definedstructure_a.a(GameProfileSerializer.d(nbttaglist.getCompound(i)), i); + } + + for (i = 0; i < nbttaglist1.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist1.getCompound(i); + NBTTagList nbttaglist2 = nbttagcompound.getList("pos", 3); + BlockPosition blockposition = new BlockPosition(nbttaglist2.h(0), nbttaglist2.h(1), nbttaglist2.h(2)); + IBlockData iblockdata = definedstructure_a.a(nbttagcompound.getInt("state")); + NBTTagCompound nbttagcompound1; + + if (nbttagcompound.hasKey("nbt")) { + nbttagcompound1 = nbttagcompound.getCompound("nbt"); + } else { + nbttagcompound1 = null; + } + + list.add(new DefinedStructure.BlockInfo(blockposition, iblockdata, nbttagcompound1)); + } + + this.a.add(list); + } + + private NBTTagList a(int... aint) { + NBTTagList nbttaglist = new NBTTagList(); + int[] aint1 = aint; + int i = aint.length; + + for (int j = 0; j < i; ++j) { + int k = aint1[j]; + + nbttaglist.add((NBTBase) (new NBTTagInt(k))); + } + + return nbttaglist; + } + + private NBTTagList a(double... adouble) { + NBTTagList nbttaglist = new NBTTagList(); + double[] adouble1 = adouble; + int i = adouble.length; + + for (int j = 0; j < i; ++j) { + double d0 = adouble1[j]; + + nbttaglist.add((NBTBase) (new NBTTagDouble(d0))); + } + + return nbttaglist; + } + + public static class EntityInfo { + + public final Vec3D a; + public final BlockPosition b; + public final NBTTagCompound c; + + public EntityInfo(Vec3D vec3d, BlockPosition blockposition, NBTTagCompound nbttagcompound) { + this.a = vec3d; + this.b = blockposition; + this.c = nbttagcompound; + } + } + + public static class BlockInfo { + + public final BlockPosition a; + public final IBlockData b; + public final NBTTagCompound c; + + public BlockInfo(BlockPosition blockposition, IBlockData iblockdata, @Nullable NBTTagCompound nbttagcompound) { + this.a = blockposition; + this.b = iblockdata; + this.c = nbttagcompound; + } + } + + static class a implements Iterable { + + public static final IBlockData a = Blocks.AIR.getBlockData(); + private final RegistryBlockID b; + private int c; + + private a() { + this.b = new RegistryBlockID<>(16); + } + + public int a(IBlockData iblockdata) { + int i = this.b.getId(iblockdata); + + if (i == -1) { + i = this.c++; + this.b.a(iblockdata, i); + } + + return i; + } + + @Nullable + public IBlockData a(int i) { + IBlockData iblockdata = (IBlockData) this.b.fromId(i); + + return iblockdata == null ? a : iblockdata; // Paper - decompile fix + } + + public Iterator iterator() { + return this.b.iterator(); + } + + public void a(IBlockData iblockdata, int i) { + this.b.a(iblockdata, i); + } + } +} diff --git a/src/main/java/net/minecraft/server/DefinedStructureManager.java b/src/main/java/net/minecraft/server/DefinedStructureManager.java new file mode 100644 index 000000000000..f45685099744 --- /dev/null +++ b/src/main/java/net/minecraft/server/DefinedStructureManager.java @@ -0,0 +1,236 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import com.mojang.datafixers.DataFixTypes; +import com.mojang.datafixers.DataFixer; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.LinkOption; +import java.util.Map; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class DefinedStructureManager implements IResourcePackListener { + + private static final Logger a = LogManager.getLogger(); + private final Map b = Maps.newConcurrentMap(); // Paper + private final DataFixer c; + private final MinecraftServer d; + private final java.nio.file.Path e; + + public DefinedStructureManager(MinecraftServer minecraftserver, File file, DataFixer datafixer) { + this.d = minecraftserver; + this.c = datafixer; + this.e = file.toPath().resolve("generated").normalize(); + minecraftserver.getResourceManager().a((IResourcePackListener) this); + } + + public DefinedStructure a(MinecraftKey minecraftkey) { + DefinedStructure definedstructure = this.b(minecraftkey); + + if (definedstructure == null) { + definedstructure = new DefinedStructure(); + this.b.put(minecraftkey, definedstructure); + } + + return definedstructure; + } + + @Nullable + public DefinedStructure b(MinecraftKey minecraftkey) { + return (DefinedStructure) this.b.computeIfAbsent(minecraftkey, (minecraftkey1) -> { + DefinedStructure definedstructure = this.f(minecraftkey1); + + return definedstructure != null ? definedstructure : this.e(minecraftkey1); + }); + } + + public void a(IResourceManager iresourcemanager) { + this.b.clear(); + } + + @Nullable + private DefinedStructure e(MinecraftKey minecraftkey) { + MinecraftKey minecraftkey1 = new MinecraftKey(minecraftkey.b(), "structures/" + minecraftkey.getKey() + ".nbt"); + + try { + IResource iresource = this.d.getResourceManager().a(minecraftkey1); + Throwable throwable = null; + + DefinedStructure definedstructure; + + try { + definedstructure = this.a(iresource.b()); + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (iresource != null) { + if (throwable != null) { + try { + iresource.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + iresource.close(); + } + } + + } + + return definedstructure; + } catch (FileNotFoundException filenotfoundexception) { + return null; + } catch (Throwable throwable3) { + DefinedStructureManager.a.error("Couldn't load structure {}: {}", minecraftkey, throwable3.toString()); + return null; + } + } + + @Nullable + private DefinedStructure f(MinecraftKey minecraftkey) { + if (!this.e.toFile().isDirectory()) { + return null; + } else { + java.nio.file.Path java_nio_file_path = this.b(minecraftkey, ".nbt"); + + try { + FileInputStream fileinputstream = new FileInputStream(java_nio_file_path.toFile()); + Throwable throwable = null; + + DefinedStructure definedstructure; + + try { + definedstructure = this.a((InputStream) fileinputstream); + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (fileinputstream != null) { + if (throwable != null) { + try { + fileinputstream.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + fileinputstream.close(); + } + } + + } + + return definedstructure; + } catch (FileNotFoundException filenotfoundexception) { + return null; + } catch (IOException ioexception) { + DefinedStructureManager.a.error("Couldn't load structure from {}", java_nio_file_path, ioexception); + return null; + } + } + } + + private DefinedStructure a(InputStream inputstream) throws IOException { + NBTTagCompound nbttagcompound = NBTCompressedStreamTools.a(inputstream); + + if (!nbttagcompound.hasKeyOfType("DataVersion", 99)) { + nbttagcompound.setInt("DataVersion", 500); + } + + DefinedStructure definedstructure = new DefinedStructure(); + + definedstructure.b(GameProfileSerializer.a(this.c, DataFixTypes.STRUCTURE, nbttagcompound, nbttagcompound.getInt("DataVersion"))); + return definedstructure; + } + + public boolean c(MinecraftKey minecraftkey) { + DefinedStructure definedstructure = (DefinedStructure) this.b.get(minecraftkey); + + if (definedstructure == null) { + return false; + } else { + java.nio.file.Path java_nio_file_path = this.b(minecraftkey, ".nbt"); + java.nio.file.Path java_nio_file_path1 = java_nio_file_path.getParent(); + + if (java_nio_file_path1 == null) { + return false; + } else { + try { + Files.createDirectories(Files.exists(java_nio_file_path1, new LinkOption[0]) ? java_nio_file_path1.toRealPath() : java_nio_file_path1); + } catch (IOException ioexception) { + DefinedStructureManager.a.error("Failed to create parent directory: {}", java_nio_file_path1); + return false; + } + + NBTTagCompound nbttagcompound = definedstructure.a(new NBTTagCompound()); + + try { + FileOutputStream fileoutputstream = new FileOutputStream(java_nio_file_path.toFile()); + Throwable throwable = null; + + try { + NBTCompressedStreamTools.a(nbttagcompound, (OutputStream) fileoutputstream); + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (fileoutputstream != null) { + if (throwable != null) { + try { + fileoutputstream.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + fileoutputstream.close(); + } + } + + } + + return true; + } catch (Throwable throwable3) { + return false; + } + } + } + } + + private java.nio.file.Path a(MinecraftKey minecraftkey, String s) { + try { + java.nio.file.Path java_nio_file_path = this.e.resolve(minecraftkey.b()); + java.nio.file.Path java_nio_file_path1 = java_nio_file_path.resolve("structures"); + + return SystemUtils.a(java_nio_file_path1, minecraftkey.getKey(), s); + } catch (InvalidPathException invalidpathexception) { + throw new ResourceKeyInvalidException("Invalid resource path: " + minecraftkey, invalidpathexception); + } + } + + private java.nio.file.Path b(MinecraftKey minecraftkey, String s) { + if (minecraftkey.getKey().contains("//")) { + throw new ResourceKeyInvalidException("Invalid resource path: " + minecraftkey); + } else { + java.nio.file.Path java_nio_file_path = this.a(minecraftkey, s); + + if (java_nio_file_path.startsWith(this.e) && SystemUtils.a(java_nio_file_path) && SystemUtils.b(java_nio_file_path)) { + return java_nio_file_path; + } else { + throw new ResourceKeyInvalidException("Invalid resource path: " + java_nio_file_path); + } + } + } + + public void d(MinecraftKey minecraftkey) { + this.b.remove(minecraftkey); + } +} diff --git a/src/main/java/net/minecraft/server/DispenseBehaviorItem.java b/src/main/java/net/minecraft/server/DispenseBehaviorItem.java new file mode 100644 index 000000000000..76e11ac35dda --- /dev/null +++ b/src/main/java/net/minecraft/server/DispenseBehaviorItem.java @@ -0,0 +1,101 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseEvent; +// CraftBukkit end + +public class DispenseBehaviorItem implements IDispenseBehavior { + + public DispenseBehaviorItem() {} + + public final ItemStack dispense(ISourceBlock isourceblock, ItemStack itemstack) { + ItemStack itemstack1 = this.a(isourceblock, itemstack); + + this.a(isourceblock); + this.a(isourceblock, (EnumDirection) isourceblock.e().get(BlockDispenser.FACING)); + return itemstack1; + } + + protected ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING); + IPosition iposition = BlockDispenser.a(isourceblock); + ItemStack itemstack1 = itemstack.cloneAndSubtract(1); + + // CraftBukkit start + if (!a(isourceblock.getWorld(), itemstack1, 6, enumdirection, isourceblock)) { + itemstack.add(1); + } + // CraftBukkit end + return itemstack; + } + + // CraftBukkit start - void -> boolean return, IPosition -> ISourceBlock last argument + public static boolean a(World world, ItemStack itemstack, int i, EnumDirection enumdirection, ISourceBlock isourceblock) { + if (itemstack.isEmpty()) return true; + IPosition iposition = BlockDispenser.a(isourceblock); + // CraftBukkit end + double d0 = iposition.getX(); + double d1 = iposition.getY(); + double d2 = iposition.getZ(); + + if (enumdirection.k() == EnumDirection.EnumAxis.Y) { + d1 -= 0.125D; + } else { + d1 -= 0.15625D; + } + + EntityItem entityitem = new EntityItem(world, d0, d1, d2, itemstack); + double d3 = world.random.nextDouble() * 0.1D + 0.2D; + + entityitem.motX = (double) enumdirection.getAdjacentX() * d3; + entityitem.motY = 0.20000000298023224D; + entityitem.motZ = (double) enumdirection.getAdjacentZ() * d3; + entityitem.motX += world.random.nextGaussian() * 0.007499999832361937D * (double) i; + entityitem.motY += world.random.nextGaussian() * 0.007499999832361937D * (double) i; + entityitem.motZ += world.random.nextGaussian() * 0.007499999832361937D * (double) i; + + // CraftBukkit start + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(entityitem.motX, entityitem.motY, entityitem.motZ)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + return false; + } + + entityitem.setItemStack(CraftItemStack.asNMSCopy(event.getItem())); + entityitem.motX = event.getVelocity().getX(); + entityitem.motY = event.getVelocity().getY(); + entityitem.motZ = event.getVelocity().getZ(); + + if (!event.getItem().getType().equals(craftItem.getType())) { + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior.getClass() != DispenseBehaviorItem.class) { + idispensebehavior.dispense(isourceblock, eventStack); + } else { + world.addEntity(entityitem); + } + return false; + } + + world.addEntity(entityitem); + + return true; + // CraftBukkit end + } + + protected void a(ISourceBlock isourceblock) { + isourceblock.getWorld().triggerEffect(1000, isourceblock.getBlockPosition(), 0); + } + + protected void a(ISourceBlock isourceblock, EnumDirection enumdirection) { + isourceblock.getWorld().triggerEffect(2000, isourceblock.getBlockPosition(), enumdirection.a()); + } +} diff --git a/src/main/java/net/minecraft/server/DispenseBehaviorProjectile.java b/src/main/java/net/minecraft/server/DispenseBehaviorProjectile.java new file mode 100644 index 000000000000..f06d164a5cf1 --- /dev/null +++ b/src/main/java/net/minecraft/server/DispenseBehaviorProjectile.java @@ -0,0 +1,66 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseEvent; +// CraftBukkit end + +public abstract class DispenseBehaviorProjectile extends DispenseBehaviorItem { + + public DispenseBehaviorProjectile() {} + + public ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + World world = isourceblock.getWorld(); + IPosition iposition = BlockDispenser.a(isourceblock); + EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING); + IProjectile iprojectile = this.a(world, iposition, itemstack); + + // iprojectile.shoot((double) enumdirection.getAdjacentX(), (double) ((float) enumdirection.getAdjacentY() + 0.1F), (double) enumdirection.getAdjacentZ(), this.getPower(), this.a()); + // CraftBukkit start + ItemStack itemstack1 = itemstack.cloneAndSubtract(1); + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) enumdirection.getAdjacentX(), (double) ((float) enumdirection.getAdjacentY() + 0.1F), (double) enumdirection.getAdjacentZ())); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + itemstack.add(1); + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + itemstack.add(1); + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) { + idispensebehavior.dispense(isourceblock, eventStack); + return itemstack; + } + } + + iprojectile.shoot(event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.getPower(), this.a()); + ((Entity) iprojectile).projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource((TileEntityDispenser) isourceblock.getTileEntity()); + // CraftBukkit end + world.addEntity((Entity) iprojectile); + // itemstack.subtract(1); // CraftBukkit - Handled during event processing + return itemstack; + } + + protected void a(ISourceBlock isourceblock) { + isourceblock.getWorld().triggerEffect(1002, isourceblock.getBlockPosition(), 0); + } + + protected abstract IProjectile a(World world, IPosition iposition, ItemStack itemstack); + + protected float a() { + return 6.0F; + } + + protected float getPower() { + return 1.1F; + } +} diff --git a/src/main/java/net/minecraft/server/DispenserRegistry.java b/src/main/java/net/minecraft/server/DispenserRegistry.java new file mode 100644 index 000000000000..2fb9f4680b2a --- /dev/null +++ b/src/main/java/net/minecraft/server/DispenserRegistry.java @@ -0,0 +1,959 @@ +package net.minecraft.server; + +import java.io.PrintStream; +import java.util.Iterator; +import java.util.Random; +import java.util.function.Function; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import java.util.List; +import org.bukkit.Location; +import org.bukkit.TreeType; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.DummyGeneratorAccess; +import org.bukkit.event.block.BlockDispenseEvent; +import org.bukkit.event.world.StructureGrowEvent; +// CraftBukkit end + +public class DispenserRegistry { + + public static final PrintStream a = System.out; + private static boolean b; + private static final Logger c = LogManager.getLogger(); + + public static boolean a() { + return DispenserRegistry.b; + } + + static void b() { + BlockDispenser.a((IMaterial) Items.ARROW, (IDispenseBehavior) (new DispenseBehaviorProjectile() { + protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) { + EntityTippedArrow entitytippedarrow = new EntityTippedArrow(world, iposition.getX(), iposition.getY(), iposition.getZ()); + + entitytippedarrow.fromPlayer = EntityArrow.PickupStatus.ALLOWED; + return entitytippedarrow; + } + })); + BlockDispenser.a((IMaterial) Items.TIPPED_ARROW, (IDispenseBehavior) (new DispenseBehaviorProjectile() { + protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) { + EntityTippedArrow entitytippedarrow = new EntityTippedArrow(world, iposition.getX(), iposition.getY(), iposition.getZ()); + + entitytippedarrow.b(itemstack); + entitytippedarrow.fromPlayer = EntityArrow.PickupStatus.ALLOWED; + return entitytippedarrow; + } + })); + BlockDispenser.a((IMaterial) Items.SPECTRAL_ARROW, (IDispenseBehavior) (new DispenseBehaviorProjectile() { + protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) { + EntitySpectralArrow entityspectralarrow = new EntitySpectralArrow(world, iposition.getX(), iposition.getY(), iposition.getZ()); + + entityspectralarrow.fromPlayer = EntityArrow.PickupStatus.ALLOWED; + return entityspectralarrow; + } + })); + BlockDispenser.a((IMaterial) Items.EGG, (IDispenseBehavior) (new DispenseBehaviorProjectile() { + protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) { + return new EntityEgg(world, iposition.getX(), iposition.getY(), iposition.getZ()); + } + })); + BlockDispenser.a((IMaterial) Items.SNOWBALL, (IDispenseBehavior) (new DispenseBehaviorProjectile() { + protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) { + return new EntitySnowball(world, iposition.getX(), iposition.getY(), iposition.getZ()); + } + })); + BlockDispenser.a((IMaterial) Items.EXPERIENCE_BOTTLE, (IDispenseBehavior) (new DispenseBehaviorProjectile() { + protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) { + return new EntityThrownExpBottle(world, iposition.getX(), iposition.getY(), iposition.getZ()); + } + + protected float a() { + return super.a() * 0.5F; + } + + protected float getPower() { + return super.getPower() * 1.25F; + } + })); + BlockDispenser.a((IMaterial) Items.SPLASH_POTION, new IDispenseBehavior() { + public ItemStack dispense(ISourceBlock isourceblock, final ItemStack itemstack) { + return (new DispenseBehaviorProjectile() { + protected IProjectile a(World world, IPosition iposition, ItemStack itemstack1) { + return new EntityPotion(world, iposition.getX(), iposition.getY(), iposition.getZ(), itemstack.cloneItemStack()); + } + + protected float a() { + return super.a() * 0.5F; + } + + protected float getPower() { + return super.getPower() * 1.25F; + } + }).dispense(isourceblock, itemstack); + } + }); + BlockDispenser.a((IMaterial) Items.LINGERING_POTION, new IDispenseBehavior() { + public ItemStack dispense(ISourceBlock isourceblock, final ItemStack itemstack) { + return (new DispenseBehaviorProjectile() { + protected IProjectile a(World world, IPosition iposition, ItemStack itemstack1) { + return new EntityPotion(world, iposition.getX(), iposition.getY(), iposition.getZ(), itemstack.cloneItemStack()); + } + + protected float a() { + return super.a() * 0.5F; + } + + protected float getPower() { + return super.getPower() * 1.25F; + } + }).dispense(isourceblock, itemstack); + } + }); + DispenseBehaviorItem dispensebehavioritem = new DispenseBehaviorItem() { + public ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING); + EntityTypes entitytypes = ((ItemMonsterEgg) itemstack.getItem()).b(itemstack.getTag()); + + // CraftBukkit start + World world = isourceblock.getWorld(); + ItemStack itemstack1 = itemstack.cloneAndSubtract(1); + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + itemstack.add(1); + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + itemstack.add(1); + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) { + idispensebehavior.dispense(isourceblock, eventStack); + return itemstack; + } + } + + if (entitytypes != null) { + entitytypes.a(isourceblock.getWorld(), itemstack, (EntityHuman) null, isourceblock.getBlockPosition().shift(enumdirection), enumdirection != EnumDirection.UP, false); + } + + // itemstack.subtract(1); // Handled during event processing + // CraftBukkit end + return itemstack; + } + }; + Iterator iterator = ItemMonsterEgg.d().iterator(); + + while (iterator.hasNext()) { + ItemMonsterEgg itemmonsteregg = (ItemMonsterEgg) iterator.next(); + + BlockDispenser.a((IMaterial) itemmonsteregg, (IDispenseBehavior) dispensebehavioritem); + } + + BlockDispenser.a((IMaterial) Items.FIREWORK_ROCKET, (IDispenseBehavior) (new DispenseBehaviorItem() { + public ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING); + double d0 = isourceblock.getX() + (double) enumdirection.getAdjacentX(); + double d1 = (double) ((float) isourceblock.getBlockPosition().getY() + 0.2F); + double d2 = isourceblock.getZ() + (double) enumdirection.getAdjacentZ(); + // CraftBukkit start + World world = isourceblock.getWorld(); + ItemStack itemstack1 = itemstack.cloneAndSubtract(1); + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1, d2)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + itemstack.add(1); + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + itemstack.add(1); + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) { + idispensebehavior.dispense(isourceblock, eventStack); + return itemstack; + } + } + + itemstack1 = CraftItemStack.asNMSCopy(event.getItem()); + EntityFireworks entityfireworks = new EntityFireworks(isourceblock.getWorld(), event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), itemstack1); + + isourceblock.getWorld().addEntity(entityfireworks); + // itemstack.subtract(1); // Handled during event processing + // CraftBukkit end + return itemstack; + } + + protected void a(ISourceBlock isourceblock) { + isourceblock.getWorld().triggerEffect(1004, isourceblock.getBlockPosition(), 0); + } + })); + BlockDispenser.a((IMaterial) Items.FIRE_CHARGE, (IDispenseBehavior) (new DispenseBehaviorItem() { + public ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING); + IPosition iposition = BlockDispenser.a(isourceblock); + double d0 = iposition.getX() + (double) ((float) enumdirection.getAdjacentX() * 0.3F); + double d1 = iposition.getY() + (double) ((float) enumdirection.getAdjacentY() * 0.3F); + double d2 = iposition.getZ() + (double) ((float) enumdirection.getAdjacentZ() * 0.3F); + World world = isourceblock.getWorld(); + Random random = world.random; + double d3 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentX(); + double d4 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentY(); + double d5 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentZ(); + + // CraftBukkit start + ItemStack itemstack1 = itemstack.cloneAndSubtract(1); + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d3, d4, d5)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + itemstack.add(1); + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + itemstack.add(1); + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) { + idispensebehavior.dispense(isourceblock, eventStack); + return itemstack; + } + } + + EntitySmallFireball fireball = new EntitySmallFireball(world, d0, d1, d2, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()); + fireball.projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource((TileEntityDispenser) isourceblock.getTileEntity()); + + world.addEntity(fireball); + // itemstack.subtract(1); // Handled during event processing + // CraftBukkit end + return itemstack; + } + + protected void a(ISourceBlock isourceblock) { + isourceblock.getWorld().triggerEffect(1018, isourceblock.getBlockPosition(), 0); + } + })); + BlockDispenser.a((IMaterial) Items.OAK_BOAT, (IDispenseBehavior) (new DispenserRegistry.a(EntityBoat.EnumBoatType.OAK))); + BlockDispenser.a((IMaterial) Items.SPRUCE_BOAT, (IDispenseBehavior) (new DispenserRegistry.a(EntityBoat.EnumBoatType.SPRUCE))); + BlockDispenser.a((IMaterial) Items.BIRCH_BOAT, (IDispenseBehavior) (new DispenserRegistry.a(EntityBoat.EnumBoatType.BIRCH))); + BlockDispenser.a((IMaterial) Items.JUNGLE_BOAT, (IDispenseBehavior) (new DispenserRegistry.a(EntityBoat.EnumBoatType.JUNGLE))); + BlockDispenser.a((IMaterial) Items.DARK_OAK_BOAT, (IDispenseBehavior) (new DispenserRegistry.a(EntityBoat.EnumBoatType.DARK_OAK))); + BlockDispenser.a((IMaterial) Items.ACACIA_BOAT, (IDispenseBehavior) (new DispenserRegistry.a(EntityBoat.EnumBoatType.ACACIA))); + DispenseBehaviorItem dispensebehavioritem1 = new DispenseBehaviorItem() { + private final DispenseBehaviorItem a = new DispenseBehaviorItem(); + + public ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + ItemBucket itembucket = (ItemBucket) itemstack.getItem(); + BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING)); + World world = isourceblock.getWorld(); + + // CraftBukkit start + int x = blockposition.getX(); + int y = blockposition.getY(); + int z = blockposition.getZ(); + IBlockData iblockdata = world.getType(blockposition); + Material material = iblockdata.getMaterial(); + if (world.isEmpty(blockposition) || !material.isBuildable() || material.isReplaceable() || ((iblockdata.getBlock() instanceof IFluidContainer) && ((IFluidContainer) iblockdata.getBlock()).canPlace(world, blockposition, iblockdata, itembucket.fluidType))) { + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(x, y, z)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) { + idispensebehavior.dispense(isourceblock, eventStack); + return itemstack; + } + } + + itembucket = (ItemBucket) CraftItemStack.asNMSCopy(event.getItem()).getItem(); + } + // CraftBukkit end + + if (itembucket.a((EntityHuman) null, world, blockposition, (MovingObjectPosition) null)) { + itembucket.a(world, itemstack, blockposition); + // CraftBukkit start - Handle stacked buckets + Item item = Items.BUCKET; + itemstack.subtract(1); + if (itemstack.isEmpty()) { + itemstack.setItem(Items.BUCKET); + itemstack.setCount(1); + } else if (((TileEntityDispenser) isourceblock.getTileEntity()).addItem(new ItemStack(item)) < 0) { + this.a.dispense(isourceblock, new ItemStack(item)); + } + // CraftBukkit end + return itemstack; + } else { + return this.a.dispense(isourceblock, itemstack); + } + } + }; + + BlockDispenser.a((IMaterial) Items.LAVA_BUCKET, (IDispenseBehavior) dispensebehavioritem1); + BlockDispenser.a((IMaterial) Items.WATER_BUCKET, (IDispenseBehavior) dispensebehavioritem1); + BlockDispenser.a((IMaterial) Items.SALMON_BUCKET, (IDispenseBehavior) dispensebehavioritem1); + BlockDispenser.a((IMaterial) Items.COD_BUCKET, (IDispenseBehavior) dispensebehavioritem1); + BlockDispenser.a((IMaterial) Items.PUFFERFISH_BUCKET, (IDispenseBehavior) dispensebehavioritem1); + BlockDispenser.a((IMaterial) Items.TROPICAL_FISH_BUCKET, (IDispenseBehavior) dispensebehavioritem1); + BlockDispenser.a((IMaterial) Items.BUCKET, (IDispenseBehavior) (new DispenseBehaviorItem() { + private final DispenseBehaviorItem a = new DispenseBehaviorItem(); + + public ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + World world = isourceblock.getWorld(); + BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING)); + IBlockData iblockdata = world.getType(blockposition); + Block block = iblockdata.getBlock(); + + if (block instanceof IFluidSource) { + FluidType fluidtype = ((IFluidSource) block).removeFluid(DummyGeneratorAccess.INSTANCE, blockposition, iblockdata); // CraftBukkit + + if (!(fluidtype instanceof FluidTypeFlowing)) { + return super.a(isourceblock, itemstack); + } else { + Item item = fluidtype.b(); + + // CraftBukkit start + org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); + + BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ())); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) { + idispensebehavior.dispense(isourceblock, eventStack); + return itemstack; + } + } + + fluidtype = ((IFluidSource) block).removeFluid(world, blockposition, iblockdata); // From above + // CraftBukkit end + + itemstack.subtract(1); + if (itemstack.isEmpty()) { + return new ItemStack(item); + } else { + if (((TileEntityDispenser) isourceblock.getTileEntity()).addItem(new ItemStack(item)) < 0) { + this.a.dispense(isourceblock, new ItemStack(item)); + } + + return itemstack; + } + } + } else { + return super.a(isourceblock, itemstack); + } + } + })); + BlockDispenser.a((IMaterial) Items.FLINT_AND_STEEL, (IDispenseBehavior) (new DispenserRegistry.c() { + protected ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + World world = isourceblock.getWorld(); + + // CraftBukkit start + org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); + + BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) { + idispensebehavior.dispense(isourceblock, eventStack); + return itemstack; + } + } + // CraftBukkit end + + this.a = true; + BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING)); + + if (ItemFlintAndSteel.a((GeneratorAccess) world, blockposition)) { + // CraftBukkit start - Ignition by dispensing flint and steel + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, blockposition, isourceblock.getBlockPosition()).isCancelled()) { + world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); + } + // CraftBukkit end + } else { + Block block = world.getType(blockposition).getBlock(); + + if (block instanceof BlockTNT) { + ((BlockTNT) block).a(world, blockposition); + world.setAir(blockposition); + } else { + this.a = false; + } + } + + if (this.a && itemstack.isDamaged(1, world.random, (EntityPlayer) null)) { + itemstack.setCount(0); + } + + return itemstack; + } + })); + BlockDispenser.a((IMaterial) Items.BONE_MEAL, (IDispenseBehavior) (new DispenserRegistry.c() { + protected ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + this.a = true; + World world = isourceblock.getWorld(); + BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING)); + // CraftBukkit start + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) { + idispensebehavior.dispense(isourceblock, eventStack); + return itemstack; + } + } + + world.captureTreeGeneration = true; + // CraftBukkit end + + if (!ItemBoneMeal.a(itemstack, world, blockposition) && !ItemBoneMeal.a(itemstack, world, blockposition, (EnumDirection) null)) { + this.a = false; + } else if (!world.isClientSide) { + world.triggerEffect(2005, blockposition, 0); + } + // CraftBukkit start + world.captureTreeGeneration = false; + if (world.capturedBlockStates.size() > 0) { + TreeType treeType = BlockSapling.treeType; + BlockSapling.treeType = null; + Location location = new Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ()); + List blocks = (List) world.capturedBlockStates.clone(); + world.capturedBlockStates.clear(); + StructureGrowEvent structureEvent = null; + if (treeType != null) { + structureEvent = new StructureGrowEvent(location, treeType, false, null, blocks); + org.bukkit.Bukkit.getPluginManager().callEvent(structureEvent); + } + if (structureEvent == null || !structureEvent.isCancelled()) { + for (org.bukkit.block.BlockState blockstate : blocks) { + blockstate.update(true); + } + } + } + // CraftBukkit end + + return itemstack; + } + })); + BlockDispenser.a((IMaterial) Blocks.TNT, (IDispenseBehavior) (new DispenseBehaviorItem() { + protected ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + World world = isourceblock.getWorld(); + BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING)); + // EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, (EntityLiving) null); + + // CraftBukkit start + ItemStack itemstack1 = itemstack.cloneAndSubtract(1); + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + itemstack.add(1); + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + itemstack.add(1); + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) { + idispensebehavior.dispense(isourceblock, eventStack); + return itemstack; + } + } + + EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), (EntityLiving) null); + // CraftBukkit end + + world.addEntity(entitytntprimed); + world.a((EntityHuman) null, entitytntprimed.locX, entitytntprimed.locY, entitytntprimed.locZ, SoundEffects.ENTITY_TNT_PRIMED, SoundCategory.BLOCKS, 1.0F, 1.0F); + // itemstack.subtract(1); // CraftBukkit - handled above + return itemstack; + } + })); + DispenserRegistry.c dispenserregistry_c = new DispenserRegistry.c() { + protected ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + this.a = !ItemArmor.a(isourceblock, itemstack).isEmpty(); + return itemstack; + } + }; + + BlockDispenser.a((IMaterial) Items.CREEPER_HEAD, (IDispenseBehavior) dispenserregistry_c); + BlockDispenser.a((IMaterial) Items.ZOMBIE_HEAD, (IDispenseBehavior) dispenserregistry_c); + BlockDispenser.a((IMaterial) Items.DRAGON_HEAD, (IDispenseBehavior) dispenserregistry_c); + BlockDispenser.a((IMaterial) Items.SKELETON_SKULL, (IDispenseBehavior) dispenserregistry_c); + BlockDispenser.a((IMaterial) Items.PLAYER_HEAD, (IDispenseBehavior) dispenserregistry_c); + BlockDispenser.a((IMaterial) Items.WITHER_SKELETON_SKULL, (IDispenseBehavior) (new DispenserRegistry.c() { + protected ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + World world = isourceblock.getWorld(); + EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING); + BlockPosition blockposition = isourceblock.getBlockPosition().shift(enumdirection); + + // CraftBukkit start + org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); + + BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ())); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) { + idispensebehavior.dispense(isourceblock, eventStack); + return itemstack; + } + } + // CraftBukkit end + + this.a = true; + if (world.isEmpty(blockposition) && BlockWitherSkull.b(world, blockposition, itemstack)) { + world.setTypeAndData(blockposition, (IBlockData) Blocks.WITHER_SKELETON_SKULL.getBlockData().set(BlockSkull.a, enumdirection.k() == EnumDirection.EnumAxis.Y ? 0 : enumdirection.opposite().get2DRotationValue() * 4), 3); + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntitySkull) { + BlockWitherSkull.a(world, blockposition, (TileEntitySkull) tileentity); + } + + itemstack.subtract(1); + } else if (ItemArmor.a(isourceblock, itemstack).isEmpty()) { + this.a = false; + } + + return itemstack; + } + })); + BlockDispenser.a((IMaterial) Blocks.CARVED_PUMPKIN, (IDispenseBehavior) (new DispenserRegistry.c() { + protected ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + World world = isourceblock.getWorld(); + BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING)); + BlockPumpkinCarved blockpumpkincarved = (BlockPumpkinCarved) Blocks.CARVED_PUMPKIN; + + // CraftBukkit start + org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); + + BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ())); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) { + idispensebehavior.dispense(isourceblock, eventStack); + return itemstack; + } + } + // CraftBukkit end + + this.a = true; + if (world.isEmpty(blockposition) && blockpumpkincarved.a((IWorldReader) world, blockposition)) { + if (!world.isClientSide) { + world.setTypeAndData(blockposition, blockpumpkincarved.getBlockData(), 3); + } + + itemstack.subtract(1); + } else { + ItemStack itemstack1 = ItemArmor.a(isourceblock, itemstack); + + if (itemstack1.isEmpty()) { + this.a = false; + } + } + + return itemstack; + } + })); + BlockDispenser.a((IMaterial) Blocks.SHULKER_BOX.getItem(), (IDispenseBehavior) (new DispenserRegistry.d())); + EnumColor[] aenumcolor = EnumColor.values(); + int i = aenumcolor.length; + + for (int j = 0; j < i; ++j) { + EnumColor enumcolor = aenumcolor[j]; + + BlockDispenser.a((IMaterial) BlockShulkerBox.a(enumcolor).getItem(), (IDispenseBehavior) (new DispenserRegistry.d())); + } + + } + + public static void c() { + if (!DispenserRegistry.b) { + DispenserRegistry.b = true; + SoundEffect.b(); + FluidType.l(); + Block.t(); + BlockFire.d(); + MobEffectList.m(); + Enchantment.h(); + if (EntityTypes.getName(EntityTypes.PLAYER) == null) { + throw new IllegalStateException("Failed loading EntityTypes"); + } else { + Item.r(); + PotionRegistry.b(); + PotionBrewer.a(); + BiomeBase.t(); + PlayerSelector.a(); + Particle.c(); + b(); + ArgumentRegistry.a(); + BiomeLayout.a(); + TileEntityTypes.a(); + ChunkGeneratorType.a(); + DimensionManager.a(); + Paintings.a(); + StatisticList.a(); + IRegistry.e(); + if (SharedConstants.b) { + a("block", IRegistry.BLOCK, Block::m); + a("biome", IRegistry.BIOME, BiomeBase::k); + a("enchantment", IRegistry.ENCHANTMENT, Enchantment::g); + a("item", IRegistry.ITEM, Item::getName); + a("effect", IRegistry.MOB_EFFECT, MobEffectList::c); + a("entity", IRegistry.ENTITY_TYPE, EntityTypes::d); + } + + d(); + // CraftBukkit start - easier than fixing the decompile + DataConverterFlattenData.a(1440, "{Name:\'minecraft:portal\',Properties:{axis:\'x\'}}", new String[]{"{Name:\'minecraft:portal\',Properties:{axis:\'x\'}}"}); + + DataConverterMaterialId.ID_MAPPING.put(409, "minecraft:prismarine_shard"); + DataConverterMaterialId.ID_MAPPING.put(410, "minecraft:prismarine_crystals"); + DataConverterMaterialId.ID_MAPPING.put(411, "minecraft:rabbit"); + DataConverterMaterialId.ID_MAPPING.put(412, "minecraft:cooked_rabbit"); + DataConverterMaterialId.ID_MAPPING.put(413, "minecraft:rabbit_stew"); + DataConverterMaterialId.ID_MAPPING.put(414, "minecraft:rabbit_foot"); + DataConverterMaterialId.ID_MAPPING.put(415, "minecraft:rabbit_hide"); + DataConverterMaterialId.ID_MAPPING.put(416, "minecraft:armor_stand"); + + DataConverterMaterialId.ID_MAPPING.put(423, "minecraft:mutton"); + DataConverterMaterialId.ID_MAPPING.put(424, "minecraft:cooked_mutton"); + DataConverterMaterialId.ID_MAPPING.put(425, "minecraft:banner"); + DataConverterMaterialId.ID_MAPPING.put(426, "minecraft:end_crystal"); + DataConverterMaterialId.ID_MAPPING.put(427, "minecraft:spruce_door"); + DataConverterMaterialId.ID_MAPPING.put(428, "minecraft:birch_door"); + DataConverterMaterialId.ID_MAPPING.put(429, "minecraft:jungle_door"); + DataConverterMaterialId.ID_MAPPING.put(430, "minecraft:acacia_door"); + DataConverterMaterialId.ID_MAPPING.put(431, "minecraft:dark_oak_door"); + DataConverterMaterialId.ID_MAPPING.put(432, "minecraft:chorus_fruit"); + DataConverterMaterialId.ID_MAPPING.put(433, "minecraft:chorus_fruit_popped"); + DataConverterMaterialId.ID_MAPPING.put(434, "minecraft:beetroot"); + DataConverterMaterialId.ID_MAPPING.put(435, "minecraft:beetroot_seeds"); + DataConverterMaterialId.ID_MAPPING.put(436, "minecraft:beetroot_soup"); + DataConverterMaterialId.ID_MAPPING.put(437, "minecraft:dragon_breath"); + DataConverterMaterialId.ID_MAPPING.put(438, "minecraft:splash_potion"); + DataConverterMaterialId.ID_MAPPING.put(439, "minecraft:spectral_arrow"); + DataConverterMaterialId.ID_MAPPING.put(440, "minecraft:tipped_arrow"); + DataConverterMaterialId.ID_MAPPING.put(441, "minecraft:lingering_potion"); + DataConverterMaterialId.ID_MAPPING.put(442, "minecraft:shield"); + DataConverterMaterialId.ID_MAPPING.put(443, "minecraft:elytra"); + DataConverterMaterialId.ID_MAPPING.put(444, "minecraft:spruce_boat"); + DataConverterMaterialId.ID_MAPPING.put(445, "minecraft:birch_boat"); + DataConverterMaterialId.ID_MAPPING.put(446, "minecraft:jungle_boat"); + DataConverterMaterialId.ID_MAPPING.put(447, "minecraft:acacia_boat"); + DataConverterMaterialId.ID_MAPPING.put(448, "minecraft:dark_oak_boat"); + DataConverterMaterialId.ID_MAPPING.put(449, "minecraft:totem_of_undying"); + DataConverterMaterialId.ID_MAPPING.put(450, "minecraft:shulker_shell"); + DataConverterMaterialId.ID_MAPPING.put(452, "minecraft:iron_nugget"); + DataConverterMaterialId.ID_MAPPING.put(453, "minecraft:knowledge_book"); + + DataConverterSpawnEgg.ID_MAPPING[23] = "Arrow"; + // CraftBukkit end + } + } + } + + private static void a(String s, IRegistry iregistry, Function function) { + LocaleLanguage localelanguage = LocaleLanguage.a(); + + iregistry.iterator().forEachRemaining((object) -> { + String s1 = (String) function.apply(object); + + if (!localelanguage.b(s1)) { + DispenserRegistry.c.warn("Missing translation for {}: {} (key: '{}')", s, iregistry.getKey(object), s1); + } + + }); + } + + private static void d() { + if (DispenserRegistry.c.isDebugEnabled()) { + System.setErr(new DebugOutputStream("STDERR", System.err)); + System.setOut(new DebugOutputStream("STDOUT", DispenserRegistry.a)); + } else { + System.setErr(new RedirectStream("STDERR", System.err)); + System.setOut(new RedirectStream("STDOUT", DispenserRegistry.a)); + } + + } + + static class b extends BlockActionContext { + + private final EnumDirection j; + + public b(World world, BlockPosition blockposition, EnumDirection enumdirection, ItemStack itemstack, EnumDirection enumdirection1) { + super(world, (EntityHuman) null, itemstack, blockposition, enumdirection1, 0.5F, 0.0F, 0.5F); + this.j = enumdirection; + } + + public BlockPosition getClickPosition() { + return this.i; + } + + public boolean b() { + return this.g.getType(this.i).a((BlockActionContext) this); + } + + public boolean c() { + return this.b(); + } + + public EnumDirection d() { + return EnumDirection.DOWN; + } + + public EnumDirection[] e() { + switch (this.j) { + case DOWN: + default: + return new EnumDirection[] { EnumDirection.DOWN, EnumDirection.NORTH, EnumDirection.EAST, EnumDirection.SOUTH, EnumDirection.WEST, EnumDirection.UP}; + case UP: + return new EnumDirection[] { EnumDirection.DOWN, EnumDirection.UP, EnumDirection.NORTH, EnumDirection.EAST, EnumDirection.SOUTH, EnumDirection.WEST}; + case NORTH: + return new EnumDirection[] { EnumDirection.DOWN, EnumDirection.NORTH, EnumDirection.EAST, EnumDirection.WEST, EnumDirection.UP, EnumDirection.SOUTH}; + case SOUTH: + return new EnumDirection[] { EnumDirection.DOWN, EnumDirection.SOUTH, EnumDirection.EAST, EnumDirection.WEST, EnumDirection.UP, EnumDirection.NORTH}; + case WEST: + return new EnumDirection[] { EnumDirection.DOWN, EnumDirection.WEST, EnumDirection.SOUTH, EnumDirection.UP, EnumDirection.NORTH, EnumDirection.EAST}; + case EAST: + return new EnumDirection[] { EnumDirection.DOWN, EnumDirection.EAST, EnumDirection.SOUTH, EnumDirection.UP, EnumDirection.NORTH, EnumDirection.WEST}; + } + } + + public EnumDirection f() { + return this.j.k() == EnumDirection.EnumAxis.Y ? EnumDirection.NORTH : this.j; + } + + public boolean isSneaking() { + return false; + } + + public float h() { + return (float) (this.j.get2DRotationValue() * 90); + } + } + + static class d extends DispenserRegistry.c { + + private d() {} + + protected ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + this.a = false; + Item item = itemstack.getItem(); + + if (item instanceof ItemBlock) { + EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING); + BlockPosition blockposition = isourceblock.getBlockPosition().shift(enumdirection); + EnumDirection enumdirection1 = isourceblock.getWorld().isEmpty(blockposition.down()) ? enumdirection : EnumDirection.UP; + + // CraftBukkit start + org.bukkit.block.Block bukkitBlock = isourceblock.getWorld().getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); + + BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ())); + if (!BlockDispenser.eventFired) { + isourceblock.getWorld().getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) { + idispensebehavior.dispense(isourceblock, eventStack); + return itemstack; + } + } + // CraftBukkit end + + this.a = ((ItemBlock) item).a((BlockActionContext) (new DispenserRegistry.b(isourceblock.getWorld(), blockposition, enumdirection, itemstack, enumdirection1))) == EnumInteractionResult.SUCCESS; + if (this.a) { + itemstack.subtract(1); + } + } + + return itemstack; + } + } + + public abstract static class c extends DispenseBehaviorItem { + + protected boolean a = true; + + public c() {} + + protected void a(ISourceBlock isourceblock) { + isourceblock.getWorld().triggerEffect(this.a ? 1000 : 1001, isourceblock.getBlockPosition(), 0); + } + } + + public static class a extends DispenseBehaviorItem { + + private final DispenseBehaviorItem a = new DispenseBehaviorItem(); + private final EntityBoat.EnumBoatType b; + + public a(EntityBoat.EnumBoatType entityboat_enumboattype) { + this.b = entityboat_enumboattype; + } + + public ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING); + World world = isourceblock.getWorld(); + double d0 = isourceblock.getX() + (double) ((float) enumdirection.getAdjacentX() * 1.125F); + double d1 = isourceblock.getY() + (double) ((float) enumdirection.getAdjacentY() * 1.125F); + double d2 = isourceblock.getZ() + (double) ((float) enumdirection.getAdjacentZ() * 1.125F); + BlockPosition blockposition = isourceblock.getBlockPosition().shift(enumdirection); + double d3; + + if (world.getFluid(blockposition).a(TagsFluid.WATER)) { + d3 = 1.0D; + } else { + if (!world.getType(blockposition).isAir() || !world.getFluid(blockposition.down()).a(TagsFluid.WATER)) { + return this.a.dispense(isourceblock, itemstack); + } + + d3 = 0.0D; + } + + // EntityBoat entityboat = new EntityBoat(world, d0, d1 + d3, d2); + // CraftBukkit start + ItemStack itemstack1 = itemstack.cloneAndSubtract(1); + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + + BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1 + d3, d2)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + itemstack.add(1); + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + itemstack.add(1); + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) { + idispensebehavior.dispense(isourceblock, eventStack); + return itemstack; + } + } + + EntityBoat entityboat = new EntityBoat(world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()); + // CraftBukkit end + + entityboat.setType(this.b); + entityboat.yaw = enumdirection.l(); + if (!world.addEntity(entityboat)) itemstack.add(1); // CraftBukkit + // itemstack.subtract(1); // CraftBukkit - handled during event processing + return itemstack; + } + + protected void a(ISourceBlock isourceblock) { + isourceblock.getWorld().triggerEffect(1000, isourceblock.getBlockPosition(), 0); + } + } +} diff --git a/src/main/java/net/minecraft/server/DragonControllerLandedFlame.java b/src/main/java/net/minecraft/server/DragonControllerLandedFlame.java new file mode 100644 index 000000000000..50d291139ceb --- /dev/null +++ b/src/main/java/net/minecraft/server/DragonControllerLandedFlame.java @@ -0,0 +1,94 @@ +package net.minecraft.server; + +public class DragonControllerLandedFlame extends AbstractDragonControllerLanded { + + private int b; + private int c; + private EntityAreaEffectCloud d; + + public DragonControllerLandedFlame(EntityEnderDragon entityenderdragon) { + super(entityenderdragon); + } + + public void b() { + ++this.b; + if (this.b % 2 == 0 && this.b < 10) { + Vec3D vec3d = this.a.a(1.0F).a(); + + vec3d.b(-0.7853982F); + double d0 = this.a.bD.locX; + double d1 = this.a.bD.locY + (double) (this.a.bD.length / 2.0F); + double d2 = this.a.bD.locZ; + + for (int i = 0; i < 8; ++i) { + double d3 = d0 + this.a.getRandom().nextGaussian() / 2.0D; + double d4 = d1 + this.a.getRandom().nextGaussian() / 2.0D; + double d5 = d2 + this.a.getRandom().nextGaussian() / 2.0D; + + for (int j = 0; j < 6; ++j) { + this.a.world.addParticle(Particles.j, d3, d4, d5, -vec3d.x * 0.07999999821186066D * (double) j, -vec3d.y * 0.6000000238418579D, -vec3d.z * 0.07999999821186066D * (double) j); + } + + vec3d.b(0.19634955F); + } + } + + } + + public void c() { + ++this.b; + if (this.b >= 200) { + if (this.c >= 4) { + this.a.getDragonControllerManager().setControllerPhase(DragonControllerPhase.TAKEOFF); + } else { + this.a.getDragonControllerManager().setControllerPhase(DragonControllerPhase.SITTING_SCANNING); + } + } else if (this.b == 10) { + Vec3D vec3d = (new Vec3D(this.a.bD.locX - this.a.locX, 0.0D, this.a.bD.locZ - this.a.locZ)).a(); + float f = 5.0F; + double d0 = this.a.bD.locX + vec3d.x * 5.0D / 2.0D; + double d1 = this.a.bD.locZ + vec3d.z * 5.0D / 2.0D; + double d2 = this.a.bD.locY + (double) (this.a.bD.length / 2.0F); + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(MathHelper.floor(d0), MathHelper.floor(d2), MathHelper.floor(d1)); + + while (this.a.world.isEmpty(blockposition_mutableblockposition ) && d2 > 0) { // Paper + --d2; + blockposition_mutableblockposition.c(MathHelper.floor(d0), MathHelper.floor(d2), MathHelper.floor(d1)); + } + + d2 = (double) (MathHelper.floor(d2) + 1); + this.d = new EntityAreaEffectCloud(this.a.world, d0, d2, d1); + this.d.setSource(this.a); + this.d.setRadius(5.0F); + this.d.setDuration(200); + this.d.setParticle(Particles.j); + this.d.a(new MobEffect(MobEffects.HARM)); + if (new com.destroystokyo.paper.event.entity.EnderDragonFlameEvent((org.bukkit.entity.EnderDragon) this.a.getBukkitEntity(), (org.bukkit.entity.AreaEffectCloud) this.d.getBukkitEntity()).callEvent()) // Paper + this.a.world.addEntity(this.d); + else this.removeAreaEffect(); // Paper + } + + } + + public void d() { + this.b = 0; + ++this.c; + } + + public void removeAreaEffect() { this.e(); } // Paper - OBFHELPER + public void e() { + if (this.d != null) { + this.d.die(); + this.d = null; + } + + } + + public DragonControllerPhase getControllerPhase() { + return DragonControllerPhase.SITTING_FLAMING; + } + + public void j() { + this.c = 0; + } +} diff --git a/src/main/java/net/minecraft/server/DragonControllerManager.java b/src/main/java/net/minecraft/server/DragonControllerManager.java new file mode 100644 index 000000000000..77df77a1266e --- /dev/null +++ b/src/main/java/net/minecraft/server/DragonControllerManager.java @@ -0,0 +1,64 @@ +package net.minecraft.server; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftEnderDragon; +import org.bukkit.event.entity.EnderDragonChangePhaseEvent; +// CraftBukkit end + +public class DragonControllerManager { + + private static final Logger a = LogManager.getLogger(); + private final EntityEnderDragon enderDragon; + private final IDragonController[] dragonControllers = new IDragonController[DragonControllerPhase.c()]; + private IDragonController currentDragonController; + + public DragonControllerManager(EntityEnderDragon entityenderdragon) { + this.enderDragon = entityenderdragon; + this.setControllerPhase(DragonControllerPhase.HOVER); + } + + public void setControllerPhase(DragonControllerPhase dragoncontrollerphase) { + if (this.currentDragonController == null || dragoncontrollerphase != this.currentDragonController.getControllerPhase()) { + if (this.currentDragonController != null) { + this.currentDragonController.e(); + } + + // CraftBukkit start - Call EnderDragonChangePhaseEvent + EnderDragonChangePhaseEvent event = new EnderDragonChangePhaseEvent( + (CraftEnderDragon) this.enderDragon.getBukkitEntity(), + (this.currentDragonController == null) ? null : CraftEnderDragon.getBukkitPhase(this.currentDragonController.getControllerPhase()), + CraftEnderDragon.getBukkitPhase(dragoncontrollerphase) + ); + this.enderDragon.world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + dragoncontrollerphase = CraftEnderDragon.getMinecraftPhase(event.getNewPhase()); + // CraftBukkit end + + this.currentDragonController = this.b(dragoncontrollerphase); + if (!this.enderDragon.world.isClientSide) { + this.enderDragon.getDataWatcher().set(EntityEnderDragon.PHASE, dragoncontrollerphase.b()); + } + + DragonControllerManager.a.debug("Dragon is now in phase {} on the {}", dragoncontrollerphase, this.enderDragon.world.isClientSide ? "client" : "server"); + this.currentDragonController.d(); + } + } + + public IDragonController a() { + return this.currentDragonController; + } + + public T b(DragonControllerPhase dragoncontrollerphase) { + int i = dragoncontrollerphase.b(); + + if (this.dragonControllers[i] == null) { + this.dragonControllers[i] = dragoncontrollerphase.a(this.enderDragon); + } + + return (T) this.dragonControllers[i]; // CraftBukkit - decompile error + } +} diff --git a/src/main/java/net/minecraft/server/DragonControllerStrafe.java b/src/main/java/net/minecraft/server/DragonControllerStrafe.java new file mode 100644 index 000000000000..141ba1e5e64a --- /dev/null +++ b/src/main/java/net/minecraft/server/DragonControllerStrafe.java @@ -0,0 +1,183 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class DragonControllerStrafe extends AbstractDragonController { + + private static final Logger b = LogManager.getLogger(); + private int c; + private PathEntity d; + private Vec3D e; + private EntityLiving f; + private boolean g; + + public DragonControllerStrafe(EntityEnderDragon entityenderdragon) { + super(entityenderdragon); + } + + public void c() { + if (this.f == null) { + DragonControllerStrafe.b.warn("Skipping player strafe phase because no player was found"); + this.a.getDragonControllerManager().setControllerPhase(DragonControllerPhase.HOLDING_PATTERN); + } else { + double d0; + double d1; + double d2; + + if (this.d != null && this.d.b()) { + d0 = this.f.locX; + d1 = this.f.locZ; + double d3 = d0 - this.a.locX; + double d4 = d1 - this.a.locZ; + + d2 = (double) MathHelper.sqrt(d3 * d3 + d4 * d4); + double d5 = Math.min(0.4000000059604645D + d2 / 80.0D - 1.0D, 10.0D); + + this.e = new Vec3D(d0, this.f.locY + d5, d1); + } + + d0 = this.e == null ? 0.0D : this.e.c(this.a.locX, this.a.locY, this.a.locZ); + if (d0 < 100.0D || d0 > 22500.0D) { + this.j(); + } + + d1 = 64.0D; + if (this.f.h(this.a) < 4096.0D) { + if (this.a.hasLineOfSight(this.f)) { + ++this.c; + Vec3D vec3d = (new Vec3D(this.f.locX - this.a.locX, 0.0D, this.f.locZ - this.a.locZ)).a(); + Vec3D vec3d1 = (new Vec3D((double) MathHelper.sin(this.a.yaw * 0.017453292F), 0.0D, (double) (-MathHelper.cos(this.a.yaw * 0.017453292F)))).a(); + float f = (float) vec3d1.b(vec3d); + float f1 = (float) (Math.acos((double) f) * 57.2957763671875D); + + f1 += 0.5F; + if (this.c >= 5 && f1 >= 0.0F && f1 < 10.0F) { + d2 = 1.0D; + Vec3D vec3d2 = this.a.f(1.0F); + double d6 = this.a.bD.locX - vec3d2.x * 1.0D; + double d7 = this.a.bD.locY + (double) (this.a.bD.length / 2.0F) + 0.5D; + double d8 = this.a.bD.locZ - vec3d2.z * 1.0D; + double d9 = this.f.locX - d6; + double d10 = this.f.locY + (double) (this.f.length / 2.0F) - (d7 + (double) (this.a.bD.length / 2.0F)); + double d11 = this.f.locZ - d8; + + this.a.world.a((EntityHuman) null, 1017, new BlockPosition(this.a), 0); + EntityDragonFireball entitydragonfireball = new EntityDragonFireball(this.a.world, this.a, d9, d10, d11); + + entitydragonfireball.setPositionRotation(d6, d7, d8, 0.0F, 0.0F); + if (new com.destroystokyo.paper.event.entity.EnderDragonShootFireballEvent((org.bukkit.entity.EnderDragon) a.getBukkitEntity(), (org.bukkit.entity.DragonFireball) entitydragonfireball.getBukkitEntity()).callEvent()) // Paper + this.a.world.addEntity(entitydragonfireball); + else entitydragonfireball.die(); // Paper + this.c = 0; + if (this.d != null) { + while (!this.d.b()) { + this.d.a(); + } + } + + this.a.getDragonControllerManager().setControllerPhase(DragonControllerPhase.HOLDING_PATTERN); + } + } else if (this.c > 0) { + --this.c; + } + } else if (this.c > 0) { + --this.c; + } + + } + } + + private void j() { + if (this.d == null || this.d.b()) { + int i = this.a.l(); + int j = i; + + if (this.a.getRandom().nextInt(8) == 0) { + this.g = !this.g; + j = i + 6; + } + + if (this.g) { + ++j; + } else { + --j; + } + + if (this.a.getEnderDragonBattle() != null && this.a.getEnderDragonBattle().c() > 0) { + j %= 12; + if (j < 0) { + j += 12; + } + } else { + j -= 12; + j &= 7; + j += 12; + } + + this.d = this.a.a(i, j, (PathPoint) null); + if (this.d != null) { + this.d.a(); + } + } + + this.k(); + } + + private void k() { + if (this.d != null && !this.d.b()) { + Vec3D vec3d = this.d.f(); + + this.d.a(); + double d0 = vec3d.x; + double d1 = vec3d.z; + + double d2; + + do { + d2 = vec3d.y + (double) (this.a.getRandom().nextFloat() * 20.0F); + } while (d2 < vec3d.y); + + this.e = new Vec3D(d0, d2, d1); + } + + } + + public void d() { + this.c = 0; + this.e = null; + this.d = null; + this.f = null; + } + + public void a(EntityLiving entityliving) { + this.f = entityliving; + int i = this.a.l(); + int j = this.a.k(this.f.locX, this.f.locY, this.f.locZ); + int k = MathHelper.floor(this.f.locX); + int l = MathHelper.floor(this.f.locZ); + double d0 = (double) k - this.a.locX; + double d1 = (double) l - this.a.locZ; + double d2 = (double) MathHelper.sqrt(d0 * d0 + d1 * d1); + double d3 = Math.min(0.4000000059604645D + d2 / 80.0D - 1.0D, 10.0D); + int i1 = MathHelper.floor(this.f.locY + d3); + PathPoint pathpoint = new PathPoint(k, i1, l); + + this.d = this.a.a(i, j, pathpoint); + if (this.d != null) { + this.d.a(); + this.k(); + } + + } + + @Nullable + public Vec3D g() { + return this.e; + } + + public DragonControllerPhase getControllerPhase() { + return DragonControllerPhase.STRAFE_PLAYER; + } +} diff --git a/src/main/java/net/minecraft/server/EULA.java b/src/main/java/net/minecraft/server/EULA.java new file mode 100644 index 000000000000..e13e17bdd2e8 --- /dev/null +++ b/src/main/java/net/minecraft/server/EULA.java @@ -0,0 +1,64 @@ +package net.minecraft.server; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.Properties; +import org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class EULA { + + private static final Logger a = LogManager.getLogger(); + private final File b; + private final boolean c; + + public EULA(File file) { + this.b = file; + this.c = SharedConstants.b || this.a(file); + } + + private boolean a(File file) { + FileInputStream fileinputstream = null; + boolean flag = false; + + try { + Properties properties = new Properties(); + + fileinputstream = new FileInputStream(file); + properties.load(fileinputstream); + flag = Boolean.parseBoolean(properties.getProperty("eula", "false")); + } catch (Exception exception) { + EULA.a.warn("Failed to load {}", file); + this.b(); + } finally { + IOUtils.closeQuietly(fileinputstream); + } + + return flag; + } + + public boolean a() { + return this.c; + } + + public void b() { + if (!SharedConstants.b) { + FileOutputStream fileoutputstream = null; + + try { + Properties properties = new Properties(); + + fileoutputstream = new FileOutputStream(this.b); + properties.setProperty("eula", "false"); + properties.store(fileoutputstream, "By changing the setting below to TRUE you are indicating your agreement to our EULA (https://account.mojang.com/documents/minecraft_eula).\nYou also agree that tacos are tasty, and the best food in the world."); // Paper - fix lag); + } catch (Exception exception) { + EULA.a.warn("Failed to save {}", this.b, exception); + } finally { + IOUtils.closeQuietly(fileoutputstream); + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/Enchantment.java b/src/main/java/net/minecraft/server/Enchantment.java new file mode 100644 index 000000000000..ab730498d11e --- /dev/null +++ b/src/main/java/net/minecraft/server/Enchantment.java @@ -0,0 +1,181 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.List; +import javax.annotation.Nullable; + +public abstract class Enchantment { + + private final EnumItemSlot[] a; + private final Enchantment.Rarity d; + @Nullable + public EnchantmentSlotType itemTarget; + @Nullable + protected String c; + + protected Enchantment(Enchantment.Rarity enchantment_rarity, EnchantmentSlotType enchantmentslottype, EnumItemSlot[] aenumitemslot) { + this.d = enchantment_rarity; + this.itemTarget = enchantmentslottype; + this.a = aenumitemslot; + } + + public List a(EntityLiving entityliving) { + List list = Lists.newArrayList(); + EnumItemSlot[] aenumitemslot = this.a; + int i = aenumitemslot.length; + + for (int j = 0; j < i; ++j) { + EnumItemSlot enumitemslot = aenumitemslot[j]; + ItemStack itemstack = entityliving.getEquipment(enumitemslot); + + if (!itemstack.isEmpty()) { + list.add(itemstack); + } + } + + return list; + } + + public Enchantment.Rarity d() { + return this.d; + } + + public int getStartLevel() { + return 1; + } + + public int getMaxLevel() { + return 1; + } + + public int a(int i) { + return 1 + i * 10; + } + + public int b(int i) { + return this.a(i) + 5; + } + + public int a(int i, DamageSource damagesource) { + return 0; + } + + public float a(int i, EnumMonsterType enummonstertype) { + return 0.0F; + } + + public final boolean b(Enchantment enchantment) { + return this.a(enchantment) && enchantment.a(this); + } + + protected boolean a(Enchantment enchantment) { + return this != enchantment; + } + + protected String f() { + if (this.c == null) { + this.c = SystemUtils.a("enchantment", IRegistry.ENCHANTMENT.getKey(this)); + } + + return this.c; + } + + public String g() { + return this.f(); + } + + public IChatBaseComponent d(int i) { + ChatMessage chatmessage = new ChatMessage(this.g(), new Object[0]); + + if (this.c()) { + chatmessage.a(EnumChatFormat.RED); + } else { + chatmessage.a(EnumChatFormat.GRAY); + } + + if (i != 1 || this.getMaxLevel() != 1) { + chatmessage.a(" ").addSibling(new ChatMessage("enchantment.level." + i, new Object[0])); + } + + return chatmessage; + } + + public boolean canEnchant(ItemStack itemstack) { + return this.itemTarget.canEnchant(itemstack.getItem()); + } + + public void a(EntityLiving entityliving, Entity entity, int i) {} + + public void b(EntityLiving entityliving, Entity entity, int i) {} + + public boolean isTreasure() { + return false; + } + + public boolean c() { + return false; + } + + public static void h() { + EnumItemSlot[] aenumitemslot = new EnumItemSlot[] { EnumItemSlot.HEAD, EnumItemSlot.CHEST, EnumItemSlot.LEGS, EnumItemSlot.FEET}; + + a("protection", new EnchantmentProtection(Enchantment.Rarity.COMMON, EnchantmentProtection.DamageType.ALL, aenumitemslot)); + a("fire_protection", new EnchantmentProtection(Enchantment.Rarity.UNCOMMON, EnchantmentProtection.DamageType.FIRE, aenumitemslot)); + a("feather_falling", new EnchantmentProtection(Enchantment.Rarity.UNCOMMON, EnchantmentProtection.DamageType.FALL, aenumitemslot)); + a("blast_protection", new EnchantmentProtection(Enchantment.Rarity.RARE, EnchantmentProtection.DamageType.EXPLOSION, aenumitemslot)); + a("projectile_protection", new EnchantmentProtection(Enchantment.Rarity.UNCOMMON, EnchantmentProtection.DamageType.PROJECTILE, aenumitemslot)); + a("respiration", new EnchantmentOxygen(Enchantment.Rarity.RARE, aenumitemslot)); + a("aqua_affinity", new EnchantmentWaterWorker(Enchantment.Rarity.RARE, aenumitemslot)); + a("thorns", new EnchantmentThorns(Enchantment.Rarity.VERY_RARE, aenumitemslot)); + a("depth_strider", new EnchantmentDepthStrider(Enchantment.Rarity.RARE, aenumitemslot)); + a("frost_walker", new EnchantmentFrostWalker(Enchantment.Rarity.RARE, new EnumItemSlot[] { EnumItemSlot.FEET})); + a("binding_curse", new EnchantmentBinding(Enchantment.Rarity.VERY_RARE, aenumitemslot)); + a("sharpness", new EnchantmentWeaponDamage(Enchantment.Rarity.COMMON, 0, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("smite", new EnchantmentWeaponDamage(Enchantment.Rarity.UNCOMMON, 1, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("bane_of_arthropods", new EnchantmentWeaponDamage(Enchantment.Rarity.UNCOMMON, 2, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("knockback", new EnchantmentKnockback(Enchantment.Rarity.UNCOMMON, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("fire_aspect", new EnchantmentFire(Enchantment.Rarity.RARE, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("looting", new EnchantmentLootBonus(Enchantment.Rarity.RARE, EnchantmentSlotType.WEAPON, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("sweeping", new EnchantmentSweeping(Enchantment.Rarity.RARE, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("efficiency", new EnchantmentDigging(Enchantment.Rarity.COMMON, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("silk_touch", new EnchantmentSilkTouch(Enchantment.Rarity.VERY_RARE, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("unbreaking", new EnchantmentDurability(Enchantment.Rarity.UNCOMMON, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("fortune", new EnchantmentLootBonus(Enchantment.Rarity.RARE, EnchantmentSlotType.DIGGER, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("power", new EnchantmentArrowDamage(Enchantment.Rarity.COMMON, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("punch", new EnchantmentArrowKnockback(Enchantment.Rarity.RARE, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("flame", new EnchantmentFlameArrows(Enchantment.Rarity.RARE, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("infinity", new EnchantmentInfiniteArrows(Enchantment.Rarity.VERY_RARE, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("luck_of_the_sea", new EnchantmentLootBonus(Enchantment.Rarity.RARE, EnchantmentSlotType.FISHING_ROD, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("lure", new EnchantmentLure(Enchantment.Rarity.RARE, EnchantmentSlotType.FISHING_ROD, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("loyalty", new EnchantmentTridentLoyalty(Enchantment.Rarity.UNCOMMON, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("impaling", new EnchantmentTridentImpaling(Enchantment.Rarity.RARE, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("riptide", new EnchantmentTridentRiptide(Enchantment.Rarity.RARE, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("channeling", new EnchantmentTridentChanneling(Enchantment.Rarity.VERY_RARE, new EnumItemSlot[] { EnumItemSlot.MAINHAND})); + a("mending", new EnchantmentMending(Enchantment.Rarity.RARE, EnumItemSlot.values())); + a("vanishing_curse", new EnchantmentVanishing(Enchantment.Rarity.VERY_RARE, EnumItemSlot.values())); + // CraftBukkit start + for (Object enchantment : IRegistry.ENCHANTMENT) { + org.bukkit.enchantments.Enchantment.registerEnchantment(new org.bukkit.craftbukkit.enchantments.CraftEnchantment((Enchantment) enchantment)); + } + // CraftBukkit end + } + + private static void a(String s, Enchantment enchantment) { + IRegistry.ENCHANTMENT.a(new MinecraftKey(s), enchantment); // CraftBukkit - decompile error + } + + public static enum Rarity { + + COMMON(10), UNCOMMON(5), RARE(2), VERY_RARE(1); + + private final int e; + + private Rarity(int i) { + this.e = i; + } + + public int a() { + return this.e; + } + } +} diff --git a/src/main/java/net/minecraft/server/EnchantmentFrostWalker.java b/src/main/java/net/minecraft/server/EnchantmentFrostWalker.java new file mode 100644 index 000000000000..dd6d92e9a1dc --- /dev/null +++ b/src/main/java/net/minecraft/server/EnchantmentFrostWalker.java @@ -0,0 +1,65 @@ +package net.minecraft.server; + +import java.util.Iterator; +// CraftBukkit start +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.event.block.EntityBlockFormEvent; +// CraftBukkit end + +public class EnchantmentFrostWalker extends Enchantment { + + public EnchantmentFrostWalker(Enchantment.Rarity enchantment_rarity, EnumItemSlot... aenumitemslot) { + super(enchantment_rarity, EnchantmentSlotType.ARMOR_FEET, aenumitemslot); + } + + public int a(int i) { + return i * 10; + } + + public int b(int i) { + return this.a(i) + 15; + } + + public boolean isTreasure() { + return true; + } + + public int getMaxLevel() { + return 2; + } + + public static void a(EntityLiving entityliving, World world, BlockPosition blockposition, int i) { + if (entityliving.onGround) { + IBlockData iblockdata = Blocks.FROSTED_ICE.getBlockData(); + float f = (float) Math.min(16, 2 + i); + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(0, 0, 0); + Iterator iterator = BlockPosition.b(blockposition.a((double) (-f), -1.0D, (double) (-f)), blockposition.a((double) f, -1.0D, (double) f)).iterator(); + + while (iterator.hasNext()) { + BlockPosition.MutableBlockPosition blockposition_mutableblockposition1 = (BlockPosition.MutableBlockPosition) iterator.next(); + + if (blockposition_mutableblockposition1.g(entityliving.locX, entityliving.locY, entityliving.locZ) <= (double) (f * f)) { + blockposition_mutableblockposition.c(blockposition_mutableblockposition1.getX(), blockposition_mutableblockposition1.getY() + 1, blockposition_mutableblockposition1.getZ()); + IBlockData iblockdata1 = world.getType(blockposition_mutableblockposition); + + if (iblockdata1.isAir()) { + IBlockData iblockdata2 = world.getType(blockposition_mutableblockposition1); + + if (iblockdata2.getMaterial() == Material.WATER && (Integer) iblockdata2.get(BlockFluids.LEVEL) == 0 && iblockdata.canPlace(world, blockposition_mutableblockposition1) && world.a(iblockdata, (BlockPosition) blockposition_mutableblockposition1)) { + // CraftBukkit Start - Call EntityBlockFormEvent for Frost Walker + if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(world, blockposition_mutableblockposition1, iblockdata, entityliving)) { + world.getBlockTickList().a(blockposition_mutableblockposition1.h(), Blocks.FROSTED_ICE, MathHelper.nextInt(entityliving.getRandom(), 60, 120)); + } + // CraftBukkit End + } + } + } + } + + } + } + + public boolean a(Enchantment enchantment) { + return super.a(enchantment) && enchantment != Enchantments.DEPTH_STRIDER; + } +} diff --git a/src/main/java/net/minecraft/server/EnchantmentManager.java b/src/main/java/net/minecraft/server/EnchantmentManager.java new file mode 100644 index 000000000000..72f0bec4e110 --- /dev/null +++ b/src/main/java/net/minecraft/server/EnchantmentManager.java @@ -0,0 +1,393 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Map.Entry; +import org.apache.commons.lang3.mutable.MutableFloat; +import org.apache.commons.lang3.mutable.MutableInt; + +public class EnchantmentManager { + + public static int getEnchantmentLevel(Enchantment enchantment, ItemStack itemstack) { + if (itemstack.isEmpty()) { + return 0; + } else { + MinecraftKey minecraftkey = IRegistry.ENCHANTMENT.getKey(enchantment); + NBTTagList nbttaglist = itemstack.getEnchantments(); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist.getCompound(i); + MinecraftKey minecraftkey1 = MinecraftKey.a(nbttagcompound.getString("id")); + + if (minecraftkey1 != null && minecraftkey1.equals(minecraftkey)) { + return nbttagcompound.getInt("lvl"); + } + } + + return 0; + } + } + + public static Map a(ItemStack itemstack) { + Map map = Maps.newLinkedHashMap(); + NBTTagList nbttaglist = itemstack.getItem() == Items.ENCHANTED_BOOK ? ItemEnchantedBook.e(itemstack) : itemstack.getEnchantments(); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist.getCompound(i); + Enchantment enchantment = (Enchantment) IRegistry.ENCHANTMENT.get(MinecraftKey.a(nbttagcompound.getString("id"))); + + if (enchantment != null) { + map.put(enchantment, nbttagcompound.getInt("lvl")); + } + } + + return map; + } + + public static void a(Map map, ItemStack itemstack) { + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = map.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + Enchantment enchantment = (Enchantment) entry.getKey(); + + if (enchantment != null) { + int i = (Integer) entry.getValue(); + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setString("id", String.valueOf(IRegistry.ENCHANTMENT.getKey(enchantment))); + nbttagcompound.setShort("lvl", (short) i); + nbttaglist.add((NBTBase) nbttagcompound); + if (itemstack.getItem() == Items.ENCHANTED_BOOK) { + ItemEnchantedBook.a(itemstack, new WeightedRandomEnchant(enchantment, i)); + } + } + } + + if (nbttaglist.isEmpty()) { + itemstack.c("Enchantments"); + } else if (itemstack.getItem() != Items.ENCHANTED_BOOK) { + itemstack.a("Enchantments", (NBTBase) nbttaglist); + } + + } + + private static void a(EnchantmentManager.a enchantmentmanager_a, ItemStack itemstack) { + if (!itemstack.isEmpty()) { + NBTTagList nbttaglist = itemstack.getEnchantments(); + + for (int i = 0; i < nbttaglist.size(); ++i) { + String s = nbttaglist.getCompound(i).getString("id"); + int j = nbttaglist.getCompound(i).getInt("lvl"); + Enchantment enchantment = (Enchantment) IRegistry.ENCHANTMENT.get(MinecraftKey.a(s)); + + if (enchantment != null) { + enchantmentmanager_a.accept(enchantment, j); + } + } + + } + } + + private static void a(EnchantmentManager.a enchantmentmanager_a, Iterable iterable) { + Iterator iterator = iterable.iterator(); + + while (iterator.hasNext()) { + ItemStack itemstack = (ItemStack) iterator.next(); + + a(enchantmentmanager_a, itemstack); + } + + } + + public static int a(Iterable iterable, DamageSource damagesource) { + MutableInt mutableint = new MutableInt(); + + a((enchantment, i) -> { + mutableint.add(enchantment.a(i, damagesource)); + }, iterable); + return mutableint.intValue(); + } + + public static float a(ItemStack itemstack, EnumMonsterType enummonstertype) { + MutableFloat mutablefloat = new MutableFloat(); + + a((enchantment, i) -> { + mutablefloat.add(enchantment.a(i, enummonstertype)); + }, itemstack); + return mutablefloat.floatValue(); + } + + public static float a(EntityLiving entityliving) { + int i = a(Enchantments.SWEEPING, entityliving); + + return i > 0 ? EnchantmentSweeping.e(i) : 0.0F; + } + + public static void a(EntityLiving entityliving, Entity entity) { + EnchantmentManager.a enchantmentmanager_a = (enchantment, i) -> { + enchantment.b(entityliving, entity, i); + }; + + if (entityliving != null) { + a(enchantmentmanager_a, entityliving.aU()); + } + + if (entity instanceof EntityHuman) { + a(enchantmentmanager_a, entityliving.getItemInMainHand()); + } + + } + + public static void b(EntityLiving entityliving, Entity entity) { + EnchantmentManager.a enchantmentmanager_a = (enchantment, i) -> { + enchantment.a(entityliving, entity, i); + }; + + if (entityliving != null) { + a(enchantmentmanager_a, entityliving.aU()); + } + + if (entityliving instanceof EntityHuman) { + a(enchantmentmanager_a, entityliving.getItemInMainHand()); + } + + } + + public static int a(Enchantment enchantment, EntityLiving entityliving) { + Iterable iterable = enchantment.a(entityliving); + + if (iterable == null) { + return 0; + } else { + int i = 0; + Iterator iterator = iterable.iterator(); + + while (iterator.hasNext()) { + ItemStack itemstack = (ItemStack) iterator.next(); + int j = getEnchantmentLevel(enchantment, itemstack); + + if (j > i) { + i = j; + } + } + + return i; + } + } + + public static int b(EntityLiving entityliving) { + return a(Enchantments.KNOCKBACK, entityliving); + } + + public static int getFireAspectEnchantmentLevel(EntityLiving entityliving) { + return a(Enchantments.FIRE_ASPECT, entityliving); + } + + public static int getOxygenEnchantmentLevel(EntityLiving entityliving) { + return a(Enchantments.OXYGEN, entityliving); + } + + public static int e(EntityLiving entityliving) { + return a(Enchantments.DEPTH_STRIDER, entityliving); + } + + public static int getDigSpeedEnchantmentLevel(EntityLiving entityliving) { + return a(Enchantments.DIG_SPEED, entityliving); + } + + public static int b(ItemStack itemstack) { + return getEnchantmentLevel(Enchantments.LUCK, itemstack); + } + + public static int c(ItemStack itemstack) { + return getEnchantmentLevel(Enchantments.LURE, itemstack); + } + + public static int g(EntityLiving entityliving) { + return a(Enchantments.LOOT_BONUS_MOBS, entityliving); + } + + public static boolean h(EntityLiving entityliving) { + return a(Enchantments.WATER_WORKER, entityliving) > 0; + } + + public static boolean i(EntityLiving entityliving) { + return a(Enchantments.FROST_WALKER, entityliving) > 0; + } + + public static boolean d(ItemStack itemstack) { + return getEnchantmentLevel(Enchantments.BINDING_CURSE, itemstack) > 0; + } + + public static boolean shouldNotDrop(ItemStack itemstack) { + return getEnchantmentLevel(Enchantments.VANISHING_CURSE, itemstack) > 0; + } + + public static int f(ItemStack itemstack) { + return getEnchantmentLevel(Enchantments.LOYALTY, itemstack); + } + + public static int g(ItemStack itemstack) { + return getEnchantmentLevel(Enchantments.RIPTIDE, itemstack); + } + + public static boolean h(ItemStack itemstack) { + return getEnchantmentLevel(Enchantments.CHANNELING, itemstack) > 0; + } + + public static ItemStack getRandomEquippedItemWithEnchant(Enchantment enchantment, EntityLiving entityliving) { return b(enchantment, entityliving); } // Paper - OBFHELPER + public static ItemStack b(Enchantment enchantment, EntityLiving entityliving) { + List list = enchantment.a(entityliving); + + if (list.isEmpty()) { + return ItemStack.a; + } else { + List list1 = Lists.newArrayList(); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + ItemStack itemstack = (ItemStack) iterator.next(); + + if (!itemstack.isEmpty() && getEnchantmentLevel(enchantment, itemstack) > 0) { + list1.add(itemstack); + } + } + + return list1.isEmpty() ? ItemStack.a : (ItemStack) list1.get(entityliving.getRandom().nextInt(list1.size())); + } + } + + public static int a(Random random, int i, int j, ItemStack itemstack) { + Item item = itemstack.getItem(); + int k = item.c(); + + if (k <= 0) { + return 0; + } else { + if (j > 15) { + j = 15; + } + + int l = random.nextInt(8) + 1 + (j >> 1) + random.nextInt(j + 1); + + return i == 0 ? Math.max(l / 3, 1) : (i == 1 ? l * 2 / 3 + 1 : Math.max(l, j * 2)); + } + } + + public static ItemStack a(Random random, ItemStack itemstack, int i, boolean flag) { + List list = b(random, itemstack, i, flag); + boolean flag1 = itemstack.getItem() == Items.BOOK; + + if (flag1) { + itemstack = new ItemStack(Items.ENCHANTED_BOOK); + } + + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + WeightedRandomEnchant weightedrandomenchant = (WeightedRandomEnchant) iterator.next(); + + if (flag1) { + ItemEnchantedBook.a(itemstack, weightedrandomenchant); + } else { + itemstack.addEnchantment(weightedrandomenchant.enchantment, weightedrandomenchant.level); + } + } + + return itemstack; + } + + public static List b(Random random, ItemStack itemstack, int i, boolean flag) { + List list = Lists.newArrayList(); + Item item = itemstack.getItem(); + int j = item.c(); + + if (j <= 0) { + return list; + } else { + i += 1 + random.nextInt(j / 4 + 1) + random.nextInt(j / 4 + 1); + float f = (random.nextFloat() + random.nextFloat() - 1.0F) * 0.15F; + + i = MathHelper.clamp(Math.round((float) i + (float) i * f), 1, Integer.MAX_VALUE); + List list1 = a(i, itemstack, flag); + + if (!list1.isEmpty()) { + list.add(WeightedRandom.a(random, list1)); + + while (random.nextInt(50) <= i) { + a(list1, (WeightedRandomEnchant) SystemUtils.a((List) list)); + if (list1.isEmpty()) { + break; + } + + list.add(WeightedRandom.a(random, list1)); + i /= 2; + } + } + + return list; + } + } + + public static void a(List list, WeightedRandomEnchant weightedrandomenchant) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + if (!weightedrandomenchant.enchantment.b(((WeightedRandomEnchant) iterator.next()).enchantment)) { + iterator.remove(); + } + } + + } + + public static boolean a(Collection collection, Enchantment enchantment) { + Iterator iterator = collection.iterator(); + + Enchantment enchantment1; + + do { + if (!iterator.hasNext()) { + return true; + } + + enchantment1 = (Enchantment) iterator.next(); + } while (enchantment1.b(enchantment)); + + return false; + } + + public static List a(int i, ItemStack itemstack, boolean flag) { + List list = Lists.newArrayList(); + Item item = itemstack.getItem(); + boolean flag1 = itemstack.getItem() == Items.BOOK; + Iterator iterator = IRegistry.ENCHANTMENT.iterator(); + + while (iterator.hasNext()) { + Enchantment enchantment = (Enchantment) iterator.next(); + + if ((!enchantment.isTreasure() || flag) && (enchantment.itemTarget.canEnchant(item) || flag1)) { + for (int j = enchantment.getMaxLevel(); j > enchantment.getStartLevel() - 1; --j) { + if (i >= enchantment.a(j) && i <= enchantment.b(j)) { + list.add(new WeightedRandomEnchant(enchantment, j)); + break; + } + } + } + } + + return list; + } + + @FunctionalInterface + interface a { + + void accept(Enchantment enchantment, int i); + } +} diff --git a/src/main/java/net/minecraft/server/EnchantmentThorns.java b/src/main/java/net/minecraft/server/EnchantmentThorns.java new file mode 100644 index 000000000000..44e649304b87 --- /dev/null +++ b/src/main/java/net/minecraft/server/EnchantmentThorns.java @@ -0,0 +1,52 @@ +package net.minecraft.server; + +import java.util.Random; + +public class EnchantmentThorns extends Enchantment { + + public EnchantmentThorns(Enchantment.Rarity enchantment_rarity, EnumItemSlot... aenumitemslot) { + super(enchantment_rarity, EnchantmentSlotType.ARMOR_CHEST, aenumitemslot); + } + + public int a(int i) { + return 10 + 20 * (i - 1); + } + + public int b(int i) { + return super.a(i) + 50; + } + + public int getMaxLevel() { + return 3; + } + + public boolean canEnchant(ItemStack itemstack) { + return itemstack.getItem() instanceof ItemArmor ? true : super.canEnchant(itemstack); + } + + public void b(EntityLiving entityliving, Entity entity, int i) { + Random random = entityliving.getRandom(); + ItemStack itemstack = EnchantmentManager.b(Enchantments.THORNS, entityliving); + + if (entity != null && a(i, random)) { // CraftBukkit + if (entity != null) { + entity.damageEntity(DamageSource.a(entityliving), (float) b(i, random)); + } + + if (!itemstack.isEmpty()) { + itemstack.damage(3, entityliving); + } + } else if (!itemstack.isEmpty()) { + itemstack.damage(1, entityliving); + } + + } + + public static boolean a(int i, Random random) { + return i <= 0 ? false : random.nextFloat() < 0.15F * (float) i; + } + + public static int b(int i, Random random) { + return i > 10 ? i - 10 : 1 + random.nextInt(4); + } +} diff --git a/src/main/java/net/minecraft/server/EnchantmentWeaponDamage.java b/src/main/java/net/minecraft/server/EnchantmentWeaponDamage.java new file mode 100644 index 000000000000..de82728b946c --- /dev/null +++ b/src/main/java/net/minecraft/server/EnchantmentWeaponDamage.java @@ -0,0 +1,52 @@ +package net.minecraft.server; + +public class EnchantmentWeaponDamage extends Enchantment { + + private static final String[] d = new String[] { "all", "undead", "arthropods"}; + private static final int[] e = new int[] { 1, 5, 5}; + private static final int[] f = new int[] { 11, 8, 8}; + private static final int[] g = new int[] { 20, 20, 20}; + public final int a; + + public EnchantmentWeaponDamage(Enchantment.Rarity enchantment_rarity, int i, EnumItemSlot... aenumitemslot) { + super(enchantment_rarity, EnchantmentSlotType.WEAPON, aenumitemslot); + this.a = i; + } + + public int a(int i) { + return EnchantmentWeaponDamage.e[this.a] + (i - 1) * EnchantmentWeaponDamage.f[this.a]; + } + + public int b(int i) { + return this.a(i) + EnchantmentWeaponDamage.g[this.a]; + } + + public int getMaxLevel() { + return 5; + } + + public float a(int i, EnumMonsterType enummonstertype) { + return this.a == 0 ? 1.0F + (float) Math.max(0, i - 1) * 0.5F : (this.a == 1 && enummonstertype == EnumMonsterType.UNDEAD ? (float) i * 2.5F : (this.a == 2 && enummonstertype == EnumMonsterType.ARTHROPOD ? (float) i * 2.5F : 0.0F)); + } + + public boolean a(Enchantment enchantment) { + return !(enchantment instanceof EnchantmentWeaponDamage); + } + + public boolean canEnchant(ItemStack itemstack) { + return itemstack.getItem() instanceof ItemAxe ? true : super.canEnchant(itemstack); + } + + public void a(EntityLiving entityliving, Entity entity, int i) { + if (entity instanceof EntityLiving) { + EntityLiving entityliving1 = (EntityLiving) entity; + + if (this.a == 2 && entityliving1.getMonsterType() == EnumMonsterType.ARTHROPOD) { + int j = 20 + entityliving.getRandom().nextInt(10 * i); + + entityliving1.addEffect(new MobEffect(MobEffects.SLOWER_MOVEMENT, j, 3), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit + } + } + + } +} diff --git a/src/main/java/net/minecraft/server/EnderDragonBattle.java b/src/main/java/net/minecraft/server/EnderDragonBattle.java new file mode 100644 index 000000000000..09eabf1235a7 --- /dev/null +++ b/src/main/java/net/minecraft/server/EnderDragonBattle.java @@ -0,0 +1,566 @@ +package net.minecraft.server; + +import com.google.common.collect.ContiguousSet; +import com.google.common.collect.DiscreteDomain; +import com.google.common.collect.Lists; +import com.google.common.collect.Range; +import com.google.common.collect.Sets; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import java.util.function.Predicate; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class EnderDragonBattle { + + private static final Logger a = LogManager.getLogger(); + private static final Predicate b = IEntitySelector.a.and(IEntitySelector.a(0.0D, 128.0D, 0.0D, 192.0D)); + public final BossBattleServer bossBattle; + private final WorldServer d; + private final List e; + private final ShapeDetector f; + private int g; + private int h; + private int i; + private int j; + private boolean k; private void setDragonKilled(boolean dragonKilled) { this.k = dragonKilled; } // Paper - OBFHELPER + private boolean l; + private UUID m; + private boolean n; private void setScanForLegacyFight(boolean scanForLegacyFight) { this.n = scanForLegacyFight; } private boolean scanForLegacyFight() { return this.n; } // Paper - OBFHELPER + private BlockPosition o; + private EnumDragonRespawn p; + private int q; + private List r; + + public EnderDragonBattle(WorldServer worldserver, NBTTagCompound nbttagcompound) { + this.bossBattle = (BossBattleServer) (new BossBattleServer(new ChatMessage("entity.minecraft.ender_dragon", new Object[0]), BossBattle.BarColor.PINK, BossBattle.BarStyle.PROGRESS)).setPlayMusic(true).c(true); + this.e = Lists.newArrayList(); + this.n = true; + // Paper start + setScanForLegacyFight(worldserver.paperConfig.scanForLegacyEnderDragon); + if (!scanForLegacyFight()) setDragonKilled(true); + // Paper end + this.d = worldserver; + if (nbttagcompound.hasKeyOfType("DragonKilled", 99)) { + if (nbttagcompound.b("DragonUUID")) { + this.m = nbttagcompound.a("DragonUUID"); + } + + this.k = nbttagcompound.getBoolean("DragonKilled"); + this.l = nbttagcompound.getBoolean("PreviouslyKilled"); + if (nbttagcompound.getBoolean("IsRespawning")) { + this.p = EnumDragonRespawn.START; + } + + if (nbttagcompound.hasKeyOfType("ExitPortalLocation", 10)) { + this.o = GameProfileSerializer.c(nbttagcompound.getCompound("ExitPortalLocation")); + } + } else { + this.k = true; + this.l = true; + } + + if (nbttagcompound.hasKeyOfType("Gateways", 9)) { + NBTTagList nbttaglist = nbttagcompound.getList("Gateways", 3); + + for (int i = 0; i < nbttaglist.size(); ++i) { + this.e.add(nbttaglist.h(i)); + } + } else { + this.e.addAll(ContiguousSet.create(Range.closedOpen(0, 20), DiscreteDomain.integers())); + Collections.shuffle(this.e, new Random(worldserver.getSeed())); + } + + this.f = ShapeDetectorBuilder.a().a(" ", " ", " ", " # ", " ", " ", " ").a(" ", " ", " ", " # ", " ", " ", " ").a(" ", " ", " ", " # ", " ", " ", " ").a(" ### ", " # # ", "# #", "# # #", "# #", " # # ", " ### ").a(" ", " ### ", " ##### ", " ##### ", " ##### ", " ### ", " ").a('#', ShapeDetectorBlock.a(BlockPredicate.a(Blocks.BEDROCK))).b(); + } + + public NBTTagCompound a() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + if (this.m != null) { + nbttagcompound.a("DragonUUID", this.m); + } + + nbttagcompound.setBoolean("DragonKilled", this.k); + nbttagcompound.setBoolean("PreviouslyKilled", this.l); + if (this.o != null) { + nbttagcompound.set("ExitPortalLocation", GameProfileSerializer.a(this.o)); + } + + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = this.e.iterator(); + + while (iterator.hasNext()) { + int i = (Integer) iterator.next(); + + nbttaglist.add((NBTBase) (new NBTTagInt(i))); + } + + nbttagcompound.set("Gateways", nbttaglist); + return nbttagcompound; + } + + public void b() { + this.bossBattle.setVisible(!this.k); + if (++this.j >= 20) { + this.k(); + this.j = 0; + } + + EnderDragonBattle.b enderdragonbattle_b = new EnderDragonBattle.b(); + + if (!this.bossBattle.getPlayers().isEmpty()) { + if (this.n && enderdragonbattle_b.a()) { + this.g(); + this.n = false; + } + + if (this.p != null) { + if (this.r == null && enderdragonbattle_b.a()) { + this.p = null; + this.e(); + } + + this.p.a(this.d, this, this.r, this.q++, this.o); + } + + if (!this.k) { + if ((this.m == null || ++this.g >= 1200) && enderdragonbattle_b.a()) { + this.h(); + this.g = 0; + } + + if (++this.i >= 100 && enderdragonbattle_b.a()) { + this.l(); + this.i = 0; + } + } + } + + } + + private void g() { + EnderDragonBattle.a.info("Scanning for legacy world dragon fight..."); + boolean flag = this.i(); + + if (flag) { + EnderDragonBattle.a.info("Found that the dragon has been killed in this world already."); + this.l = true; + } else { + EnderDragonBattle.a.info("Found that the dragon has not yet been killed in this world."); + this.l = false; + this.a(false); + } + + List list = this.d.a(EntityEnderDragon.class, IEntitySelector.a); + + if (list.isEmpty()) { + this.k = true; + } else { + EntityEnderDragon entityenderdragon = (EntityEnderDragon) list.get(0); + + this.m = entityenderdragon.getUniqueID(); + EnderDragonBattle.a.info("Found that there's a dragon still alive ({})", entityenderdragon); + this.k = false; + if (!flag) { + EnderDragonBattle.a.info("But we didn't have a portal, let's remove it."); + entityenderdragon.die(); + this.m = null; + } + } + + if (!this.l && this.k) { + this.k = false; + } + + } + + private void h() { + List list = this.d.a(EntityEnderDragon.class, IEntitySelector.a); + + if (list.isEmpty()) { + EnderDragonBattle.a.debug("Haven't seen the dragon, respawning it"); + this.n(); + } else { + EnderDragonBattle.a.debug("Haven't seen our dragon, but found another one to use."); + this.m = ((EntityEnderDragon) list.get(0)).getUniqueID(); + } + + } + + protected void a(EnumDragonRespawn enumdragonrespawn) { + if (this.p == null) { + throw new IllegalStateException("Dragon respawn isn't in progress, can't skip ahead in the animation."); + } else { + this.q = 0; + if (enumdragonrespawn == EnumDragonRespawn.END) { + this.p = null; + this.k = false; + EntityEnderDragon entityenderdragon = this.n(); + Iterator iterator = this.bossBattle.getPlayers().iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + CriterionTriggers.n.a(entityplayer, (Entity) entityenderdragon); + } + } else { + this.p = enumdragonrespawn; + } + + } + } + + private boolean i() { + for (int i = -8; i <= 8; ++i) { + int j = -8; + + label27: + while (j <= 8) { + Chunk chunk = this.d.getChunkAt(i, j); + Iterator iterator = chunk.getTileEntities().values().iterator(); + + TileEntity tileentity; + + do { + if (!iterator.hasNext()) { + ++j; + continue label27; + } + + tileentity = (TileEntity) iterator.next(); + } while (!(tileentity instanceof TileEntityEnderPortal)); + + return true; + } + } + + return false; + } + + @Nullable + private ShapeDetector.ShapeDetectorCollection j() { + int i; + int j; + + for (i = -8; i <= 8; ++i) { + for (j = -8; j <= 8; ++j) { + Chunk chunk = this.d.getChunkAt(i, j); + Iterator iterator = chunk.getTileEntities().values().iterator(); + + while (iterator.hasNext()) { + TileEntity tileentity = (TileEntity) iterator.next(); + + if (tileentity instanceof TileEntityEnderPortal) { + ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = this.f.a(this.d, tileentity.getPosition()); + + if (shapedetector_shapedetectorcollection != null) { + BlockPosition blockposition = shapedetector_shapedetectorcollection.a(3, 3, 3).getPosition(); + + if (this.o == null && blockposition.getX() == 0 && blockposition.getZ() == 0) { + this.o = blockposition; + } + + return shapedetector_shapedetectorcollection; + } + } + } + } + } + + i = this.d.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING, WorldGenEndTrophy.a).getY(); + + for (j = i; j >= 0; --j) { + ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection1 = this.f.a(this.d, new BlockPosition(WorldGenEndTrophy.a.getX(), j, WorldGenEndTrophy.a.getZ())); + + if (shapedetector_shapedetectorcollection1 != null) { + if (this.o == null) { + this.o = shapedetector_shapedetectorcollection1.a(3, 3, 3).getPosition(); + } + + return shapedetector_shapedetectorcollection1; + } + } + + return null; + } + + private boolean a(int i, int j, int k, int l) { + if (this.b(i, j, k, l)) { + return true; + } else { + this.c(i, j, k, l); + return false; + } + } + + private boolean b(int i, int j, int k, int l) { + boolean flag = true; + + for (int i1 = i; i1 <= j; ++i1) { + for (int j1 = k; j1 <= l; ++j1) { + Chunk chunk = this.d.getChunkAt(i1, j1); + + flag &= chunk.i() == ChunkStatus.POSTPROCESSED; + } + } + + return flag; + } + + private void c(int i, int j, int k, int l) { + int i1; + + for (i1 = i - 1; i1 <= j + 1; ++i1) { + this.d.getChunkAt(i1, k - 1); + this.d.getChunkAt(i1, l + 1); + } + + for (i1 = k - 1; i1 <= l + 1; ++i1) { + this.d.getChunkAt(i - 1, i1); + this.d.getChunkAt(j + 1, i1); + } + + } + + private void k() { + Set set = Sets.newHashSet(); + Iterator iterator = this.d.b(EntityPlayer.class, EnderDragonBattle.b).iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + this.bossBattle.addPlayer(entityplayer); + set.add(entityplayer); + } + + Set set1 = Sets.newHashSet(this.bossBattle.getPlayers()); + + set1.removeAll(set); + Iterator iterator1 = set1.iterator(); + + while (iterator1.hasNext()) { + EntityPlayer entityplayer1 = (EntityPlayer) iterator1.next(); + + this.bossBattle.removePlayer(entityplayer1); + } + + } + + private void l() { + this.i = 0; + this.h = 0; + WorldGenEnder.Spike[] aworldgenender_spike = WorldGenDecoratorSpike.a(this.d); + int i = aworldgenender_spike.length; + + for (int j = 0; j < i; ++j) { + WorldGenEnder.Spike worldgenender_spike = aworldgenender_spike[j]; + + this.h += this.d.a(EntityEnderCrystal.class, worldgenender_spike.f()).size(); + } + + EnderDragonBattle.a.debug("Found {} end crystals still alive", this.h); + } + + public void a(EntityEnderDragon entityenderdragon) { + if (entityenderdragon.getUniqueID().equals(this.m)) { + this.bossBattle.setProgress(0.0F); + this.bossBattle.setVisible(false); + this.a(true); + this.m(); + if (!this.l) { + this.d.setTypeUpdate(this.d.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING, WorldGenEndTrophy.a), Blocks.DRAGON_EGG.getBlockData()); + } + + this.l = true; + this.k = true; + } + + } + + private void m() { + if (!this.e.isEmpty()) { + int i = (Integer) this.e.remove(this.e.size() - 1); + int j = (int) (96.0D * Math.cos(2.0D * (-3.141592653589793D + 0.15707963267948966D * (double) i))); + int k = (int) (96.0D * Math.sin(2.0D * (-3.141592653589793D + 0.15707963267948966D * (double) i))); + + this.a(new BlockPosition(j, 75, k)); + } + } + + private void a(BlockPosition blockposition) { + this.d.triggerEffect(3000, blockposition, 0); + WorldGenerator.ax.generate(this.d, this.d.getChunkProvider().getChunkGenerator(), new Random(), blockposition, new WorldGenEndGatewayConfiguration(false)); + } + + private void a(boolean flag) { + WorldGenEndTrophy worldgenendtrophy = new WorldGenEndTrophy(flag); + + if (this.o == null) { + for (this.o = this.d.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, WorldGenEndTrophy.a).down(); this.d.getType(this.o).getBlock() == Blocks.BEDROCK && this.o.getY() > this.d.getSeaLevel(); this.o = this.o.down()) { + ; + } + } + + worldgenendtrophy.a(this.d, this.d.getChunkProvider().getChunkGenerator(), new Random(), this.o, WorldGenFeatureConfiguration.e); + } + + private EntityEnderDragon n() { + this.d.getChunkAtWorldCoords(new BlockPosition(0, 128, 0)); + EntityEnderDragon entityenderdragon = EntityTypes.ENDER_DRAGON.create(this.d); // Paper + + entityenderdragon.getDragonControllerManager().setControllerPhase(DragonControllerPhase.HOLDING_PATTERN); + entityenderdragon.setPositionRotation(0.0D, 128.0D, 0.0D, this.d.random.nextFloat() * 360.0F, 0.0F); + this.d.addEntity(entityenderdragon); + this.m = entityenderdragon.getUniqueID(); + return entityenderdragon; + } + + public void b(EntityEnderDragon entityenderdragon) { + if (entityenderdragon.getUniqueID().equals(this.m)) { + this.bossBattle.setProgress(entityenderdragon.getHealth() / entityenderdragon.getMaxHealth()); + this.g = 0; + if (entityenderdragon.hasCustomName()) { + this.bossBattle.a(entityenderdragon.getScoreboardDisplayName()); + } + } + + } + + public int c() { + return this.h; + } + + public void a(EntityEnderCrystal entityendercrystal, DamageSource damagesource) { + if (this.p != null && this.r.contains(entityendercrystal)) { + EnderDragonBattle.a.debug("Aborting respawn sequence"); + this.p = null; + this.q = 0; + this.f(); + this.a(true); + } else { + this.l(); + Entity entity = this.d.getEntity(this.m); + + if (entity instanceof EntityEnderDragon) { + ((EntityEnderDragon) entity).a(entityendercrystal, new BlockPosition(entityendercrystal), damagesource); + } + } + + } + + public boolean d() { + return this.l; + } + + public void e() { + if (this.k && this.p == null) { + BlockPosition blockposition = this.o; + + if (blockposition == null) { + EnderDragonBattle.a.debug("Tried to respawn, but need to find the portal first."); + ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = this.j(); + + if (shapedetector_shapedetectorcollection == null) { + EnderDragonBattle.a.debug("Couldn't find a portal, so we made one."); + this.a(true); + } else { + EnderDragonBattle.a.debug("Found the exit portal & temporarily using it."); + } + + blockposition = this.o; + } + + List list = Lists.newArrayList(); + BlockPosition blockposition1 = blockposition.up(1); + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + List list1 = this.d.a(EntityEnderCrystal.class, new AxisAlignedBB(blockposition1.shift(enumdirection, 2))); + + if (list1.isEmpty()) { + return; + } + + list.addAll(list1); + } + + EnderDragonBattle.a.debug("Found all crystals, respawning dragon."); + this.a((List) list); + } + + } + + private void a(List list) { + if (this.k && this.p == null) { + for (ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = this.j(); shapedetector_shapedetectorcollection != null; shapedetector_shapedetectorcollection = this.j()) { + for (int i = 0; i < this.f.c(); ++i) { + for (int j = 0; j < this.f.b(); ++j) { + for (int k = 0; k < this.f.a(); ++k) { + ShapeDetectorBlock shapedetectorblock = shapedetector_shapedetectorcollection.a(i, j, k); + + if (shapedetectorblock.a().getBlock() == Blocks.BEDROCK || shapedetectorblock.a().getBlock() == Blocks.END_PORTAL) { + this.d.setTypeUpdate(shapedetectorblock.getPosition(), Blocks.END_STONE.getBlockData()); + } + } + } + } + } + + this.p = EnumDragonRespawn.START; + this.q = 0; + this.a(false); + this.r = list; + } + + } + + public void f() { + WorldGenEnder.Spike[] aworldgenender_spike = WorldGenDecoratorSpike.a(this.d); + int i = aworldgenender_spike.length; + + for (int j = 0; j < i; ++j) { + WorldGenEnder.Spike worldgenender_spike = aworldgenender_spike[j]; + List list = this.d.a(EntityEnderCrystal.class, worldgenender_spike.f()); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityEnderCrystal entityendercrystal = (EntityEnderCrystal) iterator.next(); + + entityendercrystal.setInvulnerable(false); + entityendercrystal.setBeamTarget((BlockPosition) null); + } + } + + } + + class b { + + private EnderDragonBattle.LoadState b; + + private b() { + this.b = EnderDragonBattle.LoadState.UNKNOWN; + } + + private boolean a() { + if (this.b == EnderDragonBattle.LoadState.UNKNOWN) { + this.b = EnderDragonBattle.this.a(-8, 8, -8, 8) ? EnderDragonBattle.LoadState.LOADED : EnderDragonBattle.LoadState.NOT_LOADED; + } + + return this.b == EnderDragonBattle.LoadState.LOADED; + } + } + + static enum LoadState { + + UNKNOWN, NOT_LOADED, LOADED; + + private LoadState() {} + } +} diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java new file mode 100644 index 000000000000..88298680fbc3 --- /dev/null +++ b/src/main/java/net/minecraft/server/Entity.java @@ -0,0 +1,3237 @@ +package net.minecraft.server; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Server; +import org.bukkit.TravelAgent; +import org.bukkit.block.BlockFace; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Hanging; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Vehicle; +import co.aikar.timings.MinecraftTimings; // Paper +import co.aikar.timings.Timing; // Paper +import org.bukkit.event.entity.EntityCombustByEntityEvent; +import org.bukkit.event.hanging.HangingBreakByEntityEvent; +import org.bukkit.event.vehicle.VehicleBlockCollisionEvent; +import org.bukkit.event.vehicle.VehicleEnterEvent; +import org.bukkit.event.vehicle.VehicleExitEvent; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityAirChangeEvent; +import org.bukkit.event.entity.EntityCombustEvent; +import org.bukkit.event.entity.EntityDropItemEvent; +import org.bukkit.event.entity.EntityPortalEvent; +import org.bukkit.plugin.PluginManager; +// CraftBukkit end + +public abstract class Entity implements INamableTileEntity, ICommandListener, KeyedObject { // Paper + + // CraftBukkit start + private static final int CURRENT_LEVEL = 2; + // Paper start + public static Random SHARED_RANDOM = new Random() { + private boolean locked = false; + @Override + public synchronized void setSeed(long seed) { + if (locked) { + LogManager.getLogger().error("Ignoring setSeed on Entity.SHARED_RANDOM", new Throwable()); + } else { + super.setSeed(seed); + locked = true; + } + } + }; + List entitySlice = null; + // Paper end + static boolean isLevelAtLeast(NBTTagCompound tag, int level) { + return tag.hasKey("Bukkit.updateLevel") && tag.getInt("Bukkit.updateLevel") >= level; + } + + public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper + protected CraftEntity bukkitEntity; + + EntityTrackerEntry tracker; // Paper + Throwable addedToWorldStack; // Paper - entity debug + public CraftEntity getBukkitEntity() { + if (bukkitEntity == null) { + bukkitEntity = CraftEntity.getEntity(world.getServer(), this); + } + return bukkitEntity; + } + + @Override + public CommandSender getBukkitSender(CommandListenerWrapper wrapper) { + return getBukkitEntity(); + } + // CraftBukkit end + + protected static final Logger i = LogManager.getLogger(); + private static final List a = Collections.emptyList(); + private static final AxisAlignedBB b = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D); + private static double c = 1.0D; + private static int entityCount = 1; // Paper - MC-111480 - ID 0 is treated as special for DataWatchers, start 1 + private final EntityTypes g; public EntityTypes getEntityType() { return g; } // Paper - OBFHELPER + private int id; + public boolean j; public boolean blocksEntitySpawning() { return j; } // Paper - OBFHELPER + public final List passengers; + protected int k; + private Entity vehicle; + public boolean attachedToPlayer; + public World world; + public double lastX; + public double lastY; + public double lastZ; + public double locX; + public double locY; + public double locZ; + public double motX; + public double motY; + public double motZ; + public float yaw; + public float pitch; + public float lastYaw; + public float lastPitch; + private AxisAlignedBB boundingBox; + public boolean onGround; + public boolean positionChanged; + public boolean C; + public boolean D; + public boolean velocityChanged; + protected boolean F; + private boolean az; + public boolean dead; + public boolean shouldBeRemoved; // Paper + public boolean hasBeenCounted = false; // Paper + public float width; + public float length; + public float J; + public float K; + public float L; + public float fallDistance; + private float aA; + private float aB; + public double N; + public double O; + public double P; + public float Q; + public boolean noclip; + public float S; + protected Random random; + public int ticksLived; + public int fireTicks; + public boolean inWater; + protected double W; + protected boolean X; + public int noDamageTicks; + protected boolean justCreated; + protected boolean fireProof; + protected DataWatcher datawatcher; + protected static final DataWatcherObject ac = DataWatcher.a(Entity.class, DataWatcherRegistry.a); + private static final DataWatcherObject aD = DataWatcher.a(Entity.class, DataWatcherRegistry.b); + private static final DataWatcherObject> aE = DataWatcher.a(Entity.class, DataWatcherRegistry.f); + private static final DataWatcherObject aF = DataWatcher.a(Entity.class, DataWatcherRegistry.i); + private static final DataWatcherObject aG = DataWatcher.a(Entity.class, DataWatcherRegistry.i); + private static final DataWatcherObject aH = DataWatcher.a(Entity.class, DataWatcherRegistry.i); + public boolean inChunk; public boolean isAddedToChunk() { return inChunk; } // Paper - OBFHELPER + public int chunkX; public int getChunkX() { return chunkX; } // Paper - OBFHELPER + public int chunkY; public int getChunkY() { return chunkY; } // Paper - OBFHELPER + public int chunkZ; public int getChunkZ() { return chunkZ; } // Paper - OBFHELPER + public boolean ak; + public boolean impulse; + public int portalCooldown; + protected boolean an; public boolean inPortal() { return an; } // Paper - OBFHELPER + protected int ao; + public DimensionManager dimension; + protected BlockPosition aq; + protected Vec3D ar; + protected EnumDirection as; + private boolean invulnerable; + protected UUID uniqueID; + protected String au; + public boolean glowing; + private final Set aJ; + private boolean aK; + private final double[] aL; + private long aM; + // CraftBukkit start + public boolean persist = true; + public boolean valid; + public org.bukkit.projectiles.ProjectileSource projectileSource; // For projectiles only + public boolean forceExplosionKnockback; // SPIGOT-949 + public Timing tickTimer = MinecraftTimings.getEntityTimings(this); // Paper + public Location origin; // Paper + // Spigot start + public final byte activationType = org.spigotmc.ActivationRange.initializeEntityActivationType(this); + public final boolean defaultActivationState; + public long activatedTick = Integer.MIN_VALUE; + public boolean fromMobSpawner; + public boolean spawnedViaMobSpawner; // Paper - Yes this name is similar to above, upstream took the better one + protected int numCollisions = 0; // Paper + public void inactiveTick() { } + // Spigot end + + public float getBukkitYaw() { + return this.yaw; + } + // CraftBukkit end + + public Entity(EntityTypes entitytypes, World world) { + this.id = Entity.entityCount++; + this.passengers = Lists.newArrayList(); + this.boundingBox = Entity.b; + this.width = 0.6F; + this.length = 1.8F; + this.aA = 1.0F; + this.aB = 1.0F; + this.random = SHARED_RANDOM; // Paper + this.fireTicks = -this.getMaxFireTicks(); + this.justCreated = true; + this.uniqueID = MathHelper.a(java.util.concurrent.ThreadLocalRandom.current()); // Paper + this.au = this.uniqueID.toString(); + this.aJ = Sets.newHashSet(); + this.aL = new double[] { 0.0D, 0.0D, 0.0D}; + this.g = entitytypes; + this.world = world; + this.setPosition(0.0D, 0.0D, 0.0D); + if (world != null) { + this.dimension = world.worldProvider.getDimensionManager(); + // Spigot start + this.defaultActivationState = org.spigotmc.ActivationRange.initializeEntityActivationState(this, world.spigotConfig); + } else { + this.defaultActivationState = false; + } + // Spigot end + + this.datawatcher = new DataWatcher(this); + this.datawatcher.register(Entity.ac, (byte) 0); + this.datawatcher.register(Entity.aD, this.bf()); + this.datawatcher.register(Entity.aF, false); + this.datawatcher.register(Entity.aE, Optional.empty()); + this.datawatcher.register(Entity.aG, false); + this.datawatcher.register(Entity.aH, false); + this.x_(); + } + + public EntityTypes P() { + return this.g; + } + + public int getId() { + return this.id; + } + + public void e(int i) { + this.id = i; + } + + public Set getScoreboardTags() { + return this.aJ; + } + + public boolean addScoreboardTag(String s) { + return this.aJ.size() >= 1024 ? false : this.aJ.add(s); + } + + public boolean removeScoreboardTag(String s) { + return this.aJ.remove(s); + } + + public void killEntity() { + this.die(); + } + + protected abstract void x_(); + + public DataWatcher getDataWatcher() { + return this.datawatcher; + } + + public boolean equals(Object object) { + return object instanceof Entity ? ((Entity) object).id == this.id : false; + } + + public int hashCode() { + return this.id; + } + + public void die() { + this.dead = true; + } + + public void b(boolean flag) {} + + public void setSize(float f, float f1) { + if (f != this.width || f1 != this.length) { + float f2 = this.width; + + this.width = f; + this.length = f1; + if (this.width < f2) { + double d0 = (double) f / 2.0D; + + this.a(new AxisAlignedBB(this.locX - d0, this.locY, this.locZ - d0, this.locX + d0, this.locY + (double) this.length, this.locZ + d0)); + return; + } + + AxisAlignedBB axisalignedbb = this.getBoundingBox(); + + this.a(new AxisAlignedBB(axisalignedbb.minX, axisalignedbb.minY, axisalignedbb.minZ, axisalignedbb.minX + (double) this.width, axisalignedbb.minY + (double) this.length, axisalignedbb.minZ + (double) this.width)); + if (this.width > f2 && !this.justCreated && !this.world.isClientSide) { + this.move(EnumMoveType.SELF, (double) (f2 - this.width), 0.0D, (double) (f2 - this.width)); + } + } + + } + + protected void setYawPitch(float f, float f1) { + // CraftBukkit start - yaw was sometimes set to NaN, so we need to set it back to 0 + if (Float.isNaN(f)) { + f = 0; + } + + if (f == Float.POSITIVE_INFINITY || f == Float.NEGATIVE_INFINITY) { + if (this instanceof EntityPlayer) { + this.world.getServer().getLogger().warning(this.getName() + " was caught trying to crash the server with an invalid yaw"); + ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Infinite yaw (Hacking?)"); + } + f = 0; + } + + // pitch was sometimes set to NaN, so we need to set it back to 0 + if (Float.isNaN(f1)) { + f1 = 0; + } + + if (f1 == Float.POSITIVE_INFINITY || f1 == Float.NEGATIVE_INFINITY) { + if (this instanceof EntityPlayer) { + this.world.getServer().getLogger().warning(this.getName() + " was caught trying to crash the server with an invalid pitch"); + ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Infinite pitch (Hacking?)"); + } + f1 = 0; + } + // CraftBukkit end + + this.yaw = f % 360.0F; + this.pitch = f1 % 360.0F; + } + + public void setPosition(double d0, double d1, double d2) { + this.locX = d0; + this.locY = d1; + this.locZ = d2; + float f = this.width / 2.0F; + float f1 = this.length; + + this.a(new AxisAlignedBB(d0 - (double) f, d1, d2 - (double) f, d0 + (double) f, d1 + (double) f1, d2 + (double) f)); + if (valid) world.entityJoinedWorld(this, false); // CraftBukkit + } + + public void tick() { + if (!this.world.isClientSide) { + this.setFlag(6, this.bc()); + } + + this.W(); + } + + // CraftBukkit start + public void postTick() { + // No clean way to break out of ticking once the entity has been copied to a new world, so instead we move the portalling later in the tick cycle + if (!this.world.isClientSide && this.world instanceof WorldServer) { + this.world.methodProfiler.enter("portal"); + if (this.an) { + MinecraftServer minecraftserver = this.world.getMinecraftServer(); + + if (true || minecraftserver.getAllowNether()) { // CraftBukkit + if (!this.isPassenger()) { + int i = this.X(); + + if (this.ao++ >= i) { + this.ao = i; + this.portalCooldown = this.aQ(); + DimensionManager dimensionmanager; + + if (this.world.worldProvider.getDimensionManager() == DimensionManager.NETHER) { + dimensionmanager = DimensionManager.OVERWORLD; + } else { + dimensionmanager = DimensionManager.NETHER; + } + + this.a(dimensionmanager); + } + } + + this.an = false; + } + } else { + if (this.ao > 0) { + this.ao -= 4; + } + + if (this.ao < 0) { + this.ao = 0; + } + } + + this.E(); + this.world.methodProfiler.exit(); + } + } + // CraftBukkit end + + public void W() { + this.world.methodProfiler.enter("entityBaseTick"); + if (this.isPassenger() && this.getVehicle().dead) { + this.stopRiding(); + } + + if (this.k > 0) { + --this.k; + } + + this.J = this.K; + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + this.lastPitch = this.pitch; + this.lastYaw = this.yaw; + // Moved up to postTick + /* + if (!this.world.isClientSide && this.world instanceof WorldServer) { + this.world.methodProfiler.enter("portal"); + if (this.an) { + MinecraftServer minecraftserver = this.world.getMinecraftServer(); + + if (minecraftserver.getAllowNether()) { + if (!this.isPassenger()) { + int i = this.X(); + + if (this.ao++ >= i) { + this.ao = i; + this.portalCooldown = this.aQ(); + DimensionManager dimensionmanager; + + if (this.world.worldProvider.getDimensionManager() == DimensionManager.NETHER) { + dimensionmanager = DimensionManager.OVERWORLD; + } else { + dimensionmanager = DimensionManager.NETHER; + } + + this.a(dimensionmanager); + } + } + + this.an = false; + } + } else { + if (this.ao > 0) { + this.ao -= 4; + } + + if (this.ao < 0) { + this.ao = 0; + } + } + + this.E(); + this.world.methodProfiler.exit(); + } + */ + + this.av(); + this.r(); + if (this.world.isClientSide) { + this.extinguish(); + } else if (this.fireTicks > 0) { + if (this.fireProof) { + this.fireTicks -= 4; + if (this.fireTicks < 0) { + this.extinguish(); + } + } else { + if (this.fireTicks % 20 == 0) { + this.damageEntity(DamageSource.BURN, 1.0F); + } + + --this.fireTicks; + } + } + + if (this.ax()) { + this.burnFromLava(); + this.fallDistance *= 0.5F; + } + + // Paper start - Configurable nether ceiling damage + // Extracted to own function + /* + if (this.locY < -64.0D) { + this.aa(); + } + */ + this.checkAndDoHeightDamage(); + // Paper end + + if (!this.world.isClientSide) { + this.setFlag(0, this.fireTicks > 0); + } + + this.justCreated = false; + this.world.methodProfiler.exit(); + } + + // Paper start + protected void checkAndDoHeightDamage() { + if (this.locY < -64.0D || (this.world.paperConfig.netherVoidTopDamage && this.world.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER && this.locY >= 128.0D)) { + this.kill(); + } + } + // Paper end + + protected void E() { + if (this.portalCooldown > 0) { + --this.portalCooldown; + } + + } + + public int X() { + return 1; + } + + protected void burnFromLava() { + if (!this.fireProof) { + // CraftBukkit start - Fallen in lava TODO: this event spams! + if (this instanceof EntityLiving && fireTicks <= 0) { + // not on fire yet + // TODO: shouldn't be sending null for the block + org.bukkit.block.Block damager = null; // ((WorldServer) this.l).getWorld().getBlockAt(i, j, k); + org.bukkit.entity.Entity damagee = this.getBukkitEntity(); + EntityCombustEvent combustEvent = new org.bukkit.event.entity.EntityCombustByBlockEvent(damager, damagee, 15); + this.world.getServer().getPluginManager().callEvent(combustEvent); + + if (!combustEvent.isCancelled()) { + this.setOnFire(combustEvent.getDuration(), false); + } + } else { + // This will be called every single tick the entity is in lava, so don't throw an event + this.setOnFire(15, false); + } + // CraftBukkit end - we also don't throw an event unless the object in lava is living, to save on some event calls + this.damageEntity(DamageSource.LAVA, 4.0F); + } + } + + public void setOnFire(int i) { + // CraftBukkit start + this.setOnFire(i, true); + } + + public void setOnFire(int i, boolean callEvent) { + if (callEvent) { + EntityCombustEvent event = new EntityCombustEvent(this.getBukkitEntity(), i); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + + i = event.getDuration(); + } + // CraftBukkit end + int j = i * 20; + + if (this instanceof EntityLiving) { + j = EnchantmentProtection.a((EntityLiving) this, j); + } + + if (this.fireTicks < j) { + this.fireTicks = j; + } + + } + + public void extinguish() { + this.fireTicks = 0; + } + + protected final void kill() { this.aa(); } // Paper - OBFHELPER + protected void aa() { + this.die(); + } + + public boolean c(double d0, double d1, double d2) { + return this.b(this.getBoundingBox().d(d0, d1, d2)); + } + + private boolean b(AxisAlignedBB axisalignedbb) { + return this.world.getCubes(this, axisalignedbb) && !this.world.containsLiquid(axisalignedbb); + } + + public void move(EnumMoveType enummovetype, double d0, double d1, double d2) { + if (this.noclip) { + this.a(this.getBoundingBox().d(d0, d1, d2)); + this.recalcPosition(); + } else { + if (enummovetype == EnumMoveType.PISTON) { + this.activatedTick = MinecraftServer.currentTick + 20; // Paper + long i = this.world.getTime(); + + if (i != this.aM) { + Arrays.fill(this.aL, 0.0D); + this.aM = i; + } + + int j; + double d3; + + if (d0 != 0.0D) { + j = EnumDirection.EnumAxis.X.ordinal(); + d3 = MathHelper.a(d0 + this.aL[j], -0.51D, 0.51D); + d0 = d3 - this.aL[j]; + this.aL[j] = d3; + if (Math.abs(d0) <= 9.999999747378752E-6D) { + return; + } + } else if (d1 != 0.0D) { + j = EnumDirection.EnumAxis.Y.ordinal(); + d3 = MathHelper.a(d1 + this.aL[j], -0.51D, 0.51D); + d1 = d3 - this.aL[j]; + this.aL[j] = d3; + if (Math.abs(d1) <= 9.999999747378752E-6D) { + return; + } + } else { + if (d2 == 0.0D) { + return; + } + + j = EnumDirection.EnumAxis.Z.ordinal(); + d3 = MathHelper.a(d2 + this.aL[j], -0.51D, 0.51D); + d2 = d3 - this.aL[j]; + this.aL[j] = d3; + if (Math.abs(d2) <= 9.999999747378752E-6D) { + return; + } + } + } + + this.world.methodProfiler.enter("move"); + double d4 = this.locX; + double d5 = this.locY; + double d6 = this.locZ; + + if (this.F) { + this.F = false; + d0 *= 0.25D; + d1 *= 0.05000000074505806D; + d2 *= 0.25D; + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + } + + double d7 = d0; + double d8 = d1; + double d9 = d2; + + if ((enummovetype == EnumMoveType.SELF || enummovetype == EnumMoveType.PLAYER) && this.onGround && this.isSneaking() && this instanceof EntityHuman) { + for (double d10 = 0.05D; d0 != 0.0D && this.world.getCubes(this, this.getBoundingBox().d(d0, (double) (-this.Q), 0.0D)); d7 = d0) { + if (d0 < 0.05D && d0 >= -0.05D) { + d0 = 0.0D; + } else if (d0 > 0.0D) { + d0 -= 0.05D; + } else { + d0 += 0.05D; + } + } + + for (; d2 != 0.0D && this.world.getCubes(this, this.getBoundingBox().d(0.0D, (double) (-this.Q), d2)); d9 = d2) { + if (d2 < 0.05D && d2 >= -0.05D) { + d2 = 0.0D; + } else if (d2 > 0.0D) { + d2 -= 0.05D; + } else { + d2 += 0.05D; + } + } + + for (; d0 != 0.0D && d2 != 0.0D && this.world.getCubes(this, this.getBoundingBox().d(d0, (double) (-this.Q), d2)); d9 = d2) { + if (d0 < 0.05D && d0 >= -0.05D) { + d0 = 0.0D; + } else if (d0 > 0.0D) { + d0 -= 0.05D; + } else { + d0 += 0.05D; + } + + d7 = d0; + if (d2 < 0.05D && d2 >= -0.05D) { + d2 = 0.0D; + } else if (d2 > 0.0D) { + d2 -= 0.05D; + } else { + d2 += 0.05D; + } + } + } + + AxisAlignedBB axisalignedbb = this.getBoundingBox(); + + if (d0 != 0.0D || d1 != 0.0D || d2 != 0.0D) { + StreamAccumulator streamaccumulator = new StreamAccumulator<>(this.world.a(this, this.getBoundingBox(), d0, d1, d2)); + + if (d1 != 0.0D) { + d1 = VoxelShapes.a(EnumDirection.EnumAxis.Y, this.getBoundingBox(), streamaccumulator.a(), d1); + this.a(this.getBoundingBox().d(0.0D, d1, 0.0D)); + } + + if (d0 != 0.0D) { + d0 = VoxelShapes.a(EnumDirection.EnumAxis.X, this.getBoundingBox(), streamaccumulator.a(), d0); + if (d0 != 0.0D) { + this.a(this.getBoundingBox().d(d0, 0.0D, 0.0D)); + } + } + + if (d2 != 0.0D) { + d2 = VoxelShapes.a(EnumDirection.EnumAxis.Z, this.getBoundingBox(), streamaccumulator.a(), d2); + if (d2 != 0.0D) { + this.a(this.getBoundingBox().d(0.0D, 0.0D, d2)); + } + } + } + + boolean flag = this.onGround || d1 != d8 && d1 < 0.0D; // CraftBukkit - decompile error + double d11; + + if (this.Q > 0.0F && flag && (d7 != d0 || d9 != d2)) { + double d12 = d0; + double d13 = d1; + double d14 = d2; + AxisAlignedBB axisalignedbb1 = this.getBoundingBox(); + + this.a(axisalignedbb); + d0 = d7; + d1 = (double) this.Q; + d2 = d9; + if (d7 != 0.0D || d1 != 0.0D || d9 != 0.0D) { + StreamAccumulator streamaccumulator1 = new StreamAccumulator<>(this.world.a(this, this.getBoundingBox(), d7, d1, d9)); + AxisAlignedBB axisalignedbb2 = this.getBoundingBox(); + AxisAlignedBB axisalignedbb3 = axisalignedbb2.b(d7, 0.0D, d9); + + d11 = VoxelShapes.a(EnumDirection.EnumAxis.Y, axisalignedbb3, streamaccumulator1.a(), d1); + if (d11 != 0.0D) { + axisalignedbb2 = axisalignedbb2.d(0.0D, d11, 0.0D); + } + + double d15 = VoxelShapes.a(EnumDirection.EnumAxis.X, axisalignedbb2, streamaccumulator1.a(), d7); + + if (d15 != 0.0D) { + axisalignedbb2 = axisalignedbb2.d(d15, 0.0D, 0.0D); + } + + double d16 = VoxelShapes.a(EnumDirection.EnumAxis.Z, axisalignedbb2, streamaccumulator1.a(), d9); + + if (d16 != 0.0D) { + axisalignedbb2 = axisalignedbb2.d(0.0D, 0.0D, d16); + } + + AxisAlignedBB axisalignedbb4 = this.getBoundingBox(); + double d17 = VoxelShapes.a(EnumDirection.EnumAxis.Y, axisalignedbb4, streamaccumulator1.a(), d1); + + if (d17 != 0.0D) { + axisalignedbb4 = axisalignedbb4.d(0.0D, d17, 0.0D); + } + + double d18 = VoxelShapes.a(EnumDirection.EnumAxis.X, axisalignedbb4, streamaccumulator1.a(), d7); + + if (d18 != 0.0D) { + axisalignedbb4 = axisalignedbb4.d(d18, 0.0D, 0.0D); + } + + double d19 = VoxelShapes.a(EnumDirection.EnumAxis.Z, axisalignedbb4, streamaccumulator1.a(), d9); + + if (d19 != 0.0D) { + axisalignedbb4 = axisalignedbb4.d(0.0D, 0.0D, d19); + } + + double d20 = d15 * d15 + d16 * d16; + double d21 = d18 * d18 + d19 * d19; + + if (d20 > d21) { + d0 = d15; + d2 = d16; + d1 = -d11; + this.a(axisalignedbb2); + } else { + d0 = d18; + d2 = d19; + d1 = -d17; + this.a(axisalignedbb4); + } + + d1 = VoxelShapes.a(EnumDirection.EnumAxis.Y, this.getBoundingBox(), streamaccumulator1.a(), d1); + if (d1 != 0.0D) { + this.a(this.getBoundingBox().d(0.0D, d1, 0.0D)); + } + } + + if (d12 * d12 + d14 * d14 >= d0 * d0 + d2 * d2) { + d0 = d12; + d1 = d13; + d2 = d14; + this.a(axisalignedbb1); + } + } + + this.world.methodProfiler.exit(); + this.world.methodProfiler.enter("rest"); + this.recalcPosition(); + this.positionChanged = d7 != d0 || d9 != d2; + this.C = d1 != d8; // CraftBukkit - decompile error + this.onGround = this.C && d8 < 0.0D; + this.D = this.positionChanged || this.C; + int k = MathHelper.floor(this.locX); + int l = MathHelper.floor(this.locY - 0.20000000298023224D); + int i1 = MathHelper.floor(this.locZ); + BlockPosition blockposition = new BlockPosition(k, l, i1); + IBlockData iblockdata = this.world.getType(blockposition); + + if (iblockdata.isAir()) { + BlockPosition blockposition1 = blockposition.down(); + IBlockData iblockdata1 = this.world.getType(blockposition1); + Block block = iblockdata1.getBlock(); + + if (block instanceof BlockFence || block instanceof BlockCobbleWall || block instanceof BlockFenceGate) { + iblockdata = iblockdata1; + blockposition = blockposition1; + } + } + + this.a(d1, this.onGround, iblockdata, blockposition); + if (d7 != d0) { + this.motX = 0.0D; + } + + if (d9 != d2) { + this.motZ = 0.0D; + } + + Block block1 = iblockdata.getBlock(); + + if (d8 != d1) { + block1.a((IBlockAccess) this.world, this); + } + + // CraftBukkit start + if (positionChanged && getBukkitEntity() instanceof Vehicle) { + Vehicle vehicle = (Vehicle) this.getBukkitEntity(); + org.bukkit.block.Block bl = this.world.getWorld().getBlockAt(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ)); + + if (d7 > d0) { + bl = bl.getRelative(BlockFace.EAST); + } else if (d7 < d0) { + bl = bl.getRelative(BlockFace.WEST); + } else if (d9 > d2) { + bl = bl.getRelative(BlockFace.SOUTH); + } else if (d9 < d2) { + bl = bl.getRelative(BlockFace.NORTH); + } + + if (bl.getType() != org.bukkit.Material.AIR) { + VehicleBlockCollisionEvent event = new VehicleBlockCollisionEvent(vehicle, bl); + world.getServer().getPluginManager().callEvent(event); + } + } + // CraftBukkit end + + if (this.playStepSound() && (!this.onGround || !this.isSneaking() || !(this instanceof EntityHuman)) && !this.isPassenger()) { + double d22 = this.locX - d4; + double d23 = this.locY - d5; + + d11 = this.locZ - d6; + if (block1 != Blocks.LADDER) { + d23 = 0.0D; + } + + if (block1 != null && this.onGround) { + block1.stepOn(this.world, blockposition, this); + } + + this.K = (float) ((double) this.K + (double) MathHelper.sqrt(d22 * d22 + d11 * d11) * 0.6D); + this.L = (float) ((double) this.L + (double) MathHelper.sqrt(d22 * d22 + d23 * d23 + d11 * d11) * 0.6D); + if (this.L > this.aA && !iblockdata.isAir()) { + this.aA = this.ab(); + if (this.isInWater()) { + Entity entity = this.isVehicle() && this.bO() != null ? this.bO() : this; + float f = entity == this ? 0.35F : 0.4F; + float f1 = MathHelper.sqrt(entity.motX * entity.motX * 0.20000000298023224D + entity.motY * entity.motY + entity.motZ * entity.motZ * 0.20000000298023224D) * f; + + if (f1 > 1.0F) { + f1 = 1.0F; + } + + this.d(f1); + } else { + this.a(blockposition, iblockdata); + } + } else if (this.L > this.aB && this.ah() && iblockdata.isAir()) { + this.aB = this.e(this.L); + } + } + + try { + this.checkBlockCollisions(); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Checking entity block collision"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being checked for collision"); + + this.appendEntityCrashDetails(crashreportsystemdetails); + throw new ReportedException(crashreport); + } + + boolean flag1 = this.ap(); + + if (this.world.b(this.getBoundingBox().shrink(0.001D))) { + if (!flag1) { + ++this.fireTicks; + if (this.fireTicks == 0) { + // CraftBukkit start + EntityCombustEvent event = new org.bukkit.event.entity.EntityCombustByBlockEvent(null, getBukkitEntity(), 8); + world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + this.setOnFire(event.getDuration(), false); + } + // CraftBukkit end + } + } + + this.burn(1); + } else if (this.fireTicks <= 0) { + this.fireTicks = -this.getMaxFireTicks(); + } + + if (flag1 && this.isBurning()) { + this.a(SoundEffects.ENTITY_GENERIC_EXTINGUISH_FIRE, 0.7F, 1.6F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F); + this.fireTicks = -this.getMaxFireTicks(); + } + + this.world.methodProfiler.exit(); + } + } + + protected float ab() { + return (float) ((int) this.L + 1); + } + + public void recalcPosition() { + AxisAlignedBB axisalignedbb = this.getBoundingBox(); + + this.locX = (axisalignedbb.minX + axisalignedbb.maxX) / 2.0D; + this.locY = axisalignedbb.minY; + this.locZ = (axisalignedbb.minZ + axisalignedbb.maxZ) / 2.0D; + if (valid) world.entityJoinedWorld(this, false); // CraftBukkit + } + + protected SoundEffect ad() { + return SoundEffects.ENTITY_GENERIC_SWIM; + } + + protected SoundEffect ae() { + return SoundEffects.ENTITY_GENERIC_SPLASH; + } + + protected SoundEffect af() { + return SoundEffects.ENTITY_GENERIC_SPLASH; + } + + protected void checkBlockCollisions() { + AxisAlignedBB axisalignedbb = this.getBoundingBox(); + BlockPosition.b blockposition_b = BlockPosition.b.d(axisalignedbb.minX + 0.001D, axisalignedbb.minY + 0.001D, axisalignedbb.minZ + 0.001D); + Throwable throwable = null; + + try { + BlockPosition.b blockposition_b1 = BlockPosition.b.d(axisalignedbb.maxX - 0.001D, axisalignedbb.maxY - 0.001D, axisalignedbb.maxZ - 0.001D); + Throwable throwable1 = null; + + try { + BlockPosition.b blockposition_b2 = BlockPosition.b.r(); + Throwable throwable2 = null; + + try { + if (this.world.areChunksLoadedBetween(blockposition_b, blockposition_b1)) { + for (int i = blockposition_b.getX(); i <= blockposition_b1.getX(); ++i) { + for (int j = blockposition_b.getY(); j <= blockposition_b1.getY(); ++j) { + for (int k = blockposition_b.getZ(); k <= blockposition_b1.getZ(); ++k) { + blockposition_b2.c(i, j, k); + IBlockData iblockdata = this.world.getType(blockposition_b2); + + try { + iblockdata.a(this.world, blockposition_b2, this); + this.a(iblockdata); + } catch (Throwable throwable3) { + CrashReport crashreport = CrashReport.a(throwable3, "Colliding entity with block"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Block being collided with"); + + CrashReportSystemDetails.a(crashreportsystemdetails, blockposition_b2, iblockdata); + throw new ReportedException(crashreport); + } + } + } + } + } + } catch (Throwable throwable4) { + throwable2 = throwable4; + throw throwable4; + } finally { + if (blockposition_b2 != null) { + if (throwable2 != null) { + try { + blockposition_b2.close(); + } catch (Throwable throwable5) { + throwable2.addSuppressed(throwable5); + } + } else { + blockposition_b2.close(); + } + } + + } + } catch (Throwable throwable6) { + throwable1 = throwable6; + throw throwable6; + } finally { + if (blockposition_b1 != null) { + if (throwable1 != null) { + try { + blockposition_b1.close(); + } catch (Throwable throwable7) { + throwable1.addSuppressed(throwable7); + } + } else { + blockposition_b1.close(); + } + } + + } + } catch (Throwable throwable8) { + throwable = throwable8; + throw throwable8; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable9) { + throwable.addSuppressed(throwable9); + } + } else { + blockposition_b.close(); + } + } + + } + + } + + protected void a(IBlockData iblockdata) {} + + protected void a(BlockPosition blockposition, IBlockData iblockdata) { + if (!iblockdata.getMaterial().isLiquid()) { + SoundEffectType soundeffecttype = this.world.getType(blockposition.up()).getBlock() == Blocks.SNOW ? Blocks.SNOW.getStepSound() : iblockdata.getBlock().getStepSound(); + + this.a(soundeffecttype.d(), soundeffecttype.a() * 0.15F, soundeffecttype.b()); + } + } + + protected void d(float f) { + this.a(this.ad(), f, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F); + } + + protected float e(float f) { + return 0.0F; + } + + protected boolean ah() { + return false; + } + + public void a(SoundEffect soundeffect, float f, float f1) { + if (!this.isSilent()) { + this.world.a((EntityHuman) null, this.locX, this.locY, this.locZ, soundeffect, this.bV(), f, f1); + } + + } + + public boolean isSilent() { + return (Boolean) this.datawatcher.get(Entity.aG); + } + + public void setSilent(boolean flag) { + this.datawatcher.set(Entity.aG, flag); + } + + public boolean isNoGravity() { + return (Boolean) this.datawatcher.get(Entity.aH); + } + + public void setNoGravity(boolean flag) { + this.datawatcher.set(Entity.aH, flag); + } + + protected boolean playStepSound() { + return true; + } + + protected void a(double d0, boolean flag, IBlockData iblockdata, BlockPosition blockposition) { + if (flag) { + if (this.fallDistance > 0.0F) { + iblockdata.getBlock().fallOn(this.world, blockposition, this, this.fallDistance); + } + + this.fallDistance = 0.0F; + } else if (d0 < 0.0D) { + this.fallDistance = (float) ((double) this.fallDistance - d0); + } + + } + + @Nullable + public AxisAlignedBB al() { + return null; + } + + protected void burn(float i) { // CraftBukkit - int -> float + if (!this.fireProof) { + this.damageEntity(DamageSource.FIRE, (float) i); + } + + } + + public final boolean isFireProof() { + return this.fireProof; + } + + public void c(float f, float f1) { + if (this.isVehicle()) { + Iterator iterator = this.bP().iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + entity.c(f, f1); + } + } + + } + + public boolean isInWater() { + return this.inWater; + } + + private boolean p() { + BlockPosition.b blockposition_b = BlockPosition.b.b(this); + Throwable throwable = null; + + boolean flag; + + try { + flag = this.world.isRainingAt(blockposition_b) || this.world.isRainingAt(blockposition_b.c(this.locX, this.locY + (double) this.length, this.locZ)); + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + + return flag; + } + + private boolean q() { + return this.world.getType(new BlockPosition(this)).getBlock() == Blocks.BUBBLE_COLUMN; + } + + public boolean ao() { + return this.isInWater() || this.p(); + } + + public boolean ap() { + return this.isInWater() || this.p() || this.q(); + } + + public boolean aq() { + // Paper start + return this.doWaterMovement(); + } + + public boolean doWaterMovement() { + // Paper end + return this.isInWater() || this.q(); + } + + public boolean ar() { + return this.X && this.isInWater(); + } + + private void r() { + this.at(); + this.s(); + this.as(); + } + + public void as() { + if (this.isSwimming()) { + this.setSwimming(this.isSprinting() && this.isInWater() && !this.isPassenger()); + } else { + this.setSwimming(this.isSprinting() && this.ar() && !this.isPassenger()); + } + + } + + public boolean at() { + if (this.getVehicle() instanceof EntityBoat) { + this.inWater = false; + } else if (this.b(TagsFluid.WATER)) { + if (!this.inWater && !this.justCreated) { + this.au(); + } + + this.fallDistance = 0.0F; + this.inWater = true; + this.extinguish(); + } else { + this.inWater = false; + } + + return this.inWater; + } + + private void s() { + this.X = this.a(TagsFluid.WATER); + } + + protected void au() { + Entity entity = this.isVehicle() && this.bO() != null ? this.bO() : this; + float f = entity == this ? 0.2F : 0.9F; + float f1 = MathHelper.sqrt(entity.motX * entity.motX * 0.20000000298023224D + entity.motY * entity.motY + entity.motZ * entity.motZ * 0.20000000298023224D) * f; + + if (f1 > 1.0F) { + f1 = 1.0F; + } + + if ((double) f1 < 0.25D) { + this.a(this.ae(), f1, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F); + } else { + this.a(this.af(), f1, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F); + } + + float f2 = (float) MathHelper.floor(this.getBoundingBox().minY); + + float f3; + float f4; + int i; + + for (i = 0; (float) i < 1.0F + this.width * 20.0F; ++i) { + f3 = (this.random.nextFloat() * 2.0F - 1.0F) * this.width; + f4 = (this.random.nextFloat() * 2.0F - 1.0F) * this.width; + this.world.addParticle(Particles.e, this.locX + (double) f3, (double) (f2 + 1.0F), this.locZ + (double) f4, this.motX, this.motY - (double) (this.random.nextFloat() * 0.2F), this.motZ); + } + + for (i = 0; (float) i < 1.0F + this.width * 20.0F; ++i) { + f3 = (this.random.nextFloat() * 2.0F - 1.0F) * this.width; + f4 = (this.random.nextFloat() * 2.0F - 1.0F) * this.width; + this.world.addParticle(Particles.R, this.locX + (double) f3, (double) (f2 + 1.0F), this.locZ + (double) f4, this.motX, this.motY, this.motZ); + } + + } + + public void av() { + if (this.isSprinting() && !this.isInWater()) { + this.aw(); + } + + } + + protected void aw() { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locY - 0.20000000298023224D); + int k = MathHelper.floor(this.locZ); + BlockPosition blockposition = new BlockPosition(i, j, k); + IBlockData iblockdata = this.world.getType(blockposition); + + if (iblockdata.i() != EnumRenderType.INVISIBLE) { + this.world.addParticle(new ParticleParamBlock(Particles.d, iblockdata), this.locX + ((double) this.random.nextFloat() - 0.5D) * (double) this.width, this.getBoundingBox().minY + 0.1D, this.locZ + ((double) this.random.nextFloat() - 0.5D) * (double) this.width, -this.motX * 4.0D, 1.5D, -this.motZ * 4.0D); + } + + } + + public boolean a(Tag tag) { + if (this.getVehicle() instanceof EntityBoat) { + return false; + } else { + double d0 = this.locY + (double) this.getHeadHeight(); + BlockPosition blockposition = new BlockPosition(this.locX, d0, this.locZ); + Fluid fluid = this.world.getFluid(blockposition); + + return fluid.a(tag) && d0 < (double) ((float) blockposition.getY() + fluid.getHeight() + 0.11111111F); + } + } + + public boolean ax() { + return this.world.a(this.getBoundingBox().f(0.10000000149011612D, 0.4000000059604645D, 0.10000000149011612D), Material.LAVA); + } + + public void a(float f, float f1, float f2, float f3) { + float f4 = f * f + f1 * f1 + f2 * f2; + + if (f4 >= 1.0E-4F) { + f4 = MathHelper.c(f4); + if (f4 < 1.0F) { + f4 = 1.0F; + } + + f4 = f3 / f4; + f *= f4; + f1 *= f4; + f2 *= f4; + float f5 = MathHelper.sin(this.yaw * 0.017453292F); + float f6 = MathHelper.cos(this.yaw * 0.017453292F); + + this.motX += (double) (f * f6 - f2 * f5); + this.motY += (double) f1; + this.motZ += (double) (f2 * f6 + f * f5); + } + } + + public float az() { + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(MathHelper.floor(this.locX), 0, MathHelper.floor(this.locZ)); + + if (this.world.isLoaded(blockposition_mutableblockposition)) { + blockposition_mutableblockposition.p(MathHelper.floor(this.locY + (double) this.getHeadHeight())); + return this.world.A(blockposition_mutableblockposition); + } else { + return 0.0F; + } + } + + public void spawnIn(World world) { + // CraftBukkit start + if (world == null) { + die(); + this.world = ((CraftWorld) Bukkit.getServer().getWorlds().get(0)).getHandle(); + return; + } + // CraftBukkit end + this.world = world; + } + + public void setLocation(double d0, double d1, double d2, float f, float f1) { + this.locX = MathHelper.a(d0, -3.0E7D, 3.0E7D); + this.locY = d1; + this.locZ = MathHelper.a(d2, -3.0E7D, 3.0E7D); + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + f1 = MathHelper.a(f1, -90.0F, 90.0F); + this.yaw = f; + this.pitch = f1; + this.lastYaw = this.yaw; + this.lastPitch = this.pitch; + double d3 = (double) (this.lastYaw - f); + + if (d3 < -180.0D) { + this.lastYaw += 360.0F; + } + + if (d3 >= 180.0D) { + this.lastYaw -= 360.0F; + } + + world.getChunkAt((int) Math.floor(this.locX) >> 4, (int) Math.floor(this.locZ) >> 4); // CraftBukkit + this.setPosition(this.locX, this.locY, this.locZ); + this.setYawPitch(f, f1); + } + + public void setPositionRotation(BlockPosition blockposition, float f, float f1) { + this.setPositionRotation((double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, f, f1); + } + + public void setPositionRotation(double d0, double d1, double d2, float f, float f1) { + this.locX = d0; + this.locY = d1; + this.locZ = d2; + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + this.N = this.locX; + this.O = this.locY; + this.P = this.locZ; + this.yaw = f; + this.pitch = f1; + this.setPosition(this.locX, this.locY, this.locZ); + } + + public float g(Entity entity) { + float f = (float) (this.locX - entity.locX); + float f1 = (float) (this.locY - entity.locY); + float f2 = (float) (this.locZ - entity.locZ); + + return MathHelper.c(f * f + f1 * f1 + f2 * f2); + } + + public double d(double d0, double d1, double d2) { + double d3 = this.locX - d0; + double d4 = this.locY - d1; + double d5 = this.locZ - d2; + + return d3 * d3 + d4 * d4 + d5 * d5; + } + + public double c(BlockPosition blockposition) { + return blockposition.distanceSquared(this.locX, this.locY, this.locZ); + } + + public double d(BlockPosition blockposition) { + return blockposition.g(this.locX, this.locY, this.locZ); + } + + public double e(double d0, double d1, double d2) { + double d3 = this.locX - d0; + double d4 = this.locY - d1; + double d5 = this.locZ - d2; + + return (double) MathHelper.sqrt(d3 * d3 + d4 * d4 + d5 * d5); + } + + public double h(Entity entity) { + double d0 = this.locX - entity.locX; + double d1 = this.locY - entity.locY; + double d2 = this.locZ - entity.locZ; + + return d0 * d0 + d1 * d1 + d2 * d2; + } + + public double a(Vec3D vec3d) { + double d0 = this.locX - vec3d.x; + double d1 = this.locY - vec3d.y; + double d2 = this.locZ - vec3d.z; + + return d0 * d0 + d1 * d1 + d2 * d2; + } + + public void d(EntityHuman entityhuman) {} + + public void collide(Entity entity) { + if (!this.x(entity)) { + if (!entity.noclip && !this.noclip) { + double d0 = entity.locX - this.locX; + double d1 = entity.locZ - this.locZ; + double d2 = MathHelper.a(d0, d1); + + if (d2 >= 0.009999999776482582D) { + d2 = (double) MathHelper.sqrt(d2); + d0 /= d2; + d1 /= d2; + double d3 = 1.0D / d2; + + if (d3 > 1.0D) { + d3 = 1.0D; + } + + d0 *= d3; + d1 *= d3; + d0 *= 0.05000000074505806D; + d1 *= 0.05000000074505806D; + d0 *= (double) (1.0F - this.S); + d1 *= (double) (1.0F - this.S); + if (!this.isVehicle()) { + this.f(-d0, 0.0D, -d1); + } + + if (!entity.isVehicle()) { + entity.f(d0, 0.0D, d1); + } + } + + } + } + } + + public void f(double d0, double d1, double d2) { + this.motX += d0; + this.motY += d1; + this.motZ += d2; + this.impulse = true; + } + + protected void aA() { + this.velocityChanged = true; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else { + this.aA(); + return false; + } + } + + public final Vec3D f(float f) { + return this.d(this.g(f), this.h(f)); + } + + public float g(float f) { + return f == 1.0F ? this.pitch : this.lastPitch + (this.pitch - this.lastPitch) * f; + } + + public float h(float f) { + return f == 1.0F ? this.yaw : this.lastYaw + (this.yaw - this.lastYaw) * f; + } + + protected final Vec3D d(float f, float f1) { + float f2 = f * 0.017453292F; + float f3 = -f1 * 0.017453292F; + float f4 = MathHelper.cos(f3); + float f5 = MathHelper.sin(f3); + float f6 = MathHelper.cos(f2); + float f7 = MathHelper.sin(f2); + + return new Vec3D((double) (f5 * f6), (double) (-f7), (double) (f4 * f6)); + } + + public Vec3D getEyePosition(float partialTicks) { return i(partialTicks); } // Paper - OBFHELPER + public Vec3D i(float f) { + if (f == 1.0F) { + return new Vec3D(this.locX, this.locY + (double) this.getHeadHeight(), this.locZ); + } else { + double d0 = this.lastX + (this.locX - this.lastX) * (double) f; + double d1 = this.lastY + (this.locY - this.lastY) * (double) f + (double) this.getHeadHeight(); + double d2 = this.lastZ + (this.locZ - this.lastZ) * (double) f; + + return new Vec3D(d0, d1, d2); + } + } + + public boolean isInteractable() { + return false; + } + + public boolean isCollidable() { + return false; + } + + public void runKillTrigger(Entity entity, int kills, DamageSource damageSource) { this.a(entity, kills, damageSource); } // Paper - OBFHELPER + public void a(Entity entity, int i, DamageSource damagesource) { + if (entity instanceof EntityPlayer) { + CriterionTriggers.c.a((EntityPlayer) entity, this, damagesource); + } + + } + + public boolean c(NBTTagCompound nbttagcompound) { + String s = this.getSaveID(); + + if (this.persist && !this.dead && s != null) { // CraftBukkit - persist flag + nbttagcompound.setString("id", s); + this.save(nbttagcompound); + return true; + } else { + return false; + } + } + + public boolean d(NBTTagCompound nbttagcompound) { + return this.isPassenger() ? false : this.c(nbttagcompound); + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + try { + nbttagcompound.set("Pos", this.a(this.locX, this.locY, this.locZ)); + nbttagcompound.set("Motion", this.a(this.motX, this.motY, this.motZ)); + + // CraftBukkit start - Checking for NaN pitch/yaw and resetting to zero + // TODO: make sure this is the best way to address this. + if (Float.isNaN(this.yaw)) { + this.yaw = 0; + } + + if (Float.isNaN(this.pitch)) { + this.pitch = 0; + } + // CraftBukkit end + + nbttagcompound.set("Rotation", this.a(this.yaw, this.pitch)); + nbttagcompound.setFloat("FallDistance", this.fallDistance); + nbttagcompound.setShort("Fire", (short) this.fireTicks); + nbttagcompound.setShort("Air", (short) this.getAirTicks()); + nbttagcompound.setBoolean("OnGround", this.onGround); + nbttagcompound.setInt("Dimension", this.dimension.getDimensionID()); + nbttagcompound.setBoolean("Invulnerable", this.invulnerable); + nbttagcompound.setInt("PortalCooldown", this.portalCooldown); + nbttagcompound.a("UUID", this.getUniqueID()); + // CraftBukkit start + // PAIL: Check above UUID reads 1.8 properly, ie: UUIDMost / UUIDLeast + nbttagcompound.setLong("WorldUUIDLeast", this.world.getDataManager().getUUID().getLeastSignificantBits()); + nbttagcompound.setLong("WorldUUIDMost", this.world.getDataManager().getUUID().getMostSignificantBits()); + nbttagcompound.setInt("Bukkit.updateLevel", CURRENT_LEVEL); + nbttagcompound.setInt("Spigot.ticksLived", this.ticksLived); + // CraftBukkit end + IChatBaseComponent ichatbasecomponent = this.getCustomName(); + + if (ichatbasecomponent != null) { + nbttagcompound.setString("CustomName", IChatBaseComponent.ChatSerializer.a(ichatbasecomponent)); + } + + if (this.getCustomNameVisible()) { + nbttagcompound.setBoolean("CustomNameVisible", this.getCustomNameVisible()); + } + + if (this.isSilent()) { + nbttagcompound.setBoolean("Silent", this.isSilent()); + } + + if (this.isNoGravity()) { + nbttagcompound.setBoolean("NoGravity", this.isNoGravity()); + } + + if (this.glowing) { + nbttagcompound.setBoolean("Glowing", this.glowing); + } + + NBTTagList nbttaglist; + Iterator iterator; + + if (!this.aJ.isEmpty()) { + nbttaglist = new NBTTagList(); + iterator = this.aJ.iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + nbttaglist.add((NBTBase) (new NBTTagString(s))); + } + + nbttagcompound.set("Tags", nbttaglist); + } + + this.b(nbttagcompound); + if (this.isVehicle()) { + nbttaglist = new NBTTagList(); + iterator = this.bP().iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + if (entity.c(nbttagcompound1)) { + nbttaglist.add((NBTBase) nbttagcompound1); + } + } + + if (!nbttaglist.isEmpty()) { + nbttagcompound.set("Passengers", nbttaglist); + } + } + + // Paper start - Save the entity's origin location + if (origin != null) { + nbttagcompound.set("Paper.Origin", this.createList(origin.getX(), origin.getY(), origin.getZ())); + } + // Save entity's from mob spawner status + if (spawnedViaMobSpawner) { + nbttagcompound.setBoolean("Paper.FromMobSpawner", true); + } + // Paper start - MC-2025 fix - Save entity AABB and load it, floating point issues recalculating AABB can result in wobble + AxisAlignedBB boundingBox = this.getBoundingBox(); + nbttagcompound.set("Paper.AAAB", this.createList( + boundingBox.getMinX(), boundingBox.getMinY(), boundingBox.getMinZ(), + boundingBox.getMaxX(), boundingBox.getMaxY(), boundingBox.getMaxZ() + )); + // Paper end + return nbttagcompound; + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Saving entity NBT"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being saved"); + + this.appendEntityCrashDetails(crashreportsystemdetails); + throw new ReportedException(crashreport); + } + } + + public void f(NBTTagCompound nbttagcompound) { + try { + NBTTagList nbttaglist = nbttagcompound.getList("Pos", 6); + NBTTagList nbttaglist1 = nbttagcompound.getList("Motion", 6); + NBTTagList nbttaglist2 = nbttagcompound.getList("Rotation", 5); + + this.motX = nbttaglist1.k(0); + this.motY = nbttaglist1.k(1); + this.motZ = nbttaglist1.k(2); + + /* CraftBukkit start - Moved section down + if (Math.abs(this.motX) > 10.0D) { + this.motX = 0.0D; + } + + if (Math.abs(this.motY) > 10.0D) { + this.motY = 0.0D; + } + + if (Math.abs(this.motZ) > 10.0D) { + this.motZ = 0.0D; + } + // CraftBukkit end */ + + this.locX = nbttaglist.k(0); + this.locY = nbttaglist.k(1); + this.locZ = nbttaglist.k(2); + this.N = this.locX; + this.O = this.locY; + this.P = this.locZ; + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + this.yaw = nbttaglist2.l(0); + this.pitch = nbttaglist2.l(1); + this.lastYaw = this.yaw; + this.lastPitch = this.pitch; + this.setHeadRotation(this.yaw); + this.k(this.yaw); + this.fallDistance = nbttagcompound.getFloat("FallDistance"); + this.fireTicks = nbttagcompound.getShort("Fire"); + this.setAirTicks(nbttagcompound.getShort("Air")); + this.onGround = nbttagcompound.getBoolean("OnGround"); + if (nbttagcompound.hasKey("Dimension")) { + //this.dimension = DimensionManager.a(nbttagcompound.getInt("Dimension")); // Paper - always controlled by world + } + + this.invulnerable = nbttagcompound.getBoolean("Invulnerable"); + this.portalCooldown = nbttagcompound.getInt("PortalCooldown"); + if (nbttagcompound.b("UUID")) { + this.uniqueID = nbttagcompound.a("UUID"); + this.au = this.uniqueID.toString(); + } + + this.setPosition(this.locX, this.locY, this.locZ); + this.setYawPitch(this.yaw, this.pitch); + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.setCustomName(MCUtil.getBaseComponentFromNbt("CustomName", nbttagcompound)); // Paper - Catch ParseException + } + + this.setCustomNameVisible(nbttagcompound.getBoolean("CustomNameVisible")); + this.setSilent(nbttagcompound.getBoolean("Silent")); + this.setNoGravity(nbttagcompound.getBoolean("NoGravity")); + this.h(nbttagcompound.getBoolean("Glowing")); + if (nbttagcompound.hasKeyOfType("Tags", 9)) { + this.aJ.clear(); + NBTTagList nbttaglist3 = nbttagcompound.getList("Tags", 8); + int i = Math.min(nbttaglist3.size(), 1024); + + for (int j = 0; j < i; ++j) { + this.aJ.add(nbttaglist3.getString(j)); + } + } + + this.a(nbttagcompound); + if (this.aD()) { + this.setPosition(this.locX, this.locY, this.locZ); + } + // Paper start - MC-2025 fix - Save entity AABB and load it, floating point issues recalculating AABB can result in wobble + // Placement is important, always after the setPosition call above + if (nbttagcompound.hasKey("Paper.AABB")) { + NBTTagList savedBB = nbttagcompound.getList("Paper.AABB", 6); + this.setBoundingBox(new AxisAlignedBB( + savedBB.getDoubleAt(0), savedBB.getDoubleAt(1), savedBB.getDoubleAt(2), + savedBB.getDoubleAt(3), savedBB.getDoubleAt(4), savedBB.getDoubleAt(5) + )); + } + // Paper end + + // CraftBukkit start + if (this instanceof EntityLiving) { + EntityLiving entity = (EntityLiving) this; + + this.ticksLived = nbttagcompound.getInt("Spigot.ticksLived"); + + // Reset the persistence for tamed animals + if (entity instanceof EntityTameableAnimal && !isLevelAtLeast(nbttagcompound, 2) && !nbttagcompound.getBoolean("PersistenceRequired")) { + EntityInsentient entityinsentient = (EntityInsentient) entity; + entityinsentient.persistent = !entityinsentient.isTypeNotPersistent(); + } + } + // CraftBukkit end + + // CraftBukkit start + double limit = getBukkitEntity() instanceof Vehicle ? 100.0D : 10.0D; + if (Math.abs(this.motX) > limit) { + this.motX = 0.0D; + } + + if (Math.abs(this.motY) > limit) { + this.motY = 0.0D; + } + + if (Math.abs(this.motZ) > limit) { + this.motZ = 0.0D; + } + // CraftBukkit end + + // CraftBukkit start - Reset world + if (this instanceof EntityPlayer) { + Server server = Bukkit.getServer(); + org.bukkit.World bworld = null; + + // TODO: Remove World related checks, replaced with WorldUID + String worldName = nbttagcompound.getString("world"); + + if (nbttagcompound.hasKey("WorldUUIDMost") && nbttagcompound.hasKey("WorldUUIDLeast")) { + UUID uid = new UUID(nbttagcompound.getLong("WorldUUIDMost"), nbttagcompound.getLong("WorldUUIDLeast")); + bworld = server.getWorld(uid); + } else { + bworld = server.getWorld(worldName); + } + + if (bworld == null) { + bworld = ((org.bukkit.craftbukkit.CraftServer) server).getServer().getWorldServer(DimensionManager.OVERWORLD).getWorld(); + } + + spawnIn(bworld == null? null : ((CraftWorld) bworld).getHandle()); + } + // CraftBukkit end + + // Paper start - Restore the entity's origin location + NBTTagList originTag = nbttagcompound.getList("Paper.Origin", 6); + if (!originTag.isEmpty()) { + origin = new Location(world.getWorld(), originTag.getDoubleAt(0), originTag.getDoubleAt(1), originTag.getDoubleAt(2)); + } + + spawnedViaMobSpawner = nbttagcompound.getBoolean("Paper.FromMobSpawner"); // Restore entity's from mob spawner status + // Paper end + + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Loading entity NBT"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being loaded"); + + this.appendEntityCrashDetails(crashreportsystemdetails); + throw new ReportedException(crashreport); + } + } + + protected boolean aD() { + return true; + } + + // Paper start + private java.lang.ref.WeakReference currentChunk = null; + + public void setCurrentChunk(Chunk chunk) { + this.currentChunk = chunk != null ? new java.lang.ref.WeakReference<>(chunk) : null; + } + /** + * Returns the entities current registered chunk. If the entity is not added to a chunk yet, it will return null + */ + public Chunk getCurrentChunk() { + final Chunk chunk = currentChunk != null ? currentChunk.get() : null; + return chunk != null && chunk.isLoaded() ? chunk : (isAddedToChunk() ? world.getChunkIfLoaded(getChunkX(), getChunkZ()) : null); + } + /** + * Returns the chunk at the location, using the entities local cache if avail + * Will only return null if the location specified is not loaded + */ + public Chunk getCurrentChunkAt(int x, int z) { + if (getChunkX() == x && getChunkZ() == z) { + Chunk chunk = getCurrentChunk(); + if (chunk != null) { + return chunk; + } + } + return world.getChunkIfLoaded(x, z); + } + /** + * Returns the chunk at the entities current location, using the entities local cache if avail + * Will only return null if the location specified is not loaded + */ + public Chunk getChunkAtLocation() { + return getCurrentChunkAt((int)Math.floor(locX) >> 4, (int)Math.floor(locZ) >> 4); + } + + private MinecraftKey entityKey; + private String entityKeyString; + + @Override + public MinecraftKey getMinecraftKey() { + if (entityKey == null) { + this.entityKey = EntityTypes.getName(this.getEntityType()); + this.entityKeyString = this.entityKey != null ? this.entityKey.toString() : null; + } + return entityKey; + } + + @Override + public String getMinecraftKeyString() { + getMinecraftKey(); // Try to load if it doesn't exists. see: https://github.com/PaperMC/Paper/issues/1280 + return entityKeyString; + } + @Nullable + public final String getSaveID() { + EntityTypes type = this.getEntityType(); + return type != null && type.isPersistable() ? getMinecraftKeyString() : null; + // Paper end + } + + protected abstract void a(NBTTagCompound nbttagcompound); + + protected abstract void b(NBTTagCompound nbttagcompound); + + protected NBTTagList createList(double... adouble) { return a(adouble); } // Paper - OBFHELPER + protected NBTTagList a(double... adouble) { + NBTTagList nbttaglist = new NBTTagList(); + double[] adouble1 = adouble; + int i = adouble.length; + + for (int j = 0; j < i; ++j) { + double d0 = adouble1[j]; + + nbttaglist.add((NBTBase) (new NBTTagDouble(d0))); + } + + return nbttaglist; + } + + protected NBTTagList a(float... afloat) { + NBTTagList nbttaglist = new NBTTagList(); + float[] afloat1 = afloat; + int i = afloat.length; + + for (int j = 0; j < i; ++j) { + float f = afloat1[j]; + + nbttaglist.add((NBTBase) (new NBTTagFloat(f))); + } + + return nbttaglist; + } + + @Nullable + public EntityItem a(IMaterial imaterial) { + return this.a(imaterial, 0); + } + + @Nullable + public EntityItem a(IMaterial imaterial, int i) { + return this.a(new ItemStack(imaterial), (float) i); + } + + @Nullable + public EntityItem a_(ItemStack itemstack) { + return this.a(itemstack, 0.0F); + } + + @Nullable public final EntityItem dropItem(ItemStack itemstack, float offset) { return this.a(itemstack, offset); } // Paper - OBFHELPER + @Nullable + public EntityItem a(ItemStack itemstack, float f) { + if (itemstack.isEmpty()) { + return null; + } else { + // CraftBukkit start - Capture drops for death event + if (this instanceof EntityLiving && !((EntityLiving) this).forceDrops) { + ((EntityLiving) this).drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemstack)); + return null; + } + // CraftBukkit end + EntityItem entityitem = new EntityItem(this.world, this.locX, this.locY + (double) f, this.locZ, itemstack); + + entityitem.n(); + // CraftBukkit start + EntityDropItemEvent event = new EntityDropItemEvent(this.getBukkitEntity(), (org.bukkit.entity.Item) entityitem.getBukkitEntity()); + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) { + return null; + } + // CraftBukkit end + this.world.addEntity(entityitem); + return entityitem; + } + } + + public boolean isAlive() { + return !this.dead; + } + + public boolean inBlock() { + if (this.noclip) { + return false; + } else { + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + for (int i = 0; i < 8; ++i) { + int j = MathHelper.floor(this.locY + (double) (((float) ((i >> 0) % 2) - 0.5F) * 0.1F) + (double) this.getHeadHeight()); + int k = MathHelper.floor(this.locX + (double) (((float) ((i >> 1) % 2) - 0.5F) * this.width * 0.8F)); + int l = MathHelper.floor(this.locZ + (double) (((float) ((i >> 2) % 2) - 0.5F) * this.width * 0.8F)); + + if (blockposition_b.getX() != k || blockposition_b.getY() != j || blockposition_b.getZ() != l) { + blockposition_b.c(k, j, l); + if (this.world.getType(blockposition_b).r()) { + boolean flag = true; + + return flag; + } + } + } + + return false; + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + } + } + + public boolean b(EntityHuman entityhuman, EnumHand enumhand) { + return false; + } + + @Nullable + public AxisAlignedBB j(Entity entity) { + return null; + } + + public void aH() { + Entity entity = this.getVehicle(); + + if (this.isPassenger() && entity.dead) { + this.stopRiding(); + } else { + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + this.tick(); + if (this.isPassenger()) { + entity.k(this); + } + } + } + + public void k(Entity entity) { + if (this.w(entity)) { + entity.setPosition(this.locX, this.locY + this.aJ() + entity.aI(), this.locZ); + } + } + + public double aI() { + return 0.0D; + } + + public double aJ() { + return (double) this.length * 0.75D; + } + + public boolean startRiding(Entity entity) { + return this.a(entity, false); + } + + public boolean a(Entity entity, boolean flag) { + for (Entity entity1 = entity; entity1.vehicle != null; entity1 = entity1.vehicle) { + if (entity1.vehicle == this) { + return false; + } + } + + if (!flag && (!this.n(entity) || !entity.q(this))) { + return false; + } else { + if (this.isPassenger()) { + this.stopRiding(); + } + + this.vehicle = entity; + if (!this.vehicle.addPassenger(this)) this.vehicle = null; // CraftBukkit + return true; + } + } + + protected boolean n(Entity entity) { + return this.k <= 0; + } + + public void ejectPassengers() { + for (int i = this.passengers.size() - 1; i >= 0; --i) { + ((Entity) this.passengers.get(i)).stopRiding(); + } + + } + + // Paper start + public void stopRiding() { stopRiding(false); } + public void stopRiding(boolean suppressCancellation) { + // Paper end + if (this.vehicle != null) { + Entity entity = this.vehicle; + + this.vehicle = null; + if (!entity.removePassenger(this, suppressCancellation)) this.vehicle = entity; // CraftBukkit // Paper + } + + } + + protected boolean addPassenger(Entity entity) { // CraftBukkit + if (entity == this) throw new IllegalArgumentException("Entities cannot become a passenger of themselves"); // Paper - issue 572 + if (entity.getVehicle() != this) { + throw new IllegalStateException("Use x.startRiding(y), not y.addPassenger(x)"); + } else { + // CraftBukkit start + com.google.common.base.Preconditions.checkState(!entity.passengers.contains(this), "Circular entity riding! %s %s", this, entity); + + CraftEntity craft = (CraftEntity) entity.getBukkitEntity().getVehicle(); + Entity orig = craft == null ? null : craft.getHandle(); + if (getBukkitEntity() instanceof Vehicle && entity.getBukkitEntity() instanceof LivingEntity && entity.world.isChunkLoaded((int) entity.locX >> 4, (int) entity.locZ >> 4, false)) { // Boolean not used + VehicleEnterEvent event = new VehicleEnterEvent( + (Vehicle) getBukkitEntity(), + entity.getBukkitEntity() + ); + Bukkit.getPluginManager().callEvent(event); + CraftEntity craftn = (CraftEntity) entity.getBukkitEntity().getVehicle(); + Entity n = craftn == null ? null : craftn.getHandle(); + if (event.isCancelled() || n != orig) { + return false; + } + } + // CraftBukkit end + // Spigot start + org.spigotmc.event.entity.EntityMountEvent event = new org.spigotmc.event.entity.EntityMountEvent(entity.getBukkitEntity(), this.getBukkitEntity()); + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + // Spigot end + if (!this.world.isClientSide && entity instanceof EntityHuman && !(this.bO() instanceof EntityHuman)) { + this.passengers.add(0, entity); + } else { + this.passengers.add(entity); + } + + } + return true; // CraftBukkit + } + + // Paper start + protected boolean removePassenger(Entity entity) { return removePassenger(entity, false);} + protected boolean removePassenger(Entity entity, boolean suppressCancellation) { // CraftBukkit + // Paper end + if (entity.getVehicle() == this) { + throw new IllegalStateException("Use x.stopRiding(y), not y.removePassenger(x)"); + } else { + // CraftBukkit start + CraftEntity craft = (CraftEntity) entity.getBukkitEntity().getVehicle(); + Entity orig = craft == null ? null : craft.getHandle(); + if (getBukkitEntity() instanceof Vehicle && entity.getBukkitEntity() instanceof LivingEntity) { + VehicleExitEvent event = new VehicleExitEvent( + (Vehicle) getBukkitEntity(), + (LivingEntity) entity.getBukkitEntity(), !suppressCancellation // Paper + ); + Bukkit.getPluginManager().callEvent(event); + CraftEntity craftn = (CraftEntity) entity.getBukkitEntity().getVehicle(); + Entity n = craftn == null ? null : craftn.getHandle(); + if (event.isCancelled() || n != orig) { + return false; + } + } + // CraftBukkit end + // Spigot start + org.spigotmc.event.entity.EntityDismountEvent event = new org.spigotmc.event.entity.EntityDismountEvent(entity.getBukkitEntity(), this.getBukkitEntity(), !suppressCancellation); // Paper + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + // Spigot end + this.passengers.remove(entity); + entity.k = 60; + } + return true; // CraftBukkit + } + + protected boolean q(Entity entity) { + return this.bP().size() < 1; + } + + public float getCollisionBorderSize() { return aM(); } // Paper - OBFHELPER + public float aM() { + return 0.0F; + } + + public Vec3D getLookVec() { return aN(); } // Paper - OBFHELPER + public Vec3D aN() { + return this.d(this.pitch, this.yaw); + } + + public Vec2F aO() { + return new Vec2F(this.pitch, this.yaw); + } + + public void e(BlockPosition blockposition) { + if (this.portalCooldown > 0) { + this.portalCooldown = this.aQ(); + } else { + if (!this.world.isClientSide && !blockposition.equals(this.aq)) { + this.aq = new BlockPosition(blockposition); + ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = ((BlockPortal) Blocks.NETHER_PORTAL).c((GeneratorAccess) this.world, this.aq); + double d0 = shapedetector_shapedetectorcollection.getFacing().k() == EnumDirection.EnumAxis.X ? (double) shapedetector_shapedetectorcollection.a().getZ() : (double) shapedetector_shapedetectorcollection.a().getX(); + double d1 = shapedetector_shapedetectorcollection.getFacing().k() == EnumDirection.EnumAxis.X ? this.locZ : this.locX; + + d1 = Math.abs(MathHelper.c(d1 - (double) (shapedetector_shapedetectorcollection.getFacing().e().c() == EnumDirection.EnumAxisDirection.NEGATIVE ? 1 : 0), d0, d0 - (double) shapedetector_shapedetectorcollection.d())); + double d2 = MathHelper.c(this.locY - 1.0D, (double) shapedetector_shapedetectorcollection.a().getY(), (double) (shapedetector_shapedetectorcollection.a().getY() - shapedetector_shapedetectorcollection.e())); + + this.ar = new Vec3D(d1, d2, 0.0D); + this.as = shapedetector_shapedetectorcollection.getFacing(); + } + + this.an = true; + } + } + + public int aQ() { + return 300; + } + + public Iterable aS() { + return Entity.a; + } + + public Iterable getArmorItems() { + return Entity.a; + } + + public Iterable aU() { + return Iterables.concat(this.aS(), this.getArmorItems()); + } + + public void setEquipment(EnumItemSlot enumitemslot, ItemStack itemstack) {} + + public boolean isBurning() { + boolean flag = this.world != null && this.world.isClientSide; + + return !this.fireProof && (this.fireTicks > 0 || flag && this.getFlag(0)); + } + + public boolean isPassenger() { + return this.getVehicle() != null; + } + + public boolean isVehicle() { + return !this.bP().isEmpty(); + } + + public boolean aY() { + return true; + } + + public boolean isSneaking() { + return this.getFlag(1); + } + + public void setSneaking(boolean flag) { + this.setFlag(1, flag); + } + + public boolean isSprinting() { + return this.getFlag(3); + } + + public void setSprinting(boolean flag) { + this.setFlag(3, flag); + } + + public boolean isSwimming() { + return this.getFlag(4); + } + + public void setSwimming(boolean flag) { + // CraftBukkit start + if (this.isSwimming() != flag && this instanceof EntityLiving) { + if (CraftEventFactory.callToggleSwimEvent((EntityLiving) this, flag).isCancelled()) { + return; + } + } + // CraftBukkit end + this.setFlag(4, flag); + } + + public boolean bc() { + return this.glowing || this.world.isClientSide && this.getFlag(6); + } + + public void h(boolean flag) { + this.glowing = flag; + if (!this.world.isClientSide) { + this.setFlag(6, this.glowing); + } + + } + + public boolean isInvisible() { + return this.getFlag(5); + } + + @Nullable + public ScoreboardTeamBase getScoreboardTeam() { + if (!this.world.paperConfig.nonPlayerEntitiesOnScoreboards && !(this instanceof EntityHuman)) { return null; } // Paper + return this.world.getScoreboard().getPlayerTeam(this.getName()); + } + + public boolean r(Entity entity) { + return this.a(entity.getScoreboardTeam()); + } + + public boolean a(ScoreboardTeamBase scoreboardteambase) { + return this.getScoreboardTeam() != null ? this.getScoreboardTeam().isAlly(scoreboardteambase) : false; + } + + public void setInvisible(boolean flag) { + this.setFlag(5, flag); + } + + public boolean getFlag(int i) { + return ((Byte) this.datawatcher.get(Entity.ac) & 1 << i) != 0; + } + + public void setFlag(int i, boolean flag) { + byte b0 = (Byte) this.datawatcher.get(Entity.ac); + + if (flag) { + this.datawatcher.set(Entity.ac, (byte) (b0 | 1 << i)); + } else { + this.datawatcher.set(Entity.ac, (byte) (b0 & ~(1 << i))); + } + + } + + public int getMaxAirTicks() { return bf(); } public int bf() { // Paper - OBF HELPER + return 300; + } + + public int getAirTicks() { + return (Integer) this.datawatcher.get(Entity.aD); + } + + public void setAirTicks(int i) { + // CraftBukkit start + EntityAirChangeEvent event = new EntityAirChangeEvent(this.getBukkitEntity(), i); + event.getEntity().getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + this.datawatcher.set(Entity.aD, event.getAmount()); + // CraftBukkit end + } + + public void onLightningStrike(EntityLightning entitylightning) { + ++this.fireTicks; + // CraftBukkit start + final org.bukkit.entity.Entity thisBukkitEntity = this.getBukkitEntity(); + final org.bukkit.entity.Entity stormBukkitEntity = entitylightning.getBukkitEntity(); + final PluginManager pluginManager = Bukkit.getPluginManager(); + // CraftBukkit end + + if (this.fireTicks == 0) { + // CraftBukkit start - Call a combust event when lightning strikes + EntityCombustByEntityEvent entityCombustEvent = new EntityCombustByEntityEvent(stormBukkitEntity, thisBukkitEntity, 8); + pluginManager.callEvent(entityCombustEvent); + if (!entityCombustEvent.isCancelled()) { + this.setOnFire(entityCombustEvent.getDuration(), false); + } + // CraftBukkit end + } + + // CraftBukkit start + if (thisBukkitEntity instanceof Hanging) { + HangingBreakByEntityEvent hangingEvent = new HangingBreakByEntityEvent((Hanging) thisBukkitEntity, stormBukkitEntity); + pluginManager.callEvent(hangingEvent); + + if (hangingEvent.isCancelled()) { + return; + } + } + + if (this.fireProof) { + return; + } + CraftEventFactory.entityDamage = entitylightning; + if (!this.damageEntity(DamageSource.LIGHTNING, 5.0F)) { + CraftEventFactory.entityDamage = null; + return; + } + // CraftBukkit end + } + + public void j(boolean flag) { + if (flag) { + this.motY = Math.max(-0.9D, this.motY - 0.03D); + } else { + this.motY = Math.min(1.8D, this.motY + 0.1D); + } + + } + + public void k(boolean flag) { + if (flag) { + this.motY = Math.max(-0.3D, this.motY - 0.03D); + } else { + this.motY = Math.min(0.7D, this.motY + 0.06D); + } + + this.fallDistance = 0.0F; + } + + public void onKill(EntityLiving entityLiving) { this.b(entityLiving); } // Paper - OBFHELPER + public void b(EntityLiving entityliving) {} + + protected boolean i(double d0, double d1, double d2) { + BlockPosition blockposition = new BlockPosition(d0, d1, d2); + double d3 = d0 - (double) blockposition.getX(); + double d4 = d1 - (double) blockposition.getY(); + double d5 = d2 - (double) blockposition.getZ(); + + if (this.world.getCubes((Entity) null, this.getBoundingBox())) { + return false; + } else { + EnumDirection enumdirection = EnumDirection.UP; + double d6 = Double.MAX_VALUE; + + if (!this.world.o(blockposition.west()) && d3 < d6) { + d6 = d3; + enumdirection = EnumDirection.WEST; + } + + if (!this.world.o(blockposition.east()) && 1.0D - d3 < d6) { + d6 = 1.0D - d3; + enumdirection = EnumDirection.EAST; + } + + if (!this.world.o(blockposition.north()) && d5 < d6) { + d6 = d5; + enumdirection = EnumDirection.NORTH; + } + + if (!this.world.o(blockposition.south()) && 1.0D - d5 < d6) { + d6 = 1.0D - d5; + enumdirection = EnumDirection.SOUTH; + } + + if (!this.world.o(blockposition.up()) && 1.0D - d4 < d6) { + d6 = 1.0D - d4; + enumdirection = EnumDirection.UP; + } + + float f = this.random.nextFloat() * 0.2F + 0.1F; + float f1 = (float) enumdirection.c().a(); + + if (enumdirection.k() == EnumDirection.EnumAxis.X) { + this.motX = (double) (f1 * f); + this.motY *= 0.75D; + this.motZ *= 0.75D; + } else if (enumdirection.k() == EnumDirection.EnumAxis.Y) { + this.motX *= 0.75D; + this.motY = (double) (f1 * f); + this.motZ *= 0.75D; + } else if (enumdirection.k() == EnumDirection.EnumAxis.Z) { + this.motX *= 0.75D; + this.motY *= 0.75D; + this.motZ = (double) (f1 * f); + } + + return true; + } + } + + public void bh() { + this.F = true; + this.fallDistance = 0.0F; + } + + private static void c(IChatBaseComponent ichatbasecomponent) { + ichatbasecomponent.a((chatmodifier) -> { + chatmodifier.setChatClickable((ChatClickable) null); + }).a().forEach(Entity::c); + } + + public IChatBaseComponent getDisplayName() { + IChatBaseComponent ichatbasecomponent = this.getCustomName(); + + if (ichatbasecomponent != null) { + IChatBaseComponent ichatbasecomponent1 = ichatbasecomponent.h(); + + c(ichatbasecomponent1); + return ichatbasecomponent1; + } else { + return this.g.e(); + } + } + + @Nullable + public Entity[] bi() { + return null; + } + + public boolean s(Entity entity) { + return this == entity; + } + + public float getHeadRotation() { + return 0.0F; + } + + public void setHeadRotation(float f) {} + + public void k(float f) {} + + public boolean bk() { + return true; + } + + public boolean t(Entity entity) { + return false; + } + + public String toString() { + return String.format(Locale.ROOT, "%s[\'%s\'/%d, uuid=\'%s\', l=\'%s\', x=%.2f, y=%.2f, z=%.2f, cx=%d, cz=%d, tl=%d, v=%b, d=%b]", new Object[] { this.getClass().getSimpleName(), this.getDisplayName().getText(), Integer.valueOf(this.id), this.uniqueID.toString(), this.world == null ? "~NULL~" : this.world.getWorldData().getName(), Double.valueOf(this.locX), Double.valueOf(this.locY), Double.valueOf(this.locZ), getChunkX(), getChunkZ(), this.ticksLived, this.valid, this.dead}); // Paper - add more information + } + + public boolean isInvulnerable(DamageSource damagesource) { + return this.invulnerable && damagesource != DamageSource.OUT_OF_WORLD && !damagesource.v(); + } + + public boolean bl() { + return this.invulnerable; + } + + public void setInvulnerable(boolean flag) { + this.invulnerable = flag; + } + + public void u(Entity entity) { + this.setPositionRotation(entity.locX, entity.locY, entity.locZ, entity.yaw, entity.pitch); + } + + public void v(Entity entity) { + NBTTagCompound nbttagcompound = entity.save(new NBTTagCompound()); + + nbttagcompound.remove("Dimension"); + this.f(nbttagcompound); + this.portalCooldown = entity.portalCooldown; + this.aq = entity.aq; + this.ar = entity.ar; + this.as = entity.as; + } + + @Nullable + public Entity a(DimensionManager dimensionmanager) { + if (!this.world.isClientSide && !this.dead) { + this.world.methodProfiler.enter("changeDimension"); + MinecraftServer minecraftserver = this.bK(); + // CraftBukkit start - Move logic into new function "teleportTo(Location,boolean)" + // DimensionManager dimensionmanager1 = this.dimension; + // WorldServer worldserver = minecraftserver.getWorldServer(dimensionmanager1); + // WorldServer worldserver1 = minecraftserver.getWorldServer(dimensionmanager); + WorldServer exitWorld = null; + if (this.dimension.getDimensionID() < CraftWorld.CUSTOM_DIMENSION_OFFSET) { // Plugins must specify exit from custom Bukkit worlds + exitWorld = minecraftserver.getWorldServer(dimensionmanager); + } + + BlockPosition blockposition = null; // PAIL: CHECK + Location enter = this.getBukkitEntity().getLocation(); + Location exit; + if (exitWorld != null) { + if (blockposition != null) { + exit = new Location(exitWorld.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ()); + } else { + exit = minecraftserver.getPlayerList().calculateTarget(enter, exitWorld); + } + } + else { + exit = null; + } + boolean useTravelAgent = exitWorld != null && !(this.dimension == DimensionManager.THE_END && exitWorld.dimension == DimensionManager.THE_END); // don't use agent for custom worlds or return from THE_END + + TravelAgent agent = exit != null ? (TravelAgent) ((CraftWorld) exit.getWorld()).getHandle().getTravelAgent() : org.bukkit.craftbukkit.CraftTravelAgent.DEFAULT; // return arbitrary TA to compensate for implementation dependent plugins + boolean oldCanCreate = agent.getCanCreatePortal(); + agent.setCanCreatePortal(false); // General entities cannot create portals + + EntityPortalEvent event = new EntityPortalEvent(this.getBukkitEntity(), enter, exit, agent); + event.useTravelAgent(useTravelAgent); + event.getEntity().getServer().getPluginManager().callEvent(event); + if (event.isCancelled() || event.getTo() == null || event.getTo().getWorld() == null || !this.isAlive()) { + agent.setCanCreatePortal(oldCanCreate); + return null; + } + exit = event.useTravelAgent() ? event.getPortalTravelAgent().findOrCreate(event.getTo()) : event.getTo(); + agent.setCanCreatePortal(oldCanCreate); + + // Need to make sure the profiler state is reset afterwards (but we still want to time the call) + Entity entity = this.teleportTo(exit, true); + this.world.methodProfiler.exit(); + return entity; + } else { + return null; + } + } + + public Entity teleportTo(Location exit, boolean portal) { + if (!this.dead) { // Paper + WorldServer worldserver = ((CraftWorld) getBukkitEntity().getLocation().getWorld()).getHandle(); + WorldServer worldserver1 = ((CraftWorld) exit.getWorld()).getHandle(); + DimensionManager dimensionmanager = worldserver1.dimension; + // CraftBukkit end + + this.dimension = dimensionmanager; + /* CraftBukkit start - TODO: Check if we need this + if (dimensionmanager1 == DimensionManager.THE_END && dimensionmanager == DimensionManager.THE_END) { + worldserver1 = minecraftserver.getWorldServer(DimensionManager.OVERWORLD); + this.dimension = DimensionManager.OVERWORLD; + } + // CraftBukkit end */ + + this.world.removeEntity(this); // Paper - Fully remove entity, can't have dupes in the UUID map + this.dead = false; + this.world.methodProfiler.enter("reposition"); + /* CraftBukkit start - Handled in calculateTarget + BlockPosition blockposition; + + if (dimensionmanager == DimensionManager.THE_END) { + blockposition = worldserver1.getDimensionSpawn(); + } else { + double d0 = this.locX; + double d1 = this.locZ; + double d2 = 8.0D; + + if (dimensionmanager == DimensionManager.NETHER) { + d0 = MathHelper.a(d0 / 8.0D, worldserver1.getWorldBorder().b() + 16.0D, worldserver1.getWorldBorder().d() - 16.0D); + d1 = MathHelper.a(d1 / 8.0D, worldserver1.getWorldBorder().c() + 16.0D, worldserver1.getWorldBorder().e() - 16.0D); + } else if (dimensionmanager == DimensionManager.OVERWORLD) { + d0 = MathHelper.a(d0 * 8.0D, worldserver1.getWorldBorder().b() + 16.0D, worldserver1.getWorldBorder().d() - 16.0D); + d1 = MathHelper.a(d1 * 8.0D, worldserver1.getWorldBorder().c() + 16.0D, worldserver1.getWorldBorder().e() - 16.0D); + } + + d0 = (double) MathHelper.clamp((int) d0, -29999872, 29999872); + d1 = (double) MathHelper.clamp((int) d1, -29999872, 29999872); + float f = this.yaw; + + this.setPositionRotation(d0, this.locY, d1, 90.0F, 0.0F); + PortalTravelAgent portaltravelagent = worldserver1.getTravelAgent(); + + portaltravelagent.b(this, f); + blockposition = new BlockPosition(this); + } + + // CraftBukkit end */ + // CraftBukkit start - Ensure chunks are loaded in case TravelAgent is not used which would initially cause chunks to load during find/create + // minecraftserver.getPlayerList().changeWorld(this, j, worldserver, worldserver1); + worldserver1.getMinecraftServer().getPlayerList().repositionEntity(this, exit, portal); + // worldserver.entityJoinedWorld(this, false); // Handled in repositionEntity + // CraftBukkit end + this.world.methodProfiler.exitEnter("reloading"); + Entity entity = this.P().a((World) worldserver1); + + if (entity != null) { + entity.v(this); + /* CraftBukkit start - We need to do this... + if (dimensionmanager1 == DimensionManager.THE_END && dimensionmanager == DimensionManager.THE_END) { + BlockPosition blockposition1 = worldserver1.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, worldserver1.getSpawn()); + + entity.setPositionRotation(blockposition1, entity.yaw, entity.pitch); + } else { + entity.setPositionRotation(blockposition, entity.yaw, entity.pitch); + } + // CraftBukkit end */ + + boolean flag = entity.attachedToPlayer; + + entity.attachedToPlayer = true; + worldserver1.addEntity(entity); + entity.attachedToPlayer = flag; + worldserver1.entityJoinedWorld(entity, false); + // CraftBukkit start - Forward the CraftEntity to the new entity + this.getBukkitEntity().setHandle(entity); + entity.bukkitEntity = this.getBukkitEntity(); + + if (this instanceof EntityInsentient) { + ((EntityInsentient)this).unleash(true, false); // Unleash to prevent duping of leads. + } + // CraftBukkit end + } + + this.dead = true; + this.world.methodProfiler.exit(); + worldserver.p(); + worldserver1.p(); + // this.world.methodProfiler.exit(); // CraftBukkit: Moved up to keep balanced + return entity; + } else { + return null; + } + } + + public boolean bm() { + return true; + } + + public float a(Explosion explosion, IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, Fluid fluid, float f) { + return f; + } + + public boolean a(Explosion explosion, IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, float f) { + return true; + } + + public int bn() { + return 3; + } + + public Vec3D getPortalOffset() { + return this.ar; + } + + public EnumDirection getPortalDirection() { + return this.as; + } + + public boolean isIgnoreBlockTrigger() { + return false; + } + + public void appendEntityCrashDetails(CrashReportSystemDetails crashreportsystemdetails) { + crashreportsystemdetails.a("Entity Type", () -> { + return EntityTypes.getName(this.P()) + " (" + this.getClass().getCanonicalName() + ")"; + }); + crashreportsystemdetails.a("Entity ID", (Object) this.id); + crashreportsystemdetails.a("Entity Name", () -> { + return this.getDisplayName().getString(); + }); + crashreportsystemdetails.a("Entity's Exact location", (Object) String.format(Locale.ROOT, "%.2f, %.2f, %.2f", this.locX, this.locY, this.locZ)); + crashreportsystemdetails.a("Entity's Block location", (Object) CrashReportSystemDetails.a(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ))); + crashreportsystemdetails.a("Entity's Momentum", (Object) String.format(Locale.ROOT, "%.2f, %.2f, %.2f", this.motX, this.motY, this.motZ)); + crashreportsystemdetails.a("Entity's Passengers", () -> { + return this.bP().toString(); + }); + crashreportsystemdetails.a("Entity's Vehicle", () -> { + return this.getVehicle().toString(); + }); + } + + public void setUUID(UUID uuid) { a(uuid); } // Paper - OBFHELPER + public void a(UUID uuid) { + this.uniqueID = uuid; + this.au = this.uniqueID.toString(); + } + + public UUID getUniqueID() { + return this.uniqueID; + } + + public String bu() { + return this.au; + } + + public String getName() { + return this.au; + } + + public boolean bw() { + return this.pushedByWater(); + } + + public boolean pushedByWater() { + // Paper end + return true; + } + + public IChatBaseComponent getScoreboardDisplayName() { + return ScoreboardTeam.a(this.getScoreboardTeam(), this.getDisplayName()).a((chatmodifier) -> { + chatmodifier.setChatHoverable(this.bC()).setInsertion(this.bu()); + }); + } + + public void setCustomName(@Nullable IChatBaseComponent ichatbasecomponent) { + this.datawatcher.set(Entity.aE, Optional.ofNullable(ichatbasecomponent)); + } + + @Nullable + public IChatBaseComponent getCustomName() { + return (IChatBaseComponent) ((Optional) this.datawatcher.get(Entity.aE)).orElse((Object) null); + } + + public boolean hasCustomName() { + return ((Optional) this.datawatcher.get(Entity.aE)).isPresent(); + } + + public void setCustomNameVisible(boolean flag) { + this.datawatcher.set(Entity.aF, flag); + } + + public boolean getCustomNameVisible() { + return (Boolean) this.datawatcher.get(Entity.aF); + } + + public void enderTeleportTo(double d0, double d1, double d2) { + this.aK = true; + this.setPositionRotation(d0, d1, d2, this.yaw, this.pitch); + this.world.entityJoinedWorld(this, false); + } + + public void a(DataWatcherObject datawatcherobject) {} + + public EnumDirection getDirection() { + return EnumDirection.fromAngle((double) this.yaw); + } + + public EnumDirection getAdjustedDirection() { + return this.getDirection(); + } + + protected ChatHoverable bC() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + MinecraftKey minecraftkey = EntityTypes.getName(this.P()); + + nbttagcompound.setString("id", this.bu()); + if (minecraftkey != null) { + nbttagcompound.setString("type", minecraftkey.toString()); + } + + nbttagcompound.setString("name", IChatBaseComponent.ChatSerializer.a(this.getDisplayName())); + return new ChatHoverable(ChatHoverable.EnumHoverAction.SHOW_ENTITY, new ChatComponentText(nbttagcompound.toString())); + } + + public boolean a(EntityPlayer entityplayer) { + return true; + } + + public AxisAlignedBB getBoundingBox() { + return this.boundingBox; + } + + public void setBoundingBox(AxisAlignedBB axisAlignedBB) { this.a(axisAlignedBB); } // Paper - OBFHELPER + public void a(AxisAlignedBB axisalignedbb) { + // CraftBukkit start - block invalid bounding boxes + double minX = axisalignedbb.minX, + minY = axisalignedbb.minY, + minZ = axisalignedbb.minZ, + maxX = axisalignedbb.maxX, + maxY = axisalignedbb.maxY, + maxZ = axisalignedbb.maxZ; + double len = axisalignedbb.maxX - axisalignedbb.minX; + if (len < 0) maxX = minX; + if (len > 64) maxX = minX + 64.0; + + len = axisalignedbb.maxY - axisalignedbb.minY; + if (len < 0) maxY = minY; + if (len > 64) maxY = minY + 64.0; + + len = axisalignedbb.maxZ - axisalignedbb.minZ; + if (len < 0) maxZ = minZ; + if (len > 64) maxZ = minZ + 64.0; + this.boundingBox = new AxisAlignedBB(minX, minY, minZ, maxX, maxY, maxZ); + // CraftBukkit end + } + + public float getHeadHeight() { + return this.length * 0.85F; + } + + public boolean bG() { + return this.az; + } + + public void n(boolean flag) { + this.az = flag; + } + + public boolean c(int i, ItemStack itemstack) { + return false; + } + + public void sendMessage(IChatBaseComponent ichatbasecomponent) {} + + public BlockPosition getChunkCoordinates() { + return new BlockPosition(this); + } + + public Vec3D bI() { + return new Vec3D(this.locX, this.locY, this.locZ); + } + + public World getWorld() { + return this.world; + } + + @Nullable + public MinecraftServer bK() { + return this.world.getMinecraftServer(); + } + + public EnumInteractionResult a(EntityHuman entityhuman, Vec3D vec3d, EnumHand enumhand) { + return EnumInteractionResult.PASS; + } + + public boolean bL() { + return false; + } + + protected void a(EntityLiving entityliving, Entity entity) { + if (entity instanceof EntityLiving) { + EnchantmentManager.a((EntityLiving) entity, (Entity) entityliving); + } + + EnchantmentManager.b(entityliving, entity); + } + + public void b(EntityPlayer entityplayer) {} + + public void c(EntityPlayer entityplayer) {} + + public float a(EnumBlockRotation enumblockrotation) { + float f = MathHelper.g(this.yaw); + + switch (enumblockrotation) { + case CLOCKWISE_180: + return f + 180.0F; + case COUNTERCLOCKWISE_90: + return f + 270.0F; + case CLOCKWISE_90: + return f + 90.0F; + default: + return f; + } + } + + public float a(EnumBlockMirror enumblockmirror) { + float f = MathHelper.g(this.yaw); + + switch (enumblockmirror) { + case LEFT_RIGHT: + return -f; + case FRONT_BACK: + return 180.0F - f; + default: + return f; + } + } + + public boolean bM() { + return false; + } + + public boolean bN() { + boolean flag = this.aK; + + this.aK = false; + return flag; + } + + @Nullable + public Entity bO() { + return null; + } + + public List bP() { + return (List) (this.passengers.isEmpty() ? Collections.emptyList() : Lists.newArrayList(this.passengers)); + } + + public boolean w(Entity entity) { + Iterator iterator = this.bP().iterator(); + + Entity entity1; + + do { + if (!iterator.hasNext()) { + return false; + } + + entity1 = (Entity) iterator.next(); + } while (!entity1.equals(entity)); + + return true; + } + + public boolean a(Class oclass) { + Iterator iterator = this.bP().iterator(); + + Entity entity; + + do { + if (!iterator.hasNext()) { + return false; + } + + entity = (Entity) iterator.next(); + } while (!oclass.isAssignableFrom(entity.getClass())); + + return true; + } + + public Collection getAllPassengers() { + Set set = Sets.newHashSet(); + Iterator iterator = this.bP().iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + set.add(entity); + entity.a(false, set); + } + + return set; + } + + public boolean bR() { + Set set = Sets.newHashSet(); + + this.a(true, set); + return set.size() == 1; + } + + private void a(boolean flag, Set set) { + Entity entity; + + for (Iterator iterator = this.bP().iterator(); iterator.hasNext(); entity.a(flag, set)) { + entity = (Entity) iterator.next(); + if (!flag || EntityPlayer.class.isAssignableFrom(entity.getClass())) { + set.add(entity); + } + } + + } + + public Entity getRootVehicle() { + Entity entity; + + for (entity = this; entity.isPassenger(); entity = entity.getVehicle()) { + ; + } + + return entity; + } + + public boolean x(Entity entity) { + return this.getRootVehicle() == entity.getRootVehicle(); + } + + public boolean y(Entity entity) { + Iterator iterator = this.bP().iterator(); + + Entity entity1; + + do { + if (!iterator.hasNext()) { + return false; + } + + entity1 = (Entity) iterator.next(); + if (entity1.equals(entity)) { + return true; + } + } while (!entity1.y(entity)); + + return true; + } + + public boolean bT() { + Entity entity = this.bO(); + + return entity instanceof EntityHuman ? ((EntityHuman) entity).dn() : !this.world.isClientSide; + } + + @Nullable + public Entity getVehicle() { + return this.vehicle; + } + + public EnumPistonReaction getPushReaction() { + return EnumPistonReaction.NORMAL; + } + + public SoundCategory getDeathSoundCategory() { return bV();} // Paper - OBFHELPER + public SoundCategory bV() { + return SoundCategory.NEUTRAL; + } + + public int getMaxFireTicks() { + return 1; + } + + public CommandListenerWrapper getCommandListener() { + return new CommandListenerWrapper(this, new Vec3D(this.locX, this.locY, this.locZ), this.aO(), this.world instanceof WorldServer ? (WorldServer) this.world : null, this.y(), this.getDisplayName().getString(), this.getScoreboardDisplayName(), this.world.getMinecraftServer(), this); + } + + protected int y() { + return 0; + } + + public boolean j(int i) { + return this.y() >= i; + } + + public boolean a() { + return this.world.getGameRules().getBoolean("sendCommandFeedback"); + } + + public boolean b() { + return true; + } + + public boolean B_() { + return true; + } + + public void a(ArgumentAnchor.Anchor argumentanchor_anchor, Vec3D vec3d) { + Vec3D vec3d1 = argumentanchor_anchor.a(this); + double d0 = vec3d.x - vec3d1.x; + double d1 = vec3d.y - vec3d1.y; + double d2 = vec3d.z - vec3d1.z; + double d3 = (double) MathHelper.sqrt(d0 * d0 + d2 * d2); + + this.pitch = MathHelper.g((float) (-(MathHelper.c(d1, d3) * 57.2957763671875D))); + this.yaw = MathHelper.g((float) (MathHelper.c(d2, d0) * 57.2957763671875D) - 90.0F); + this.setHeadRotation(this.yaw); + this.lastPitch = this.pitch; + this.lastYaw = this.yaw; + } + + public boolean b(Tag tag) { + AxisAlignedBB axisalignedbb = this.getBoundingBox().shrink(0.001D); + int i = MathHelper.floor(axisalignedbb.minX); + int j = MathHelper.f(axisalignedbb.maxX); + int k = MathHelper.floor(axisalignedbb.minY); + int l = MathHelper.f(axisalignedbb.maxY); + int i1 = MathHelper.floor(axisalignedbb.minZ); + int j1 = MathHelper.f(axisalignedbb.maxZ); + + if (!this.world.isAreaLoaded(i, k, i1, j, l, j1, true)) { + return false; + } else { + double d0 = 0.0D; + boolean flag = this.bw(); + boolean flag1 = false; + Vec3D vec3d = Vec3D.a; + int k1 = 0; + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + for (int l1 = i; l1 < j; ++l1) { + for (int i2 = k; i2 < l; ++i2) { + for (int j2 = i1; j2 < j1; ++j2) { + blockposition_b.c(l1, i2, j2); + Fluid fluid = this.world.getFluid(blockposition_b); + + if (fluid.a(tag)) { + double d1 = (double) ((float) i2 + fluid.getHeight()); + + if (d1 >= axisalignedbb.minY) { + flag1 = true; + d0 = Math.max(d1 - axisalignedbb.minY, d0); + if (flag) { + Vec3D vec3d1 = fluid.a((IWorldReader) this.world, (BlockPosition) blockposition_b); + + if (d0 < 0.4D) { + vec3d1 = vec3d1.a(d0); + } + + vec3d = vec3d.e(vec3d1); + ++k1; + } + } + } + } + } + } + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + + if (vec3d.b() > 0.0D) { + if (k1 > 0) { + vec3d = vec3d.a(1.0D / (double) k1); + } + + if (!(this instanceof EntityHuman)) { + vec3d = vec3d.a(); + } + + double d2 = 0.014D; + + this.motX += vec3d.x * 0.014D; + this.motY += vec3d.y * 0.014D; + this.motZ += vec3d.z * 0.014D; + } + + this.W = d0; + return flag1; + } + } + + public double bY() { + return this.W; + } +} diff --git a/src/main/java/net/minecraft/server/EntityAgeable.java b/src/main/java/net/minecraft/server/EntityAgeable.java new file mode 100644 index 000000000000..aa54e380e4c8 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityAgeable.java @@ -0,0 +1,191 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public abstract class EntityAgeable extends EntityCreature { + + private static final DataWatcherObject bC = DataWatcher.a(EntityAgeable.class, DataWatcherRegistry.i); + protected int a; + protected int b; + protected int c; + private float bD = -1.0F; + private float bE; + public boolean ageLocked; // CraftBukkit + + protected EntityAgeable(EntityTypes entitytypes, World world) { + super(entitytypes, world); + } + + // Spigot start + @Override + public void inactiveTick() + { + super.inactiveTick(); + if ( this.world.isClientSide || this.ageLocked ) + { // CraftBukkit + this.a( this.isBaby() ); + } else + { + int i = this.getAge(); + + if ( i < 0 ) + { + ++i; + this.setAgeRaw( i ); + } else if ( i > 0 ) + { + --i; + this.setAgeRaw( i ); + } + } + } + // Spigot end + + @Nullable + public abstract EntityAgeable createChild(EntityAgeable entityageable); + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + Item item = itemstack.getItem(); + + if (item instanceof ItemMonsterEgg && ((ItemMonsterEgg) item).a(itemstack.getTag(), this.P())) { + if (!this.world.isClientSide) { + EntityAgeable entityageable = this.createChild(this); + + if (entityageable != null) { + entityageable.setAgeRaw(-24000); + entityageable.setPositionRotation(this.locX, this.locY, this.locZ, 0.0F, 0.0F); + this.world.addEntity(entityageable, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG); // CraftBukkit + if (itemstack.hasName()) { + entityageable.setCustomName(itemstack.getName()); + } + + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + } + } + + return true; + } else { + return false; + } + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityAgeable.bC, false); + } + + public int getAge() { + return this.world.isClientSide ? ((Boolean) this.datawatcher.get(EntityAgeable.bC) ? -1 : 1) : this.a; + } + + public void setAge(int i, boolean flag) { + if (ageLocked) return; // Paper - GH-1459 + int j = this.getAge(); + int k = j; + + j += i * 20; + if (j > 0) { + j = 0; + if (k < 0) { + this.l(); + } + } + + int l = j - k; + + this.setAgeRaw(j); + if (flag) { + this.b += l; + if (this.c == 0) { + this.c = 40; + } + } + + if (this.getAge() == 0) { + this.setAgeRaw(this.b); + } + + } + + public void setAge(int i) { + this.setAge(i, false); + } + + public void setAgeRaw(int i) { + this.datawatcher.set(EntityAgeable.bC, i < 0); + this.a = i; + this.a(this.isBaby()); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("Age", this.getAge()); + nbttagcompound.setInt("ForcedAge", this.b); + nbttagcompound.setBoolean("AgeLocked", this.ageLocked); // CraftBukkit + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setAgeRaw(nbttagcompound.getInt("Age")); + this.b = nbttagcompound.getInt("ForcedAge"); + this.ageLocked = nbttagcompound.getBoolean("AgeLocked"); // CraftBukkit + } + + public void a(DataWatcherObject datawatcherobject) { + if (EntityAgeable.bC.equals(datawatcherobject)) { + this.a(this.isBaby()); + } + + super.a(datawatcherobject); + } + + public void movementTick() { + super.movementTick(); + if (this.world.isClientSide || ageLocked) { // CraftBukkit + if (this.c > 0) { + if (this.c % 4 == 0) { + this.world.addParticle(Particles.z, this.locX + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width, this.locY + 0.5D + (double) (this.random.nextFloat() * this.length), this.locZ + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width, 0.0D, 0.0D, 0.0D); + } + + --this.c; + } + } else { + int i = this.getAge(); + + if (i < 0) { + ++i; + this.setAgeRaw(i); + if (i == 0) { + this.l(); + } + } else if (i > 0) { + --i; + this.setAgeRaw(i); + } + } + + } + + protected void l() {} + + public boolean isBaby() { + return this.getAge() < 0; + } + + public void a(boolean flag) { + this.a(flag ? 0.5F : 1.0F); + } + + public final void setSize(float f, float f1) { + this.bD = f; + this.bE = f1; + this.a(1.0F); + } + + protected final void a(float f) { + super.setSize(this.bD * f, this.bE * f); + } +} diff --git a/src/main/java/net/minecraft/server/EntityAnimal.java b/src/main/java/net/minecraft/server/EntityAnimal.java new file mode 100644 index 000000000000..a7481df5fbba --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityAnimal.java @@ -0,0 +1,173 @@ +package net.minecraft.server; + +import java.util.UUID; +import javax.annotation.Nullable; + +public abstract class EntityAnimal extends EntityAgeable implements IAnimal { + + protected Block bF; + public int bC; // CraftBukkit - private -> public + public UUID breedCause; + public ItemStack breedItem; // CraftBukkit - Add breedItem variable + + protected EntityAnimal(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.bF = Blocks.GRASS_BLOCK; + } + + protected void mobTick() { + if (this.getAge() != 0) { + this.bC = 0; + } + + super.mobTick(); + } + + public void movementTick() { + super.movementTick(); + if (this.getAge() != 0) { + this.bC = 0; + } + + if (this.bC > 0) { + --this.bC; + if (this.bC % 10 == 0) { + double d0 = this.random.nextGaussian() * 0.02D; + double d1 = this.random.nextGaussian() * 0.02D; + double d2 = this.random.nextGaussian() * 0.02D; + + this.world.addParticle(Particles.A, this.locX + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width, this.locY + 0.5D + (double) (this.random.nextFloat() * this.length), this.locZ + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width, d0, d1, d2); + } + } + + } + + /* CraftBukkit start + // Function disabled as it has no special function anymore after + // setSitting is disabled. + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else { + this.bC = 0; + return super.damageEntity(damagesource, f); + } + } + // CraftBukkit end */ + + public float a(BlockPosition blockposition, IWorldReader iworldreader) { + return iworldreader.getType(blockposition.down()).getBlock() == this.bF ? 10.0F : iworldreader.A(blockposition) - 0.5F; + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("InLove", this.bC); + if (this.breedCause != null) { + nbttagcompound.a("LoveCause", this.breedCause); + } + + } + + public double aI() { + return 0.14D; + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.bC = nbttagcompound.getInt("InLove"); + this.breedCause = nbttagcompound.b("LoveCause") ? nbttagcompound.a("LoveCause") : null; + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.getBoundingBox().minY); + int k = MathHelper.floor(this.locZ); + BlockPosition blockposition = new BlockPosition(i, j, k); + + return generatoraccess.getType(blockposition.down()).getBlock() == this.bF && generatoraccess.getLightLevel(blockposition, 0) > 8 && super.a(generatoraccess, flag); + } + + public int z() { + return 120; + } + + public boolean isTypeNotPersistent() { + return false; + } + + protected int getExpValue(EntityHuman entityhuman) { + return 1 + this.world.random.nextInt(3); + } + + public boolean f(ItemStack itemstack) { + return itemstack.getItem() == Items.WHEAT; + } + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (this.f(itemstack)) { + if (this.getAge() == 0 && this.dD()) { + this.a(entityhuman, itemstack); + this.f(entityhuman); + return true; + } + + if (this.isBaby()) { + this.a(entityhuman, itemstack); + this.setAge((int) ((float) (-this.getAge() / 20) * 0.1F), true); + return true; + } + } + + return super.a(entityhuman, enumhand); + } + + protected void a(EntityHuman entityhuman, ItemStack itemstack) { + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + } + + public boolean dD() { + return this.bC <= 0; + } + + public void f(@Nullable EntityHuman entityhuman) { + this.bC = 600; + if (entityhuman != null) { + this.breedCause = entityhuman.getUniqueID(); + } + this.breedItem = entityhuman.inventory.getItemInHand(); // CraftBukkit + + this.world.broadcastEntityEffect(this, (byte) 18); + } + + public void d(int i) { + this.bC = i; + } + + @Nullable + public EntityPlayer getBreedCause() { + if (this.breedCause == null) { + return null; + } else { + EntityHuman entityhuman = this.world.b(this.breedCause); + + return entityhuman instanceof EntityPlayer ? (EntityPlayer) entityhuman : null; + } + } + + public boolean isInLove() { + return this.bC > 0; + } + + public void resetLove() { + this.bC = 0; + } + + public boolean mate(EntityAnimal entityanimal) { + return entityanimal == this ? false : (entityanimal.getClass() != this.getClass() ? false : this.isInLove() && entityanimal.isInLove()); + } +} diff --git a/src/main/java/net/minecraft/server/EntityAreaEffectCloud.java b/src/main/java/net/minecraft/server/EntityAreaEffectCloud.java new file mode 100644 index 000000000000..b2814c0e7063 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityAreaEffectCloud.java @@ -0,0 +1,448 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.Map.Entry; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.entity.LivingEntity; +// CraftBukkit end + +public class EntityAreaEffectCloud extends Entity { + + private static final Logger a = LogManager.getLogger(); + private static final DataWatcherObject b = DataWatcher.a(EntityAreaEffectCloud.class, DataWatcherRegistry.c); + private static final DataWatcherObject c = DataWatcher.a(EntityAreaEffectCloud.class, DataWatcherRegistry.b); + private static final DataWatcherObject d = DataWatcher.a(EntityAreaEffectCloud.class, DataWatcherRegistry.i); + private static final DataWatcherObject e = DataWatcher.a(EntityAreaEffectCloud.class, DataWatcherRegistry.j); + private PotionRegistry potionRegistry; + public List effects; + private final Map h; + private int aw; + public int waitTime; + public int reapplicationDelay; + private boolean hasColor; + public int durationOnUse; + public float radiusOnUse; + public float radiusPerTick; + private EntityLiving aD; + private UUID aE; + + public EntityAreaEffectCloud(World world) { + super(EntityTypes.AREA_EFFECT_CLOUD, world); + this.potionRegistry = Potions.EMPTY; + this.effects = Lists.newArrayList(); + this.h = Maps.newHashMap(); + this.aw = 600; + this.waitTime = 20; + this.reapplicationDelay = 20; + this.noclip = true; + this.fireProof = true; + this.setRadius(3.0F); + } + + public EntityAreaEffectCloud(World world, double d0, double d1, double d2) { + this(world); + this.setPosition(d0, d1, d2); + } + + protected void x_() { + this.getDataWatcher().register(EntityAreaEffectCloud.c, 0); + this.getDataWatcher().register(EntityAreaEffectCloud.b, 0.5F); + this.getDataWatcher().register(EntityAreaEffectCloud.d, false); + this.getDataWatcher().register(EntityAreaEffectCloud.e, Particles.s); + } + + public void setRadius(float f) { + double d0 = this.locX; + double d1 = this.locY; + double d2 = this.locZ; + + this.setSize(f * 2.0F, 0.5F); + this.setPosition(d0, d1, d2); + if (!this.world.isClientSide) { + this.getDataWatcher().set(EntityAreaEffectCloud.b, f); + } + + } + + public float getRadius() { + return (Float) this.getDataWatcher().get(EntityAreaEffectCloud.b); + } + + public void a(PotionRegistry potionregistry) { + this.potionRegistry = potionregistry; + if (!this.hasColor) { + this.x(); + } + + } + + private void x() { + if (this.potionRegistry == Potions.EMPTY && this.effects.isEmpty()) { + this.getDataWatcher().set(EntityAreaEffectCloud.c, 0); + } else { + this.getDataWatcher().set(EntityAreaEffectCloud.c, PotionUtil.a((Collection) PotionUtil.a(this.potionRegistry, (Collection) this.effects))); + } + + } + + public void a(MobEffect mobeffect) { + this.effects.add(mobeffect); + if (!this.hasColor) { + this.x(); + } + + } + + // CraftBukkit start accessor methods + public void refreshEffects() { + if (!this.hasColor) { + this.getDataWatcher().set(EntityAreaEffectCloud.c, Integer.valueOf(PotionUtil.a((Collection) PotionUtil.a(this.potionRegistry, (Collection) this.effects)))); // PAIL: rename + } + } + + public String getType() { + return ((MinecraftKey) IRegistry.POTION.getKey(this.potionRegistry)).toString(); + } + + public void setType(String string) { + a(IRegistry.POTION.get(new MinecraftKey(string))); + } + // CraftBukkit end + + public int getColor() { + return (Integer) this.getDataWatcher().get(EntityAreaEffectCloud.c); + } + + public void setColor(int i) { + this.hasColor = true; + this.getDataWatcher().set(EntityAreaEffectCloud.c, i); + } + + public ParticleParam getParticle() { + return (ParticleParam) this.getDataWatcher().get(EntityAreaEffectCloud.e); + } + + public void setParticle(ParticleParam particleparam) { + this.getDataWatcher().set(EntityAreaEffectCloud.e, particleparam); + } + + protected void a(boolean flag) { + this.getDataWatcher().set(EntityAreaEffectCloud.d, flag); + } + + public boolean l() { + return (Boolean) this.getDataWatcher().get(EntityAreaEffectCloud.d); + } + + public int getDuration() { + return this.aw; + } + + public void setDuration(int i) { + this.aw = i; + } + + public void tick() { + super.tick(); + boolean flag = this.l(); + float f = this.getRadius(); + + if (this.world.isClientSide) { + ParticleParam particleparam = this.getParticle(); + float f1; + float f2; + float f3; + int i; + int j; + int k; + + if (flag) { + if (this.random.nextBoolean()) { + for (int l = 0; l < 2; ++l) { + float f4 = this.random.nextFloat() * 6.2831855F; + + f1 = MathHelper.c(this.random.nextFloat()) * 0.2F; + f2 = MathHelper.cos(f4) * f1; + f3 = MathHelper.sin(f4) * f1; + if (particleparam.b() == Particles.s) { + int i1 = this.random.nextBoolean() ? 16777215 : this.getColor(); + + i = i1 >> 16 & 255; + j = i1 >> 8 & 255; + k = i1 & 255; + this.world.b(particleparam, this.locX + (double) f2, this.locY, this.locZ + (double) f3, (double) ((float) i / 255.0F), (double) ((float) j / 255.0F), (double) ((float) k / 255.0F)); + } else { + this.world.b(particleparam, this.locX + (double) f2, this.locY, this.locZ + (double) f3, 0.0D, 0.0D, 0.0D); + } + } + } + } else { + float f5 = 3.1415927F * f * f; + + for (int j1 = 0; (float) j1 < f5; ++j1) { + f1 = this.random.nextFloat() * 6.2831855F; + f2 = MathHelper.c(this.random.nextFloat()) * f; + f3 = MathHelper.cos(f1) * f2; + float f6 = MathHelper.sin(f1) * f2; + + if (particleparam.b() == Particles.s) { + i = this.getColor(); + j = i >> 16 & 255; + k = i >> 8 & 255; + int k1 = i & 255; + + this.world.b(particleparam, this.locX + (double) f3, this.locY, this.locZ + (double) f6, (double) ((float) j / 255.0F), (double) ((float) k / 255.0F), (double) ((float) k1 / 255.0F)); + } else { + this.world.b(particleparam, this.locX + (double) f3, this.locY, this.locZ + (double) f6, (0.5D - this.random.nextDouble()) * 0.15D, 0.009999999776482582D, (0.5D - this.random.nextDouble()) * 0.15D); + } + } + } + } else { + if (this.ticksLived >= this.waitTime + this.aw) { + this.die(); + return; + } + + boolean flag1 = this.ticksLived < this.waitTime; + + if (flag != flag1) { + this.a(flag1); + } + + if (flag1) { + return; + } + + if (this.radiusPerTick != 0.0F) { + f += this.radiusPerTick; + if (f < 0.5F) { + this.die(); + return; + } + + this.setRadius(f); + } + + if (this.ticksLived % 5 == 0) { + Iterator iterator = this.h.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + + if (this.ticksLived >= (Integer) entry.getValue()) { + iterator.remove(); + } + } + + List list = Lists.newArrayList(); + Iterator iterator1 = this.potionRegistry.a().iterator(); + + while (iterator1.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator1.next(); + + list.add(new MobEffect(mobeffect.getMobEffect(), mobeffect.getDuration() / 4, mobeffect.getAmplifier(), mobeffect.isAmbient(), mobeffect.isShowParticles())); + } + + list.addAll(this.effects); + if (list.isEmpty()) { + this.h.clear(); + } else { + List list1 = this.world.a(EntityLiving.class, this.getBoundingBox()); + + if (!list1.isEmpty()) { + Iterator iterator2 = list1.iterator(); + + List entities = new java.util.ArrayList(); // CraftBukkit + while (iterator2.hasNext()) { + EntityLiving entityliving = (EntityLiving) iterator2.next(); + + if (!this.h.containsKey(entityliving) && entityliving.de()) { + double d0 = entityliving.locX - this.locX; + double d1 = entityliving.locZ - this.locZ; + double d2 = d0 * d0 + d1 * d1; + + if (d2 <= (double) (f * f)) { + // CraftBukkit start + entities.add((LivingEntity) entityliving.getBukkitEntity()); + } + } + } + org.bukkit.event.entity.AreaEffectCloudApplyEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callAreaEffectCloudApplyEvent(this, entities); + if (!event.isCancelled()) { + for (LivingEntity entity : event.getAffectedEntities()) { + if (entity instanceof CraftLivingEntity) { + EntityLiving entityliving = ((CraftLivingEntity) entity).getHandle(); + // CraftBukkit end + this.h.put(entityliving, this.ticksLived + this.reapplicationDelay); + Iterator iterator3 = list.iterator(); + + while (iterator3.hasNext()) { + MobEffect mobeffect1 = (MobEffect) iterator3.next(); + + if (mobeffect1.getMobEffect().isInstant()) { + mobeffect1.getMobEffect().applyInstantEffect(this, this.getSource(), entityliving, mobeffect1.getAmplifier(), 0.5D); + } else { + entityliving.addEffect(new MobEffect(mobeffect1), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.AREA_EFFECT_CLOUD); // CraftBukkit + } + } + + if (this.radiusOnUse != 0.0F) { + f += this.radiusOnUse; + if (f < 0.5F) { + this.die(); + return; + } + + this.setRadius(f); + } + + if (this.durationOnUse != 0) { + this.aw += this.durationOnUse; + if (this.aw <= 0) { + this.die(); + return; + } + } + } + } + } + } + } + } + } + + } + + public void setRadiusOnUse(float f) { + this.radiusOnUse = f; + } + + public void setRadiusPerTick(float f) { + this.radiusPerTick = f; + } + + public void setWaitTime(int i) { + this.waitTime = i; + } + + public void setSource(@Nullable EntityLiving entityliving) { + this.aD = entityliving; + this.aE = entityliving == null ? null : entityliving.getUniqueID(); + } + + @Nullable + public EntityLiving getSource() { + if (this.aD == null && this.aE != null && this.world instanceof WorldServer) { + Entity entity = ((WorldServer) this.world).getEntity(this.aE); + + if (entity instanceof EntityLiving) { + this.aD = (EntityLiving) entity; + } + } + + return this.aD; + } + + protected void a(NBTTagCompound nbttagcompound) { + this.ticksLived = nbttagcompound.getInt("Age"); + this.aw = nbttagcompound.getInt("Duration"); + this.waitTime = nbttagcompound.getInt("WaitTime"); + this.reapplicationDelay = nbttagcompound.getInt("ReapplicationDelay"); + this.durationOnUse = nbttagcompound.getInt("DurationOnUse"); + this.radiusOnUse = nbttagcompound.getFloat("RadiusOnUse"); + this.radiusPerTick = nbttagcompound.getFloat("RadiusPerTick"); + this.setRadius(nbttagcompound.getFloat("Radius")); + this.aE = nbttagcompound.a("OwnerUUID"); + if (nbttagcompound.hasKeyOfType("Particle", 8)) { + try { + this.setParticle(ArgumentParticle.b(new StringReader(nbttagcompound.getString("Particle")))); + } catch (CommandSyntaxException commandsyntaxexception) { + EntityAreaEffectCloud.a.warn("Couldn't load custom particle {}", nbttagcompound.getString("Particle"), commandsyntaxexception); + } + } + + if (nbttagcompound.hasKeyOfType("Color", 99)) { + this.setColor(nbttagcompound.getInt("Color")); + } + + if (nbttagcompound.hasKeyOfType("Potion", 8)) { + this.a(PotionUtil.c(nbttagcompound)); + } + + if (nbttagcompound.hasKeyOfType("Effects", 9)) { + NBTTagList nbttaglist = nbttagcompound.getList("Effects", 10); + + this.effects.clear(); + + for (int i = 0; i < nbttaglist.size(); ++i) { + MobEffect mobeffect = MobEffect.b(nbttaglist.getCompound(i)); + + if (mobeffect != null) { + this.a(mobeffect); + } + } + } + + } + + protected void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setInt("Age", this.ticksLived); + nbttagcompound.setInt("Duration", this.aw); + nbttagcompound.setInt("WaitTime", this.waitTime); + nbttagcompound.setInt("ReapplicationDelay", this.reapplicationDelay); + nbttagcompound.setInt("DurationOnUse", this.durationOnUse); + nbttagcompound.setFloat("RadiusOnUse", this.radiusOnUse); + nbttagcompound.setFloat("RadiusPerTick", this.radiusPerTick); + nbttagcompound.setFloat("Radius", this.getRadius()); + nbttagcompound.setString("Particle", this.getParticle().a()); + if (this.aE != null) { + nbttagcompound.a("OwnerUUID", this.aE); + } + + if (this.hasColor) { + nbttagcompound.setInt("Color", this.getColor()); + } + + if (this.potionRegistry != Potions.EMPTY && this.potionRegistry != null) { + nbttagcompound.setString("Potion", IRegistry.POTION.getKey(this.potionRegistry).toString()); + } + + if (!this.effects.isEmpty()) { + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = this.effects.iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + nbttaglist.add((NBTBase) mobeffect.a(new NBTTagCompound())); + } + + nbttagcompound.set("Effects", nbttaglist); + } + + } + + public void a(DataWatcherObject datawatcherobject) { + if (EntityAreaEffectCloud.b.equals(datawatcherobject)) { + this.setRadius(this.getRadius()); + } + + super.a(datawatcherobject); + } + + public EnumPistonReaction getPushReaction() { + return EnumPistonReaction.IGNORE; + } +} diff --git a/src/main/java/net/minecraft/server/EntityArmorStand.java b/src/main/java/net/minecraft/server/EntityArmorStand.java new file mode 100644 index 000000000000..a5cc5e2842c4 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityArmorStand.java @@ -0,0 +1,816 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.craftbukkit.CraftEquipmentSlot; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerArmorStandManipulateEvent; +// CraftBukkit end + +public class EntityArmorStand extends EntityLiving { + + private static final Vector3f bx = new Vector3f(0.0F, 0.0F, 0.0F); + private static final Vector3f by = new Vector3f(0.0F, 0.0F, 0.0F); + private static final Vector3f bz = new Vector3f(-10.0F, 0.0F, -10.0F); + private static final Vector3f bA = new Vector3f(-15.0F, 0.0F, 10.0F); + private static final Vector3f bB = new Vector3f(-1.0F, 0.0F, -1.0F); + private static final Vector3f bC = new Vector3f(1.0F, 0.0F, 1.0F); + public static final DataWatcherObject a = DataWatcher.a(EntityArmorStand.class, DataWatcherRegistry.a); + public static final DataWatcherObject b = DataWatcher.a(EntityArmorStand.class, DataWatcherRegistry.k); + public static final DataWatcherObject c = DataWatcher.a(EntityArmorStand.class, DataWatcherRegistry.k); + public static final DataWatcherObject d = DataWatcher.a(EntityArmorStand.class, DataWatcherRegistry.k); + public static final DataWatcherObject e = DataWatcher.a(EntityArmorStand.class, DataWatcherRegistry.k); + public static final DataWatcherObject f = DataWatcher.a(EntityArmorStand.class, DataWatcherRegistry.k); + public static final DataWatcherObject g = DataWatcher.a(EntityArmorStand.class, DataWatcherRegistry.k); + private static final Predicate bD = (entity) -> { + return entity instanceof EntityMinecartAbstract && ((EntityMinecartAbstract) entity).v() == EntityMinecartAbstract.EnumMinecartType.RIDEABLE; + }; + private final NonNullList bE; + private final NonNullList bF; + private boolean bG; + public long h; + private int bH;public void setDisabledSlots(int i) { bH = i;} public int getDisabledSlots() { return bH ;} // Paper - OBFHELPER + private boolean bI; + public Vector3f headPose; + public Vector3f bodyPose; + public Vector3f leftArmPose; + public Vector3f rightArmPose; + public Vector3f leftLegPose; + public Vector3f rightLegPose; + public boolean canMove = true; // Paper + // Paper start - Allow ArmorStands not to tick + public boolean canTick = true; + private boolean noTickPoseDirty = false; + private boolean noTickEquipmentDirty = false; + // Paper end + + public EntityArmorStand(World world) { + super(EntityTypes.ARMOR_STAND, world); + this.bE = NonNullList.a(2, ItemStack.a); + this.bF = NonNullList.a(4, ItemStack.a); + this.headPose = EntityArmorStand.bx; + this.bodyPose = EntityArmorStand.by; + this.leftArmPose = EntityArmorStand.bz; + this.rightArmPose = EntityArmorStand.bA; + this.leftLegPose = EntityArmorStand.bB; + this.rightLegPose = EntityArmorStand.bC; + this.noclip = this.isNoGravity(); + if (world != null) this.canTick = world.paperConfig.armorStandTick; // Paper - armour stand ticking + this.setSize(0.5F, 1.975F); + this.Q = 0.0F; + } + + public EntityArmorStand(World world, double d0, double d1, double d2) { + this(world); + this.setPosition(d0, d1, d2); + } + + // CraftBukkit start - SPIGOT-3607, SPIGOT-3637 + @Override + public float getBukkitYaw() { + return this.yaw; + } + // CraftBukkit end + + public final void setSize(float f, float f1) { + double d0 = this.locX; + double d1 = this.locY; + double d2 = this.locZ; + float f2 = this.isMarker() ? 0.0F : (this.isBaby() ? 0.5F : 1.0F); + + super.setSize(f * f2, f1 * f2); + this.setPosition(d0, d1, d2); + } + + public boolean cP() { + return super.cP() && !this.isNoGravity(); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityArmorStand.a, (byte) 0); + this.datawatcher.register(EntityArmorStand.b, EntityArmorStand.bx); + this.datawatcher.register(EntityArmorStand.c, EntityArmorStand.by); + this.datawatcher.register(EntityArmorStand.d, EntityArmorStand.bz); + this.datawatcher.register(EntityArmorStand.e, EntityArmorStand.bA); + this.datawatcher.register(EntityArmorStand.f, EntityArmorStand.bB); + this.datawatcher.register(EntityArmorStand.g, EntityArmorStand.bC); + } + + public Iterable aS() { + return this.bE; + } + + public Iterable getArmorItems() { + return this.bF; + } + + public ItemStack getEquipment(EnumItemSlot enumitemslot) { + switch (enumitemslot.a()) { + case HAND: + return (ItemStack) this.bE.get(enumitemslot.b()); + case ARMOR: + return (ItemStack) this.bF.get(enumitemslot.b()); + default: + return ItemStack.a; + } + } + + public void setSlot(EnumItemSlot enumitemslot, ItemStack itemstack) { + switch (enumitemslot.a()) { + case HAND: + this.b(itemstack); + this.bE.set(enumitemslot.b(), itemstack); + break; + case ARMOR: + this.b(itemstack); + this.bF.set(enumitemslot.b(), itemstack); + } + + this.noTickEquipmentDirty = true; // Paper - Allow equipment to be updated even when tick disabled + } + + public boolean c(int i, ItemStack itemstack) { + EnumItemSlot enumitemslot; + + if (i == 98) { + enumitemslot = EnumItemSlot.MAINHAND; + } else if (i == 99) { + enumitemslot = EnumItemSlot.OFFHAND; + } else if (i == 100 + EnumItemSlot.HEAD.b()) { + enumitemslot = EnumItemSlot.HEAD; + } else if (i == 100 + EnumItemSlot.CHEST.b()) { + enumitemslot = EnumItemSlot.CHEST; + } else if (i == 100 + EnumItemSlot.LEGS.b()) { + enumitemslot = EnumItemSlot.LEGS; + } else { + if (i != 100 + EnumItemSlot.FEET.b()) { + return false; + } + + enumitemslot = EnumItemSlot.FEET; + } + + if (!itemstack.isEmpty() && !EntityInsentient.b(enumitemslot, itemstack) && enumitemslot != EnumItemSlot.HEAD) { + return false; + } else { + this.setSlot(enumitemslot, itemstack); + return true; + } + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + NBTTagList nbttaglist = new NBTTagList(); + + NBTTagCompound nbttagcompound1; + + for (Iterator iterator = this.bF.iterator(); iterator.hasNext(); nbttaglist.add((NBTBase) nbttagcompound1)) { + ItemStack itemstack = (ItemStack) iterator.next(); + + nbttagcompound1 = new NBTTagCompound(); + if (!itemstack.isEmpty()) { + itemstack.save(nbttagcompound1); + } + } + + nbttagcompound.set("ArmorItems", nbttaglist); + NBTTagList nbttaglist1 = new NBTTagList(); + + NBTTagCompound nbttagcompound2; + + for (Iterator iterator1 = this.bE.iterator(); iterator1.hasNext(); nbttaglist1.add((NBTBase) nbttagcompound2)) { + ItemStack itemstack1 = (ItemStack) iterator1.next(); + + nbttagcompound2 = new NBTTagCompound(); + if (!itemstack1.isEmpty()) { + itemstack1.save(nbttagcompound2); + } + } + + nbttagcompound.set("HandItems", nbttaglist1); + nbttagcompound.setBoolean("Invisible", this.isInvisible()); + nbttagcompound.setBoolean("Small", this.isSmall()); + nbttagcompound.setBoolean("ShowArms", this.hasArms()); + nbttagcompound.setInt("DisabledSlots", this.bH); + nbttagcompound.setBoolean("NoBasePlate", this.hasBasePlate()); + if (this.isMarker()) { + nbttagcompound.setBoolean("Marker", this.isMarker()); + } + + nbttagcompound.set("Pose", this.z()); + nbttagcompound.setBoolean("Paper.CanTick", this.canTick); // Paper - persist no tick setting + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + NBTTagList nbttaglist; + int i; + + if (nbttagcompound.hasKeyOfType("ArmorItems", 9)) { + nbttaglist = nbttagcompound.getList("ArmorItems", 10); + + for (i = 0; i < this.bF.size(); ++i) { + this.bF.set(i, ItemStack.a(nbttaglist.getCompound(i))); + } + } + + if (nbttagcompound.hasKeyOfType("HandItems", 9)) { + nbttaglist = nbttagcompound.getList("HandItems", 10); + + for (i = 0; i < this.bE.size(); ++i) { + this.bE.set(i, ItemStack.a(nbttaglist.getCompound(i))); + } + } + + this.setInvisible(nbttagcompound.getBoolean("Invisible")); + this.setSmall(nbttagcompound.getBoolean("Small")); + this.setArms(nbttagcompound.getBoolean("ShowArms")); + this.bH = nbttagcompound.getInt("DisabledSlots"); + this.setBasePlate(nbttagcompound.getBoolean("NoBasePlate")); + this.setMarker(nbttagcompound.getBoolean("Marker")); + this.bI = !this.isMarker(); + this.noclip = this.isNoGravity(); + // Paper start - persist no tick + if (nbttagcompound.hasKey("Paper.CanTick")) { + this.canTick = nbttagcompound.getBoolean("Paper.CanTick"); + } + // Paper end + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Pose"); + + this.g(nbttagcompound1); + } + + private void g(NBTTagCompound nbttagcompound) { + NBTTagList nbttaglist = nbttagcompound.getList("Head", 5); + + this.setHeadPose(nbttaglist.isEmpty() ? EntityArmorStand.bx : new Vector3f(nbttaglist)); + NBTTagList nbttaglist1 = nbttagcompound.getList("Body", 5); + + this.setBodyPose(nbttaglist1.isEmpty() ? EntityArmorStand.by : new Vector3f(nbttaglist1)); + NBTTagList nbttaglist2 = nbttagcompound.getList("LeftArm", 5); + + this.setLeftArmPose(nbttaglist2.isEmpty() ? EntityArmorStand.bz : new Vector3f(nbttaglist2)); + NBTTagList nbttaglist3 = nbttagcompound.getList("RightArm", 5); + + this.setRightArmPose(nbttaglist3.isEmpty() ? EntityArmorStand.bA : new Vector3f(nbttaglist3)); + NBTTagList nbttaglist4 = nbttagcompound.getList("LeftLeg", 5); + + this.setLeftLegPose(nbttaglist4.isEmpty() ? EntityArmorStand.bB : new Vector3f(nbttaglist4)); + NBTTagList nbttaglist5 = nbttagcompound.getList("RightLeg", 5); + + this.setRightLegPose(nbttaglist5.isEmpty() ? EntityArmorStand.bC : new Vector3f(nbttaglist5)); + } + + private NBTTagCompound z() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + if (!EntityArmorStand.bx.equals(this.headPose)) { + nbttagcompound.set("Head", this.headPose.a()); + } + + if (!EntityArmorStand.by.equals(this.bodyPose)) { + nbttagcompound.set("Body", this.bodyPose.a()); + } + + if (!EntityArmorStand.bz.equals(this.leftArmPose)) { + nbttagcompound.set("LeftArm", this.leftArmPose.a()); + } + + if (!EntityArmorStand.bA.equals(this.rightArmPose)) { + nbttagcompound.set("RightArm", this.rightArmPose.a()); + } + + if (!EntityArmorStand.bB.equals(this.leftLegPose)) { + nbttagcompound.set("LeftLeg", this.leftLegPose.a()); + } + + if (!EntityArmorStand.bC.equals(this.rightLegPose)) { + nbttagcompound.set("RightLeg", this.rightLegPose.a()); + } + + return nbttagcompound; + } + + public boolean isCollidable() { + return false; + } + + protected void C(Entity entity) {} + + protected void cN() { + List list = this.world.getEntities(this, this.getBoundingBox(), EntityArmorStand.bD); + + for (int i = 0; i < list.size(); ++i) { + Entity entity = (Entity) list.get(i); + + if (this.h(entity) <= 0.2D) { + entity.collide(this); + } + } + + } + + public EnumInteractionResult a(EntityHuman entityhuman, Vec3D vec3d, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (!this.isMarker() && itemstack.getItem() != Items.NAME_TAG) { + if (!this.world.isClientSide && !entityhuman.isSpectator()) { + EnumItemSlot enumitemslot = EntityInsentient.e(itemstack); + + if (itemstack.isEmpty()) { + EnumItemSlot enumitemslot1 = this.b(vec3d); + EnumItemSlot enumitemslot2 = this.c(enumitemslot1) ? enumitemslot : enumitemslot1; + + if (this.a(enumitemslot2)) { + this.a(entityhuman, enumitemslot2, itemstack, enumhand); + } + } else { + if (this.c(enumitemslot)) { + return EnumInteractionResult.FAIL; + } + + if (enumitemslot.a() == EnumItemSlot.Function.HAND && !this.hasArms()) { + return EnumInteractionResult.FAIL; + } + + this.a(entityhuman, enumitemslot, itemstack, enumhand); + } + + return EnumInteractionResult.SUCCESS; + } else { + return EnumInteractionResult.SUCCESS; + } + } else { + return EnumInteractionResult.PASS; + } + } + + protected EnumItemSlot b(Vec3D vec3d) { + EnumItemSlot enumitemslot = EnumItemSlot.MAINHAND; + boolean flag = this.isSmall(); + double d0 = flag ? vec3d.y * 2.0D : vec3d.y; + EnumItemSlot enumitemslot1 = EnumItemSlot.FEET; + + if (d0 >= 0.1D && d0 < 0.1D + (flag ? 0.8D : 0.45D) && this.a(enumitemslot1)) { + enumitemslot = EnumItemSlot.FEET; + } else if (d0 >= 0.9D + (flag ? 0.3D : 0.0D) && d0 < 0.9D + (flag ? 1.0D : 0.7D) && this.a(EnumItemSlot.CHEST)) { + enumitemslot = EnumItemSlot.CHEST; + } else if (d0 >= 0.4D && d0 < 0.4D + (flag ? 1.0D : 0.8D) && this.a(EnumItemSlot.LEGS)) { + enumitemslot = EnumItemSlot.LEGS; + } else if (d0 >= 1.6D && this.a(EnumItemSlot.HEAD)) { + enumitemslot = EnumItemSlot.HEAD; + } else if (!this.a(EnumItemSlot.MAINHAND) && this.a(EnumItemSlot.OFFHAND)) { + enumitemslot = EnumItemSlot.OFFHAND; + } + + return enumitemslot; + } + + public boolean isSlotDisabled(EnumItemSlot slot) { return this.c(slot); } // Paper - OBFHELPER + public boolean c(EnumItemSlot enumitemslot) { + return (this.bH & 1 << enumitemslot.c()) != 0 || enumitemslot.a() == EnumItemSlot.Function.HAND && !this.hasArms(); + } + + private void a(EntityHuman entityhuman, EnumItemSlot enumitemslot, ItemStack itemstack, EnumHand enumhand) { + ItemStack itemstack1 = this.getEquipment(enumitemslot); + + if (itemstack1.isEmpty() || (this.bH & 1 << enumitemslot.c() + 8) == 0) { + if (!itemstack1.isEmpty() || (this.bH & 1 << enumitemslot.c() + 16) == 0) { + ItemStack itemstack2; + // CraftBukkit start + org.bukkit.inventory.ItemStack armorStandItem = CraftItemStack.asCraftMirror(itemstack1); + org.bukkit.inventory.ItemStack playerHeldItem = CraftItemStack.asCraftMirror(itemstack); + + Player player = (Player) entityhuman.getBukkitEntity(); + ArmorStand self = (ArmorStand) this.getBukkitEntity(); + + EquipmentSlot slot = CraftEquipmentSlot.getSlot(enumitemslot); + PlayerArmorStandManipulateEvent armorStandManipulateEvent = new PlayerArmorStandManipulateEvent(player,self,playerHeldItem,armorStandItem,slot); + this.world.getServer().getPluginManager().callEvent(armorStandManipulateEvent); + + if (armorStandManipulateEvent.isCancelled()) { + return; + } + // CraftBukkit end + + if (entityhuman.abilities.canInstantlyBuild && itemstack1.isEmpty() && !itemstack.isEmpty()) { + itemstack2 = itemstack.cloneItemStack(); + itemstack2.setCount(1); + this.setSlot(enumitemslot, itemstack2); + } else if (!itemstack.isEmpty() && itemstack.getCount() > 1) { + if (itemstack1.isEmpty()) { + itemstack2 = itemstack.cloneItemStack(); + itemstack2.setCount(1); + this.setSlot(enumitemslot, itemstack2); + itemstack.subtract(1); + } + } else { + this.setSlot(enumitemslot, itemstack); + entityhuman.a(enumhand, itemstack1); + } + } + } + } + + public boolean damageEntity(DamageSource damagesource, float f) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, damagesource, f)) { + return false; + } + // CraftBukkit end + if (!this.world.isClientSide && !this.dead) { + if (DamageSource.OUT_OF_WORLD.equals(damagesource)) { + this.killEntity(); // CraftBukkit - this.die() -> this.killEntity() + return false; + } else if (!this.isInvulnerable(damagesource) && !this.bG && !this.isMarker()) { + if (damagesource.isExplosion()) { + this.D(); + this.killEntity(); // CraftBukkit - this.die() -> this.killEntity() + return false; + } else if (DamageSource.FIRE.equals(damagesource)) { + if (this.isBurning()) { + this.a(0.15F); + } else { + this.setOnFire(5); + } + + return false; + } else if (DamageSource.BURN.equals(damagesource) && this.getHealth() > 0.5F) { + this.a(4.0F); + return false; + } else { + boolean flag = damagesource.j() instanceof EntityArrow; + boolean flag1 = "player".equals(damagesource.q()); + + if (!flag1 && !flag) { + return false; + } else if (damagesource.getEntity() instanceof EntityHuman && !((EntityHuman) damagesource.getEntity()).abilities.mayBuild) { + return false; + } else if (damagesource.v()) { + this.F(); + this.A(); + this.killEntity(); // CraftBukkit - this.die() -> this.killEntity() + return false; + } else { + long i = this.world.getTime(); + + if (i - this.h > 5L && !flag) { + this.world.broadcastEntityEffect(this, (byte) 32); + this.h = i; + } else { + this.B(); + this.A(); + this.killEntity(); // CraftBukkit - this.die() -> this.killEntity() + } + + return true; + } + } + } else { + return false; + } + } else { + return false; + } + } + + private void A() { + if (this.world instanceof WorldServer) { + ((WorldServer) this.world).a(new ParticleParamBlock(Particles.d, Blocks.OAK_PLANKS.getBlockData()), this.locX, this.locY + (double) this.length / 1.5D, this.locZ, 10, (double) (this.width / 4.0F), (double) (this.length / 4.0F), (double) (this.width / 4.0F), 0.05D); + } + + } + + private void a(float f) { + float f1 = this.getHealth(); + + f1 -= f; + if (f1 <= 0.5F) { + this.D(); + this.killEntity(); // CraftBukkit - this.die() -> this.killEntity() + } else { + this.setHealth(f1); + } + + } + + private void B() { + drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(new ItemStack(Items.ARMOR_STAND))); // CraftBukkit - add to drops + this.D(); + } + + private void D() { + this.F(); + + ItemStack itemstack; + int i; + + for (i = 0; i < this.bE.size(); ++i) { + itemstack = (ItemStack) this.bE.get(i); + if (!itemstack.isEmpty()) { + drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemstack)); // CraftBukkit - add to drops + this.bE.set(i, ItemStack.a); + } + } + + for (i = 0; i < this.bF.size(); ++i) { + itemstack = (ItemStack) this.bF.get(i); + if (!itemstack.isEmpty()) { + drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemstack)); // CraftBukkit - add to drops + this.bF.set(i, ItemStack.a); + } + } + + } + + private void F() { + this.world.a((EntityHuman) null, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_ARMOR_STAND_BREAK, this.bV(), 1.0F, 1.0F); + } + + protected float e(float f, float f1) { + this.aR = this.lastYaw; + this.aQ = this.yaw; + return 0.0F; + } + + public float getHeadHeight() { + return this.isBaby() ? this.length * 0.5F : this.length * 0.9F; + } + + public double aI() { + return this.isMarker() ? 0.0D : 0.10000000149011612D; + } + + public void a(float f, float f1, float f2) { + if (!this.isNoGravity()) { + super.a(f, f1, f2); + } + } + + public void k(float f) { + this.aR = this.lastYaw = f; + this.aT = this.aS = f; + } + + public void setHeadRotation(float f) { + this.aR = this.lastYaw = f; + this.aT = this.aS = f; + } + + public void tick() { + // Paper start + if (!this.canTick) { + if (this.noTickPoseDirty) { + this.noTickPoseDirty = false; + this.updatePose(); + } + + if (this.noTickEquipmentDirty) { + this.noTickEquipmentDirty = false; + this.updateEntityEquipment(); + } + + return; + } + // Paper end + + super.tick(); + + // Paper start - Split into separate method + updatePose(); + } + + public void updatePose() { + // Paper end + Vector3f vector3f = (Vector3f) this.datawatcher.get(EntityArmorStand.b); + + if (!this.headPose.equals(vector3f)) { + this.setHeadPose(vector3f); + } + + Vector3f vector3f1 = (Vector3f) this.datawatcher.get(EntityArmorStand.c); + + if (!this.bodyPose.equals(vector3f1)) { + this.setBodyPose(vector3f1); + } + + Vector3f vector3f2 = (Vector3f) this.datawatcher.get(EntityArmorStand.d); + + if (!this.leftArmPose.equals(vector3f2)) { + this.setLeftArmPose(vector3f2); + } + + Vector3f vector3f3 = (Vector3f) this.datawatcher.get(EntityArmorStand.e); + + if (!this.rightArmPose.equals(vector3f3)) { + this.setRightArmPose(vector3f3); + } + + Vector3f vector3f4 = (Vector3f) this.datawatcher.get(EntityArmorStand.f); + + if (!this.leftLegPose.equals(vector3f4)) { + this.setLeftLegPose(vector3f4); + } + + Vector3f vector3f5 = (Vector3f) this.datawatcher.get(EntityArmorStand.g); + + if (!this.rightLegPose.equals(vector3f5)) { + this.setRightLegPose(vector3f5); + } + + boolean flag = this.isMarker(); + + if (this.bI != flag) { + this.a(flag); + this.j = !flag; + this.bI = flag; + } + + } + + private void a(boolean flag) { + if (flag) { + this.setSize(0.0F, 0.0F); + } else { + this.setSize(0.5F, 1.975F); + } + + } + + protected void C() { + this.setInvisible(this.bG); + } + + public void setInvisible(boolean flag) { + this.bG = flag; + super.setInvisible(flag); + } + + public boolean isBaby() { + return this.isSmall(); + } + + public void killEntity() { + org.bukkit.event.entity.EntityDeathEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityDeathEvent(this, drops); // CraftBukkit - call event // Paper - make cancellable + if (event.isCancelled()) return; // Paper - make cancellable + this.die(); + } + + public boolean bL() { + return this.isInvisible(); + } + + public EnumPistonReaction getPushReaction() { + return this.isMarker() ? EnumPistonReaction.IGNORE : super.getPushReaction(); + } + + public void setSmall(boolean flag) { + this.datawatcher.set(EntityArmorStand.a, this.a((Byte) this.datawatcher.get(EntityArmorStand.a), 1, flag)); + this.setSize(0.5F, 1.975F); + } + + public boolean isSmall() { + return ((Byte) this.datawatcher.get(EntityArmorStand.a) & 1) != 0; + } + + public void setArms(boolean flag) { + this.datawatcher.set(EntityArmorStand.a, this.a((Byte) this.datawatcher.get(EntityArmorStand.a), 4, flag)); + } + + public boolean hasArms() { + return ((Byte) this.datawatcher.get(EntityArmorStand.a) & 4) != 0; + } + + public void setBasePlate(boolean flag) { + this.datawatcher.set(EntityArmorStand.a, this.a((Byte) this.datawatcher.get(EntityArmorStand.a), 8, flag)); + } + + public boolean hasBasePlate() { + return ((Byte) this.datawatcher.get(EntityArmorStand.a) & 8) != 0; + } + + public void setMarker(boolean flag) { + this.datawatcher.set(EntityArmorStand.a, this.a((Byte) this.datawatcher.get(EntityArmorStand.a), 16, flag)); + this.setSize(0.5F, 1.975F); + } + + public boolean isMarker() { + return ((Byte) this.datawatcher.get(EntityArmorStand.a) & 16) != 0; + } + + private byte a(byte b0, int i, boolean flag) { + if (flag) { + b0 = (byte) (b0 | i); + } else { + b0 = (byte) (b0 & ~i); + } + + return b0; + } + + public void setHeadPose(Vector3f vector3f) { + this.headPose = vector3f; + this.datawatcher.set(EntityArmorStand.b, vector3f); + this.noTickPoseDirty = true; // Paper - Allow updates when not ticking + } + + public void setBodyPose(Vector3f vector3f) { + this.bodyPose = vector3f; + this.datawatcher.set(EntityArmorStand.c, vector3f); + this.noTickPoseDirty = true; // Paper - Allow updates when not ticking + } + + public void setLeftArmPose(Vector3f vector3f) { + this.leftArmPose = vector3f; + this.datawatcher.set(EntityArmorStand.d, vector3f); + this.noTickPoseDirty = true; // Paper - Allow updates when not ticking + } + + public void setRightArmPose(Vector3f vector3f) { + this.rightArmPose = vector3f; + this.datawatcher.set(EntityArmorStand.e, vector3f); + this.noTickPoseDirty = true; // Paper - Allow updates when not ticking + } + + public void setLeftLegPose(Vector3f vector3f) { + this.leftLegPose = vector3f; + this.datawatcher.set(EntityArmorStand.f, vector3f); + this.noTickPoseDirty = true; // Paper - Allow updates when not ticking + } + + public void setRightLegPose(Vector3f vector3f) { + this.rightLegPose = vector3f; + this.datawatcher.set(EntityArmorStand.g, vector3f); + this.noTickPoseDirty = true; // Paper - Allow updates when not ticking + } + + public Vector3f r() { + return this.headPose; + } + + public Vector3f s() { + return this.bodyPose; + } + + public boolean isInteractable() { + return super.isInteractable() && !this.isMarker(); + } + + public EnumMainHand getMainHand() { + return EnumMainHand.RIGHT; + } + + protected SoundEffect m(int i) { + return SoundEffects.ENTITY_ARMOR_STAND_FALL; + } + + @Nullable + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_ARMOR_STAND_HIT; + } + + @Nullable + protected SoundEffect cs() { + return SoundEffects.ENTITY_ARMOR_STAND_BREAK; + } + + public void onLightningStrike(EntityLightning entitylightning) {} + + public boolean de() { + return false; + } + + public void a(DataWatcherObject datawatcherobject) { + if (EntityArmorStand.a.equals(datawatcherobject)) { + this.setSize(0.5F, 1.975F); + } + + super.a(datawatcherobject); + } + + public boolean df() { + return false; + } + + // Paper start + @Override + public void move(EnumMoveType moveType, double x, double y, double z) { + if (this.canMove) { + super.move(moveType, x, y, z); + } + } + + @Override + public boolean canBreatheUnderwater() { // Skips a bit of damage handling code, probably a micro-optimization + return true; + } + // Paper end +} diff --git a/src/main/java/net/minecraft/server/EntityArrow.java b/src/main/java/net/minecraft/server/EntityArrow.java new file mode 100644 index 000000000000..6aa518f17fb0 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityArrow.java @@ -0,0 +1,613 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityCombustByEntityEvent; +import org.bukkit.event.player.PlayerPickupArrowEvent; +// CraftBukkit end + +public abstract class EntityArrow extends Entity implements IProjectile { + + private static final Predicate g = IEntitySelector.f.and(IEntitySelector.a.and(Entity::isInteractable)); + private static final DataWatcherObject h = DataWatcher.a(EntityArrow.class, DataWatcherRegistry.a); + protected static final DataWatcherObject> a = DataWatcher.a(EntityArrow.class, DataWatcherRegistry.o); + public int tileX; + public int tileY; + public int tileZ; + @Nullable + private IBlockData az; + public boolean inGround; + protected int c; + public EntityArrow.PickupStatus fromPlayer; + public int shake; + public UUID shooter; + public int despawnCounter; // PAIL + private int aB; + private double damage; + public int knockbackStrength; + + // Spigot Start + @Override + public void inactiveTick() + { + if ( this.inGround ) + { + this.despawnCounter += 1; + } + super.inactiveTick(); + } + // Spigot End + + protected EntityArrow(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.tileX = -1; + this.tileY = -1; + this.tileZ = -1; + this.fromPlayer = EntityArrow.PickupStatus.DISALLOWED; + this.damage = 2.0D; + this.setSize(0.5F, 0.5F); + } + + protected EntityArrow(EntityTypes entitytypes, double d0, double d1, double d2, World world) { + this(entitytypes, world); + this.setPosition(d0, d1, d2); + } + + protected EntityArrow(EntityTypes entitytypes, EntityLiving entityliving, World world) { + this(entitytypes, entityliving.locX, entityliving.locY + (double) entityliving.getHeadHeight() - 0.10000000149011612D, entityliving.locZ, world); + this.setShooter(entityliving); + if (entityliving instanceof EntityHuman) { + this.fromPlayer = EntityArrow.PickupStatus.ALLOWED; + } + + } + + protected void x_() { + this.datawatcher.register(EntityArrow.h, (byte) 0); + this.datawatcher.register(EntityArrow.a, Optional.empty()); + } + + public void a(Entity entity, float f, float f1, float f2, float f3, float f4) { + float f5 = -MathHelper.sin(f1 * 0.017453292F) * MathHelper.cos(f * 0.017453292F); + float f6 = -MathHelper.sin(f * 0.017453292F); + float f7 = MathHelper.cos(f1 * 0.017453292F) * MathHelper.cos(f * 0.017453292F); + + this.shoot((double) f5, (double) f6, (double) f7, f3, f4); + this.motX += entity.motX; + this.motZ += entity.motZ; + if (!entity.onGround) { + this.motY += entity.motY; + } + + } + + public void shoot(double d0, double d1, double d2, float f, float f1) { + float f2 = MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + + d0 /= (double) f2; + d1 /= (double) f2; + d2 /= (double) f2; + d0 += this.random.nextGaussian() * 0.007499999832361937D * (double) f1; + d1 += this.random.nextGaussian() * 0.007499999832361937D * (double) f1; + d2 += this.random.nextGaussian() * 0.007499999832361937D * (double) f1; + d0 *= (double) f; + d1 *= (double) f; + d2 *= (double) f; + this.motX = d0; + this.motY = d1; + this.motZ = d2; + float f3 = MathHelper.sqrt(d0 * d0 + d2 * d2); + + this.yaw = (float) (MathHelper.c(d0, d2) * 57.2957763671875D); + this.pitch = (float) (MathHelper.c(d1, (double) f3) * 57.2957763671875D); + this.lastYaw = this.yaw; + this.lastPitch = this.pitch; + this.despawnCounter = 0; + } + + public void tick() { + super.tick(); + boolean flag = this.q(); + + if (this.lastPitch == 0.0F && this.lastYaw == 0.0F) { + float f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + this.yaw = (float) (MathHelper.c(this.motX, this.motZ) * 57.2957763671875D); + this.pitch = (float) (MathHelper.c(this.motY, (double) f) * 57.2957763671875D); + this.lastYaw = this.yaw; + this.lastPitch = this.pitch; + } + + BlockPosition blockposition = new BlockPosition(this.tileX, this.tileY, this.tileZ); + IBlockData iblockdata = this.world.getType(blockposition); + + if (!iblockdata.isAir() && !flag) { + VoxelShape voxelshape = iblockdata.getCollisionShape(this.world, blockposition); + + if (!voxelshape.isEmpty()) { + Iterator iterator = voxelshape.d().iterator(); + + while (iterator.hasNext()) { + AxisAlignedBB axisalignedbb = (AxisAlignedBB) iterator.next(); + + if (axisalignedbb.a(blockposition).b(new Vec3D(this.locX, this.locY, this.locZ))) { + this.inGround = true; + break; + } + } + } + } + + if (this.shake > 0) { + --this.shake; + } + + if (this.ao()) { + this.extinguish(); + } + + if (this.inGround && !flag) { + if (this.az != iblockdata && this.world.getCubes((Entity) null, this.getBoundingBox().g(0.05D))) { + this.inGround = false; + this.motX *= (double) (this.random.nextFloat() * 0.2F); + this.motY *= (double) (this.random.nextFloat() * 0.2F); + this.motZ *= (double) (this.random.nextFloat() * 0.2F); + this.despawnCounter = 0; + this.aB = 0; + } else { + this.f(); + } + + ++this.c; + } else { + this.c = 0; + ++this.aB; + Vec3D vec3d = new Vec3D(this.locX, this.locY, this.locZ); + Vec3D vec3d1 = new Vec3D(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + MovingObjectPosition movingobjectposition = this.world.rayTrace(vec3d, vec3d1, FluidCollisionOption.NEVER, true, false); + + vec3d = new Vec3D(this.locX, this.locY, this.locZ); + vec3d1 = new Vec3D(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + if (movingobjectposition != null) { + vec3d1 = new Vec3D(movingobjectposition.pos.x, movingobjectposition.pos.y, movingobjectposition.pos.z); + } + + Entity entity = this.a(vec3d, vec3d1); + + if (entity != null) { + movingobjectposition = new MovingObjectPosition(entity); + } + + if (movingobjectposition != null && movingobjectposition.entity instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) movingobjectposition.entity; + Entity entity1 = this.getShooter(); + + if (entity1 instanceof EntityHuman && !((EntityHuman) entity1).a(entityhuman)) { + movingobjectposition = null; + } + } + + // Paper start - Call ProjectileCollideEvent + // TODO: flag - noclip - call cancelled? + if (movingobjectposition != null && movingobjectposition.entity != null) { + com.destroystokyo.paper.event.entity.ProjectileCollideEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileCollideEvent(this, movingobjectposition); + if (event.isCancelled()) { + movingobjectposition = null; + } + } + // Paper end + + if (movingobjectposition != null && !flag) { + this.a(movingobjectposition); + this.impulse = true; + } + + if (this.isCritical()) { + for (int i = 0; i < 4; ++i) { + this.world.addParticle(Particles.h, this.locX + this.motX * (double) i / 4.0D, this.locY + this.motY * (double) i / 4.0D, this.locZ + this.motZ * (double) i / 4.0D, -this.motX, -this.motY + 0.2D, -this.motZ); + } + } + + this.locX += this.motX; + this.locY += this.motY; + this.locZ += this.motZ; + float f1 = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + if (flag) { + this.yaw = (float) (MathHelper.c(-this.motX, -this.motZ) * 57.2957763671875D); + } else { + this.yaw = (float) (MathHelper.c(this.motX, this.motZ) * 57.2957763671875D); + } + + for (this.pitch = (float) (MathHelper.c(this.motY, (double) f1) * 57.2957763671875D); this.pitch - this.lastPitch < -180.0F; this.lastPitch -= 360.0F) { + ; + } + + while (this.pitch - this.lastPitch >= 180.0F) { + this.lastPitch += 360.0F; + } + + while (this.yaw - this.lastYaw < -180.0F) { + this.lastYaw -= 360.0F; + } + + while (this.yaw - this.lastYaw >= 180.0F) { + this.lastYaw += 360.0F; + } + + this.pitch = this.lastPitch + (this.pitch - this.lastPitch) * 0.2F; + this.yaw = this.lastYaw + (this.yaw - this.lastYaw) * 0.2F; + float f2 = 0.99F; + float f3 = 0.05F; + + if (this.isInWater()) { + for (int j = 0; j < 4; ++j) { + float f4 = 0.25F; + + this.world.addParticle(Particles.e, this.locX - this.motX * 0.25D, this.locY - this.motY * 0.25D, this.locZ - this.motZ * 0.25D, this.motX, this.motY, this.motZ); + } + + f2 = this.p(); + } + + this.motX *= (double) f2; + this.motY *= (double) f2; + this.motZ *= (double) f2; + if (!this.isNoGravity() && !flag) { + this.motY -= 0.05000000074505806D; + } + + this.setPosition(this.locX, this.locY, this.locZ); + this.checkBlockCollisions(); + } + } + + protected void f() { + ++this.despawnCounter; + if (this.despawnCounter >= (fromPlayer == PickupStatus.CREATIVE_ONLY ? world.paperConfig.creativeArrowDespawnRate : (fromPlayer == PickupStatus.DISALLOWED ? world.paperConfig.nonPlayerArrowDespawnRate : world.spigotConfig.arrowDespawnRate))) { // Spigot // Paper + this.die(); + } + + } + + protected void a(MovingObjectPosition movingobjectposition) { + org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this, movingobjectposition); // CraftBukkit - Call event + if (movingobjectposition.entity != null) { + this.b(movingobjectposition); + } else { + BlockPosition blockposition = movingobjectposition.getBlockPosition(); + + this.tileX = blockposition.getX(); + this.tileY = blockposition.getY(); + this.tileZ = blockposition.getZ(); + IBlockData iblockdata = this.world.getType(blockposition); + + this.az = iblockdata; + this.motX = (double) ((float) (movingobjectposition.pos.x - this.locX)); + this.motY = (double) ((float) (movingobjectposition.pos.y - this.locY)); + this.motZ = (double) ((float) (movingobjectposition.pos.z - this.locZ)); + float f = MathHelper.sqrt(this.motX * this.motX + this.motY * this.motY + this.motZ * this.motZ) * 20.0F; + + this.locX -= this.motX / (double) f; + this.locY -= this.motY / (double) f; + this.locZ -= this.motZ / (double) f; + this.a(this.i(), 1.0F, 1.2F / (this.random.nextFloat() * 0.2F + 0.9F)); + this.inGround = true; + this.shake = 7; + this.setCritical(false); + if (!iblockdata.isAir()) { + this.az.a(this.world, blockposition, (Entity) this); + } + } + + } + + protected void b(MovingObjectPosition movingobjectposition) { + Entity entity = movingobjectposition.entity; + float f = MathHelper.sqrt(this.motX * this.motX + this.motY * this.motY + this.motZ * this.motZ); + int i = MathHelper.f((double) f * this.damage); + + if (this.isCritical()) { + i += this.random.nextInt(i / 2 + 2); + } + + Entity entity1 = this.getShooter(); + DamageSource damagesource; + + if (entity1 == null) { + damagesource = DamageSource.arrow(this, this); + } else { + damagesource = DamageSource.arrow(this, entity1); + } + + if (this.isBurning() && !(entity instanceof EntityEnderman)) { + // CraftBukkit start + EntityCombustByEntityEvent combustEvent = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), 5); + org.bukkit.Bukkit.getPluginManager().callEvent(combustEvent); + if (!combustEvent.isCancelled()) { + entity.setOnFire(combustEvent.getDuration(), false); + } + // CraftBukkit end + } + + if (entity.damageEntity(damagesource, (float) i)) { + if (entity instanceof EntityLiving) { + EntityLiving entityliving = (EntityLiving) entity; + + if (!this.world.isClientSide) { + entityliving.setArrowCount(entityliving.getArrowCount() + 1); + } + + if (this.knockbackStrength > 0) { + float f1 = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + if (f1 > 0.0F) { + entityliving.f(this.motX * (double) this.knockbackStrength * 0.6000000238418579D / (double) f1, 0.1D, this.motZ * (double) this.knockbackStrength * 0.6000000238418579D / (double) f1); + } + } + + if (entity1 instanceof EntityLiving) { + EnchantmentManager.a(entityliving, entity1); + EnchantmentManager.b((EntityLiving) entity1, (Entity) entityliving); + } + + this.a(entityliving); + if (entity1 != null && entityliving != entity1 && entityliving instanceof EntityHuman && entity1 instanceof EntityPlayer) { + ((EntityPlayer) entity1).playerConnection.sendPacket(new PacketPlayOutGameStateChange(6, 0.0F)); + } + } + + this.a(SoundEffects.ENTITY_ARROW_HIT, 1.0F, 1.2F / (this.random.nextFloat() * 0.2F + 0.9F)); + if (!(entity instanceof EntityEnderman)) { + this.die(); + } + } else { + this.motX *= -0.10000000149011612D; + this.motY *= -0.10000000149011612D; + this.motZ *= -0.10000000149011612D; + this.yaw += 180.0F; + this.lastYaw += 180.0F; + this.aB = 0; + if (!this.world.isClientSide && this.motX * this.motX + this.motY * this.motY + this.motZ * this.motZ < 0.0010000000474974513D) { + if (this.fromPlayer == EntityArrow.PickupStatus.ALLOWED) { + this.a(this.getItemStack(), 0.1F); + } + + this.die(); + } + } + + } + + protected SoundEffect i() { + return SoundEffects.ENTITY_ARROW_HIT; + } + + public void move(EnumMoveType enummovetype, double d0, double d1, double d2) { + super.move(enummovetype, d0, d1, d2); + if (this.inGround) { + this.tileX = MathHelper.floor(this.locX); + this.tileY = MathHelper.floor(this.locY); + this.tileZ = MathHelper.floor(this.locZ); + } + + } + + protected void a(EntityLiving entityliving) {} + + @Nullable + protected Entity a(Vec3D vec3d, Vec3D vec3d1) { + Entity entity = null; + List list = this.world.getEntities(this, this.getBoundingBox().b(this.motX, this.motY, this.motZ).g(1.0D), EntityArrow.g); + double d0 = 0.0D; + + for (int i = 0; i < list.size(); ++i) { + Entity entity1 = (Entity) list.get(i); + + if (entity1 != this.getShooter() || this.aB >= 5) { + AxisAlignedBB axisalignedbb = entity1.getBoundingBox().g(0.30000001192092896D); + MovingObjectPosition movingobjectposition = axisalignedbb.b(vec3d, vec3d1); + + if (movingobjectposition != null) { + double d1 = vec3d.distanceSquared(movingobjectposition.pos); + + if (d1 < d0 || d0 == 0.0D) { + entity = entity1; + d0 = d1; + } + } + } + } + + return entity; + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setInt("xTile", this.tileX); + nbttagcompound.setInt("yTile", this.tileY); + nbttagcompound.setInt("zTile", this.tileZ); + nbttagcompound.setShort("life", (short) this.despawnCounter); + if (this.az != null) { + nbttagcompound.set("inBlockState", GameProfileSerializer.a(this.az)); + } + + nbttagcompound.setByte("shake", (byte) this.shake); + nbttagcompound.setByte("inGround", (byte) (this.inGround ? 1 : 0)); + nbttagcompound.setByte("pickup", (byte) this.fromPlayer.ordinal()); + nbttagcompound.setDouble("damage", this.damage); + nbttagcompound.setBoolean("crit", this.isCritical()); + if (this.shooter != null) { + nbttagcompound.a("OwnerUUID", this.shooter); + } + + } + + public void a(NBTTagCompound nbttagcompound) { + this.tileX = nbttagcompound.getInt("xTile"); + this.tileY = nbttagcompound.getInt("yTile"); + this.tileZ = nbttagcompound.getInt("zTile"); + this.despawnCounter = nbttagcompound.getShort("life"); + if (nbttagcompound.hasKeyOfType("inBlockState", 10)) { + this.az = GameProfileSerializer.d(nbttagcompound.getCompound("inBlockState")); + } + + this.shake = nbttagcompound.getByte("shake") & 255; + this.inGround = nbttagcompound.getByte("inGround") == 1; + if (nbttagcompound.hasKeyOfType("damage", 99)) { + this.damage = nbttagcompound.getDouble("damage"); + } + + if (nbttagcompound.hasKeyOfType("pickup", 99)) { + this.fromPlayer = EntityArrow.PickupStatus.a(nbttagcompound.getByte("pickup")); + } else if (nbttagcompound.hasKeyOfType("player", 99)) { + this.fromPlayer = nbttagcompound.getBoolean("player") ? EntityArrow.PickupStatus.ALLOWED : EntityArrow.PickupStatus.DISALLOWED; + } + + this.setCritical(nbttagcompound.getBoolean("crit")); + if (nbttagcompound.b("OwnerUUID")) { + this.shooter = nbttagcompound.a("OwnerUUID"); + } + + } + + public void setShooter(@Nullable Entity entity) { + this.shooter = entity == null ? null : entity.getUniqueID(); + this.projectileSource = entity == null ? null : (LivingEntity) entity.getBukkitEntity(); // CraftBukkit + } + + @Nullable + public Entity getShooter() { + return this.shooter != null && this.world instanceof WorldServer ? ((WorldServer) this.world).getEntity(this.shooter) : null; + } + + public void d(EntityHuman entityhuman) { + if (!this.world.isClientSide && (this.inGround || this.q()) && this.shake <= 0) { + // CraftBukkit start + ItemStack itemstack = this.getItemStack(); + if (this.fromPlayer == PickupStatus.ALLOWED && !itemstack.isEmpty() && entityhuman.inventory.canHold(itemstack) > 0) { + EntityItem item = new EntityItem(this.world, this.locX, this.locY, this.locZ, itemstack); + PlayerPickupArrowEvent event = new PlayerPickupArrowEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), new org.bukkit.craftbukkit.entity.CraftItem(this.world.getServer(), this, item), (org.bukkit.entity.Arrow) this.getBukkitEntity()); + // event.setCancelled(!entityhuman.canPickUpLoot); TODO + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + itemstack = item.getItemStack(); + } + boolean flag = this.fromPlayer == EntityArrow.PickupStatus.ALLOWED || this.fromPlayer == EntityArrow.PickupStatus.CREATIVE_ONLY && entityhuman.abilities.canInstantlyBuild || this.q() && this.getShooter().getUniqueID() == entityhuman.getUniqueID(); + + if (this.fromPlayer == EntityArrow.PickupStatus.ALLOWED && !entityhuman.inventory.pickup(itemstack)) { + // CraftBukkit end + flag = false; + } + + if (flag) { + entityhuman.receive(this, 1); + this.die(); + } + + } + } + + protected abstract ItemStack getItemStack(); + + protected boolean playStepSound() { + return false; + } + + public void setDamage(double d0) { + this.damage = d0; + } + + public double getDamage() { + return this.damage; + } + + public void setKnockbackStrength(int i) { + this.knockbackStrength = i; + } + + public boolean bk() { + return false; + } + + public float getHeadHeight() { + return 0.0F; + } + + public void setCritical(boolean flag) { + this.a(1, flag); + } + + private void a(int i, boolean flag) { + byte b0 = (Byte) this.datawatcher.get(EntityArrow.h); + + if (flag) { + this.datawatcher.set(EntityArrow.h, (byte) (b0 | i)); + } else { + this.datawatcher.set(EntityArrow.h, (byte) (b0 & ~i)); + } + + } + + public boolean isCritical() { + byte b0 = (Byte) this.datawatcher.get(EntityArrow.h); + + return (b0 & 1) != 0; + } + + public void a(EntityLiving entityliving, float f) { + int i = EnchantmentManager.a(Enchantments.ARROW_DAMAGE, entityliving); + int j = EnchantmentManager.a(Enchantments.ARROW_KNOCKBACK, entityliving); + + this.setDamage((double) (f * 2.0F) + this.random.nextGaussian() * 0.25D + (double) ((float) this.world.getDifficulty().a() * 0.11F)); + if (i > 0) { + this.setDamage(this.getDamage() + (double) i * 0.5D + 0.5D); + } + + if (j > 0) { + this.setKnockbackStrength(j); + } + + if (EnchantmentManager.a(Enchantments.ARROW_FIRE, entityliving) > 0) { + this.setOnFire(100); + } + + } + + protected float p() { + return 0.6F; + } + + public void o(boolean flag) { + this.noclip = flag; + this.a(2, flag); + } + + public boolean q() { + return !this.world.isClientSide ? this.noclip : ((Byte) this.datawatcher.get(EntityArrow.h) & 2) != 0; + } + + public static enum PickupStatus { + + DISALLOWED, ALLOWED, CREATIVE_ONLY; + + private PickupStatus() {} + + public static EntityArrow.PickupStatus a(int i) { + if (i < 0 || i > values().length) { + i = 0; + } + + return values()[i]; + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityBat.java b/src/main/java/net/minecraft/server/EntityBat.java new file mode 100644 index 000000000000..766154435659 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityBat.java @@ -0,0 +1,218 @@ +package net.minecraft.server; + +import java.time.LocalDate; +import java.time.temporal.ChronoField; +import javax.annotation.Nullable; +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class EntityBat extends EntityAmbient { + + private static final DataWatcherObject a = DataWatcher.a(EntityBat.class, DataWatcherRegistry.a); + private BlockPosition b; + + public EntityBat(World world) { + super(EntityTypes.BAT, world); + this.setSize(0.5F, 0.9F); + this.setAsleep(true); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityBat.a, (byte) 0); + } + + protected float cD() { + return 0.1F; + } + + protected float cE() { + return super.cE() * 0.95F; + } + + @Nullable + public SoundEffect D() { + return this.isAsleep() && this.random.nextInt(4) != 0 ? null : SoundEffects.ENTITY_BAT_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_BAT_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_BAT_DEATH; + } + + public boolean isCollidable() { + return false; + } + + protected void C(Entity entity) {} + + protected void cN() {} + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(6.0D); + } + + public boolean isAsleep() { + return ((Byte) this.datawatcher.get(EntityBat.a) & 1) != 0; + } + + public void setAsleep(boolean flag) { + byte b0 = (Byte) this.datawatcher.get(EntityBat.a); + + if (flag) { + this.datawatcher.set(EntityBat.a, (byte) (b0 | 1)); + } else { + this.datawatcher.set(EntityBat.a, (byte) (b0 & -2)); + } + + } + + public void tick() { + super.tick(); + if (this.isAsleep()) { + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + this.locY = (double) MathHelper.floor(this.locY) + 1.0D - (double) this.length; + } else { + this.motY *= 0.6000000238418579D; + } + + } + + protected void mobTick() { + super.mobTick(); + BlockPosition blockposition = new BlockPosition(this); + BlockPosition blockposition1 = blockposition.up(); + + if (this.isAsleep()) { + if (this.world.getType(blockposition1).isOccluding()) { + if (this.random.nextInt(200) == 0) { + this.aS = (float) this.random.nextInt(360); + } + + if (this.world.b(this, 4.0D) != null) { + // CraftBukkit Start - Call BatToggleSleepEvent + if (CraftEventFactory.handleBatToggleSleepEvent(this, true)) { + this.setAsleep(false); + this.world.a((EntityHuman) null, 1025, blockposition, 0); + } + // CraftBukkit End + } + } else { + // CraftBukkit Start - Call BatToggleSleepEvent + if (CraftEventFactory.handleBatToggleSleepEvent(this, true)) { + this.setAsleep(false); + this.world.a((EntityHuman) null, 1025, blockposition, 0); + } + // CraftBukkit End - Call BatToggleSleepEvent + } + } else { + if (this.b != null && (!this.world.isEmpty(this.b) || this.b.getY() < 1)) { + this.b = null; + } + + if (this.b == null || this.random.nextInt(30) == 0 || this.b.distanceSquared((double) ((int) this.locX), (double) ((int) this.locY), (double) ((int) this.locZ)) < 4.0D) { + this.b = new BlockPosition((int) this.locX + this.random.nextInt(7) - this.random.nextInt(7), (int) this.locY + this.random.nextInt(6) - 2, (int) this.locZ + this.random.nextInt(7) - this.random.nextInt(7)); + } + + double d0 = (double) this.b.getX() + 0.5D - this.locX; + double d1 = (double) this.b.getY() + 0.1D - this.locY; + double d2 = (double) this.b.getZ() + 0.5D - this.locZ; + + this.motX += (Math.signum(d0) * 0.5D - this.motX) * 0.10000000149011612D; + this.motY += (Math.signum(d1) * 0.699999988079071D - this.motY) * 0.10000000149011612D; + this.motZ += (Math.signum(d2) * 0.5D - this.motZ) * 0.10000000149011612D; + float f = (float) (MathHelper.c(this.motZ, this.motX) * 57.2957763671875D) - 90.0F; + float f1 = MathHelper.g(f - this.yaw); + + this.bj = 0.5F; + this.yaw += f1; + if (this.random.nextInt(100) == 0 && this.world.getType(blockposition1).isOccluding()) { + // CraftBukkit Start - Call BatToggleSleepEvent + if (CraftEventFactory.handleBatToggleSleepEvent(this, false)) { + this.setAsleep(true); + } + // CraftBukkit End + } + } + + } + + protected boolean playStepSound() { + return false; + } + + public void c(float f, float f1) {} + + protected void a(double d0, boolean flag, IBlockData iblockdata, BlockPosition blockposition) {} + + public boolean isIgnoreBlockTrigger() { + return true; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else { + if (!this.world.isClientSide && this.isAsleep()) { + // CraftBukkit Start - Call BatToggleSleepEvent + if (CraftEventFactory.handleBatToggleSleepEvent(this, true)) { + this.setAsleep(false); + } + // CraftBukkit End - Call BatToggleSleepEvent + } + + return super.damageEntity(damagesource, f); + } + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.datawatcher.set(EntityBat.a, nbttagcompound.getByte("BatFlags")); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setByte("BatFlags", (Byte) this.datawatcher.get(EntityBat.a)); + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + BlockPosition blockposition = new BlockPosition(this.locX, this.getBoundingBox().minY, this.locZ); + + if (blockposition.getY() >= generatoraccess.getSeaLevel()) { + return false; + } else { + int i = generatoraccess.getLightLevel(blockposition); + byte b0 = 4; + + if (this.dr()) { + b0 = 7; + } else if (this.random.nextBoolean()) { + return false; + } + + return i > this.random.nextInt(b0) ? false : super.a(generatoraccess, flag); + } + } + + private boolean dr() { + LocalDate localdate = LocalDate.now(); + int i = localdate.get(ChronoField.DAY_OF_MONTH); + int j = localdate.get(ChronoField.MONTH_OF_YEAR); + + return j == 10 && i >= 20 || j == 11 && i <= 3; + } + + public float getHeadHeight() { + return this.length / 2.0F; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.an; + } +} diff --git a/src/main/java/net/minecraft/server/EntityBoat.java b/src/main/java/net/minecraft/server/EntityBoat.java new file mode 100644 index 000000000000..d132afb1845e --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityBoat.java @@ -0,0 +1,942 @@ +package net.minecraft.server; + +import java.util.List; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.Location; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.vehicle.VehicleDamageEvent; +import org.bukkit.event.vehicle.VehicleDestroyEvent; +import org.bukkit.event.vehicle.VehicleEntityCollisionEvent; +import org.bukkit.event.vehicle.VehicleMoveEvent; +// CraftBukkit end + +public class EntityBoat extends Entity { + + private static final DataWatcherObject a = DataWatcher.a(EntityBoat.class, DataWatcherRegistry.b); + private static final DataWatcherObject b = DataWatcher.a(EntityBoat.class, DataWatcherRegistry.b); + private static final DataWatcherObject c = DataWatcher.a(EntityBoat.class, DataWatcherRegistry.c); + private static final DataWatcherObject d = DataWatcher.a(EntityBoat.class, DataWatcherRegistry.b); + private static final DataWatcherObject e = DataWatcher.a(EntityBoat.class, DataWatcherRegistry.i); + private static final DataWatcherObject f = DataWatcher.a(EntityBoat.class, DataWatcherRegistry.i); + private static final DataWatcherObject g = DataWatcher.a(EntityBoat.class, DataWatcherRegistry.b); + private final float[] h; + private float aw; + private float ax; + private float ay; + private int az; + private double aA; + private double aB; + private double aC; + private double aD; + private double aE; + private boolean aF; + private boolean aG; + private boolean aH; + private boolean aI; + private double aJ; + private float aK; + private EntityBoat.EnumStatus aL; + private EntityBoat.EnumStatus aM; + private double aN; + private boolean aO; + private boolean aP; + private float aQ; + private float aR; + private float aS; + + // CraftBukkit start + // PAIL: Some of these haven't worked since a few updates, and since 1.9 they are less and less applicable. + public double maxSpeed = 0.4D; + public double occupiedDeceleration = 0.2D; + public double unoccupiedDeceleration = -1; + public boolean landBoats = false; + // CraftBukkit end + + public EntityBoat(World world) { + super(EntityTypes.BOAT, world); + this.h = new float[2]; + this.j = true; + this.setSize(1.375F, 0.5625F); + } + + public EntityBoat(World world, double d0, double d1, double d2) { + this(world); + this.setPosition(d0, d1, d2); + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + this.lastX = d0; + this.lastY = d1; + this.lastZ = d2; + } + + protected boolean playStepSound() { + return false; + } + + protected void x_() { + this.datawatcher.register(EntityBoat.a, 0); + this.datawatcher.register(EntityBoat.b, 1); + this.datawatcher.register(EntityBoat.c, 0.0F); + this.datawatcher.register(EntityBoat.d, EntityBoat.EnumBoatType.OAK.ordinal()); + this.datawatcher.register(EntityBoat.e, false); + this.datawatcher.register(EntityBoat.f, false); + this.datawatcher.register(EntityBoat.g, 0); + } + + @Nullable + public AxisAlignedBB j(Entity entity) { + return entity.isCollidable() ? entity.getBoundingBox() : null; + } + + @Nullable + public AxisAlignedBB al() { + return this.getBoundingBox(); + } + + public boolean isCollidable() { + return true; + } + + public double aJ() { + return -0.1D; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else if (!this.world.isClientSide && !this.dead) { + if (damagesource instanceof EntityDamageSourceIndirect && damagesource.getEntity() != null && this.w(damagesource.getEntity())) { + return false; + } else { + // CraftBukkit start + Vehicle vehicle = (Vehicle) this.getBukkitEntity(); + org.bukkit.entity.Entity attacker = (damagesource.getEntity() == null) ? null : damagesource.getEntity().getBukkitEntity(); + + VehicleDamageEvent event = new VehicleDamageEvent(vehicle, attacker, (double) f); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return true; + } + // f = event.getDamage(); // TODO Why don't we do this? + // CraftBukkit end + + this.c(-this.o()); + this.b(10); + this.setDamage(this.m() + f * 10.0F); + this.aA(); + boolean flag = damagesource.getEntity() instanceof EntityHuman && ((EntityHuman) damagesource.getEntity()).abilities.canInstantlyBuild; + + if (flag || this.m() > 40.0F) { + // CraftBukkit start + VehicleDestroyEvent destroyEvent = new VehicleDestroyEvent(vehicle, attacker); + this.world.getServer().getPluginManager().callEvent(destroyEvent); + + if (destroyEvent.isCancelled()) { + this.setDamage(40F); // Maximize damage so this doesn't get triggered again right away + return true; + } + // CraftBukkit end + if (!flag && this.world.getGameRules().getBoolean("doEntityDrops")) { + this.a((IMaterial) this.f()); + } + + this.die(); + } + + return true; + } + } else { + return true; + } + } + + public void j(boolean flag) { + if (!this.world.isClientSide) { + this.aO = true; + this.aP = flag; + if (this.z() == 0) { + this.d(60); + } + } + + this.world.addParticle(Particles.R, this.locX + (double) this.random.nextFloat(), this.locY + 0.7D, this.locZ + (double) this.random.nextFloat(), 0.0D, 0.0D, 0.0D); + if (this.random.nextInt(20) == 0) { + this.world.a(this.locX, this.locY, this.locZ, this.ae(), this.bV(), 1.0F, 0.8F + 0.4F * this.random.nextFloat(), false); + } + + } + + public void collide(Entity entity) { + if (entity instanceof EntityBoat) { + if (entity.getBoundingBox().minY < this.getBoundingBox().maxY) { + // CraftBukkit start + VehicleEntityCollisionEvent event = new VehicleEntityCollisionEvent((Vehicle) this.getBukkitEntity(), entity.getBukkitEntity()); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + // CraftBukkit end + super.collide(entity); + } + } else if (entity.getBoundingBox().minY <= this.getBoundingBox().minY) { + // CraftBukkit start + VehicleEntityCollisionEvent event = new VehicleEntityCollisionEvent((Vehicle) this.getBukkitEntity(), entity.getBukkitEntity()); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + // CraftBukkit end + super.collide(entity); + } + + } + + public Item f() { + switch (this.getType()) { + case OAK: + default: + return Items.OAK_BOAT; + case SPRUCE: + return Items.SPRUCE_BOAT; + case BIRCH: + return Items.BIRCH_BOAT; + case JUNGLE: + return Items.JUNGLE_BOAT; + case ACACIA: + return Items.ACACIA_BOAT; + case DARK_OAK: + return Items.DARK_OAK_BOAT; + } + } + + public boolean isInteractable() { + return !this.dead; + } + + public EnumDirection getAdjustedDirection() { + return this.getDirection().e(); + } + + private Location lastLocation; // CraftBukkit + public void tick() { + this.aM = this.aL; + this.aL = this.s(); + if (this.aL != EntityBoat.EnumStatus.UNDER_WATER && this.aL != EntityBoat.EnumStatus.UNDER_FLOWING_WATER) { + this.ax = 0.0F; + } else { + ++this.ax; + } + + if (!this.world.isClientSide && this.ax >= 60.0F) { + this.ejectPassengers(); + } + + if (this.n() > 0) { + this.b(this.n() - 1); + } + + if (this.m() > 0.0F) { + this.setDamage(this.m() - 1.0F); + } + + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + super.tick(); + this.r(); + if (this.bT()) { + if (this.bP().isEmpty() || !(this.bP().get(0) instanceof EntityHuman)) { + this.a(false, false); + } + + this.v(); + if (this.world.isClientSide) { + this.x(); + this.world.a((Packet) (new PacketPlayInBoatMove(this.a(0), this.a(1)))); + } + + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + } else { + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + } + + // CraftBukkit start + org.bukkit.Server server = this.world.getServer(); + org.bukkit.World bworld = this.world.getWorld(); + + Location to = new Location(bworld, this.locX, this.locY, this.locZ, this.yaw, this.pitch); + Vehicle vehicle = (Vehicle) this.getBukkitEntity(); + + server.getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleUpdateEvent(vehicle)); + + if (lastLocation != null && !lastLocation.equals(to)) { + VehicleMoveEvent event = new VehicleMoveEvent(vehicle, lastLocation, to); + server.getPluginManager().callEvent(event); + } + lastLocation = vehicle.getLocation(); + // CraftBukkit end + + this.q(); + + for (int i = 0; i <= 1; ++i) { + if (this.a(i)) { + if (!this.isSilent() && (double) (this.h[i] % 6.2831855F) <= 0.7853981852531433D && ((double) this.h[i] + 0.39269909262657166D) % 6.2831854820251465D >= 0.7853981852531433D) { + SoundEffect soundeffect = this.i(); + + if (soundeffect != null) { + Vec3D vec3d = this.f(1.0F); + double d0 = i == 1 ? -vec3d.z : vec3d.z; + double d1 = i == 1 ? vec3d.x : -vec3d.x; + + this.world.a((EntityHuman) null, this.locX + d0, this.locY, this.locZ + d1, soundeffect, this.bV(), 1.0F, 0.8F + 0.4F * this.random.nextFloat()); + } + } + + this.h[i] = (float) ((double) this.h[i] + 0.39269909262657166D); + } else { + this.h[i] = 0.0F; + } + } + + this.checkBlockCollisions(); + List list = this.world.getEntities(this, this.getBoundingBox().grow(0.20000000298023224D, -0.009999999776482582D, 0.20000000298023224D), IEntitySelector.a(this)); + + if (!list.isEmpty()) { + boolean flag = !this.world.isClientSide && !(this.bO() instanceof EntityHuman); + + for (int j = 0; j < list.size(); ++j) { + Entity entity = (Entity) list.get(j); + + if (!entity.w(this)) { + if (flag && this.bP().size() < 2 && !entity.isPassenger() && entity.width < this.width && entity instanceof EntityLiving && !(entity instanceof EntityWaterAnimal) && !(entity instanceof EntityHuman)) { + entity.startRiding(this); + } else { + this.collide(entity); + } + } + } + } + + } + + private void q() { + int i; + + if (this.world.isClientSide) { + i = this.z(); + if (i > 0) { + this.aQ += 0.05F; + } else { + this.aQ -= 0.1F; + } + + this.aQ = MathHelper.a(this.aQ, 0.0F, 1.0F); + this.aS = this.aR; + this.aR = 10.0F * (float) Math.sin((double) (0.5F * (float) this.world.getTime())) * this.aQ; + } else { + if (!this.aO) { + this.d(0); + } + + i = this.z(); + if (i > 0) { + --i; + this.d(i); + int j = 60 - i - 1; + + if (j > 0 && i == 0) { + this.d(0); + if (this.aP) { + this.motY -= 0.7D; + this.ejectPassengers(); + } else { + this.motY = this.a(EntityHuman.class) ? 2.7D : 0.6D; + } + } + + this.aO = false; + } + } + + } + + @Nullable + protected SoundEffect i() { + switch (this.s()) { + case IN_WATER: + case UNDER_WATER: + case UNDER_FLOWING_WATER: + return SoundEffects.ENTITY_BOAT_PADDLE_WATER; + case ON_LAND: + return SoundEffects.ENTITY_BOAT_PADDLE_LAND; + case IN_AIR: + default: + return null; + } + } + + private void r() { + if (this.az > 0 && !this.bT()) { + double d0 = this.locX + (this.aA - this.locX) / (double) this.az; + double d1 = this.locY + (this.aB - this.locY) / (double) this.az; + double d2 = this.locZ + (this.aC - this.locZ) / (double) this.az; + double d3 = MathHelper.g(this.aD - (double) this.yaw); + + this.yaw = (float) ((double) this.yaw + d3 / (double) this.az); + this.pitch = (float) ((double) this.pitch + (this.aE - (double) this.pitch) / (double) this.az); + --this.az; + this.setPosition(d0, d1, d2); + this.setYawPitch(this.yaw, this.pitch); + } + } + + public void a(boolean flag, boolean flag1) { + this.datawatcher.set(EntityBoat.e, flag); + this.datawatcher.set(EntityBoat.f, flag1); + } + + private EntityBoat.EnumStatus s() { + EntityBoat.EnumStatus entityboat_enumstatus = this.u(); + + if (entityboat_enumstatus != null) { + this.aJ = this.getBoundingBox().maxY; + return entityboat_enumstatus; + } else if (this.t()) { + return EntityBoat.EnumStatus.IN_WATER; + } else { + float f = this.l(); + + if (f > 0.0F) { + this.aK = f; + return EntityBoat.EnumStatus.ON_LAND; + } else { + return EntityBoat.EnumStatus.IN_AIR; + } + } + } + + public float k() { + AxisAlignedBB axisalignedbb = this.getBoundingBox(); + int i = MathHelper.floor(axisalignedbb.minX); + int j = MathHelper.f(axisalignedbb.maxX); + int k = MathHelper.floor(axisalignedbb.maxY); + int l = MathHelper.f(axisalignedbb.maxY - this.aN); + int i1 = MathHelper.floor(axisalignedbb.minZ); + int j1 = MathHelper.f(axisalignedbb.maxZ); + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + label161: + for (int k1 = k; k1 < l; ++k1) { + float f = 0.0F; + + for (int l1 = i; l1 < j; ++l1) { + for (int i2 = i1; i2 < j1; ++i2) { + blockposition_b.c(l1, k1, i2); + Fluid fluid = this.world.getFluid(blockposition_b); + + if (fluid.a(TagsFluid.WATER)) { + f = Math.max(f, (float) k1 + fluid.getHeight()); + } + + if (f >= 1.0F) { + continue label161; + } + } + } + + if (f < 1.0F) { + float f1 = (float) blockposition_b.getY() + f; + + return f1; + } + } + + float f2 = (float) (l + 1); + + return f2; + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + } + + public float l() { + AxisAlignedBB axisalignedbb = this.getBoundingBox(); + AxisAlignedBB axisalignedbb1 = new AxisAlignedBB(axisalignedbb.minX, axisalignedbb.minY - 0.001D, axisalignedbb.minZ, axisalignedbb.maxX, axisalignedbb.minY, axisalignedbb.maxZ); + int i = MathHelper.floor(axisalignedbb1.minX) - 1; + int j = MathHelper.f(axisalignedbb1.maxX) + 1; + int k = MathHelper.floor(axisalignedbb1.minY) - 1; + int l = MathHelper.f(axisalignedbb1.maxY) + 1; + int i1 = MathHelper.floor(axisalignedbb1.minZ) - 1; + int j1 = MathHelper.f(axisalignedbb1.maxZ) + 1; + VoxelShape voxelshape = VoxelShapes.a(axisalignedbb1); + float f = 0.0F; + int k1 = 0; + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + for (int l1 = i; l1 < j; ++l1) { + for (int i2 = i1; i2 < j1; ++i2) { + int j2 = (l1 != i && l1 != j - 1 ? 0 : 1) + (i2 != i1 && i2 != j1 - 1 ? 0 : 1); + + if (j2 != 2) { + for (int k2 = k; k2 < l; ++k2) { + if (j2 <= 0 || k2 != k && k2 != l - 1) { + blockposition_b.c(l1, k2, i2); + IBlockData iblockdata = this.world.getType(blockposition_b); + + if (!(iblockdata.getBlock() instanceof BlockWaterLily) && VoxelShapes.c(iblockdata.getCollisionShape(this.world, blockposition_b).a((double) l1, (double) k2, (double) i2), voxelshape, OperatorBoolean.AND)) { + f += iblockdata.getBlock().n(); + ++k1; + } + } + } + } + } + } + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + + return f / (float) k1; + } + + private boolean t() { + AxisAlignedBB axisalignedbb = this.getBoundingBox(); + int i = MathHelper.floor(axisalignedbb.minX); + int j = MathHelper.f(axisalignedbb.maxX); + int k = MathHelper.floor(axisalignedbb.minY); + int l = MathHelper.f(axisalignedbb.minY + 0.001D); + int i1 = MathHelper.floor(axisalignedbb.minZ); + int j1 = MathHelper.f(axisalignedbb.maxZ); + boolean flag = false; + + this.aJ = Double.MIN_VALUE; + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + for (int k1 = i; k1 < j; ++k1) { + for (int l1 = k; l1 < l; ++l1) { + for (int i2 = i1; i2 < j1; ++i2) { + blockposition_b.c(k1, l1, i2); + Fluid fluid = this.world.getFluid(blockposition_b); + + if (fluid.a(TagsFluid.WATER)) { + float f = (float) l1 + fluid.getHeight(); + + this.aJ = Math.max((double) f, this.aJ); + flag |= axisalignedbb.minY < (double) f; + } + } + } + } + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + + return flag; + } + + @Nullable + private EntityBoat.EnumStatus u() { + AxisAlignedBB axisalignedbb = this.getBoundingBox(); + double d0 = axisalignedbb.maxY + 0.001D; + int i = MathHelper.floor(axisalignedbb.minX); + int j = MathHelper.f(axisalignedbb.maxX); + int k = MathHelper.floor(axisalignedbb.maxY); + int l = MathHelper.f(d0); + int i1 = MathHelper.floor(axisalignedbb.minZ); + int j1 = MathHelper.f(axisalignedbb.maxZ); + boolean flag = false; + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + for (int k1 = i; k1 < j; ++k1) { + for (int l1 = k; l1 < l; ++l1) { + for (int i2 = i1; i2 < j1; ++i2) { + blockposition_b.c(k1, l1, i2); + Fluid fluid = this.world.getFluid(blockposition_b); + + if (fluid.a(TagsFluid.WATER) && d0 < (double) ((float) blockposition_b.getY() + fluid.getHeight())) { + if (!fluid.d()) { + EntityBoat.EnumStatus entityboat_enumstatus = EntityBoat.EnumStatus.UNDER_FLOWING_WATER; + + return entityboat_enumstatus; + } + + flag = true; + } + } + } + } + + return flag ? EntityBoat.EnumStatus.UNDER_WATER : null; + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + } + + private void v() { + double d0 = -0.03999999910593033D; + double d1 = this.isNoGravity() ? 0.0D : -0.03999999910593033D; + double d2 = 0.0D; + + this.aw = 0.05F; + if (this.aM == EntityBoat.EnumStatus.IN_AIR && this.aL != EntityBoat.EnumStatus.IN_AIR && this.aL != EntityBoat.EnumStatus.ON_LAND) { + this.aJ = this.getBoundingBox().minY + (double) this.length; + this.setPosition(this.locX, (double) (this.k() - this.length) + 0.101D, this.locZ); + this.motY = 0.0D; + this.aN = 0.0D; + this.aL = EntityBoat.EnumStatus.IN_WATER; + } else { + if (this.aL == EntityBoat.EnumStatus.IN_WATER) { + d2 = (this.aJ - this.getBoundingBox().minY) / (double) this.length; + this.aw = 0.9F; + } else if (this.aL == EntityBoat.EnumStatus.UNDER_FLOWING_WATER) { + d1 = -7.0E-4D; + this.aw = 0.9F; + } else if (this.aL == EntityBoat.EnumStatus.UNDER_WATER) { + d2 = 0.009999999776482582D; + this.aw = 0.45F; + } else if (this.aL == EntityBoat.EnumStatus.IN_AIR) { + this.aw = 0.9F; + } else if (this.aL == EntityBoat.EnumStatus.ON_LAND) { + this.aw = this.aK; + if (this.bO() instanceof EntityHuman) { + this.aK /= 2.0F; + } + } + + this.motX *= (double) this.aw; + this.motZ *= (double) this.aw; + this.ay *= this.aw; + this.motY += d1; + if (d2 > 0.0D) { + double d3 = 0.65D; + + this.motY += d2 * 0.06153846016296973D; + double d4 = 0.75D; + + this.motY *= 0.75D; + } + } + + } + + private void x() { + if (this.isVehicle()) { + float f = 0.0F; + + if (this.aF) { + this.ay += -1.0F; + } + + if (this.aG) { + ++this.ay; + } + + if (this.aG != this.aF && !this.aH && !this.aI) { + f += 0.005F; + } + + this.yaw += this.ay; + if (this.aH) { + f += 0.04F; + } + + if (this.aI) { + f -= 0.005F; + } + + this.motX += (double) (MathHelper.sin(-this.yaw * 0.017453292F) * f); + this.motZ += (double) (MathHelper.cos(this.yaw * 0.017453292F) * f); + this.a(this.aG && !this.aF || this.aH, this.aF && !this.aG || this.aH); + } + } + + public void k(Entity entity) { + if (this.w(entity)) { + float f = 0.0F; + float f1 = (float) ((this.dead ? 0.009999999776482582D : this.aJ()) + entity.aI()); + + if (this.bP().size() > 1) { + int i = this.bP().indexOf(entity); + + if (i == 0) { + f = 0.2F; + } else { + f = -0.6F; + } + + if (entity instanceof EntityAnimal) { + f = (float) ((double) f + 0.2D); + } + } + + Vec3D vec3d = (new Vec3D((double) f, 0.0D, 0.0D)).b(-this.yaw * 0.017453292F - 1.5707964F); + + entity.setPosition(this.locX + vec3d.x, this.locY + (double) f1, this.locZ + vec3d.z); + entity.yaw += this.ay; + entity.setHeadRotation(entity.getHeadRotation() + this.ay); + this.a(entity); + if (entity instanceof EntityAnimal && this.bP().size() > 1) { + int j = entity.getId() % 2 == 0 ? 90 : 270; + + entity.k(((EntityAnimal) entity).aQ + (float) j); + entity.setHeadRotation(entity.getHeadRotation() + (float) j); + } + + } + } + + protected void a(Entity entity) { + entity.k(this.yaw); + float f = MathHelper.g(entity.yaw - this.yaw); + float f1 = MathHelper.a(f, -105.0F, 105.0F); + + entity.lastYaw += f1 - f; + entity.yaw += f1 - f; + entity.setHeadRotation(entity.yaw); + } + + protected void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setString("Type", this.getType().a()); + } + + protected void a(NBTTagCompound nbttagcompound) { + if (nbttagcompound.hasKeyOfType("Type", 8)) { + this.setType(EntityBoat.EnumBoatType.a(nbttagcompound.getString("Type"))); + } + + } + + public boolean b(EntityHuman entityhuman, EnumHand enumhand) { + if (entityhuman.isSneaking()) { + return false; + } else { + if (!this.world.isClientSide && this.ax < 60.0F) { + entityhuman.startRiding(this); + } + + return true; + } + } + + protected void a(double d0, boolean flag, IBlockData iblockdata, BlockPosition blockposition) { + this.aN = this.motY; + if (!this.isPassenger()) { + if (flag) { + if (this.fallDistance > 3.0F) { + if (this.aL != EntityBoat.EnumStatus.ON_LAND) { + this.fallDistance = 0.0F; + return; + } + + this.c(this.fallDistance, 1.0F); + if (!this.world.isClientSide && !this.dead) { + // CraftBukkit start + Vehicle vehicle = (Vehicle) this.getBukkitEntity(); + VehicleDestroyEvent destroyEvent = new VehicleDestroyEvent(vehicle, null); + this.world.getServer().getPluginManager().callEvent(destroyEvent); + if (!destroyEvent.isCancelled()) { + this.die(); + if (this.world.getGameRules().getBoolean("doEntityDrops")) { + int i; + + for (i = 0; i < 3; ++i) { + this.a((IMaterial) this.getType().b()); + } + + for (i = 0; i < 2; ++i) { + this.a((IMaterial) Items.STICK); + } + } + } + } // CraftBukkit end + } + + this.fallDistance = 0.0F; + } else if (!this.world.getFluid((new BlockPosition(this)).down()).a(TagsFluid.WATER) && d0 < 0.0D) { + this.fallDistance = (float) ((double) this.fallDistance - d0); + } + + } + } + + public boolean a(int i) { + return (Boolean) this.datawatcher.get(i == 0 ? EntityBoat.e : EntityBoat.f) && this.bO() != null; + } + + public void setDamage(float f) { + this.datawatcher.set(EntityBoat.c, f); + } + + public float m() { + return (Float) this.datawatcher.get(EntityBoat.c); + } + + public void b(int i) { + this.datawatcher.set(EntityBoat.a, i); + } + + public int n() { + return (Integer) this.datawatcher.get(EntityBoat.a); + } + + private void d(int i) { + this.datawatcher.set(EntityBoat.g, i); + } + + private int z() { + return (Integer) this.datawatcher.get(EntityBoat.g); + } + + public void c(int i) { + this.datawatcher.set(EntityBoat.b, i); + } + + public int o() { + return (Integer) this.datawatcher.get(EntityBoat.b); + } + + public void setType(EntityBoat.EnumBoatType entityboat_enumboattype) { + this.datawatcher.set(EntityBoat.d, entityboat_enumboattype.ordinal()); + } + + public EntityBoat.EnumBoatType getType() { + return EntityBoat.EnumBoatType.a((Integer) this.datawatcher.get(EntityBoat.d)); + } + + protected boolean q(Entity entity) { + return this.bP().size() < 2 && !this.a(TagsFluid.WATER); + } + + @Nullable + public Entity bO() { + List list = this.bP(); + + return list.isEmpty() ? null : (Entity) list.get(0); + } + + public static enum EnumBoatType { + + OAK(Blocks.OAK_PLANKS, "oak"), SPRUCE(Blocks.SPRUCE_PLANKS, "spruce"), BIRCH(Blocks.BIRCH_PLANKS, "birch"), JUNGLE(Blocks.JUNGLE_PLANKS, "jungle"), ACACIA(Blocks.ACACIA_PLANKS, "acacia"), DARK_OAK(Blocks.DARK_OAK_PLANKS, "dark_oak"); + + private final String g; + private final Block h; + + private EnumBoatType(Block block, String s) { + this.g = s; + this.h = block; + } + + public String a() { + return this.g; + } + + public Block b() { + return this.h; + } + + public String toString() { + return this.g; + } + + public static EntityBoat.EnumBoatType a(int i) { + EntityBoat.EnumBoatType[] aentityboat_enumboattype = values(); + + if (i < 0 || i >= aentityboat_enumboattype.length) { + i = 0; + } + + return aentityboat_enumboattype[i]; + } + + public static EntityBoat.EnumBoatType a(String s) { + EntityBoat.EnumBoatType[] aentityboat_enumboattype = values(); + + for (int i = 0; i < aentityboat_enumboattype.length; ++i) { + if (aentityboat_enumboattype[i].a().equals(s)) { + return aentityboat_enumboattype[i]; + } + } + + return aentityboat_enumboattype[0]; + } + } + + public static enum EnumStatus { + + IN_WATER, UNDER_WATER, UNDER_FLOWING_WATER, ON_LAND, IN_AIR; + + private EnumStatus() {} + } +} diff --git a/src/main/java/net/minecraft/server/EntityCaveSpider.java b/src/main/java/net/minecraft/server/EntityCaveSpider.java new file mode 100644 index 000000000000..480beced86cc --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityCaveSpider.java @@ -0,0 +1,52 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class EntityCaveSpider extends EntitySpider { + + public EntityCaveSpider(World world) { + super(EntityTypes.CAVE_SPIDER, world); + this.setSize(0.7F, 0.5F); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(12.0D); + } + + public boolean B(Entity entity) { + if (super.B(entity)) { + if (entity instanceof EntityLiving) { + byte b0 = 0; + + if (this.world.getDifficulty() == EnumDifficulty.NORMAL) { + b0 = 7; + } else if (this.world.getDifficulty() == EnumDifficulty.HARD) { + b0 = 15; + } + + if (b0 > 0) { + ((EntityLiving) entity).addEffect(new MobEffect(MobEffects.POISON, b0 * 20, 0), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit + } + } + + return true; + } else { + return false; + } + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + return groupdataentity; + } + + public float getHeadHeight() { + return 0.45F; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.z; + } +} diff --git a/src/main/java/net/minecraft/server/EntityChicken.java b/src/main/java/net/minecraft/server/EntityChicken.java new file mode 100644 index 000000000000..070a9e7b1495 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityChicken.java @@ -0,0 +1,150 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class EntityChicken extends EntityAnimal { + + private static final RecipeItemStack bK = RecipeItemStack.a(Items.WHEAT_SEEDS, Items.MELON_SEEDS, Items.PUMPKIN_SEEDS, Items.BEETROOT_SEEDS); + public float bC; + public float bD; + public float bE; + public float bG; + public float bH = 1.0F; + public int bI; + public boolean bJ; + + public EntityChicken(World world) { + super(EntityTypes.CHICKEN, world); + this.setSize(0.4F, 0.7F); + this.bI = this.random.nextInt(6000) + 6000; + this.a(PathType.WATER, 0.0F); + } + + protected void n() { + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(1, new PathfinderGoalPanic(this, 1.4D)); + this.goalSelector.a(2, new PathfinderGoalBreed(this, 1.0D)); + this.goalSelector.a(3, new PathfinderGoalTempt(this, 1.0D, false, EntityChicken.bK)); + this.goalSelector.a(4, new PathfinderGoalFollowParent(this, 1.1D)); + this.goalSelector.a(5, new PathfinderGoalRandomStrollLand(this, 1.0D)); + this.goalSelector.a(6, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(7, new PathfinderGoalRandomLookaround(this)); + } + + public float getHeadHeight() { + return this.length; + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(4.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.25D); + } + + public void movementTick() { + // CraftBukkit start + if (this.isChickenJockey()) { + this.persistent = !this.isTypeNotPersistent(); + } + // CraftBukkit end + super.movementTick(); + this.bG = this.bC; + this.bE = this.bD; + this.bD = (float) ((double) this.bD + (double) (this.onGround ? -1 : 4) * 0.3D); + this.bD = MathHelper.a(this.bD, 0.0F, 1.0F); + if (!this.onGround && this.bH < 1.0F) { + this.bH = 1.0F; + } + + this.bH = (float) ((double) this.bH * 0.9D); + if (!this.onGround && this.motY < 0.0D) { + this.motY *= 0.6D; + } + + this.bC += this.bH * 2.0F; + if (!this.world.isClientSide && !this.isBaby() && !this.isChickenJockey() && --this.bI <= 0) { + this.a(SoundEffects.ENTITY_CHICKEN_EGG, 1.0F, (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F); + this.forceDrops = true; // CraftBukkit + this.a((IMaterial) Items.EGG); + this.forceDrops = false; // CraftBukkit + this.bI = this.random.nextInt(6000) + 6000; + } + + } + + public void c(float f, float f1) {} + + protected SoundEffect D() { + return SoundEffects.ENTITY_CHICKEN_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_CHICKEN_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_CHICKEN_DEATH; + } + + protected void a(BlockPosition blockposition, IBlockData iblockdata) { + this.a(SoundEffects.ENTITY_CHICKEN_STEP, 0.15F, 1.0F); + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.J; + } + + public EntityChicken createChild(EntityAgeable entityageable) { + return EntityTypes.CHICKEN.create(world); // Paper + } + + public boolean f(ItemStack itemstack) { + return EntityChicken.bK.test(itemstack); + } + + protected int getExpValue(EntityHuman entityhuman) { + return this.isChickenJockey() ? 10 : super.getExpValue(entityhuman); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.bJ = nbttagcompound.getBoolean("IsChickenJockey"); + if (nbttagcompound.hasKey("EggLayTime")) { + this.bI = nbttagcompound.getInt("EggLayTime"); + } + + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("IsChickenJockey", this.bJ); + nbttagcompound.setInt("EggLayTime", this.bI); + } + + public boolean isTypeNotPersistent() { + return this.isChickenJockey() && !this.isVehicle(); + } + + public void k(Entity entity) { + super.k(entity); + float f = MathHelper.sin(this.aQ * 0.017453292F); + float f1 = MathHelper.cos(this.aQ * 0.017453292F); + float f2 = 0.1F; + float f3 = 0.0F; + + entity.setPosition(this.locX + (double) (0.1F * f), this.locY + (double) (this.length * 0.5F) + entity.aI() + 0.0D, this.locZ - (double) (0.1F * f1)); + if (entity instanceof EntityLiving) { + ((EntityLiving) entity).aQ = this.aQ; + } + + } + + public boolean isChickenJockey() { + return this.bJ; + } + + public void s(boolean flag) { + this.bJ = flag; + } +} diff --git a/src/main/java/net/minecraft/server/EntityCow.java b/src/main/java/net/minecraft/server/EntityCow.java new file mode 100644 index 000000000000..cc53e915d787 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityCow.java @@ -0,0 +1,97 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +// CraftBukkit end + +public class EntityCow extends EntityAnimal { + + protected EntityCow(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.setSize(0.9F, 1.4F); + } + + public EntityCow(World world) { + this(EntityTypes.COW, world); + } + + protected void n() { + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(1, new PathfinderGoalPanic(this, 2.0D)); + this.goalSelector.a(2, new PathfinderGoalBreed(this, 1.0D)); + this.goalSelector.a(3, new PathfinderGoalTempt(this, 1.25D, RecipeItemStack.a(Items.WHEAT), false)); + this.goalSelector.a(4, new PathfinderGoalFollowParent(this, 1.25D)); + this.goalSelector.a(5, new PathfinderGoalRandomStrollLand(this, 1.0D)); + this.goalSelector.a(6, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(7, new PathfinderGoalRandomLookaround(this)); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(10.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.20000000298023224D); + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_COW_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_COW_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_COW_DEATH; + } + + protected void a(BlockPosition blockposition, IBlockData iblockdata) { + this.a(SoundEffects.ENTITY_COW_STEP, 0.15F, 1.0F); + } + + protected float cD() { + return 0.4F; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.S; + } + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (itemstack.getItem() == Items.BUCKET && !entityhuman.abilities.canInstantlyBuild && !this.isBaby()) { + // CraftBukkit start - Got milk? + org.bukkit.Location loc = this.getBukkitEntity().getLocation(); + org.bukkit.event.player.PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent(entityhuman, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), null, itemstack, Items.MILK_BUCKET, enumhand); // Paper - add enumHand + + if (event.isCancelled()) { + return false; + } + + ItemStack result = CraftItemStack.asNMSCopy(event.getItemStack()); + entityhuman.a(SoundEffects.ENTITY_COW_MILK, 1.0F, 1.0F); + itemstack.subtract(1); + if (itemstack.isEmpty()) { + entityhuman.a(enumhand, result); + } else if (!entityhuman.inventory.pickup(result)) { + entityhuman.drop(result, false); + } + // CraftBukkit end + + return true; + } else { + return super.a(entityhuman, enumhand); + } + } + + public EntityCow createChild(EntityAgeable entityageable) { + return EntityTypes.COW.create(world); // Paper + } + + public float getHeadHeight() { + return this.isBaby() ? this.length : 1.3F; + } +} diff --git a/src/main/java/net/minecraft/server/EntityCreature.java b/src/main/java/net/minecraft/server/EntityCreature.java new file mode 100644 index 000000000000..d659c57dbe08 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityCreature.java @@ -0,0 +1,111 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.event.entity.EntityUnleashEvent; +// CraftBukkit end + +public abstract class EntityCreature extends EntityInsentient { + + public org.bukkit.craftbukkit.entity.CraftCreature getBukkitCreature() { return (org.bukkit.craftbukkit.entity.CraftCreature) super.getBukkitEntity(); } // Paper + public BlockPosition movingTarget = null; public BlockPosition getMovingTarget() { return movingTarget; } // Paper + private BlockPosition a; + private float b; + + protected EntityCreature(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.a = BlockPosition.ZERO; + this.b = -1.0F; + } + + public float a(BlockPosition blockposition) { + return this.a(blockposition, (IWorldReader) this.world); + } + + public float a(BlockPosition blockposition, IWorldReader iworldreader) { + return 0.0F; + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + return super.a(generatoraccess, flag) && this.a(new BlockPosition(this.locX, this.getBoundingBox().minY, this.locZ), (IWorldReader) generatoraccess) >= 0.0F; + } + + public boolean dr() { + return !this.navigation.p(); + } + + public boolean ds() { + return this.f(new BlockPosition(this)); + } + + public boolean f(BlockPosition blockposition) { + return this.b == -1.0F ? true : this.a.n(blockposition) < (double) (this.b * this.b); + } + + public void a(BlockPosition blockposition, int i) { + this.a = blockposition; + this.b = (float) i; + } + + public BlockPosition dt() { + return this.a; + } + + public float du() { + return this.b; + } + + public void dv() { + this.b = -1.0F; + } + + public boolean dw() { + return this.b != -1.0F; + } + + protected void dl() { + super.dl(); + if (this.isLeashed() && this.getLeashHolder() != null && this.getLeashHolder().world == this.world) { + Entity entity = this.getLeashHolder(); + + this.a(new BlockPosition((int) entity.locX, (int) entity.locY, (int) entity.locZ), 5); + float f = this.g(entity); + + if (this instanceof EntityTameableAnimal && ((EntityTameableAnimal) this).isSitting()) { + if (f > 10.0F) { + this.world.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), EntityUnleashEvent.UnleashReason.DISTANCE)); // CraftBukkit + this.unleash(true, true); + } + + return; + } + + this.u(f); + if (f > 10.0F) { + this.world.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), EntityUnleashEvent.UnleashReason.DISTANCE)); // CraftBukkit + this.unleash(true, true); + this.goalSelector.c(1); + } else if (f > 6.0F) { + double d0 = (entity.locX - this.locX) / (double) f; + double d1 = (entity.locY - this.locY) / (double) f; + double d2 = (entity.locZ - this.locZ) / (double) f; + + this.motX += d0 * Math.abs(d0) * 0.4D; + this.motY += d1 * Math.abs(d1) * 0.4D; + this.motZ += d2 * Math.abs(d2) * 0.4D; + } else { + this.goalSelector.d(1); + float f1 = 2.0F; + Vec3D vec3d = (new Vec3D(entity.locX - this.locX, entity.locY - this.locY, entity.locZ - this.locZ)).a().a((double) Math.max(f - 2.0F, 0.0F)); + + this.getNavigation().a(this.locX + vec3d.x, this.locY + vec3d.y, this.locZ + vec3d.z, this.dx()); + } + } + + } + + protected double dx() { + return 1.0D; + } + + protected void u(float f) {} +} diff --git a/src/main/java/net/minecraft/server/EntityCreeper.java b/src/main/java/net/minecraft/server/EntityCreeper.java new file mode 100644 index 000000000000..945a75dd6234 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityCreeper.java @@ -0,0 +1,267 @@ +package net.minecraft.server; + +import java.util.Collection; +import java.util.Iterator; +import javax.annotation.Nullable; +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.ExplosionPrimeEvent; +// CraftBukkit end + +public class EntityCreeper extends EntityMonster { + + private static final DataWatcherObject a = DataWatcher.a(EntityCreeper.class, DataWatcherRegistry.b); + private static final DataWatcherObject b = DataWatcher.a(EntityCreeper.class, DataWatcherRegistry.i); + private static final DataWatcherObject c = DataWatcher.a(EntityCreeper.class, DataWatcherRegistry.i);private static final DataWatcherObject isIgnitedDW = c; // Paper OBFHELPER + private int bC; + public int fuseTicks; // Paper - public + public int maxFuseTicks = 30; + public int explosionRadius = 3; + private int bG; + + public EntityCreeper(World world) { + super(EntityTypes.CREEPER, world); + this.setSize(0.6F, 1.7F); + } + + protected void n() { + this.goalSelector.a(1, new PathfinderGoalFloat(this)); + this.goalSelector.a(2, new PathfinderGoalSwell(this)); + this.goalSelector.a(3, new PathfinderGoalAvoidTarget<>(this, EntityOcelot.class, 6.0F, 1.0D, 1.2D)); + this.goalSelector.a(4, new PathfinderGoalMeleeAttack(this, 1.0D, false)); + this.goalSelector.a(5, new PathfinderGoalRandomStrollLand(this, 0.8D)); + this.goalSelector.a(6, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(6, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new PathfinderGoalNearestAttackableTarget<>(this, EntityHuman.class, true)); + this.targetSelector.a(2, new PathfinderGoalHurtByTarget(this, false, new Class[0])); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.25D); + } + + public int bn() { + return this.getGoalTarget() == null ? 3 : 3 + (int) (this.getHealth() - 1.0F); + } + + public void c(float f, float f1) { + super.c(f, f1); + this.fuseTicks = (int) ((float) this.fuseTicks + f * 1.5F); + if (this.fuseTicks > this.maxFuseTicks - 5) { + this.fuseTicks = this.maxFuseTicks - 5; + } + + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityCreeper.a, -1); + this.datawatcher.register(EntityCreeper.b, false); + this.datawatcher.register(EntityCreeper.c, false); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + if ((Boolean) this.datawatcher.get(EntityCreeper.b)) { + nbttagcompound.setBoolean("powered", true); + } + + nbttagcompound.setShort("Fuse", (short) this.maxFuseTicks); + nbttagcompound.setByte("ExplosionRadius", (byte) this.explosionRadius); + nbttagcompound.setBoolean("ignited", this.isIgnited()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.datawatcher.set(EntityCreeper.b, nbttagcompound.getBoolean("powered")); + if (nbttagcompound.hasKeyOfType("Fuse", 99)) { + this.maxFuseTicks = nbttagcompound.getShort("Fuse"); + } + + if (nbttagcompound.hasKeyOfType("ExplosionRadius", 99)) { + this.explosionRadius = nbttagcompound.getByte("ExplosionRadius"); + } + + if (nbttagcompound.getBoolean("ignited")) { + this.dB(); + } + + } + + public void tick() { + if (this.isAlive()) { + this.bC = this.fuseTicks; + if (this.isIgnited()) { + this.a(1); + } + + int i = this.dz(); + + if (i > 0 && this.fuseTicks == 0) { + this.a(SoundEffects.ENTITY_CREEPER_PRIMED, 1.0F, 0.5F); + } + + this.fuseTicks += i; + if (this.fuseTicks < 0) { + this.fuseTicks = 0; + } + + if (this.fuseTicks >= this.maxFuseTicks) { + this.fuseTicks = this.maxFuseTicks; + this.dE(); + } + } + + super.tick(); + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_CREEPER_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_CREEPER_DEATH; + } + + public void die(DamageSource damagesource) { + // super.die(damagesource); // CraftBukkit - Moved to end + if (this.world.getGameRules().getBoolean("doMobLoot")) { + if (damagesource.getEntity() instanceof EntitySkeleton) { + this.a((IMaterial) ItemRecord.a(this.random)); + } else if (damagesource.getEntity() instanceof EntityCreeper && damagesource.getEntity() != this && ((EntityCreeper) damagesource.getEntity()).isPowered() && ((EntityCreeper) damagesource.getEntity()).canCauseHeadDrop()) { + ((EntityCreeper) damagesource.getEntity()).setCausedHeadDrop(); + this.a((IMaterial) Items.CREEPER_HEAD); + } + } + super.die(damagesource); // CraftBukkit - Moved from above + + } + + public boolean B(Entity entity) { + return true; + } + + public boolean isPowered() { + return (Boolean) this.datawatcher.get(EntityCreeper.b); + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.x; + } + + public int dz() { + return (Integer) this.datawatcher.get(EntityCreeper.a); + } + + public void a(int i) { + this.datawatcher.set(EntityCreeper.a, i); + } + + public void onLightningStrike(EntityLightning entitylightning) { + super.onLightningStrike(entitylightning); + // CraftBukkit start + if (CraftEventFactory.callCreeperPowerEvent(this, entitylightning, org.bukkit.event.entity.CreeperPowerEvent.PowerCause.LIGHTNING).isCancelled()) { + return; + } + + this.setPowered(true); + } + + public void setPowered(boolean powered) { + this.datawatcher.set(EntityCreeper.b, powered); + } + // CraftBukkit end + + protected boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (itemstack.getItem() == Items.FLINT_AND_STEEL) { + this.world.a(entityhuman, this.locX, this.locY, this.locZ, SoundEffects.ITEM_FLINTANDSTEEL_USE, this.bV(), 1.0F, this.random.nextFloat() * 0.4F + 0.8F); + entityhuman.a(enumhand); + if (!this.world.isClientSide) { + this.dB(); + itemstack.damage(1, entityhuman); + return true; + } + } + + return super.a(entityhuman, enumhand); + } + + public void explode() { this.dE(); } // Paper - OBFHELPER + private void dE() { + if (!this.world.isClientSide) { + boolean flag = this.world.getGameRules().getBoolean("mobGriefing"); + float f = this.isPowered() ? 2.0F : 1.0F; + + // CraftBukkit start + ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), this.explosionRadius * f, false); + this.world.getServer().getPluginManager().callEvent(event); + if (!event.isCancelled()) { + this.killed = true; + this.world.createExplosion(this, this.locX, this.locY, this.locZ, event.getRadius(), event.getFire(), flag); + this.die(); + this.createEffectCloud(); + } else { + fuseTicks = 0; + this.datawatcher.set(isIgnitedDW, Boolean.valueOf(false)); // Paper + } + // CraftBukkit end + } + + } + + private void createEffectCloud() { + Collection collection = this.getEffects(); + + if (!collection.isEmpty() && !world.paperConfig.disableCreeperLingeringEffect) { // Paper + EntityAreaEffectCloud entityareaeffectcloud = new EntityAreaEffectCloud(this.world, this.locX, this.locY, this.locZ); + + entityareaeffectcloud.setSource(this); // CraftBukkit + entityareaeffectcloud.setRadius(2.5F); + entityareaeffectcloud.setRadiusOnUse(-0.5F); + entityareaeffectcloud.setWaitTime(10); + entityareaeffectcloud.setDuration(entityareaeffectcloud.getDuration() / 2); + entityareaeffectcloud.setRadiusPerTick(-entityareaeffectcloud.getRadius() / (float) entityareaeffectcloud.getDuration()); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + entityareaeffectcloud.a(new MobEffect(mobeffect)); + } + + this.world.addEntity(entityareaeffectcloud); + } + + } + + public boolean isIgnited() { + return (Boolean) this.datawatcher.get(EntityCreeper.c); + } + + // Paper start + public void setIgnited(boolean ignited) { + if (isIgnited() != ignited) { + com.destroystokyo.paper.event.entity.CreeperIgniteEvent event = new com.destroystokyo.paper.event.entity.CreeperIgniteEvent((org.bukkit.entity.Creeper) getBukkitEntity(), ignited); + if (event.callEvent()) { + this.datawatcher.set(EntityCreeper.c, event.isIgnited()); + } + } + } + + public void dB() { + setIgnited(true); + // Paper end + } + + public boolean canCauseHeadDrop() { + return this.bG < 1 && this.world.getGameRules().getBoolean("doMobLoot"); + } + + public void setCausedHeadDrop() { + ++this.bG; + } +} diff --git a/src/main/java/net/minecraft/server/EntityDamageSourceIndirect.java b/src/main/java/net/minecraft/server/EntityDamageSourceIndirect.java new file mode 100644 index 000000000000..93ca59f3b431 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityDamageSourceIndirect.java @@ -0,0 +1,38 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class EntityDamageSourceIndirect extends EntityDamageSource { + + private final Entity owner; + + public EntityDamageSourceIndirect(String s, Entity entity, @Nullable Entity entity1) { + super(s, entity); + this.owner = entity1; + } + + @Nullable + public Entity j() { + return this.w; + } + + @Nullable + public Entity getEntity() { + return this.owner; + } + + public IChatBaseComponent getLocalizedDeathMessage(EntityLiving entityliving) { + IChatBaseComponent ichatbasecomponent = this.owner == null ? this.w.getScoreboardDisplayName() : this.owner.getScoreboardDisplayName(); + ItemStack itemstack = this.owner instanceof EntityLiving ? ((EntityLiving) this.owner).getItemInMainHand() : ItemStack.a; + String s = "death.attack." + this.translationIndex; + String s1 = s + ".item"; + + return !itemstack.isEmpty() && itemstack.hasName() ? new ChatMessage(s1, new Object[] { entityliving.getScoreboardDisplayName(), ichatbasecomponent, itemstack.A()}) : new ChatMessage(s, new Object[] { entityliving.getScoreboardDisplayName(), ichatbasecomponent}); + } + + // CraftBukkit start + public Entity getProximateDamageSource() { + return super.getEntity(); + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/EntityDolphin.java b/src/main/java/net/minecraft/server/EntityDolphin.java new file mode 100644 index 000000000000..8bf15a685878 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityDolphin.java @@ -0,0 +1,544 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public class EntityDolphin extends EntityWaterAnimal { + + private static final DataWatcherObject b = DataWatcher.a(EntityDolphin.class, DataWatcherRegistry.l); + private static final DataWatcherObject c = DataWatcher.a(EntityDolphin.class, DataWatcherRegistry.i); + private static final DataWatcherObject bC = DataWatcher.a(EntityDolphin.class, DataWatcherRegistry.b); + public static final Predicate a = (entityitem) -> { + return !entityitem.q() && entityitem.isAlive() && entityitem.isInWater(); + }; + + public EntityDolphin(World world) { + super(EntityTypes.DOLPHIN, world); + this.setSize(0.9F, 0.6F); + this.moveController = new EntityDolphin.a(this); + this.lookController = new ControllerLookDolphin(this, 10); + this.p(true); + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + this.setAirTicks(this.bf()); + this.pitch = 0.0F; + return super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + } + + public boolean ca() { + return false; + } + + protected void a(int i) {} + + public void g(BlockPosition blockposition) { + this.datawatcher.set(EntityDolphin.b, blockposition); + } + + public BlockPosition l() { + return (BlockPosition) this.datawatcher.get(EntityDolphin.b); + } + + public boolean dy() { + return (Boolean) this.datawatcher.get(EntityDolphin.c); + } + + public void a(boolean flag) { + this.datawatcher.set(EntityDolphin.c, flag); + } + + public int dz() { + return (Integer) this.datawatcher.get(EntityDolphin.bC); + } + + public void b(int i) { + this.datawatcher.set(EntityDolphin.bC, i); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityDolphin.b, BlockPosition.ZERO); + this.datawatcher.register(EntityDolphin.c, false); + this.datawatcher.register(EntityDolphin.bC, 2400); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("TreasurePosX", this.l().getX()); + nbttagcompound.setInt("TreasurePosY", this.l().getY()); + nbttagcompound.setInt("TreasurePosZ", this.l().getZ()); + nbttagcompound.setBoolean("GotFish", this.dy()); + nbttagcompound.setInt("Moistness", this.dz()); + } + + public void a(NBTTagCompound nbttagcompound) { + int i = nbttagcompound.getInt("TreasurePosX"); + int j = nbttagcompound.getInt("TreasurePosY"); + int k = nbttagcompound.getInt("TreasurePosZ"); + + this.g(new BlockPosition(i, j, k)); + super.a(nbttagcompound); + this.a(nbttagcompound.getBoolean("GotFish")); + this.b(nbttagcompound.getInt("Moistness")); + } + + protected void n() { + this.goalSelector.a(0, new PathfinderGoalBreath(this)); + this.goalSelector.a(0, new PathfinderGoalWater(this)); + this.goalSelector.a(1, new EntityDolphin.b(this)); + this.goalSelector.a(2, new EntityDolphin.c(this, 4.0D)); + this.goalSelector.a(4, new PathfinderGoalRandomSwim(this, 1.0D, 10)); + this.goalSelector.a(4, new PathfinderGoalRandomLookaround(this)); + this.goalSelector.a(5, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(5, new PathfinderGoalWaterJump(this, 10)); + this.goalSelector.a(6, new PathfinderGoalMeleeAttack(this, 1.2000000476837158D, true)); + this.goalSelector.a(8, new EntityDolphin.d()); + this.goalSelector.a(8, new PathfinderGoalFollowBoat(this)); + this.goalSelector.a(9, new PathfinderGoalAvoidTarget<>(this, EntityGuardian.class, 8.0F, 1.0D, 1.0D)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, true, new Class[] { EntityGuardian.class})); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(10.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(1.2000000476837158D); + this.getAttributeMap().b(GenericAttributes.ATTACK_DAMAGE); + this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue(3.0D); + } + + protected NavigationAbstract b(World world) { + return new NavigationGuardian(this, world); + } + + public boolean B(Entity entity) { + boolean flag = entity.damageEntity(DamageSource.mobAttack(this), (float) ((int) this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).getValue())); + + if (flag) { + this.a((EntityLiving) this, entity); + this.a(SoundEffects.ENTITY_DOLPHIN_ATTACK, 1.0F, 1.0F); + } + + return flag; + } + + public int bf() { + return 4800; + } + + protected int l(int i) { + return this.bf(); + } + + public float getHeadHeight() { + return 0.3F; + } + + public int K() { + return 1; + } + + public int L() { + return 1; + } + + protected boolean n(Entity entity) { + return true; + } + + protected void a(EntityItem entityitem) { + if (this.getEquipment(EnumItemSlot.MAINHAND).isEmpty()) { + ItemStack itemstack = entityitem.getItemStack(); + + if (this.d(itemstack)) { + this.setSlot(EnumItemSlot.MAINHAND, itemstack); + this.dropChanceHand[EnumItemSlot.MAINHAND.b()] = 2.0F; + this.receive(entityitem, itemstack.getCount()); + entityitem.die(); + } + } + + } + + public void tick() { + super.tick(); + if (!this.isNoAI()) { + if (this.ap()) { + this.b(2400); + } else { + this.b(this.dz() - 1); + if (this.dz() <= 0) { + this.damageEntity(DamageSource.DRYOUT, 1.0F); + } + + if (this.onGround) { + this.motY += 0.5D; + this.motX += (double) ((this.random.nextFloat() * 2.0F - 1.0F) * 0.2F); + this.motZ += (double) ((this.random.nextFloat() * 2.0F - 1.0F) * 0.2F); + this.yaw = this.random.nextFloat() * 360.0F; + this.onGround = false; + this.impulse = true; + } + } + + if (this.world.isClientSide && this.isInWater() && this.motX * this.motX + this.motY * this.motY + this.motZ * this.motZ > 0.03D) { + Vec3D vec3d = this.f(0.0F); + float f = MathHelper.cos(this.yaw * 0.017453292F) * 0.3F; + float f1 = MathHelper.sin(this.yaw * 0.017453292F) * 0.3F; + float f2 = 1.2F - this.random.nextFloat() * 0.7F; + + for (int i = 0; i < 2; ++i) { + this.world.addParticle(Particles.X, this.locX - vec3d.x * (double) f2 + (double) f, this.locY - vec3d.y, this.locZ - vec3d.z * (double) f2 + (double) f1, 0.0D, 0.0D, 0.0D); + this.world.addParticle(Particles.X, this.locX - vec3d.x * (double) f2 - (double) f, this.locY - vec3d.y, this.locZ - vec3d.z * (double) f2 - (double) f1, 0.0D, 0.0D, 0.0D); + } + } + + } + } + + protected boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (!itemstack.isEmpty() && itemstack.getItem().a(TagsItem.FISHES)) { + if (!this.world.isClientSide) { + this.a(SoundEffects.ENTITY_DOLPHIN_EAT, 1.0F, 1.0F); + } + + this.a(true); + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + return true; + } else { + return super.a(entityhuman, enumhand); + } + } + + @Nullable + public EntityItem f(ItemStack itemstack) { + if (itemstack.isEmpty()) { + return null; + } else { + double d0 = this.locY - 0.30000001192092896D + (double) this.getHeadHeight(); + EntityItem entityitem = new EntityItem(this.world, this.locX, d0, this.locZ, itemstack); + + entityitem.a(40); + entityitem.c(this.getUniqueID()); + float f = 0.3F; + + entityitem.motX = (double) (-MathHelper.sin(this.yaw * 0.017453292F) * MathHelper.cos(this.pitch * 0.017453292F) * f); + entityitem.motY = (double) (MathHelper.sin(this.pitch * 0.017453292F) * f * 1.5F); + entityitem.motZ = (double) (MathHelper.cos(this.yaw * 0.017453292F) * MathHelper.cos(this.pitch * 0.017453292F) * f); + float f1 = this.random.nextFloat() * 6.2831855F; + + f = 0.02F * this.random.nextFloat(); + entityitem.motX += (double) (MathHelper.cos(f1) * f); + entityitem.motZ += (double) (MathHelper.sin(f1) * f); + this.world.addEntity(entityitem); + return entityitem; + } + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + return this.locY > 45.0D && this.locY < (double) generatoraccess.getSeaLevel() && generatoraccess.getBiome(new BlockPosition(this)) != Biomes.OCEAN || generatoraccess.getBiome(new BlockPosition(this)) != Biomes.DEEP_OCEAN && super.a(generatoraccess, flag); + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_DOLPHIN_HURT; + } + + @Nullable + protected SoundEffect cs() { + return SoundEffects.ENTITY_DOLPHIN_DEATH; + } + + @Nullable + protected SoundEffect D() { + return this.isInWater() ? SoundEffects.ENTITY_DOLPHIN_AMBIENT_WATER : SoundEffects.ENTITY_DOLPHIN_AMBIENT; + } + + protected SoundEffect ae() { + return SoundEffects.ENTITY_DOLPHIN_SPLASH; + } + + protected SoundEffect ad() { + return SoundEffects.ENTITY_DOLPHIN_SWIM; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.aN; + } + + protected boolean dA() { + BlockPosition blockposition = this.getNavigation().i(); + + return blockposition != null ? this.c(blockposition) < 144.0D : false; + } + + public void a(float f, float f1, float f2) { + if (this.cP() && this.isInWater()) { + this.a(f, f1, f2, this.cK()); + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + this.motX *= 0.8999999761581421D; + this.motY *= 0.8999999761581421D; + this.motZ *= 0.8999999761581421D; + if (this.getGoalTarget() == null) { + this.motY -= 0.005D; + } + } else { + super.a(f, f1, f2); + } + + } + + public boolean a(EntityHuman entityhuman) { + return true; + } + + static class b extends PathfinderGoal { + + private final EntityDolphin a; + private boolean b; + + b(EntityDolphin entitydolphin) { + this.a = entitydolphin; + this.a(3); + } + + public boolean f() { + return false; + } + + public boolean a() { + return this.a.dy() && this.a.getAirTicks() >= 100; + } + + public boolean b() { + BlockPosition blockposition = this.a.l(); + + return this.a.c(new BlockPosition((double) blockposition.getX(), this.a.locY, (double) blockposition.getZ())) > 16.0D && !this.b && this.a.getAirTicks() >= 100; + } + + public void c() { + this.b = false; + this.a.getNavigation().q(); + World world = this.a.world; + BlockPosition blockposition = new BlockPosition(this.a); + String s = (double) world.random.nextFloat() >= 0.5D ? "Ocean_Ruin" : "Shipwreck"; + BlockPosition blockposition1 = world.a(s, blockposition, 50, false); + + if (blockposition1 == null) { + BlockPosition blockposition2 = world.a(s.equals("Ocean_Ruin") ? "Shipwreck" : "Ocean_Ruin", blockposition, 50, false); + + if (blockposition2 == null) { + this.b = true; + return; + } + + this.a.g(blockposition2); + } else { + this.a.g(blockposition1); + } + + world.broadcastEntityEffect(this.a, (byte) 38); + } + + public void d() { + BlockPosition blockposition = this.a.l(); + + if (this.a.c(new BlockPosition((double) blockposition.getX(), this.a.locY, (double) blockposition.getZ())) <= 16.0D || this.b) { + this.a.a(false); + } + + } + + public void e() { + BlockPosition blockposition = this.a.l(); + World world = this.a.world; + + if (this.a.dA() || this.a.getNavigation().p()) { + Vec3D vec3d = RandomPositionGenerator.a((EntityCreature) this.a, 16, 1, new Vec3D((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ()), 0.39269909262657166D); + + if (vec3d == null) { + vec3d = RandomPositionGenerator.a(this.a, 8, 4, new Vec3D((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ())); + } + + if (vec3d != null) { + BlockPosition blockposition1 = new BlockPosition(vec3d); + + if (!world.getFluid(blockposition1).a(TagsFluid.WATER) || !world.getType(blockposition1).a((IBlockAccess) world, blockposition1, PathMode.WATER)) { + vec3d = RandomPositionGenerator.a(this.a, 8, 5, new Vec3D((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ())); + } + } + + if (vec3d == null) { + this.b = true; + return; + } + + this.a.getControllerLook().a(vec3d.x, vec3d.y, vec3d.z, (float) (this.a.L() + 20), (float) this.a.K()); + this.a.getNavigation().a(vec3d.x, vec3d.y, vec3d.z, 1.3D); + if (world.random.nextInt(80) == 0) { + world.broadcastEntityEffect(this.a, (byte) 38); + } + } + + } + } + + static class c extends PathfinderGoal { + + private final EntityDolphin a; + private final double b; + private EntityHuman c; + + c(EntityDolphin entitydolphin, double d0) { + this.a = entitydolphin; + this.b = d0; + this.a(3); + } + + public boolean a() { + this.c = this.a.world.findNearbyPlayer(this.a, 10.0D); + return this.c == null ? false : this.c.isSwimming(); + } + + public boolean b() { + return this.c != null && this.c.isSwimming() && this.a.h(this.c) < 256.0D; + } + + public void c() { + this.c.addEffect(new MobEffect(MobEffects.DOLPHINS_GRACE, 100), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.DOLPHIN); // CraftBukkit + } + + public void d() { + this.c = null; + this.a.getNavigation().q(); + } + + public void e() { + this.a.getControllerLook().a(this.c, (float) (this.a.L() + 20), (float) this.a.K()); + if (this.a.h(this.c) < 6.25D) { + this.a.getNavigation().q(); + } else { + this.a.getNavigation().a((Entity) this.c, this.b); + } + + if (this.c.isSwimming() && this.c.world.random.nextInt(6) == 0) { + this.c.addEffect(new MobEffect(MobEffects.DOLPHINS_GRACE, 100), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.DOLPHIN); // CraftBukkit + } + + } + } + + class d extends PathfinderGoal { + + private int b; + + private d() {} + + public boolean a() { + if (this.b > EntityDolphin.this.ticksLived) { + return false; + } else { + List list = EntityDolphin.this.world.a(EntityItem.class, EntityDolphin.this.getBoundingBox().grow(8.0D, 8.0D, 8.0D), EntityDolphin.a); + + return !list.isEmpty() || !EntityDolphin.this.getEquipment(EnumItemSlot.MAINHAND).isEmpty(); + } + } + + public void c() { + List list = EntityDolphin.this.world.a(EntityItem.class, EntityDolphin.this.getBoundingBox().grow(8.0D, 8.0D, 8.0D), EntityDolphin.a); + + if (!list.isEmpty()) { + EntityDolphin.this.getNavigation().a((Entity) list.get(0), 1.2000000476837158D); + EntityDolphin.this.a(SoundEffects.ENTITY_DOLPHIN_PLAY, 1.0F, 1.0F); + } + + this.b = 0; + } + + public void d() { + ItemStack itemstack = EntityDolphin.this.getEquipment(EnumItemSlot.MAINHAND); + + if (!itemstack.isEmpty()) { + EntityDolphin.this.f(itemstack); + EntityDolphin.this.setSlot(EnumItemSlot.MAINHAND, ItemStack.a); + this.b = EntityDolphin.this.ticksLived + EntityDolphin.this.random.nextInt(100); + } + + } + + public void e() { + List list = EntityDolphin.this.world.a(EntityItem.class, EntityDolphin.this.getBoundingBox().grow(8.0D, 8.0D, 8.0D), EntityDolphin.a); + ItemStack itemstack = EntityDolphin.this.getEquipment(EnumItemSlot.MAINHAND); + + if (!itemstack.isEmpty()) { + EntityDolphin.this.f(itemstack); + EntityDolphin.this.setSlot(EnumItemSlot.MAINHAND, ItemStack.a); + } else if (!list.isEmpty()) { + EntityDolphin.this.getNavigation().a((Entity) list.get(0), 1.2000000476837158D); + } + + } + } + + static class a extends ControllerMove { + + private final EntityDolphin i; + + public a(EntityDolphin entitydolphin) { + super(entitydolphin); + this.i = entitydolphin; + } + + public void a() { + if (this.i.isInWater()) { + this.i.motY += 0.005D; + } + + if (this.h == ControllerMove.Operation.MOVE_TO && !this.i.getNavigation().p()) { + double d0 = this.b - this.i.locX; + double d1 = this.c - this.i.locY; + double d2 = this.d - this.i.locZ; + double d3 = d0 * d0 + d1 * d1 + d2 * d2; + + if (d3 < 2.500000277905201E-7D) { + this.a.r(0.0F); + } else { + float f = (float) (MathHelper.c(d2, d0) * 57.2957763671875D) - 90.0F; + + this.i.yaw = this.a(this.i.yaw, f, 10.0F); + this.i.aQ = this.i.yaw; + this.i.aS = this.i.yaw; + float f1 = (float) (this.e * this.i.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue()); + + if (this.i.isInWater()) { + this.i.o(f1 * 0.02F); + float f2 = -((float) (MathHelper.c(d1, (double) MathHelper.sqrt(d0 * d0 + d2 * d2)) * 57.2957763671875D)); + + f2 = MathHelper.a(MathHelper.g(f2), -85.0F, 85.0F); + this.i.pitch = this.a(this.i.pitch, f2, 5.0F); + float f3 = MathHelper.cos(this.i.pitch * 0.017453292F); + float f4 = MathHelper.sin(this.i.pitch * 0.017453292F); + + this.i.bj = f3 * f1; + this.i.bi = -f4 * f1; + } else { + this.i.o(f1 * 0.1F); + } + + } + } else { + this.i.o(0.0F); + this.i.t(0.0F); + this.i.s(0.0F); + this.i.r(0.0F); + } + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityDragonFireball.java b/src/main/java/net/minecraft/server/EntityDragonFireball.java new file mode 100644 index 000000000000..c0d66a02339c --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityDragonFireball.java @@ -0,0 +1,67 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +public class EntityDragonFireball extends EntityFireball { + + public EntityDragonFireball(World world) { + super(EntityTypes.DRAGON_FIREBALL, world, 1.0F, 1.0F); + } + + public EntityDragonFireball(World world, EntityLiving entityliving, double d0, double d1, double d2) { + super(EntityTypes.DRAGON_FIREBALL, entityliving, d0, d1, d2, world, 1.0F, 1.0F); + } + + protected void a(MovingObjectPosition movingobjectposition) { + if (movingobjectposition.entity == null || !movingobjectposition.entity.s(this.shooter)) { + if (!this.world.isClientSide) { + List list = this.world.a(EntityLiving.class, this.getBoundingBox().grow(4.0D, 2.0D, 4.0D)); + EntityAreaEffectCloud entityareaeffectcloud = new EntityAreaEffectCloud(this.world, this.locX, this.locY, this.locZ); + + entityareaeffectcloud.setSource(this.shooter); + entityareaeffectcloud.setParticle(Particles.j); + entityareaeffectcloud.setRadius(3.0F); + entityareaeffectcloud.setDuration(600); + entityareaeffectcloud.setRadiusPerTick((7.0F - entityareaeffectcloud.getRadius()) / (float) entityareaeffectcloud.getDuration()); + entityareaeffectcloud.a(new MobEffect(MobEffects.HARM, 1, 1)); + if (!list.isEmpty()) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityLiving entityliving = (EntityLiving) iterator.next(); + double d0 = this.h(entityliving); + + if (d0 < 16.0D) { + entityareaeffectcloud.setPosition(entityliving.locX, entityliving.locY, entityliving.locZ); + break; + } + } + } + + if (new com.destroystokyo.paper.event.entity.EnderDragonFireballHitEvent((org.bukkit.entity.DragonFireball) this.getBukkitEntity(), list.stream().map(EntityLiving::getBukkitLivingEntity).collect(java.util.stream.Collectors.toList()), (org.bukkit.entity.AreaEffectCloud) entityareaeffectcloud.getBukkitEntity()).callEvent()) { // Paper + this.world.triggerEffect(2006, new BlockPosition(this.locX, this.locY, this.locZ), 0); + this.world.addEntity(entityareaeffectcloud); + } else entityareaeffectcloud.die(); // Paper + this.die(); + } + + } + } + + public boolean isInteractable() { + return false; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + return false; + } + + protected ParticleParam i() { + return Particles.j; + } + + protected boolean f() { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/EntityDrowned.java b/src/main/java/net/minecraft/server/EntityDrowned.java new file mode 100644 index 000000000000..0e2b5ee7431c --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityDrowned.java @@ -0,0 +1,438 @@ +package net.minecraft.server; + +import java.util.Random; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public class EntityDrowned extends EntityZombie implements IRangedEntity { + + private boolean bC; + protected final NavigationGuardian a; + protected final Navigation b; + + public EntityDrowned(World world) { + super(EntityTypes.DROWNED, world); + this.Q = 1.0F; + this.moveController = new EntityDrowned.e(this); + this.a(PathType.WATER, 0.0F); + this.a = new NavigationGuardian(this, world); + this.b = new Navigation(this, world); + } + + protected void l() { + this.goalSelector.a(1, new EntityDrowned.d(this, 1.0D)); + this.goalSelector.a(2, new EntityDrowned.g(this, 1.0D, 40, 10.0F)); + this.goalSelector.a(2, new EntityDrowned.a(this, 1.0D, false)); + this.goalSelector.a(5, new EntityDrowned.c(this, 1.0D)); + this.goalSelector.a(6, new EntityDrowned.f(this, 1.0D, this.world.getSeaLevel())); + this.goalSelector.a(7, new PathfinderGoalRandomStroll(this, 1.0D)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, true, new Class[] { EntityDrowned.class})); + this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget<>(this, EntityHuman.class, 10, true, false, new EntityDrowned.b(this))); + if ( world.spigotConfig.zombieAggressiveTowardsVillager ) this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget<>(this, EntityVillager.class, false)); + this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget<>(this, EntityIronGolem.class, true)); + this.targetSelector.a(5, new PathfinderGoalNearestAttackableTarget<>(this, EntityTurtle.class, 10, true, false, EntityTurtle.bC)); + } + + protected NavigationAbstract b(World world) { + return super.b(world); + } + + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + groupdataentity = super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + if (this.getEquipment(EnumItemSlot.OFFHAND).isEmpty() && this.random.nextFloat() < 0.03F) { + this.setSlot(EnumItemSlot.OFFHAND, new ItemStack(Items.NAUTILUS_SHELL)); + this.dropChanceHand[EnumItemSlot.OFFHAND.b()] = 2.0F; + } + + return groupdataentity; + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + BiomeBase biomebase = generatoraccess.getBiome(new BlockPosition(this.locX, this.locY, this.locZ)); + + return biomebase != Biomes.RIVER && biomebase != Biomes.FROZEN_RIVER ? this.random.nextInt(40) == 0 && this.dF() && super.a(generatoraccess, flag) : this.random.nextInt(15) == 0 && super.a(generatoraccess, flag); + } + + private boolean dF() { + return this.getBoundingBox().minY < (double) (this.world.getSeaLevel() - 5); + } + + protected boolean dz() { + return false; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.aM; + } + + protected SoundEffect D() { + return this.isInWater() ? SoundEffects.ENTITY_DROWNED_AMBIENT_WATER : SoundEffects.ENTITY_DROWNED_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return this.isInWater() ? SoundEffects.ENTITY_DROWNED_HURT_WATER : SoundEffects.ENTITY_DROWNED_HURT; + } + + protected SoundEffect cs() { + return this.isInWater() ? SoundEffects.ENTITY_DROWNED_DEATH_WATER : SoundEffects.ENTITY_DROWNED_DEATH; + } + + protected SoundEffect dA() { + return SoundEffects.ENTITY_DROWNED_STEP; + } + + protected SoundEffect ad() { + return SoundEffects.ENTITY_DROWNED_SWIM; + } + + protected ItemStack dB() { + return ItemStack.a; + } + + protected void a(DifficultyDamageScaler difficultydamagescaler) { + if ((double) this.random.nextFloat() > 0.9D) { + int i = this.random.nextInt(16); + + if (i < 10) { + this.setSlot(EnumItemSlot.MAINHAND, new ItemStack(Items.TRIDENT)); + } else { + this.setSlot(EnumItemSlot.MAINHAND, new ItemStack(Items.FISHING_ROD)); + } + } + + } + + protected boolean a(ItemStack itemstack, ItemStack itemstack1, EnumItemSlot enumitemslot) { + return itemstack1.getItem() == Items.NAUTILUS_SHELL ? false : (itemstack1.getItem() == Items.TRIDENT ? (itemstack.getItem() == Items.TRIDENT ? itemstack.getDamage() < itemstack1.getDamage() : false) : (itemstack.getItem() == Items.TRIDENT ? true : super.a(itemstack, itemstack1, enumitemslot))); + } + + protected boolean dC() { + return false; + } + + public boolean a(IWorldReader iworldreader) { + return iworldreader.a_(this, this.getBoundingBox()) && iworldreader.getCubes(this, this.getBoundingBox()); + } + + public boolean f(@Nullable EntityLiving entityliving) { + return entityliving != null ? !this.world.L() || entityliving.isInWater() : false; + } + + public boolean bw() { + return !this.isSwimming(); + } + + private boolean dI() { + if (this.bC) { + return true; + } else { + EntityLiving entityliving = this.getGoalTarget(); + + return entityliving != null && entityliving.isInWater(); + } + } + + public void a(float f, float f1, float f2) { + if (this.cP() && this.isInWater() && this.dI()) { + this.a(f, f1, f2, 0.01F); + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + this.motX *= 0.8999999761581421D; + this.motY *= 0.8999999761581421D; + this.motZ *= 0.8999999761581421D; + } else { + super.a(f, f1, f2); + } + + } + + public void as() { + if (!this.world.isClientSide) { + if (this.cP() && this.isInWater() && this.dI()) { + this.navigation = this.a; + this.setSwimming(true); + } else { + this.navigation = this.b; + this.setSwimming(false); + } + } + + } + + protected boolean dD() { + PathEntity pathentity = this.getNavigation().m(); + + if (pathentity != null) { + PathPoint pathpoint = pathentity.i(); + + if (pathpoint != null) { + double d0 = this.d((double) pathpoint.a, (double) pathpoint.b, (double) pathpoint.c); + + if (d0 < 4.0D) { + return true; + } + } + } + + return false; + } + + public void a(EntityLiving entityliving, float f) { + EntityThrownTrident entitythrowntrident = new EntityThrownTrident(this.world, this, new ItemStack(Items.TRIDENT)); + double d0 = entityliving.locX - this.locX; + double d1 = entityliving.getBoundingBox().minY + (double) (entityliving.length / 3.0F) - entitythrowntrident.locY; + double d2 = entityliving.locZ - this.locZ; + double d3 = (double) MathHelper.sqrt(d0 * d0 + d2 * d2); + + entitythrowntrident.shoot(d0, d1 + d3 * 0.20000000298023224D, d2, 1.6F, (float) (14 - this.world.getDifficulty().a() * 4)); + this.a(SoundEffects.ENTITY_DROWNED_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F)); + this.world.addEntity(entitythrowntrident); + } + + public void a(boolean flag) { + this.bC = flag; + } + + static class b implements Predicate { + + private final EntityDrowned a; + + public b(EntityDrowned entitydrowned) { + this.a = entitydrowned; + } + + public boolean test(@Nullable EntityHuman entityhuman) { + return this.a.f((EntityLiving) entityhuman); + } + } + + static class e extends ControllerMove { + + private final EntityDrowned i; + + public e(EntityDrowned entitydrowned) { + super(entitydrowned); + this.i = entitydrowned; + } + + public void a() { + EntityLiving entityliving = this.i.getGoalTarget(); + + if (this.i.dI() && this.i.isInWater()) { + if (entityliving != null && entityliving.locY > this.i.locY || this.i.bC) { + this.i.motY += 0.002D; + } + + if (this.h != ControllerMove.Operation.MOVE_TO || this.i.getNavigation().p()) { + this.i.o(0.0F); + return; + } + + double d0 = this.b - this.i.locX; + double d1 = this.c - this.i.locY; + double d2 = this.d - this.i.locZ; + double d3 = (double) MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + + d1 /= d3; + float f = (float) (MathHelper.c(d2, d0) * 57.2957763671875D) - 90.0F; + + this.i.yaw = this.a(this.i.yaw, f, 90.0F); + this.i.aQ = this.i.yaw; + float f1 = (float) (this.e * this.i.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue()); + + this.i.o(this.i.cK() + (f1 - this.i.cK()) * 0.125F); + this.i.motY += (double) this.i.cK() * d1 * 0.1D; + this.i.motX += (double) this.i.cK() * d0 * 0.005D; + this.i.motZ += (double) this.i.cK() * d2 * 0.005D; + } else { + if (!this.i.onGround) { + this.i.motY -= 0.008D; + } + + super.a(); + } + + } + } + + static class a extends PathfinderGoalZombieAttack { + + private final EntityDrowned d; + + public a(EntityDrowned entitydrowned, double d0, boolean flag) { + super((EntityZombie) entitydrowned, d0, flag); + this.d = entitydrowned; + } + + public boolean a() { + return super.a() && this.d.f(this.d.getGoalTarget()); + } + + public boolean b() { + return super.b() && this.d.f(this.d.getGoalTarget()); + } + } + + static class d extends PathfinderGoal { + + private final EntityCreature a; + private double b; + private double c; + private double d; + private final double e; + private final World f; + + public d(EntityCreature entitycreature, double d0) { + this.a = entitycreature; + this.e = d0; + this.f = entitycreature.world; + this.a(1); + } + + public boolean a() { + if (!this.f.L()) { + return false; + } else if (this.a.isInWater()) { + return false; + } else { + Vec3D vec3d = this.g(); + + if (vec3d == null) { + return false; + } else { + this.b = vec3d.x; + this.c = vec3d.y; + this.d = vec3d.z; + return true; + } + } + } + + public boolean b() { + return !this.a.getNavigation().p(); + } + + public void c() { + this.a.getNavigation().a(this.b, this.c, this.d, this.e); + } + + @Nullable + private Vec3D g() { + Random random = this.a.getRandom(); + BlockPosition blockposition = new BlockPosition(this.a.locX, this.a.getBoundingBox().minY, this.a.locZ); + + for (int i = 0; i < 10; ++i) { + BlockPosition blockposition1 = blockposition.a(random.nextInt(20) - 10, 2 - random.nextInt(8), random.nextInt(20) - 10); + + if (this.f.getType(blockposition1).getBlock() == Blocks.WATER) { + return new Vec3D((double) blockposition1.getX(), (double) blockposition1.getY(), (double) blockposition1.getZ()); + } + } + + return null; + } + } + + static class c extends PathfinderGoalGotoTarget { + + private final EntityDrowned f; + + public c(EntityDrowned entitydrowned, double d0) { + super(entitydrowned, d0, 8, 2); + this.f = entitydrowned; + } + + public boolean a() { + return super.a() && !this.f.world.L() && this.f.isInWater() && this.f.locY >= (double) (this.f.world.getSeaLevel() - 3); + } + + public boolean b() { + return super.b(); + } + + protected boolean a(IWorldReader iworldreader, BlockPosition blockposition) { + BlockPosition blockposition1 = blockposition.up(); + + return iworldreader.isEmpty(blockposition1) && iworldreader.isEmpty(blockposition1.up()) ? iworldreader.getType(blockposition).q() : false; + } + + public void c() { + this.f.a(false); + this.f.navigation = this.f.b; + super.c(); + } + + public void d() { + super.d(); + } + } + + static class f extends PathfinderGoal { + + private final EntityDrowned a; + private final double b; + private final int c; + private boolean d; + + public f(EntityDrowned entitydrowned, double d0, int i) { + this.a = entitydrowned; + this.b = d0; + this.c = i; + } + + public boolean a() { + return !this.a.world.L() && this.a.isInWater() && this.a.locY < (double) (this.c - 2); + } + + public boolean b() { + return this.a() && !this.d; + } + + public void e() { + if (this.a.locY < (double) (this.c - 1) && (this.a.getNavigation().p() || this.a.dD())) { + Vec3D vec3d = RandomPositionGenerator.a(this.a, 4, 8, new Vec3D(this.a.locX, (double) (this.c - 1), this.a.locZ)); + + if (vec3d == null) { + this.d = true; + return; + } + + this.a.getNavigation().a(vec3d.x, vec3d.y, vec3d.z, this.b); + } + + } + + public void c() { + this.a.a(true); + this.d = false; + } + + public void d() { + this.a.a(false); + } + } + + static class g extends PathfinderGoalArrowAttack { + + private final EntityDrowned a; + + public g(IRangedEntity irangedentity, double d0, int i, float f) { + super(irangedentity, d0, i, f); + this.a = (EntityDrowned) irangedentity; + } + + public boolean a() { + return super.a() && this.a.getItemInMainHand().getItem() == Items.TRIDENT; + } + + public void c() { + super.c(); + this.a.s(true); + } + + public void d() { + super.d(); + this.a.s(false); + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityEgg.java b/src/main/java/net/minecraft/server/EntityEgg.java new file mode 100644 index 000000000000..174b87971f21 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityEgg.java @@ -0,0 +1,71 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.entity.Ageable; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerEggThrowEvent; +// CraftBukkit end + +public class EntityEgg extends EntityProjectile { + + public EntityEgg(World world) { + super(EntityTypes.EGG, world); + } + + public EntityEgg(World world, EntityLiving entityliving) { + super(EntityTypes.EGG, entityliving, world); + } + + public EntityEgg(World world, double d0, double d1, double d2) { + super(EntityTypes.EGG, d0, d1, d2, world); + } + + protected void a(MovingObjectPosition movingobjectposition) { + if (movingobjectposition.entity != null) { + movingobjectposition.entity.damageEntity(DamageSource.projectile(this, this.getShooter()), 0.0F); + } + + if (!this.world.isClientSide) { + boolean hatching = this.random.nextInt(8) == 0; // CraftBukkit + if (true) { + byte b0 = 1; + + if (this.random.nextInt(32) == 0) { + b0 = 4; + } + + // CraftBukkit start + if (!hatching) { + b0 = 0; + } + EntityType hatchingType = EntityType.CHICKEN; + + Entity shooter = this.getShooter(); + if (shooter instanceof EntityPlayer) { + PlayerEggThrowEvent event = new PlayerEggThrowEvent((Player) shooter.getBukkitEntity(), (org.bukkit.entity.Egg) this.getBukkitEntity(), hatching, b0, hatchingType); + this.world.getServer().getPluginManager().callEvent(event); + + b0 = event.getNumHatches(); + hatching = event.isHatching(); + hatchingType = event.getHatchingType(); + } + + if (hatching) { + for (int i = 0; i < b0; ++i) { + Entity entity = world.getWorld().createEntity(new org.bukkit.Location(world.getWorld(), this.locX, this.locY, this.locZ, this.yaw, 0.0F), hatchingType.getEntityClass()); + if (entity.getBukkitEntity() instanceof Ageable) { + ((Ageable) entity.getBukkitEntity()).setBaby(); + } + world.getWorld().addEntity(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.EGG); + } + } + // CraftBukkit end + } + + this.world.broadcastEntityEffect(this, (byte) 3); + this.die(); + } + + } +} diff --git a/src/main/java/net/minecraft/server/EntityEnderCrystal.java b/src/main/java/net/minecraft/server/EntityEnderCrystal.java new file mode 100644 index 000000000000..adf38a7f8b3e --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityEnderCrystal.java @@ -0,0 +1,147 @@ +package net.minecraft.server; + +import java.util.Optional; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.ExplosionPrimeEvent; +// CraftBukkit end + +public class EntityEnderCrystal extends Entity { + + private static final DataWatcherObject> b = DataWatcher.a(EntityEnderCrystal.class, DataWatcherRegistry.m); + private static final DataWatcherObject c = DataWatcher.a(EntityEnderCrystal.class, DataWatcherRegistry.i); + public int a; + + public EntityEnderCrystal(World world) { + super(EntityTypes.END_CRYSTAL, world); + this.j = true; + this.setSize(2.0F, 2.0F); + this.a = this.random.nextInt(100000); + } + + public EntityEnderCrystal(World world, double d0, double d1, double d2) { + this(world); + this.setPosition(d0, d1, d2); + } + + protected boolean playStepSound() { + return false; + } + + protected void x_() { + this.getDataWatcher().register(EntityEnderCrystal.b, Optional.empty()); + this.getDataWatcher().register(EntityEnderCrystal.c, true); + } + + public void tick() { + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + ++this.a; + if (!this.world.isClientSide) { + BlockPosition blockposition = new BlockPosition(this); + + if (this.world.worldProvider instanceof WorldProviderTheEnd && this.world.getType(blockposition).isAir()) { + // CraftBukkit start + if (!CraftEventFactory.callBlockIgniteEvent(this.world, blockposition, this).isCancelled()) { + this.world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); + } + // CraftBukkit end + } + } + + } + + protected void b(NBTTagCompound nbttagcompound) { + if (this.getBeamTarget() != null) { + nbttagcompound.set("BeamTarget", GameProfileSerializer.a(this.getBeamTarget())); + } + + nbttagcompound.setBoolean("ShowBottom", this.isShowingBottom()); + } + + protected void a(NBTTagCompound nbttagcompound) { + if (nbttagcompound.hasKeyOfType("BeamTarget", 10)) { + this.setBeamTarget(GameProfileSerializer.c(nbttagcompound.getCompound("BeamTarget"))); + } + + if (nbttagcompound.hasKeyOfType("ShowBottom", 1)) { + this.setShowingBottom(nbttagcompound.getBoolean("ShowBottom")); + } + + } + + public boolean isInteractable() { + return true; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else if (damagesource.getEntity() instanceof EntityEnderDragon) { + return false; + } else { + if (!this.dead && !this.world.isClientSide) { + // CraftBukkit start - All non-living entities need this + if (CraftEventFactory.handleNonLivingEntityDamageEvent(this, damagesource, f)) { + return false; + } + // CraftBukkit end + this.die(); + if (!this.world.isClientSide) { + if (!damagesource.isExplosion()) { + // CraftBukkit start + ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), 6.0F, true); + this.world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + this.dead = false; + return false; + } + this.world.explode(this, this.locX, this.locY, this.locZ, event.getRadius(), event.getFire()); + // CraftBukkit end + } + + this.a(damagesource); + } + } + + return true; + } + } + + public void killEntity() { + this.a(DamageSource.GENERIC); + super.killEntity(); + } + + private void a(DamageSource damagesource) { + if (this.world.worldProvider instanceof WorldProviderTheEnd) { + WorldProviderTheEnd worldprovidertheend = (WorldProviderTheEnd) this.world.worldProvider; + EnderDragonBattle enderdragonbattle = worldprovidertheend.r(); + + if (enderdragonbattle != null) { + enderdragonbattle.a(this, damagesource); + } + } + + } + + public void setBeamTarget(@Nullable BlockPosition blockposition) { + this.getDataWatcher().set(EntityEnderCrystal.b, Optional.ofNullable(blockposition)); + } + + @Nullable + public BlockPosition getBeamTarget() { + return (BlockPosition) ((Optional) this.getDataWatcher().get(EntityEnderCrystal.b)).orElse((Object) null); + } + + public void setShowingBottom(boolean flag) { + this.getDataWatcher().set(EntityEnderCrystal.c, flag); + } + + public boolean isShowingBottom() { + return (Boolean) this.getDataWatcher().get(EntityEnderCrystal.c); + } +} diff --git a/src/main/java/net/minecraft/server/EntityEnderDragon.java b/src/main/java/net/minecraft/server/EntityEnderDragon.java new file mode 100644 index 000000000000..fcc82d8eb53f --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityEnderDragon.java @@ -0,0 +1,967 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +// CraftBukkit start +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.EntityRegainHealthEvent; +// CraftBukkit end +import com.destroystokyo.paper.event.block.TNTPrimeEvent; // Paper - TNTPrimeEvent + +// PAIL: Fixme +public class EntityEnderDragon extends EntityInsentient implements IComplex, IMonster { + + private static final Logger bQ = LogManager.getLogger(); + public static final DataWatcherObject PHASE = DataWatcher.a(EntityEnderDragon.class, DataWatcherRegistry.b); + public double[][] b = new double[64][3]; + public int c = -1; + public EntityComplexPart[] children; + public EntityComplexPart bD = new EntityComplexPart(this, "head", 6.0F, 6.0F); + public EntityComplexPart bE = new EntityComplexPart(this, "neck", 6.0F, 6.0F); + public EntityComplexPart bF = new EntityComplexPart(this, "body", 8.0F, 8.0F); + public EntityComplexPart bG = new EntityComplexPart(this, "tail", 4.0F, 4.0F); + public EntityComplexPart bH = new EntityComplexPart(this, "tail", 4.0F, 4.0F); + public EntityComplexPart bI = new EntityComplexPart(this, "tail", 4.0F, 4.0F); + public EntityComplexPart bJ = new EntityComplexPart(this, "wing", 4.0F, 4.0F); + public EntityComplexPart bK = new EntityComplexPart(this, "wing", 4.0F, 4.0F); + public float bL; + public float bM; + public boolean bN; + public int bO; + public EntityEnderCrystal currentEnderCrystal; + private final EnderDragonBattle bR; + private final DragonControllerManager bS; + private int bT = 100; + private int bU; + private final PathPoint[] bV = new PathPoint[24]; + private final int[] bW = new int[24]; + private final Path bX = new Path(); + private Explosion explosionSource = new Explosion(null, this, Double.NaN, Double.NaN, Double.NaN, Float.NaN, true, true); // CraftBukkit - reusable source for CraftTNTPrimed.getSource() + + public EntityEnderDragon(World world) { + super(EntityTypes.ENDER_DRAGON, world); + this.children = new EntityComplexPart[] { this.bD, this.bE, this.bF, this.bG, this.bH, this.bI, this.bJ, this.bK}; + this.setHealth(this.getMaxHealth()); + this.setSize(16.0F, 8.0F); + this.noclip = true; + this.fireProof = true; + this.ak = true; + if (!world.isClientSide && world.worldProvider instanceof WorldProviderTheEnd) { + this.bR = ((WorldProviderTheEnd) world.worldProvider).r(); + } else { + this.bR = null; + } + + this.bS = new DragonControllerManager(this); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(200.0D); + } + + protected void x_() { + super.x_(); + this.getDataWatcher().register(EntityEnderDragon.PHASE, DragonControllerPhase.HOVER.b()); + } + + public double[] a(int i, float f) { + if (this.getHealth() <= 0.0F) { + f = 0.0F; + } + + f = 1.0F - f; + int j = this.c - i & 63; + int k = this.c - i - 1 & 63; + double[] adouble = new double[3]; + double d0 = this.b[j][0]; + double d1 = MathHelper.g(this.b[k][0] - d0); + + adouble[0] = d0 + d1 * (double) f; + d0 = this.b[j][1]; + d1 = this.b[k][1] - d0; + adouble[1] = d0 + d1 * (double) f; + adouble[2] = this.b[j][2] + (this.b[k][2] - this.b[j][2]) * (double) f; + return adouble; + } + + public void movementTick() { + float f; + float f1; + + if (this.world.isClientSide) { + this.setHealth(this.getHealth()); + if (!this.isSilent()) { + f = MathHelper.cos(this.bM * 6.2831855F); + f1 = MathHelper.cos(this.bL * 6.2831855F); + if (f1 <= -0.3F && f >= -0.3F) { + this.world.a(this.locX, this.locY, this.locZ, SoundEffects.ENTITY_ENDER_DRAGON_FLAP, this.bV(), 5.0F, 0.8F + this.random.nextFloat() * 0.3F, false); + } + + if (!this.bS.a().a() && --this.bT < 0) { + this.world.a(this.locX, this.locY, this.locZ, SoundEffects.ENTITY_ENDER_DRAGON_GROWL, this.bV(), 2.5F, 0.8F + this.random.nextFloat() * 0.3F, false); + this.bT = 200 + this.random.nextInt(200); + } + } + } + + this.bL = this.bM; + float f2; + + if (this.getHealth() <= 0.0F) { + f = (this.random.nextFloat() - 0.5F) * 8.0F; + f1 = (this.random.nextFloat() - 0.5F) * 4.0F; + f2 = (this.random.nextFloat() - 0.5F) * 8.0F; + this.world.addParticle(Particles.u, this.locX + (double) f, this.locY + 2.0D + (double) f1, this.locZ + (double) f2, 0.0D, 0.0D, 0.0D); + } else { + this.dt(); + f = 0.2F / (MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ) * 10.0F + 1.0F); + f *= (float) Math.pow(2.0D, this.motY); + if (this.bS.a().a()) { + this.bM += 0.1F; + } else if (this.bN) { + this.bM += f * 0.5F; + } else { + this.bM += f; + } + + this.yaw = MathHelper.g(this.yaw); + if (this.isNoAI()) { + this.bM = 0.5F; + } else { + if (this.c < 0) { + for (int i = 0; i < this.b.length; ++i) { + this.b[i][0] = (double) this.yaw; + this.b[i][1] = this.locY; + } + } + + if (++this.c == this.b.length) { + this.c = 0; + } + + this.b[this.c][0] = (double) this.yaw; + this.b[this.c][1] = this.locY; + double d0; + double d1; + double d2; + float f3; + float f4; + + if (this.world.isClientSide) { + if (this.bl > 0) { + double d3 = this.locX + (this.bm - this.locX) / (double) this.bl; + + d0 = this.locY + (this.bn - this.locY) / (double) this.bl; + d1 = this.locZ + (this.bo - this.locZ) / (double) this.bl; + d2 = MathHelper.g(this.bp - (double) this.yaw); + this.yaw = (float) ((double) this.yaw + d2 / (double) this.bl); + this.pitch = (float) ((double) this.pitch + (this.bq - (double) this.pitch) / (double) this.bl); + --this.bl; + this.setPosition(d3, d0, d1); + this.setYawPitch(this.yaw, this.pitch); + } + + this.bS.a().b(); + } else { + IDragonController idragoncontroller = this.bS.a(); + + idragoncontroller.c(); + if (this.bS.a() != idragoncontroller) { + idragoncontroller = this.bS.a(); + idragoncontroller.c(); + } + + Vec3D vec3d = idragoncontroller.g(); + + if (vec3d != null && idragoncontroller.getControllerPhase() != DragonControllerPhase.HOVER) { // CraftBukkit - Don't move when hovering + d0 = vec3d.x - this.locX; + d1 = vec3d.y - this.locY; + d2 = vec3d.z - this.locZ; + double d4 = d0 * d0 + d1 * d1 + d2 * d2; + + f3 = idragoncontroller.f(); + d1 = MathHelper.a(d1 / (double) MathHelper.sqrt(d0 * d0 + d2 * d2), (double) (-f3), (double) f3); + this.motY += d1 * 0.10000000149011612D; + this.yaw = MathHelper.g(this.yaw); + double d5 = MathHelper.a(MathHelper.g(180.0D - MathHelper.c(d0, d2) * 57.2957763671875D - (double) this.yaw), -50.0D, 50.0D); + Vec3D vec3d1 = (new Vec3D(vec3d.x - this.locX, vec3d.y - this.locY, vec3d.z - this.locZ)).a(); + Vec3D vec3d2 = (new Vec3D((double) MathHelper.sin(this.yaw * 0.017453292F), this.motY, (double) (-MathHelper.cos(this.yaw * 0.017453292F)))).a(); + + f4 = Math.max(((float) vec3d2.b(vec3d1) + 0.5F) / 1.5F, 0.0F); + this.bk *= 0.8F; + this.bk = (float) ((double) this.bk + d5 * (double) idragoncontroller.h()); + this.yaw += this.bk * 0.1F; + float f5 = (float) (2.0D / (d4 + 1.0D)); + float f6 = 0.06F; + + this.a(0.0F, 0.0F, -1.0F, 0.06F * (f4 * f5 + (1.0F - f5))); + if (this.bN) { + this.move(EnumMoveType.SELF, this.motX * 0.800000011920929D, this.motY * 0.800000011920929D, this.motZ * 0.800000011920929D); + } else { + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + } + + Vec3D vec3d3 = (new Vec3D(this.motX, this.motY, this.motZ)).a(); + float f7 = ((float) vec3d3.b(vec3d2) + 1.0F) / 2.0F; + + f7 = 0.8F + 0.15F * f7; + this.motX *= (double) f7; + this.motZ *= (double) f7; + this.motY *= 0.9100000262260437D; + } + } + + this.aQ = this.yaw; + this.bD.width = 1.0F; + this.bD.length = 1.0F; + this.bE.width = 3.0F; + this.bE.length = 3.0F; + this.bG.width = 2.0F; + this.bG.length = 2.0F; + this.bH.width = 2.0F; + this.bH.length = 2.0F; + this.bI.width = 2.0F; + this.bI.length = 2.0F; + this.bF.length = 3.0F; + this.bF.width = 5.0F; + this.bJ.length = 2.0F; + this.bJ.width = 4.0F; + this.bK.length = 3.0F; + this.bK.width = 4.0F; + Vec3D[] avec3d = new Vec3D[this.children.length]; + + for (int j = 0; j < this.children.length; ++j) { + avec3d[j] = new Vec3D(this.children[j].locX, this.children[j].locY, this.children[j].locZ); + } + + f2 = (float) (this.a(5, 1.0F)[1] - this.a(10, 1.0F)[1]) * 10.0F * 0.017453292F; + float f8 = MathHelper.cos(f2); + float f9 = MathHelper.sin(f2); + float f10 = this.yaw * 0.017453292F; + float f11 = MathHelper.sin(f10); + float f12 = MathHelper.cos(f10); + + this.bF.tick(); + this.bF.setPositionRotation(this.locX + (double) (f11 * 0.5F), this.locY, this.locZ - (double) (f12 * 0.5F), 0.0F, 0.0F); + this.bJ.tick(); + this.bJ.setPositionRotation(this.locX + (double) (f12 * 4.5F), this.locY + 2.0D, this.locZ + (double) (f11 * 4.5F), 0.0F, 0.0F); + this.bK.tick(); + this.bK.setPositionRotation(this.locX - (double) (f12 * 4.5F), this.locY + 2.0D, this.locZ - (double) (f11 * 4.5F), 0.0F, 0.0F); + if (!this.world.isClientSide && this.hurtTicks == 0) { + this.a(this.world.getEntities(this, this.bJ.getBoundingBox().grow(4.0D, 2.0D, 4.0D).d(0.0D, -2.0D, 0.0D))); + this.a(this.world.getEntities(this, this.bK.getBoundingBox().grow(4.0D, 2.0D, 4.0D).d(0.0D, -2.0D, 0.0D))); + this.b(this.world.getEntities(this, this.bD.getBoundingBox().g(1.0D))); + this.b(this.world.getEntities(this, this.bE.getBoundingBox().g(1.0D))); + } + + double[] adouble = this.a(5, 1.0F); + float f13 = MathHelper.sin(this.yaw * 0.017453292F - this.bk * 0.01F); + float f14 = MathHelper.cos(this.yaw * 0.017453292F - this.bk * 0.01F); + + this.bD.tick(); + this.bE.tick(); + f3 = this.u(1.0F); + this.bD.setPositionRotation(this.locX + (double) (f13 * 6.5F * f8), this.locY + (double) f3 + (double) (f9 * 6.5F), this.locZ - (double) (f14 * 6.5F * f8), 0.0F, 0.0F); + this.bE.setPositionRotation(this.locX + (double) (f13 * 5.5F * f8), this.locY + (double) f3 + (double) (f9 * 5.5F), this.locZ - (double) (f14 * 5.5F * f8), 0.0F, 0.0F); + + int k; + + for (k = 0; k < 3; ++k) { + EntityComplexPart entitycomplexpart = null; + + if (k == 0) { + entitycomplexpart = this.bG; + } + + if (k == 1) { + entitycomplexpart = this.bH; + } + + if (k == 2) { + entitycomplexpart = this.bI; + } + + double[] adouble1 = this.a(12 + k * 2, 1.0F); + float f15 = this.yaw * 0.017453292F + this.c(adouble1[0] - adouble[0]) * 0.017453292F; + float f16 = MathHelper.sin(f15); + float f17 = MathHelper.cos(f15); + float f18 = 1.5F; + + f4 = (float) (k + 1) * 2.0F; + entitycomplexpart.tick(); + entitycomplexpart.setPositionRotation(this.locX - (double) ((f11 * 1.5F + f16 * f4) * f8), this.locY + (adouble1[1] - adouble[1]) - (double) ((f4 + 1.5F) * f9) + 1.5D, this.locZ + (double) ((f12 * 1.5F + f17 * f4) * f8), 0.0F, 0.0F); + } + + if (!this.world.isClientSide) { + this.bN = this.b(this.bD.getBoundingBox()) | this.b(this.bE.getBoundingBox()) | this.b(this.bF.getBoundingBox()); + if (this.bR != null) { + this.bR.b(this); + } + } + + for (k = 0; k < this.children.length; ++k) { + this.children[k].lastX = avec3d[k].x; + this.children[k].lastY = avec3d[k].y; + this.children[k].lastZ = avec3d[k].z; + } + + } + } + } + + private float u(float f) { + double d0; + + if (this.bS.a().a()) { + d0 = -1.0D; + } else { + double[] adouble = this.a(5, 1.0F); + double[] adouble1 = this.a(0, 1.0F); + + d0 = adouble[1] - adouble1[1]; + } + + return (float) d0; + } + + private void dt() { + if (this.currentEnderCrystal != null) { + if (this.currentEnderCrystal.dead) { + this.currentEnderCrystal = null; + } else if (this.ticksLived % 10 == 0 && this.getHealth() < this.getMaxHealth()) { + // CraftBukkit start + EntityRegainHealthEvent event = new EntityRegainHealthEvent(this.getBukkitEntity(), 1.0F, EntityRegainHealthEvent.RegainReason.ENDER_CRYSTAL); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + this.setHealth((float) (this.getHealth() + event.getAmount())); + } + // CraftBukkit end + } + } + + if (this.random.nextInt(10) == 0) { + List list = this.world.a(EntityEnderCrystal.class, this.getBoundingBox().g(32.0D)); + EntityEnderCrystal entityendercrystal = null; + double d0 = Double.MAX_VALUE; + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityEnderCrystal entityendercrystal1 = (EntityEnderCrystal) iterator.next(); + double d1 = entityendercrystal1.h(this); + + if (d1 < d0) { + d0 = d1; + entityendercrystal = entityendercrystal1; + } + } + + this.currentEnderCrystal = entityendercrystal; + } + + } + + private void a(List list) { + double d0 = (this.bF.getBoundingBox().minX + this.bF.getBoundingBox().maxX) / 2.0D; + double d1 = (this.bF.getBoundingBox().minZ + this.bF.getBoundingBox().maxZ) / 2.0D; + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + if (entity instanceof EntityLiving) { + double d2 = entity.locX - d0; + double d3 = entity.locZ - d1; + double d4 = d2 * d2 + d3 * d3; + + entity.f(d2 / d4 * 4.0D, 0.20000000298023224D, d3 / d4 * 4.0D); + if (!this.bS.a().a() && ((EntityLiving) entity).cg() < entity.ticksLived - 2) { + entity.damageEntity(DamageSource.mobAttack(this), 5.0F); + this.a((EntityLiving) this, entity); + } + } + } + + } + + private void b(List list) { + for (int i = 0; i < list.size(); ++i) { + Entity entity = (Entity) list.get(i); + + if (entity instanceof EntityLiving) { + entity.damageEntity(DamageSource.mobAttack(this), 10.0F); + this.a((EntityLiving) this, entity); + } + } + + } + + private float c(double d0) { + return (float) MathHelper.g(d0); + } + + private boolean b(AxisAlignedBB axisalignedbb) { + int i = MathHelper.floor(axisalignedbb.minX); + int j = MathHelper.floor(axisalignedbb.minY); + int k = MathHelper.floor(axisalignedbb.minZ); + int l = MathHelper.floor(axisalignedbb.maxX); + int i1 = MathHelper.floor(axisalignedbb.maxY); + int j1 = MathHelper.floor(axisalignedbb.maxZ); + boolean flag = false; + boolean flag1 = false; + // CraftBukkit start - Create a list to hold all the destroyed blocks + List destroyedBlocks = new java.util.ArrayList(); + org.bukkit.craftbukkit.CraftWorld craftWorld = this.world.getWorld(); + // CraftBukkit end + + for (int k1 = i; k1 <= l; ++k1) { + for (int l1 = j; l1 <= i1; ++l1) { + for (int i2 = k; i2 <= j1; ++i2) { + BlockPosition blockposition = new BlockPosition(k1, l1, i2); + IBlockData iblockdata = this.world.getType(blockposition); + Block block = iblockdata.getBlock(); + + if (!iblockdata.isAir() && iblockdata.getMaterial() != Material.FIRE) { + if (!this.world.getGameRules().getBoolean("mobGriefing")) { + flag = true; + } else if (block != Blocks.BARRIER && block != Blocks.OBSIDIAN && block != Blocks.END_STONE && block != Blocks.BEDROCK && block != Blocks.END_PORTAL && block != Blocks.END_PORTAL_FRAME) { + if (block != Blocks.COMMAND_BLOCK && block != Blocks.REPEATING_COMMAND_BLOCK && block != Blocks.CHAIN_COMMAND_BLOCK && block != Blocks.IRON_BARS && block != Blocks.END_GATEWAY) { + // CraftBukkit start - Add blocks to list rather than destroying them + // flag1 = this.world.setAir(blockposition) || flag1; + flag1 = true; + destroyedBlocks.add(craftWorld.getBlockAt(k1, l1, i2)); + // CraftBukkit end + } else { + flag = true; + } + } else { + flag = true; + } + } + } + } + } + + // CraftBukkit start - Set off an EntityExplodeEvent for the dragon exploding all these blocks + org.bukkit.entity.Entity bukkitEntity = this.getBukkitEntity(); + EntityExplodeEvent event = new EntityExplodeEvent(bukkitEntity, bukkitEntity.getLocation(), destroyedBlocks, 0F); + bukkitEntity.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + // This flag literally means 'Dragon hit something hard' (Obsidian, White Stone or Bedrock) and will cause the dragon to slow down. + // We should consider adding an event extension for it, or perhaps returning true if the event is cancelled. + return flag; + } else if (event.getYield() == 0F) { + // Yield zero ==> no drops + for (org.bukkit.block.Block block : event.blockList()) { + this.world.setAir(new BlockPosition(block.getX(), block.getY(), block.getZ())); + } + } else { + for (org.bukkit.block.Block block : event.blockList()) { + org.bukkit.Material blockId = block.getType(); + if (blockId == org.bukkit.Material.AIR) { + continue; + } + + int blockX = block.getX(); + int blockY = block.getY(); + int blockZ = block.getZ(); + + Block nmsBlock = org.bukkit.craftbukkit.util.CraftMagicNumbers.getBlock(blockId); + if (nmsBlock.a(explosionSource)) { + BlockPosition pos = new BlockPosition(blockX, blockY, blockZ); + nmsBlock.dropNaturally(world.getType(pos), world, pos, event.getYield(), 0); + } + // Paper start - TNTPrimeEvent + org.bukkit.block.Block tntBlock = world.getWorld().getBlockAt(blockX, blockY, blockZ); + if(!new TNTPrimeEvent(tntBlock, TNTPrimeEvent.PrimeReason.EXPLOSION, explosionSource.getSource().bukkitEntity).callEvent()) + continue; + // Paper end + nmsBlock.wasExploded(world, new BlockPosition(blockX, blockY, blockZ), explosionSource); + + this.world.setAir(new BlockPosition(blockX, blockY, blockZ)); + } + } + // CraftBukkit end + + if (flag1) { + double d0 = axisalignedbb.minX + (axisalignedbb.maxX - axisalignedbb.minX) * (double) this.random.nextFloat(); + double d1 = axisalignedbb.minY + (axisalignedbb.maxY - axisalignedbb.minY) * (double) this.random.nextFloat(); + double d2 = axisalignedbb.minZ + (axisalignedbb.maxZ - axisalignedbb.minZ) * (double) this.random.nextFloat(); + + this.world.addParticle(Particles.u, d0, d1, d2, 0.0D, 0.0D, 0.0D); + } + + return flag; + } + + public boolean a(EntityComplexPart entitycomplexpart, DamageSource damagesource, float f) { + f = this.bS.a().a(entitycomplexpart, damagesource, f); + if (entitycomplexpart != this.bD) { + f = f / 4.0F + Math.min(f, 1.0F); + } + + if (f < 0.01F) { + return false; + } else { + if (damagesource.getEntity() instanceof EntityHuman || damagesource.isExplosion()) { + float f1 = this.getHealth(); + + this.dealDamage(damagesource, f); + if (this.getHealth() <= 0.0F && !this.bS.a().a()) { + this.setHealth(1.0F); + this.bS.setControllerPhase(DragonControllerPhase.DYING); + } + + if (this.bS.a().a()) { + this.bU = (int) ((float) this.bU + (f1 - this.getHealth())); + if ((float) this.bU > 0.25F * this.getMaxHealth()) { + this.bU = 0; + this.bS.setControllerPhase(DragonControllerPhase.TAKEOFF); + } + } + } + + return true; + } + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (damagesource instanceof EntityDamageSource && ((EntityDamageSource) damagesource).y()) { + this.a(this.bF, damagesource, f); + } + + return false; + } + + protected boolean dealDamage(DamageSource damagesource, float f) { + return super.damageEntity(damagesource, f); + } + + public void killEntity() { + this.die(); + if (this.bR != null) { + this.bR.b(this); + this.bR.a(this); + } + + } + + protected void cb() { + if (this.bR != null) { + this.bR.b(this); + } + + ++this.bO; + if (this.bO >= 180 && this.bO <= 200) { + float f = (this.random.nextFloat() - 0.5F) * 8.0F; + float f1 = (this.random.nextFloat() - 0.5F) * 4.0F; + float f2 = (this.random.nextFloat() - 0.5F) * 8.0F; + + this.world.addParticle(Particles.t, this.locX + (double) f, this.locY + 2.0D + (double) f1, this.locZ + (double) f2, 0.0D, 0.0D, 0.0D); + } + + boolean flag = this.world.getGameRules().getBoolean("doMobLoot"); + short short0 = 500; + + if (this.bR != null && !this.bR.d()) { + short0 = 12000; + } + + if (!this.world.isClientSide) { + if (this.bO > 150 && this.bO % 5 == 0 && flag) { + this.a(MathHelper.d((float) short0 * 0.08F)); + } + + if (this.bO == 1) { + // CraftBukkit start - Use relative location for far away sounds + // this.world.a(1028, new BlockPosition(this), 0); + // Paper start + //int viewDistance = ((WorldServer) this.world).spigotConfig.viewDistance * 16; // Paper - updated to use worlds actual view distance incase we have to uncomment this due to removal of player view distance API + for (EntityHuman human : world.players) { + EntityPlayer player = (EntityPlayer) human; + int viewDistance = player.getViewDistance(); + // Paper end + double deltaX = this.locX - player.locX; + double deltaZ = this.locZ - player.locZ; + double distanceSquared = deltaX * deltaX + deltaZ * deltaZ; + if ( world.spigotConfig.dragonDeathSoundRadius > 0 && distanceSquared > world.spigotConfig.dragonDeathSoundRadius * world.spigotConfig.dragonDeathSoundRadius ) continue; // Spigot + if (distanceSquared > viewDistance * viewDistance) { + double deltaLength = Math.sqrt(distanceSquared); + double relativeX = player.locX + (deltaX / deltaLength) * viewDistance; + double relativeZ = player.locZ + (deltaZ / deltaLength) * viewDistance; + player.playerConnection.sendPacket(new PacketPlayOutWorldEvent(1028, new BlockPosition((int) relativeX, (int) this.locY, (int) relativeZ), 0, true)); + } else { + player.playerConnection.sendPacket(new PacketPlayOutWorldEvent(1028, new BlockPosition((int) this.locX, (int) this.locY, (int) this.locZ), 0, true)); + } + } + // CraftBukkit end + } + } + + this.move(EnumMoveType.SELF, 0.0D, 0.10000000149011612D, 0.0D); + this.yaw += 20.0F; + this.aQ = this.yaw; + if (this.bO == 200 && !this.world.isClientSide) { + if (flag) { + this.a(MathHelper.d((float) short0 * 0.2F)); + } + + if (this.bR != null) { + this.bR.a(this); + } + + this.die(); + } + + } + + private void a(int i) { + while (i > 0) { + int j = EntityExperienceOrb.getOrbValue(i); + + i -= j; + this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j, org.bukkit.entity.ExperienceOrb.SpawnReason.ENTITY_DEATH, this.killer, this)); // Paper + } + + } + + public int l() { + if (this.bV[0] == null) { + for (int i = 0; i < 24; ++i) { + int j = 5; + int k; + int l; + + if (i < 12) { + k = (int) (60.0F * MathHelper.cos(2.0F * (-3.1415927F + 0.2617994F * (float) i))); + l = (int) (60.0F * MathHelper.sin(2.0F * (-3.1415927F + 0.2617994F * (float) i))); + } else { + int i1; + + if (i < 20) { + i1 = i - 12; + k = (int) (40.0F * MathHelper.cos(2.0F * (-3.1415927F + 0.3926991F * (float) i1))); + l = (int) (40.0F * MathHelper.sin(2.0F * (-3.1415927F + 0.3926991F * (float) i1))); + j += 10; + } else { + i1 = i - 20; + k = (int) (20.0F * MathHelper.cos(2.0F * (-3.1415927F + 0.7853982F * (float) i1))); + l = (int) (20.0F * MathHelper.sin(2.0F * (-3.1415927F + 0.7853982F * (float) i1))); + } + } + + int j1 = Math.max(this.world.getSeaLevel() + 10, this.world.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, new BlockPosition(k, 0, l)).getY() + j); + + this.bV[i] = new PathPoint(k, j1, l); + } + + this.bW[0] = 6146; + this.bW[1] = 8197; + this.bW[2] = 8202; + this.bW[3] = 16404; + this.bW[4] = 32808; + this.bW[5] = 32848; + this.bW[6] = 65696; + this.bW[7] = 131392; + this.bW[8] = 131712; + this.bW[9] = 263424; + this.bW[10] = 526848; + this.bW[11] = 525313; + this.bW[12] = 1581057; + this.bW[13] = 3166214; + this.bW[14] = 2138120; + this.bW[15] = 6373424; + this.bW[16] = 4358208; + this.bW[17] = 12910976; + this.bW[18] = 9044480; + this.bW[19] = 9706496; + this.bW[20] = 15216640; + this.bW[21] = 13688832; + this.bW[22] = 11763712; + this.bW[23] = 8257536; + } + + return this.k(this.locX, this.locY, this.locZ); + } + + public int k(double d0, double d1, double d2) { + float f = 10000.0F; + int i = 0; + PathPoint pathpoint = new PathPoint(MathHelper.floor(d0), MathHelper.floor(d1), MathHelper.floor(d2)); + byte b0 = 0; + + if (this.bR == null || this.bR.c() == 0) { + b0 = 12; + } + + for (int j = b0; j < 24; ++j) { + if (this.bV[j] != null) { + float f1 = this.bV[j].b(pathpoint); + + if (f1 < f) { + f = f1; + i = j; + } + } + } + + return i; + } + + @Nullable + public PathEntity a(int i, int j, @Nullable PathPoint pathpoint) { + PathPoint pathpoint1; + + for (int k = 0; k < 24; ++k) { + pathpoint1 = this.bV[k]; + pathpoint1.i = false; + pathpoint1.g = 0.0F; + pathpoint1.e = 0.0F; + pathpoint1.f = 0.0F; + pathpoint1.h = null; + pathpoint1.d = -1; + } + + PathPoint pathpoint2 = this.bV[i]; + + pathpoint1 = this.bV[j]; + pathpoint2.e = 0.0F; + pathpoint2.f = pathpoint2.a(pathpoint1); + pathpoint2.g = pathpoint2.f; + this.bX.a(); + this.bX.a(pathpoint2); + PathPoint pathpoint3 = pathpoint2; + byte b0 = 0; + + if (this.bR == null || this.bR.c() == 0) { + b0 = 12; + } + + label70: + while (!this.bX.e()) { + PathPoint pathpoint4 = this.bX.c(); + + if (pathpoint4.equals(pathpoint1)) { + if (pathpoint != null) { + pathpoint.h = pathpoint1; + pathpoint1 = pathpoint; + } + + return this.a(pathpoint2, pathpoint1); + } + + if (pathpoint4.a(pathpoint1) < pathpoint3.a(pathpoint1)) { + pathpoint3 = pathpoint4; + } + + pathpoint4.i = true; + int l = 0; + int i1 = 0; + + while (true) { + if (i1 < 24) { + if (this.bV[i1] != pathpoint4) { + ++i1; + continue; + } + + l = i1; + } + + i1 = b0; + + while (true) { + if (i1 >= 24) { + continue label70; + } + + if ((this.bW[l] & 1 << i1) > 0) { + PathPoint pathpoint5 = this.bV[i1]; + + if (!pathpoint5.i) { + float f = pathpoint4.e + pathpoint4.a(pathpoint5); + + if (!pathpoint5.a() || f < pathpoint5.e) { + pathpoint5.h = pathpoint4; + pathpoint5.e = f; + pathpoint5.f = pathpoint5.a(pathpoint1); + if (pathpoint5.a()) { + this.bX.a(pathpoint5, pathpoint5.e + pathpoint5.f); + } else { + pathpoint5.g = pathpoint5.e + pathpoint5.f; + this.bX.a(pathpoint5); + } + } + } + } + + ++i1; + } + } + } + + if (pathpoint3 == pathpoint2) { + return null; + } else { + EntityEnderDragon.bQ.debug("Failed to find path from {} to {}", i, j); + if (pathpoint != null) { + pathpoint.h = pathpoint3; + pathpoint3 = pathpoint; + } + + return this.a(pathpoint2, pathpoint3); + } + } + + private PathEntity a(PathPoint pathpoint, PathPoint pathpoint1) { + int i = 1; + + PathPoint pathpoint2; + + for (pathpoint2 = pathpoint1; pathpoint2.h != null; pathpoint2 = pathpoint2.h) { + ++i; + } + + PathPoint[] apathpoint = new PathPoint[i]; + + pathpoint2 = pathpoint1; + --i; + + for (apathpoint[i] = pathpoint1; pathpoint2.h != null; apathpoint[i] = pathpoint2) { + pathpoint2 = pathpoint2.h; + --i; + } + + return new PathEntity(apathpoint); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("DragonPhase", this.bS.a().getControllerPhase().b()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKey("DragonPhase")) { + this.bS.setControllerPhase(DragonControllerPhase.getById(nbttagcompound.getInt("DragonPhase"))); + } + + } + + protected void I() {} + + public Entity[] bi() { + return this.children; + } + + public boolean isInteractable() { + return false; + } + + public World J_() { + return this.world; + } + + public SoundCategory bV() { + return SoundCategory.HOSTILE; + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_ENDER_DRAGON_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_ENDER_DRAGON_HURT; + } + + protected float cD() { + return 5.0F; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.aH; + } + + public Vec3D a(float f) { + IDragonController idragoncontroller = this.bS.a(); + DragonControllerPhase dragoncontrollerphase = idragoncontroller.getControllerPhase(); + float f1; + Vec3D vec3d; + + if (dragoncontrollerphase != DragonControllerPhase.LANDING && dragoncontrollerphase != DragonControllerPhase.TAKEOFF) { + if (idragoncontroller.a()) { + float f2 = this.pitch; + + f1 = 1.5F; + this.pitch = -45.0F; + vec3d = this.f(f); + this.pitch = f2; + } else { + vec3d = this.f(f); + } + } else { + BlockPosition blockposition = this.world.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, WorldGenEndTrophy.a); + + f1 = Math.max(MathHelper.sqrt(this.d(blockposition)) / 4.0F, 1.0F); + float f3 = 6.0F / f1; + float f4 = this.pitch; + float f5 = 1.5F; + + this.pitch = -f3 * 1.5F * 5.0F; + vec3d = this.f(f); + this.pitch = f4; + } + + return vec3d; + } + + public void a(EntityEnderCrystal entityendercrystal, BlockPosition blockposition, DamageSource damagesource) { + EntityHuman entityhuman; + + if (damagesource.getEntity() instanceof EntityHuman) { + entityhuman = (EntityHuman) damagesource.getEntity(); + } else { + entityhuman = this.world.a(blockposition, 64.0D, 64.0D); + } + + if (entityendercrystal == this.currentEnderCrystal) { + this.a(this.bD, DamageSource.b(entityhuman), 10.0F); + } + + this.bS.a().a(entityendercrystal, blockposition, damagesource, entityhuman); + } + + public void a(DataWatcherObject datawatcherobject) { + if (EntityEnderDragon.PHASE.equals(datawatcherobject) && this.world.isClientSide) { + this.bS.setControllerPhase(DragonControllerPhase.getById((Integer) this.getDataWatcher().get(EntityEnderDragon.PHASE))); + } + + super.a(datawatcherobject); + } + + public DragonControllerManager getDragonControllerManager() { + return this.bS; + } + + @Nullable + public EnderDragonBattle getEnderDragonBattle() { + return this.bR; + } + + public boolean addEffect(MobEffect mobeffect) { + return false; + } + + protected boolean n(Entity entity) { + return false; + } + + public boolean bm() { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/EntityEnderPearl.java b/src/main/java/net/minecraft/server/EntityEnderPearl.java new file mode 100644 index 000000000000..a372f6508fa6 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityEnderPearl.java @@ -0,0 +1,125 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; +// CraftBukkit start +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +// CraftBukkit end + +public class EntityEnderPearl extends EntityProjectile { + + private EntityLiving e; + + public EntityEnderPearl(World world) { + super(EntityTypes.ENDER_PEARL, world); + } + + public EntityEnderPearl(World world, EntityLiving entityliving) { + super(EntityTypes.ENDER_PEARL, entityliving, world); + this.e = entityliving; + } + + protected void a(MovingObjectPosition movingobjectposition) { + EntityLiving entityliving = this.getShooter(); + + if (movingobjectposition.entity != null) { + if (movingobjectposition.entity == this.e) { + return; + } + + movingobjectposition.entity.damageEntity(DamageSource.projectile(this, entityliving), 0.0F); + } + + if (movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.BLOCK) { + BlockPosition blockposition = movingobjectposition.getBlockPosition(); + TileEntity tileentity = this.world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityEndGateway) { + TileEntityEndGateway tileentityendgateway = (TileEntityEndGateway) tileentity; + + if (entityliving != null) { + if (entityliving instanceof EntityPlayer) { + CriterionTriggers.d.a((EntityPlayer) entityliving, this.world.getType(blockposition)); + } + + tileentityendgateway.a((Entity) entityliving); + this.die(); + return; + } + + tileentityendgateway.a((Entity) this); + return; + } + } + + for (int i = 0; i < 32; ++i) { + this.world.addParticle(Particles.K, this.locX, this.locY + this.random.nextDouble() * 2.0D, this.locZ, this.random.nextGaussian(), 0.0D, this.random.nextGaussian()); + } + + if (!this.world.isClientSide) { + if (entityliving instanceof EntityPlayer) { + EntityPlayer entityplayer = (EntityPlayer) entityliving; + + if (entityplayer.playerConnection.a().isConnected() && entityplayer.world == this.world && !entityplayer.isSleeping()) { + // CraftBukkit start - Fire PlayerTeleportEvent + org.bukkit.craftbukkit.entity.CraftPlayer player = entityplayer.getBukkitEntity(); + org.bukkit.Location location = getBukkitEntity().getLocation(); + location.setPitch(player.getLocation().getPitch()); + location.setYaw(player.getLocation().getYaw()); + + PlayerTeleportEvent teleEvent = new PlayerTeleportEvent(player, player.getLocation(), location, PlayerTeleportEvent.TeleportCause.ENDER_PEARL); + Bukkit.getPluginManager().callEvent(teleEvent); + + if (!teleEvent.isCancelled() && !entityplayer.playerConnection.isDisconnected()) { + if (this.random.nextFloat() < 0.05F && this.world.getGameRules().getBoolean("doMobSpawning")) { + EntityEndermite entityendermite = EntityTypes.ENDERMITE.create(world); // Paper + + entityendermite.setPlayerSpawned(true); + entityendermite.setPositionRotation(entityliving.locX, entityliving.locY, entityliving.locZ, entityliving.yaw, entityliving.pitch); + this.world.addEntity(entityendermite, CreatureSpawnEvent.SpawnReason.ENDER_PEARL); + } + + if (entityliving.isPassenger()) { + entityliving.stopRiding(); + } + + entityplayer.playerConnection.teleport(teleEvent.getTo()); + entityliving.fallDistance = 0.0F; + CraftEventFactory.entityDamage = this; + entityliving.damageEntity(DamageSource.FALL, 5.0F); + CraftEventFactory.entityDamage = null; + } + // CraftBukkit end + } + } else if (entityliving != null) { + entityliving.enderTeleportTo(this.locX, this.locY, this.locZ); + entityliving.fallDistance = 0.0F; + } + + this.die(); + } + + } + + public void tick() { + EntityLiving entityliving = this.getShooter(); + + if (entityliving != null && entityliving instanceof EntityHuman && !entityliving.isAlive()) { + this.die(); + } else { + super.tick(); + } + + } + + @Nullable + public Entity a(DimensionManager dimensionmanager) { + if (this.shooter.dimension != dimensionmanager) { + this.shooter = null; + } + + return super.a(dimensionmanager); + } +} diff --git a/src/main/java/net/minecraft/server/EntityEnderman.java b/src/main/java/net/minecraft/server/EntityEnderman.java new file mode 100644 index 000000000000..945040442850 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityEnderman.java @@ -0,0 +1,429 @@ +package net.minecraft.server; + +import java.util.Optional; +import com.destroystokyo.paper.event.entity.EndermanEscapeEvent; // Paper +import java.util.Random; +import java.util.UUID; +import java.util.function.Function; +import javax.annotation.Nullable; + +public class EntityEnderman extends EntityMonster { + + private static final UUID a = UUID.fromString("020E0DFB-87AE-4653-9556-831010E291A0"); + private static final AttributeModifier b = (new AttributeModifier(EntityEnderman.a, "Attacking speed boost", 0.15000000596046448D, 0)).a(false); + private static final DataWatcherObject> c = DataWatcher.a(EntityEnderman.class, DataWatcherRegistry.h); + private static final DataWatcherObject bC = DataWatcher.a(EntityEnderman.class, DataWatcherRegistry.i); + private int bD; + private int bE; + + public EntityEnderman(World world) { + super(EntityTypes.ENDERMAN, world); + this.setSize(0.6F, 2.9F); + this.Q = 1.0F; + this.a(PathType.WATER, -1.0F); + } + + protected void n() { + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(2, new PathfinderGoalMeleeAttack(this, 1.0D, false)); + this.goalSelector.a(7, new PathfinderGoalRandomStrollLand(this, 1.0D, 0.0F)); + this.goalSelector.a(8, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this)); + this.goalSelector.a(10, new EntityEnderman.PathfinderGoalEndermanPlaceBlock(this)); + this.goalSelector.a(11, new EntityEnderman.PathfinderGoalEndermanPickupBlock(this)); + this.targetSelector.a(1, new EntityEnderman.PathfinderGoalPlayerWhoLookedAtTarget(this)); + this.targetSelector.a(2, new PathfinderGoalHurtByTarget(this, false, new Class[0])); + this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget<>(this, EntityEndermite.class, 10, true, false, EntityEndermite::isPlayerSpawned)); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(40.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.30000001192092896D); + this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue(7.0D); + this.getAttributeInstance(GenericAttributes.FOLLOW_RANGE).setValue(64.0D); + } + + public void setGoalTarget(@Nullable EntityLiving entityliving) { + // CraftBukkit start - fire event + setGoalTarget(entityliving, org.bukkit.event.entity.EntityTargetEvent.TargetReason.UNKNOWN, true); + } + + // Paper start + private boolean tryEscape(EndermanEscapeEvent.Reason reason) { + return new EndermanEscapeEvent((org.bukkit.craftbukkit.entity.CraftEnderman) this.getBukkitEntity(), reason).callEvent(); + } + // Paper end + + @Override + public boolean setGoalTarget(EntityLiving entityliving, org.bukkit.event.entity.EntityTargetEvent.TargetReason reason, boolean fireEvent) { + if (!super.setGoalTarget(entityliving, reason, fireEvent)) { + return false; + } + entityliving = getGoalTarget(); + // CraftBukkit end + AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED); + + if (entityliving == null) { + this.bE = 0; + this.datawatcher.set(EntityEnderman.bC, false); + attributeinstance.c(EntityEnderman.b); + } else { + this.bE = this.ticksLived; + this.datawatcher.set(EntityEnderman.bC, true); + if (!attributeinstance.a(EntityEnderman.b)) { + attributeinstance.b(EntityEnderman.b); + } + } + return true; + + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityEnderman.c, Optional.empty()); + this.datawatcher.register(EntityEnderman.bC, false); + } + + public void l() { + if (this.ticksLived >= this.bD + 400) { + this.bD = this.ticksLived; + if (!this.isSilent()) { + this.world.a(this.locX, this.locY + (double) this.getHeadHeight(), this.locZ, SoundEffects.ENTITY_ENDERMAN_STARE, this.bV(), 2.5F, 1.0F, false); + } + } + + } + + public void a(DataWatcherObject datawatcherobject) { + if (EntityEnderman.bC.equals(datawatcherobject) && this.dB() && this.world.isClientSide) { + this.l(); + } + + super.a(datawatcherobject); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + IBlockData iblockdata = this.getCarried(); + + if (iblockdata != null) { + nbttagcompound.set("carriedBlockState", GameProfileSerializer.a(iblockdata)); + } + + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + IBlockData iblockdata = null; + + if (nbttagcompound.hasKeyOfType("carriedBlockState", 10)) { + iblockdata = GameProfileSerializer.d(nbttagcompound.getCompound("carriedBlockState")); + if (iblockdata.isAir()) { + iblockdata = null; + } + } + + this.setCarried(iblockdata); + } + + // Paper start - OBFHELPER - ok not really, but verify this on updates + private boolean f(EntityHuman entityhuman) { + boolean shouldAttack = f_real(entityhuman); + com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent event = new com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent((org.bukkit.entity.Enderman) getBukkitEntity(), (org.bukkit.entity.Player) entityhuman.getBukkitEntity()); + event.setCancelled(!shouldAttack); + return event.callEvent(); + } + private boolean f_real(EntityHuman entityhuman) { + // Paper end + ItemStack itemstack = (ItemStack) entityhuman.inventory.armor.get(3); + + if (itemstack.getItem() == Blocks.CARVED_PUMPKIN.getItem()) { + return false; + } else { + Vec3D vec3d = entityhuman.f(1.0F).a(); + Vec3D vec3d1 = new Vec3D(this.locX - entityhuman.locX, this.getBoundingBox().minY + (double) this.getHeadHeight() - (entityhuman.locY + (double) entityhuman.getHeadHeight()), this.locZ - entityhuman.locZ); + double d0 = vec3d1.b(); + + vec3d1 = vec3d1.a(); + double d1 = vec3d.b(vec3d1); + + return d1 > 1.0D - 0.025D / d0 ? entityhuman.hasLineOfSight(this) : false; + } + } + + public float getHeadHeight() { + return 2.55F; + } + + public void movementTick() { + if (this.world.isClientSide) { + for (int i = 0; i < 2; ++i) { + this.world.addParticle(Particles.K, this.locX + (this.random.nextDouble() - 0.5D) * (double) this.width, this.locY + this.random.nextDouble() * (double) this.length - 0.25D, this.locZ + (this.random.nextDouble() - 0.5D) * (double) this.width, (this.random.nextDouble() - 0.5D) * 2.0D, -this.random.nextDouble(), (this.random.nextDouble() - 0.5D) * 2.0D); + } + } + + this.bg = false; + super.movementTick(); + } + + protected void mobTick() { + if (this.ap()) { + this.damageEntity(DamageSource.DROWN, 1.0F); + } + + if (this.world.L() && this.ticksLived >= this.bE + 600) { + float f = this.az(); + + if (f > 0.5F && this.world.e(new BlockPosition(this)) && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && tryEscape(EndermanEscapeEvent.Reason.RUNAWAY)) { // Paper + this.setGoalTarget((EntityLiving) null); + this.dz(); + } + } + + super.mobTick(); + } + + public boolean teleportRandomly() { return dz(); } // Paper - OBFHELPER + protected boolean dz() { + double d0 = this.locX + (this.random.nextDouble() - 0.5D) * 64.0D; + double d1 = this.locY + (double) (this.random.nextInt(64) - 32); + double d2 = this.locZ + (this.random.nextDouble() - 0.5D) * 64.0D; + + return this.k(d0, d1, d2); + } + + protected boolean a(Entity entity) { + Vec3D vec3d = new Vec3D(this.locX - entity.locX, this.getBoundingBox().minY + (double) (this.length / 2.0F) - entity.locY + (double) entity.getHeadHeight(), this.locZ - entity.locZ); + + vec3d = vec3d.a(); + double d0 = 16.0D; + double d1 = this.locX + (this.random.nextDouble() - 0.5D) * 8.0D - vec3d.x * 16.0D; + double d2 = this.locY + (double) (this.random.nextInt(16) - 8) - vec3d.y * 16.0D; + double d3 = this.locZ + (this.random.nextDouble() - 0.5D) * 8.0D - vec3d.z * 16.0D; + + return this.k(d1, d2, d3); + } + + private boolean k(double d0, double d1, double d2) { + boolean flag = this.j(d0, d1, d2); + + if (flag) { + this.world.a((EntityHuman) null, this.lastX, this.lastY, this.lastZ, SoundEffects.ENTITY_ENDERMAN_TELEPORT, this.bV(), 1.0F, 1.0F); + this.a(SoundEffects.ENTITY_ENDERMAN_TELEPORT, 1.0F, 1.0F); + } + + return flag; + } + + protected SoundEffect D() { + return this.dB() ? SoundEffects.ENTITY_ENDERMAN_SCREAM : SoundEffects.ENTITY_ENDERMAN_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_ENDERMAN_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_ENDERMAN_DEATH; + } + + protected void dropEquipment(boolean flag, int i) { + super.dropEquipment(flag, i); + IBlockData iblockdata = this.getCarried(); + + if (iblockdata != null) { + this.a((IMaterial) iblockdata.getBlock()); + } + + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.C; + } + + public void setCarried(@Nullable IBlockData iblockdata) { + this.datawatcher.set(EntityEnderman.c, Optional.ofNullable(iblockdata)); + } + + @Nullable + public IBlockData getCarried() { + return (IBlockData) ((Optional) this.datawatcher.get(EntityEnderman.c)).orElse((Object) null); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else if (damagesource instanceof EntityDamageSourceIndirect && tryEscape(EndermanEscapeEvent.Reason.INDIRECT)) { // Paper + for (int i = 0; i < 64; ++i) { + if (this.dz()) { + return true; + } + } + + return false; + } else { + boolean flag = super.damageEntity(damagesource, f); + + if (damagesource.ignoresArmor() && this.random.nextInt(10) != 0 && tryEscape(damagesource == DamageSource.DROWN ? EndermanEscapeEvent.Reason.DROWN : EndermanEscapeEvent.Reason.CRITICAL_HIT)) { // Paper + this.dz(); + } + + return flag; + } + } + + public boolean dB() { + return (Boolean) this.datawatcher.get(EntityEnderman.bC); + } + + static class PathfinderGoalEndermanPickupBlock extends PathfinderGoal { + + private final EntityEnderman enderman; + + public PathfinderGoalEndermanPickupBlock(EntityEnderman entityenderman) { + this.enderman = entityenderman; + } + + public boolean a() { + return this.enderman.getCarried() != null ? false : (!this.enderman.world.getGameRules().getBoolean("mobGriefing") ? false : this.enderman.getRandom().nextInt(20) == 0); + } + + public void e() { + Random random = this.enderman.getRandom(); + World world = this.enderman.world; + int i = MathHelper.floor(this.enderman.locX - 2.0D + random.nextDouble() * 4.0D); + int j = MathHelper.floor(this.enderman.locY + random.nextDouble() * 3.0D); + int k = MathHelper.floor(this.enderman.locZ - 2.0D + random.nextDouble() * 4.0D); + BlockPosition blockposition = new BlockPosition(i, j, k); + IBlockData iblockdata = world.getTypeIfLoaded(blockposition); // Paper + if (iblockdata == null) return; // Paper + Block block = iblockdata.getBlock(); + MovingObjectPosition movingobjectposition = world.rayTrace(new Vec3D((double) ((float) MathHelper.floor(this.enderman.locX) + 0.5F), (double) ((float) j + 0.5F), (double) ((float) MathHelper.floor(this.enderman.locZ) + 0.5F)), new Vec3D((double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F)), FluidCollisionOption.NEVER, true, false); + boolean flag = movingobjectposition != null && movingobjectposition.getBlockPosition().equals(blockposition); + + if (block.a(TagsBlock.ENDERMAN_HOLDABLE) && flag) { + // CraftBukkit start - Pickup event + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.enderman, blockposition, Blocks.AIR.getBlockData()).isCancelled()) { + //this.enderman.setCarried(iblockdata); // Paper - moved down + world.setAir(blockposition); + this.enderman.setCarried(Block.getValidBlockForPosition(iblockdata, this.enderman.world, blockposition)); // Paper - Fix MC-124320 + } + // CraftBukkit end + } + + } + } + + static class PathfinderGoalEndermanPlaceBlock extends PathfinderGoal { + + private EntityEnderman getEnderman() { return this.a; } // Paper - OBFHELPER + private final EntityEnderman a; + + public PathfinderGoalEndermanPlaceBlock(EntityEnderman entityenderman) { + this.a = entityenderman; + } + + public boolean a() { + return this.a.getCarried() == null ? false : (!this.a.world.getGameRules().getBoolean("mobGriefing") ? false : this.a.getRandom().nextInt(2000) == 0); + } + + public void e() { + Random random = this.a.getRandom(); + World world = this.a.world; + int i = MathHelper.floor(this.a.locX - 1.0D + random.nextDouble() * 2.0D); + int j = MathHelper.floor(this.a.locY + random.nextDouble() * 2.0D); + int k = MathHelper.floor(this.a.locZ - 1.0D + random.nextDouble() * 2.0D); + BlockPosition blockposition = new BlockPosition(i, j, k); + IBlockData iblockdata = world.getTypeIfLoaded(blockposition); // Paper + if (iblockdata == null) return; // Paper + IBlockData iblockdata1 = world.getType(blockposition.down()); + IBlockData iblockdata2 = Block.getValidBlockForPosition(getEnderman().getCarried(), getEnderman().world, blockposition); // Paper - Fix MC-124320 + + if (iblockdata2 != null && this.a(world, blockposition, iblockdata2, iblockdata, iblockdata1)) { + // CraftBukkit start - Place event + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.a, blockposition, iblockdata2).isCancelled()) { + world.setTypeAndData(blockposition, iblockdata2, 3); + this.a.setCarried((IBlockData) null); + } + // CraftBukkit end + } + + } + + private boolean a(IWorldReader iworldreader, BlockPosition blockposition, IBlockData iblockdata, IBlockData iblockdata1, IBlockData iblockdata2) { + return iblockdata1.isAir() && !iblockdata2.isAir() && iblockdata2.g() && iblockdata.canPlace(iworldreader, blockposition); + } + } + + static class PathfinderGoalPlayerWhoLookedAtTarget extends PathfinderGoalNearestAttackableTarget { + + private final EntityEnderman i; public EntityEnderman getEnderman() { return i; } // Paper - OBFHELPER + private EntityHuman j; + private int k; + private int l; + + public PathfinderGoalPlayerWhoLookedAtTarget(EntityEnderman entityenderman) { + super(entityenderman, EntityHuman.class, false); + this.i = entityenderman; + } + + public boolean a() { + double d0 = this.i(); + + this.j = this.i.world.a(this.i.locX, this.i.locY, this.i.locZ, d0, d0, (Function) null, (entityhuman) -> { + return entityhuman != null && this.i.f(entityhuman); + }); + return this.j != null; + } + + public void c() { + this.k = 5; + this.l = 0; + } + + public void d() { + this.j = null; + super.d(); + } + + public boolean b() { + if (this.j != null) { + if (!this.i.f(this.j)) { + return false; + } else { + this.i.a((Entity) this.j, 10.0F, 10.0F); + return true; + } + } else { + return this.d != null && ((EntityHuman) this.d).isAlive() ? true : super.b(); + } + } + + public void e() { + if (this.j != null) { + if (--this.k <= 0) { + this.d = this.j; + this.j = null; + super.c(); + } + } else { + if (this.d != null) { + if (this.i.f((EntityHuman) this.d)) { + if (((EntityHuman) this.d).h(this.i) < 16.0D && this.getEnderman().tryEscape(EndermanEscapeEvent.Reason.STARE)) { // Paper + this.i.dz(); + } + + this.l = 0; + } else if (((EntityHuman) this.d).h(this.i) > 256.0D && this.l++ >= 30 && this.i.a((Entity) this.d)) { + this.l = 0; + } + } + + super.e(); + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityEvoker.java b/src/main/java/net/minecraft/server/EntityEvoker.java new file mode 100644 index 000000000000..fc20bbe2723c --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityEvoker.java @@ -0,0 +1,310 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public class EntityEvoker extends EntityIllagerWizard { + + private EntitySheep c; + + public EntityEvoker(World world) { + super(EntityTypes.EVOKER, world); + this.setSize(0.6F, 1.95F); + this.b_ = 10; + } + + protected void n() { + super.n(); + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(1, new EntityEvoker.b()); + this.goalSelector.a(2, new PathfinderGoalAvoidTarget<>(this, EntityHuman.class, 8.0F, 0.6D, 1.0D)); + this.goalSelector.a(4, new EntityEvoker.c()); + this.goalSelector.a(5, new EntityEvoker.a()); + this.goalSelector.a(6, new EntityEvoker.d()); + this.goalSelector.a(8, new PathfinderGoalRandomStroll(this, 0.6D)); + this.goalSelector.a(9, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 3.0F, 1.0F)); + this.goalSelector.a(10, new PathfinderGoalLookAtPlayer(this, EntityInsentient.class, 8.0F)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, true, new Class[] { EntityEvoker.class})); + this.targetSelector.a(2, (new PathfinderGoalNearestAttackableTarget<>(this, EntityHuman.class, true)).b(300)); + this.targetSelector.a(3, (new PathfinderGoalNearestAttackableTarget<>(this, EntityVillager.class, false)).b(300)); + this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget<>(this, EntityIronGolem.class, false)); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.5D); + this.getAttributeInstance(GenericAttributes.FOLLOW_RANGE).setValue(12.0D); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(24.0D); + } + + protected void x_() { + super.x_(); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + } + + protected MinecraftKey getDefaultLootTable() { + return LootTables.aB; + } + + protected void mobTick() { + super.mobTick(); + } + + public void tick() { + super.tick(); + } + + public boolean r(Entity entity) { + return entity == null ? false : (entity == this ? true : (super.r(entity) ? true : (entity instanceof EntityVex ? this.r(((EntityVex) entity).l()) : (entity instanceof EntityLiving && ((EntityLiving) entity).getMonsterType() == EnumMonsterType.ILLAGER ? this.getScoreboardTeam() == null && entity.getScoreboardTeam() == null : false)))); + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_EVOKER_AMBIENT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_EVOKER_DEATH; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_EVOKER_HURT; + } + + private void a(@Nullable EntitySheep entitysheep) { + this.c = entitysheep; + } + + @Nullable + private EntitySheep dD() { + return this.c; + } + + protected SoundEffect dz() { + return SoundEffects.ENTITY_EVOKER_CAST_SPELL; + } + + public class d extends EntityIllagerWizard.c { + + private final Predicate e = (entitysheep) -> { + return entitysheep.getColor() == EnumColor.BLUE; + }; + + public d() { + super(); + } + + public boolean a() { + if (EntityEvoker.this.getGoalTarget() != null) { + return false; + } else if (EntityEvoker.this.dA()) { + return false; + } else if (EntityEvoker.this.ticksLived < this.c) { + return false; + } else if (!EntityEvoker.this.world.getGameRules().getBoolean("mobGriefing")) { + return false; + } else { + List list = EntityEvoker.this.world.a(EntitySheep.class, EntityEvoker.this.getBoundingBox().grow(16.0D, 4.0D, 16.0D), this.e); + + if (list.isEmpty()) { + return false; + } else { + EntityEvoker.this.a((EntitySheep) list.get(EntityEvoker.this.random.nextInt(list.size()))); + return true; + } + } + } + + public boolean b() { + return EntityEvoker.this.dD() != null && this.b > 0; + } + + public void d() { + super.d(); + EntityEvoker.this.a((EntitySheep) null); + } + + protected void j() { + EntitySheep entitysheep = EntityEvoker.this.dD(); + + if (entitysheep != null && entitysheep.isAlive()) { + entitysheep.setColor(EnumColor.RED); + } + + } + + protected int m() { + return 40; + } + + protected int g() { + return 60; + } + + protected int i() { + return 140; + } + + protected SoundEffect k() { + return SoundEffects.ENTITY_EVOKER_PREPARE_WOLOLO; + } + + protected EntityIllagerWizard.Spell l() { + return EntityIllagerWizard.Spell.WOLOLO; + } + } + + class c extends EntityIllagerWizard.c { + + private c() { + super(); + } + + public boolean a() { + if (!super.a()) { + return false; + } else { + int i = EntityEvoker.this.world.a(EntityVex.class, EntityEvoker.this.getBoundingBox().g(16.0D)).size(); + + return EntityEvoker.this.random.nextInt(8) + 1 > i; + } + } + + protected int g() { + return 100; + } + + protected int i() { + return 340; + } + + protected void j() { + for (int i = 0; i < 3; ++i) { + BlockPosition blockposition = (new BlockPosition(EntityEvoker.this)).a(-2 + EntityEvoker.this.random.nextInt(5), 1, -2 + EntityEvoker.this.random.nextInt(5)); + EntityVex entityvex = EntityTypes.VEX.create(EntityEvoker.this.world); // Paper + entityvex.setPositionRotation(blockposition, 0.0F, 0.0F); + entityvex.prepare(EntityEvoker.this.world.getDamageScaler(blockposition), (GroupDataEntity) null, (NBTTagCompound) null); + entityvex.a((EntityInsentient) EntityEvoker.this); + entityvex.g(blockposition); + entityvex.a(20 * (30 + EntityEvoker.this.random.nextInt(90))); + EntityEvoker.this.world.addEntity(entityvex); + } + + } + + protected SoundEffect k() { + return SoundEffects.ENTITY_EVOKER_PREPARE_SUMMON; + } + + protected EntityIllagerWizard.Spell l() { + return EntityIllagerWizard.Spell.SUMMON_VEX; + } + } + + class a extends EntityIllagerWizard.c { + + private a() { + super(); + } + + protected int g() { + return 40; + } + + protected int i() { + return 100; + } + + protected void j() { + EntityLiving entityliving = EntityEvoker.this.getGoalTarget(); + double d0 = Math.min(entityliving.locY, EntityEvoker.this.locY); + double d1 = Math.max(entityliving.locY, EntityEvoker.this.locY) + 1.0D; + float f = (float) MathHelper.c(entityliving.locZ - EntityEvoker.this.locZ, entityliving.locX - EntityEvoker.this.locX); + int i; + + if (EntityEvoker.this.h(entityliving) < 9.0D) { + float f1; + + for (i = 0; i < 5; ++i) { + f1 = f + (float) i * 3.1415927F * 0.4F; + this.a(EntityEvoker.this.locX + (double) MathHelper.cos(f1) * 1.5D, EntityEvoker.this.locZ + (double) MathHelper.sin(f1) * 1.5D, d0, d1, f1, 0); + } + + for (i = 0; i < 8; ++i) { + f1 = f + (float) i * 3.1415927F * 2.0F / 8.0F + 1.2566371F; + this.a(EntityEvoker.this.locX + (double) MathHelper.cos(f1) * 2.5D, EntityEvoker.this.locZ + (double) MathHelper.sin(f1) * 2.5D, d0, d1, f1, 3); + } + } else { + for (i = 0; i < 16; ++i) { + double d2 = 1.25D * (double) (i + 1); + int j = 1 * i; + + this.a(EntityEvoker.this.locX + (double) MathHelper.cos(f) * d2, EntityEvoker.this.locZ + (double) MathHelper.sin(f) * d2, d0, d1, f, j); + } + } + + } + + private void a(double d0, double d1, double d2, double d3, float f, int i) { + BlockPosition blockposition = new BlockPosition(d0, d3, d1); + boolean flag = false; + double d4 = 0.0D; + + do { + if (!EntityEvoker.this.world.q(blockposition) && EntityEvoker.this.world.q(blockposition.down())) { + if (!EntityEvoker.this.world.isEmpty(blockposition)) { + IBlockData iblockdata = EntityEvoker.this.world.getType(blockposition); + VoxelShape voxelshape = iblockdata.getCollisionShape(EntityEvoker.this.world, blockposition); + + if (!voxelshape.isEmpty()) { + d4 = voxelshape.c(EnumDirection.EnumAxis.Y); + } + } + + flag = true; + break; + } + + blockposition = blockposition.down(); + } while (blockposition.getY() >= MathHelper.floor(d2) - 1); + + if (flag) { + EntityEvokerFangs entityevokerfangs = new EntityEvokerFangs(EntityEvoker.this.world, d0, (double) blockposition.getY() + d4, d1, f, i, EntityEvoker.this); + + EntityEvoker.this.world.addEntity(entityevokerfangs); + } + + } + + protected SoundEffect k() { + return SoundEffects.ENTITY_EVOKER_PREPARE_ATTACK; + } + + protected EntityIllagerWizard.Spell l() { + return EntityIllagerWizard.Spell.FANGS; + } + } + + class b extends EntityIllagerWizard.b { + + private b() { + super(); + } + + public void e() { + if (EntityEvoker.this.getGoalTarget() != null) { + EntityEvoker.this.getControllerLook().a(EntityEvoker.this.getGoalTarget(), (float) EntityEvoker.this.L(), (float) EntityEvoker.this.K()); + } else if (EntityEvoker.this.dD() != null) { + EntityEvoker.this.getControllerLook().a(EntityEvoker.this.dD(), (float) EntityEvoker.this.L(), (float) EntityEvoker.this.K()); + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityEvokerFangs.java b/src/main/java/net/minecraft/server/EntityEvokerFangs.java new file mode 100644 index 000000000000..cbe9acba1f38 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityEvokerFangs.java @@ -0,0 +1,127 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.UUID; +import javax.annotation.Nullable; + +public class EntityEvokerFangs extends Entity { + + private int a; + private boolean b; + private int c; + private boolean d; + private EntityLiving e; + private UUID f; + + public EntityEvokerFangs(World world) { + super(EntityTypes.EVOKER_FANGS, world); + this.c = 22; + this.setSize(0.5F, 0.8F); + } + + public EntityEvokerFangs(World world, double d0, double d1, double d2, float f, int i, EntityLiving entityliving) { + this(world); + this.a = i; + this.a(entityliving); + this.yaw = f * 57.295776F; + this.setPosition(d0, d1, d2); + } + + protected void x_() {} + + public void a(@Nullable EntityLiving entityliving) { + this.e = entityliving; + this.f = entityliving == null ? null : entityliving.getUniqueID(); + } + + @Nullable + public EntityLiving getOwner() { + if (this.e == null && this.f != null && this.world instanceof WorldServer) { + Entity entity = ((WorldServer) this.world).getEntity(this.f); + + if (entity instanceof EntityLiving) { + this.e = (EntityLiving) entity; + } + } + + return this.e; + } + + protected void a(NBTTagCompound nbttagcompound) { + this.a = nbttagcompound.getInt("Warmup"); + if (nbttagcompound.b("OwnerUUID")) { + this.f = nbttagcompound.a("OwnerUUID"); + } + + } + + protected void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setInt("Warmup", this.a); + if (this.f != null) { + nbttagcompound.a("OwnerUUID", this.f); + } + + } + + public void tick() { + super.tick(); + if (this.world.isClientSide) { + if (this.d) { + --this.c; + if (this.c == 14) { + for (int i = 0; i < 12; ++i) { + double d0 = this.locX + (this.random.nextDouble() * 2.0D - 1.0D) * (double) this.width * 0.5D; + double d1 = this.locY + 0.05D + this.random.nextDouble(); + double d2 = this.locZ + (this.random.nextDouble() * 2.0D - 1.0D) * (double) this.width * 0.5D; + double d3 = (this.random.nextDouble() * 2.0D - 1.0D) * 0.3D; + double d4 = 0.3D + this.random.nextDouble() * 0.3D; + double d5 = (this.random.nextDouble() * 2.0D - 1.0D) * 0.3D; + + this.world.addParticle(Particles.h, d0, d1 + 1.0D, d2, d3, d4, d5); + } + } + } + } else if (--this.a < 0) { + if (this.a == -8) { + List list = this.world.a(EntityLiving.class, this.getBoundingBox().grow(0.2D, 0.0D, 0.2D)); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityLiving entityliving = (EntityLiving) iterator.next(); + + this.c(entityliving); + } + } + + if (!this.b) { + this.world.broadcastEntityEffect(this, (byte) 4); + this.b = true; + } + + if (--this.c < 0) { + this.die(); + } + } + + } + + private void c(EntityLiving entityliving) { + EntityLiving entityliving1 = this.getOwner(); + + if (entityliving.isAlive() && !entityliving.bl() && entityliving != entityliving1) { + if (entityliving1 == null) { + org.bukkit.craftbukkit.event.CraftEventFactory.entityDamage = this; // CraftBukkit + entityliving.damageEntity(DamageSource.MAGIC, 6.0F); + org.bukkit.craftbukkit.event.CraftEventFactory.entityDamage = null; // CraftBukkit + } else { + if (entityliving1.r(entityliving)) { + return; + } + + entityliving.damageEntity(DamageSource.c(this, entityliving1), 6.0F); + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityExperienceOrb.java b/src/main/java/net/minecraft/server/EntityExperienceOrb.java new file mode 100644 index 000000000000..3606b1014373 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityExperienceOrb.java @@ -0,0 +1,288 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityTargetLivingEntityEvent; +import org.bukkit.event.entity.EntityTargetEvent; +// CraftBukkit end + +public class EntityExperienceOrb extends Entity { + + public int a; + public int b; + public int c; + private int d = 5; + public int value; + private EntityHuman targetPlayer; + private int targetTime; + // Paper start + public java.util.UUID sourceEntityId; + public java.util.UUID triggerEntityId; + public org.bukkit.entity.ExperienceOrb.SpawnReason spawnReason = org.bukkit.entity.ExperienceOrb.SpawnReason.UNKNOWN; + + private void loadPaperNBT(NBTTagCompound nbttagcompound) { + if (!nbttagcompound.hasKeyOfType("Paper.ExpData", 10)) { // 10 = compound + return; + } + NBTTagCompound comp = nbttagcompound.getCompound("Paper.ExpData"); + if (comp.hasUUID("source")) { + this.sourceEntityId = comp.getUUID("source"); + } + if (comp.hasUUID("trigger")) { + this.triggerEntityId = comp.getUUID("trigger"); + } + if (comp.hasKey("reason")) { + String reason = comp.getString("reason"); + try { + spawnReason = org.bukkit.entity.ExperienceOrb.SpawnReason.valueOf(reason); + } catch (Exception e) { + this.world.getServer().getLogger().warning("Invalid spawnReason set for experience orb: " + e.getMessage() + " - " + reason); + } + } + } + private void savePaperNBT(NBTTagCompound nbttagcompound) { + NBTTagCompound comp = new NBTTagCompound(); + if (sourceEntityId != null) { + comp.setUUID("source", sourceEntityId); + } + if (triggerEntityId != null) { + comp.setUUID("trigger", triggerEntityId); + } + if (spawnReason != null && spawnReason != org.bukkit.entity.ExperienceOrb.SpawnReason.UNKNOWN) { + comp.setString("reason", spawnReason.name()); + } + nbttagcompound.set("Paper.ExpData", comp); + } + + public EntityExperienceOrb(World world, double d0, double d1, double d2, int i) { + this(world, d0, d1, d2, i, null, null); + } + + public EntityExperienceOrb(World world, double d0, double d1, double d2, int i, org.bukkit.entity.ExperienceOrb.SpawnReason reason, Entity triggerId) { + this(world, d0, d1, d2, i, reason, triggerId, null); + } + + public EntityExperienceOrb(World world, double d0, double d1, double d2, int i, org.bukkit.entity.ExperienceOrb.SpawnReason reason, Entity triggerId, Entity sourceId) { + super(EntityTypes.EXPERIENCE_ORB, world); + this.sourceEntityId = sourceId != null ? sourceId.getUniqueID() : null; + this.triggerEntityId = triggerId != null ? triggerId.getUniqueID() : null; + this.spawnReason = reason != null ? reason : org.bukkit.entity.ExperienceOrb.SpawnReason.UNKNOWN; + // Paper end + this.setSize(0.5F, 0.5F); + this.setPosition(d0, d1, d2); + this.yaw = (float) (Math.random() * 360.0D); + this.motX = (double) ((float) (Math.random() * 0.20000000298023224D - 0.10000000149011612D) * 2.0F); + this.motY = (double) ((float) (Math.random() * 0.2D) * 2.0F); + this.motZ = (double) ((float) (Math.random() * 0.20000000298023224D - 0.10000000149011612D) * 2.0F); + this.value = i; + } + + public EntityExperienceOrb(World world) { + super(EntityTypes.EXPERIENCE_ORB, world); + this.setSize(0.25F, 0.25F); + } + + protected boolean playStepSound() { + return false; + } + + protected void x_() {} + + public void tick() { + super.tick(); + EntityHuman prevTarget = this.targetPlayer;// CraftBukkit - store old target + if (this.c > 0) { + --this.c; + } + + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + if (this.a(TagsFluid.WATER)) { + this.k(); + } else if (!this.isNoGravity()) { + this.motY -= 0.029999999329447746D; + } + + if (this.world.getFluid(new BlockPosition(this)).a(TagsFluid.LAVA)) { + this.motY = 0.20000000298023224D; + this.motX = (double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.2F); + this.motZ = (double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.2F); + this.a(SoundEffects.ENTITY_GENERIC_BURN, 0.4F, 2.0F + this.random.nextFloat() * 0.4F); + } + + this.i(this.locX, (this.getBoundingBox().minY + this.getBoundingBox().maxY) / 2.0D, this.locZ); + double d0 = 8.0D; + + if (this.targetTime < this.a - 20 + this.getId() % 100) { + if (this.targetPlayer == null || this.targetPlayer.h(this) > 64.0D) { + this.targetPlayer = this.world.findNearbyPlayer(this, 8.0D); + } + + this.targetTime = this.a; + } + + if (this.targetPlayer != null && this.targetPlayer.isSpectator()) { + this.targetPlayer = null; + } + + if (this.targetPlayer != null) { + // CraftBukkit start + boolean cancelled = false; + if (this.targetPlayer != prevTarget) { + EntityTargetLivingEntityEvent event = CraftEventFactory.callEntityTargetLivingEvent(this, targetPlayer, EntityTargetEvent.TargetReason.CLOSEST_PLAYER); + EntityLiving target = event.getTarget() == null ? null : ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getTarget()).getHandle(); + targetPlayer = target instanceof EntityHuman ? (EntityHuman) target : null; + cancelled = event.isCancelled(); + } + + if (!cancelled && targetPlayer != null) { + double d1 = (this.targetPlayer.locX - this.locX) / 8.0D; + double d2 = (this.targetPlayer.locY + (double) this.targetPlayer.getHeadHeight() / 2.0D - this.locY) / 8.0D; + double d3 = (this.targetPlayer.locZ - this.locZ) / 8.0D; + double d4 = Math.sqrt(d1 * d1 + d2 * d2 + d3 * d3); + double d5 = 1.0D - d4; + + if (d5 > 0.0D) { + d5 *= d5; + this.motX += d1 / d4 * d5 * 0.1D; + this.motY += d2 / d4 * d5 * 0.1D; + this.motZ += d3 / d4 * d5 * 0.1D; + } + } + // CraftBukkit end + } + + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + float f = 0.98F; + + if (this.onGround) { + f = this.world.getType(new BlockPosition(MathHelper.floor(this.locX), MathHelper.floor(this.getBoundingBox().minY) - 1, MathHelper.floor(this.locZ))).getBlock().n() * 0.98F; + } + + this.motX *= (double) f; + this.motY *= 0.9800000190734863D; + this.motZ *= (double) f; + if (this.onGround) { + this.motY *= -0.8999999761581421D; + } + + ++this.a; + ++this.b; + if (this.b >= 6000) { + this.die(); + } + + } + + private void k() { + this.motY += 5.000000237487257E-4D; + this.motY = Math.min(this.motY, 0.05999999865889549D); + this.motX *= 0.9900000095367432D; + this.motZ *= 0.9900000095367432D; + } + + protected void au() {} + + protected void burn(int i) { + this.damageEntity(DamageSource.FIRE, (float) i); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else { + this.aA(); + this.d = (int) ((float) this.d - f); + if (this.d <= 0) { + this.die(); + } + + return false; + } + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setShort("Health", (short) this.d); + nbttagcompound.setShort("Age", (short) this.b); + nbttagcompound.setInt("Value", this.value); // Paper - save as Integer + savePaperNBT(nbttagcompound); // Paper + } + + public void a(NBTTagCompound nbttagcompound) { + this.d = nbttagcompound.getShort("Health"); + this.b = nbttagcompound.getShort("Age"); + this.value = nbttagcompound.getInt("Value"); // Paper - load as Integer + loadPaperNBT(nbttagcompound); // Paper + } + + public void d(EntityHuman entityhuman) { + if (!this.world.isClientSide) { + if (this.c == 0 && entityhuman.bJ == 0 && new com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent(((EntityPlayer) entityhuman).getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) this.getBukkitEntity()).callEvent()) { // Paper + entityhuman.bJ = 2; + entityhuman.receive(this, 1); + ItemStack itemstack = EnchantmentManager.b(Enchantments.MENDING, (EntityLiving) entityhuman); + + if (!itemstack.isEmpty() && itemstack.f()) { + int i = Math.min(this.c(this.value), itemstack.getDamage()); + + // CraftBukkit start + org.bukkit.event.player.PlayerItemMendEvent event = CraftEventFactory.callPlayerItemMendEvent(entityhuman, this, itemstack, i); + i = event.getRepairAmount(); + if (!event.isCancelled()) { + this.value -= this.b(i); + itemstack.setDamage(itemstack.getDamage() - i); + } + // CraftBukkit end + } + + if (this.value > 0) { + entityhuman.giveExp(CraftEventFactory.callPlayerExpChangeEvent(entityhuman, this).getAmount()); // CraftBukkit - this.value -> event.getAmount() // Paper - supply experience orb object + } + + this.die(); + } + + } + } + + public int durToXp(int i) { return b(i); } // Paper OBFHELPER + private int b(int i) { + return i / 2; + } + + public int xpToDur(int i) { return c(i); } // Paper OBFHELPER + private int c(int i) { + return i * 2; + } + + public int f() { + return this.value; + } + + public static int getOrbValue(int i) { + // CraftBukkit start + if (i > 162670129) return i - 100000; + if (i > 81335063) return 81335063; + if (i > 40667527) return 40667527; + if (i > 20333759) return 20333759; + if (i > 10166857) return 10166857; + if (i > 5083423) return 5083423; + if (i > 2541701) return 2541701; + if (i > 1270849) return 1270849; + if (i > 635413) return 635413; + if (i > 317701) return 317701; + if (i > 158849) return 158849; + if (i > 79423) return 79423; + if (i > 39709) return 39709; + if (i > 19853) return 19853; + if (i > 9923) return 9923; + if (i > 4957) return 4957; + // CraftBukkit end + return i >= 2477 ? 2477 : (i >= 1237 ? 1237 : (i >= 617 ? 617 : (i >= 307 ? 307 : (i >= 149 ? 149 : (i >= 73 ? 73 : (i >= 37 ? 37 : (i >= 17 ? 17 : (i >= 7 ? 7 : (i >= 3 ? 3 : 1))))))))); + } + + public boolean bk() { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/EntityFallingBlock.java b/src/main/java/net/minecraft/server/EntityFallingBlock.java new file mode 100644 index 000000000000..3eaee8d890a7 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityFallingBlock.java @@ -0,0 +1,287 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Iterator; +import java.util.List; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class EntityFallingBlock extends Entity { + + private IBlockData block; + public int ticksLived; + public boolean dropItem; + private boolean f; + public boolean hurtEntities; + private int fallHurtMax; + private float fallHurtAmount; + public NBTTagCompound tileEntityData; + protected static final DataWatcherObject d = DataWatcher.a(EntityFallingBlock.class, DataWatcherRegistry.l); + + public EntityFallingBlock(World world) { + super(EntityTypes.FALLING_BLOCK, world); + this.block = Blocks.SAND.getBlockData(); + this.dropItem = true; + this.fallHurtMax = 40; + this.fallHurtAmount = 2.0F; + } + + public EntityFallingBlock(World world, double d0, double d1, double d2, IBlockData iblockdata) { + this(world); + this.block = iblockdata; + this.j = true; + this.setSize(0.98F, 0.98F); + this.setPosition(d0, d1 + (double) ((1.0F - this.length) / 2.0F), d2); + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + this.lastX = d0; + this.lastY = d1; + this.lastZ = d2; + this.a(new BlockPosition(this)); + } + + public boolean bk() { + return false; + } + + public void a(BlockPosition blockposition) { + this.datawatcher.set(EntityFallingBlock.d, blockposition); + } + + protected boolean playStepSound() { + return false; + } + + protected void x_() { + this.datawatcher.register(EntityFallingBlock.d, BlockPosition.ZERO); + } + + public boolean isInteractable() { + return !this.dead; + } + + public void tick() { + if (this.block.isAir()) { + this.die(); + } else { + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + Block block = this.block.getBlock(); + BlockPosition blockposition; + + if (this.ticksLived++ == 0) { + blockposition = new BlockPosition(this); + if (this.world.getType(blockposition).getBlock() == block && !CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, Blocks.AIR.getBlockData()).isCancelled()) { + this.world.setAir(blockposition); + } else if (!this.world.isClientSide) { + this.die(); + return; + } + } + + if (!this.isNoGravity()) { + this.motY -= 0.03999999910593033D; + } + + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + + // Paper start - Configurable EntityFallingBlock height nerf + if (this.world.paperConfig.fallingBlockHeightNerf != 0 && this.locY > this.world.paperConfig.fallingBlockHeightNerf) { + if (this.dropItem && this.world.getGameRules().getBoolean("doEntityDrops")) { + this.dropItem(new ItemStack(block), 0.0F); + } + + this.die(); + } + // Paper end + + if (!this.world.isClientSide) { + blockposition = new BlockPosition(this); + boolean flag = this.block.getBlock() instanceof BlockConcretePowder; + boolean flag1 = flag && this.world.getFluid(blockposition).a(TagsFluid.WATER); + double d0 = this.motX * this.motX + this.motY * this.motY + this.motZ * this.motZ; + + if (flag && d0 > 1.0D) { + MovingObjectPosition movingobjectposition = this.world.rayTrace(new Vec3D(this.lastX, this.lastY, this.lastZ), new Vec3D(this.locX, this.locY, this.locZ), FluidCollisionOption.SOURCE_ONLY); + + if (movingobjectposition != null && this.world.getFluid(movingobjectposition.getBlockPosition()).a(TagsFluid.WATER)) { + blockposition = movingobjectposition.getBlockPosition(); + flag1 = true; + } + } + + if (!this.onGround && !flag1) { + if (this.ticksLived > 100 && !this.world.isClientSide && (blockposition.getY() < 1 || blockposition.getY() > 256) || this.ticksLived > 600) { + if (this.dropItem && this.world.getGameRules().getBoolean("doEntityDrops")) { + this.a((IMaterial) block); + } + + this.die(); + } + } else { + IBlockData iblockdata = this.world.getType(blockposition); + + if (!flag1 && BlockFalling.canFallThrough(this.world.getType(new BlockPosition(this.locX, this.locY - 0.009999999776482582D, this.locZ)))) { + this.onGround = false; + // return; // CraftBukkit + } + + this.motX *= 0.699999988079071D; + this.motZ *= 0.699999988079071D; + this.motY *= -0.5D; + if (iblockdata.getBlock() != Blocks.MOVING_PISTON) { + this.die(); + if (!this.f) { + // CraftBukkit start + if (iblockdata.getMaterial().isReplaceable() && (flag1 || !BlockFalling.canFallThrough(this.world.getType(blockposition.down())))) { + if (CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, this.block).isCancelled()) { + return; + } + this.world.setTypeAndData(blockposition, this.block, 3); + // CraftBukkit end + if (block instanceof BlockFalling) { + ((BlockFalling) block).a(this.world, blockposition, this.block, iblockdata); + } + + if (this.tileEntityData != null && block instanceof ITileEntity) { + TileEntity tileentity = this.world.getTileEntity(blockposition); + + if (tileentity != null) { + NBTTagCompound nbttagcompound = tileentity.save(new NBTTagCompound()); + Iterator iterator = this.tileEntityData.getKeys().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + NBTBase nbtbase = this.tileEntityData.get(s); + + if (!"x".equals(s) && !"y".equals(s) && !"z".equals(s)) { + nbttagcompound.set(s, nbtbase.clone()); + } + } + + tileentity.load(nbttagcompound); + tileentity.update(); + } + } + } else if (this.dropItem && this.world.getGameRules().getBoolean("doEntityDrops")) { + this.a((IMaterial) block); + } + } else if (block instanceof BlockFalling) { + ((BlockFalling) block).a(this.world, blockposition); + } + } + } + } + + this.motX *= 0.9800000190734863D; + this.motY *= 0.9800000190734863D; + this.motZ *= 0.9800000190734863D; + } + } + + public void c(float f, float f1) { + if (this.hurtEntities) { + int i = MathHelper.f(f - 1.0F); + + if (i > 0) { + List list = Lists.newArrayList(this.world.getEntities(this, this.getBoundingBox())); + boolean flag = this.block.a(TagsBlock.ANVIL); + DamageSource damagesource = flag ? DamageSource.ANVIL : DamageSource.FALLING_BLOCK; + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + CraftEventFactory.entityDamage = this; // CraftBukkit + entity.damageEntity(damagesource, (float) Math.min(MathHelper.d((float) i * this.fallHurtAmount), this.fallHurtMax)); + CraftEventFactory.entityDamage = null; // CraftBukkit + } + + if (flag && (double) this.random.nextFloat() < 0.05000000074505806D + (double) i * 0.05D) { + IBlockData iblockdata = BlockAnvil.a_(this.block); + + if (iblockdata == null) { + this.f = true; + } else { + this.block = iblockdata; + } + } + } + } + + } + + protected void b(NBTTagCompound nbttagcompound) { + nbttagcompound.set("BlockState", GameProfileSerializer.a(this.block)); + nbttagcompound.setInt("Time", this.ticksLived); + nbttagcompound.setBoolean("DropItem", this.dropItem); + nbttagcompound.setBoolean("HurtEntities", this.hurtEntities); + nbttagcompound.setFloat("FallHurtAmount", this.fallHurtAmount); + nbttagcompound.setInt("FallHurtMax", this.fallHurtMax); + if (this.tileEntityData != null) { + nbttagcompound.set("TileEntityData", this.tileEntityData); + } + + } + + protected void a(NBTTagCompound nbttagcompound) { + this.block = GameProfileSerializer.d(nbttagcompound.getCompound("BlockState")); + + // Paper start - Block FallingBlocks with Command Blocks + // Check mappings on update - dc = "repeating_command_block" - dd = "chain_command_block" + final Block b = this.block.getBlock(); + if (this.world.paperConfig.filterNBTFromSpawnEgg && (b == Blocks.COMMAND_BLOCK || b == Blocks.REPEATING_COMMAND_BLOCK || b == Blocks.CHAIN_COMMAND_BLOCK)) { + this.block = Blocks.STONE.getBlockData(); + } + // Paper end + + this.ticksLived = nbttagcompound.getInt("Time"); + if (nbttagcompound.hasKeyOfType("HurtEntities", 99)) { + this.hurtEntities = nbttagcompound.getBoolean("HurtEntities"); + this.fallHurtAmount = nbttagcompound.getFloat("FallHurtAmount"); + this.fallHurtMax = nbttagcompound.getInt("FallHurtMax"); + } else if (this.block.a(TagsBlock.ANVIL)) { + this.hurtEntities = true; + } + + if (nbttagcompound.hasKeyOfType("DropItem", 99)) { + this.dropItem = nbttagcompound.getBoolean("DropItem"); + } + + if (nbttagcompound.hasKeyOfType("TileEntityData", 10)) { + this.tileEntityData = nbttagcompound.getCompound("TileEntityData"); + } + + if (this.block.isAir()) { + this.block = Blocks.SAND.getBlockData(); + } + + // Paper start - Try and load origin location from the old NBT tags for backwards compatibility + if (nbttagcompound.hasKey("SourceLoc_x")) { + int srcX = nbttagcompound.getInt("SourceLoc_x"); + int srcY = nbttagcompound.getInt("SourceLoc_y"); + int srcZ = nbttagcompound.getInt("SourceLoc_z"); + origin = new org.bukkit.Location(world.getWorld(), srcX, srcY, srcZ); + } + // Paper end + } + + public void a(boolean flag) { + this.hurtEntities = flag; + } + + public void appendEntityCrashDetails(CrashReportSystemDetails crashreportsystemdetails) { + super.appendEntityCrashDetails(crashreportsystemdetails); + crashreportsystemdetails.a("Immitating BlockState", (Object) this.block.toString()); + } + + public IBlockData getBlock() { + return this.block; + } + + public boolean bM() { + return true; + } +} diff --git a/src/main/java/net/minecraft/server/EntityFireball.java b/src/main/java/net/minecraft/server/EntityFireball.java new file mode 100644 index 000000000000..58cc4824cfee --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityFireball.java @@ -0,0 +1,206 @@ +package net.minecraft.server; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public abstract class EntityFireball extends Entity { + + public EntityLiving shooter; + private int e; + private int f; + public double dirX; + public double dirY; + public double dirZ; + public float bukkitYield = 1; // CraftBukkit + public boolean isIncendiary = true; // CraftBukkit + + protected EntityFireball(EntityTypes entitytypes, World world, float f, float f1) { + super(entitytypes, world); + this.setSize(f, f1); + } + + public EntityFireball(EntityTypes entitytypes, double d0, double d1, double d2, double d3, double d4, double d5, World world, float f, float f1) { + this(entitytypes, world, f, f1); + this.setPositionRotation(d0, d1, d2, this.yaw, this.pitch); + this.setPosition(d0, d1, d2); + double d6 = (double) MathHelper.sqrt(d3 * d3 + d4 * d4 + d5 * d5); + + this.dirX = d3 / d6 * 0.1D; + this.dirY = d4 / d6 * 0.1D; + this.dirZ = d5 / d6 * 0.1D; + } + + public EntityFireball(EntityTypes entitytypes, EntityLiving entityliving, double d0, double d1, double d2, World world, float f, float f1) { + this(entitytypes, world, f, f1); + this.shooter = entityliving; + this.projectileSource = (org.bukkit.entity.LivingEntity) entityliving.getBukkitEntity(); // CraftBukkit + this.setPositionRotation(entityliving.locX, entityliving.locY, entityliving.locZ, entityliving.yaw, entityliving.pitch); + this.setPosition(this.locX, this.locY, this.locZ); + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + // CraftBukkit start - Added setDirection method + this.setDirection(d0, d1, d2); + } + + public void setDirection(double d0, double d1, double d2) { + // CraftBukkit end + d0 += this.random.nextGaussian() * 0.4D; + d1 += this.random.nextGaussian() * 0.4D; + d2 += this.random.nextGaussian() * 0.4D; + double d3 = (double) MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + + this.dirX = d0 / d3 * 0.1D; + this.dirY = d1 / d3 * 0.1D; + this.dirZ = d2 / d3 * 0.1D; + } + + protected void x_() {} + + public void tick() { + if (!this.world.isClientSide && (this.shooter != null && this.shooter.dead || !this.world.isLoaded(new BlockPosition(this)))) { + this.die(); + } else { + super.tick(); + if (this.f()) { + this.setOnFire(1); + } + + ++this.f; + MovingObjectPosition movingobjectposition = ProjectileHelper.a(this, true, this.f >= 25, this.shooter); + + // Paper start - Call ProjectileCollideEvent + if (movingobjectposition != null && movingobjectposition.entity != null) { + com.destroystokyo.paper.event.entity.ProjectileCollideEvent event = CraftEventFactory.callProjectileCollideEvent(this, movingobjectposition); + if (event.isCancelled()) { + movingobjectposition = null; + } + } + // Paper end + + if (movingobjectposition != null) { + this.a(movingobjectposition); + + // CraftBukkit start - Fire ProjectileHitEvent + if (this.dead) { + CraftEventFactory.callProjectileHitEvent(this, movingobjectposition); + } + // CraftBukkit end + } + + this.locX += this.motX; + this.locY += this.motY; + this.locZ += this.motZ; + ProjectileHelper.a(this, 0.2F); + float f = this.k(); + + if (this.isInWater()) { + for (int i = 0; i < 4; ++i) { + float f1 = 0.25F; + + this.world.addParticle(Particles.e, this.locX - this.motX * 0.25D, this.locY - this.motY * 0.25D, this.locZ - this.motZ * 0.25D, this.motX, this.motY, this.motZ); + } + + f = 0.8F; + } + + this.motX += this.dirX; + this.motY += this.dirY; + this.motZ += this.dirZ; + this.motX *= (double) f; + this.motY *= (double) f; + this.motZ *= (double) f; + this.world.addParticle(this.i(), this.locX, this.locY + 0.5D, this.locZ, 0.0D, 0.0D, 0.0D); + this.setPosition(this.locX, this.locY, this.locZ); + } + } + + protected boolean f() { + return true; + } + + protected ParticleParam i() { + return Particles.M; + } + + protected float k() { + return 0.95F; + } + + protected abstract void a(MovingObjectPosition movingobjectposition); + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.set("direction", this.a(new double[] { this.motX, this.motY, this.motZ})); + nbttagcompound.set("power", this.a(new double[] { this.dirX, this.dirY, this.dirZ})); + nbttagcompound.setInt("life", this.e); + } + + public void a(NBTTagCompound nbttagcompound) { + NBTTagList nbttaglist; + + if (nbttagcompound.hasKeyOfType("power", 9)) { + nbttaglist = nbttagcompound.getList("power", 6); + if (nbttaglist.size() == 3) { + this.dirX = nbttaglist.k(0); + this.dirY = nbttaglist.k(1); + this.dirZ = nbttaglist.k(2); + } + } + + this.e = nbttagcompound.getInt("life"); + if (nbttagcompound.hasKeyOfType("direction", 9) && nbttagcompound.getList("direction", 6).size() == 3) { + nbttaglist = nbttagcompound.getList("direction", 6); + this.motX = nbttaglist.k(0); + this.motY = nbttaglist.k(1); + this.motZ = nbttaglist.k(2); + } else { + this.die(); + } + + } + + public boolean isInteractable() { + return true; + } + + public float aM() { + return 1.0F; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else { + this.aA(); + if (damagesource.getEntity() != null) { + // CraftBukkit start + if (CraftEventFactory.handleNonLivingEntityDamageEvent(this, damagesource, f)) { + return false; + } + // CraftBukkit end + Vec3D vec3d = damagesource.getEntity().aN(); + + if (vec3d != null) { + this.motX = vec3d.x; + this.motY = vec3d.y; + this.motZ = vec3d.z; + this.dirX = this.motX * 0.1D; + this.dirY = this.motY * 0.1D; + this.dirZ = this.motZ * 0.1D; + } + + if (damagesource.getEntity() instanceof EntityLiving) { + this.shooter = (EntityLiving) damagesource.getEntity(); + this.projectileSource = (org.bukkit.projectiles.ProjectileSource) this.shooter.getBukkitEntity(); + } + + return true; + } else { + return false; + } + } + } + + public float az() { + return 1.0F; + } +} diff --git a/src/main/java/net/minecraft/server/EntityFireworks.java b/src/main/java/net/minecraft/server/EntityFireworks.java new file mode 100644 index 000000000000..9764c76fba60 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityFireworks.java @@ -0,0 +1,229 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.UUID; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class EntityFireworks extends Entity { + + public static final DataWatcherObject FIREWORK_ITEM = DataWatcher.a(EntityFireworks.class, DataWatcherRegistry.g); + private static final DataWatcherObject b = DataWatcher.a(EntityFireworks.class, DataWatcherRegistry.b); + private int ticksFlown; + public int expectedLifespan; + public UUID spawningEntity; // Paper + private EntityLiving e;public EntityLiving getBoostedEntity() { return e; } // Paper - OBFHELPER + + public EntityFireworks(World world) { + super(EntityTypes.FIREWORK_ROCKET, world); + this.setSize(0.25F, 0.25F); + } + + // Spigot Start + @Override + public void inactiveTick() { + this.ticksFlown += 1; + super.inactiveTick(); + } + // Spigot End + + protected void x_() { + this.datawatcher.register(EntityFireworks.FIREWORK_ITEM, ItemStack.a); + this.datawatcher.register(EntityFireworks.b, 0); + } + + public EntityFireworks(World world, double d0, double d1, double d2, ItemStack itemstack) { + super(EntityTypes.FIREWORK_ROCKET, world); + this.ticksFlown = 0; + this.setSize(0.25F, 0.25F); + this.setPosition(d0, d1, d2); + int i = 1; + + if (!itemstack.isEmpty() && itemstack.hasTag()) { + this.datawatcher.set(EntityFireworks.FIREWORK_ITEM, itemstack.cloneItemStack()); + i += itemstack.a("Fireworks").getByte("Flight"); + } + + this.motX = this.random.nextGaussian() * 0.001D; + this.motZ = this.random.nextGaussian() * 0.001D; + this.motY = 0.05D; + this.expectedLifespan = 10 * i + this.random.nextInt(6) + this.random.nextInt(7); + } + + public EntityFireworks(World world, ItemStack itemstack, EntityLiving entityliving) { + this(world, entityliving.locX, entityliving.locY, entityliving.locZ, itemstack); + this.datawatcher.set(EntityFireworks.b, entityliving.getId()); + this.e = entityliving; + } + + public void tick() { + this.N = this.locX; + this.O = this.locY; + this.P = this.locZ; + super.tick(); + if (this.f()) { + if (this.e == null) { + Entity entity = this.world.getEntity((Integer) this.datawatcher.get(EntityFireworks.b)); + + if (entity instanceof EntityLiving) { + this.e = (EntityLiving) entity; + } + } + + if (this.e != null) { + if (this.e.dc()) { + Vec3D vec3d = this.e.aN(); + double d0 = 1.5D; + double d1 = 0.1D; + + this.e.motX += vec3d.x * 0.1D + (vec3d.x * 1.5D - this.e.motX) * 0.5D; + this.e.motY += vec3d.y * 0.1D + (vec3d.y * 1.5D - this.e.motY) * 0.5D; + this.e.motZ += vec3d.z * 0.1D + (vec3d.z * 1.5D - this.e.motZ) * 0.5D; + } + + this.setPosition(this.e.locX, this.e.locY, this.e.locZ); + this.motX = this.e.motX; + this.motY = this.e.motY; + this.motZ = this.e.motZ; + } + } else { + this.motX *= 1.15D; + this.motZ *= 1.15D; + this.motY += 0.04D; + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + } + + float f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + this.yaw = (float) (MathHelper.c(this.motX, this.motZ) * 57.2957763671875D); + + for (this.pitch = (float) (MathHelper.c(this.motY, (double) f) * 57.2957763671875D); this.pitch - this.lastPitch < -180.0F; this.lastPitch -= 360.0F) { + ; + } + + while (this.pitch - this.lastPitch >= 180.0F) { + this.lastPitch += 360.0F; + } + + while (this.yaw - this.lastYaw < -180.0F) { + this.lastYaw -= 360.0F; + } + + while (this.yaw - this.lastYaw >= 180.0F) { + this.lastYaw += 360.0F; + } + + this.pitch = this.lastPitch + (this.pitch - this.lastPitch) * 0.2F; + this.yaw = this.lastYaw + (this.yaw - this.lastYaw) * 0.2F; + if (this.ticksFlown == 0 && !this.isSilent()) { + this.world.a((EntityHuman) null, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_FIREWORK_ROCKET_LAUNCH, SoundCategory.AMBIENT, 3.0F, 1.0F); + } + + ++this.ticksFlown; + if (this.world.isClientSide && this.ticksFlown % 2 < 2) { + this.world.addParticle(Particles.w, this.locX, this.locY - 0.3D, this.locZ, this.random.nextGaussian() * 0.05D, -this.motY * 0.5D, this.random.nextGaussian() * 0.05D); + } + + if (!this.world.isClientSide && this.ticksFlown > this.expectedLifespan) { + // CraftBukkit start + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this).isCancelled()) { + this.world.broadcastEntityEffect(this, (byte) 17); + this.i(); + } + // CraftBukkit end + this.die(); + } + + } + + private void i() { + float f = 0.0F; + ItemStack itemstack = (ItemStack) this.datawatcher.get(EntityFireworks.FIREWORK_ITEM); + NBTTagCompound nbttagcompound = itemstack.isEmpty() ? null : itemstack.b("Fireworks"); + NBTTagList nbttaglist = nbttagcompound != null ? nbttagcompound.getList("Explosions", 10) : null; + + if (nbttaglist != null && !nbttaglist.isEmpty()) { + f = (float) (5 + nbttaglist.size() * 2); + } + + if (f > 0.0F) { + if (this.e != null) { + CraftEventFactory.entityDamage = this; // CraftBukkit + this.e.damageEntity(DamageSource.FIREWORKS, (float) (5 + nbttaglist.size() * 2)); + CraftEventFactory.entityDamage = null; // CraftBukkit + } + + double d0 = 5.0D; + Vec3D vec3d = new Vec3D(this.locX, this.locY, this.locZ); + List list = this.world.a(EntityLiving.class, this.getBoundingBox().g(5.0D)); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityLiving entityliving = (EntityLiving) iterator.next(); + + if (entityliving != this.e && this.h(entityliving) <= 25.0D) { + boolean flag = false; + + for (int i = 0; i < 2; ++i) { + MovingObjectPosition movingobjectposition = this.world.rayTrace(vec3d, new Vec3D(entityliving.locX, entityliving.locY + (double) entityliving.length * 0.5D * (double) i, entityliving.locZ), FluidCollisionOption.NEVER, true, false); + + if (movingobjectposition == null || movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.MISS) { + flag = true; + break; + } + } + + if (flag) { + float f1 = f * (float) Math.sqrt((5.0D - (double) this.g(entityliving)) / 5.0D); + + CraftEventFactory.entityDamage = this; // CraftBukkit + entityliving.damageEntity(DamageSource.FIREWORKS, f1); + CraftEventFactory.entityDamage = null; // CraftBukkit + } + } + } + } + + } + + public boolean f() { + return (Integer) this.datawatcher.get(EntityFireworks.b) > 0; + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setInt("Life", this.ticksFlown); + nbttagcompound.setInt("LifeTime", this.expectedLifespan); + ItemStack itemstack = (ItemStack) this.datawatcher.get(EntityFireworks.FIREWORK_ITEM); + + if (!itemstack.isEmpty()) { + nbttagcompound.set("FireworksItem", itemstack.save(new NBTTagCompound())); + } + + // Paper start + if (spawningEntity != null) { + nbttagcompound.setUUID("SpawningEntity", spawningEntity); + } + // Paper end + + } + + public void a(NBTTagCompound nbttagcompound) { + this.ticksFlown = nbttagcompound.getInt("Life"); + this.expectedLifespan = nbttagcompound.getInt("LifeTime"); + ItemStack itemstack = ItemStack.a(nbttagcompound.getCompound("FireworksItem")); + + if (!itemstack.isEmpty()) { + this.datawatcher.set(EntityFireworks.FIREWORK_ITEM, itemstack); + } + // Paper start + if (nbttagcompound.hasUUID("SpawningEntity")) { + spawningEntity = nbttagcompound.getUUID("SpawningEntity"); + } + // Paper end + } + + public boolean bk() { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/EntityFish.java b/src/main/java/net/minecraft/server/EntityFish.java new file mode 100644 index 000000000000..5da2d72af9f8 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityFish.java @@ -0,0 +1,196 @@ +package net.minecraft.server; + +public abstract class EntityFish extends EntityWaterAnimal implements IAnimal { + + private static final DataWatcherObject a = DataWatcher.a(EntityFish.class, DataWatcherRegistry.i); + + public EntityFish(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.moveController = new EntityFish.a(this); + } + + public float getHeadHeight() { + return this.length * 0.65F; + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(3.0D); + } + + public boolean isPersistent() { + return this.isFromBucket() || super.isPersistent(); + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + BlockPosition blockposition = new BlockPosition(this); + + return generatoraccess.getType(blockposition).getBlock() == Blocks.WATER && generatoraccess.getType(blockposition.up()).getBlock() == Blocks.WATER ? super.a(generatoraccess, flag) : false; + } + + public boolean isTypeNotPersistent() { + return true; // CraftBukkit + } + + public int dg() { + return 8; + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityFish.a, false); + } + + public boolean isFromBucket() { + return (Boolean) this.datawatcher.get(EntityFish.a); + } + + public void setFromBucket(boolean flag) { + this.datawatcher.set(EntityFish.a, flag); + this.persistent = this.isPersistent(); // CraftBukkit - SPIGOT-4106 update persistence + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("FromBucket", this.isFromBucket()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setFromBucket(nbttagcompound.getBoolean("FromBucket")); + } + + protected void n() { + super.n(); + this.goalSelector.a(0, new PathfinderGoalPanic(this, 1.25D)); + this.goalSelector.a(2, new PathfinderGoalAvoidTarget<>(this, EntityHuman.class, 8.0F, 1.6D, 1.4D, IEntitySelector.f)); + this.goalSelector.a(4, new EntityFish.b(this)); + } + + protected NavigationAbstract b(World world) { + return new NavigationGuardian(this, world); + } + + public void a(float f, float f1, float f2) { + if (this.cP() && this.isInWater()) { + this.a(f, f1, f2, 0.01F); + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + this.motX *= 0.8999999761581421D; + this.motY *= 0.8999999761581421D; + this.motZ *= 0.8999999761581421D; + if (this.getGoalTarget() == null) { + this.motY -= 0.005D; + } + } else { + super.a(f, f1, f2); + } + + } + + public void movementTick() { + if (!this.isInWater() && this.onGround && this.C) { + this.motY += 0.4000000059604645D; + this.motX += (double) ((this.random.nextFloat() * 2.0F - 1.0F) * 0.05F); + this.motZ += (double) ((this.random.nextFloat() * 2.0F - 1.0F) * 0.05F); + this.onGround = false; + this.impulse = true; + this.a(this.dz(), this.cD(), this.cE()); + } + + super.movementTick(); + } + + protected boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (itemstack.getItem() == Items.WATER_BUCKET && this.isAlive()) { + this.a(SoundEffects.ITEM_BUCKET_FILL_FISH, 1.0F, 1.0F); + itemstack.subtract(1); + ItemStack itemstack1 = this.l(); + + this.f(itemstack1); + if (!this.world.isClientSide) { + CriterionTriggers.j.a((EntityPlayer) entityhuman, itemstack1); + } + + if (itemstack.isEmpty()) { + entityhuman.a(enumhand, itemstack1); + } else if (!entityhuman.inventory.pickup(itemstack1)) { + entityhuman.drop(itemstack1, false); + } + + this.die(); + return true; + } else { + return super.a(entityhuman, enumhand); + } + } + + protected void f(ItemStack itemstack) { + if (this.hasCustomName()) { + itemstack.a(this.getCustomName()); + } + + } + + protected abstract ItemStack l(); + + protected boolean dy() { + return true; + } + + protected abstract SoundEffect dz(); + + protected SoundEffect ad() { + return SoundEffects.ENTITY_FISH_SWIM; + } + + static class a extends ControllerMove { + + private final EntityFish i; + + a(EntityFish entityfish) { + super(entityfish); + this.i = entityfish; + } + + public void a() { + if (this.i.a(TagsFluid.WATER)) { + this.i.motY += 0.005D; + } + + if (this.h == ControllerMove.Operation.MOVE_TO && !this.i.getNavigation().p()) { + double d0 = this.b - this.i.locX; + double d1 = this.c - this.i.locY; + double d2 = this.d - this.i.locZ; + double d3 = (double) MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + + d1 /= d3; + float f = (float) (MathHelper.c(d2, d0) * 57.2957763671875D) - 90.0F; + + this.i.yaw = this.a(this.i.yaw, f, 90.0F); + this.i.aQ = this.i.yaw; + float f1 = (float) (this.e * this.i.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue()); + + this.i.o(this.i.cK() + (f1 - this.i.cK()) * 0.125F); + this.i.motY += (double) this.i.cK() * d1 * 0.1D; + } else { + this.i.o(0.0F); + } + } + } + + static class b extends PathfinderGoalRandomSwim { + + private final EntityFish h; + + public b(EntityFish entityfish) { + super(entityfish, 1.0D, 40); + this.h = entityfish; + } + + public boolean a() { + return this.h.dy() && super.a(); + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityFishingHook.java b/src/main/java/net/minecraft/server/EntityFishingHook.java new file mode 100644 index 000000000000..e1c685eede0a --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityFishingHook.java @@ -0,0 +1,548 @@ +package net.minecraft.server; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +// CraftBukkit start +import org.bukkit.entity.Player; +import org.bukkit.entity.FishHook; +import org.bukkit.event.player.PlayerFishEvent; +// CraftBukkit end + +public class EntityFishingHook extends Entity { + + private static final DataWatcherObject b = DataWatcher.a(EntityFishingHook.class, DataWatcherRegistry.b); + private boolean isInGround; + private int d; + public EntityHuman owner; + private int f; + private int g; + private int h; + private int aw; + private float ax; + public Entity hooked; + private EntityFishingHook.HookState ay; + private int az; + private int aA; + + private EntityFishingHook(World world) { + super(EntityTypes.FISHING_BOBBER, world); + this.ay = EntityFishingHook.HookState.FLYING; + } + + public EntityFishingHook(World world, EntityHuman entityhuman) { + this(world); + this.a(entityhuman); + this.k(); + } + + private void a(EntityHuman entityhuman) { + this.setSize(0.25F, 0.25F); + this.ak = true; + this.owner = entityhuman; + this.owner.hookedFish = this; + } + + public void a(int i) { + this.aA = i; + } + + public void b(int i) { + this.az = i; + } + + private void k() { + float f = this.owner.pitch; + float f1 = this.owner.yaw; + float f2 = MathHelper.cos(-f1 * 0.017453292F - 3.1415927F); + float f3 = MathHelper.sin(-f1 * 0.017453292F - 3.1415927F); + float f4 = -MathHelper.cos(-f * 0.017453292F); + float f5 = MathHelper.sin(-f * 0.017453292F); + double d0 = this.owner.locX - (double) f3 * 0.3D; + double d1 = this.owner.locY + (double) this.owner.getHeadHeight(); + double d2 = this.owner.locZ - (double) f2 * 0.3D; + + this.setPositionRotation(d0, d1, d2, f1, f); + this.motX = (double) (-f3); + this.motY = (double) MathHelper.a(-(f5 / f4), -5.0F, 5.0F); + this.motZ = (double) (-f2); + float f6 = MathHelper.sqrt(this.motX * this.motX + this.motY * this.motY + this.motZ * this.motZ); + + this.motX *= 0.6D / (double) f6 + 0.5D + this.random.nextGaussian() * 0.0045D; + this.motY *= 0.6D / (double) f6 + 0.5D + this.random.nextGaussian() * 0.0045D; + this.motZ *= 0.6D / (double) f6 + 0.5D + this.random.nextGaussian() * 0.0045D; + float f7 = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + this.yaw = (float) (MathHelper.c(this.motX, this.motZ) * 57.2957763671875D); + this.pitch = (float) (MathHelper.c(this.motY, (double) f7) * 57.2957763671875D); + this.lastYaw = this.yaw; + this.lastPitch = this.pitch; + } + + protected void x_() { + this.getDataWatcher().register(EntityFishingHook.b, 0); + } + + public void a(DataWatcherObject datawatcherobject) { + if (EntityFishingHook.b.equals(datawatcherobject)) { + int i = (Integer) this.getDataWatcher().get(EntityFishingHook.b); + + this.hooked = i > 0 ? this.world.getEntity(i - 1) : null; + } + + super.a(datawatcherobject); + } + + public void tick() { + super.tick(); + if (this.owner == null) { + this.die(); + } else if (this.world.isClientSide || !this.l()) { + if (this.isInGround) { + ++this.d; + if (this.d >= 1200) { + this.die(); + return; + } + } + + float f = 0.0F; + BlockPosition blockposition = new BlockPosition(this); + Fluid fluid = this.world.getFluid(blockposition); + + if (fluid.a(TagsFluid.WATER)) { + f = fluid.getHeight(); + } + + double d0; + + if (this.ay == EntityFishingHook.HookState.FLYING) { + if (this.hooked != null) { + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + this.ay = EntityFishingHook.HookState.HOOKED_IN_ENTITY; + return; + } + + if (f > 0.0F) { + this.motX *= 0.3D; + this.motY *= 0.2D; + this.motZ *= 0.3D; + this.ay = EntityFishingHook.HookState.BOBBING; + return; + } + + if (!this.world.isClientSide) { + this.n(); + } + + if (!this.isInGround && !this.onGround && !this.positionChanged) { + ++this.f; + } else { + this.f = 0; + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + } + } else { + if (this.ay == EntityFishingHook.HookState.HOOKED_IN_ENTITY) { + if (this.hooked != null) { + if (this.hooked.dead) { + this.hooked = null; + this.ay = EntityFishingHook.HookState.FLYING; + } else { + this.locX = this.hooked.locX; + double d1 = (double) this.hooked.length; + + this.locY = this.hooked.getBoundingBox().minY + d1 * 0.8D; + this.locZ = this.hooked.locZ; + this.setPosition(this.locX, this.locY, this.locZ); + } + } + + return; + } + + if (this.ay == EntityFishingHook.HookState.BOBBING) { + this.motX *= 0.9D; + this.motZ *= 0.9D; + d0 = this.locY + this.motY - (double) blockposition.getY() - (double) f; + if (Math.abs(d0) < 0.01D) { + d0 += Math.signum(d0) * 0.1D; + } + + this.motY -= d0 * (double) this.random.nextFloat() * 0.2D; + if (!this.world.isClientSide && f > 0.0F) { + this.a(blockposition); + } + } + } + + if (!fluid.a(TagsFluid.WATER)) { + this.motY -= 0.03D; + } + + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + this.m(); + d0 = 0.92D; + this.motX *= 0.92D; + this.motY *= 0.92D; + this.motZ *= 0.92D; + this.setPosition(this.locX, this.locY, this.locZ); + + // Paper start - These shouldn't be going through portals + if (this.inPortal()) { + this.die(); + } + // Paper end + } + } + + private boolean l() { + ItemStack itemstack = this.owner.getItemInMainHand(); + ItemStack itemstack1 = this.owner.getItemInOffHand(); + boolean flag = itemstack.getItem() == Items.FISHING_ROD; + boolean flag1 = itemstack1.getItem() == Items.FISHING_ROD; + + if (!this.owner.dead && this.owner.isAlive() && (flag || flag1) && this.h(this.owner) <= 1024.0D) { + return false; + } else { + this.die(); + return true; + } + } + + private void m() { + float f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + this.yaw = (float) (MathHelper.c(this.motX, this.motZ) * 57.2957763671875D); + + for (this.pitch = (float) (MathHelper.c(this.motY, (double) f) * 57.2957763671875D); this.pitch - this.lastPitch < -180.0F; this.lastPitch -= 360.0F) { + ; + } + + while (this.pitch - this.lastPitch >= 180.0F) { + this.lastPitch += 360.0F; + } + + while (this.yaw - this.lastYaw < -180.0F) { + this.lastYaw -= 360.0F; + } + + while (this.yaw - this.lastYaw >= 180.0F) { + this.lastYaw += 360.0F; + } + + this.pitch = this.lastPitch + (this.pitch - this.lastPitch) * 0.2F; + this.yaw = this.lastYaw + (this.yaw - this.lastYaw) * 0.2F; + } + + private void n() { + Vec3D vec3d = new Vec3D(this.locX, this.locY, this.locZ); + Vec3D vec3d1 = new Vec3D(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + MovingObjectPosition movingobjectposition = this.world.rayTrace(vec3d, vec3d1, FluidCollisionOption.NEVER, true, false); + + vec3d = new Vec3D(this.locX, this.locY, this.locZ); + vec3d1 = new Vec3D(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + + // Paper start - Call ProjectileCollideEvent + if (movingobjectposition != null && movingobjectposition.entity != null) { + com.destroystokyo.paper.event.entity.ProjectileCollideEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileCollideEvent(this, movingobjectposition); + if (event.isCancelled()) { + movingobjectposition = null; + } + } + // Paper end + + if (movingobjectposition != null) { + vec3d1 = new Vec3D(movingobjectposition.pos.x, movingobjectposition.pos.y, movingobjectposition.pos.z); + } + + Entity entity = null; + List list = this.world.getEntities(this, this.getBoundingBox().b(this.motX, this.motY, this.motZ).g(1.0D)); + double d0 = 0.0D; + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + Entity entity1 = (Entity) iterator.next(); + + if (this.a(entity1) && (entity1 != this.owner || this.f >= 5)) { + AxisAlignedBB axisalignedbb = entity1.getBoundingBox().g(0.30000001192092896D); + MovingObjectPosition movingobjectposition1 = axisalignedbb.b(vec3d, vec3d1); + + if (movingobjectposition1 != null) { + double d1 = vec3d.distanceSquared(movingobjectposition1.pos); + + if (d1 < d0 || d0 == 0.0D) { + entity = entity1; + d0 = d1; + } + } + } + } + + if (entity != null) { + movingobjectposition = new MovingObjectPosition(entity); + } + + if (movingobjectposition != null && movingobjectposition.type != MovingObjectPosition.EnumMovingObjectType.MISS) { + org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this, movingobjectposition); // Craftbukkit - Call event + if (movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.ENTITY) { + this.hooked = movingobjectposition.entity; + this.o(); + } else { + this.isInGround = true; + } + } + + } + + private void o() { + this.getDataWatcher().set(EntityFishingHook.b, this.hooked.getId() + 1); + } + + private void a(BlockPosition blockposition) { + WorldServer worldserver = (WorldServer) this.world; + int i = 1; + BlockPosition blockposition1 = blockposition.up(); + + if (this.random.nextFloat() < 0.25F && this.world.isRainingAt(blockposition1)) { + ++i; + } + + if (this.random.nextFloat() < 0.5F && !this.world.e(blockposition1)) { + --i; + } + + if (this.g > 0) { + --this.g; + if (this.g <= 0) { + this.h = 0; + this.aw = 0; + // CraftBukkit start + PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.FAILED_ATTEMPT); + this.world.getServer().getPluginManager().callEvent(playerFishEvent); + // CraftBukkit end + } else { + this.motY -= 0.2D * (double) this.random.nextFloat() * (double) this.random.nextFloat(); + } + } else { + float f; + float f1; + float f2; + double d0; + double d1; + double d2; + Block block; + + if (this.aw > 0) { + this.aw -= i; + if (this.aw > 0) { + this.ax = (float) ((double) this.ax + this.random.nextGaussian() * 4.0D); + f = this.ax * 0.017453292F; + f1 = MathHelper.sin(f); + f2 = MathHelper.cos(f); + d0 = this.locX + (double) (f1 * (float) this.aw * 0.1F); + d1 = (double) ((float) MathHelper.floor(this.getBoundingBox().minY) + 1.0F); + d2 = this.locZ + (double) (f2 * (float) this.aw * 0.1F); + block = worldserver.getType(new BlockPosition(d0, d1 - 1.0D, d2)).getBlock(); + if (block == Blocks.WATER) { + if (this.random.nextFloat() < 0.15F) { + worldserver.a(Particles.e, d0, d1 - 0.10000000149011612D, d2, 1, (double) f1, 0.1D, (double) f2, 0.0D); + } + + float f3 = f1 * 0.04F; + float f4 = f2 * 0.04F; + + worldserver.a(Particles.x, d0, d1, d2, 0, (double) f4, 0.01D, (double) (-f3), 1.0D); + worldserver.a(Particles.x, d0, d1, d2, 0, (double) (-f4), 0.01D, (double) f3, 1.0D); + } + } else { + // CraftBukkit start + PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.BITE); + this.world.getServer().getPluginManager().callEvent(playerFishEvent); + if (playerFishEvent.isCancelled()) { + return; + } + // CraftBukkit end + this.motY = (double) (-0.4F * MathHelper.a(this.random, 0.6F, 1.0F)); + this.a(SoundEffects.ENTITY_FISHING_BOBBER_SPLASH, 0.25F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F); + double d3 = this.getBoundingBox().minY + 0.5D; + + worldserver.a(Particles.e, this.locX, d3, this.locZ, (int) (1.0F + this.width * 20.0F), (double) this.width, 0.0D, (double) this.width, 0.20000000298023224D); + worldserver.a(Particles.x, this.locX, d3, this.locZ, (int) (1.0F + this.width * 20.0F), (double) this.width, 0.0D, (double) this.width, 0.20000000298023224D); + this.g = MathHelper.nextInt(this.random, 20, 40); + } + } else if (this.h > 0) { + this.h -= i; + f = 0.15F; + if (this.h < 20) { + f = (float) ((double) f + (double) (20 - this.h) * 0.05D); + } else if (this.h < 40) { + f = (float) ((double) f + (double) (40 - this.h) * 0.02D); + } else if (this.h < 60) { + f = (float) ((double) f + (double) (60 - this.h) * 0.01D); + } + + if (this.random.nextFloat() < f) { + f1 = MathHelper.a(this.random, 0.0F, 360.0F) * 0.017453292F; + f2 = MathHelper.a(this.random, 25.0F, 60.0F); + d0 = this.locX + (double) (MathHelper.sin(f1) * f2 * 0.1F); + d1 = (double) ((float) MathHelper.floor(this.getBoundingBox().minY) + 1.0F); + d2 = this.locZ + (double) (MathHelper.cos(f1) * f2 * 0.1F); + block = worldserver.getType(new BlockPosition((int) d0, (int) d1 - 1, (int) d2)).getBlock(); + if (block == Blocks.WATER) { + worldserver.a(Particles.R, d0, d1, d2, 2 + this.random.nextInt(2), 0.10000000149011612D, 0.0D, 0.10000000149011612D, 0.0D); + } + } + + if (this.h <= 0) { + this.ax = MathHelper.a(this.random, 0.0F, 360.0F); + this.aw = MathHelper.nextInt(this.random, 20, 80); + } + } else { + this.h = MathHelper.nextInt(this.random, world.paperConfig.fishingMinTicks, world.paperConfig.fishingMaxTicks); // Paper + this.h -= this.aA * 20 * 5; + } + } + + } + + protected boolean a(Entity entity) { + return entity.isInteractable() || entity instanceof EntityItem; + } + + public void b(NBTTagCompound nbttagcompound) {} + + public void a(NBTTagCompound nbttagcompound) {} + + public int b(ItemStack itemstack) { + if (!this.world.isClientSide && this.owner != null) { + int i = 0; + + if (this.hooked != null) { + // CraftBukkit start + PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), this.hooked.getBukkitEntity(), (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_ENTITY); + this.world.getServer().getPluginManager().callEvent(playerFishEvent); + + if (playerFishEvent.isCancelled()) { + return 0; + } + // CraftBukkit end + this.f(); + CriterionTriggers.D.a((EntityPlayer) this.owner, itemstack, this, Collections.emptyList()); + this.world.broadcastEntityEffect(this, (byte) 31); + i = this.hooked instanceof EntityItem ? 3 : 5; + } else if (this.g > 0) { + LootTableInfo.Builder loottableinfo_builder = (new LootTableInfo.Builder((WorldServer) this.world)).position(new BlockPosition(this)); + + loottableinfo_builder.luck((float) this.az + this.owner.dJ()); + List list = this.world.getMinecraftServer().getLootTableRegistry().getLootTable(LootTables.aO).populateLoot(this.random, loottableinfo_builder.build()); + + CriterionTriggers.D.a((EntityPlayer) this.owner, itemstack, this, list); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + ItemStack itemstack1 = (ItemStack) iterator.next(); + EntityItem entityitem = new EntityItem(this.world, this.locX, this.locY, this.locZ, itemstack1); + // CraftBukkit start + PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), entityitem.getBukkitEntity(), (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_FISH); + playerFishEvent.setExpToDrop(this.random.nextInt(6) + 1); + this.world.getServer().getPluginManager().callEvent(playerFishEvent); + + if (playerFishEvent.isCancelled()) { + return 0; + } + // CraftBukkit end + double d0 = this.owner.locX - this.locX; + double d1 = this.owner.locY - this.locY; + double d2 = this.owner.locZ - this.locZ; + double d3 = (double) MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + double d4 = 0.1D; + + entityitem.motX = d0 * 0.1D; + entityitem.motY = d1 * 0.1D + (double) MathHelper.sqrt(d3) * 0.08D; + entityitem.motZ = d2 * 0.1D; + this.world.addEntity(entityitem); + // CraftBukkit start - this.random.nextInt(6) + 1 -> playerFishEvent.getExpToDrop() + if (playerFishEvent.getExpToDrop() > 0) { + this.owner.world.addEntity(new EntityExperienceOrb(this.owner.world, this.owner.locX, this.owner.locY + 0.5D, this.owner.locZ + 0.5D, playerFishEvent.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.FISHING, this.owner, this)); // Paper + } + // CraftBukkit end + if (itemstack1.getItem().a(TagsItem.FISHES)) { + this.owner.a(StatisticList.FISH_CAUGHT, 1); + } + } + + i = 1; + } + + if (this.isInGround) { + // CraftBukkit start + PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.IN_GROUND); + this.world.getServer().getPluginManager().callEvent(playerFishEvent); + + if (playerFishEvent.isCancelled()) { + return 0; + } + // CraftBukkit end + i = 2; + } + // CraftBukkit start + if (i == 0) { + PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.FAILED_ATTEMPT); + this.world.getServer().getPluginManager().callEvent(playerFishEvent); + if (playerFishEvent.isCancelled()) { + return 0; + } + } + // CraftBukkit end + + this.die(); + return i; + } else { + return 0; + } + } + + protected void f() { + if (this.owner != null) { + double d0 = this.owner.locX - this.locX; + double d1 = this.owner.locY - this.locY; + double d2 = this.owner.locZ - this.locZ; + double d3 = 0.1D; + + this.hooked.motX += d0 * 0.1D; + this.hooked.motY += d1 * 0.1D; + this.hooked.motZ += d2 * 0.1D; + } + } + + protected boolean playStepSound() { + return false; + } + + public void die() { + super.die(); + if (this.owner != null) { + this.owner.hookedFish = null; + } + + } + + public EntityHuman i() { + return this.owner; + } + + public boolean bm() { + return false; + } + + static enum HookState { + + FLYING, HOOKED_IN_ENTITY, BOBBING; + + private HookState() {} + } +} diff --git a/src/main/java/net/minecraft/server/EntityGhast.java b/src/main/java/net/minecraft/server/EntityGhast.java new file mode 100644 index 000000000000..5fac201b9f90 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityGhast.java @@ -0,0 +1,292 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; + +public class EntityGhast extends EntityFlying implements IMonster { + + private static final DataWatcherObject a = DataWatcher.a(EntityGhast.class, DataWatcherRegistry.i); + private int b = 1; + + public EntityGhast(World world) { + super(EntityTypes.GHAST, world); + this.setSize(4.0F, 4.0F); + this.fireProof = true; + this.b_ = 5; + this.moveController = new EntityGhast.ControllerGhast(this); + } + + protected void n() { + this.goalSelector.a(5, new EntityGhast.PathfinderGoalGhastIdleMove(this)); + this.goalSelector.a(7, new EntityGhast.PathfinderGoalGhastMoveTowardsTarget(this)); + this.goalSelector.a(7, new EntityGhast.PathfinderGoalGhastAttackTarget(this)); + this.targetSelector.a(1, new PathfinderGoalTargetNearestPlayer(this)); + } + + public void a(boolean flag) { + this.datawatcher.set(EntityGhast.a, flag); + } + + public int getPower() { + return this.b; + } + + public void tick() { + super.tick(); + if (!this.world.isClientSide && this.world.getDifficulty() == EnumDifficulty.PEACEFUL) { + this.die(); + } + + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else if (damagesource.j() instanceof EntityLargeFireball && damagesource.getEntity() instanceof EntityHuman) { + super.damageEntity(damagesource, 1000.0F); + return true; + } else { + return super.damageEntity(damagesource, f); + } + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityGhast.a, false); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(10.0D); + this.getAttributeInstance(GenericAttributes.FOLLOW_RANGE).setValue(100.0D); + } + + public SoundCategory bV() { + return SoundCategory.HOSTILE; + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_GHAST_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_GHAST_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_GHAST_DEATH; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.aq; + } + + protected float cD() { + return 10.0F; + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + return this.random.nextInt(20) == 0 && super.a(generatoraccess, flag) && generatoraccess.getDifficulty() != EnumDifficulty.PEACEFUL; + } + + public int dg() { + return 1; + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("ExplosionPower", this.b); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKeyOfType("ExplosionPower", 99)) { + this.b = nbttagcompound.getInt("ExplosionPower"); + } + + } + + public float getHeadHeight() { + return 2.6F; + } + + static class PathfinderGoalGhastAttackTarget extends PathfinderGoal { + + private final EntityGhast ghast; + public int a; + + public PathfinderGoalGhastAttackTarget(EntityGhast entityghast) { + this.ghast = entityghast; + } + + public boolean a() { + return this.ghast.getGoalTarget() != null; + } + + public void c() { + this.a = 0; + } + + public void d() { + this.ghast.a(false); + } + + public void e() { + EntityLiving entityliving = this.ghast.getGoalTarget(); + double d0 = 64.0D; + + if (entityliving.h(this.ghast) < 4096.0D && this.ghast.hasLineOfSight(entityliving)) { + World world = this.ghast.world; + + ++this.a; + if (this.a == 10) { + world.a((EntityHuman) null, 1015, new BlockPosition(this.ghast), 0); + } + + if (this.a == 20) { + double d1 = 4.0D; + Vec3D vec3d = this.ghast.f(1.0F); + double d2 = entityliving.locX - (this.ghast.locX + vec3d.x * 4.0D); + double d3 = entityliving.getBoundingBox().minY + (double) (entityliving.length / 2.0F) - (0.5D + this.ghast.locY + (double) (this.ghast.length / 2.0F)); + double d4 = entityliving.locZ - (this.ghast.locZ + vec3d.z * 4.0D); + + world.a((EntityHuman) null, 1016, new BlockPosition(this.ghast), 0); + EntityLargeFireball entitylargefireball = new EntityLargeFireball(world, this.ghast, d2, d3, d4); + + // CraftBukkit - set bukkitYield when setting explosionpower + entitylargefireball.bukkitYield = entitylargefireball.yield = this.ghast.getPower(); + entitylargefireball.locX = this.ghast.locX + vec3d.x * 4.0D; + entitylargefireball.locY = this.ghast.locY + (double) (this.ghast.length / 2.0F) + 0.5D; + entitylargefireball.locZ = this.ghast.locZ + vec3d.z * 4.0D; + world.addEntity(entitylargefireball); + this.a = -40; + } + } else if (this.a > 0) { + --this.a; + } + + this.ghast.a(this.a > 10); + } + } + + static class PathfinderGoalGhastMoveTowardsTarget extends PathfinderGoal { + + private final EntityGhast a; + + public PathfinderGoalGhastMoveTowardsTarget(EntityGhast entityghast) { + this.a = entityghast; + this.a(2); + } + + public boolean a() { + return true; + } + + public void e() { + if (this.a.getGoalTarget() == null) { + this.a.yaw = -((float) MathHelper.c(this.a.motX, this.a.motZ)) * 57.295776F; + this.a.aQ = this.a.yaw; + } else { + EntityLiving entityliving = this.a.getGoalTarget(); + double d0 = 64.0D; + + if (entityliving.h(this.a) < 4096.0D) { + double d1 = entityliving.locX - this.a.locX; + double d2 = entityliving.locZ - this.a.locZ; + + this.a.yaw = -((float) MathHelper.c(d1, d2)) * 57.295776F; + this.a.aQ = this.a.yaw; + } + } + + } + } + + static class PathfinderGoalGhastIdleMove extends PathfinderGoal { + + private final EntityGhast a; + + public PathfinderGoalGhastIdleMove(EntityGhast entityghast) { + this.a = entityghast; + this.a(1); + } + + public boolean a() { + ControllerMove controllermove = this.a.getControllerMove(); + + if (!controllermove.b()) { + return true; + } else { + double d0 = controllermove.d() - this.a.locX; + double d1 = controllermove.e() - this.a.locY; + double d2 = controllermove.f() - this.a.locZ; + double d3 = d0 * d0 + d1 * d1 + d2 * d2; + + return d3 < 1.0D || d3 > 3600.0D; + } + } + + public boolean b() { + return false; + } + + public void c() { + Random random = this.a.getRandom(); + double d0 = this.a.locX + (double) ((random.nextFloat() * 2.0F - 1.0F) * 16.0F); + double d1 = this.a.locY + (double) ((random.nextFloat() * 2.0F - 1.0F) * 16.0F); + double d2 = this.a.locZ + (double) ((random.nextFloat() * 2.0F - 1.0F) * 16.0F); + + this.a.getControllerMove().a(d0, d1, d2, 1.0D); + } + } + + static class ControllerGhast extends ControllerMove { + + private final EntityGhast i; + private int j; + + public ControllerGhast(EntityGhast entityghast) { + super(entityghast); + this.i = entityghast; + } + + public void a() { + if (this.h == ControllerMove.Operation.MOVE_TO) { + double d0 = this.b - this.i.locX; + double d1 = this.c - this.i.locY; + double d2 = this.d - this.i.locZ; + double d3 = d0 * d0 + d1 * d1 + d2 * d2; + + if (this.j-- <= 0) { + this.j += this.i.getRandom().nextInt(5) + 2; + d3 = (double) MathHelper.sqrt(d3); + if (this.b(this.b, this.c, this.d, d3)) { + this.i.motX += d0 / d3 * 0.1D; + this.i.motY += d1 / d3 * 0.1D; + this.i.motZ += d2 / d3 * 0.1D; + } else { + this.h = ControllerMove.Operation.WAIT; + } + } + + } + } + + private boolean b(double d0, double d1, double d2, double d3) { + double d4 = (d0 - this.i.locX) / d3; + double d5 = (d1 - this.i.locY) / d3; + double d6 = (d2 - this.i.locZ) / d3; + AxisAlignedBB axisalignedbb = this.i.getBoundingBox(); + + for (int i = 1; (double) i < d3; ++i) { + axisalignedbb = axisalignedbb.d(d4, d5, d6); + if (!this.i.world.getCubes(this.i, axisalignedbb)) { + return false; + } + } + + return true; + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityGuardianElder.java b/src/main/java/net/minecraft/server/EntityGuardianElder.java new file mode 100644 index 000000000000..6ccc15e9dd0c --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityGuardianElder.java @@ -0,0 +1,80 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; + +public class EntityGuardianElder extends EntityGuardian { + + public EntityGuardianElder(World world) { + super(EntityTypes.ELDER_GUARDIAN, world); + this.setSize(this.width * 2.35F, this.length * 2.35F); + this.di(); + if (this.goalRandomStroll != null) { + this.goalRandomStroll.setTimeBetweenMovement(400); + } + + } + + public void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.30000001192092896D); + this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue(8.0D); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(80.0D); + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.E; + } + + public int l() { + return 60; + } + + protected SoundEffect D() { + return this.aq() ? SoundEffects.ENTITY_ELDER_GUARDIAN_AMBIENT : SoundEffects.ENTITY_ELDER_GUARDIAN_AMBIENT_LAND; + } + + protected SoundEffect d(DamageSource damagesource) { + return this.aq() ? SoundEffects.ENTITY_ELDER_GUARDIAN_HURT : SoundEffects.ENTITY_ELDER_GUARDIAN_HURT_LAND; + } + + protected SoundEffect cs() { + return this.aq() ? SoundEffects.ENTITY_ELDER_GUARDIAN_DEATH : SoundEffects.ENTITY_ELDER_GUARDIAN_DEATH_LAND; + } + + protected SoundEffect dA() { + return SoundEffects.ENTITY_ELDER_GUARDIAN_FLOP; + } + + protected void mobTick() { + super.mobTick(); + boolean flag = true; + + if ((this.ticksLived + this.getId()) % 1200 == 0) { + MobEffectList mobeffectlist = MobEffects.SLOWER_DIG; + List list = this.world.b(EntityPlayer.class, (entityplayer) -> { + return this.h(entityplayer) < 2500.0D && entityplayer.playerInteractManager.c(); + }); + boolean flag1 = true; + boolean flag2 = true; + boolean flag3 = true; + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + if (!entityplayer.hasEffect(mobeffectlist) || entityplayer.getEffect(mobeffectlist).getAmplifier() < 2 || entityplayer.getEffect(mobeffectlist).getDuration() < 1200) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutGameStateChange(10, 0.0F)); + entityplayer.addEffect(new MobEffect(mobeffectlist, 6000, 2), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit + } + } + } + + if (!this.dw()) { + this.a(new BlockPosition(this), 16); + } + + } +} diff --git a/src/main/java/net/minecraft/server/EntityHanging.java b/src/main/java/net/minecraft/server/EntityHanging.java new file mode 100644 index 000000000000..381140eeef63 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityHanging.java @@ -0,0 +1,299 @@ +package net.minecraft.server; + +import java.util.function.Predicate; +import javax.annotation.Nullable; +import org.apache.commons.lang3.Validate; + +// CraftBukkit start +import org.bukkit.entity.Hanging; +import org.bukkit.event.hanging.HangingBreakByEntityEvent; +import org.bukkit.event.hanging.HangingBreakEvent; +// CraftBukkit end + +public abstract class EntityHanging extends Entity { + + protected static final Predicate a = (entity) -> { + return entity instanceof EntityHanging; + }; + private int d; + public BlockPosition blockPosition; + @Nullable + public EnumDirection direction; + + protected EntityHanging(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.setSize(0.5F, 0.5F); + } + + protected EntityHanging(EntityTypes entitytypes, World world, BlockPosition blockposition) { + this(entitytypes, world); + this.blockPosition = blockposition; + } + + protected void x_() {} + + public void setDirection(EnumDirection enumdirection) { + Validate.notNull(enumdirection); + Validate.isTrue(enumdirection.k().c()); + this.direction = enumdirection; + this.yaw = (float) (this.direction.get2DRotationValue() * 90); + this.lastYaw = this.yaw; + this.updateBoundingBox(); + } + + // CraftBukkit start - break out BB calc into own method + public static AxisAlignedBB calculateBoundingBox(Entity entity, BlockPosition blockPosition, EnumDirection direction, int width, int height) { + double d0 = (double) blockPosition.getX() + 0.5D; + double d1 = (double) blockPosition.getY() + 0.5D; + double d2 = (double) blockPosition.getZ() + 0.5D; + double d3 = 0.46875D; + double d4 = a(width); + double d5 = a(height); + + d0 -= (double) direction.getAdjacentX() * 0.46875D; + d2 -= (double) direction.getAdjacentZ() * 0.46875D; + d1 += d5; + EnumDirection enumdirection = direction.f(); + + d0 += d4 * (double) enumdirection.getAdjacentX(); + d2 += d4 * (double) enumdirection.getAdjacentZ(); + if (entity != null) { + entity.locX = d0; + entity.locY = d1; + entity.locZ = d2; + } + double d6 = (double) width; + double d7 = (double) height; + double d8 = (double) width; + + if (direction.k() == EnumDirection.EnumAxis.Z) { + d8 = 1.0D; + } else { + d6 = 1.0D; + } + + d6 /= 32.0D; + d7 /= 32.0D; + d8 /= 32.0D; + return new AxisAlignedBB(d0 - d6, d1 - d7, d2 - d8, d0 + d6, d1 + d7, d2 + d8); + } + + protected void updateBoundingBox() { + if (this.direction != null) { + // CraftBukkit start code moved in to calculateBoundingBox + this.a(calculateBoundingBox(this, this.blockPosition, this.direction, this.getWidth(), this.getHeight())); + // CraftBukkit end + } + } + + private static double a(int i) { // CraftBukkit - static + return i % 32 == 0 ? 0.5D : 0.0D; + } + + public void tick() { + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + if (this.d++ == this.world.spigotConfig.hangingTickFrequency && !this.world.isClientSide) { // Spigot + this.d = 0; + if (!this.dead && !this.survives()) { + // CraftBukkit start - fire break events + Material material = this.world.getType(new BlockPosition(this)).getMaterial(); + HangingBreakEvent.RemoveCause cause; + + if (!material.equals(Material.AIR)) { + // TODO: This feels insufficient to catch 100% of suffocation cases + cause = HangingBreakEvent.RemoveCause.OBSTRUCTION; + } else { + cause = HangingBreakEvent.RemoveCause.PHYSICS; + } + + HangingBreakEvent event = new HangingBreakEvent((Hanging) this.getBukkitEntity(), cause); + this.world.getServer().getPluginManager().callEvent(event); + + if (dead || event.isCancelled()) { + return; + } + // CraftBukkit end + this.die(); + this.a((Entity) null); + } + } + + } + + public boolean survives() { + if (!this.world.getCubes(this, this.getBoundingBox())) { + return false; + } else { + int i = Math.max(1, this.getWidth() / 16); + int j = Math.max(1, this.getHeight() / 16); + BlockPosition blockposition = this.blockPosition.shift(this.direction.opposite()); + EnumDirection enumdirection = this.direction.f(); + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + for (int k = 0; k < i; ++k) { + for (int l = 0; l < j; ++l) { + int i1 = (i - 1) / -2; + int j1 = (j - 1) / -2; + + blockposition_mutableblockposition.g(blockposition).c(enumdirection, k + i1).c(EnumDirection.UP, l + j1); + IBlockData iblockdata = this.world.getType(blockposition_mutableblockposition); + + if (!iblockdata.getMaterial().isBuildable() && !BlockDiodeAbstract.isDiode(iblockdata)) { + return false; + } + } + } + + return this.world.getEntities(this, this.getBoundingBox(), EntityHanging.a).isEmpty(); + } + } + + public boolean isInteractable() { + return true; + } + + public boolean t(Entity entity) { + return entity instanceof EntityHuman ? this.damageEntity(DamageSource.playerAttack((EntityHuman) entity), 0.0F) : false; + } + + public EnumDirection getDirection() { + return this.direction; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else { + if (!this.dead && !this.world.isClientSide) { + // CraftBukkit start - fire break events + HangingBreakEvent event = new HangingBreakEvent((Hanging) this.getBukkitEntity(), HangingBreakEvent.RemoveCause.DEFAULT); + if (damagesource.getEntity() != null) { + event = new HangingBreakByEntityEvent((Hanging) this.getBukkitEntity(), damagesource.getEntity() == null ? null : damagesource.getEntity().getBukkitEntity(), damagesource.isExplosion() ? HangingBreakEvent.RemoveCause.EXPLOSION : HangingBreakEvent.RemoveCause.ENTITY); + } else if (damagesource.isExplosion()) { + event = new HangingBreakEvent((Hanging) this.getBukkitEntity(), HangingBreakEvent.RemoveCause.EXPLOSION); + } + + this.world.getServer().getPluginManager().callEvent(event); + + if (this.dead || event.isCancelled()) { + return true; + } + // CraftBukkit end + + this.die(); + this.aA(); + this.a(damagesource.getEntity()); + } + + return true; + } + } + + public void move(EnumMoveType enummovetype, double d0, double d1, double d2) { + if (!this.world.isClientSide && !this.dead && d0 * d0 + d1 * d1 + d2 * d2 > 0.0D) { + if (this.dead) return; // CraftBukkit + + // CraftBukkit start - fire break events + // TODO - Does this need its own cause? Seems to only be triggered by pistons + HangingBreakEvent event = new HangingBreakEvent((Hanging) this.getBukkitEntity(), HangingBreakEvent.RemoveCause.PHYSICS); + this.world.getServer().getPluginManager().callEvent(event); + + if (this.dead || event.isCancelled()) { + return; + } + // CraftBukkit end + + this.die(); + this.a((Entity) null); + } + + } + + public void f(double d0, double d1, double d2) { + if (false && !this.world.isClientSide && !this.dead && d0 * d0 + d1 * d1 + d2 * d2 > 0.0D) { // CraftBukkit - not needed + this.die(); + this.a((Entity) null); + } + + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setByte("Facing", (byte) this.direction.get2DRotationValue()); + BlockPosition blockposition = this.getBlockPosition(); + + nbttagcompound.setInt("TileX", blockposition.getX()); + nbttagcompound.setInt("TileY", blockposition.getY()); + nbttagcompound.setInt("TileZ", blockposition.getZ()); + } + + public void a(NBTTagCompound nbttagcompound) { + this.blockPosition = new BlockPosition(nbttagcompound.getInt("TileX"), nbttagcompound.getInt("TileY"), nbttagcompound.getInt("TileZ")); + this.setDirection(EnumDirection.fromType2(nbttagcompound.getByte("Facing"))); + } + + public abstract int getWidth(); + + public abstract int getHeight(); + + public abstract void a(@Nullable Entity entity); + + public abstract void m(); + + public EntityItem a(ItemStack itemstack, float f) { + EntityItem entityitem = new EntityItem(this.world, this.locX + (double) ((float) this.direction.getAdjacentX() * 0.15F), this.locY + (double) f, this.locZ + (double) ((float) this.direction.getAdjacentZ() * 0.15F), itemstack); + + entityitem.n(); + this.world.addEntity(entityitem); + return entityitem; + } + + protected boolean aD() { + return false; + } + + public void setPosition(double d0, double d1, double d2) { + this.blockPosition = new BlockPosition(d0, d1, d2); + this.updateBoundingBox(); + this.impulse = true; + } + + public BlockPosition getBlockPosition() { + return this.blockPosition; + } + + public float a(EnumBlockRotation enumblockrotation) { + if (this.direction != null && this.direction.k() != EnumDirection.EnumAxis.Y) { + switch (enumblockrotation) { + case CLOCKWISE_180: + this.direction = this.direction.opposite(); + break; + case COUNTERCLOCKWISE_90: + this.direction = this.direction.f(); + break; + case CLOCKWISE_90: + this.direction = this.direction.e(); + } + } + + float f = MathHelper.g(this.yaw); + + switch (enumblockrotation) { + case CLOCKWISE_180: + return f + 180.0F; + case COUNTERCLOCKWISE_90: + return f + 90.0F; + case CLOCKWISE_90: + return f + 270.0F; + default: + return f; + } + } + + public float a(EnumBlockMirror enumblockmirror) { + return this.a(enumblockmirror.a(this.direction)); + } + + public void onLightningStrike(EntityLightning entitylightning) {} +} diff --git a/src/main/java/net/minecraft/server/EntityHorse.java b/src/main/java/net/minecraft/server/EntityHorse.java new file mode 100644 index 000000000000..1b9425f3e688 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityHorse.java @@ -0,0 +1,276 @@ +package net.minecraft.server; + +import java.util.UUID; +import javax.annotation.Nullable; + +public class EntityHorse extends EntityHorseAbstract { + + private static final UUID bM = UUID.fromString("556E1665-8B10-40C8-8F9D-CF9B1667F295"); + private static final DataWatcherObject bN = DataWatcher.a(EntityHorse.class, DataWatcherRegistry.b); + private static final DataWatcherObject bO = DataWatcher.a(EntityHorse.class, DataWatcherRegistry.b); + private static final String[] bP = new String[] { "textures/entity/horse/horse_white.png", "textures/entity/horse/horse_creamy.png", "textures/entity/horse/horse_chestnut.png", "textures/entity/horse/horse_brown.png", "textures/entity/horse/horse_black.png", "textures/entity/horse/horse_gray.png", "textures/entity/horse/horse_darkbrown.png"}; + private static final String[] bQ = new String[] { "hwh", "hcr", "hch", "hbr", "hbl", "hgr", "hdb"}; + private static final String[] bR = new String[] { null, "textures/entity/horse/horse_markings_white.png", "textures/entity/horse/horse_markings_whitefield.png", "textures/entity/horse/horse_markings_whitedots.png", "textures/entity/horse/horse_markings_blackdots.png"}; + private static final String[] bS = new String[] { "", "wo_", "wmo", "wdo", "bdo"}; + private String bT; + private final String[] bU = new String[3]; + + public EntityHorse(World world) { + super(EntityTypes.HORSE, world); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityHorse.bN, 0); + this.datawatcher.register(EntityHorse.bO, EnumHorseArmor.NONE.a()); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("Variant", this.getVariant()); + if (!this.inventoryChest.getItem(1).isEmpty()) { + nbttagcompound.set("ArmorItem", this.inventoryChest.getItem(1).save(new NBTTagCompound())); + } + + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setVariant(nbttagcompound.getInt("Variant")); + if (nbttagcompound.hasKeyOfType("ArmorItem", 10)) { + ItemStack itemstack = ItemStack.a(nbttagcompound.getCompound("ArmorItem")); + + if (!itemstack.isEmpty() && EnumHorseArmor.b(itemstack.getItem())) { + this.inventoryChest.setItem(1, itemstack); + } + } + + this.dS(); + } + + public void setVariant(int i) { + this.datawatcher.set(EntityHorse.bN, i); + this.eg(); + } + + public int getVariant() { + return (Integer) this.datawatcher.get(EntityHorse.bN); + } + + private void eg() { + this.bT = null; + } + + protected void dS() { + super.dS(); + this.h(this.inventoryChest.getItem(1)); + } + + public void h(ItemStack itemstack) { + EnumHorseArmor enumhorsearmor = EnumHorseArmor.a(itemstack); + + this.datawatcher.set(EntityHorse.bO, enumhorsearmor.a()); + this.eg(); + if (!this.world.isClientSide) { + this.getAttributeInstance(GenericAttributes.h).b(EntityHorse.bM); + int i = enumhorsearmor.c(); + + if (i != 0) { + this.getAttributeInstance(GenericAttributes.h).b((new AttributeModifier(EntityHorse.bM, "Horse armor bonus", (double) i, 0)).a(false)); + } + } + + } + + public EnumHorseArmor dH() { + return EnumHorseArmor.a((Integer) this.datawatcher.get(EntityHorse.bO)); + } + + public void a(IInventory iinventory) { + EnumHorseArmor enumhorsearmor = this.dH(); + + super.a(iinventory); + EnumHorseArmor enumhorsearmor1 = this.dH(); + + if (this.ticksLived > 20 && enumhorsearmor != enumhorsearmor1 && enumhorsearmor1 != EnumHorseArmor.NONE) { + this.a(SoundEffects.ENTITY_HORSE_ARMOR, 0.5F, 1.0F); + } + + } + + protected void a(SoundEffectType soundeffecttype) { + super.a(soundeffecttype); + if (this.random.nextInt(10) == 0) { + this.a(SoundEffects.ENTITY_HORSE_BREATHE, soundeffecttype.a() * 0.6F, soundeffecttype.b()); + } + + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue((double) this.ec()); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(this.ee()); + this.getAttributeInstance(EntityHorse.attributeJumpStrength).setValue(this.ed()); + } + + public void tick() { + super.tick(); + if (this.world.isClientSide && this.datawatcher.a()) { + this.datawatcher.e(); + this.eg(); + } + + } + + protected SoundEffect D() { + super.D(); + return SoundEffects.ENTITY_HORSE_AMBIENT; + } + + protected SoundEffect cs() { + super.cs(); + return SoundEffects.ENTITY_HORSE_DEATH; + } + + protected SoundEffect d(DamageSource damagesource) { + super.d(damagesource); + return SoundEffects.ENTITY_HORSE_HURT; + } + + protected SoundEffect dB() { + super.dB(); + return SoundEffects.ENTITY_HORSE_ANGRY; + } + + protected MinecraftKey getDefaultLootTable() { + return LootTables.N; + } + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + boolean flag = !itemstack.isEmpty(); + + if (flag && itemstack.getItem() instanceof ItemMonsterEgg) { + return super.a(entityhuman, enumhand); + } else { + if (!this.isBaby()) { + if (this.isTamed() && entityhuman.isSneaking()) { + this.c(entityhuman); + return true; + } + + if (this.isVehicle()) { + return super.a(entityhuman, enumhand); + } + } + + if (flag) { + if (this.b(entityhuman, itemstack)) { + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + return true; + } + + if (itemstack.a(entityhuman, (EntityLiving) this, enumhand)) { + return true; + } + + if (!this.isTamed()) { + this.dZ(); + return true; + } + + boolean flag1 = EnumHorseArmor.a(itemstack) != EnumHorseArmor.NONE; + boolean flag2 = !this.isBaby() && !this.dV() && itemstack.getItem() == Items.SADDLE; + + if (flag1 || flag2) { + this.c(entityhuman); + return true; + } + } + + if (this.isBaby()) { + return super.a(entityhuman, enumhand); + } else { + this.g(entityhuman); + return true; + } + } + } + + public boolean mate(EntityAnimal entityanimal) { + return entityanimal == this ? false : (!(entityanimal instanceof EntityHorseDonkey) && !(entityanimal instanceof EntityHorse) ? false : this.eb() && ((EntityHorseAbstract) entityanimal).eb()); + } + + public EntityAgeable createChild(EntityAgeable entityageable) { + Object object; + + if (entityageable instanceof EntityHorseDonkey) { + object = EntityTypes.MULE.create(world); // Paper + } else { + EntityHorse entityhorse = (EntityHorse) entityageable; + + object = EntityTypes.HORSE.create(world); // Paper + int i = this.random.nextInt(9); + int j; + + if (i < 4) { + j = this.getVariant() & 255; + } else if (i < 8) { + j = entityhorse.getVariant() & 255; + } else { + j = this.random.nextInt(7); + } + + int k = this.random.nextInt(5); + + if (k < 2) { + j |= this.getVariant() & '\uff00'; + } else if (k < 4) { + j |= entityhorse.getVariant() & '\uff00'; + } else { + j |= this.random.nextInt(5) << 8 & '\uff00'; + } + + ((EntityHorse) object).setVariant(j); + } + + this.a(entityageable, (EntityHorseAbstract) object); + return (EntityAgeable) object; + } + + public boolean ef() { + return true; + } + + public boolean g(ItemStack itemstack) { + return EnumHorseArmor.b(itemstack.getItem()); + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + Object object = super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + int i; + + if (object instanceof EntityHorse.a) { + i = ((EntityHorse.a) object).a; + } else { + i = this.random.nextInt(7); + object = new EntityHorse.a(i); + } + + this.setVariant(i | this.random.nextInt(5) << 8); + return (GroupDataEntity) object; + } + + public static class a implements GroupDataEntity { + + public int a; + + public a(int i) { + this.a = i; + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityHorseAbstract.java b/src/main/java/net/minecraft/server/EntityHorseAbstract.java new file mode 100644 index 000000000000..953277631a8d --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityHorseAbstract.java @@ -0,0 +1,913 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Predicate; +import javax.annotation.Nullable; +import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; // CraftBukkit + +public abstract class EntityHorseAbstract extends EntityAnimal implements IInventoryListener, IJumpable { + + private static final Predicate bM = (entity) -> { + return entity instanceof EntityHorseAbstract && ((EntityHorseAbstract) entity).hasReproduced(); + }; + public static final IAttribute attributeJumpStrength = (new AttributeRanged((IAttribute) null, "horse.jumpStrength", 0.7D, 0.0D, 2.0D)).a("Jump Strength").a(true); + private static final DataWatcherObject bN = DataWatcher.a(EntityHorseAbstract.class, DataWatcherRegistry.a); + private static final DataWatcherObject> bO = DataWatcher.a(EntityHorseAbstract.class, DataWatcherRegistry.o); + private int bP; + private int bQ; + private int bR; + public int bD; + public int bE; + protected boolean bG; + public InventoryHorseChest inventoryChest; + protected int bI; + protected float jumpPower; + private boolean canSlide; + private float bT; + private float bU; + private float bV; + private float bW; + private float bX; + private float bY; + protected boolean bK = true; + protected int bL; + public int maxDomestication = 100; // CraftBukkit - store max domestication value + + protected EntityHorseAbstract(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.setSize(1.3964844F, 1.6F); + this.Q = 1.0F; + this.loadChest(); + } + + protected void n() { + this.goalSelector.a(1, new PathfinderGoalPanic(this, 1.2D)); + this.goalSelector.a(1, new PathfinderGoalTame(this, 1.2D)); + this.goalSelector.a(2, new PathfinderGoalBreed(this, 1.0D, EntityHorseAbstract.class)); + this.goalSelector.a(4, new PathfinderGoalFollowParent(this, 1.0D)); + this.goalSelector.a(6, new PathfinderGoalRandomStrollLand(this, 0.7D)); + this.goalSelector.a(7, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this)); + this.dI(); + } + + protected void dI() { + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityHorseAbstract.bN, (byte) 0); + this.datawatcher.register(EntityHorseAbstract.bO, Optional.empty()); + } + + protected boolean p(int i) { + return ((Byte) this.datawatcher.get(EntityHorseAbstract.bN) & i) != 0; + } + + protected void d(int i, boolean flag) { + byte b0 = (Byte) this.datawatcher.get(EntityHorseAbstract.bN); + + if (flag) { + this.datawatcher.set(EntityHorseAbstract.bN, (byte) (b0 | i)); + } else { + this.datawatcher.set(EntityHorseAbstract.bN, (byte) (b0 & ~i)); + } + + } + + public boolean isTamed() { + return this.p(2); + } + + @Nullable + public UUID getOwnerUUID() { + return (UUID) ((Optional) this.datawatcher.get(EntityHorseAbstract.bO)).orElse((Object) null); + } + + public void setOwnerUUID(@Nullable UUID uuid) { + this.datawatcher.set(EntityHorseAbstract.bO, Optional.ofNullable(uuid)); + } + + public float dL() { + return 0.5F; + } + + public void a(boolean flag) { + this.a(flag ? this.dL() : 1.0F); + } + + public boolean dM() { + return this.bG; + } + + public void setTamed(boolean flag) { + this.d(2, flag); + } + + public void v(boolean flag) { + this.bG = flag; + } + + public boolean a(EntityHuman entityhuman) { + return world.paperConfig.allowLeashingUndeadHorse ? super.a(entityhuman) : super.a(entityhuman) && this.getMonsterType() != EnumMonsterType.UNDEAD; // Paper + } + + protected void u(float f) { + if (f > 6.0F && this.dN()) { + this.y(false); + } + + } + + public boolean dN() { + return this.p(16); + } + + public boolean dO() { + return this.p(32); + } + + public boolean hasReproduced() { + return this.p(8); + } + + public void w(boolean flag) { + this.d(8, flag); + } + + public void x(boolean flag) { + this.d(4, flag); + } + + public int getTemper() { + return this.bI; + } + + public void setTemper(int i) { + this.bI = i; + } + + public int r(int i) { + int j = MathHelper.clamp(this.getTemper() + i, 0, this.getMaxDomestication()); + + this.setTemper(j); + return j; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + Entity entity = damagesource.getEntity(); + + return this.isVehicle() && entity != null && this.y(entity) ? false : super.damageEntity(damagesource, f); + } + + public boolean isCollidable() { + return !this.isVehicle(); + } + + private void dy() { + this.dC(); + if (!this.isSilent()) { + this.world.a((EntityHuman) null, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_HORSE_EAT, this.bV(), 1.0F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F); + } + + } + + public void c(float f, float f1) { + if (f > 1.0F) { + this.a(SoundEffects.ENTITY_HORSE_LAND, 0.4F, 1.0F); + } + + int i = MathHelper.f((f * 0.5F - 3.0F) * f1); + + if (i > 0) { + this.damageEntity(DamageSource.FALL, (float) i); + if (this.isVehicle()) { + Iterator iterator = this.getAllPassengers().iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + entity.damageEntity(DamageSource.FALL, (float) i); + } + } + + IBlockData iblockdata = this.world.getType(new BlockPosition(this.locX, this.locY - 0.2D - (double) this.lastYaw, this.locZ)); + Block block = iblockdata.getBlock(); + + if (!iblockdata.isAir() && !this.isSilent()) { + SoundEffectType soundeffecttype = block.getStepSound(); + + this.world.a((EntityHuman) null, this.locX, this.locY, this.locZ, soundeffecttype.d(), this.bV(), soundeffecttype.a() * 0.5F, soundeffecttype.b() * 0.75F); + } + + } + } + + protected int dA() { + return 2; + } + + public void loadChest() { + InventoryHorseChest inventoryhorsechest = this.inventoryChest; + + this.inventoryChest = new InventoryHorseChest(this.getDisplayName(), this.dA(), this); // CraftBukkit + this.inventoryChest.a(this.getCustomName()); + if (inventoryhorsechest != null) { + inventoryhorsechest.b(this); + int i = Math.min(inventoryhorsechest.getSize(), this.inventoryChest.getSize()); + + for (int j = 0; j < i; ++j) { + ItemStack itemstack = inventoryhorsechest.getItem(j); + + if (!itemstack.isEmpty()) { + this.inventoryChest.setItem(j, itemstack.cloneItemStack()); + } + } + } + + this.inventoryChest.a((IInventoryListener) this); + this.dS(); + } + + protected void dS() { + if (!this.world.isClientSide) { + this.x(!this.inventoryChest.getItem(0).isEmpty() && this.dU()); + } + } + + public void a(IInventory iinventory) { + boolean flag = this.dV(); + + this.dS(); + if (this.ticksLived > 20 && !flag && this.dV()) { + this.a(SoundEffects.ENTITY_HORSE_SADDLE, 0.5F, 1.0F); + } + + } + + @Nullable + protected EntityHorseAbstract a(Entity entity, double d0) { + double d1 = Double.MAX_VALUE; + Entity entity1 = null; + List list = this.world.getEntities(entity, entity.getBoundingBox().b(d0, d0, d0), EntityHorseAbstract.bM); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + Entity entity2 = (Entity) iterator.next(); + double d2 = entity2.d(entity.locX, entity.locY, entity.locZ); + + if (d2 < d1) { + entity1 = entity2; + d1 = d2; + } + } + + return (EntityHorseAbstract) entity1; + } + + public double getJumpStrength() { + return this.getAttributeInstance(EntityHorseAbstract.attributeJumpStrength).getValue(); + } + + @Nullable + protected SoundEffect cs() { + return null; + } + + @Nullable + protected SoundEffect d(DamageSource damagesource) { + if (this.random.nextInt(3) == 0) { + this.dH(); + } + + return null; + } + + @Nullable + protected SoundEffect D() { + if (this.random.nextInt(10) == 0 && !this.isFrozen()) { + this.dH(); + } + + return null; + } + + public boolean dU() { + return true; + } + + public boolean dV() { + return this.p(4); + } + + @Nullable + protected SoundEffect dB() { + this.dH(); + return null; + } + + protected void a(BlockPosition blockposition, IBlockData iblockdata) { + if (!iblockdata.getMaterial().isLiquid()) { + SoundEffectType soundeffecttype = iblockdata.getBlock().getStepSound(); + + if (this.world.getType(blockposition.up()).getBlock() == Blocks.SNOW) { + soundeffecttype = Blocks.SNOW.getStepSound(); + } + + if (this.isVehicle() && this.bK) { + ++this.bL; + if (this.bL > 5 && this.bL % 3 == 0) { + this.a(soundeffecttype); + } else if (this.bL <= 5) { + this.a(SoundEffects.ENTITY_HORSE_STEP_WOOD, soundeffecttype.a() * 0.15F, soundeffecttype.b()); + } + } else if (soundeffecttype == SoundEffectType.a) { + this.a(SoundEffects.ENTITY_HORSE_STEP_WOOD, soundeffecttype.a() * 0.15F, soundeffecttype.b()); + } else { + this.a(SoundEffects.ENTITY_HORSE_STEP, soundeffecttype.a() * 0.15F, soundeffecttype.b()); + } + + } + } + + protected void a(SoundEffectType soundeffecttype) { + this.a(SoundEffects.ENTITY_HORSE_GALLOP, soundeffecttype.a() * 0.15F, soundeffecttype.b()); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeMap().b(EntityHorseAbstract.attributeJumpStrength); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(53.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.22499999403953552D); + } + + public int dg() { + return 6; + } + + public int getMaxDomestication() { + return this.maxDomestication; // CraftBukkit - return stored max domestication instead of 100 + } + + protected float cD() { + return 0.8F; + } + + public int z() { + return 400; + } + + public void c(EntityHuman entityhuman) { + if (!this.world.isClientSide && (!this.isVehicle() || this.w(entityhuman)) && this.isTamed()) { + this.inventoryChest.a(this.getCustomName()); + entityhuman.openHorseInventory(this, this.inventoryChest); + } + + } + + protected boolean b(EntityHuman entityhuman, ItemStack itemstack) { + boolean flag = false; + float f = 0.0F; + short short0 = 0; + byte b0 = 0; + Item item = itemstack.getItem(); + + if (item == Items.WHEAT) { + f = 2.0F; + short0 = 20; + b0 = 3; + } else if (item == Items.SUGAR) { + f = 1.0F; + short0 = 30; + b0 = 3; + } else if (item == Blocks.HAY_BLOCK.getItem()) { + f = 20.0F; + short0 = 180; + } else if (item == Items.APPLE) { + f = 3.0F; + short0 = 60; + b0 = 3; + } else if (item == Items.GOLDEN_CARROT) { + f = 4.0F; + short0 = 60; + b0 = 5; + if (this.isTamed() && this.getAge() == 0 && !this.isInLove()) { + flag = true; + this.f(entityhuman); + } + } else if (item == Items.GOLDEN_APPLE || item == Items.ENCHANTED_GOLDEN_APPLE) { + f = 10.0F; + short0 = 240; + b0 = 10; + if (this.isTamed() && this.getAge() == 0 && !this.isInLove()) { + flag = true; + this.f(entityhuman); + } + } + + if (this.getHealth() < this.getMaxHealth() && f > 0.0F) { + this.heal(f, RegainReason.EATING); // CraftBukkit + flag = true; + } + + if (this.isBaby() && short0 > 0) { + this.world.addParticle(Particles.z, this.locX + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width, this.locY + 0.5D + (double) (this.random.nextFloat() * this.length), this.locZ + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width, 0.0D, 0.0D, 0.0D); + if (!this.world.isClientSide) { + this.setAge(short0); + } + + flag = true; + } + + if (b0 > 0 && (flag || !this.isTamed()) && this.getTemper() < this.getMaxDomestication()) { + flag = true; + if (!this.world.isClientSide) { + this.r(b0); + } + } + + if (flag) { + this.dy(); + } + + return flag; + } + + protected void g(EntityHuman entityhuman) { + this.y(false); + this.setStanding(false); + if (!this.world.isClientSide) { + entityhuman.yaw = this.yaw; + entityhuman.pitch = this.pitch; + entityhuman.startRiding(this); + } + + } + + protected boolean isFrozen() { + return super.isFrozen() && this.isVehicle() && this.dV() || this.dN() || this.dO(); + } + + public boolean f(ItemStack itemstack) { + return false; + } + + private void dz() { + this.bD = 1; + } + + public void die(DamageSource damagesource) { + // super.die(damagesource); // Moved down + if (!this.world.isClientSide && this.inventoryChest != null) { + for (int i = 0; i < this.inventoryChest.getSize(); ++i) { + ItemStack itemstack = this.inventoryChest.getItem(i); + + if (!itemstack.isEmpty()) { + this.a_(itemstack); + } + } + + } + super.die(damagesource); // CraftBukkit + } + + public void movementTick() { + if (this.random.nextInt(200) == 0) { + this.dz(); + } + + super.movementTick(); + if (!this.world.isClientSide) { + if (this.random.nextInt(900) == 0 && this.deathTicks == 0) { + this.heal(1.0F, RegainReason.REGEN); // CraftBukkit + } + + if (this.dY()) { + if (!this.dN() && !this.isVehicle() && this.random.nextInt(300) == 0 && this.world.getType(new BlockPosition(MathHelper.floor(this.locX), MathHelper.floor(this.locY) - 1, MathHelper.floor(this.locZ))).getBlock() == Blocks.GRASS_BLOCK) { + this.y(true); + } + + if (this.dN() && ++this.bP > 50) { + this.bP = 0; + this.y(false); + } + } + + this.dX(); + } + } + + protected void dX() { + if (this.hasReproduced() && this.isBaby() && !this.dN()) { + EntityHorseAbstract entityhorseabstract = this.a(this, 16.0D); + + if (entityhorseabstract != null && this.h((Entity) entityhorseabstract) > 4.0D) { + this.navigation.a((Entity) entityhorseabstract); + } + } + + } + + public boolean dY() { + return true; + } + + public void tick() { + super.tick(); + if (this.bQ > 0 && ++this.bQ > 30) { + this.bQ = 0; + this.d(64, false); + } + + if ((this.bT() || this.cP()) && this.bR > 0 && ++this.bR > 20) { + this.bR = 0; + this.setStanding(false); + } + + if (this.bD > 0 && ++this.bD > 8) { + this.bD = 0; + } + + if (this.bE > 0) { + ++this.bE; + if (this.bE > 300) { + this.bE = 0; + } + } + + this.bU = this.bT; + if (this.dN()) { + this.bT += (1.0F - this.bT) * 0.4F + 0.05F; + if (this.bT > 1.0F) { + this.bT = 1.0F; + } + } else { + this.bT += (0.0F - this.bT) * 0.4F - 0.05F; + if (this.bT < 0.0F) { + this.bT = 0.0F; + } + } + + this.bW = this.bV; + if (this.dO()) { + this.bT = 0.0F; + this.bU = this.bT; + this.bV += (1.0F - this.bV) * 0.4F + 0.05F; + if (this.bV > 1.0F) { + this.bV = 1.0F; + } + } else { + this.canSlide = false; + this.bV += (0.8F * this.bV * this.bV * this.bV - this.bV) * 0.6F - 0.05F; + if (this.bV < 0.0F) { + this.bV = 0.0F; + } + } + + this.bY = this.bX; + if (this.p(64)) { + this.bX += (1.0F - this.bX) * 0.7F + 0.05F; + if (this.bX > 1.0F) { + this.bX = 1.0F; + } + } else { + this.bX += (0.0F - this.bX) * 0.7F - 0.05F; + if (this.bX < 0.0F) { + this.bX = 0.0F; + } + } + + } + + private void dC() { + if (!this.world.isClientSide) { + this.bQ = 1; + this.d(64, true); + } + + } + + public void y(boolean flag) { + this.d(16, flag); + } + + public void setStanding(boolean flag) { + if (flag) { + this.y(false); + } + + this.d(32, flag); + } + + private void dH() { + if (this.bT() || this.cP()) { + this.bR = 1; + this.setStanding(true); + } + + } + + public void dZ() { + this.dH(); + SoundEffect soundeffect = this.dB(); + + if (soundeffect != null) { + this.a(soundeffect, this.cD(), this.cE()); + } + + } + + public boolean h(EntityHuman entityhuman) { + this.setOwnerUUID(entityhuman.getUniqueID()); + this.setTamed(true); + if (entityhuman instanceof EntityPlayer) { + CriterionTriggers.x.a((EntityPlayer) entityhuman, (EntityAnimal) this); + } + + this.world.broadcastEntityEffect(this, (byte) 7); + return true; + } + + public void a(float f, float f1, float f2) { + if (this.isVehicle() && this.dh() && this.dV()) { + EntityLiving entityliving = (EntityLiving) this.bO(); + + this.yaw = entityliving.yaw; + this.lastYaw = this.yaw; + this.pitch = entityliving.pitch * 0.5F; + this.setYawPitch(this.yaw, this.pitch); + this.aQ = this.yaw; + this.aS = this.aQ; + f = entityliving.bh * 0.5F; + f2 = entityliving.bj; + if (f2 <= 0.0F) { + f2 *= 0.25F; + this.bL = 0; + } + + if (this.onGround && this.jumpPower == 0.0F && this.dO() && !this.canSlide) { + f = 0.0F; + f2 = 0.0F; + } + + if (this.jumpPower > 0.0F && !this.dM() && this.onGround) { + this.motY = this.getJumpStrength() * (double) this.jumpPower; + if (this.hasEffect(MobEffects.JUMP)) { + this.motY += (double) ((float) (this.getEffect(MobEffects.JUMP).getAmplifier() + 1) * 0.1F); + } + + this.v(true); + this.impulse = true; + if (f2 > 0.0F) { + float f3 = MathHelper.sin(this.yaw * 0.017453292F); + float f4 = MathHelper.cos(this.yaw * 0.017453292F); + + this.motX += (double) (-0.4F * f3 * this.jumpPower); + this.motZ += (double) (0.4F * f4 * this.jumpPower); + this.ea(); + } + + this.jumpPower = 0.0F; + } + + this.aU = this.cK() * 0.1F; + if (this.bT()) { + this.o((float) this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue()); + super.a(f, f1, f2); + } else if (entityliving instanceof EntityHuman) { + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + } + + if (this.onGround) { + this.jumpPower = 0.0F; + this.v(false); + } + + this.aI = this.aJ; + double d0 = this.locX - this.lastX; + double d1 = this.locZ - this.lastZ; + float f5 = MathHelper.sqrt(d0 * d0 + d1 * d1) * 4.0F; + + if (f5 > 1.0F) { + f5 = 1.0F; + } + + this.aJ += (f5 - this.aJ) * 0.4F; + this.aK += this.aJ; + } else { + this.aU = 0.02F; + super.a(f, f1, f2); + } + } + + protected void ea() { + this.a(SoundEffects.ENTITY_HORSE_JUMP, 0.4F, 1.0F); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("EatingHaystack", this.dN()); + nbttagcompound.setBoolean("Bred", this.hasReproduced()); + nbttagcompound.setInt("Temper", this.getTemper()); + nbttagcompound.setBoolean("Tame", this.isTamed()); + if (this.getOwnerUUID() != null) { + nbttagcompound.setString("OwnerUUID", this.getOwnerUUID().toString()); + } + nbttagcompound.setInt("Bukkit.MaxDomestication", this.maxDomestication); // CraftBukkit + + if (!this.inventoryChest.getItem(0).isEmpty()) { + nbttagcompound.set("SaddleItem", this.inventoryChest.getItem(0).save(new NBTTagCompound())); + } + + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.y(nbttagcompound.getBoolean("EatingHaystack")); + this.w(nbttagcompound.getBoolean("Bred")); + this.setTemper(nbttagcompound.getInt("Temper")); + this.setTamed(nbttagcompound.getBoolean("Tame")); + String s; + + if (nbttagcompound.hasKeyOfType("OwnerUUID", 8)) { + s = nbttagcompound.getString("OwnerUUID"); + } else { + String s1 = nbttagcompound.getString("Owner"); + + s = NameReferencingFileConverter.a(this.bK(), s1); + } + + if (!s.isEmpty()) { + this.setOwnerUUID(UUID.fromString(s)); + } + // CraftBukkit start + if (nbttagcompound.hasKey("Bukkit.MaxDomestication")) { + this.maxDomestication = nbttagcompound.getInt("Bukkit.MaxDomestication"); + } + // CraftBukkit end + + AttributeInstance attributeinstance = this.getAttributeMap().a("Speed"); + + if (attributeinstance != null) { + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(attributeinstance.b() * 0.25D); + } + + if (nbttagcompound.hasKeyOfType("SaddleItem", 10)) { + ItemStack itemstack = ItemStack.a(nbttagcompound.getCompound("SaddleItem")); + + if (itemstack.getItem() == Items.SADDLE) { + this.inventoryChest.setItem(0, itemstack); + } + } + + this.dS(); + } + + public boolean mate(EntityAnimal entityanimal) { + return false; + } + + protected boolean eb() { + return !this.isVehicle() && !this.isPassenger() && this.isTamed() && !this.isBaby() && this.getHealth() >= this.getMaxHealth() && this.isInLove(); + } + + @Nullable + public EntityAgeable createChild(EntityAgeable entityageable) { + return null; + } + + protected void a(EntityAgeable entityageable, EntityHorseAbstract entityhorseabstract) { + double d0 = this.getAttributeInstance(GenericAttributes.maxHealth).b() + entityageable.getAttributeInstance(GenericAttributes.maxHealth).b() + (double) this.ec(); + + entityhorseabstract.getAttributeInstance(GenericAttributes.maxHealth).setValue(d0 / 3.0D); + double d1 = this.getAttributeInstance(EntityHorseAbstract.attributeJumpStrength).b() + entityageable.getAttributeInstance(EntityHorseAbstract.attributeJumpStrength).b() + this.ed(); + + entityhorseabstract.getAttributeInstance(EntityHorseAbstract.attributeJumpStrength).setValue(d1 / 3.0D); + double d2 = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).b() + entityageable.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).b() + this.ee(); + + entityhorseabstract.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(d2 / 3.0D); + } + + public boolean dh() { + return this.bO() instanceof EntityLiving; + } + + public boolean G_() { + return this.dV(); + } + + public void b(int i) { + // CraftBukkit start + float power; + if (i >= 90) { + power = 1.0F; + } else { + power = 0.4F + 0.4F * (float) i / 90.0F; + } + org.bukkit.event.entity.HorseJumpEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callHorseJumpEvent(this, power); + if (event.isCancelled()) { + return; + } + // CraftBukkit end + this.canSlide = true; + this.dH(); + } + + public void I_() {} + + public void k(Entity entity) { + super.k(entity); + if (entity instanceof EntityInsentient) { + EntityInsentient entityinsentient = (EntityInsentient) entity; + + this.aQ = entityinsentient.aQ; + } + + if (this.bW > 0.0F) { + float f = MathHelper.sin(this.aQ * 0.017453292F); + float f1 = MathHelper.cos(this.aQ * 0.017453292F); + float f2 = 0.7F * this.bW; + float f3 = 0.15F * this.bW; + + entity.setPosition(this.locX + (double) (f2 * f), this.locY + this.aJ() + entity.aI() + (double) f3, this.locZ - (double) (f2 * f1)); + if (entity instanceof EntityLiving) { + ((EntityLiving) entity).aQ = this.aQ; + } + } + + } + + protected float ec() { + return 15.0F + (float) this.random.nextInt(8) + (float) this.random.nextInt(9); + } + + protected double ed() { + return 0.4000000059604645D + this.random.nextDouble() * 0.2D + this.random.nextDouble() * 0.2D + this.random.nextDouble() * 0.2D; + } + + protected double ee() { + return (0.44999998807907104D + this.random.nextDouble() * 0.3D + this.random.nextDouble() * 0.3D + this.random.nextDouble() * 0.3D) * 0.25D; + } + + public boolean z_() { + return false; + } + + public float getHeadHeight() { + return this.length; + } + + public boolean ef() { + return false; + } + + public boolean g(ItemStack itemstack) { + return false; + } + + public boolean c(int i, ItemStack itemstack) { + int j = i - 400; + + if (j >= 0 && j < 2 && j < this.inventoryChest.getSize()) { + if (j == 0 && itemstack.getItem() != Items.SADDLE) { + return false; + } else if (j == 1 && (!this.ef() || !this.g(itemstack))) { + return false; + } else { + this.inventoryChest.setItem(j, itemstack); + this.dS(); + return true; + } + } else { + int k = i - 500 + 2; + + if (k >= 2 && k < this.inventoryChest.getSize()) { + this.inventoryChest.setItem(k, itemstack); + return true; + } else { + return false; + } + } + } + + @Nullable + public Entity bO() { + return this.bP().isEmpty() ? null : (Entity) this.bP().get(0); + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + groupdataentity = super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + if (this.random.nextInt(5) == 0) { + this.setAgeRaw(-24000); + } + + return groupdataentity; + } +} diff --git a/src/main/java/net/minecraft/server/EntityHorseChestedAbstract.java b/src/main/java/net/minecraft/server/EntityHorseChestedAbstract.java new file mode 100644 index 000000000000..6eddc2a84967 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityHorseChestedAbstract.java @@ -0,0 +1,191 @@ +package net.minecraft.server; + +public abstract class EntityHorseChestedAbstract extends EntityHorseAbstract { + + private static final DataWatcherObject bM = DataWatcher.a(EntityHorseChestedAbstract.class, DataWatcherRegistry.i); + + protected EntityHorseChestedAbstract(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.bK = false; + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityHorseChestedAbstract.bM, false); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue((double) this.ec()); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.17499999701976776D); + this.getAttributeInstance(EntityHorseChestedAbstract.attributeJumpStrength).setValue(0.5D); + } + + public boolean isCarryingChest() { + return (Boolean) this.datawatcher.get(EntityHorseChestedAbstract.bM); + } + + public void setCarryingChest(boolean flag) { + this.datawatcher.set(EntityHorseChestedAbstract.bM, flag); + } + + protected int dA() { + return this.isCarryingChest() ? 17 : super.dA(); + } + + public double aJ() { + return super.aJ() - 0.25D; + } + + protected SoundEffect dB() { + super.dB(); + return SoundEffects.ENTITY_DONKEY_ANGRY; + } + + public void die(DamageSource damagesource) { + // super.die(damagesource); // CraftBukkit - moved down + if (this.isCarryingChest()) { + if (!this.world.isClientSide) { + this.a((IMaterial) Blocks.CHEST); + } + + // this.setCarryingChest(false); // CraftBukkit - moved down + } + // CraftBukkit start + super.die(damagesource); + this.setCarryingChest(false); + // CraftBukkit end + + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("ChestedHorse", this.isCarryingChest()); + if (this.isCarryingChest()) { + NBTTagList nbttaglist = new NBTTagList(); + + for (int i = 2; i < this.inventoryChest.getSize(); ++i) { + ItemStack itemstack = this.inventoryChest.getItem(i); + + if (!itemstack.isEmpty()) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.setByte("Slot", (byte) i); + itemstack.save(nbttagcompound1); + nbttaglist.add((NBTBase) nbttagcompound1); + } + } + + nbttagcompound.set("Items", nbttaglist); + } + + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setCarryingChest(nbttagcompound.getBoolean("ChestedHorse")); + if (this.isCarryingChest()) { + NBTTagList nbttaglist = nbttagcompound.getList("Items", 10); + + this.loadChest(); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.getCompound(i); + int j = nbttagcompound1.getByte("Slot") & 255; + + if (j >= 2 && j < this.inventoryChest.getSize()) { + this.inventoryChest.setItem(j, ItemStack.a(nbttagcompound1)); + } + } + } + + this.dS(); + } + + public boolean c(int i, ItemStack itemstack) { + if (i == 499) { + if (this.isCarryingChest() && itemstack.isEmpty()) { + this.setCarryingChest(false); + this.loadChest(); + return true; + } + + if (!this.isCarryingChest() && itemstack.getItem() == Blocks.CHEST.getItem()) { + this.setCarryingChest(true); + this.loadChest(); + return true; + } + } + + return super.c(i, itemstack); + } + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (itemstack.getItem() instanceof ItemMonsterEgg) { + return super.a(entityhuman, enumhand); + } else { + if (!this.isBaby()) { + if (this.isTamed() && entityhuman.isSneaking()) { + this.c(entityhuman); + return true; + } + + if (this.isVehicle()) { + return super.a(entityhuman, enumhand); + } + } + + if (!itemstack.isEmpty()) { + boolean flag = this.b(entityhuman, itemstack); + + if (!flag) { + if (!this.isTamed() || itemstack.getItem() == Items.NAME_TAG) { + if (itemstack.a(entityhuman, (EntityLiving) this, enumhand)) { + return true; + } + + this.dZ(); + return true; + } + + if (!this.isCarryingChest() && itemstack.getItem() == Blocks.CHEST.getItem()) { + this.setCarryingChest(true); + this.dC(); + flag = true; + this.loadChest(); + } + + if (!this.isBaby() && !this.dV() && itemstack.getItem() == Items.SADDLE) { + this.c(entityhuman); + return true; + } + } + + if (flag) { + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + return true; + } + } + + if (this.isBaby()) { + return super.a(entityhuman, enumhand); + } else { + this.g(entityhuman); + return true; + } + } + } + + protected void dC() { + this.a(SoundEffects.ENTITY_DONKEY_CHEST, 1.0F, (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F); + } + + public int dH() { + return 5; + } +} diff --git a/src/main/java/net/minecraft/server/EntityHorseDonkey.java b/src/main/java/net/minecraft/server/EntityHorseDonkey.java new file mode 100644 index 000000000000..65c40e72bf3d --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityHorseDonkey.java @@ -0,0 +1,41 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class EntityHorseDonkey extends EntityHorseChestedAbstract { + + public EntityHorseDonkey(World world) { + super(EntityTypes.DONKEY, world); + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.O; + } + + protected SoundEffect D() { + super.D(); + return SoundEffects.ENTITY_DONKEY_AMBIENT; + } + + protected SoundEffect cs() { + super.cs(); + return SoundEffects.ENTITY_DONKEY_DEATH; + } + + protected SoundEffect d(DamageSource damagesource) { + super.d(damagesource); + return SoundEffects.ENTITY_DONKEY_HURT; + } + + public boolean mate(EntityAnimal entityanimal) { + return entityanimal == this ? false : (!(entityanimal instanceof EntityHorseDonkey) && !(entityanimal instanceof EntityHorse) ? false : this.eb() && ((EntityHorseAbstract) entityanimal).eb()); + } + + public EntityAgeable createChild(EntityAgeable entityageable) { + Object object = entityageable instanceof EntityHorse ? EntityTypes.MULE.create(world) : EntityTypes.DONKEY.create(world); // Paper + + this.a(entityageable, (EntityHorseAbstract) object); + return (EntityAgeable) object; + } +} diff --git a/src/main/java/net/minecraft/server/EntityHorseSkeleton.java b/src/main/java/net/minecraft/server/EntityHorseSkeleton.java new file mode 100644 index 000000000000..0a092acdfec9 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityHorseSkeleton.java @@ -0,0 +1,170 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class EntityHorseSkeleton extends EntityHorseAbstract { + + private final PathfinderGoalHorseTrap bM = new PathfinderGoalHorseTrap(this); + private boolean bN; + private int bO; public int getTrapTime() { return this.bO; } // Paper - OBFHELPER + + public EntityHorseSkeleton(World world) { + super(EntityTypes.SKELETON_HORSE, world); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(15.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.20000000298023224D); + this.getAttributeInstance(EntityHorseSkeleton.attributeJumpStrength).setValue(this.ed()); + } + + protected void dI() {} + + protected SoundEffect D() { + super.D(); + return this.a(TagsFluid.WATER) ? SoundEffects.ENTITY_SKELETON_HORSE_AMBIENT_WATER : SoundEffects.ENTITY_SKELETON_HORSE_AMBIENT; + } + + protected SoundEffect cs() { + super.cs(); + return SoundEffects.ENTITY_SKELETON_HORSE_DEATH; + } + + protected SoundEffect d(DamageSource damagesource) { + super.d(damagesource); + return SoundEffects.ENTITY_SKELETON_HORSE_HURT; + } + + protected SoundEffect ad() { + if (this.onGround) { + if (!this.isVehicle()) { + return SoundEffects.ENTITY_SKELETON_HORSE_STEP_WATER; + } + + ++this.bL; + if (this.bL > 5 && this.bL % 3 == 0) { + return SoundEffects.ENTITY_SKELETON_HORSE_GALLOP_WATER; + } + + if (this.bL <= 5) { + return SoundEffects.ENTITY_SKELETON_HORSE_STEP_WATER; + } + } + + return SoundEffects.ENTITY_SKELETON_HORSE_SWIM; + } + + protected void d(float f) { + if (this.onGround) { + super.d(0.3F); + } else { + super.d(Math.min(0.1F, f * 25.0F)); + } + + } + + protected void ea() { + if (this.isInWater()) { + this.a(SoundEffects.ENTITY_SKELETON_HORSE_JUMP_WATER, 0.4F, 1.0F); + } else { + super.ea(); + } + + } + + public EnumMonsterType getMonsterType() { + return EnumMonsterType.UNDEAD; + } + + public double aJ() { + return super.aJ() - 0.1875D; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.R; + } + + public void movementTick() { + super.movementTick(); + if (this.dy() && this.bO++ >= 18000) { + this.die(); + } + + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("SkeletonTrap", this.dy()); + nbttagcompound.setInt("SkeletonTrapTime", this.bO); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.s(nbttagcompound.getBoolean("SkeletonTrap")); + this.bO = nbttagcompound.getInt("SkeletonTrapTime"); + } + + public boolean aY() { + return true; + } + + protected float cJ() { + return 0.96F; + } + + public boolean isTrap() { return this.dy(); } // Paper - OBFHELPER + public boolean dy() { + return this.bN; + } + + public void setTrap(boolean trap) { this.s(trap); } // Paper - OBFHELPER + public void s(boolean flag) { + if (flag != this.bN) { + this.bN = flag; + if (flag) { + this.goalSelector.a(1, this.bM); + } else { + this.goalSelector.a((PathfinderGoal) this.bM); + } + + } + } + + @Nullable + public EntityAgeable createChild(EntityAgeable entityageable) { + return EntityTypes.SKELETON_HORSE.create(world); // Paper + } + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (itemstack.getItem() instanceof ItemMonsterEgg) { + return super.a(entityhuman, enumhand); + } else if (!this.isTamed()) { + return false; + } else if (this.isBaby()) { + return super.a(entityhuman, enumhand); + } else if (entityhuman.isSneaking()) { + this.c(entityhuman); + return true; + } else if (this.isVehicle()) { + return super.a(entityhuman, enumhand); + } else { + if (!itemstack.isEmpty()) { + if (itemstack.getItem() == Items.SADDLE && !this.dV()) { + this.c(entityhuman); + return true; + } + + if (itemstack.a(entityhuman, (EntityLiving) this, enumhand)) { + return true; + } + } + + this.g(entityhuman); + return true; + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityHorseZombie.java b/src/main/java/net/minecraft/server/EntityHorseZombie.java new file mode 100644 index 000000000000..a1873f557c4c --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityHorseZombie.java @@ -0,0 +1,79 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class EntityHorseZombie extends EntityHorseAbstract { + + public EntityHorseZombie(World world) { + super(EntityTypes.ZOMBIE_HORSE, world); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(15.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.20000000298023224D); + this.getAttributeInstance(EntityHorseZombie.attributeJumpStrength).setValue(this.ed()); + } + + public EnumMonsterType getMonsterType() { + return EnumMonsterType.UNDEAD; + } + + protected SoundEffect D() { + super.D(); + return SoundEffects.ENTITY_ZOMBIE_HORSE_AMBIENT; + } + + protected SoundEffect cs() { + super.cs(); + return SoundEffects.ENTITY_ZOMBIE_HORSE_DEATH; + } + + protected SoundEffect d(DamageSource damagesource) { + super.d(damagesource); + return SoundEffects.ENTITY_ZOMBIE_HORSE_HURT; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.Q; + } + + @Nullable + public EntityAgeable createChild(EntityAgeable entityageable) { + return EntityTypes.ZOMBIE_HORSE.create(world); // Paper + } + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (itemstack.getItem() instanceof ItemMonsterEgg) { + return super.a(entityhuman, enumhand); + } else if (!this.isTamed()) { + return false; + } else if (this.isBaby()) { + return super.a(entityhuman, enumhand); + } else if (entityhuman.isSneaking()) { + this.c(entityhuman); + return true; + } else if (this.isVehicle()) { + return super.a(entityhuman, enumhand); + } else { + if (!itemstack.isEmpty()) { + if (!this.dV() && itemstack.getItem() == Items.SADDLE) { + this.c(entityhuman); + return true; + } + + if (itemstack.a(entityhuman, (EntityLiving) this, enumhand)) { + return true; + } + } + + this.g(entityhuman); + return true; + } + } + + protected void dI() {} +} diff --git a/src/main/java/net/minecraft/server/EntityHuman.java b/src/main/java/net/minecraft/server/EntityHuman.java new file mode 100644 index 000000000000..dab958a042d4 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityHuman.java @@ -0,0 +1,2136 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.mojang.authlib.GameProfile; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.EntityCombustByEntityEvent; +import org.bukkit.event.player.PlayerBedLeaveEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerVelocityEvent; +import org.bukkit.util.Vector; +// CraftBukkit end + +public abstract class EntityHuman extends EntityLiving { + + private static final DataWatcherObject a = DataWatcher.a(EntityHuman.class, DataWatcherRegistry.c); + private static final DataWatcherObject b = DataWatcher.a(EntityHuman.class, DataWatcherRegistry.b); + protected static final DataWatcherObject bx = DataWatcher.a(EntityHuman.class, DataWatcherRegistry.a); + protected static final DataWatcherObject by = DataWatcher.a(EntityHuman.class, DataWatcherRegistry.a); + protected static final DataWatcherObject bz = DataWatcher.a(EntityHuman.class, DataWatcherRegistry.p); + protected static final DataWatcherObject bA = DataWatcher.a(EntityHuman.class, DataWatcherRegistry.p); + public PlayerInventory inventory = new PlayerInventory(this); + protected InventoryEnderChest enderChest = new InventoryEnderChest(this); // CraftBukkit - add "this" to constructor + public Container defaultContainer; + public Container activeContainer; + protected FoodMetaData foodData = new FoodMetaData(this); // CraftBukkit - add "this" to constructor + protected int bG; + public float bH; + public float bI; + public int bJ; + public double bK; + public double bL; + public double bM; + public double bN; + public double bO; + public double bP; + public boolean sleeping; + public BlockPosition bedPosition; + public int sleepTicks; + public float bS; + public float bT; + private boolean d; + protected boolean bU; + private BlockPosition e; + private boolean f; + public PlayerAbilities abilities = new PlayerAbilities(); + public int expLevel; + public int expTotal; + public float exp; + protected int bZ; + protected float ca = 0.02F; + private int g; + private GameProfile h; public void setProfile(GameProfile profile) { this.h = profile; } // Paper - OBFHELPER + private ItemStack cd; + private final ItemCooldown ce; + @Nullable + public EntityFishingHook hookedFish; + // Paper start + public boolean affectsSpawning = true; + // Paper end + // Paper start - Player view distance API + private int viewDistance = -1; + public int getViewDistance() { + return viewDistance == -1 ? ((WorldServer) world).getPlayerChunkMap().getViewDistance() : viewDistance; + } + public void setViewDistance(int viewDistance) { + this.viewDistance = viewDistance; + } + // Paper end + + // CraftBukkit start + public boolean fauxSleeping; + public String spawnWorld = ""; + public int oldLevel = -1; + + @Override + public CraftHumanEntity getBukkitEntity() { + return (CraftHumanEntity) super.getBukkitEntity(); + } + // CraftBukkit end + + public EntityHuman(World world, GameProfile gameprofile) { + super(EntityTypes.PLAYER, world); + this.cd = ItemStack.a; + this.ce = this.g(); + this.a(a(gameprofile)); + this.h = gameprofile; + this.defaultContainer = new ContainerPlayer(this.inventory, !world.isClientSide, this); + this.activeContainer = this.defaultContainer; + BlockPosition blockposition = world.getSpawn(); + + this.setPositionRotation((double) blockposition.getX() + 0.5D, (double) (blockposition.getY() + 1), (double) blockposition.getZ() + 0.5D, 0.0F, 0.0F); + this.bd = 180.0F; + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeMap().b(GenericAttributes.ATTACK_DAMAGE).setValue(1.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.10000000149011612D); + this.getAttributeMap().b(GenericAttributes.g); + this.getAttributeMap().b(GenericAttributes.j); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityHuman.a, 0.0F); + this.datawatcher.register(EntityHuman.b, 0); + this.datawatcher.register(EntityHuman.bx, (byte) 0); + this.datawatcher.register(EntityHuman.by, (byte) 1); + this.datawatcher.register(EntityHuman.bz, new NBTTagCompound()); + this.datawatcher.register(EntityHuman.bA, new NBTTagCompound()); + } + + public void tick() { + this.noclip = this.isSpectator(); + if (this.isSpectator()) { + this.onGround = false; + } + + if (this.bJ > 0) { + --this.bJ; + } + + if (this.isSleeping()) { + ++this.sleepTicks; + if (this.sleepTicks > 100) { + this.sleepTicks = 100; + } + + if (!this.world.isClientSide) { + if (!this.p()) { + this.a(true, true, false); + } else if (this.world.L()) { + this.a(false, true, true); + } + } + } else if (this.sleepTicks > 0) { + ++this.sleepTicks; + if (this.sleepTicks >= 110) { + this.sleepTicks = 0; + } + } + + this.n(); + this.dg(); + super.tick(); + if (!this.world.isClientSide && this.activeContainer != null && !this.activeContainer.canUse(this)) { + this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.CANT_USE); // Paper + this.activeContainer = this.defaultContainer; + } + + if (this.isBurning() && this.abilities.isInvulnerable) { + this.extinguish(); + } + + this.o(); + if (!this.world.isClientSide) { + this.foodData.a(this); + this.a(StatisticList.PLAY_ONE_MINUTE); + if (this.isAlive()) { + this.a(StatisticList.TIME_SINCE_DEATH); + } + + if (this.isSneaking()) { + this.a(StatisticList.SNEAK_TIME); + } + + if (!this.isSleeping()) { + this.a(StatisticList.TIME_SINCE_REST); + } + } + + int i = 29999999; + double d0 = MathHelper.a(this.locX, -2.9999999E7D, 2.9999999E7D); + double d1 = MathHelper.a(this.locZ, -2.9999999E7D, 2.9999999E7D); + + if (d0 != this.locX || d1 != this.locZ) { + this.setPosition(d0, this.locY, d1); + } + + ++this.aH; + ItemStack itemstack = this.getItemInMainHand(); + + if (!ItemStack.matches(this.cd, itemstack)) { + if (!ItemStack.d(this.cd, itemstack)) { + this.dH(); + } + + this.cd = itemstack.isEmpty() ? ItemStack.a : itemstack.cloneItemStack(); + } + + this.l(); + this.ce.a(); + this.dh(); + } + + protected boolean dg() { + this.bU = this.a(TagsFluid.WATER); + return this.bU; + } + + private void l() { + ItemStack itemstack = this.getEquipment(EnumItemSlot.HEAD); + + if (itemstack.getItem() == Items.TURTLE_HELMET && !this.a(TagsFluid.WATER)) { + this.addEffect(new MobEffect(MobEffects.WATER_BREATHING, 200, 0, false, false, true), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.TURTLE_HELMET); // CraftBukkit + } + + } + + protected ItemCooldown g() { + return new ItemCooldown(); + } + + private void n() { + IBlockData iblockdata = this.world.a(this.getBoundingBox().grow(0.0D, -0.4000000059604645D, 0.0D).shrink(0.001D), Blocks.BUBBLE_COLUMN); + + if (iblockdata != null) { + if (!this.d && !this.justCreated && iblockdata.getBlock() == Blocks.BUBBLE_COLUMN && !this.isSpectator()) { + boolean flag = (Boolean) iblockdata.get(BlockBubbleColumn.a); + + if (flag) { + this.world.a(this.locX, this.locY, this.locZ, SoundEffects.BLOCK_BUBBLE_COLUMN_WHIRLPOOL_INSIDE, this.bV(), 1.0F, 1.0F, false); + } else { + this.world.a(this.locX, this.locY, this.locZ, SoundEffects.BLOCK_BUBBLE_COLUMN_UPWARDS_INSIDE, this.bV(), 1.0F, 1.0F, false); + } + } + + this.d = true; + } else { + this.d = false; + } + + } + + private void o() { + this.bK = this.bN; + this.bL = this.bO; + this.bM = this.bP; + double d0 = this.locX - this.bN; + double d1 = this.locY - this.bO; + double d2 = this.locZ - this.bP; + double d3 = 10.0D; + + if (d0 > 10.0D) { + this.bN = this.locX; + this.bK = this.bN; + } + + if (d2 > 10.0D) { + this.bP = this.locZ; + this.bM = this.bP; + } + + if (d1 > 10.0D) { + this.bO = this.locY; + this.bL = this.bO; + } + + if (d0 < -10.0D) { + this.bN = this.locX; + this.bK = this.bN; + } + + if (d2 < -10.0D) { + this.bP = this.locZ; + this.bM = this.bP; + } + + if (d1 < -10.0D) { + this.bO = this.locY; + this.bL = this.bO; + } + + this.bN += d0 * 0.25D; + this.bP += d2 * 0.25D; + this.bO += d1 * 0.25D; + } + + protected void dh() { + float f; + float f1; + + if (this.dc()) { + f = 0.6F; + f1 = 0.6F; + } else if (this.isSleeping()) { + f = 0.2F; + f1 = 0.2F; + } else if (!this.isSwimming() && !this.isRiptiding()) { + if (this.isSneaking()) { + f = 0.6F; + f1 = 1.65F; + } else { + f = 0.6F; + f1 = 1.8F; + } + } else { + f = 0.6F; + f1 = 0.6F; + } + + if (f != this.width || f1 != this.length) { + AxisAlignedBB axisalignedbb = this.getBoundingBox(); + + axisalignedbb = new AxisAlignedBB(axisalignedbb.minX, axisalignedbb.minY, axisalignedbb.minZ, axisalignedbb.minX + (double) f, axisalignedbb.minY + (double) f1, axisalignedbb.minZ + (double) f); + if (this.world.getCubes((Entity) null, axisalignedbb)) { + this.setSize(f, f1); + } + } + + } + + public int X() { + return this.abilities.isInvulnerable ? 1 : 80; + } + + protected SoundEffect ad() { + return SoundEffects.ENTITY_PLAYER_SWIM; + } + + protected SoundEffect ae() { + return SoundEffects.ENTITY_PLAYER_SPLASH; + } + + protected SoundEffect af() { + return SoundEffects.ENTITY_PLAYER_SPLASH_HIGH_SPEED; + } + + public int aQ() { + return 10; + } + + public void a(SoundEffect soundeffect, float f, float f1) { + this.world.a(this, this.locX, this.locY, this.locZ, soundeffect, this.bV(), f, f1); + } + + public SoundCategory bV() { + return SoundCategory.PLAYERS; + } + + public int getMaxFireTicks() { + return 20; + } + + protected boolean isFrozen() { + return this.getHealth() <= 0.0F || this.isSleeping(); + } + + // Paper start - unused code, but to keep signatures aligned + public void closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) { + closeInventory(); + this.activeContainer = this.defaultContainer; + } + // Paper end + + public void closeInventory() { + this.activeContainer = this.defaultContainer; + } + + public void aH() { + if (!this.world.isClientSide && this.isSneaking() && this.isPassenger()) { + this.stopRiding(); + this.setSneaking(false); + } else { + double d0 = this.locX; + double d1 = this.locY; + double d2 = this.locZ; + float f = this.yaw; + float f1 = this.pitch; + + super.aH(); + this.bH = this.bI; + this.bI = 0.0F; + this.l(this.locX - d0, this.locY - d1, this.locZ - d2); + if (this.getVehicle() instanceof EntityPig) { + this.pitch = f1; + this.yaw = f; + this.aQ = ((EntityPig) this.getVehicle()).aQ; + } + + } + } + + protected void doTick() { + super.doTick(); + this.cy(); + this.aS = this.yaw; + } + + public void movementTick() { + if (this.bG > 0) { + --this.bG; + } + + if (this.world.getDifficulty() == EnumDifficulty.PEACEFUL && this.world.getGameRules().getBoolean("naturalRegeneration")) { + if (this.getHealth() < this.getMaxHealth() && this.ticksLived % 20 == 0) { + // CraftBukkit - added regain reason of "REGEN" for filtering purposes. + this.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.REGEN); + } + + if (this.foodData.c() && this.ticksLived % 10 == 0) { + this.foodData.a(this.foodData.getFoodLevel() + 1); + } + } + + this.inventory.p(); + this.bH = this.bI; + super.movementTick(); + AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED); + + if (!this.world.isClientSide) { + attributeinstance.setValue((double) this.abilities.b()); + } + + this.aU = this.ca; + if (this.isSprinting()) { + this.aU = (float) ((double) this.aU + (double) this.ca * 0.3D); + } + + this.o((float) attributeinstance.getValue()); + float f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + float f1 = (float) ( org.bukkit.craftbukkit.TrigMath.atan(-this.motY * 0.20000000298023224D) * 15.0D); // CraftBukkit + + if (f > 0.1F) { + f = 0.1F; + } + + if (!this.onGround || this.getHealth() <= 0.0F || this.isSwimming()) { + f = 0.0F; + } + + if (this.onGround || this.getHealth() <= 0.0F) { + f1 = 0.0F; + } + + this.bI += (f - this.bI) * 0.4F; + this.aN += (f1 - this.aN) * 0.8F; + if (this.getHealth() > 0.0F && !this.isSpectator()) { + AxisAlignedBB axisalignedbb; + + if (this.isPassenger() && !this.getVehicle().dead) { + axisalignedbb = this.getBoundingBox().b(this.getVehicle().getBoundingBox()).grow(1.0D, 0.0D, 1.0D); + } else { + axisalignedbb = this.getBoundingBox().grow(1.0D, 0.5D, 1.0D); + } + + List list = this.world.getEntities(this, axisalignedbb); + + for (int i = 0; i < list.size(); ++i) { + Entity entity = (Entity) list.get(i); + + if (!entity.dead) { + this.c(entity); + } + } + } + + this.j(this.getShoulderEntityLeft()); + this.j(this.getShoulderEntityRight()); + if (!this.world.isClientSide && (this.fallDistance > 0.5F || this.isInWater() || this.isPassenger()) || this.abilities.isFlying) { + if (!this.world.paperConfig.parrotsHangOnBetter) this.releaseShoulderEntities(); // Paper - Hang on! + } + + } + + private void j(@Nullable NBTTagCompound nbttagcompound) { + if (nbttagcompound != null && !nbttagcompound.hasKey("Silent") || !nbttagcompound.getBoolean("Silent")) { + String s = nbttagcompound.getString("id"); + + if (EntityTypes.a(s) == EntityTypes.PARROT) { + EntityParrot.a(this.world, (Entity) this); + } + } + + } + + private void c(Entity entity) { + entity.d(this); + } + + public int getScore() { + return (Integer) this.datawatcher.get(EntityHuman.b); + } + + public void setScore(int i) { + this.datawatcher.set(EntityHuman.b, i); + } + + public void addScore(int i) { + int j = this.getScore(); + + this.datawatcher.set(EntityHuman.b, j + i); + } + + public void die(DamageSource damagesource) { + super.die(damagesource); + this.setSize(0.2F, 0.2F); + this.setPosition(this.locX, this.locY, this.locZ); + this.motY = 0.10000000149011612D; + if ("Notch".equals(this.getDisplayName().getString())) { + this.a(new ItemStack(Items.APPLE), true, false); + } + + if (!this.world.getGameRules().getBoolean("keepInventory") && !this.isSpectator()) { + this.removeCursedItems(); + this.inventory.dropContents(); + } + + if (damagesource != null) { + this.motX = (double) (-MathHelper.cos((this.aD + this.yaw) * 0.017453292F) * 0.1F); + this.motZ = (double) (-MathHelper.sin((this.aD + this.yaw) * 0.017453292F) * 0.1F); + } else { + this.motX = 0.0D; + this.motZ = 0.0D; + } + + this.a(StatisticList.DEATHS); + this.a(StatisticList.CUSTOM.b(StatisticList.TIME_SINCE_DEATH)); + this.a(StatisticList.CUSTOM.b(StatisticList.TIME_SINCE_REST)); + this.extinguish(); + this.setFlag(0, false); + } + + protected void removeCursedItems() { + for (int i = 0; i < this.inventory.getSize(); ++i) { + ItemStack itemstack = this.inventory.getItem(i); + + if (!itemstack.isEmpty() && EnchantmentManager.shouldNotDrop(itemstack)) { + this.inventory.splitWithoutUpdate(i); + } + } + + } + + protected SoundEffect d(DamageSource damagesource) { + return damagesource == DamageSource.BURN ? SoundEffects.ENTITY_PLAYER_HURT_ON_FIRE : (damagesource == DamageSource.DROWN ? SoundEffects.ENTITY_PLAYER_HURT_DROWN : SoundEffects.ENTITY_PLAYER_HURT); + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_PLAYER_DEATH; + } + + @Nullable + public EntityItem a(boolean flag) { + // Called only when dropped by Q or CTRL-Q + return this.a(this.inventory.splitStack(this.inventory.itemInHandIndex, flag && !this.inventory.getItemInHand().isEmpty() ? this.inventory.getItemInHand().getCount() : 1), false, true); + } + + @Nullable + public EntityItem drop(ItemStack itemstack, boolean flag) { + return this.a(itemstack, false, flag); + } + + @Nullable + public EntityItem a(ItemStack itemstack, boolean flag, boolean flag1) { + if (itemstack.isEmpty()) { + return null; + } else { + double d0 = this.locY - 0.30000001192092896D + (double) this.getHeadHeight(); + EntityItem entityitem = new EntityItem(this.world, this.locX, d0, this.locZ, itemstack); + + entityitem.a(40); + if (flag1) { + entityitem.c(this.getUniqueID()); + } + + float f; + float f1; + + if (flag) { + f = this.random.nextFloat() * 0.5F; + f1 = this.random.nextFloat() * 6.2831855F; + entityitem.motX = (double) (-MathHelper.sin(f1) * f); + entityitem.motZ = (double) (MathHelper.cos(f1) * f); + entityitem.motY = 0.20000000298023224D; + } else { + f = 0.3F; + entityitem.motX = (double) (-MathHelper.sin(this.yaw * 0.017453292F) * MathHelper.cos(this.pitch * 0.017453292F) * f); + entityitem.motZ = (double) (MathHelper.cos(this.yaw * 0.017453292F) * MathHelper.cos(this.pitch * 0.017453292F) * f); + entityitem.motY = (double) (-MathHelper.sin(this.pitch * 0.017453292F) * f + 0.1F); + f1 = this.random.nextFloat() * 6.2831855F; + f = 0.02F * this.random.nextFloat(); + entityitem.motX += Math.cos((double) f1) * (double) f; + entityitem.motY += (double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.1F); + entityitem.motZ += Math.sin((double) f1) * (double) f; + } + + // CraftBukkit start - fire PlayerDropItemEvent + Player player = (Player) this.getBukkitEntity(); + Item drop = (Item) entityitem.getBukkitEntity(); + + PlayerDropItemEvent event = new PlayerDropItemEvent(player, drop); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + org.bukkit.inventory.ItemStack cur = player.getInventory().getItemInHand(); + if (flag1 && (cur == null || cur.getAmount() == 0)) { + // The complete stack was dropped + player.getInventory().setItemInHand(drop.getItemStack()); + } else if (flag1 && cur.isSimilar(drop.getItemStack()) && cur.getAmount() < cur.getMaxStackSize() && drop.getItemStack().getAmount() == 1) { + // Only one item is dropped + cur.setAmount(cur.getAmount() + 1); + player.getInventory().setItemInHand(cur); + } else { + // Fallback + player.getInventory().addItem(drop.getItemStack()); + } + return null; + } + // CraftBukkit end + // Paper start - remove player from map on drop + if (itemstack.getItem() == Items.FILLED_MAP) { + WorldMap worldmap = ItemWorldMap.getSavedMap(itemstack, this.world); + worldmap.updateSeenPlayers(this, itemstack); + } + // Paper stop + + ItemStack itemstack1 = this.a(entityitem); + + if (flag1) { + if (!itemstack1.isEmpty()) { + this.a(StatisticList.ITEM_DROPPED.b(itemstack1.getItem()), itemstack.getCount()); + } + + this.a(StatisticList.DROP); + } + + return entityitem; + } + } + + protected ItemStack a(EntityItem entityitem) { + this.world.addEntity(entityitem); + return entityitem.getItemStack(); + } + + public float b(IBlockData iblockdata) { + float f = this.inventory.a(iblockdata); + + if (f > 1.0F) { + int i = EnchantmentManager.getDigSpeedEnchantmentLevel(this); + ItemStack itemstack = this.getItemInMainHand(); + + if (i > 0 && !itemstack.isEmpty()) { + f += (float) (i * i + 1); + } + } + + if (MobEffectUtil.a(this)) { + f *= 1.0F + (float) (MobEffectUtil.b(this) + 1) * 0.2F; + } + + if (this.hasEffect(MobEffects.SLOWER_DIG)) { + float f1; + + switch (this.getEffect(MobEffects.SLOWER_DIG).getAmplifier()) { + case 0: + f1 = 0.3F; + break; + case 1: + f1 = 0.09F; + break; + case 2: + f1 = 0.0027F; + break; + case 3: + default: + f1 = 8.1E-4F; + } + + f *= f1; + } + + if (this.a(TagsFluid.WATER) && !EnchantmentManager.h((EntityLiving) this)) { + f /= 5.0F; + } + + if (!this.onGround) { + f /= 5.0F; + } + + return f; + } + + public boolean hasBlock(IBlockData iblockdata) { + return iblockdata.getMaterial().isAlwaysDestroyable() || this.inventory.b(iblockdata); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.a(a(this.h)); + NBTTagList nbttaglist = nbttagcompound.getList("Inventory", 10); + + this.inventory.b(nbttaglist); + this.inventory.itemInHandIndex = nbttagcompound.getInt("SelectedItemSlot"); + this.sleeping = nbttagcompound.getBoolean("Sleeping"); + this.sleepTicks = nbttagcompound.getShort("SleepTimer"); + this.exp = nbttagcompound.getFloat("XpP"); + this.expLevel = nbttagcompound.getInt("XpLevel"); + this.expTotal = nbttagcompound.getInt("XpTotal"); + this.bZ = nbttagcompound.getInt("XpSeed"); + if (this.bZ == 0) { + this.bZ = this.random.nextInt(); + } + + this.setScore(nbttagcompound.getInt("Score")); + if (this.sleeping) { + this.bedPosition = new BlockPosition(this); + this.a(true, true, false); + } + + // CraftBukkit start + this.spawnWorld = nbttagcompound.getString("SpawnWorld"); + if ("".equals(spawnWorld)) { + this.spawnWorld = this.world.getServer().getWorlds().get(0).getName(); + } + // CraftBukkit end + + if (nbttagcompound.hasKeyOfType("SpawnX", 99) && nbttagcompound.hasKeyOfType("SpawnY", 99) && nbttagcompound.hasKeyOfType("SpawnZ", 99)) { + this.e = new BlockPosition(nbttagcompound.getInt("SpawnX"), nbttagcompound.getInt("SpawnY"), nbttagcompound.getInt("SpawnZ")); + this.f = nbttagcompound.getBoolean("SpawnForced"); + } + + this.foodData.a(nbttagcompound); + this.abilities.b(nbttagcompound); + if (nbttagcompound.hasKeyOfType("EnderItems", 9)) { + this.enderChest.a(nbttagcompound.getList("EnderItems", 10)); + } + + if (nbttagcompound.hasKeyOfType("ShoulderEntityLeft", 10)) { + this.setShoulderEntityLeft(nbttagcompound.getCompound("ShoulderEntityLeft")); + } + + if (nbttagcompound.hasKeyOfType("ShoulderEntityRight", 10)) { + this.setShoulderEntityRight(nbttagcompound.getCompound("ShoulderEntityRight")); + } + + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("DataVersion", 1631); + nbttagcompound.set("Inventory", this.inventory.a(new NBTTagList())); + nbttagcompound.setInt("SelectedItemSlot", this.inventory.itemInHandIndex); + nbttagcompound.setBoolean("Sleeping", this.sleeping); + nbttagcompound.setShort("SleepTimer", (short) this.sleepTicks); + nbttagcompound.setFloat("XpP", this.exp); + nbttagcompound.setInt("XpLevel", this.expLevel); + nbttagcompound.setInt("XpTotal", this.expTotal); + nbttagcompound.setInt("XpSeed", this.bZ); + nbttagcompound.setInt("Score", this.getScore()); + if (this.e != null) { + nbttagcompound.setInt("SpawnX", this.e.getX()); + nbttagcompound.setInt("SpawnY", this.e.getY()); + nbttagcompound.setInt("SpawnZ", this.e.getZ()); + nbttagcompound.setBoolean("SpawnForced", this.f); + } + + this.foodData.b(nbttagcompound); + this.abilities.a(nbttagcompound); + nbttagcompound.set("EnderItems", this.enderChest.i()); + if (!this.getShoulderEntityLeft().isEmpty()) { + nbttagcompound.set("ShoulderEntityLeft", this.getShoulderEntityLeft()); + } + + if (!this.getShoulderEntityRight().isEmpty()) { + nbttagcompound.set("ShoulderEntityRight", this.getShoulderEntityRight()); + } + nbttagcompound.setString("SpawnWorld", spawnWorld); // CraftBukkit - fixes bed spawns for multiworld worlds + + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else if (this.abilities.isInvulnerable && !damagesource.ignoresInvulnerability()) { + return false; + } else { + this.ticksFarFromPlayer = 0; + if (this.getHealth() <= 0.0F) { + return false; + } else { + if (this.isSleeping() && !this.world.isClientSide) { + this.a(true, true, false); + } + + // this.releaseShoulderEntities(); // CraftBukkit - moved down + if (damagesource.s()) { + if (this.world.getDifficulty() == EnumDifficulty.PEACEFUL) { + return false; // CraftBukkit - f = 0.0f -> return false + } + + if (this.world.getDifficulty() == EnumDifficulty.EASY) { + f = Math.min(f / 2.0F + 1.0F, f); + } + + if (this.world.getDifficulty() == EnumDifficulty.HARD) { + f = f * 3.0F / 2.0F; + } + } + + // CraftBukkit start - Don't filter out 0 damage + boolean damaged = super.damageEntity(damagesource, f); + if (damaged) { + this.releaseShoulderEntities(); + } + return damaged; + // CraftBukkit end + } + } + } + + protected void c(EntityLiving entityliving) { + super.c(entityliving); + if (entityliving.getItemInMainHand().getItem() instanceof ItemAxe) { + this.p(true); + } + + } + + public boolean a(EntityHuman entityhuman) { + // CraftBukkit start - Change to check OTHER player's scoreboard team according to API + // To summarize this method's logic, it's "Can parameter hurt this" + org.bukkit.scoreboard.Team team; + if (entityhuman instanceof EntityPlayer) { + EntityPlayer thatPlayer = (EntityPlayer) entityhuman; + team = thatPlayer.getBukkitEntity().getScoreboard().getPlayerTeam(thatPlayer.getBukkitEntity()); + if (team == null || team.allowFriendlyFire()) { + return true; + } + } else { + // This should never be called, but is implemented anyway + org.bukkit.OfflinePlayer thisPlayer = entityhuman.world.getServer().getOfflinePlayer(entityhuman.getName()); + team = entityhuman.world.getServer().getScoreboardManager().getMainScoreboard().getPlayerTeam(thisPlayer); + if (team == null || team.allowFriendlyFire()) { + return true; + } + } + + if (this instanceof EntityPlayer) { + return !team.hasPlayer(((EntityPlayer) this).getBukkitEntity()); + } + return !team.hasPlayer(this.world.getServer().getOfflinePlayer(this.getName())); + // CraftBukkit end + } + + protected void damageArmor(float f) { + this.inventory.a(f); + } + + protected void damageShield(float f) { + if (f >= 3.0F && this.activeItem.getItem() == Items.SHIELD) { + int i = 1 + MathHelper.d(f); + + this.activeItem.damage(i, this); + if (this.activeItem.isEmpty()) { + EnumHand enumhand = this.cU(); + + if (enumhand == EnumHand.MAIN_HAND) { + this.setSlot(EnumItemSlot.MAINHAND, ItemStack.a); + } else { + this.setSlot(EnumItemSlot.OFFHAND, ItemStack.a); + } + + this.activeItem = ItemStack.a; + this.a(SoundEffects.ITEM_SHIELD_BREAK, 0.8F, 0.8F + this.world.random.nextFloat() * 0.4F); + } + } + + } + + public float dk() { + int i = 0; + Iterator iterator = this.inventory.armor.iterator(); + + while (iterator.hasNext()) { + ItemStack itemstack = (ItemStack) iterator.next(); + + if (!itemstack.isEmpty()) { + ++i; + } + } + + return (float) i / (float) this.inventory.armor.size(); + } + + // CraftBukkit start + protected boolean damageEntity0(DamageSource damagesource, float f) { // void -> boolean + if (true) { + return super.damageEntity0(damagesource, f); + } + // CraftBukkit end + if (!this.isInvulnerable(damagesource)) { + f = this.applyArmorModifier(damagesource, f); + f = this.applyMagicModifier(damagesource, f); + float f1 = f; + + f = Math.max(f - this.getAbsorptionHearts(), 0.0F); + this.setAbsorptionHearts(this.getAbsorptionHearts() - (f1 - f)); + float f2 = f1 - f; + + if (f2 > 0.0F && f2 < 3.4028235E37F) { + this.a(StatisticList.DAMAGE_ABSORBED, Math.round(f2 * 10.0F)); + } + + if (f != 0.0F) { + this.applyExhaustion(damagesource.getExhaustionCost()); + float f3 = this.getHealth(); + + this.setHealth(this.getHealth() - f); + this.getCombatTracker().trackDamage(damagesource, f3, f); + if (f < 3.4028235E37F) { + this.a(StatisticList.DAMAGE_TAKEN, Math.round(f * 10.0F)); + } + + } + } + return false; // CraftBukkit + } + + public void openSign(TileEntitySign tileentitysign) {} + + public void a(CommandBlockListenerAbstract commandblocklistenerabstract) {} + + public void a(TileEntityCommand tileentitycommand) {} + + public void a(TileEntityStructure tileentitystructure) {} + + public void openTrade(IMerchant imerchant) {} + + public void openContainer(IInventory iinventory) {} + + public void openHorseInventory(EntityHorseAbstract entityhorseabstract, IInventory iinventory) {} + + public void openTileEntity(ITileEntityContainer itileentitycontainer) {} + + public void a(ItemStack itemstack, EnumHand enumhand) {} + + public EnumInteractionResult a(Entity entity, EnumHand enumhand) { + if (this.isSpectator()) { + if (entity instanceof IInventory) { + this.openContainer((IInventory) entity); + } + + return EnumInteractionResult.PASS; + } else { + ItemStack itemstack = this.b(enumhand); + ItemStack itemstack1 = itemstack.isEmpty() ? ItemStack.a : itemstack.cloneItemStack(); + + if (entity.b(this, enumhand)) { + if (this.abilities.canInstantlyBuild && itemstack == this.b(enumhand) && itemstack.getCount() < itemstack1.getCount()) { + itemstack.setCount(itemstack1.getCount()); + } + + return EnumInteractionResult.SUCCESS; + } else { + if (!itemstack.isEmpty() && entity instanceof EntityLiving) { + if (this.abilities.canInstantlyBuild) { + itemstack = itemstack1; + } + + if (itemstack.a(this, (EntityLiving) entity, enumhand)) { + if (itemstack.isEmpty() && !this.abilities.canInstantlyBuild) { + this.a(enumhand, ItemStack.a); + } + + return EnumInteractionResult.SUCCESS; + } + } + + return EnumInteractionResult.PASS; + } + } + } + + public double aI() { + return -0.35D; + } + + // Paper start + public void stopRiding() { stopRiding(false); } + public void stopRiding(boolean suppressCancellation) { + // Paper end + super.stopRiding(suppressCancellation); // Paper - suppress + this.k = 0; + } + + // Paper start - send SoundEffect to everyone who can see fromEntity + private static void sendSoundEffect(EntityHuman fromEntity, double x, double y, double z, SoundEffect soundEffect, SoundCategory soundCategory, float volume, float pitch) { + fromEntity.world.sendSoundEffect(fromEntity, x, y, z, soundEffect, soundCategory, volume, pitch); // This will not send the effect to the entity himself + if (fromEntity instanceof EntityPlayer) { + ((EntityPlayer) fromEntity).playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(soundEffect, soundCategory, x, y, z, volume, pitch)); + } + } + // Paper end + + public void attack(Entity entity) { + if (entity.bk()) { + if (!entity.t(this)) { + float f = (float) this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).getValue(); + float f1; + + if (entity instanceof EntityLiving) { + f1 = EnchantmentManager.a(this.getItemInMainHand(), ((EntityLiving) entity).getMonsterType()); + } else { + f1 = EnchantmentManager.a(this.getItemInMainHand(), EnumMonsterType.UNDEFINED); + } + + float f2 = this.r(0.5F); + + f *= 0.2F + f2 * f2 * 0.8F; + f1 *= f2; + this.dH(); + if (f > 0.0F || f1 > 0.0F) { + boolean flag = f2 > 0.9F; + boolean flag1 = false; + byte b0 = 0; + int i = b0 + EnchantmentManager.b((EntityLiving) this); + + if (this.isSprinting() && flag) { + sendSoundEffect(this, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_PLAYER_ATTACK_KNOCKBACK, this.bV(), 1.0F, 1.0F); // Paper - send while respecting visibility + ++i; + flag1 = true; + } + + boolean flag2 = flag && this.fallDistance > 0.0F && !this.onGround && !this.z_() && !this.isInWater() && !this.hasEffect(MobEffects.BLINDNESS) && !this.isPassenger() && entity instanceof EntityLiving; + + flag2 = flag2 && !world.paperConfig.disablePlayerCrits; // Paper + flag2 = flag2 && !this.isSprinting(); + if (flag2) { + f *= 1.5F; + } + + f += f1; + boolean flag3 = false; + double d0 = (double) (this.K - this.J); + + if (flag && !flag2 && !flag1 && this.onGround && d0 < (double) this.cK()) { + ItemStack itemstack = this.b(EnumHand.MAIN_HAND); + + if (itemstack.getItem() instanceof ItemSword) { + flag3 = true; + } + } + + float f3 = 0.0F; + boolean flag4 = false; + int j = EnchantmentManager.getFireAspectEnchantmentLevel(this); + + if (entity instanceof EntityLiving) { + f3 = ((EntityLiving) entity).getHealth(); + if (j > 0 && !entity.isBurning()) { + // CraftBukkit start - Call a combust event when somebody hits with a fire enchanted item + EntityCombustByEntityEvent combustEvent = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), 1); + org.bukkit.Bukkit.getPluginManager().callEvent(combustEvent); + + if (!combustEvent.isCancelled()) { + flag4 = true; + entity.setOnFire(combustEvent.getDuration(), false); + } + // CraftBukkit end + } + } + + double d1 = entity.motX; + double d2 = entity.motY; + double d3 = entity.motZ; + boolean flag5 = entity.damageEntity(DamageSource.playerAttack(this), f); + + if (flag5) { + if (i > 0) { + if (entity instanceof EntityLiving) { + ((EntityLiving) entity).a(this, (float) i * 0.5F, (double) MathHelper.sin(this.yaw * 0.017453292F), (double) (-MathHelper.cos(this.yaw * 0.017453292F))); + } else { + entity.f((double) (-MathHelper.sin(this.yaw * 0.017453292F) * (float) i * 0.5F), 0.1D, (double) (MathHelper.cos(this.yaw * 0.017453292F) * (float) i * 0.5F)); + } + + this.motX *= 0.6D; + this.motZ *= 0.6D; + // Paper start - Configuration option to disable automatic sprint interruption + if (!world.paperConfig.disableSprintInterruptionOnAttack) { + this.setSprinting(false); + } + // Paper end + } + + if (flag3) { + float f4 = 1.0F + EnchantmentManager.a((EntityLiving) this) * f; + List list = this.world.a(EntityLiving.class, entity.getBoundingBox().grow(1.0D, 0.25D, 1.0D)); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityLiving entityliving = (EntityLiving) iterator.next(); + + if (entityliving != this && entityliving != entity && !this.r(entityliving) && (!(entityliving instanceof EntityArmorStand) || !((EntityArmorStand) entityliving).isMarker()) && this.h(entityliving) < 9.0D) { + // CraftBukkit start - Only apply knockback if the damage hits + if (entityliving.damageEntity(DamageSource.playerAttack(this).sweep(), f4)) { + entityliving.a(this, 0.4F, (double) MathHelper.sin(this.yaw * 0.017453292F), (double) (-MathHelper.cos(this.yaw * 0.017453292F))); + } + // CraftBukkit end + } + } + + sendSoundEffect(this, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_PLAYER_ATTACK_SWEEP, this.bV(), 1.0F, 1.0F); // Paper - send while respecting visibility + this.dl(); + } + + if (entity instanceof EntityPlayer && entity.velocityChanged) { + // CraftBukkit start - Add Velocity Event + boolean cancelled = false; + Player player = (Player) entity.getBukkitEntity(); + org.bukkit.util.Vector velocity = new Vector( d1, d2, d3 ); + + PlayerVelocityEvent event = new PlayerVelocityEvent(player, velocity.clone()); + world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + cancelled = true; + } else if (!velocity.equals(event.getVelocity())) { + player.setVelocity(event.getVelocity()); + } + + if (!cancelled) { + ((EntityPlayer) entity).playerConnection.sendPacket(new PacketPlayOutEntityVelocity(entity)); + entity.velocityChanged = false; + entity.motX = d1; + entity.motY = d2; + entity.motZ = d3; + } + // CraftBukkit end + } + + if (flag2) { + sendSoundEffect(this, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_PLAYER_ATTACK_CRIT, this.bV(), 1.0F, 1.0F); // Paper - send while respecting visibility + this.a(entity); + } + + if (!flag2 && !flag3) { + if (flag) { + sendSoundEffect(this, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_PLAYER_ATTACK_STRONG, this.bV(), 1.0F, 1.0F); // Paper - send while respecting visibility + } else { + sendSoundEffect(this, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_PLAYER_ATTACK_WEAK, this.bV(), 1.0F, 1.0F); // Paper - send while respecting visibility + } + } + + if (f1 > 0.0F) { + this.b(entity); + } + + this.z(entity); + if (entity instanceof EntityLiving) { + EnchantmentManager.a((EntityLiving) entity, (Entity) this); + } + + EnchantmentManager.b((EntityLiving) this, entity); + ItemStack itemstack1 = this.getItemInMainHand(); + Object object = entity; + + if (entity instanceof EntityComplexPart) { + IComplex icomplex = ((EntityComplexPart) entity).owner; + + if (icomplex instanceof EntityLiving) { + object = (EntityLiving) icomplex; + } + } + + if (!itemstack1.isEmpty() && object instanceof EntityLiving) { + itemstack1.a((EntityLiving) object, this); + if (itemstack1.isEmpty()) { + this.a(EnumHand.MAIN_HAND, ItemStack.a); + } + } + + if (entity instanceof EntityLiving) { + float f5 = f3 - ((EntityLiving) entity).getHealth(); + + this.a(StatisticList.DAMAGE_DEALT, Math.round(f5 * 10.0F)); + if (j > 0) { + // CraftBukkit start - Call a combust event when somebody hits with a fire enchanted item + EntityCombustByEntityEvent combustEvent = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), j * 4); + org.bukkit.Bukkit.getPluginManager().callEvent(combustEvent); + + if (!combustEvent.isCancelled()) { + entity.setOnFire(combustEvent.getDuration()); + } + // CraftBukkit end + } + + if (this.world instanceof WorldServer && f5 > 2.0F) { + int k = (int) ((double) f5 * 0.5D); + + ((WorldServer) this.world).a(Particles.i, entity.locX, entity.locY + (double) (entity.length * 0.5F), entity.locZ, k, 0.1D, 0.0D, 0.1D, 0.2D); + } + } + + this.applyExhaustion(world.spigotConfig.combatExhaustion); // Spigot - Change to use configurable value + } else { + sendSoundEffect(this, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_PLAYER_ATTACK_NODAMAGE, this.bV(), 1.0F, 1.0F); // Paper - send while respecting visibility + if (flag4) { + entity.extinguish(); + } + // CraftBukkit start - resync on cancelled event + if (this instanceof EntityPlayer) { + ((EntityPlayer) this).getBukkitEntity().updateInventory(); + } + // CraftBukkit end + } + } + + } + } + } + + protected void d(EntityLiving entityliving) { + this.attack(entityliving); + } + + public void p(boolean flag) { + float f = 0.25F + (float) EnchantmentManager.getDigSpeedEnchantmentLevel(this) * 0.05F; + + if (flag) { + f += 0.75F; + } + + if (this.random.nextFloat() < f) { + this.getCooldownTracker().a(Items.SHIELD, 100); + this.da(); + this.world.broadcastEntityEffect(this, (byte) 30); + } + + } + + public void a(Entity entity) {} + + public void b(Entity entity) {} + + public void dl() { + double d0 = (double) (-MathHelper.sin(this.yaw * 0.017453292F)); + double d1 = (double) MathHelper.cos(this.yaw * 0.017453292F); + + if (this.world instanceof WorldServer) { + ((WorldServer) this.world).a(Particles.O, this.locX + d0, this.locY + (double) this.length * 0.5D, this.locZ + d1, 0, d0, 0.0D, d1, 0.0D); + } + + } + + public void die() { + super.die(); + this.defaultContainer.b(this); + if (this.activeContainer != null) { + this.activeContainer.b(this); + } + + } + + public boolean inBlock() { + return !this.sleeping && super.inBlock(); + } + + public boolean dn() { + return false; + } + + public GameProfile getProfile() { + return this.h; + } + + // CraftBukkit start - moved bed result checks from below into separate method + private EntityHuman.EnumBedResult getBedResult(BlockPosition blockposition, EnumDirection enumdirection) { + if (!this.world.isClientSide) { + if (this.isSleeping() || !this.isAlive()) { + return EntityHuman.EnumBedResult.OTHER_PROBLEM; + } + + // CraftBukkit - moved world and biome check from BlockBed interact handling here + if (!world.worldProvider.canRespawn() || world.getBiome(blockposition) == Biomes.NETHER || !this.world.worldProvider.isOverworld()) { + return EntityHuman.EnumBedResult.NOT_POSSIBLE_HERE; + } + + if (this.world.L()) { + return EntityHuman.EnumBedResult.NOT_POSSIBLE_NOW; + } + + if (!this.a(blockposition, enumdirection)) { + return EntityHuman.EnumBedResult.TOO_FAR_AWAY; + } + + if (!this.u()) { + double d0 = 8.0D; + double d1 = 5.0D; + List list = this.world.a(EntityMonster.class, new AxisAlignedBB((double) blockposition.getX() - 8.0D, (double) blockposition.getY() - 5.0D, (double) blockposition.getZ() - 8.0D, (double) blockposition.getX() + 8.0D, (double) blockposition.getY() + 5.0D, (double) blockposition.getZ() + 8.0D), (Predicate) (new EntityHuman.c(this))); + + if (!list.isEmpty()) { + return EntityHuman.EnumBedResult.NOT_SAFE; + } + } + } + return EntityHuman.EnumBedResult.OK; + } + + public EntityHuman.EnumBedResult a(BlockPosition blockposition) { + // CraftBukkit start - moved checks into separate method above, add force + return this.a(blockposition, false); + } + + public EntityHuman.EnumBedResult a(BlockPosition blockposition, boolean force) { + EnumDirection enumdirection = (EnumDirection) this.world.getType(blockposition).get(BlockFacingHorizontal.FACING); + EntityHuman.EnumBedResult bedResult = force ? EnumBedResult.OK : this.getBedResult(blockposition, enumdirection); + + if (bedResult == EntityHuman.EnumBedResult.OTHER_PROBLEM) { + return bedResult; // return immediately if the result is not bypassable by plugins + } + + if (this.getBukkitEntity() instanceof Player) { + bedResult = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBedEnterEvent(this, blockposition, bedResult); + + if (bedResult != EntityHuman.EnumBedResult.OK) { + return bedResult; + } + } + // CraftBukkit end + + if (this.isPassenger()) { + this.stopRiding(); + } + + this.releaseShoulderEntities(); + this.a(StatisticList.CUSTOM.b(StatisticList.TIME_SINCE_REST)); + this.setSize(0.2F, 0.2F); + if (this.world.isLoaded(blockposition)) { + float f = 0.5F + (float) enumdirection.getAdjacentX() * 0.4F; + float f1 = 0.5F + (float) enumdirection.getAdjacentZ() * 0.4F; + + this.a(enumdirection); + this.setPosition((double) ((float) blockposition.getX() + f), (double) ((float) blockposition.getY() + 0.6875F), (double) ((float) blockposition.getZ() + f1)); + } else { + this.setPosition((double) ((float) blockposition.getX() + 0.5F), (double) ((float) blockposition.getY() + 0.6875F), (double) ((float) blockposition.getZ() + 0.5F)); + } + + this.sleeping = true; + this.sleepTicks = 0; + this.bedPosition = blockposition; + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + if (!this.world.isClientSide) { + this.world.everyoneSleeping(); + } + + return EntityHuman.EnumBedResult.OK; + } + + private boolean a(BlockPosition blockposition, EnumDirection enumdirection) { + if (Math.abs(this.locX - (double) blockposition.getX()) <= 3.0D && Math.abs(this.locY - (double) blockposition.getY()) <= 2.0D && Math.abs(this.locZ - (double) blockposition.getZ()) <= 3.0D) { + return true; + } else { + BlockPosition blockposition1 = blockposition.shift(enumdirection.opposite()); + + return Math.abs(this.locX - (double) blockposition1.getX()) <= 3.0D && Math.abs(this.locY - (double) blockposition1.getY()) <= 2.0D && Math.abs(this.locZ - (double) blockposition1.getZ()) <= 3.0D; + } + } + + private void a(EnumDirection enumdirection) { + this.bS = -1.8F * (float) enumdirection.getAdjacentX(); + this.bT = -1.8F * (float) enumdirection.getAdjacentZ(); + } + + public void a(boolean flag, boolean flag1, boolean flag2) { + this.setSize(0.6F, 1.8F); + IBlockData iblockdata = this.world.getType(this.bedPosition); + + if (this.bedPosition != null && iblockdata.getBlock() instanceof BlockBed) { + this.world.setTypeAndData(this.bedPosition, (IBlockData) iblockdata.set(BlockBed.OCCUPIED, false), 4); + BlockPosition blockposition = BlockBed.a(this.world, this.bedPosition, 0); + + if (blockposition == null) { + blockposition = this.bedPosition.up(); + } + + this.setPosition((double) ((float) blockposition.getX() + 0.5F), (double) ((float) blockposition.getY() + 0.1F), (double) ((float) blockposition.getZ() + 0.5F)); + } + + this.sleeping = false; + if (!this.world.isClientSide && flag1) { + this.world.everyoneSleeping(); + } + + // CraftBukkit start - fire PlayerBedLeaveEvent + if (this.getBukkitEntity() instanceof Player) { + Player player = (Player) this.getBukkitEntity(); + + org.bukkit.block.Block bed; + BlockPosition blockposition = this.bedPosition; + if (blockposition != null) { + bed = this.world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + } else { + bed = this.world.getWorld().getBlockAt(player.getLocation()); + } + + PlayerBedLeaveEvent event = new PlayerBedLeaveEvent(player, bed, flag2); + this.world.getServer().getPluginManager().callEvent(event); + flag2 = event.shouldSetSpawnLocation(); + } + // CraftBukkit end + + this.sleepTicks = flag ? 0 : 100; + if (flag2) { + this.setRespawnPosition(this.bedPosition, false); + } + + } + + private boolean p() { + return this.world.getType(this.bedPosition).getBlock() instanceof BlockBed; + } + + @Nullable + public static BlockPosition getBed(IBlockAccess iblockaccess, BlockPosition blockposition, boolean flag) { + Block block = iblockaccess.getType(blockposition).getBlock(); + + if (!(block instanceof BlockBed)) { + if (!flag) { + return null; + } else { + boolean flag1 = block.a(); + boolean flag2 = iblockaccess.getType(blockposition.up()).getBlock().a(); + + return flag1 && flag2 ? blockposition : null; + } + } else { + return BlockBed.a(iblockaccess, blockposition, 0); + } + } + + public boolean isSleeping() { + return this.sleeping; + } + + public boolean isDeeplySleeping() { + return this.sleeping && this.sleepTicks >= 100; + } + + public void a(IChatBaseComponent ichatbasecomponent, boolean flag) {} + + public BlockPosition getBed() { + return this.e; + } + + public boolean isRespawnForced() { + return this.f; + } + + public void setRespawnPosition(BlockPosition blockposition, boolean flag) { + if (blockposition != null) { + this.e = blockposition; + this.f = flag; + this.spawnWorld = this.world.worldData.getName(); // CraftBukkit + } else { + this.e = null; + this.f = false; + this.spawnWorld = ""; // CraftBukkit + } + + } + + public void a(MinecraftKey minecraftkey) { + this.b(StatisticList.CUSTOM.b(minecraftkey)); + } + + public void a(MinecraftKey minecraftkey, int i) { + this.a(StatisticList.CUSTOM.b(minecraftkey), i); + } + + public void b(Statistic statistic) { + this.a(statistic, 1); + } + + public void a(Statistic statistic, int i) {} + + public void a(Statistic statistic) {} + + public int discoverRecipes(Collection collection) { + return 0; + } + + public void a(MinecraftKey[] aminecraftkey) {} + + public int undiscoverRecipes(Collection collection) { + return 0; + } + + public void jump() { this.cH(); } // Paper - OBFHELPER + public void cH() { + super.cH(); + this.a(StatisticList.JUMP); + if (this.isSprinting()) { + this.applyExhaustion(world.spigotConfig.jumpSprintExhaustion); // Spigot - Change to use configurable value + } else { + this.applyExhaustion(world.spigotConfig.jumpWalkExhaustion); // Spigot - Change to use configurable value + } + + } + + public void a(float f, float f1, float f2) { + double d0 = this.locX; + double d1 = this.locY; + double d2 = this.locZ; + double d3; + + if (this.isSwimming() && !this.isPassenger()) { + d3 = this.aN().y; + double d4 = d3 < -0.2D ? 0.085D : 0.06D; + + if (d3 <= 0.0D || this.bg || !this.world.getType(new BlockPosition(this.locX, this.locY + 1.0D - 0.1D, this.locZ)).s().e()) { + this.motY += (d3 - this.motY) * d4; + } + } + + if (this.abilities.isFlying && !this.isPassenger()) { + d3 = this.motY; + float f3 = this.aU; + + this.aU = this.abilities.a() * (float) (this.isSprinting() ? 2 : 1); + super.a(f, f1, f2); + this.motY = d3 * 0.6D; + this.aU = f3; + this.fallDistance = 0.0F; + // CraftBukkit start + if (getFlag(7) && !org.bukkit.craftbukkit.event.CraftEventFactory.callToggleGlideEvent(this, false).isCancelled()) { + this.setFlag(7, false); + } + // CraftBukkit end + } else { + super.a(f, f1, f2); + } + + this.checkMovement(this.locX - d0, this.locY - d1, this.locZ - d2); + } + + public void as() { + if (this.abilities.isFlying) { + this.setSwimming(false); + } else { + super.as(); + } + + } + + public float cK() { + return (float) this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue(); + } + + public void checkMovement(double d0, double d1, double d2) { + if (!this.isPassenger()) { + int i; + + if (this.isSwimming()) { + i = Math.round(MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2) * 100.0F); + if (i > 0) { + this.a(StatisticList.SWIM_ONE_CM, i); + this.applyExhaustion(0.01F * (float) i * 0.01F); + } + } else if (this.a(TagsFluid.WATER)) { + i = Math.round(MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2) * 100.0F); + if (i > 0) { + this.a(StatisticList.WALK_UNDER_WATER_ONE_CM, i); + this.applyExhaustion(world.spigotConfig.swimMultiplier * (float) i * 0.01F); // Spigot + } + } else if (this.isInWater()) { + i = Math.round(MathHelper.sqrt(d0 * d0 + d2 * d2) * 100.0F); + if (i > 0) { + this.a(StatisticList.WALK_ON_WATER_ONE_CM, i); + this.applyExhaustion(world.spigotConfig.swimMultiplier * (float) i * 0.01F); // Spigot + } + } else if (this.z_()) { + if (d1 > 0.0D) { + this.a(StatisticList.CLIMB_ONE_CM, (int) Math.round(d1 * 100.0D)); + } + } else if (this.onGround) { + i = Math.round(MathHelper.sqrt(d0 * d0 + d2 * d2) * 100.0F); + if (i > 0) { + if (this.isSprinting()) { + this.a(StatisticList.SPRINT_ONE_CM, i); + this.applyExhaustion(world.spigotConfig.sprintMultiplier * (float) i * 0.01F); // Spigot + } else if (this.isSneaking()) { + this.a(StatisticList.CROUCH_ONE_CM, i); + this.applyExhaustion(world.spigotConfig.otherMultiplier * (float) i * 0.01F); // Spigot + } else { + this.a(StatisticList.WALK_ONE_CM, i); + this.applyExhaustion(world.spigotConfig.otherMultiplier * (float) i * 0.01F); // Spigot + } + } + } else if (this.dc()) { + i = Math.round(MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2) * 100.0F); + this.a(StatisticList.AVIATE_ONE_CM, i); + } else { + i = Math.round(MathHelper.sqrt(d0 * d0 + d2 * d2) * 100.0F); + if (i > 25) { + this.a(StatisticList.FLY_ONE_CM, i); + } + } + + } + } + + private void l(double d0, double d1, double d2) { + if (this.isPassenger()) { + int i = Math.round(MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2) * 100.0F); + + if (i > 0) { + if (this.getVehicle() instanceof EntityMinecartAbstract) { + this.a(StatisticList.MINECART_ONE_CM, i); + } else if (this.getVehicle() instanceof EntityBoat) { + this.a(StatisticList.BOAT_ONE_CM, i); + } else if (this.getVehicle() instanceof EntityPig) { + this.a(StatisticList.PIG_ONE_CM, i); + } else if (this.getVehicle() instanceof EntityHorseAbstract) { + this.a(StatisticList.HORSE_ONE_CM, i); + } + } + } + + } + + public void c(float f, float f1) { + if (!this.abilities.canFly) { + if (f >= 2.0F) { + this.a(StatisticList.FALL_ONE_CM, (int) Math.round((double) f * 100.0D)); + } + + super.c(f, f1); + } + } + + protected void au() { + if (!this.isSpectator()) { + super.au(); + } + + } + + protected SoundEffect m(int i) { + return i > 4 ? SoundEffects.ENTITY_PLAYER_BIG_FALL : SoundEffects.ENTITY_PLAYER_SMALL_FALL; + } + + public void b(EntityLiving entityliving) { + this.b(StatisticList.ENTITY_KILLED.b(entityliving.P())); + } + + public void bh() { + if (!this.abilities.isFlying) { + super.bh(); + } + + } + + public void giveExp(int i) { + this.addScore(i); + this.exp += (float) i / (float) this.getExpToLevel(); + this.expTotal = MathHelper.clamp(this.expTotal + i, 0, Integer.MAX_VALUE); + + while (this.exp < 0.0F) { + float f = this.exp * (float) this.getExpToLevel(); + + if (this.expLevel > 0) { + this.levelDown(-1); + this.exp = 1.0F + f / (float) this.getExpToLevel(); + } else { + this.levelDown(-1); + this.exp = 0.0F; + } + } + + while (this.exp >= 1.0F) { + this.exp = (this.exp - 1.0F) * (float) this.getExpToLevel(); + this.levelDown(1); + this.exp /= (float) this.getExpToLevel(); + } + + } + + public int du() { + return this.bZ; + } + + public void enchantDone(ItemStack itemstack, int i) { + this.expLevel -= i; + if (this.expLevel < 0) { + this.expLevel = 0; + this.exp = 0.0F; + this.expTotal = 0; + } + + this.bZ = this.random.nextInt(); + } + + public void levelDown(int i) { + this.expLevel += i; + if (this.expLevel < 0) { + this.expLevel = 0; + this.exp = 0.0F; + this.expTotal = 0; + } + + if (i > 0 && this.expLevel % 5 == 0 && (float) this.g < (float) this.ticksLived - 100.0F) { + float f = this.expLevel > 30 ? 1.0F : (float) this.expLevel / 30.0F; + + this.world.a((EntityHuman) null, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_PLAYER_LEVELUP, this.bV(), f * 0.75F, 1.0F); + this.g = this.ticksLived; + } + + } + + public int getExpToLevel() { + return this.expLevel >= 30 ? 112 + (this.expLevel - 30) * 9 : (this.expLevel >= 15 ? 37 + (this.expLevel - 15) * 5 : 7 + this.expLevel * 2); + } + + public void applyExhaustion(float f) { + if (!this.abilities.isInvulnerable) { + if (!this.world.isClientSide) { + this.foodData.a(f); + } + + } + } + + public FoodMetaData getFoodData() { + return this.foodData; + } + + public boolean q(boolean flag) { + return !this.abilities.isInvulnerable && (flag || this.foodData.c()); + } + + public boolean dx() { + return this.getHealth() > 0.0F && this.getHealth() < this.getMaxHealth(); + } + + public boolean dy() { + return this.abilities.mayBuild; + } + + public boolean a(BlockPosition blockposition, EnumDirection enumdirection, ItemStack itemstack) { + if (this.abilities.mayBuild) { + return true; + } else { + BlockPosition blockposition1 = blockposition.shift(enumdirection.opposite()); + ShapeDetectorBlock shapedetectorblock = new ShapeDetectorBlock(this.world, blockposition1, false); + + return itemstack.b(this.world.F(), shapedetectorblock); + } + } + + protected int getExpValue(EntityHuman entityhuman) { + if (!this.world.getGameRules().getBoolean("keepInventory") && !this.isSpectator()) { + int i = this.expLevel * 7; + + return i > 100 ? 100 : i; + } else { + return 0; + } + } + + protected boolean alwaysGivesExp() { + return true; + } + + protected boolean playStepSound() { + return !this.abilities.isFlying; + } + + public void updateAbilities() {} + + public void a(EnumGamemode enumgamemode) {} + + public IChatBaseComponent getDisplayName() { + return new ChatComponentText(this.h.getName()); + } + + public InventoryEnderChest getEnderChest() { + return this.enderChest; + } + + public ItemStack getEquipment(EnumItemSlot enumitemslot) { + return enumitemslot == EnumItemSlot.MAINHAND ? this.inventory.getItemInHand() : (enumitemslot == EnumItemSlot.OFFHAND ? (ItemStack) this.inventory.extraSlots.get(0) : (enumitemslot.a() == EnumItemSlot.Function.ARMOR ? (ItemStack) this.inventory.armor.get(enumitemslot.b()) : ItemStack.a)); + } + + public void setSlot(EnumItemSlot enumitemslot, ItemStack itemstack) { + if (enumitemslot == EnumItemSlot.MAINHAND) { + this.b(itemstack); + this.inventory.items.set(this.inventory.itemInHandIndex, itemstack); + } else if (enumitemslot == EnumItemSlot.OFFHAND) { + this.b(itemstack); + this.inventory.extraSlots.set(0, itemstack); + } else if (enumitemslot.a() == EnumItemSlot.Function.ARMOR) { + this.b(itemstack); + this.inventory.armor.set(enumitemslot.b(), itemstack); + } + + } + + public boolean d(ItemStack itemstack) { + this.b(itemstack); + return this.inventory.pickup(itemstack); + } + + public Iterable aS() { + return Lists.newArrayList(new ItemStack[] { this.getItemInMainHand(), this.getItemInOffHand()}); + } + + public Iterable getArmorItems() { + return this.inventory.armor; + } + + public boolean g(NBTTagCompound nbttagcompound) { + if (!this.isPassenger() && this.onGround && !this.isInWater()) { + if (this.getShoulderEntityLeft().isEmpty()) { + this.setShoulderEntityLeft(nbttagcompound); + return true; + } else if (this.getShoulderEntityRight().isEmpty()) { + this.setShoulderEntityRight(nbttagcompound); + return true; + } else { + return false; + } + } else { + return false; + } + } + + protected void releaseShoulderEntities() { + // CraftBukkit start + if (this.spawnEntityFromShoulder(this.getShoulderEntityLeft())) { + this.setShoulderEntityLeft(new NBTTagCompound()); + } + if (this.spawnEntityFromShoulder(this.getShoulderEntityRight())) { + this.setShoulderEntityRight(new NBTTagCompound()); + } + // CraftBukkit end + } + // Paper start + public Entity releaseLeftShoulderEntity() { + Entity entity = this.spawnEntityFromShoulder0(this.getShoulderEntityLeft()); + if (entity != null) { + this.setShoulderEntityLeft(new NBTTagCompound()); + } + return entity; + } + + public Entity releaseRightShoulderEntity() { + Entity entity = this.spawnEntityFromShoulder0(this.getShoulderEntityRight()); + if (entity != null) { + this.setShoulderEntityRight(new NBTTagCompound()); + } + return entity; + } + + // Paper - incase any plugins used NMS to call this... old method signature to avoid other diff + private boolean spawnEntityFromShoulder(@Nullable NBTTagCompound nbttagcompound) { + return spawnEntityFromShoulder0(nbttagcompound) != null; + } + // Paper - Moved to new method that now returns entity, and properly null checks + private Entity spawnEntityFromShoulder0(@Nullable NBTTagCompound nbttagcompound) { // CraftBukkit void->boolean - Paper - return Entity + if (!this.world.isClientSide && nbttagcompound != null && !nbttagcompound.isEmpty()) { // Paper - null check + Entity entity = EntityTypes.a(nbttagcompound, this.world); + if (entity == null) { // Paper - null check + return null; + } + + if (entity instanceof EntityTameableAnimal) { + ((EntityTameableAnimal) entity).setOwnerUUID(this.uniqueID); + } + + entity.setPosition(this.locX, this.locY + 0.699999988079071D, this.locZ); + if (this.world.addEntity(entity, CreatureSpawnEvent.SpawnReason.SHOULDER_ENTITY)) { // CraftBukkit + return entity; + } + } + + return null; + } + // Paper end + + public abstract boolean isSpectator(); + + public boolean isSwimming() { + return !this.abilities.isFlying && !this.isSpectator() && super.isSwimming(); + } + + public abstract boolean u(); + + public boolean bw() { + return !this.abilities.isFlying; + } + + public Scoreboard getScoreboard() { + return this.world.getScoreboard(); + } + + public IChatBaseComponent getScoreboardDisplayName() { + IChatBaseComponent ichatbasecomponent = ScoreboardTeam.a(this.getScoreboardTeam(), this.getDisplayName()); + + return this.c(ichatbasecomponent); + } + + public IChatBaseComponent dC() { + return (new ChatComponentText("")).addSibling(this.getDisplayName()).a(" (").a(this.h.getId().toString()).a(")"); + } + + private IChatBaseComponent c(IChatBaseComponent ichatbasecomponent) { + String s = this.getProfile().getName(); + + return ichatbasecomponent.a((chatmodifier) -> { + chatmodifier.setChatClickable(new ChatClickable(ChatClickable.EnumClickAction.SUGGEST_COMMAND, "/tell " + s + " ")).setChatHoverable(this.bC()).setInsertion(s); + }); + } + + public String getName() { + return this.getProfile().getName(); + } + + public float getHeadHeight() { + float f = 1.62F; + + if (this.isSleeping()) { + f = 0.2F; + } else if (!this.isSwimming() && !this.dc() && this.length != 0.6F) { + if (this.isSneaking() || this.length == 1.65F) { + f -= 0.08F; + } + } else { + f = 0.4F; + } + + return f; + } + + public void setAbsorptionHearts(float f) { + if (f < 0.0F) { + f = 0.0F; + } + + this.getDataWatcher().set(EntityHuman.a, f); + } + + public float getAbsorptionHearts() { + return (Float) this.getDataWatcher().get(EntityHuman.a); + } + + public static UUID a(GameProfile gameprofile) { + UUID uuid = gameprofile.getId(); + + if (uuid == null) { + uuid = getOfflineUUID(gameprofile.getName()); + } + + return uuid; + } + + public static UUID getOfflineUUID(String s) { + return UUID.nameUUIDFromBytes(("OfflinePlayer:" + s).getBytes(StandardCharsets.UTF_8)); + } + + public boolean a(ChestLock chestlock) { + if (chestlock.a()) { + return true; + } else { + ItemStack itemstack = this.getItemInMainHand(); + + return !itemstack.isEmpty() && itemstack.hasName() ? itemstack.getName().getString().equals(chestlock.getKey()) : false; + } + } + + public boolean c(int i, ItemStack itemstack) { + if (i >= 0 && i < this.inventory.items.size()) { + this.inventory.setItem(i, itemstack); + return true; + } else { + EnumItemSlot enumitemslot; + + if (i == 100 + EnumItemSlot.HEAD.b()) { + enumitemslot = EnumItemSlot.HEAD; + } else if (i == 100 + EnumItemSlot.CHEST.b()) { + enumitemslot = EnumItemSlot.CHEST; + } else if (i == 100 + EnumItemSlot.LEGS.b()) { + enumitemslot = EnumItemSlot.LEGS; + } else if (i == 100 + EnumItemSlot.FEET.b()) { + enumitemslot = EnumItemSlot.FEET; + } else { + enumitemslot = null; + } + + if (i == 98) { + this.setSlot(EnumItemSlot.MAINHAND, itemstack); + return true; + } else if (i == 99) { + this.setSlot(EnumItemSlot.OFFHAND, itemstack); + return true; + } else if (enumitemslot == null) { + int j = i - 200; + + if (j >= 0 && j < this.enderChest.getSize()) { + this.enderChest.setItem(j, itemstack); + return true; + } else { + return false; + } + } else { + if (!itemstack.isEmpty()) { + if (!(itemstack.getItem() instanceof ItemArmor) && !(itemstack.getItem() instanceof ItemElytra)) { + if (enumitemslot != EnumItemSlot.HEAD) { + return false; + } + } else if (EntityInsentient.e(itemstack) != enumitemslot) { + return false; + } + } + + this.inventory.setItem(enumitemslot.b() + this.inventory.items.size(), itemstack); + return true; + } + } + } + + public EnumMainHand getMainHand() { + return (Byte) this.datawatcher.get(EntityHuman.by) == 0 ? EnumMainHand.LEFT : EnumMainHand.RIGHT; + } + + public void a(EnumMainHand enummainhand) { + this.datawatcher.set(EntityHuman.by, (byte) (enummainhand == EnumMainHand.LEFT ? 0 : 1)); + } + + public NBTTagCompound getShoulderEntityLeft() { + return (NBTTagCompound) this.datawatcher.get(EntityHuman.bz); + } + + public void setShoulderEntityLeft(NBTTagCompound nbttagcompound) { + this.datawatcher.set(EntityHuman.bz, nbttagcompound); + } + + public NBTTagCompound getShoulderEntityRight() { + return (NBTTagCompound) this.datawatcher.get(EntityHuman.bA); + } + + public void setShoulderEntityRight(NBTTagCompound nbttagcompound) { + this.datawatcher.set(EntityHuman.bA, nbttagcompound); + } + + public float getCooldownPeriod() { return dG(); } // Paper - OBFHELPER + public float dG() { + return (float) (1.0D / this.getAttributeInstance(GenericAttributes.g).getValue() * 20.0D); + } + + public float getCooledAttackStrength(float adjustTicks) { return r(adjustTicks); } // Paper - OBFHELPER + public float r(float f) { + return MathHelper.a(((float) this.aH + f) / this.dG(), 0.0F, 1.0F); + } + + public void resetCooldown() { dH(); } // Paper - OBFHELPER + public void dH() { + this.aH = 0; + } + + public ItemCooldown getCooldownTracker() { + return this.ce; + } + + public void collide(Entity entity) { + if (!this.isSleeping()) { + super.collide(entity); + } + + } + + public float dJ() { + return (float) this.getAttributeInstance(GenericAttributes.j).getValue(); + } + + public boolean isCreativeAndOp() { + return this.abilities.canInstantlyBuild && this.y() >= 2; + } + + static class c implements Predicate { + + private final EntityHuman a; + + private c(EntityHuman entityhuman) { + this.a = entityhuman; + } + + public boolean test(@Nullable EntityMonster entitymonster) { + return entitymonster.c(this.a); + } + } + + public static enum EnumBedResult { + + OK, NOT_POSSIBLE_HERE, NOT_POSSIBLE_NOW, TOO_FAR_AWAY, OTHER_PROBLEM, NOT_SAFE; + + private EnumBedResult() {} + } + + public static enum EnumChatVisibility { + + FULL(0, "options.chat.visibility.full"), SYSTEM(1, "options.chat.visibility.system"), HIDDEN(2, "options.chat.visibility.hidden"); + + private static final EntityHuman.EnumChatVisibility[] d = (EntityHuman.EnumChatVisibility[]) Arrays.stream(values()).sorted(Comparator.comparingInt(EntityHuman.EnumChatVisibility::a)).toArray((i) -> { + return new EntityHuman.EnumChatVisibility[i]; + }); + private final int e; + private final String f; + + private EnumChatVisibility(int i, String s) { + this.e = i; + this.f = s; + } + + public int a() { + return this.e; + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityIllagerIllusioner.java b/src/main/java/net/minecraft/server/EntityIllagerIllusioner.java new file mode 100644 index 000000000000..8b595979e7e5 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityIllagerIllusioner.java @@ -0,0 +1,220 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class EntityIllagerIllusioner extends EntityIllagerWizard implements IRangedEntity { + + private int c; + private final Vec3D[][] bC; + + public EntityIllagerIllusioner(World world) { + super(EntityTypes.ILLUSIONER, world); + this.setSize(0.6F, 1.95F); + this.b_ = 5; + this.bC = new Vec3D[2][4]; + + for (int i = 0; i < 4; ++i) { + this.bC[0][i] = new Vec3D(0.0D, 0.0D, 0.0D); + this.bC[1][i] = new Vec3D(0.0D, 0.0D, 0.0D); + } + + } + + protected void n() { + super.n(); + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(1, new EntityIllagerWizard.b()); + this.goalSelector.a(4, new EntityIllagerIllusioner.b()); + this.goalSelector.a(5, new EntityIllagerIllusioner.a()); + this.goalSelector.a(6, new PathfinderGoalBowShoot<>(this, 0.5D, 20, 15.0F)); + this.goalSelector.a(8, new PathfinderGoalRandomStroll(this, 0.6D)); + this.goalSelector.a(9, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 3.0F, 1.0F)); + this.goalSelector.a(10, new PathfinderGoalLookAtPlayer(this, EntityInsentient.class, 8.0F)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, true, new Class[] { EntityIllagerIllusioner.class})); + this.targetSelector.a(2, (new PathfinderGoalNearestAttackableTarget<>(this, EntityHuman.class, true)).b(300)); + this.targetSelector.a(3, (new PathfinderGoalNearestAttackableTarget<>(this, EntityVillager.class, false)).b(300)); + this.targetSelector.a(3, (new PathfinderGoalNearestAttackableTarget<>(this, EntityIronGolem.class, false)).b(300)); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.5D); + this.getAttributeInstance(GenericAttributes.FOLLOW_RANGE).setValue(18.0D); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(32.0D); + } + + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + this.setSlot(EnumItemSlot.MAINHAND, new ItemStack(Items.BOW)); + return super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + } + + protected void x_() { + super.x_(); + } + + protected MinecraftKey getDefaultLootTable() { + return LootTables.a; + } + + public void movementTick() { + super.movementTick(); + if (this.world.isClientSide && this.isInvisible()) { + --this.c; + if (this.c < 0) { + this.c = 0; + } + + if (this.hurtTicks != 1 && this.ticksLived % 1200 != 0) { + if (this.hurtTicks == this.aC - 1) { + this.c = 3; + + for (int i = 0; i < 4; ++i) { + this.bC[0][i] = this.bC[1][i]; + this.bC[1][i] = new Vec3D(0.0D, 0.0D, 0.0D); + } + } + } else { + this.c = 3; + float f = -6.0F; + boolean flag = true; + + int j; + + for (j = 0; j < 4; ++j) { + this.bC[0][j] = this.bC[1][j]; + this.bC[1][j] = new Vec3D((double) (-6.0F + (float) this.random.nextInt(13)) * 0.5D, (double) Math.max(0, this.random.nextInt(6) - 4), (double) (-6.0F + (float) this.random.nextInt(13)) * 0.5D); + } + + for (j = 0; j < 16; ++j) { + this.world.addParticle(Particles.g, this.locX + (this.random.nextDouble() - 0.5D) * (double) this.width, this.locY + this.random.nextDouble() * (double) this.length, this.locZ + (this.random.nextDouble() - 0.5D) * (double) this.width, 0.0D, 0.0D, 0.0D); + } + + this.world.a(this.locX, this.locY, this.locZ, SoundEffects.ENTITY_ILLUSIONER_MIRROR_MOVE, this.bV(), 1.0F, 1.0F, false); + } + } + + } + + public boolean r(Entity entity) { + return super.r(entity) ? true : (entity instanceof EntityLiving && ((EntityLiving) entity).getMonsterType() == EnumMonsterType.ILLAGER ? this.getScoreboardTeam() == null && entity.getScoreboardTeam() == null : false); + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_ILLUSIONER_AMBIENT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_ILLUSIONER_DEATH; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_ILLUSIONER_HURT; + } + + protected SoundEffect dz() { + return SoundEffects.ENTITY_ILLUSIONER_CAST_SPELL; + } + + public void a(EntityLiving entityliving, float f) { + EntityArrow entityarrow = this.v(f); + double d0 = entityliving.locX - this.locX; + double d1 = entityliving.getBoundingBox().minY + (double) (entityliving.length / 3.0F) - entityarrow.locY; + double d2 = entityliving.locZ - this.locZ; + double d3 = (double) MathHelper.sqrt(d0 * d0 + d2 * d2); + + entityarrow.shoot(d0, d1 + d3 * 0.20000000298023224D, d2, 1.6F, (float) (14 - this.world.getDifficulty().a() * 4)); + // Paper start + org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, this.getItemInMainHand(), this.getItemInOffHand(), entityarrow,0.8F); + if (event.isCancelled()) { + event.getProjectile().remove(); + return; + } + + if (event.getProjectile() == entityarrow.getBukkitEntity()) { + this.world.addEntity(entityarrow); + } + this.a(SoundEffects.ENTITY_SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F)); + // Paper end + } + + protected EntityArrow v(float f) { + EntityTippedArrow entitytippedarrow = new EntityTippedArrow(this.world, this); + + entitytippedarrow.a((EntityLiving) this, f); + return entitytippedarrow; + } + + public void s(boolean flag) { + this.a(1, flag); + } + + class a extends EntityIllagerWizard.c { + + private int e; + + private a() { + super(); + } + + public boolean a() { + return !super.a() ? false : (EntityIllagerIllusioner.this.getGoalTarget() == null ? false : (EntityIllagerIllusioner.this.getGoalTarget().getId() == this.e ? false : EntityIllagerIllusioner.this.world.getDamageScaler(new BlockPosition(EntityIllagerIllusioner.this)).a((float) EnumDifficulty.NORMAL.ordinal()))); + } + + public void c() { + super.c(); + this.e = EntityIllagerIllusioner.this.getGoalTarget().getId(); + } + + protected int g() { + return 20; + } + + protected int i() { + return 180; + } + + protected void j() { + EntityIllagerIllusioner.this.getGoalTarget().addEffect(new MobEffect(MobEffects.BLINDNESS, 400), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit + } + + protected SoundEffect k() { + return SoundEffects.ENTITY_ILLUSIONER_PREPARE_BLINDNESS; + } + + protected EntityIllagerWizard.Spell l() { + return EntityIllagerWizard.Spell.BLINDNESS; + } + } + + class b extends EntityIllagerWizard.c { + + private b() { + super(); + } + + public boolean a() { + return !super.a() ? false : !EntityIllagerIllusioner.this.hasEffect(MobEffects.INVISIBILITY); + } + + protected int g() { + return 20; + } + + protected int i() { + return 340; + } + + protected void j() { + EntityIllagerIllusioner.this.addEffect(new MobEffect(MobEffects.INVISIBILITY, 1200), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ILLUSION); // CraftBukkit + } + + @Nullable + protected SoundEffect k() { + return SoundEffects.ENTITY_ILLUSIONER_PREPARE_MIRROR; + } + + protected EntityIllagerWizard.Spell l() { + return EntityIllagerWizard.Spell.DISAPPEAR; + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityInsentient.java b/src/main/java/net/minecraft/server/EntityInsentient.java new file mode 100644 index 000000000000..3059682a495a --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityInsentient.java @@ -0,0 +1,1312 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.UUID; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityCombustByEntityEvent; +import org.bukkit.event.entity.EntityPickupItemEvent; +import org.bukkit.event.entity.EntityTargetLivingEntityEvent; +import org.bukkit.event.entity.EntityTargetEvent; +import org.bukkit.event.entity.EntityUnleashEvent; +import org.bukkit.event.entity.EntityUnleashEvent.UnleashReason; +// CraftBukkit end + +public abstract class EntityInsentient extends EntityLiving { + + private static final DataWatcherObject a = DataWatcher.a(EntityInsentient.class, DataWatcherRegistry.a); + public int a_; + protected int b_; + protected ControllerLook lookController; + protected ControllerMove moveController; + protected ControllerJump h; + private final EntityAIBodyControl b; + protected NavigationAbstract navigation; + public PathfinderGoalSelector goalSelector; + @Nullable public PathfinderGoalFloat goalFloat; // Paper + public PathfinderGoalSelector targetSelector; + private EntityLiving goalTarget; + private final EntitySenses bC; + private final NonNullList bD; + public float[] dropChanceHand; + private final NonNullList bE; + public float[] dropChanceArmor; + // public boolean canPickUpLoot; // CraftBukkit - moved up to EntityLiving + public boolean persistent; + private final Map bH; + public MinecraftKey lootTableKey; + public long lootTableSeed; + private boolean bK; + private Entity leashHolder; + private NBTTagCompound bM; + + protected EntityInsentient(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.bD = NonNullList.a(2, ItemStack.a); + this.dropChanceHand = new float[2]; + this.bE = NonNullList.a(4, ItemStack.a); + this.dropChanceArmor = new float[4]; + this.bH = Maps.newEnumMap(PathType.class); + this.goalSelector = new PathfinderGoalSelector(world != null && world.methodProfiler != null ? world.methodProfiler : null); + this.targetSelector = new PathfinderGoalSelector(world != null && world.methodProfiler != null ? world.methodProfiler : null); + this.lookController = new ControllerLook(this); + this.moveController = new ControllerMove(this); + this.h = new ControllerJump(this); + this.b = this.o(); + this.navigation = this.b(world); + this.bC = new EntitySenses(this); + Arrays.fill(this.dropChanceArmor, 0.085F); + Arrays.fill(this.dropChanceHand, 0.085F); + if (world != null && !world.isClientSide) { + this.n(); + } + + // CraftBukkit start - default persistance to type's persistance value + this.persistent = !isTypeNotPersistent(); + // CraftBukkit end + } + + protected void n() {} + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeMap().b(GenericAttributes.FOLLOW_RANGE).setValue(16.0D); + } + + protected NavigationAbstract b(World world) { + return new Navigation(this, world); + } + + public float a(PathType pathtype) { + Float ofloat = (Float) this.bH.get(pathtype); + + return ofloat == null ? pathtype.a() : ofloat; + } + + public void a(PathType pathtype, float f) { + this.bH.put(pathtype, f); + } + + protected EntityAIBodyControl o() { + return new EntityAIBodyControl(this); + } + + public ControllerLook getControllerLook() { + return this.lookController; + } + + // Paper start + @Override + public void inactiveTick() { + super.inactiveTick(); + this.goalSelector.inactiveTick(); + if (this.targetSelector.inactiveTick()) { + this.targetSelector.doTick(); + } + } + // Paper end + + public ControllerMove getControllerMove() { + return this.moveController; + } + + public ControllerJump getControllerJump() { + return this.h; + } + + public NavigationAbstract getNavigation() { + return this.navigation; + } + + public EntitySenses getEntitySenses() { + return this.bC; + } + + @Nullable + public EntityLiving getGoalTarget() { + return this.goalTarget; + } + + public org.bukkit.craftbukkit.entity.CraftMob getBukkitMob() { return (org.bukkit.craftbukkit.entity.CraftMob) super.getBukkitEntity(); } // Paper + public void setGoalTarget(@Nullable EntityLiving entityliving) { + // CraftBukkit start - fire event + setGoalTarget(entityliving, EntityTargetEvent.TargetReason.UNKNOWN, true); + } + + public boolean setGoalTarget(EntityLiving entityliving, EntityTargetEvent.TargetReason reason, boolean fireEvent) { + if (getGoalTarget() == entityliving) return false; + if (fireEvent) { + if (reason == EntityTargetEvent.TargetReason.UNKNOWN && getGoalTarget() != null && entityliving == null) { + reason = getGoalTarget().isAlive() ? EntityTargetEvent.TargetReason.FORGOT_TARGET : EntityTargetEvent.TargetReason.TARGET_DIED; + } + if (reason == EntityTargetEvent.TargetReason.UNKNOWN) { + world.getServer().getLogger().log(java.util.logging.Level.WARNING, "Unknown target reason, please report on the issue tracker", new Exception()); + } + CraftLivingEntity ctarget = null; + if (entityliving != null) { + ctarget = (CraftLivingEntity) entityliving.getBukkitEntity(); + } + EntityTargetLivingEntityEvent event = new EntityTargetLivingEntityEvent(this.getBukkitEntity(), ctarget, reason); + world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + + if (event.getTarget() != null) { + entityliving = ((CraftLivingEntity) event.getTarget()).getHandle(); + } else { + entityliving = null; + } + } + this.goalTarget = entityliving; + return true; + // CraftBukkit end + } + + public boolean b(Class oclass) { + return oclass != EntityGhast.class; + } + + public void x() {} + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityInsentient.a, (byte) 0); + } + + public int z() { + return 80; + } + + public void A() { + SoundEffect soundeffect = this.D(); + + if (soundeffect != null) { + this.a(soundeffect, this.cD(), this.cE()); + } + + } + + public void W() { + super.W(); + this.world.methodProfiler.enter("mobBaseTick"); + if (this.isAlive() && this.random.nextInt(1000) < this.a_++) { + this.l(); + this.A(); + } + + this.world.methodProfiler.exit(); + } + + protected void c(DamageSource damagesource) { + this.l(); + super.c(damagesource); + } + + private void l() { + this.a_ = -this.z(); + } + + protected int getExpValue(EntityHuman entityhuman) { + if (this.b_ > 0) { + int i = this.b_; + + int j; + + for (j = 0; j < this.bE.size(); ++j) { + if (!((ItemStack) this.bE.get(j)).isEmpty() && this.dropChanceArmor[j] <= 1.0F) { + i += 1 + this.random.nextInt(3); + } + } + + for (j = 0; j < this.bD.size(); ++j) { + if (!((ItemStack) this.bD.get(j)).isEmpty() && this.dropChanceHand[j] <= 1.0F) { + i += 1 + this.random.nextInt(3); + } + } + + return i; + } else { + return this.b_; + } + } + + public void doSpawnEffect() { + if (this.world.isClientSide) { + for (int i = 0; i < 20; ++i) { + double d0 = this.random.nextGaussian() * 0.02D; + double d1 = this.random.nextGaussian() * 0.02D; + double d2 = this.random.nextGaussian() * 0.02D; + double d3 = 10.0D; + + this.world.addParticle(Particles.J, this.locX + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width - d0 * 10.0D, this.locY + (double) (this.random.nextFloat() * this.length) - d1 * 10.0D, this.locZ + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width - d2 * 10.0D, d0, d1, d2); + } + } else { + this.world.broadcastEntityEffect(this, (byte) 20); + } + + } + + public void tick() { + super.tick(); + if (!this.world.isClientSide) { + this.dl(); + if (this.ticksLived % 5 == 0) { + boolean flag = !(this.bO() instanceof EntityInsentient); + boolean flag1 = !(this.getVehicle() instanceof EntityBoat); + + this.goalSelector.a(1, flag); + this.goalSelector.a(4, flag && flag1); + this.goalSelector.a(2, flag); + } + } + + } + + protected float e(float f, float f1) { + this.b.a(); + return f1; + } + + @Nullable + protected SoundEffect D() { + return null; + } + + @Nullable + protected Item getLoot() { + return null; + } + + protected void dropDeathLoot(boolean flag, int i) { + Item item = this.getLoot(); + + if (item != null) { + int j = this.random.nextInt(3); + + if (i > 0) { + j += this.random.nextInt(i + 1); + } + + for (int k = 0; k < j; ++k) { + this.a((IMaterial) item); + } + } + + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("CanPickUpLoot", this.dj()); + nbttagcompound.setBoolean("PersistenceRequired", this.persistent); + NBTTagList nbttaglist = new NBTTagList(); + + NBTTagCompound nbttagcompound1; + + for (Iterator iterator = this.bE.iterator(); iterator.hasNext(); nbttaglist.add((NBTBase) nbttagcompound1)) { + ItemStack itemstack = (ItemStack) iterator.next(); + + nbttagcompound1 = new NBTTagCompound(); + if (!itemstack.isEmpty()) { + itemstack.save(nbttagcompound1); + } + } + + nbttagcompound.set("ArmorItems", nbttaglist); + NBTTagList nbttaglist1 = new NBTTagList(); + + NBTTagCompound nbttagcompound2; + + for (Iterator iterator1 = this.bD.iterator(); iterator1.hasNext(); nbttaglist1.add((NBTBase) nbttagcompound2)) { + ItemStack itemstack1 = (ItemStack) iterator1.next(); + + nbttagcompound2 = new NBTTagCompound(); + if (!itemstack1.isEmpty()) { + itemstack1.save(nbttagcompound2); + } + } + + nbttagcompound.set("HandItems", nbttaglist1); + NBTTagList nbttaglist2 = new NBTTagList(); + float[] afloat = this.dropChanceArmor; + int i = afloat.length; + + int j; + + for (j = 0; j < i; ++j) { + float f = afloat[j]; + + nbttaglist2.add((NBTBase) (new NBTTagFloat(f))); + } + + nbttagcompound.set("ArmorDropChances", nbttaglist2); + NBTTagList nbttaglist3 = new NBTTagList(); + float[] afloat1 = this.dropChanceHand; + + j = afloat1.length; + + for (int k = 0; k < j; ++k) { + float f1 = afloat1[k]; + + nbttaglist3.add((NBTBase) (new NBTTagFloat(f1))); + } + + nbttagcompound.set("HandDropChances", nbttaglist3); + nbttagcompound.setBoolean("Leashed", this.bK); + if (this.leashHolder != null) { + nbttagcompound2 = new NBTTagCompound(); + if (this.leashHolder instanceof EntityLiving) { + UUID uuid = this.leashHolder.getUniqueID(); + + nbttagcompound2.a("UUID", uuid); + } else if (this.leashHolder instanceof EntityHanging) { + BlockPosition blockposition = ((EntityHanging) this.leashHolder).getBlockPosition(); + + nbttagcompound2.setInt("X", blockposition.getX()); + nbttagcompound2.setInt("Y", blockposition.getY()); + nbttagcompound2.setInt("Z", blockposition.getZ()); + } + + nbttagcompound.set("Leash", nbttagcompound2); + } + + nbttagcompound.setBoolean("LeftHanded", this.isLeftHanded()); + if (this.lootTableKey != null) { + nbttagcompound.setString("DeathLootTable", this.lootTableKey.toString()); + if (this.lootTableSeed != 0L) { + nbttagcompound.setLong("DeathLootTableSeed", this.lootTableSeed); + } + } + + if (this.isNoAI()) { + nbttagcompound.setBoolean("NoAI", this.isNoAI()); + } + + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + + // CraftBukkit start - If looting or persistence is false only use it if it was set after we started using it + if (nbttagcompound.hasKeyOfType("CanPickUpLoot", 1)) { + boolean data = nbttagcompound.getBoolean("CanPickUpLoot"); + if (isLevelAtLeast(nbttagcompound, 1) || data) { + this.p(data); + } + } + + boolean data = nbttagcompound.getBoolean("PersistenceRequired"); + if (isLevelAtLeast(nbttagcompound, 1) || data) { + this.persistent = data; + } + // CraftBukkit end + NBTTagList nbttaglist; + int i; + + if (nbttagcompound.hasKeyOfType("ArmorItems", 9)) { + nbttaglist = nbttagcompound.getList("ArmorItems", 10); + + for (i = 0; i < this.bE.size(); ++i) { + this.bE.set(i, ItemStack.a(nbttaglist.getCompound(i))); + } + } + + if (nbttagcompound.hasKeyOfType("HandItems", 9)) { + nbttaglist = nbttagcompound.getList("HandItems", 10); + + for (i = 0; i < this.bD.size(); ++i) { + this.bD.set(i, ItemStack.a(nbttaglist.getCompound(i))); + } + } + + if (nbttagcompound.hasKeyOfType("ArmorDropChances", 9)) { + nbttaglist = nbttagcompound.getList("ArmorDropChances", 5); + + for (i = 0; i < nbttaglist.size(); ++i) { + this.dropChanceArmor[i] = nbttaglist.l(i); + } + } + + if (nbttagcompound.hasKeyOfType("HandDropChances", 9)) { + nbttaglist = nbttagcompound.getList("HandDropChances", 5); + + for (i = 0; i < nbttaglist.size(); ++i) { + this.dropChanceHand[i] = nbttaglist.l(i); + } + } + + this.bK = nbttagcompound.getBoolean("Leashed"); + if (this.bK && nbttagcompound.hasKeyOfType("Leash", 10)) { + this.bM = nbttagcompound.getCompound("Leash"); + } + + this.r(nbttagcompound.getBoolean("LeftHanded")); + if (nbttagcompound.hasKeyOfType("DeathLootTable", 8)) { + this.lootTableKey = new MinecraftKey(nbttagcompound.getString("DeathLootTable")); + this.lootTableSeed = nbttagcompound.getLong("DeathLootTableSeed"); + } + + this.setNoAI(nbttagcompound.getBoolean("NoAI")); + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return null; + } + // CraftBukkit - start + public MinecraftKey getLootTable() { + return getDefaultLootTable(); + } + // CraftBukkit - end + + protected void a(boolean flag, int i, DamageSource damagesource) { + MinecraftKey minecraftkey = this.lootTableKey; + + if (minecraftkey == null) { + minecraftkey = this.getDefaultLootTable(); + } + + if (minecraftkey != null) { + LootTable loottable = this.world.getMinecraftServer().getLootTableRegistry().getLootTable(minecraftkey); + + this.lootTableKey = null; + LootTableInfo.Builder loottableinfo_builder = (new LootTableInfo.Builder((WorldServer) this.world)).entity(this).damageSource(damagesource).position(new BlockPosition(this)); + + if (flag && this.killer != null) { + loottableinfo_builder = loottableinfo_builder.killer(this.killer).luck(this.killer.dJ()); + } + + Collection collection = loottable.populateLoot(this.lootTableSeed == 0L ? this.random : new Random(this.lootTableSeed), loottableinfo_builder.build()); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + ItemStack itemstack = (ItemStack) iterator.next(); + + this.a_(itemstack); + } + + this.dropEquipment(flag, i); + } else { + super.a(flag, i, damagesource); + } + + } + + public void r(float f) { + this.bj = f; + } + + public void s(float f) { + this.bi = f; + } + + public void t(float f) { + this.bh = f; + } + + public void o(float f) { + super.o(f); + this.r(f); + } + + public void movementTick() { + super.movementTick(); + this.world.methodProfiler.enter("looting"); + if (!this.world.isClientSide && this.dj() && !this.killed && this.world.getGameRules().getBoolean("mobGriefing")) { + List list = this.world.a(EntityItem.class, this.getBoundingBox().grow(1.0D, 0.0D, 1.0D)); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityItem entityitem = (EntityItem) iterator.next(); + + if (!entityitem.dead && !entityitem.getItemStack().isEmpty() && !entityitem.q()) { + // Paper Start + if (!entityitem.canMobPickup) { + continue; + } + // Paper End + this.a(entityitem); + } + } + } + + this.world.methodProfiler.exit(); + } + + protected void a(EntityItem entityitem) { + ItemStack itemstack = entityitem.getItemStack(); + EnumItemSlot enumitemslot = e(itemstack); + ItemStack itemstack1 = this.getEquipment(enumitemslot); + boolean flag = this.a(itemstack, itemstack1, enumitemslot); + + // CraftBukkit start + boolean canPickup = flag && this.d(itemstack); + + EntityPickupItemEvent entityEvent = new EntityPickupItemEvent((LivingEntity) getBukkitEntity(), (org.bukkit.entity.Item) entityitem.getBukkitEntity(), 0); + entityEvent.setCancelled(!canPickup); + this.world.getServer().getPluginManager().callEvent(entityEvent); + canPickup = !entityEvent.isCancelled(); + if (canPickup) { + // CraftBukkit end + double d0 = (double) this.c(enumitemslot); + + if (!itemstack1.isEmpty() && (double) (this.random.nextFloat() - 0.1F) < d0) { + this.forceDrops = true; // CraftBukkit + this.a_(itemstack1); + this.forceDrops = false; // CraftBukkit + } + + this.setSlot(enumitemslot, itemstack); + switch (enumitemslot.a()) { + case HAND: + this.dropChanceHand[enumitemslot.b()] = 2.0F; + break; + case ARMOR: + this.dropChanceArmor[enumitemslot.b()] = 2.0F; + } + + this.persistent = true; + this.receive(entityitem, itemstack.getCount()); + entityitem.die(); + } + + } + + protected boolean a(ItemStack itemstack, ItemStack itemstack1, EnumItemSlot enumitemslot) { + boolean flag = true; + + if (!itemstack1.isEmpty()) { + if (enumitemslot.a() == EnumItemSlot.Function.HAND) { + if (itemstack.getItem() instanceof ItemSword && !(itemstack1.getItem() instanceof ItemSword)) { + flag = true; + } else if (itemstack.getItem() instanceof ItemSword && itemstack1.getItem() instanceof ItemSword) { + ItemSword itemsword = (ItemSword) itemstack.getItem(); + ItemSword itemsword1 = (ItemSword) itemstack1.getItem(); + + if (itemsword.d() == itemsword1.d()) { + flag = itemstack.getDamage() < itemstack1.getDamage() || itemstack.hasTag() && !itemstack1.hasTag(); + } else { + flag = itemsword.d() > itemsword1.d(); + } + } else if (itemstack.getItem() instanceof ItemBow && itemstack1.getItem() instanceof ItemBow) { + flag = itemstack.hasTag() && !itemstack1.hasTag(); + } else { + flag = false; + } + } else if (itemstack.getItem() instanceof ItemArmor && !(itemstack1.getItem() instanceof ItemArmor)) { + flag = true; + } else if (itemstack.getItem() instanceof ItemArmor && itemstack1.getItem() instanceof ItemArmor && !EnchantmentManager.d(itemstack1)) { + ItemArmor itemarmor = (ItemArmor) itemstack.getItem(); + ItemArmor itemarmor1 = (ItemArmor) itemstack1.getItem(); + + if (itemarmor.e() == itemarmor1.e()) { + flag = itemstack.getDamage() < itemstack1.getDamage() || itemstack.hasTag() && !itemstack1.hasTag(); + } else { + flag = itemarmor.e() > itemarmor1.e(); + } + } else { + flag = false; + } + } + + return flag; + } + + protected boolean d(ItemStack itemstack) { + return true; + } + + public boolean isTypeNotPersistent() { + return true; + } + + protected void I() { + if (this.persistent) { + this.ticksFarFromPlayer = 0; + } else { + Chunk currentChunk = getChunkAtLocation(); // Paper + if (currentChunk != null && currentChunk.scheduledForUnload != null) return; // Paper + EntityHuman entityhuman = this.world.findNearbyPlayer(this, -1.0D); + + if (entityhuman != null && entityhuman.affectsSpawning) { // Paper - Affects Spawning API + double d0 = entityhuman.locX - this.locX; + double d1 = entityhuman.locY - this.locY; + double d2 = entityhuman.locZ - this.locZ; + double d3 = d0 * d0 + d1 * d1 + d2 * d2; + + if (d3 > world.paperConfig.hardDespawnDistance) { // CraftBukkit - remove isTypeNotPersistent() check // Paper - custom despawn distances + this.die(); + } + + if (this.ticksFarFromPlayer > 600 && this.random.nextInt(800) == 0 && d3 > world.paperConfig.softDespawnDistance) { // CraftBukkit - remove isTypeNotPersistent() check // Paper - custom despawn distances + this.die(); + } else if (d3 < world.paperConfig.softDespawnDistance) { // Paper - custom despawn distances + this.ticksFarFromPlayer = 0; + } + } + + } + } + + protected final void doTick() { + ++this.ticksFarFromPlayer; + this.world.methodProfiler.enter("checkDespawn"); + this.I(); + this.world.methodProfiler.exit(); + // Spigot Start + if ( this.fromMobSpawner ) + { + // Paper start - Allow nerfed mobs to jump and float + if (goalFloat != null) { + if (goalFloat.validConditions()) goalFloat.update(); + this.getControllerJump().jumpIfSet(); + } + // Paper end + return; + } + // Spigot End + this.world.methodProfiler.enter("sensing"); + this.bC.a(); + this.world.methodProfiler.exit(); + this.world.methodProfiler.enter("targetSelector"); + this.targetSelector.doTick(); + this.world.methodProfiler.exit(); + this.world.methodProfiler.enter("goalSelector"); + this.goalSelector.doTick(); + this.world.methodProfiler.exit(); + this.world.methodProfiler.enter("navigation"); + this.navigation.d(); + this.world.methodProfiler.exit(); + this.world.methodProfiler.enter("mob tick"); + this.mobTick(); + this.world.methodProfiler.exit(); + if (this.isPassenger() && this.getVehicle() instanceof EntityInsentient) { + EntityInsentient entityinsentient = (EntityInsentient) this.getVehicle(); + + entityinsentient.getNavigation().a(this.getNavigation().m(), 1.5D); + entityinsentient.getControllerMove().a(this.getControllerMove()); + } + + this.world.methodProfiler.enter("controls"); + this.world.methodProfiler.enter("move"); + this.moveController.a(); + this.world.methodProfiler.exitEnter("look"); + this.lookController.a(); + this.world.methodProfiler.exitEnter("jump"); + this.h.b(); + this.world.methodProfiler.exit(); + this.world.methodProfiler.exit(); + } + + protected void mobTick() {} + + public int K() { + return 40; + } + + public int L() { + return 10; + } + + public void a(Entity entity, float f, float f1) { + double d0 = entity.locX - this.locX; + double d1 = entity.locZ - this.locZ; + double d2; + + if (entity instanceof EntityLiving) { + EntityLiving entityliving = (EntityLiving) entity; + + d2 = entityliving.locY + (double) entityliving.getHeadHeight() - (this.locY + (double) this.getHeadHeight()); + } else { + d2 = (entity.getBoundingBox().minY + entity.getBoundingBox().maxY) / 2.0D - (this.locY + (double) this.getHeadHeight()); + } + + double d3 = (double) MathHelper.sqrt(d0 * d0 + d1 * d1); + float f2 = (float) (MathHelper.c(d1, d0) * 57.2957763671875D) - 90.0F; + float f3 = (float) (-(MathHelper.c(d2, d3) * 57.2957763671875D)); + + this.pitch = this.c(this.pitch, f3, f1); + this.yaw = this.c(this.yaw, f2, f); + } + + private float c(float f, float f1, float f2) { + float f3 = MathHelper.g(f1 - f); + + if (f3 > f2) { + f3 = f2; + } + + if (f3 < -f2) { + f3 = -f2; + } + + return f + f3; + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + IBlockData iblockdata = generatoraccess.getType((new BlockPosition(this)).down()); + + return iblockdata.a((Entity) this); + } + + public final boolean canSpawn() { + return this.a((IWorldReader) this.world); + } + + public boolean a(IWorldReader iworldreader) { + return !iworldreader.containsLiquid(this.getBoundingBox()) && iworldreader.getCubes(this, this.getBoundingBox()) && iworldreader.a_(this, this.getBoundingBox()); + } + + public int dg() { + return 4; + } + + public boolean c(int i) { + return false; + } + + public int bn() { + if (this.getGoalTarget() == null) { + return 3; + } else { + int i = (int) (this.getHealth() - this.getMaxHealth() * 0.33F); + + i -= (3 - this.world.getDifficulty().a()) * 4; + if (i < 0) { + i = 0; + } + + return i + 3; + } + } + + public Iterable aS() { + return this.bD; + } + + public Iterable getArmorItems() { + return this.bE; + } + + public ItemStack getEquipment(EnumItemSlot enumitemslot) { + switch (enumitemslot.a()) { + case HAND: + return (ItemStack) this.bD.get(enumitemslot.b()); + case ARMOR: + return (ItemStack) this.bE.get(enumitemslot.b()); + default: + return ItemStack.a; + } + } + + public void setSlot(EnumItemSlot enumitemslot, ItemStack itemstack) { + switch (enumitemslot.a()) { + case HAND: + this.bD.set(enumitemslot.b(), itemstack); + break; + case ARMOR: + this.bE.set(enumitemslot.b(), itemstack); + } + + } + + protected void dropEquipment(boolean flag, int i) { + EnumItemSlot[] aenumitemslot = EnumItemSlot.values(); + int j = aenumitemslot.length; + + for (int k = 0; k < j; ++k) { + EnumItemSlot enumitemslot = aenumitemslot[k]; + ItemStack itemstack = this.getEquipment(enumitemslot); + float f = this.c(enumitemslot); + boolean flag1 = f > 1.0F; + + if (!itemstack.isEmpty() && !EnchantmentManager.shouldNotDrop(itemstack) && (flag || flag1) && this.random.nextFloat() - (float) i * 0.01F < f) { + if (!flag1 && itemstack.e()) { + itemstack.setDamage(itemstack.h() - this.random.nextInt(1 + this.random.nextInt(Math.max(itemstack.h() - 3, 1)))); + } + + this.a_(itemstack); + } + } + + } + + protected float c(EnumItemSlot enumitemslot) { + float f; + + switch (enumitemslot.a()) { + case HAND: + f = this.dropChanceHand[enumitemslot.b()]; + break; + case ARMOR: + f = this.dropChanceArmor[enumitemslot.b()]; + break; + default: + f = 0.0F; + } + + return f; + } + + protected void a(DifficultyDamageScaler difficultydamagescaler) { + if (this.random.nextFloat() < 0.15F * difficultydamagescaler.d()) { + int i = this.random.nextInt(2); + float f = this.world.getDifficulty() == EnumDifficulty.HARD ? 0.1F : 0.25F; + + if (this.random.nextFloat() < 0.095F) { + ++i; + } + + if (this.random.nextFloat() < 0.095F) { + ++i; + } + + if (this.random.nextFloat() < 0.095F) { + ++i; + } + + boolean flag = true; + EnumItemSlot[] aenumitemslot = EnumItemSlot.values(); + int j = aenumitemslot.length; + + for (int k = 0; k < j; ++k) { + EnumItemSlot enumitemslot = aenumitemslot[k]; + + if (enumitemslot.a() == EnumItemSlot.Function.ARMOR) { + ItemStack itemstack = this.getEquipment(enumitemslot); + + if (!flag && this.random.nextFloat() < f) { + break; + } + + flag = false; + if (itemstack.isEmpty()) { + Item item = a(enumitemslot, i); + + if (item != null) { + this.setSlot(enumitemslot, new ItemStack(item)); + } + } + } + } + } + + } + + public static EnumItemSlot e(ItemStack itemstack) { + Item item = itemstack.getItem(); + + return item != Blocks.CARVED_PUMPKIN.getItem() && (!(item instanceof ItemBlock) || !(((ItemBlock) item).getBlock() instanceof BlockSkullAbstract)) ? (item instanceof ItemArmor ? ((ItemArmor) item).b() : (item == Items.ELYTRA ? EnumItemSlot.CHEST : (item == Items.SHIELD ? EnumItemSlot.OFFHAND : EnumItemSlot.MAINHAND))) : EnumItemSlot.HEAD; + } + + @Nullable + public static Item a(EnumItemSlot enumitemslot, int i) { + switch (enumitemslot) { + case HEAD: + if (i == 0) { + return Items.LEATHER_HELMET; + } else if (i == 1) { + return Items.GOLDEN_HELMET; + } else if (i == 2) { + return Items.CHAINMAIL_HELMET; + } else if (i == 3) { + return Items.IRON_HELMET; + } else if (i == 4) { + return Items.DIAMOND_HELMET; + } + case CHEST: + if (i == 0) { + return Items.LEATHER_CHESTPLATE; + } else if (i == 1) { + return Items.GOLDEN_CHESTPLATE; + } else if (i == 2) { + return Items.CHAINMAIL_CHESTPLATE; + } else if (i == 3) { + return Items.IRON_CHESTPLATE; + } else if (i == 4) { + return Items.DIAMOND_CHESTPLATE; + } + case LEGS: + if (i == 0) { + return Items.LEATHER_LEGGINGS; + } else if (i == 1) { + return Items.GOLDEN_LEGGINGS; + } else if (i == 2) { + return Items.CHAINMAIL_LEGGINGS; + } else if (i == 3) { + return Items.IRON_LEGGINGS; + } else if (i == 4) { + return Items.DIAMOND_LEGGINGS; + } + case FEET: + if (i == 0) { + return Items.LEATHER_BOOTS; + } else if (i == 1) { + return Items.GOLDEN_BOOTS; + } else if (i == 2) { + return Items.CHAINMAIL_BOOTS; + } else if (i == 3) { + return Items.IRON_BOOTS; + } else if (i == 4) { + return Items.DIAMOND_BOOTS; + } + default: + return null; + } + } + + protected void b(DifficultyDamageScaler difficultydamagescaler) { + float f = difficultydamagescaler.d(); + + if (!this.getItemInMainHand().isEmpty() && this.random.nextFloat() < 0.25F * f) { + this.setSlot(EnumItemSlot.MAINHAND, EnchantmentManager.a(this.random, this.getItemInMainHand(), (int) (5.0F + f * (float) this.random.nextInt(18)), false)); + } + + EnumItemSlot[] aenumitemslot = EnumItemSlot.values(); + int i = aenumitemslot.length; + + for (int j = 0; j < i; ++j) { + EnumItemSlot enumitemslot = aenumitemslot[j]; + + if (enumitemslot.a() == EnumItemSlot.Function.ARMOR) { + ItemStack itemstack = this.getEquipment(enumitemslot); + + if (!itemstack.isEmpty() && this.random.nextFloat() < 0.5F * f) { + this.setSlot(enumitemslot, EnchantmentManager.a(this.random, itemstack, (int) (5.0F + f * (float) this.random.nextInt(18)), false)); + } + } + } + + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + this.getAttributeInstance(GenericAttributes.FOLLOW_RANGE).b(new AttributeModifier("Random spawn bonus", this.random.nextGaussian() * 0.05D, 1)); + if (this.random.nextFloat() < 0.05F) { + this.r(true); + } else { + this.r(false); + } + + return groupdataentity; + } + + public boolean dh() { + return false; + } + + public void di() { + this.persistent = true; + } + + public void a(EnumItemSlot enumitemslot, float f) { + switch (enumitemslot.a()) { + case HAND: + this.dropChanceHand[enumitemslot.b()] = f; + break; + case ARMOR: + this.dropChanceArmor[enumitemslot.b()] = f; + } + + } + + public boolean dj() { + return this.canPickUpLoot; + } + + public void p(boolean flag) { + this.canPickUpLoot = flag; + } + + public boolean isPersistent() { + return this.persistent; + } + + public final boolean b(EntityHuman entityhuman, EnumHand enumhand) { + if (this.isLeashed() && this.getLeashHolder() == entityhuman) { + // CraftBukkit start - fire PlayerUnleashEntityEvent + if (CraftEventFactory.callPlayerUnleashEntityEvent(this, entityhuman).isCancelled()) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutAttachEntity(this, this.getLeashHolder())); + return false; + } + // CraftBukkit end + this.unleash(true, !entityhuman.abilities.canInstantlyBuild); + return true; + } else { + ItemStack itemstack = entityhuman.b(enumhand); + + if (itemstack.getItem() == Items.LEAD && this.a(entityhuman)) { + // CraftBukkit start - fire PlayerLeashEntityEvent + if (CraftEventFactory.callPlayerLeashEntityEvent(this, entityhuman, entityhuman).isCancelled()) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutAttachEntity(this, this.getLeashHolder())); + return false; + } + // CraftBukkit end + this.setLeashHolder(entityhuman, true); + itemstack.subtract(1); + return true; + } else { + return this.a(entityhuman, enumhand) ? true : super.b(entityhuman, enumhand); + } + } + } + + protected boolean a(EntityHuman entityhuman, EnumHand enumhand) { + return false; + } + + protected void dl() { + if (this.bM != null) { + this.dr(); + } + + if (this.bK) { + if (!this.isAlive()) { + this.world.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), UnleashReason.PLAYER_UNLEASH)); // CraftBukkit + this.unleash(true, true); + } + + if (this.leashHolder == null || this.leashHolder.dead) { + this.world.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), UnleashReason.HOLDER_GONE)); // CraftBukkit + this.unleash(true, true); + } + } + } + + public void unleash(boolean flag, boolean flag1) { + if (this.bK) { + this.bK = false; + this.leashHolder = null; + if (!this.world.isClientSide && flag1) { + this.forceDrops = true; // CraftBukkit + this.a((IMaterial) Items.LEAD); + this.forceDrops = false; // CraftBukkit + } + + if (!this.world.isClientSide && flag && this.world instanceof WorldServer) { + ((WorldServer) this.world).getTracker().a((Entity) this, (Packet) (new PacketPlayOutAttachEntity(this, (Entity) null))); + } + } + + } + + public boolean a(EntityHuman entityhuman) { + return !this.isLeashed() && !(this instanceof IMonster); + } + + public boolean isLeashed() { + return this.bK; + } + + public Entity getLeashHolder() { + return this.leashHolder; + } + + public void setLeashHolder(Entity entity, boolean flag) { + this.bK = true; + this.leashHolder = entity; + if (!this.world.isClientSide && flag && this.world instanceof WorldServer) { + ((WorldServer) this.world).getTracker().a((Entity) this, (Packet) (new PacketPlayOutAttachEntity(this, this.leashHolder))); + } + + if (this.isPassenger()) { + this.stopRiding(); + } + + } + + public boolean a(Entity entity, boolean flag) { + boolean flag1 = super.a(entity, flag); + + if (flag1 && this.isLeashed()) { + this.unleash(true, true); + } + + return flag1; + } + + private void dr() { + if (this.bK && this.bM != null) { + if (this.bM.b("UUID")) { + UUID uuid = this.bM.a("UUID"); + List list = this.world.a(EntityLiving.class, this.getBoundingBox().g(10.0D)); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityLiving entityliving = (EntityLiving) iterator.next(); + + if (entityliving.getUniqueID().equals(uuid)) { + this.setLeashHolder(entityliving, true); + break; + } + } + } else if (this.bM.hasKeyOfType("X", 99) && this.bM.hasKeyOfType("Y", 99) && this.bM.hasKeyOfType("Z", 99)) { + BlockPosition blockposition = new BlockPosition(this.bM.getInt("X"), this.bM.getInt("Y"), this.bM.getInt("Z")); + EntityLeash entityleash = EntityLeash.b(this.world, blockposition); + + if (entityleash == null) { + entityleash = EntityLeash.a(this.world, blockposition); + } + + this.setLeashHolder(entityleash, true); + } else { + this.world.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), UnleashReason.UNKNOWN)); // CraftBukkit + this.unleash(false, true); + } + } + + this.bM = null; + } + + public boolean c(int i, ItemStack itemstack) { + EnumItemSlot enumitemslot; + + if (i == 98) { + enumitemslot = EnumItemSlot.MAINHAND; + } else if (i == 99) { + enumitemslot = EnumItemSlot.OFFHAND; + } else if (i == 100 + EnumItemSlot.HEAD.b()) { + enumitemslot = EnumItemSlot.HEAD; + } else if (i == 100 + EnumItemSlot.CHEST.b()) { + enumitemslot = EnumItemSlot.CHEST; + } else if (i == 100 + EnumItemSlot.LEGS.b()) { + enumitemslot = EnumItemSlot.LEGS; + } else { + if (i != 100 + EnumItemSlot.FEET.b()) { + return false; + } + + enumitemslot = EnumItemSlot.FEET; + } + + if (!itemstack.isEmpty() && !b(enumitemslot, itemstack) && enumitemslot != EnumItemSlot.HEAD) { + return false; + } else { + this.setSlot(enumitemslot, itemstack); + return true; + } + } + + public boolean bT() { + return this.dh() && super.bT(); + } + + public static boolean b(EnumItemSlot enumitemslot, ItemStack itemstack) { + EnumItemSlot enumitemslot1 = e(itemstack); + + return enumitemslot1 == enumitemslot || enumitemslot1 == EnumItemSlot.MAINHAND && enumitemslot == EnumItemSlot.OFFHAND || enumitemslot1 == EnumItemSlot.OFFHAND && enumitemslot == EnumItemSlot.MAINHAND; + } + + public boolean cP() { + return super.cP() && !this.isNoAI(); + } + + public void setNoAI(boolean flag) { + byte b0 = (Byte) this.datawatcher.get(EntityInsentient.a); + + this.datawatcher.set(EntityInsentient.a, flag ? (byte) (b0 | 1) : (byte) (b0 & -2)); + } + + public void r(boolean flag) { + byte b0 = (Byte) this.datawatcher.get(EntityInsentient.a); + + this.datawatcher.set(EntityInsentient.a, flag ? (byte) (b0 | 2) : (byte) (b0 & -3)); + } + + public boolean isNoAI() { + return ((Byte) this.datawatcher.get(EntityInsentient.a) & 1) != 0; + } + + public boolean isLeftHanded() { + return ((Byte) this.datawatcher.get(EntityInsentient.a) & 2) != 0; + } + + public EnumMainHand getMainHand() { + return this.isLeftHanded() ? EnumMainHand.LEFT : EnumMainHand.RIGHT; + } + + public boolean B(Entity entity) { + float f = (float) this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).getValue(); + int i = 0; + + if (entity instanceof EntityLiving) { + f += EnchantmentManager.a(this.getItemInMainHand(), ((EntityLiving) entity).getMonsterType()); + i += EnchantmentManager.b((EntityLiving) this); + } + + boolean flag = entity.damageEntity(DamageSource.mobAttack(this), f); + + if (flag) { + if (i > 0 && entity instanceof EntityLiving) { + ((EntityLiving) entity).a(this, (float) i * 0.5F, (double) MathHelper.sin(this.yaw * 0.017453292F), (double) (-MathHelper.cos(this.yaw * 0.017453292F))); + this.motX *= 0.6D; + this.motZ *= 0.6D; + } + + int j = EnchantmentManager.getFireAspectEnchantmentLevel(this); + + if (j > 0) { + // CraftBukkit start - Call a combust event when somebody hits with a fire enchanted item + EntityCombustByEntityEvent combustEvent = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), j * 4); + org.bukkit.Bukkit.getPluginManager().callEvent(combustEvent); + + if (!combustEvent.isCancelled()) { + entity.setOnFire(combustEvent.getDuration(), false); + } + // CraftBukkit end + } + + if (entity instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) entity; + ItemStack itemstack = this.getItemInMainHand(); + ItemStack itemstack1 = entityhuman.isHandRaised() ? entityhuman.cW() : ItemStack.a; + + if (!itemstack.isEmpty() && !itemstack1.isEmpty() && itemstack.getItem() instanceof ItemAxe && itemstack1.getItem() == Items.SHIELD) { + float f1 = 0.25F + (float) EnchantmentManager.getDigSpeedEnchantmentLevel(this) * 0.05F; + + if (this.random.nextFloat() < f1) { + entityhuman.getCooldownTracker().a(Items.SHIELD, 100); + this.world.broadcastEntityEffect(entityhuman, (byte) 30); + } + } + } + + this.a((EntityLiving) this, entity); + } + + return flag; + } + + public boolean isInDaylight() { return dq(); } // Paper - OBFHELPER + protected boolean dq() { + if (this.world.L() && !this.world.isClientSide) { + float f = this.az(); + BlockPosition blockposition = this.getVehicle() instanceof EntityBoat ? (new BlockPosition(this.locX, (double) Math.round(this.locY), this.locZ)).up() : new BlockPosition(this.locX, (double) Math.round(this.locY), this.locZ); + + if (f > 0.5F && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && this.world.e(blockposition)) { + return true; + } + } + + return false; + } + + protected void c(Tag tag) { + if (this.getNavigation().t()) { + super.c(tag); + } else { + this.motY += 0.30000001192092896D; + } + + } +} diff --git a/src/main/java/net/minecraft/server/EntityIronGolem.java b/src/main/java/net/minecraft/server/EntityIronGolem.java new file mode 100644 index 000000000000..ae269270416e --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityIronGolem.java @@ -0,0 +1,193 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class EntityIronGolem extends EntityGolem { + + protected static final DataWatcherObject a = DataWatcher.a(EntityIronGolem.class, DataWatcherRegistry.a); + private int b; + @Nullable + private Village c; + private int bC; + private int bD; + + public EntityIronGolem(World world) { + super(EntityTypes.IRON_GOLEM, world); + this.setSize(1.4F, 2.7F); + } + + protected void n() { + this.goalSelector.a(1, new PathfinderGoalMeleeAttack(this, 1.0D, true)); + this.goalSelector.a(2, new PathfinderGoalMoveTowardsTarget(this, 0.9D, 32.0F)); + this.goalSelector.a(3, new PathfinderGoalMoveThroughVillage(this, 0.6D, true)); + this.goalSelector.a(4, new PathfinderGoalMoveTowardsRestriction(this, 1.0D)); + this.goalSelector.a(5, new PathfinderGoalOfferFlower(this)); + this.goalSelector.a(6, new PathfinderGoalRandomStrollLand(this, 0.6D)); + this.goalSelector.a(7, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new PathfinderGoalDefendVillage(this)); + this.targetSelector.a(2, new PathfinderGoalHurtByTarget(this, false, new Class[0])); + this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget<>(this, EntityInsentient.class, 10, false, true, (entityinsentient) -> { + return entityinsentient != null && IMonster.e.test(entityinsentient) && !(entityinsentient instanceof EntityCreeper); + })); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityIronGolem.a, (byte) 0); + } + + protected void mobTick() { + if (--this.b <= 0) { + this.b = 70 + this.random.nextInt(50); + this.c = this.world.af().getClosestVillage(new BlockPosition(this), 32); + if (this.c == null) { + this.dv(); + } else { + BlockPosition blockposition = this.c.a(); + + this.a(blockposition, (int) ((float) this.c.b() * 0.6F)); + } + } + + super.mobTick(); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(100.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.25D); + this.getAttributeInstance(GenericAttributes.c).setValue(1.0D); + } + + protected int k(int i) { + return i; + } + + protected void C(Entity entity) { + if (entity instanceof IMonster && !(entity instanceof EntityCreeper) && this.getRandom().nextInt(20) == 0) { + this.setGoalTarget((EntityLiving) entity, org.bukkit.event.entity.EntityTargetLivingEntityEvent.TargetReason.COLLISION, true); // CraftBukkit - set reason + } + + super.C(entity); + } + + public void movementTick() { + super.movementTick(); + if (this.bC > 0) { + --this.bC; + } + + if (this.bD > 0) { + --this.bD; + } + + if (this.motX * this.motX + this.motZ * this.motZ > 2.500000277905201E-7D && this.random.nextInt(5) == 0) { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locY - 0.20000000298023224D); + int k = MathHelper.floor(this.locZ); + IBlockData iblockdata = this.world.getType(new BlockPosition(i, j, k)); + + if (!iblockdata.isAir()) { + this.world.addParticle(new ParticleParamBlock(Particles.d, iblockdata), this.locX + ((double) this.random.nextFloat() - 0.5D) * (double) this.width, this.getBoundingBox().minY + 0.1D, this.locZ + ((double) this.random.nextFloat() - 0.5D) * (double) this.width, 4.0D * ((double) this.random.nextFloat() - 0.5D), 0.5D, ((double) this.random.nextFloat() - 0.5D) * 4.0D); + } + } + + } + + public boolean b(Class oclass) { + return this.isPlayerCreated() && EntityHuman.class.isAssignableFrom(oclass) ? false : (oclass == EntityCreeper.class ? false : super.b(oclass)); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("PlayerCreated", this.isPlayerCreated()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setPlayerCreated(nbttagcompound.getBoolean("PlayerCreated")); + } + + public boolean B(Entity entity) { + this.bC = 10; + this.world.broadcastEntityEffect(this, (byte) 4); + boolean flag = entity.damageEntity(DamageSource.mobAttack(this), (float) (7 + this.random.nextInt(15))); + + if (flag) { + entity.motY += 0.4000000059604645D; + this.a((EntityLiving) this, entity); + } + + this.a(SoundEffects.ENTITY_IRON_GOLEM_ATTACK, 1.0F, 1.0F); + return flag; + } + + public Village l() { + return this.c; + } + + public void a(boolean flag) { + if (flag) { + this.bD = 400; + this.world.broadcastEntityEffect(this, (byte) 11); + } else { + this.bD = 0; + this.world.broadcastEntityEffect(this, (byte) 34); + } + + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_IRON_GOLEM_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_IRON_GOLEM_DEATH; + } + + protected void a(BlockPosition blockposition, IBlockData iblockdata) { + this.a(SoundEffects.ENTITY_IRON_GOLEM_STEP, 1.0F, 1.0F); + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.G; + } + + public int dz() { + return this.bD; + } + + public boolean isPlayerCreated() { + return ((Byte) this.datawatcher.get(EntityIronGolem.a) & 1) != 0; + } + + public void setPlayerCreated(boolean flag) { + byte b0 = (Byte) this.datawatcher.get(EntityIronGolem.a); + + if (flag) { + this.datawatcher.set(EntityIronGolem.a, (byte) (b0 | 1)); + } else { + this.datawatcher.set(EntityIronGolem.a, (byte) (b0 & -2)); + } + + } + + public void die(DamageSource damagesource) { + if (!this.isPlayerCreated() && this.killer != null && this.c != null) { + this.c.a(this.killer.getProfile().getName(), -5); + } + + super.die(damagesource); + } + + public boolean a(IWorldReader iworldreader) { + BlockPosition blockposition = new BlockPosition(this.locX, this.locY, this.locZ); + IBlockData iblockdata = iworldreader.getType(blockposition); + IBlockData iblockdata1 = iworldreader.getType(blockposition.down()); + IBlockData iblockdata2 = iworldreader.getType(blockposition.up()); + + return iblockdata1.q() && SpawnerCreature.a(iblockdata2, iblockdata2.s()) && SpawnerCreature.a(iblockdata, FluidTypes.EMPTY.i()) && iworldreader.getCubes(this, this.getBoundingBox()) && iworldreader.a_(this, this.getBoundingBox()); + } +} diff --git a/src/main/java/net/minecraft/server/EntityItem.java b/src/main/java/net/minecraft/server/EntityItem.java new file mode 100644 index 000000000000..03fee65e45e8 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityItem.java @@ -0,0 +1,462 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.UUID; +import javax.annotation.Nullable; +// CraftBukkit start +import org.bukkit.event.entity.EntityPickupItemEvent; +import org.bukkit.event.player.PlayerPickupItemEvent; +// CraftBukkit end +import org.bukkit.event.player.PlayerAttemptPickupItemEvent; // Paper + +public class EntityItem extends Entity { + + private static final DataWatcherObject b = DataWatcher.a(EntityItem.class, DataWatcherRegistry.g); + public int age; // PAIL + public int pickupDelay; + public boolean canMobPickup = true; // Paper + private int e; + private UUID f; + private UUID g; + public float a; + private int lastTick = MinecraftServer.currentTick - 1; // CraftBukkit + + public EntityItem(World world) { + super(EntityTypes.ITEM, world); + this.e = 5; + this.a = (float) (Math.random() * 3.141592653589793D * 2.0D); + this.setSize(0.25F, 0.25F); + } + + public EntityItem(World world, double d0, double d1, double d2) { + this(world); + this.setPosition(d0, d1, d2); + this.yaw = (float) (Math.random() * 360.0D); + this.motX = (double) ((float) (Math.random() * 0.20000000298023224D - 0.10000000149011612D)); + this.motY = 0.20000000298023224D; + this.motZ = (double) ((float) (Math.random() * 0.20000000298023224D - 0.10000000149011612D)); + } + + public EntityItem(World world, double d0, double d1, double d2, ItemStack itemstack) { + this(world, d0, d1, d2); + this.setItemStack(itemstack); + } + + protected boolean playStepSound() { + return false; + } + + protected void x_() { + this.getDataWatcher().register(EntityItem.b, ItemStack.a); + } + + public void tick() { + if (this.getItemStack().isEmpty()) { + this.die(); + } else { + super.tick(); + // CraftBukkit start - Use wall time for pickup and despawn timers + int elapsedTicks = MinecraftServer.currentTick - this.lastTick; + if (this.pickupDelay != 32767) this.pickupDelay -= elapsedTicks; + if (this.age != -32768) this.age += elapsedTicks; + this.lastTick = MinecraftServer.currentTick; + // CraftBukkit end + + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + double d0 = this.motX; + double d1 = this.motY; + double d2 = this.motZ; + + if (this.a(TagsFluid.WATER)) { + this.u(); + } else if (!this.isNoGravity()) { + this.motY -= 0.03999999910593033D; + } + + if (this.world.isClientSide) { + this.noclip = false; + } else { + this.noclip = this.i(this.locX, (this.getBoundingBox().minY + this.getBoundingBox().maxY) / 2.0D, this.locZ); + } + + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + boolean flag = (int) this.lastX != (int) this.locX || (int) this.lastY != (int) this.locY || (int) this.lastZ != (int) this.locZ; + + if (flag || this.ticksLived % 25 == 0) { + if (this.world.getFluid(new BlockPosition(this)).a(TagsFluid.LAVA)) { + this.motY = 0.20000000298023224D; + this.motX = (double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.2F); + this.motZ = (double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.2F); + this.a(SoundEffects.ENTITY_GENERIC_BURN, 0.4F, 2.0F + this.random.nextFloat() * 0.4F); + } + + if (!this.world.isClientSide) { + this.v(); + } + } + + float f = 0.98F; + + if (this.onGround) { + f = this.world.getType(new BlockPosition(MathHelper.floor(this.locX), MathHelper.floor(this.getBoundingBox().minY) - 1, MathHelper.floor(this.locZ))).getBlock().n() * 0.98F; + } + + this.motX *= (double) f; + this.motY *= 0.9800000190734863D; + this.motZ *= (double) f; + if (this.onGround) { + this.motY *= -0.5D; + } + + /* Craftbukkit start - moved up + if (this.age != -32768) { + ++this.age; + } + // Craftbukkit end */ + + this.impulse |= this.at(); + if (!this.world.isClientSide) { + double d3 = this.motX - d0; + double d4 = this.motY - d1; + double d5 = this.motZ - d2; + double d6 = d3 * d3 + d4 * d4 + d5 * d5; + + if (d6 > 0.01D) { + this.impulse = true; + } + } + + if (!this.world.isClientSide && this.age >= world.spigotConfig.itemDespawnRate) { // Spigot + // CraftBukkit start - fire ItemDespawnEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemDespawnEvent(this).isCancelled()) { + this.age = 0; + return; + } + // CraftBukkit end + this.die(); + } + + } + } + + // Spigot start - copied from above + @Override + public void inactiveTick() { + // CraftBukkit start - Use wall time for pickup and despawn timers + int elapsedTicks = MinecraftServer.currentTick - this.lastTick; + if (this.pickupDelay != 32767) this.pickupDelay -= elapsedTicks; + if (this.age != -32768) this.age += elapsedTicks; + this.lastTick = MinecraftServer.currentTick; + // CraftBukkit end + + if (!this.world.isClientSide && this.age >= world.spigotConfig.itemDespawnRate) { // Spigot + // CraftBukkit start - fire ItemDespawnEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemDespawnEvent(this).isCancelled()) { + this.age = 0; + return; + } + // CraftBukkit end + this.die(); + } + } + // Spigot end + + private void u() { + if (this.motY < 0.05999999865889549D) { + this.motY += 5.000000237487257E-4D; + } + + this.motX *= 0.9900000095367432D; + this.motZ *= 0.9900000095367432D; + } + + private void v() { + // Paper start - avoid item merge if stack size above max stack size + ItemStack stack = getItemStack(); + if (stack.getCount() >= stack.getMaxStackSize()) return; + // Paper end + // Spigot start + double radius = world.spigotConfig.itemMerge; + Iterator iterator = this.world.a(EntityItem.class, this.getBoundingBox().grow(radius, radius, radius)).iterator(); + // Spigot end + + while (iterator.hasNext()) { + EntityItem entityitem = (EntityItem) iterator.next(); + + this.a(entityitem); + } + + } + + private boolean a(EntityItem entityitem) { + if (entityitem == this) { + return false; + } else if (entityitem.isAlive() && this.isAlive()) { + ItemStack itemstack = this.getItemStack(); + ItemStack itemstack1 = entityitem.getItemStack().cloneItemStack(); + + if (this.pickupDelay != 32767 && entityitem.pickupDelay != 32767) { + if (this.age != -32768 && entityitem.age != -32768) { + if (itemstack1.getItem() != itemstack.getItem()) { + return false; + } else if (itemstack1.hasTag() ^ itemstack.hasTag()) { + return false; + } else if (itemstack1.hasTag() && !itemstack1.getTag().equals(itemstack.getTag())) { + return false; + } else if (itemstack1.getItem() == null) { + return false; + } else if (itemstack1.getCount() < itemstack.getCount()) { + return entityitem.a(this); + } else if (itemstack1.getCount() + itemstack.getCount() > itemstack1.getMaxStackSize()) { + return false; + } else { + // Spigot start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemMergeEvent(entityitem, this).isCancelled()) return false; // CraftBukkit + itemstack.add(itemstack1.getCount()); + this.pickupDelay = Math.max(entityitem.pickupDelay, this.pickupDelay); + this.age = Math.min(entityitem.age, this.age); + this.setItemStack(itemstack); + entityitem.die(); + // Spigot end + return true; + } + } else { + return false; + } + } else { + return false; + } + } else { + return false; + } + } + + public void f() { + this.age = 4800; + } + + protected void burn(int i) { + this.damageEntity(DamageSource.FIRE, (float) i); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else if (!this.getItemStack().isEmpty() && this.getItemStack().getItem() == Items.NETHER_STAR && damagesource.isExplosion()) { + return false; + } else { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, damagesource, f)) { + return false; + } + // CraftBukkit end + this.aA(); + this.e = (int) ((float) this.e - f); + if (this.e <= 0) { + this.die(); + } + + return false; + } + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setShort("Health", (short) this.e); + nbttagcompound.setShort("Age", (short) this.age); + nbttagcompound.setShort("PickupDelay", (short) this.pickupDelay); + if (this.l() != null) { + nbttagcompound.set("Thrower", GameProfileSerializer.a(this.l())); + } + + if (this.k() != null) { + nbttagcompound.set("Owner", GameProfileSerializer.a(this.k())); + } + + if (!this.getItemStack().isEmpty()) { + nbttagcompound.set("Item", this.getItemStack().save(new NBTTagCompound())); + } + + } + + public void a(NBTTagCompound nbttagcompound) { + this.e = nbttagcompound.getShort("Health"); + this.age = nbttagcompound.getShort("Age"); + if (nbttagcompound.hasKey("PickupDelay")) { + this.pickupDelay = nbttagcompound.getShort("PickupDelay"); + } + + if (nbttagcompound.hasKeyOfType("Owner", 10)) { + this.g = GameProfileSerializer.b(nbttagcompound.getCompound("Owner")); + } + + if (nbttagcompound.hasKeyOfType("Thrower", 10)) { + this.f = GameProfileSerializer.b(nbttagcompound.getCompound("Thrower")); + } + + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Item"); + + this.setItemStack(ItemStack.a(nbttagcompound1)); + if (this.getItemStack().isEmpty()) { + this.die(); + } + + } + + public void d(EntityHuman entityhuman) { + if (!this.world.isClientSide) { + ItemStack itemstack = this.getItemStack(); + Item item = itemstack.getItem(); + int i = itemstack.getCount(); + + // CraftBukkit start - fire PlayerPickupItemEvent + int canHold = entityhuman.inventory.canHold(itemstack); + int remaining = i - canHold; + boolean flyAtPlayer = false; // Paper + + // Paper start + if (this.pickupDelay <= 0) { + PlayerAttemptPickupItemEvent attemptEvent = new PlayerAttemptPickupItemEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining); + this.world.getServer().getPluginManager().callEvent(attemptEvent); + + flyAtPlayer = attemptEvent.getFlyAtPlayer(); + if (attemptEvent.isCancelled()) { + if (flyAtPlayer) { + entityhuman.receive(this, i); + } + + return; + } + } + // Paper end + + if (this.pickupDelay <= 0 && canHold > 0) { + itemstack.setCount(canHold); + // Call legacy event + PlayerPickupItemEvent playerEvent = new PlayerPickupItemEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining); + playerEvent.setCancelled(!entityhuman.canPickUpLoot); + this.world.getServer().getPluginManager().callEvent(playerEvent); + flyAtPlayer = playerEvent.getFlyAtPlayer(); // Paper + if (playerEvent.isCancelled()) { + // Paper Start + if (flyAtPlayer) { + entityhuman.receive(this, i); + } + // Paper End + return; + } + + // Call newer event afterwards + EntityPickupItemEvent entityEvent = new EntityPickupItemEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining); + entityEvent.setCancelled(!entityhuman.canPickUpLoot); + this.world.getServer().getPluginManager().callEvent(entityEvent); + if (entityEvent.isCancelled()) { + return; + } + + itemstack.setCount(canHold + remaining); + + // Possibly < 0; fix here so we do not have to modify code below + this.pickupDelay = 0; + } + // CraftBukkit end + + if (this.pickupDelay == 0 && (this.g == null || 6000 - this.age <= 200 || this.g.equals(entityhuman.getUniqueID())) && entityhuman.inventory.pickup(itemstack)) { + entityhuman.receive(this, i); + // Paper Start + if (flyAtPlayer) { + entityhuman.receive(this, i); + } + // Paper End + if (itemstack.isEmpty()) { + this.die(); + itemstack.setCount(i); + } + + entityhuman.a(StatisticList.ITEM_PICKED_UP.b(item), i); + } + + } + } + + public IChatBaseComponent getDisplayName() { + IChatBaseComponent ichatbasecomponent = this.getCustomName(); + + return (IChatBaseComponent) (ichatbasecomponent != null ? ichatbasecomponent : new ChatMessage(this.getItemStack().j(), new Object[0])); + } + + public boolean bk() { + return false; + } + + @Nullable + public Entity a(DimensionManager dimensionmanager) { + Entity entity = super.a(dimensionmanager); + + if (!this.world.isClientSide && entity instanceof EntityItem) { + ((EntityItem) entity).v(); + } + + return entity; + } + + public ItemStack getItemStack() { + return (ItemStack) this.getDataWatcher().get(EntityItem.b); + } + + public void setItemStack(ItemStack itemstack) { + com.google.common.base.Preconditions.checkArgument(!itemstack.isEmpty(), "Cannot drop air"); // CraftBukkit + this.getDataWatcher().set(EntityItem.b, itemstack); + this.getDataWatcher().markDirty(EntityItem.b); // CraftBukkit - SPIGOT-4591, must mark dirty + } + + @Nullable public UUID getOwner() { return k(); } // Paper - OBFHELPER + @Nullable public UUID k() { // Paper + return this.g; + } + + public void setOwner(@Nullable UUID owner) { b(owner); } // Paper - OBFHELPER + public void b(@Nullable UUID uuid) { + this.g = uuid; + } + + @Nullable public UUID getThrower() { return l(); } // Paper - OBFHELPER + @Nullable public UUID l() { // Paper + return this.f; + } + + public void setThrower(@Nullable UUID thrower) { c(thrower); } // Paper - OBFHELPER + public void c(@Nullable UUID uuid) { + this.f = uuid; + } + + public void n() { + this.pickupDelay = 10; + } + + public void o() { + this.pickupDelay = 0; + } + + public void p() { + this.pickupDelay = 32767; + } + + public void a(int i) { + this.pickupDelay = i; + } + + public boolean q() { + return this.pickupDelay > 0; + } + + public void s() { + this.age = -6000; + } + + public void t() { + this.p(); + this.age = world.spigotConfig.itemDespawnRate - 1; // Spigot + } +} diff --git a/src/main/java/net/minecraft/server/EntityItemFrame.java b/src/main/java/net/minecraft/server/EntityItemFrame.java new file mode 100644 index 000000000000..964509a3377a --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityItemFrame.java @@ -0,0 +1,281 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; +import org.apache.commons.lang3.Validate; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class EntityItemFrame extends EntityHanging { + + private static final Logger d = LogManager.getLogger(); + private static final DataWatcherObject e = DataWatcher.a(EntityItemFrame.class, DataWatcherRegistry.g); + private static final DataWatcherObject f = DataWatcher.a(EntityItemFrame.class, DataWatcherRegistry.b); + private float g = 1.0F; + + public EntityItemFrame(World world) { + super(EntityTypes.ITEM_FRAME, world); + } + + public EntityItemFrame(World world, BlockPosition blockposition, EnumDirection enumdirection) { + super(EntityTypes.ITEM_FRAME, world, blockposition); + this.setDirection(enumdirection); + } + + public float getHeadHeight() { + return 0.0F; + } + + protected void x_() { + this.getDataWatcher().register(EntityItemFrame.e, ItemStack.a); + this.getDataWatcher().register(EntityItemFrame.f, 0); + } + + public void setDirection(EnumDirection enumdirection) { + Validate.notNull(enumdirection); + this.direction = enumdirection; + if (enumdirection.k().c()) { + this.pitch = 0.0F; + this.yaw = (float) (this.direction.get2DRotationValue() * 90); + } else { + this.pitch = (float) (-90 * enumdirection.c().a()); + this.yaw = 0.0F; + } + + this.lastPitch = this.pitch; + this.lastYaw = this.yaw; + this.updateBoundingBox(); + } + + protected void updateBoundingBox() { + if (this.direction != null) { + double d0 = 0.46875D; + + this.locX = (double) this.blockPosition.getX() + 0.5D - (double) this.direction.getAdjacentX() * 0.46875D; + this.locY = (double) this.blockPosition.getY() + 0.5D - (double) this.direction.getAdjacentY() * 0.46875D; + this.locZ = (double) this.blockPosition.getZ() + 0.5D - (double) this.direction.getAdjacentZ() * 0.46875D; + double d1 = (double) this.getWidth(); + double d2 = (double) this.getHeight(); + double d3 = (double) this.getWidth(); + EnumDirection.EnumAxis enumdirection_enumaxis = this.direction.k(); + + switch (enumdirection_enumaxis) { + case X: + d1 = 1.0D; + break; + case Y: + d2 = 1.0D; + break; + case Z: + d3 = 1.0D; + } + + d1 /= 32.0D; + d2 /= 32.0D; + d3 /= 32.0D; + this.a(new AxisAlignedBB(this.locX - d1, this.locY - d2, this.locZ - d3, this.locX + d1, this.locY + d2, this.locZ + d3)); + } + } + + public boolean survives() { + if (!this.world.getCubes(this, this.getBoundingBox())) { + return false; + } else { + IBlockData iblockdata = this.world.getType(this.blockPosition.shift(this.direction.opposite())); + + return !iblockdata.getMaterial().isBuildable() && (!this.direction.k().c() || !BlockDiodeAbstract.isDiode(iblockdata)) ? false : this.world.getEntities(this, this.getBoundingBox(), EntityItemFrame.a).isEmpty(); + } + } + + public float aM() { + return 0.0F; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else if (!damagesource.isExplosion() && !this.getItem().isEmpty()) { + if (!this.world.isClientSide) { + // CraftBukkit start - fire EntityDamageEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, damagesource, f, false) || this.dead) { + return true; + } + // CraftBukkit end + this.b(damagesource.getEntity(), false); + this.a(SoundEffects.ENTITY_ITEM_FRAME_REMOVE_ITEM, 1.0F, 1.0F); + } + + return true; + } else { + return super.damageEntity(damagesource, f); + } + } + + public int getWidth() { + return 12; + } + + public int getHeight() { + return 12; + } + + public void a(@Nullable Entity entity) { + this.a(SoundEffects.ENTITY_ITEM_FRAME_BREAK, 1.0F, 1.0F); + this.b(entity, true); + } + + public void m() { + this.a(SoundEffects.ENTITY_ITEM_FRAME_PLACE, 1.0F, 1.0F); + } + + public void b(@Nullable Entity entity, boolean flag) { + if (this.world.getGameRules().getBoolean("doEntityDrops")) { + ItemStack itemstack = this.getItem(); + + this.setItem(ItemStack.a); + if (entity instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) entity; + + if (entityhuman.abilities.canInstantlyBuild) { + this.c(itemstack); + return; + } + } + + if (flag) { + this.a((IMaterial) Items.ITEM_FRAME); + } + + if (!itemstack.isEmpty() && this.random.nextFloat() < this.g) { + itemstack = itemstack.cloneItemStack(); + this.c(itemstack); + this.a_(itemstack); + } + + } + } + + private void c(ItemStack itemstack) { + if (itemstack.getItem() == Items.FILLED_MAP) { + WorldMap worldmap = ItemWorldMap.getSavedMap(itemstack, this.world); + + worldmap.a(this.blockPosition, this.getId()); + } + + itemstack.a((EntityItemFrame) null); + } + + public ItemStack getItem() { + return (ItemStack) this.getDataWatcher().get(EntityItemFrame.e); + } + + public void setItem(ItemStack itemstack) { + this.setItem(itemstack, true); + } + + public void setItem(ItemStack itemstack, boolean flag) { + // CraftBukkit start + this.setItem(itemstack, flag, true); + } + + public void setItem(ItemStack itemstack, boolean flag, boolean playSound) { + // CraftBukkit end + if (!itemstack.isEmpty()) { + itemstack = itemstack.cloneItemStack(); + itemstack.setCount(1); + itemstack.a(this); + } + + this.getDataWatcher().set(EntityItemFrame.e, itemstack); + if (!itemstack.isEmpty() && playSound) { // CraftBukkit + this.a(SoundEffects.ENTITY_ITEM_FRAME_ADD_ITEM, 1.0F, 1.0F); + } + + if (flag && this.blockPosition != null) { + this.world.updateAdjacentComparators(this.blockPosition, Blocks.AIR); + } + + } + + public void a(DataWatcherObject datawatcherobject) { + if (datawatcherobject.equals(EntityItemFrame.e)) { + ItemStack itemstack = this.getItem(); + + if (!itemstack.isEmpty() && itemstack.y() != this) { + itemstack.a(this); + } + } + + } + + public int getRotation() { + return (Integer) this.getDataWatcher().get(EntityItemFrame.f); + } + + public void setRotation(int i) { + this.setRotation(i, true); + } + + private void setRotation(int i, boolean flag) { + this.getDataWatcher().set(EntityItemFrame.f, i % 8); + if (flag && this.blockPosition != null) { + this.world.updateAdjacentComparators(this.blockPosition, Blocks.AIR); + } + + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + if (!this.getItem().isEmpty()) { + nbttagcompound.set("Item", this.getItem().save(new NBTTagCompound())); + nbttagcompound.setByte("ItemRotation", (byte) this.getRotation()); + nbttagcompound.setFloat("ItemDropChance", this.g); + } + + nbttagcompound.setByte("Facing", (byte) this.direction.a()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Item"); + + if (nbttagcompound1 != null && !nbttagcompound1.isEmpty()) { + ItemStack itemstack = ItemStack.a(nbttagcompound1); + + if (itemstack.isEmpty()) { + EntityItemFrame.d.warn("Unable to load item from: {}", nbttagcompound1); + } + + this.setItem(itemstack, false); + this.setRotation(nbttagcompound.getByte("ItemRotation"), false); + if (nbttagcompound.hasKeyOfType("ItemDropChance", 99)) { + this.g = nbttagcompound.getFloat("ItemDropChance"); + } + } + + this.setDirection(EnumDirection.fromType1(nbttagcompound.getByte("Facing"))); + } + + public boolean b(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (!this.world.isClientSide) { + if (this.getItem().isEmpty()) { + if (!itemstack.isEmpty()) { + this.setItem(itemstack); + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + } + } else { + this.a(SoundEffects.ENTITY_ITEM_FRAME_ROTATE_ITEM, 1.0F, 1.0F); + this.setRotation(this.getRotation() + 1); + } + } + + return true; + } + + public int q() { + return this.getItem().isEmpty() ? 0 : this.getRotation() % 8 + 1; + } +} diff --git a/src/main/java/net/minecraft/server/EntityLargeFireball.java b/src/main/java/net/minecraft/server/EntityLargeFireball.java new file mode 100644 index 000000000000..cb85392d7130 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityLargeFireball.java @@ -0,0 +1,55 @@ +package net.minecraft.server; + +import org.bukkit.event.entity.ExplosionPrimeEvent; // CraftBukkit + +public class EntityLargeFireball extends EntityFireball { + + public int yield = 1; + + public EntityLargeFireball(World world) { + super(EntityTypes.FIREBALL, world, 1.0F, 1.0F); + isIncendiary = this.world.getGameRules().getBoolean("mobGriefing"); // CraftBukkit + } + + public EntityLargeFireball(World world, EntityLiving entityliving, double d0, double d1, double d2) { + super(EntityTypes.FIREBALL, entityliving, d0, d1, d2, world, 1.0F, 1.0F); + isIncendiary = this.world.getGameRules().getBoolean("mobGriefing"); // CraftBukkit + } + + protected void a(MovingObjectPosition movingobjectposition) { + if (!this.world.isClientSide) { + if (movingobjectposition.entity != null) { + movingobjectposition.entity.damageEntity(DamageSource.fireball(this, this.shooter), 6.0F); + this.a(this.shooter, movingobjectposition.entity); + } + + boolean flag = this.world.getGameRules().getBoolean("mobGriefing"); + + // CraftBukkit start - fire ExplosionPrimeEvent + ExplosionPrimeEvent event = new ExplosionPrimeEvent((org.bukkit.entity.Explosive) org.bukkit.craftbukkit.entity.CraftEntity.getEntity(this.world.getServer(), this)); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + // give 'this' instead of (Entity) null so we know what causes the damage + this.world.createExplosion(this, this.locX, this.locY, this.locZ, event.getRadius(), event.getFire(), isIncendiary); + } + // CraftBukkit end + this.die(); + } + + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("ExplosionPower", this.yield); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKeyOfType("ExplosionPower", 99)) { + // CraftBukkit - set bukkitYield when setting explosionpower + bukkitYield = this.yield = nbttagcompound.getInt("ExplosionPower"); + } + + } +} diff --git a/src/main/java/net/minecraft/server/EntityLeash.java b/src/main/java/net/minecraft/server/EntityLeash.java new file mode 100644 index 000000000000..b5f56e0e8ad1 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityLeash.java @@ -0,0 +1,152 @@ +package net.minecraft.server; + +import org.bukkit.craftbukkit.event.CraftEventFactory; + +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; + +public class EntityLeash extends EntityHanging { + + public EntityLeash(World world) { + super(EntityTypes.LEASH_KNOT, world); + } + + public EntityLeash(World world, BlockPosition blockposition) { + super(EntityTypes.LEASH_KNOT, world, blockposition); + this.setPosition((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D); + float f = 0.125F; + float f1 = 0.1875F; + float f2 = 0.25F; + + this.a(new AxisAlignedBB(this.locX - 0.1875D, this.locY - 0.25D + 0.125D, this.locZ - 0.1875D, this.locX + 0.1875D, this.locY + 0.25D + 0.125D, this.locZ + 0.1875D)); + this.attachedToPlayer = true; + } + + public void setPosition(double d0, double d1, double d2) { + super.setPosition((double) MathHelper.floor(d0) + 0.5D, (double) MathHelper.floor(d1) + 0.5D, (double) MathHelper.floor(d2) + 0.5D); + } + + protected void updateBoundingBox() { + this.locX = (double) this.blockPosition.getX() + 0.5D; + this.locY = (double) this.blockPosition.getY() + 0.5D; + this.locZ = (double) this.blockPosition.getZ() + 0.5D; + if (valid) world.entityJoinedWorld(this, false); // CraftBukkit + } + + public void setDirection(EnumDirection enumdirection) {} + + public int getWidth() { + return 9; + } + + public int getHeight() { + return 9; + } + + public float getHeadHeight() { + return -0.0625F; + } + + public void a(@Nullable Entity entity) { + this.a(SoundEffects.ENTITY_LEASH_KNOT_BREAK, 1.0F, 1.0F); + } + + public void b(NBTTagCompound nbttagcompound) {} + + public void a(NBTTagCompound nbttagcompound) {} + + public boolean b(EntityHuman entityhuman, EnumHand enumhand) { + if (this.world.isClientSide) { + return true; + } else { + boolean flag = false; + double d0 = 7.0D; + List list = this.world.a(EntityInsentient.class, new AxisAlignedBB(this.locX - 7.0D, this.locY - 7.0D, this.locZ - 7.0D, this.locX + 7.0D, this.locY + 7.0D, this.locZ + 7.0D)); + Iterator iterator = list.iterator(); + + EntityInsentient entityinsentient; + + while (iterator.hasNext()) { + entityinsentient = (EntityInsentient) iterator.next(); + if (entityinsentient.isLeashed() && entityinsentient.getLeashHolder() == entityhuman) { + // CraftBukkit start + if (CraftEventFactory.callPlayerLeashEntityEvent(entityinsentient, this, entityhuman).isCancelled()) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutAttachEntity(entityinsentient, entityinsentient.getLeashHolder())); + continue; + } + // CraftBukkit end + entityinsentient.setLeashHolder(this, true); + flag = true; + } + } + + if (!flag) { + // CraftBukkit start - Move below + // this.die(); + boolean die = true; + // CraftBukkit end + if (true || entityhuman.abilities.canInstantlyBuild) { // CraftBukkit - Process for non-creative as well + iterator = list.iterator(); + + while (iterator.hasNext()) { + entityinsentient = (EntityInsentient) iterator.next(); + if (entityinsentient.isLeashed() && entityinsentient.getLeashHolder() == this) { + // CraftBukkit start + if (CraftEventFactory.callPlayerUnleashEntityEvent(entityinsentient, entityhuman).isCancelled()) { + die = false; + continue; + } + entityinsentient.unleash(true, !entityhuman.abilities.canInstantlyBuild); // false -> survival mode boolean + // CraftBukkit end + } + } + // CraftBukkit start + if (die) { + this.die(); + } + // CraftBukkit end + } + } + + return true; + } + } + + public boolean survives() { + return this.world.getType(this.blockPosition).getBlock() instanceof BlockFence; + } + + public static EntityLeash a(World world, BlockPosition blockposition) { + EntityLeash entityleash = new EntityLeash(world, blockposition); + + world.addEntity(entityleash); + entityleash.m(); + return entityleash; + } + + @Nullable + public static EntityLeash b(World world, BlockPosition blockposition) { + int i = blockposition.getX(); + int j = blockposition.getY(); + int k = blockposition.getZ(); + List list = world.a(EntityLeash.class, new AxisAlignedBB((double) i - 1.0D, (double) j - 1.0D, (double) k - 1.0D, (double) i + 1.0D, (double) j + 1.0D, (double) k + 1.0D)); + Iterator iterator = list.iterator(); + + EntityLeash entityleash; + + do { + if (!iterator.hasNext()) { + return null; + } + + entityleash = (EntityLeash) iterator.next(); + } while (!entityleash.getBlockPosition().equals(blockposition)); + + return entityleash; + } + + public void m() { + this.a(SoundEffects.ENTITY_LEASH_KNOT_PLACE, 1.0F, 1.0F); + } +} diff --git a/src/main/java/net/minecraft/server/EntityLightning.java b/src/main/java/net/minecraft/server/EntityLightning.java new file mode 100644 index 000000000000..50f62000955e --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityLightning.java @@ -0,0 +1,153 @@ +package net.minecraft.server; + +import java.util.Collection; +import java.util.List; +import javax.annotation.Nullable; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class EntityLightning extends EntityWeather { + + private int lifeTicks; + public long a; + private int c; + private final boolean d; + @Nullable + private EntityPlayer e; + public boolean isEffect; // CraftBukkit + public boolean isSilent = false; // Spigot + + public EntityLightning(World world, double d0, double d1, double d2, boolean flag) { + super(EntityTypes.LIGHTNING_BOLT, world); + this.isEffect = flag; // CraftBukkit + this.setPositionRotation(d0, d1, d2, 0.0F, 0.0F); + this.lifeTicks = 2; + this.a = this.random.nextLong(); + this.c = this.random.nextInt(3) + 1; + this.d = flag; + EnumDifficulty enumdifficulty = world.getDifficulty(); + + if (enumdifficulty == EnumDifficulty.NORMAL || enumdifficulty == EnumDifficulty.HARD) { + this.a(4); + } + + } + + // Spigot start + public EntityLightning(World world, double d0, double d1, double d2, boolean isEffect, boolean isSilent) + { + this( world, d0, d1, d2, isEffect ); + this.isSilent = isSilent; + } + // Spigot end + + public SoundCategory bV() { + return SoundCategory.WEATHER; + } + + public void d(@Nullable EntityPlayer entityplayer) { + this.e = entityplayer; + } + + public void tick() { + super.tick(); + if (!isSilent && this.lifeTicks == 2) { // Spigot + // CraftBukkit start - Use relative location for far away sounds + // this.world.a((EntityHuman) null, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_LIGHTNING_BOLT_THUNDER, SoundCategory.WEATHER, 10000.0F, 0.8F + this.random.nextFloat() * 0.2F); + float pitch = 0.8F + this.random.nextFloat() * 0.2F; + int viewDistance = ((WorldServer) this.world).getServer().getViewDistance() * 16; + for (EntityPlayer player : (List) (List) this.world.players) { + double deltaX = this.locX - player.locX; + double deltaZ = this.locZ - player.locZ; + double distanceSquared = deltaX * deltaX + deltaZ * deltaZ; + // Paper start - Limit lightning strike effect distance + if (distanceSquared <= this.world.paperConfig.sqrMaxLightningImpactSoundDistance) { + player.playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(SoundEffects.ENTITY_LIGHTNING_BOLT_IMPACT, + SoundCategory.WEATHER, this.locX, this.locY, this.locZ, 2.0f, 0.5F + this.random.nextFloat() * 0.2F)); + } + + if (world.paperConfig.sqrMaxThunderDistance != -1 && distanceSquared >= world.paperConfig.sqrMaxThunderDistance) { + continue; + } + + // Paper end + if (distanceSquared > viewDistance * viewDistance) { + double deltaLength = Math.sqrt(distanceSquared); + double relativeX = player.locX + (deltaX / deltaLength) * viewDistance; + double relativeZ = player.locZ + (deltaZ / deltaLength) * viewDistance; + player.playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(SoundEffects.ENTITY_LIGHTNING_BOLT_THUNDER, SoundCategory.WEATHER, relativeX, this.locY, relativeZ, 10000.0F, pitch)); + } else { + player.playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(SoundEffects.ENTITY_LIGHTNING_BOLT_THUNDER, SoundCategory.WEATHER, this.locX, this.locY, this.locZ, 10000.0F, pitch)); + } + } + // CraftBukkit end + //this.world.a((EntityHuman) null, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_LIGHTNING_BOLT_IMPACT, SoundCategory.WEATHER, 2.0f, 0.5F + this.random.nextFloat() * 0.2F); // Paper - Limit lightning strike effect distance (the packet is now sent from inside the loop) + } + + --this.lifeTicks; + if (this.lifeTicks < 0) { + if (this.c == 0) { + this.die(); + } else if (this.lifeTicks < -this.random.nextInt(10)) { + --this.c; + this.lifeTicks = 1; + this.a = this.random.nextLong(); + this.a(0); + } + } + + if (this.lifeTicks >= 0 && !this.isEffect) { // CraftBukkit - add !this.isEffect + if (this.world.isClientSide) { + this.world.d(2); + } else if (!this.d) { + double d0 = 3.0D; + List list = this.world.getEntities(this, new AxisAlignedBB(this.locX - 3.0D, this.locY - 3.0D, this.locZ - 3.0D, this.locX + 3.0D, this.locY + 6.0D + 3.0D, this.locZ + 3.0D)); + + for (int i = 0; i < list.size(); ++i) { + Entity entity = (Entity) list.get(i); + + entity.onLightningStrike(this); + } + + if (this.e != null) { + CriterionTriggers.E.a(this.e, (Collection) list); + } + } + } + + } + + private void a(int i) { + if (!this.d && !this.world.isClientSide && this.world.getGameRules().getBoolean("doFireTick")) { + IBlockData iblockdata = Blocks.FIRE.getBlockData(); + BlockPosition blockposition = new BlockPosition(this); + + if (this.world.areChunksLoaded(blockposition, 10) && this.world.getType(blockposition).isAir() && iblockdata.canPlace(this.world, blockposition)) { + // CraftBukkit start - add "!isEffect" + if (!isEffect && !CraftEventFactory.callBlockIgniteEvent(world, blockposition, this).isCancelled()) { + this.world.setTypeUpdate(blockposition, iblockdata); + } + // CraftBukkit end + } + + for (int j = 0; j < i; ++j) { + BlockPosition blockposition1 = blockposition.a(this.random.nextInt(3) - 1, this.random.nextInt(3) - 1, this.random.nextInt(3) - 1); + + if (this.world.getType(blockposition1).isAir() && iblockdata.canPlace(this.world, blockposition1)) { + // CraftBukkit start - add "!isEffect" + if (!isEffect && !CraftEventFactory.callBlockIgniteEvent(world, blockposition1, this).isCancelled()) { + this.world.setTypeUpdate(blockposition1, iblockdata); + } + // CraftBukkit end + } + } + + } + } + + protected void x_() {} + + protected void a(NBTTagCompound nbttagcompound) {} + + protected void b(NBTTagCompound nbttagcompound) {} +} diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java new file mode 100644 index 000000000000..b4f21171b82c --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -0,0 +1,2913 @@ +package net.minecraft.server; + +import com.destroystokyo.paper.event.player.PlayerArmorChangeEvent; +import com.google.common.base.Objects; +import com.google.common.collect.Maps; + +import java.util.Arrays; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.UUID; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import java.util.ArrayList; +import java.util.stream.Collectors; + +import com.google.common.base.Function; +import com.google.common.collect.Lists; +import org.bukkit.Location; +import org.bukkit.craftbukkit.attribute.CraftAttributeMap; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityDamageEvent.DamageModifier; +import org.bukkit.event.entity.EntityPotionEffectEvent; +import org.bukkit.event.entity.EntityRegainHealthEvent; +import org.bukkit.event.entity.EntityResurrectEvent; +import org.bukkit.event.entity.EntityTeleportEvent; +import org.bukkit.event.player.PlayerItemConsumeEvent; +// CraftBukkit end + +import co.aikar.timings.MinecraftTimings; // Paper + +public abstract class EntityLiving extends Entity { + + private static final Logger a = LogManager.getLogger(); + private static final UUID b = UUID.fromString("662A6B8D-DA3E-4C1C-8813-96EA6097278D"); + private static final AttributeModifier c = (new AttributeModifier(EntityLiving.b, "Sprinting speed boost", 0.30000001192092896D, 2)).a(false); + protected static final DataWatcherObject aw = DataWatcher.a(EntityLiving.class, DataWatcherRegistry.a); + public static final DataWatcherObject HEALTH = DataWatcher.a(EntityLiving.class, DataWatcherRegistry.c); + private static final DataWatcherObject g = DataWatcher.a(EntityLiving.class, DataWatcherRegistry.b); + private static final DataWatcherObject h = DataWatcher.a(EntityLiving.class, DataWatcherRegistry.i); + private static final DataWatcherObject bx = DataWatcher.a(EntityLiving.class, DataWatcherRegistry.b); + private AttributeMapBase attributeMap; + public CombatTracker combatTracker = new CombatTracker(this); + public final Map effects = Maps.newHashMap(); + private final NonNullList bB; + private final NonNullList bC; + public boolean ax; + public EnumHand ay; + public int az; + public int aA; + public int hurtTicks; + public int aC; + public float aD; + public int deathTicks; + public float aF; + public float aG; + protected int aH; + public float aI; + public float aJ; + public float aK; + public int maxNoDamageTicks; + public float aM; + public float aN; + public float aO; + public float aP; + public float aQ; + public float aR; + public float aS; + public float aT; + public float aU; + public EntityHuman killer; + public int lastDamageByPlayerTime; // Paper - public + protected boolean killed; protected void setDying(boolean dying) { this.killed = dying; } protected boolean isDying() { return this.killed; } // Paper - OBFHELPER + protected int ticksFarFromPlayer; + protected float aZ; + protected float ba; + protected float bb; + protected float bc; + protected float bd; + protected int be; protected int getKillCount() { return this.be; } // Paper - OBFHELPER + public float lastDamage; + protected boolean bg; + public float bh; + public float bi; + public float bj; + public float bk; + protected int bl; + protected double bm; + protected double bn; + protected double bo; + protected double bp; + protected double bq; + protected double br; + protected int bs; + public boolean updateEffects; + public EntityLiving lastDamager; + public int hurtTimestamp; + private EntityLiving bG; + private int bH; + private float bI; + private int bJ; + private float bK; + public ItemStack activeItem; // Paper - public + protected int bu; + protected int bv; + private BlockPosition bL; + private DamageSource bM; + private long bN; + protected int bw; + private float bO; + private float bP; + // CraftBukkit start + public int expToDrop; + public int maxAirTicks = 300; + boolean forceDrops; + ArrayList drops = new ArrayList(); + public org.bukkit.craftbukkit.attribute.CraftAttributeMap craftAttributes; + public boolean collides = true; + public boolean canPickUpLoot; + public org.bukkit.craftbukkit.entity.CraftLivingEntity getBukkitLivingEntity() { return (org.bukkit.craftbukkit.entity.CraftLivingEntity) super.getBukkitEntity(); } // Paper + public boolean silentDeath = false; // Paper - mark entity as dying silently for cancellable death event + + @Override + public float getBukkitYaw() { + return getHeadRotation(); + } + // CraftBukkit end + // Spigot start + public void inactiveTick() + { + super.inactiveTick(); + ++this.ticksFarFromPlayer; // Above all the floats + } + // Spigot end + + protected EntityLiving(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.bB = NonNullList.a(2, ItemStack.a); + this.bC = NonNullList.a(4, ItemStack.a); + this.maxNoDamageTicks = 20; + this.aU = 0.02F; + this.updateEffects = true; + this.activeItem = ItemStack.a; + this.initAttributes(); + // CraftBukkit - setHealth(getMaxHealth()) inlined and simplified to skip the instanceof check for EntityPlayer, as getBukkitEntity() is not initialized in constructor + this.datawatcher.set(EntityLiving.HEALTH, (float) this.getAttributeInstance(GenericAttributes.maxHealth).getValue()); + this.j = true; + this.aP = (float) ((Math.random() + 1.0D) * 0.009999999776482582D); + this.setPosition(this.locX, this.locY, this.locZ); + this.aO = (float) Math.random() * 12398.0F; + this.yaw = (float) (Math.random() * 6.2831854820251465D); + this.aS = this.yaw; + this.Q = 0.6F; + } + + public void killEntity() { + this.damageEntity(DamageSource.OUT_OF_WORLD, Float.MAX_VALUE); + } + + protected void x_() { + this.datawatcher.register(EntityLiving.aw, (byte) 0); + this.datawatcher.register(EntityLiving.g, 0); + this.datawatcher.register(EntityLiving.h, false); + this.datawatcher.register(EntityLiving.bx, 0); + this.datawatcher.register(EntityLiving.HEALTH, 1.0F); + } + + protected void initAttributes() { + this.getAttributeMap().b(GenericAttributes.maxHealth); + this.getAttributeMap().b(GenericAttributes.c); + this.getAttributeMap().b(GenericAttributes.MOVEMENT_SPEED); + this.getAttributeMap().b(GenericAttributes.h); + this.getAttributeMap().b(GenericAttributes.i); + } + + protected void a(double d0, boolean flag, IBlockData iblockdata, BlockPosition blockposition) { + if (!this.isInWater()) { + this.at(); + } + + if (!this.world.isClientSide && this.fallDistance > 3.0F && flag) { + float f = (float) MathHelper.f(this.fallDistance - 3.0F); + + if (!iblockdata.isAir()) { + double d1 = Math.min((double) (0.2F + f / 15.0F), 2.5D); + int i = (int) (150.0D * d1); + + // CraftBukkit start - visiblity api + if (this instanceof EntityPlayer) { + ((WorldServer) this.world).sendParticles((EntityPlayer) this, new ParticleParamBlock(Particles.d, iblockdata), this.locX, this.locY, this.locZ, i, 0.0D, 0.0D, 0.0D, 0.15000000596046448D, false); + } else { + ((WorldServer) this.world).a(new ParticleParamBlock(Particles.d, iblockdata), this.locX, this.locY, this.locZ, i, 0.0D, 0.0D, 0.0D, 0.15000000596046448D); + } + // CraftBukkit end + } + } + + super.a(d0, flag, iblockdata, blockposition); + } + + public boolean canBreatheUnderwater() { return this.ca(); } // Paper - OBFHELPER + public boolean ca() { + return this.getMonsterType() == EnumMonsterType.UNDEAD; + } + + public void W() { + this.aF = this.aG; + super.W(); + this.world.methodProfiler.enter("livingEntityBaseTick"); + boolean flag = this instanceof EntityHuman; + + if (this.isAlive()) { + if (this.inBlock()) { + this.damageEntity(DamageSource.STUCK, 1.0F); + } else if (flag && !this.world.getWorldBorder().a(this.getBoundingBox())) { + double d0 = this.world.getWorldBorder().a((Entity) this) + this.world.getWorldBorder().getDamageBuffer(); + + if (d0 < 0.0D) { + double d1 = this.world.getWorldBorder().getDamageAmount(); + + if (d1 > 0.0D) { + this.damageEntity(DamageSource.STUCK, (float) Math.max(1, MathHelper.floor(-d0 * d1))); + } + } + } + } + + if (this.isFireProof() || this.world.isClientSide) { + this.extinguish(); + } + + boolean flag1 = flag && ((EntityHuman) this).abilities.isInvulnerable; + + if (this.isAlive()) { + if (this.a(TagsFluid.WATER) && this.world.getType(new BlockPosition(this.locX, this.locY + (double) this.getHeadHeight(), this.locZ)).getBlock() != Blocks.BUBBLE_COLUMN) { + if (!this.canBreatheUnderwater() && !MobEffectUtil.c(this) && !flag1) { // Paper - use OBFHELPER so it can be overridden + this.setAirTicks(this.k(this.getAirTicks())); + if (this.getAirTicks() == -20) { + this.setAirTicks(0); + + for (int i = 0; i < 8; ++i) { + float f = this.random.nextFloat() - this.random.nextFloat(); + float f1 = this.random.nextFloat() - this.random.nextFloat(); + float f2 = this.random.nextFloat() - this.random.nextFloat(); + + this.world.addParticle(Particles.e, this.locX + (double) f, this.locY + (double) f1, this.locZ + (double) f2, this.motX, this.motY, this.motZ); + } + + this.damageEntity(DamageSource.DROWN, 2.0F); + } + } + + if (!this.world.isClientSide && this.isPassenger() && this.getVehicle() != null && !this.getVehicle().aY()) { + this.stopRiding(); + } + } else if (this.getAirTicks() < this.bf()) { + this.setAirTicks(this.l(this.getAirTicks())); + } + + if (!this.world.isClientSide) { + BlockPosition blockposition = new BlockPosition(this); + + if (!Objects.equal(this.bL, blockposition)) { + this.bL = blockposition; + this.b(blockposition); + } + } + } + + if (this.isAlive() && this.ap()) { + this.extinguish(); + } + + this.aM = this.aN; + if (this.hurtTicks > 0) { + --this.hurtTicks; + } + + if (this.noDamageTicks > 0 && !(this instanceof EntityPlayer)) { + --this.noDamageTicks; + } + + if (this.getHealth() <= 0.0F) { + this.cb(); + } + + if (this.lastDamageByPlayerTime > 0) { + --this.lastDamageByPlayerTime; + } else { + this.killer = null; + } + + if (this.bG != null && !this.bG.isAlive()) { + this.bG = null; + } + + if (this.lastDamager != null) { + if (!this.lastDamager.isAlive()) { + this.setLastDamager((EntityLiving) null); + } else if (this.ticksLived - this.hurtTimestamp > 100) { + this.setLastDamager((EntityLiving) null); + } + } + + this.tickPotionEffects(); + this.bc = this.bb; + this.aR = this.aQ; + this.aT = this.aS; + this.lastYaw = this.yaw; + this.lastPitch = this.pitch; + this.world.methodProfiler.exit(); + } + + // CraftBukkit start + public int getExpReward() { + int exp = this.getExpValue(this.killer); + + if (!this.world.isClientSide && (this.lastDamageByPlayerTime > 0 || this.alwaysGivesExp()) && this.isDropExperience() && this.world.getGameRules().getBoolean("doMobLoot")) { + return exp; + } else { + return 0; + } + } + // CraftBukkit end + + protected void b(BlockPosition blockposition) { + int i = EnchantmentManager.a(Enchantments.FROST_WALKER, this); + + if (i > 0) { + EnchantmentFrostWalker.a(this, this.world, blockposition, i); + } + + } + + public boolean isBaby() { + return false; + } + + public boolean aY() { + return false; + } + + protected void cb() { + ++this.deathTicks; + if (this.deathTicks >= 20 && !this.dead) { // CraftBukkit - (this.deathTicks == 20) -> (this.deathTicks >= 20 && !this.dead) + int i; + + // CraftBukkit start - Update getExpReward() above if the removed if() changes! + i = this.expToDrop; + while (i > 0) { + int j = EntityExperienceOrb.getOrbValue(i); + + i -= j; + EntityLiving attacker = killer != null ? killer : lastDamager; // Paper + this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j, this instanceof EntityPlayer ? org.bukkit.entity.ExperienceOrb.SpawnReason.PLAYER_DEATH : org.bukkit.entity.ExperienceOrb.SpawnReason.ENTITY_DEATH, attacker, this)); // Paper + } + this.expToDrop = 0; + // CraftBukkit end + + this.die(); + + for (i = 0; i < 20; ++i) { + double d0 = this.random.nextGaussian() * 0.02D; + double d1 = this.random.nextGaussian() * 0.02D; + double d2 = this.random.nextGaussian() * 0.02D; + + this.world.addParticle(Particles.J, this.locX + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width, this.locY + (double) (this.random.nextFloat() * this.length), this.locZ + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width, d0, d1, d2); + } + } + + } + + protected boolean isDropExperience() { + return !this.isBaby(); + } + + protected int k(int i) { + int j = EnchantmentManager.getOxygenEnchantmentLevel(this); + + return j > 0 && this.random.nextInt(j + 1) > 0 ? i : i - 1; + } + + protected int l(int i) { + return Math.min(i + 4, this.bf()); + } + + protected int getExpValue(EntityHuman entityhuman) { + return 0; + } + + protected boolean alwaysGivesExp() { + return false; + } + + public Random getRandom() { + return this.random; + } + + @Nullable + public EntityLiving getLastDamager() { + return this.lastDamager; + } + + public int cg() { + return this.hurtTimestamp; + } + + public void setLastDamager(@Nullable EntityLiving entityliving) { + this.lastDamager = entityliving; + this.hurtTimestamp = this.ticksLived; + } + + public EntityLiving ch() { + return this.bG; + } + + public int ci() { + return this.bH; + } + + public void z(Entity entity) { + if (entity instanceof EntityLiving) { + this.bG = (EntityLiving) entity; + } else { + this.bG = null; + } + + this.bH = this.ticksLived; + } + + public int cj() { + return this.ticksFarFromPlayer; + } + + protected void b(ItemStack itemstack) { + if (!itemstack.isEmpty()) { + SoundEffect soundeffect = SoundEffects.ITEM_ARMOR_EQUIP_GENERIC; + Item item = itemstack.getItem(); + + if (item instanceof ItemArmor) { + soundeffect = ((ItemArmor) item).d().b(); + } else if (item == Items.ELYTRA) { + soundeffect = SoundEffects.ITEM_ARMOR_EQUIP_ELYTRA; + } + + this.a(soundeffect, 1.0F, 1.0F); + } + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setFloat("Health", this.getHealth()); + nbttagcompound.setShort("HurtTime", (short) this.hurtTicks); + nbttagcompound.setInt("HurtByTimestamp", this.hurtTimestamp); + nbttagcompound.setShort("DeathTime", (short) this.deathTicks); + nbttagcompound.setFloat("AbsorptionAmount", this.getAbsorptionHearts()); + EnumItemSlot[] aenumitemslot = EnumItemSlot.values(); + int i = aenumitemslot.length; + + EnumItemSlot enumitemslot; + int j; + ItemStack itemstack; + + for (j = 0; j < i; ++j) { + enumitemslot = aenumitemslot[j]; + itemstack = this.getEquipment(enumitemslot); + if (!itemstack.isEmpty()) { + this.getAttributeMap().a(itemstack.a(enumitemslot)); + } + } + + nbttagcompound.set("Attributes", GenericAttributes.a(this.getAttributeMap())); + aenumitemslot = EnumItemSlot.values(); + i = aenumitemslot.length; + + for (j = 0; j < i; ++j) { + enumitemslot = aenumitemslot[j]; + itemstack = this.getEquipment(enumitemslot); + if (!itemstack.isEmpty()) { + this.getAttributeMap().b(itemstack.a(enumitemslot)); + } + } + + if (!this.effects.isEmpty()) { + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = this.effects.values().iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + nbttaglist.add((NBTBase) mobeffect.a(new NBTTagCompound())); + } + + nbttagcompound.set("ActiveEffects", nbttaglist); + } + + nbttagcompound.setBoolean("FallFlying", this.dc()); + } + + public void a(NBTTagCompound nbttagcompound) { + // Paper start - jvm keeps optimizing the setter + float absorptionAmount = nbttagcompound.getFloat("AbsorptionAmount"); + if (Float.isNaN(absorptionAmount)) { + absorptionAmount = 0; + } + this.setAbsorptionHearts(absorptionAmount); + // Paper end + if (nbttagcompound.hasKeyOfType("Attributes", 9) && this.world != null && !this.world.isClientSide) { + GenericAttributes.a(this.getAttributeMap(), nbttagcompound.getList("Attributes", 10)); + } + + if (nbttagcompound.hasKeyOfType("ActiveEffects", 9)) { + NBTTagList nbttaglist = nbttagcompound.getList("ActiveEffects", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.getCompound(i); + MobEffect mobeffect = MobEffect.b(nbttagcompound1); + + if (mobeffect != null) { + this.effects.put(mobeffect.getMobEffect(), mobeffect); + } + } + } + + // CraftBukkit start + if (nbttagcompound.hasKey("Bukkit.MaxHealth")) { + NBTBase nbtbase = nbttagcompound.get("Bukkit.MaxHealth"); + if (nbtbase.getTypeId() == 5) { + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(((NBTTagFloat) nbtbase).asDouble()); + } else if (nbtbase.getTypeId() == 3) { + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(((NBTTagInt) nbtbase).asDouble()); + } + } + // CraftBukkit end + + if (nbttagcompound.hasKeyOfType("Health", 99)) { + this.setHealth(nbttagcompound.getFloat("Health")); + } + + this.hurtTicks = nbttagcompound.getShort("HurtTime"); + this.deathTicks = nbttagcompound.getShort("DeathTime"); + this.hurtTimestamp = nbttagcompound.getInt("HurtByTimestamp"); + if (nbttagcompound.hasKeyOfType("Team", 8)) { + String s = nbttagcompound.getString("Team"); + ScoreboardTeam scoreboardteam = this.world.getScoreboard().getTeam(s); + if (!world.paperConfig.nonPlayerEntitiesOnScoreboards && !(this instanceof EntityHuman)) { scoreboardteam = null; } // Paper + boolean flag = scoreboardteam != null && this.world.getScoreboard().addPlayerToTeam(this.bu(), scoreboardteam); + + if (!flag) { + EntityLiving.a.warn("Unable to add mob to team \"{}\" (that team probably doesn't exist)", s); + } + } + + if (nbttagcompound.getBoolean("FallFlying")) { + this.setFlag(7, true); + } + + } + + // CraftBukkit start + private boolean isTickingEffects = false; + private List effectsToProcess = Lists.newArrayList(); + + private static class ProcessableEffect { + + private MobEffectList type; + private MobEffect effect; + private final EntityPotionEffectEvent.Cause cause; + + private ProcessableEffect(MobEffect effect, EntityPotionEffectEvent.Cause cause) { + this.effect = effect; + this.cause = cause; + } + + private ProcessableEffect(MobEffectList type, EntityPotionEffectEvent.Cause cause) { + this.type = type; + this.cause = cause; + } + } + // CraftBukkit end + + protected void tickPotionEffects() { + Iterator iterator = this.effects.keySet().iterator(); + + isTickingEffects = true; // CraftBukkit + try { + while (iterator.hasNext()) { + MobEffectList mobeffectlist = (MobEffectList) iterator.next(); + MobEffect mobeffect = (MobEffect) this.effects.get(mobeffectlist); + + if (!mobeffect.tick(this)) { + if (!this.world.isClientSide) { + // CraftBukkit start + EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, mobeffect, null, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.EXPIRATION); + if (event.isCancelled()) { + continue; + } + // CraftBukkit end + iterator.remove(); + this.b(mobeffect); + } + } else if (mobeffect.getDuration() % 600 == 0) { + this.a(mobeffect, false); + } + } + } catch (ConcurrentModificationException concurrentmodificationexception) { + ; + } + // CraftBukkit start + isTickingEffects = false; + for (ProcessableEffect e : effectsToProcess) { + if (e.effect != null) { + addEffect(e.effect, e.cause); + } else { + removeEffect(e.type, e.cause); + } + } + effectsToProcess.clear(); + // CraftBukkit end + + if (this.updateEffects) { + if (!this.world.isClientSide) { + this.C(); + } + + this.updateEffects = false; + } + + int i = (Integer) this.datawatcher.get(EntityLiving.g); + boolean flag = (Boolean) this.datawatcher.get(EntityLiving.h); + + if (i > 0) { + boolean flag1; + + if (this.isInvisible()) { + flag1 = this.random.nextInt(15) == 0; + } else { + flag1 = this.random.nextBoolean(); + } + + if (flag) { + flag1 &= this.random.nextInt(5) == 0; + } + + if (flag1 && i > 0) { + double d0 = (double) (i >> 16 & 255) / 255.0D; + double d1 = (double) (i >> 8 & 255) / 255.0D; + double d2 = (double) (i >> 0 & 255) / 255.0D; + + this.world.addParticle(flag ? Particles.a : Particles.s, this.locX + (this.random.nextDouble() - 0.5D) * (double) this.width, this.locY + this.random.nextDouble() * (double) this.length, this.locZ + (this.random.nextDouble() - 0.5D) * (double) this.width, d0, d1, d2); + } + } + + } + + protected void C() { + if (this.effects.isEmpty()) { + this.cl(); + this.setInvisible(false); + } else { + Collection collection = this.effects.values(); + + this.datawatcher.set(EntityLiving.h, c(collection)); + this.datawatcher.set(EntityLiving.g, PotionUtil.a(collection)); + this.setInvisible(this.hasEffect(MobEffects.INVISIBILITY)); + } + + } + + public static boolean c(Collection collection) { + Iterator iterator = collection.iterator(); + + MobEffect mobeffect; + + do { + if (!iterator.hasNext()) { + return true; + } + + mobeffect = (MobEffect) iterator.next(); + } while (mobeffect.isAmbient()); + + return false; + } + + protected void cl() { + this.datawatcher.set(EntityLiving.h, false); + this.datawatcher.set(EntityLiving.g, 0); + } + + // CraftBukkit start + public boolean removeAllEffects() { + return removeAllEffects(org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN); + } + + public boolean removeAllEffects(EntityPotionEffectEvent.Cause cause) { + // CraftBukkit end + if (this.world.isClientSide) { + return false; + } else { + Iterator iterator = this.effects.values().iterator(); + + boolean flag; + + for (flag = false; iterator.hasNext(); flag = true) { + // CraftBukkit start + MobEffect effect = (MobEffect) iterator.next(); + EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, effect, null, cause, EntityPotionEffectEvent.Action.CLEARED); + if (event.isCancelled()) { + continue; + } + this.b(effect); + // CraftBukkit end + iterator.remove(); + } + + return flag; + } + } + + public Collection getEffects() { + return this.effects.values(); + } + + public Map co() { + return this.effects; + } + + public boolean hasEffect(MobEffectList mobeffectlist) { + return this.effects.containsKey(mobeffectlist); + } + + @Nullable + public MobEffect getEffect(MobEffectList mobeffectlist) { + return (MobEffect) this.effects.get(mobeffectlist); + } + + // CraftBukkit start + public boolean addEffect(MobEffect mobeffect) { + return addEffect(mobeffect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN); + } + + public boolean addEffect(MobEffect mobeffect, EntityPotionEffectEvent.Cause cause) { + org.spigotmc.AsyncCatcher.catchOp( "effect add"); // Spigot + if (isTickingEffects) { + effectsToProcess.add(new ProcessableEffect(mobeffect, cause)); + return true; + } + // CraftBukkit end + + if (!this.d(mobeffect)) { + return false; + } else { + MobEffect mobeffect1 = (MobEffect) this.effects.get(mobeffect.getMobEffect()); + + // CraftBukkit start + boolean override = false; + if (mobeffect1 != null) { + override = new MobEffect(mobeffect1).a(mobeffect); + } + + EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, mobeffect1, mobeffect, cause, override); + if (event.isCancelled()) { + return false; + } + // CraftBukkit end + + if (mobeffect1 == null) { + this.effects.put(mobeffect.getMobEffect(), mobeffect); + this.a(mobeffect); + return true; + // CraftBukkit start + } else if (event.isOverride()) { + mobeffect1.a(mobeffect); + this.a(mobeffect1, true); + // CraftBukkit end + return true; + } else { + return false; + } + } + } + + public boolean d(MobEffect mobeffect) { + if (this.getMonsterType() == EnumMonsterType.UNDEAD) { + MobEffectList mobeffectlist = mobeffect.getMobEffect(); + + if (mobeffectlist == MobEffects.REGENERATION || mobeffectlist == MobEffects.POISON) { + return false; + } + } + + return true; + } + + public boolean cp() { + return this.getMonsterType() == EnumMonsterType.UNDEAD; + } + + // CraftBukkit start + @Nullable + public MobEffect c(@Nullable MobEffectList mobeffectlist) { + return c(mobeffectlist, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN); + } + + @Nullable + public MobEffect c(@Nullable MobEffectList mobeffectlist, EntityPotionEffectEvent.Cause cause) { + if (isTickingEffects) { + effectsToProcess.add(new ProcessableEffect(mobeffectlist, cause)); + return null; + } + + MobEffect effect = this.effects.get(mobeffectlist); + if (effect == null) { + return null; + } + + EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, effect, null, cause); + if (event.isCancelled()) { + return null; + } + + return (MobEffect) this.effects.remove(mobeffectlist); + } + + public boolean removeEffect(MobEffectList mobeffectlist) { + return removeEffect(mobeffectlist, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.UNKNOWN); + } + + public boolean removeEffect(MobEffectList mobeffectlist, EntityPotionEffectEvent.Cause cause) { + MobEffect mobeffect = this.c(mobeffectlist, cause); + // CraftBukkit end + + if (mobeffect != null) { + this.b(mobeffect); + return true; + } else { + return false; + } + } + + protected void a(MobEffect mobeffect) { + this.updateEffects = true; + if (!this.world.isClientSide) { + mobeffect.getMobEffect().b(this, this.getAttributeMap(), mobeffect.getAmplifier()); + } + + } + + protected void a(MobEffect mobeffect, boolean flag) { + this.updateEffects = true; + if (flag && !this.world.isClientSide) { + MobEffectList mobeffectlist = mobeffect.getMobEffect(); + + mobeffectlist.a(this, this.getAttributeMap(), mobeffect.getAmplifier()); + mobeffectlist.b(this, this.getAttributeMap(), mobeffect.getAmplifier()); + } + + } + + protected void b(MobEffect mobeffect) { + this.updateEffects = true; + if (!this.world.isClientSide) { + mobeffect.getMobEffect().a(this, this.getAttributeMap(), mobeffect.getAmplifier()); + } + + } + + // CraftBukkit start - Delegate so we can handle providing a reason for health being regained + public void heal(float f) { + heal(f, EntityRegainHealthEvent.RegainReason.CUSTOM); + } + + public void heal(float f, EntityRegainHealthEvent.RegainReason regainReason) { + // Paper start - Forward + heal(f, regainReason, false); + } + + public void heal(float f, EntityRegainHealthEvent.RegainReason regainReason, boolean isFastRegen) { + // Paper end + float f1 = this.getHealth(); + + if (f1 > 0.0F) { + EntityRegainHealthEvent event = new EntityRegainHealthEvent(this.getBukkitEntity(), f, regainReason, isFastRegen); // Paper - Add isFastRegen + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + this.setHealth((float) (this.getHealth() + event.getAmount())); + } + // CraftBukkit end + } + + } + + public float getHealth() { + // CraftBukkit start - Use unscaled health + if (this instanceof EntityPlayer) { + return (float) ((EntityPlayer) this).getBukkitEntity().getHealth(); + } + // CraftBukkit end + return (Float) this.datawatcher.get(EntityLiving.HEALTH); + } + + public void setHealth(float f) { + // Paper start + if (Float.isNaN(f)) { f = getMaxHealth(); if (this.valid) { + System.err.println("[NAN-HEALTH] " + getName() + " had NaN health set"); + } } // Paper end + // CraftBukkit start - Handle scaled health + if (this instanceof EntityPlayer) { + org.bukkit.craftbukkit.entity.CraftPlayer player = ((EntityPlayer) this).getBukkitEntity(); + // Squeeze + if (f < 0.0F) { + player.setRealHealth(0.0D); + } else if (f > player.getMaxHealth()) { + player.setRealHealth(player.getMaxHealth()); + } else { + player.setRealHealth(f); + } + + player.updateScaledHealth(false); + return; + } + // CraftBukkit end + this.datawatcher.set(EntityLiving.HEALTH, MathHelper.a(f, 0.0F, this.getMaxHealth())); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else if (this.world.isClientSide) { + return false; + } else if (this.getHealth() <= 0.0F) { + return false; + } else if (damagesource.p() && this.hasEffect(MobEffects.FIRE_RESISTANCE)) { + return false; + } else { + this.ticksFarFromPlayer = 0; + float f1 = f; + + // CraftBukkit - Moved into damageEntity0(DamageSource, float) + if (false && (damagesource == DamageSource.ANVIL || damagesource == DamageSource.FALLING_BLOCK) && !this.getEquipment(EnumItemSlot.HEAD).isEmpty()) { + this.getEquipment(EnumItemSlot.HEAD).damage((int) (f * 4.0F + this.random.nextFloat() * f * 2.0F), this); + f *= 0.75F; + } + + boolean flag = f > 0.0F && this.applyBlockingModifier(damagesource); // Copied from below + float f2 = 0.0F; + + // CraftBukkit - Moved into damageEntity0(DamageSource, float) + if (false && f > 0.0F && this.applyBlockingModifier(damagesource)) { + this.damageShield(f); + f2 = f; + f = 0.0F; + if (!damagesource.b()) { + Entity entity = damagesource.j(); + + if (entity instanceof EntityLiving) { + this.c((EntityLiving) entity); + } + } + + flag = true; + } + + this.aJ = 1.5F; + boolean flag1 = true; + + if ((float) this.noDamageTicks > (float) this.maxNoDamageTicks / 2.0F) { + if (f <= this.lastDamage) { + this.forceExplosionKnockback = true; // CraftBukkit - SPIGOT-949 - for vanilla consistency, cooldown does not prevent explosion knockback + return false; + } + + // CraftBukkit start + if (!this.damageEntity0(damagesource, f - this.lastDamage)) { + return false; + } + // CraftBukkit end + this.lastDamage = f; + flag1 = false; + } else { + // CraftBukkit start + if (!this.damageEntity0(damagesource, f)) { + return false; + } + this.lastDamage = f; + this.noDamageTicks = this.maxNoDamageTicks; + // this.damageEntity0(damagesource, f); + // CraftBukkit end + this.aC = 10; + this.hurtTicks = this.aC; + } + + // CraftBukkit start + if (this instanceof EntityAnimal) { + ((EntityAnimal) this).resetLove(); + if (this instanceof EntityTameableAnimal) { + ((EntityTameableAnimal) this).getGoalSit().setSitting(false); + } + } + // CraftBukkit end + + this.aD = 0.0F; + Entity entity1 = damagesource.getEntity(); + + if (entity1 != null) { + if (entity1 instanceof EntityLiving) { + this.setLastDamager((EntityLiving) entity1); + } + + if (entity1 instanceof EntityHuman) { + this.lastDamageByPlayerTime = 100; + this.killer = (EntityHuman) entity1; + } else if (entity1 instanceof EntityWolf) { + EntityWolf entitywolf = (EntityWolf) entity1; + + if (entitywolf.isTamed()) { + this.lastDamageByPlayerTime = 100; + this.killer = null; + } + } + } + + boolean knockbackCancelled = world.paperConfig.disableExplosionKnockback && damagesource.isExplosion() && this instanceof EntityHuman; // Paper - Disable explosion knockback + if (flag1) { + if (flag) { + this.world.broadcastEntityEffect(this, (byte) 29); + } else if (damagesource instanceof EntityDamageSource && ((EntityDamageSource) damagesource).y()) { + this.world.broadcastEntityEffect(this, (byte) 33); + } else { + byte b0; + + if (damagesource == DamageSource.DROWN) { + b0 = 36; + } else if (damagesource.p()) { + b0 = 37; + } else { + b0 = 2; + } + + if (!knockbackCancelled) // Paper - Disable explosion knockback + this.world.broadcastEntityEffect(this, b0); + } + + if (damagesource != DamageSource.DROWN && (!flag || f > 0.0F)) { + this.aA(); + } + + if (entity1 != null) { + double d0 = entity1.locX - this.locX; + + double d1; + + for (d1 = entity1.locZ - this.locZ; d0 * d0 + d1 * d1 < 1.0E-4D; d1 = (Math.random() - Math.random()) * 0.01D) { + d0 = (Math.random() - Math.random()) * 0.01D; + } + + this.aD = (float) (MathHelper.c(d1, d0) * 57.2957763671875D - (double) this.yaw); + this.a(entity1, 0.4F, d0, d1); + } else { + this.aD = (float) ((int) (Math.random() * 2.0D) * 180); + } + } + + if (knockbackCancelled) this.world.broadcastEntityEffect(this, (byte) 2); // Paper - Disable explosion knockback + + if (this.getHealth() <= 0.0F) { + if (!this.e(damagesource)) { + // Paper start - moved into CraftEventFactory event caller for cancellable death event + //SoundEffect soundeffect = this.cs(); + + //if (flag1 && soundeffect != null) { + // this.a(soundeffect, this.cD(), this.cE()); + //} + this.silentDeath = !flag1; // mark entity as dying silently + // Paper end + + this.die(damagesource); + this.silentDeath = false; // Paper - cancellable death event - reset to default + } + } else if (flag1) { + this.c(damagesource); + } + + boolean flag2 = !flag || f > 0.0F; + + if (flag2) { + this.bM = damagesource; + this.bN = this.world.getTime(); + } + + if (this instanceof EntityPlayer) { + CriterionTriggers.h.a((EntityPlayer) this, damagesource, f1, f, flag); + if (f2 > 0.0F && f2 < 3.4028235E37F) { + ((EntityPlayer) this).a(StatisticList.DAMAGE_BLOCKED_BY_SHIELD, Math.round(f2 * 10.0F)); + } + } + + if (entity1 instanceof EntityPlayer) { + CriterionTriggers.g.a((EntityPlayer) entity1, this, damagesource, f1, f, flag); + } + + return flag2; + } + } + + protected void c(EntityLiving entityliving) { + entityliving.a(this, 0.5F, this.locX - entityliving.locX, this.locZ - entityliving.locZ); + } + + private boolean e(DamageSource damagesource) { + if (damagesource.ignoresInvulnerability()) { + return false; + } else { + ItemStack itemstack = null; + EnumHand[] aenumhand = EnumHand.values(); + int i = aenumhand.length; + + // CraftBukkit start + ItemStack itemstack1 = ItemStack.a; + for (int j = 0; j < i; ++j) { + EnumHand enumhand = aenumhand[j]; + itemstack1 = this.b(enumhand); + + if (itemstack1.getItem() == Items.TOTEM_OF_UNDYING) { + itemstack = itemstack1.cloneItemStack(); + // itemstack1.subtract(1); // CraftBukkit + break; + } + } + + EntityResurrectEvent event = new EntityResurrectEvent((LivingEntity) this.getBukkitEntity()); + event.setCancelled(itemstack == null); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + if (!itemstack1.isEmpty()) { + itemstack1.subtract(1); + } + if (itemstack != null && this instanceof EntityPlayer) { + // CraftBukkit end + EntityPlayer entityplayer = (EntityPlayer) this; + + entityplayer.b(StatisticList.ITEM_USED.b(Items.TOTEM_OF_UNDYING)); + CriterionTriggers.B.a(entityplayer, itemstack); + } + + this.setHealth(1.0F); + // CraftBukkit start + this.removeAllEffects(org.bukkit.event.entity.EntityPotionEffectEvent.Cause.TOTEM); + this.addEffect(new MobEffect(MobEffects.REGENERATION, 900, 1), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.TOTEM); + this.addEffect(new MobEffect(MobEffects.ABSORBTION, 100, 1), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.TOTEM); + // CraftBukkit end + this.world.broadcastEntityEffect(this, (byte) 35); + } + + return !event.isCancelled(); + } + } + + @Nullable + public DamageSource cr() { + if (this.world.getTime() - this.bN > 40L) { + this.bM = null; + } + + return this.bM; + } + + protected void c(DamageSource damagesource) { + SoundEffect soundeffect = this.d(damagesource); + + if (soundeffect != null) { + this.a(soundeffect, this.cD(), this.cE()); + } + + } + + private boolean applyBlockingModifier(DamageSource damagesource) { + if (!damagesource.ignoresArmor() && this.isBlocking()) { + Vec3D vec3d = damagesource.w(); + + if (vec3d != null) { + Vec3D vec3d1 = this.f(1.0F); + Vec3D vec3d2 = vec3d.a(new Vec3D(this.locX, this.locY, this.locZ)).a(); + + vec3d2 = new Vec3D(vec3d2.x, 0.0D, vec3d2.z); + if (vec3d2.b(vec3d1) < 0.0D) { + return true; + } + } + } + + return false; + } + + public void c(ItemStack itemstack) { + super.a(SoundEffects.ENTITY_ITEM_BREAK, 0.8F, 0.8F + this.world.random.nextFloat() * 0.4F); + this.a(itemstack, 5); + } + + public void die(DamageSource damagesource) { + if (!this.killed) { + Entity entity = damagesource.getEntity(); + EntityLiving entityliving = this.cv(); + + // Paper start - move down to make death event cancellable + //if (this.be >= 0 && entityliving != null) { + // entityliving.a(this, this.be, damagesource); + //} + + //if (entity != null) { + // entity.b(this); + //} + + this.killed = true; + //this.getCombatTracker().g(); + + org.bukkit.event.entity.EntityDeathEvent deathEvent = null; + // Paper end + if (!this.world.isClientSide) { + int i = 0; + + if (entity instanceof EntityHuman) { + i = EnchantmentManager.g((EntityLiving) entity); + } + + if (this.isDropExperience() && this.world.getGameRules().getBoolean("doMobLoot")) { + boolean flag = this.lastDamageByPlayerTime > 0; + + this.a(flag, i, damagesource); + // CraftBukkit start - Call death event + deathEvent = CraftEventFactory.callEntityDeathEvent(this, this.drops); // Paper - cancellable death event + this.drops = new ArrayList(); + } else { + deathEvent = CraftEventFactory.callEntityDeathEvent(this); // Paper - cancellable death event + // CraftBukkit end + } + } + + // Paper start - cancellable death event + if (deathEvent == null || !deathEvent.isCancelled()) { + // triggers and stats got moved down + if (this.getKillCount() >= 0 && entityliving != null) { + entityliving.runKillTrigger(this, this.getKillCount(), damagesource); + } + + if (entity != null) { + entity.onKill(this); + } + + this.getCombatTracker().reset(); + this.setDying(true); + this.world.broadcastEntityEffect(this, (byte) 3); + } else { + this.setDying(false); // Paper - reset if cancelled + this.setHealth((float) deathEvent.getReviveHealth()); + } + // Paper end + } + } + + protected void a(boolean flag, int i, DamageSource damagesource) { + this.dropDeathLoot(flag, i); + this.dropEquipment(flag, i); + } + + protected void dropEquipment(boolean flag, int i) {} + + public void a(Entity entity, float f, double d0, double d1) { + if (this.random.nextDouble() >= this.getAttributeInstance(GenericAttributes.c).getValue()) { + this.impulse = true; + float f1 = MathHelper.sqrt(d0 * d0 + d1 * d1); + + // Paper start - preserve old velocity + double oldMotX = this.motX; + double oldMotY = this.motY; + double oldMotZ = this.motZ; + // Paper end + + this.motX /= 2.0D; + this.motZ /= 2.0D; + this.motX -= d0 / (double) f1 * (double) f; + this.motZ -= d1 / (double) f1 * (double) f; + if (this.onGround) { + this.motY /= 2.0D; + this.motY += (double) f; + if (this.motY > 0.4000000059604645D) { + this.motY = 0.4000000059604645D; + } + } + + // Paper start - call EntityKnockbackByEntityEvent + org.bukkit.util.Vector delta = new org.bukkit.util.Vector(this.motX - oldMotX, this.motY - oldMotY, this.motZ - oldMotZ); + // Restore old velocity to be able to access it in the event + this.motX = oldMotX; + this.motY = oldMotY; + this.motZ = oldMotZ; + if (entity == null || new com.destroystokyo.paper.event.entity.EntityKnockbackByEntityEvent((LivingEntity) getBukkitEntity(), entity.getBukkitEntity(), f, delta).callEvent()) { + this.motX += delta.getX(); + this.motY += delta.getY(); + this.motZ += delta.getZ(); + } + // Paper end + } + } + + @Nullable + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_GENERIC_HURT; + } + + @Nullable public SoundEffect getDeathSoundEffect() { return cs();} // Paper - OBFHELPER + @Nullable + protected SoundEffect cs() { + return SoundEffects.ENTITY_GENERIC_DEATH; + } + + protected SoundEffect m(int i) { + return i > 4 ? SoundEffects.ENTITY_GENERIC_BIG_FALL : SoundEffects.ENTITY_GENERIC_SMALL_FALL; + } + + protected void dropDeathLoot(boolean flag, int i) {} + + public boolean z_() { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.getBoundingBox().minY); + int k = MathHelper.floor(this.locZ); + + if (this instanceof EntityHuman && ((EntityHuman) this).isSpectator()) { + return false; + } else { + BlockPosition blockposition = new BlockPosition(i, j, k); + IBlockData iblockdata = this.world.getType(blockposition); + Block block = iblockdata.getBlock(); + + return block != Blocks.LADDER && block != Blocks.VINE ? block instanceof BlockTrapdoor && this.b(blockposition, iblockdata) : true; + } + } + + private boolean b(BlockPosition blockposition, IBlockData iblockdata) { + if ((Boolean) iblockdata.get(BlockTrapdoor.OPEN)) { + IBlockData iblockdata1 = this.world.getType(blockposition.down()); + + if (iblockdata1.getBlock() == Blocks.LADDER && iblockdata1.get(BlockLadder.FACING) == iblockdata.get(BlockTrapdoor.FACING)) { + return true; + } + } + + return false; + } + + public boolean isAlive() { + return !this.dead && this.getHealth() > 0.0F; + } + + public void c(float f, float f1) { + super.c(f, f1); + MobEffect mobeffect = this.getEffect(MobEffects.JUMP); + float f2 = mobeffect == null ? 0.0F : (float) (mobeffect.getAmplifier() + 1); + int i = MathHelper.f((f - 3.0F - f2) * f1); + + if (i > 0) { + // CraftBukkit start + if (!this.damageEntity(DamageSource.FALL, (float) i)) { + return; + } + // CraftBukkit end + this.a(this.m(i), 1.0F, 1.0F); + // this.damageEntity(DamageSource.FALL, (float) i); // CraftBukkit - moved up + int j = MathHelper.floor(this.locX); + int k = MathHelper.floor(this.locY - 0.20000000298023224D); + int l = MathHelper.floor(this.locZ); + IBlockData iblockdata = this.world.getType(new BlockPosition(j, k, l)); + + if (!iblockdata.isAir()) { + SoundEffectType soundeffecttype = iblockdata.getBlock().getStepSound(); + + this.a(soundeffecttype.g(), soundeffecttype.a() * 0.5F, soundeffecttype.b() * 0.75F); + } + } + + } + + public int getArmorStrength() { + AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.h); + + return MathHelper.floor(attributeinstance.getValue()); + } + + protected void damageArmor(float f) {} + + protected void damageShield(float f) {} + + protected float applyArmorModifier(DamageSource damagesource, float f) { + if (!damagesource.ignoresArmor()) { + // this.damageArmor(f); // CraftBukkit - Moved into damageEntity0(DamageSource, float) + f = CombatMath.a(f, (float) this.getArmorStrength(), (float) this.getAttributeInstance(GenericAttributes.i).getValue()); + } + + return f; + } + + protected float applyMagicModifier(DamageSource damagesource, float f) { + if (damagesource.isStarvation()) { + return f; + } else { + int i; + + // CraftBukkit - Moved to damageEntity0(DamageSource, float) + if (false && this.hasEffect(MobEffects.RESISTANCE) && damagesource != DamageSource.OUT_OF_WORLD) { + i = (this.getEffect(MobEffects.RESISTANCE).getAmplifier() + 1) * 5; + int j = 25 - i; + float f1 = f * (float) j; + float f2 = f; + + f = Math.max(f1 / 25.0F, 0.0F); + float f3 = f2 - f; + + if (f3 > 0.0F && f3 < 3.4028235E37F) { + if (this instanceof EntityPlayer) { + ((EntityPlayer) this).a(StatisticList.DAMAGE_RESISTED, Math.round(f3 * 10.0F)); + } else if (damagesource.getEntity() instanceof EntityPlayer) { + ((EntityPlayer) damagesource.getEntity()).a(StatisticList.DAMAGE_DEALT_RESISTED, Math.round(f3 * 10.0F)); + } + } + } + + if (f <= 0.0F) { + return 0.0F; + } else { + i = EnchantmentManager.a(this.getArmorItems(), damagesource); + if (i > 0) { + f = CombatMath.a(f, (float) i); + } + + return f; + } + } + } + + // CraftBukkit start + protected boolean damageEntity0(final DamageSource damagesource, float f) { // void -> boolean, add final + if (!this.isInvulnerable(damagesource)) { + final boolean human = this instanceof EntityHuman; + float originalDamage = f; + Function hardHat = new Function() { + @Override + public Double apply(Double f) { + if ((damagesource == DamageSource.ANVIL || damagesource == DamageSource.FALLING_BLOCK) && !EntityLiving.this.getEquipment(EnumItemSlot.HEAD).isEmpty()) { + return -(f - (f * 0.75F)); + + } + return -0.0; + } + }; + float hardHatModifier = hardHat.apply((double) f).floatValue(); + f += hardHatModifier; + + Function blocking = new Function() { + @Override + public Double apply(Double f) { + return -((EntityLiving.this.applyBlockingModifier(damagesource)) ? f : 0.0); + } + }; + float blockingModifier = blocking.apply((double) f).floatValue(); + f += blockingModifier; + + Function armor = new Function() { + @Override + public Double apply(Double f) { + return -(f - EntityLiving.this.applyArmorModifier(damagesource, f.floatValue())); + } + }; + float armorModifier = armor.apply((double) f).floatValue(); + f += armorModifier; + + Function resistance = new Function() { + @Override + public Double apply(Double f) { + if (!damagesource.isStarvation() && EntityLiving.this.hasEffect(MobEffects.RESISTANCE) && damagesource != DamageSource.OUT_OF_WORLD) { + int i = (EntityLiving.this.getEffect(MobEffects.RESISTANCE).getAmplifier() + 1) * 5; + int j = 25 - i; + float f1 = f.floatValue() * (float) j; + return -(f - (f1 / 25.0F)); + } + return -0.0; + } + }; + float resistanceModifier = resistance.apply((double) f).floatValue(); + f += resistanceModifier; + + Function magic = new Function() { + @Override + public Double apply(Double f) { + return -(f - EntityLiving.this.applyMagicModifier(damagesource, f.floatValue())); + } + }; + float magicModifier = magic.apply((double) f).floatValue(); + f += magicModifier; + + Function absorption = new Function() { + @Override + public Double apply(Double f) { + return -(Math.max(f - Math.max(f - EntityLiving.this.getAbsorptionHearts(), 0.0F), 0.0F)); + } + }; + float absorptionModifier = absorption.apply((double) f).floatValue(); + + EntityDamageEvent event = CraftEventFactory.handleLivingEntityDamageEvent(this, damagesource, originalDamage, hardHatModifier, blockingModifier, armorModifier, resistanceModifier, magicModifier, absorptionModifier, hardHat, blocking, armor, resistance, magic, absorption); + if (event.isCancelled()) { + return false; + } + + f = (float) event.getFinalDamage(); + + // Resistance + if (event.getDamage(DamageModifier.RESISTANCE) < 0) { + float f3 = (float) -event.getDamage(DamageModifier.RESISTANCE); + if (f3 > 0.0F && f3 < 3.4028235E37F) { + if (this instanceof EntityPlayer) { + ((EntityPlayer) this).a(StatisticList.DAMAGE_RESISTED, Math.round(f3 * 10.0F)); + } else if (damagesource.getEntity() instanceof EntityPlayer) { + ((EntityPlayer) damagesource.getEntity()).a(StatisticList.DAMAGE_DEALT_RESISTED, Math.round(f3 * 10.0F)); + } + } + } + + // Apply damage to helmet + if ((damagesource == DamageSource.ANVIL || damagesource == DamageSource.FALLING_BLOCK) && this.getEquipment(EnumItemSlot.HEAD) != null) { + this.getEquipment(EnumItemSlot.HEAD).damage((int) (event.getDamage() * 4.0F + this.random.nextFloat() * event.getDamage() * 2.0F), this); + } + + // Apply damage to armor + if (!damagesource.ignoresArmor()) { + float armorDamage = (float) (event.getDamage() + event.getDamage(DamageModifier.BLOCKING) + event.getDamage(DamageModifier.HARD_HAT)); + this.damageArmor(armorDamage); + } + + // Apply blocking code // PAIL: steal from above + if (event.getDamage(DamageModifier.BLOCKING) < 0) { + this.world.broadcastEntityEffect(this, (byte) 29); // SPIGOT-4635 - shield damage sound + this.damageShield((float) -event.getDamage(DamageModifier.BLOCKING)); + Entity entity = damagesource.j(); + + if (entity instanceof EntityLiving) { + this.c((EntityLiving) entity); + } + } + + absorptionModifier = (float) -event.getDamage(DamageModifier.ABSORPTION); + this.setAbsorptionHearts(Math.max(this.getAbsorptionHearts() - absorptionModifier, 0.0F)); + float f2 = absorptionModifier; + + if (f2 > 0.0F && f2 < 3.4028235E37F && this instanceof EntityHuman) { + ((EntityHuman) this).a(StatisticList.DAMAGE_ABSORBED, Math.round(f2 * 10.0F)); + } + if (f2 > 0.0F && f2 < 3.4028235E37F && damagesource.getEntity() instanceof EntityPlayer) { + ((EntityPlayer) damagesource.getEntity()).a(StatisticList.DAMAGE_DEALT_ABSORBED, Math.round(f2 * 10.0F)); + } + + if (f > 0 || !human) { + if (human) { + // PAIL: Be sure to drag all this code from the EntityHuman subclass each update. + ((EntityHuman) this).applyExhaustion(damagesource.getExhaustionCost()); + if (f < 3.4028235E37F) { + ((EntityHuman) this).a(StatisticList.DAMAGE_TAKEN, Math.round(f * 10.0F)); + } + } + // CraftBukkit end + float f3 = this.getHealth(); + + this.setHealth(f3 - f); + this.getCombatTracker().trackDamage(damagesource, f3, f); + // CraftBukkit start + if (!human) { + this.setAbsorptionHearts(this.getAbsorptionHearts() - f); + } + + return true; + } else { + // Duplicate triggers if blocking + if (event.getDamage(DamageModifier.BLOCKING) < 0) { + if (this instanceof EntityPlayer) { + CriterionTriggers.h.a((EntityPlayer) this, damagesource, f, originalDamage, true); + f2 = (float) -event.getDamage(DamageModifier.BLOCKING); + if (f2 > 0.0F && f2 < 3.4028235E37F) { + ((EntityPlayer) this).a(StatisticList.DAMAGE_BLOCKED_BY_SHIELD, Math.round(originalDamage * 10.0F)); + } + } + + if (damagesource.getEntity() instanceof EntityPlayer) { + CriterionTriggers.g.a((EntityPlayer) damagesource.getEntity(), this, damagesource, f, originalDamage, true); + } + + return false; + } else { + return originalDamage > 0; + } + // CraftBukkit end + } + } + return false; // CraftBukkit + } + + public CombatTracker getCombatTracker() { + return this.combatTracker; + } + + @Nullable + public EntityLiving cv() { + return (EntityLiving) (this.combatTracker.c() != null ? this.combatTracker.c() : (this.killer != null ? this.killer : (this.lastDamager != null ? this.lastDamager : null))); + } + + public final float getMaxHealth() { + return (float) this.getAttributeInstance(GenericAttributes.maxHealth).getValue(); + } + + public final int getArrowCount() { + return (Integer) this.datawatcher.get(EntityLiving.bx); + } + + public final void setArrowCount(int i) { + this.datawatcher.set(EntityLiving.bx, i); + } + + private int l() { + return MobEffectUtil.a(this) ? 6 - (1 + MobEffectUtil.b(this)) : (this.hasEffect(MobEffects.SLOWER_DIG) ? 6 + (1 + this.getEffect(MobEffects.SLOWER_DIG).getAmplifier()) * 2 : 6); + } + + public void a(EnumHand enumhand) { + if (!this.ax || this.az >= this.l() / 2 || this.az < 0) { + this.az = -1; + this.ax = true; + this.ay = enumhand; + if (this.world instanceof WorldServer) { + ((WorldServer) this.world).getTracker().a((Entity) this, (Packet) (new PacketPlayOutAnimation(this, enumhand == EnumHand.MAIN_HAND ? 0 : 3))); + } + } + + } + + protected void aa() { + this.damageEntity(DamageSource.OUT_OF_WORLD, 4.0F); + } + + protected void cy() { + int i = this.l(); + + if (this.ax) { + ++this.az; + if (this.az >= i) { + this.az = 0; + this.ax = false; + } + } else { + this.az = 0; + } + + this.aG = (float) this.az / (float) i; + } + + public AttributeInstance getAttributeInstance(IAttribute iattribute) { + return this.getAttributeMap().a(iattribute); + } + + public AttributeMapBase getAttributeMap() { + if (this.attributeMap == null) { + this.attributeMap = new AttributeMapServer(); + this.craftAttributes = new CraftAttributeMap(attributeMap); // CraftBukkit + } + + return this.attributeMap; + } + + public EnumMonsterType getMonsterType() { + return EnumMonsterType.UNDEFINED; + } + + public ItemStack getItemInMainHand() { + return this.getEquipment(EnumItemSlot.MAINHAND); + } + + public ItemStack getItemInOffHand() { + return this.getEquipment(EnumItemSlot.OFFHAND); + } + + public ItemStack b(EnumHand enumhand) { + if (enumhand == EnumHand.MAIN_HAND) { + return this.getEquipment(EnumItemSlot.MAINHAND); + } else if (enumhand == EnumHand.OFF_HAND) { + return this.getEquipment(EnumItemSlot.OFFHAND); + } else { + throw new IllegalArgumentException("Invalid hand " + enumhand); + } + } + + public void a(EnumHand enumhand, ItemStack itemstack) { + if (enumhand == EnumHand.MAIN_HAND) { + this.setSlot(EnumItemSlot.MAINHAND, itemstack); + } else { + if (enumhand != EnumHand.OFF_HAND) { + throw new IllegalArgumentException("Invalid hand " + enumhand); + } + + this.setSlot(EnumItemSlot.OFFHAND, itemstack); + } + + } + + public boolean a(EnumItemSlot enumitemslot) { + return !this.getEquipment(enumitemslot).isEmpty(); + } + + public abstract Iterable getArmorItems(); + + public abstract ItemStack getEquipment(EnumItemSlot enumitemslot); + + public abstract void setSlot(EnumItemSlot enumitemslot, ItemStack itemstack); + + public void setSprinting(boolean flag) { + super.setSprinting(flag); + AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED); + + if (attributeinstance.a(EntityLiving.b) != null) { + attributeinstance.c(EntityLiving.c); + } + + if (flag) { + attributeinstance.b(EntityLiving.c); + } + + } + + public float getDeathSoundVolume() { return cD();} // Paper - OBFHELPER + protected float cD() { + return 1.0F; + } + + public float getDeathSoundPitch() { return cE();} // Paper - OBFHELPER + protected float cE() { + return this.isBaby() ? (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.5F : (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F; + } + + protected boolean isFrozen() { + return this.getHealth() <= 0.0F; + } + + public void A(Entity entity) { + double d0; + + if (!(entity instanceof EntityBoat) && !(entity instanceof EntityHorseAbstract)) { + double d1 = entity.locX; + double d2 = entity.getBoundingBox().minY + (double) entity.length; + + d0 = entity.locZ; + EnumDirection enumdirection = entity.getAdjustedDirection(); + + if (enumdirection != null) { + EnumDirection enumdirection1 = enumdirection.e(); + int[][] aint = new int[][] { { 0, 1}, { 0, -1}, { -1, 1}, { -1, -1}, { 1, 1}, { 1, -1}, { -1, 0}, { 1, 0}, { 0, 1}}; + double d3 = Math.floor(this.locX) + 0.5D; + double d4 = Math.floor(this.locZ) + 0.5D; + double d5 = this.getBoundingBox().maxX - this.getBoundingBox().minX; + double d6 = this.getBoundingBox().maxZ - this.getBoundingBox().minZ; + AxisAlignedBB axisalignedbb = new AxisAlignedBB(d3 - d5 / 2.0D, entity.getBoundingBox().minY, d4 - d6 / 2.0D, d3 + d5 / 2.0D, Math.floor(entity.getBoundingBox().minY) + (double) this.length, d4 + d6 / 2.0D); + int[][] aint1 = aint; + int i = aint.length; + + for (int j = 0; j < i; ++j) { + int[] aint2 = aint1[j]; + double d7 = (double) (enumdirection.getAdjacentX() * aint2[0] + enumdirection1.getAdjacentX() * aint2[1]); + double d8 = (double) (enumdirection.getAdjacentZ() * aint2[0] + enumdirection1.getAdjacentZ() * aint2[1]); + double d9 = d3 + d7; + double d10 = d4 + d8; + AxisAlignedBB axisalignedbb1 = axisalignedbb.d(d7, 0.0D, d8); + + if (this.world.getCubes(this, axisalignedbb1)) { + if (this.world.getType(new BlockPosition(d9, this.locY, d10)).q()) { + this.enderTeleportTo(d9, this.locY + 1.0D, d10); + return; + } + + BlockPosition blockposition = new BlockPosition(d9, this.locY - 1.0D, d10); + + if (this.world.getType(blockposition).q() || this.world.getFluid(blockposition).a(TagsFluid.WATER)) { + d1 = d9; + d2 = this.locY + 1.0D; + d0 = d10; + } + } else if (this.world.getCubes(this, axisalignedbb1.d(0.0D, 1.0D, 0.0D)) && this.world.getType(new BlockPosition(d9, this.locY + 1.0D, d10)).q()) { + d1 = d9; + d2 = this.locY + 2.0D; + d0 = d10; + } + } + } + + this.enderTeleportTo(d1, d2, d0); + } else { + double d11 = (double) (this.width / 2.0F + entity.width / 2.0F) + 0.4D; + float f; + + if (entity instanceof EntityBoat) { + f = 0.0F; + } else { + f = 1.5707964F * (float) (this.getMainHand() == EnumMainHand.RIGHT ? -1 : 1); + } + + float f1 = -MathHelper.sin(-this.yaw * 0.017453292F - 3.1415927F + f); + float f2 = -MathHelper.cos(-this.yaw * 0.017453292F - 3.1415927F + f); + + d0 = Math.abs(f1) > Math.abs(f2) ? d11 / (double) Math.abs(f1) : d11 / (double) Math.abs(f2); + double d12 = this.locX + (double) f1 * d0; + double d13 = this.locZ + (double) f2 * d0; + + this.setPosition(d12, entity.locY + (double) entity.length + 0.001D, d13); + if (!this.world.getCubes(this, this.getBoundingBox().b(entity.getBoundingBox()))) { + this.setPosition(d12, entity.locY + (double) entity.length + 1.001D, d13); + if (!this.world.getCubes(this, this.getBoundingBox().b(entity.getBoundingBox()))) { + this.setPosition(entity.locX, entity.locY + (double) this.length + 0.001D, entity.locZ); + } + } + } + } + + protected float cG() { + return 0.42F; + } + + protected void cH() { + this.motY = (double) this.cG(); + if (this.hasEffect(MobEffects.JUMP)) { + this.motY += (double) ((float) (this.getEffect(MobEffects.JUMP).getAmplifier() + 1) * 0.1F); + } + + if (this.isSprinting()) { + float f = this.yaw * 0.017453292F; + + this.motX -= (double) (MathHelper.sin(f) * 0.2F); + this.motZ += (double) (MathHelper.cos(f) * 0.2F); + } + + this.impulse = true; + } + + protected void c(Tag tag) { + this.motY += 0.03999999910593033D; + } + + protected float cJ() { + return 0.8F; + } + + public void a(float f, float f1, float f2) { + double d0; + double d1; + float f3; + double d2; + + if (this.cP() || this.bT()) { + d0 = 0.08D; + if (this.motY <= 0.0D && this.hasEffect(MobEffects.SLOW_FALLING)) { + d0 = 0.01D; + this.fallDistance = 0.0F; + } + + float f4; + + if (this.isInWater() && (!(this instanceof EntityHuman) || !((EntityHuman) this).abilities.isFlying)) { + d1 = this.locY; + float f5 = this.isSprinting() ? 0.9F : this.cJ(); + + f4 = 0.02F; + f3 = (float) EnchantmentManager.e(this); + if (f3 > 3.0F) { + f3 = 3.0F; + } + + if (!this.onGround) { + f3 *= 0.5F; + } + + if (f3 > 0.0F) { + f5 += (0.54600006F - f5) * f3 / 3.0F; + f4 += (this.cK() - f4) * f3 / 3.0F; + } + + if (this.hasEffect(MobEffects.DOLPHINS_GRACE)) { + f5 = 0.96F; + } + + this.a(f, f1, f2, f4); + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + this.motX *= (double) f5; + this.motY *= 0.800000011920929D; + this.motZ *= (double) f5; + if (!this.isNoGravity() && !this.isSprinting()) { + if (this.motY <= 0.0D && Math.abs(this.motY - 0.005D) >= 0.003D && Math.abs(this.motY - d0 / 16.0D) < 0.003D) { + this.motY = -0.003D; + } else { + this.motY -= d0 / 16.0D; + } + } + + if (this.positionChanged && this.c(this.motX, this.motY + 0.6000000238418579D - this.locY + d1, this.motZ)) { + this.motY = 0.30000001192092896D; + } + } else if (this.ax() && (!(this instanceof EntityHuman) || !((EntityHuman) this).abilities.isFlying)) { + d1 = this.locY; + this.a(f, f1, f2, 0.02F); + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + this.motX *= 0.5D; + this.motY *= 0.5D; + this.motZ *= 0.5D; + if (!this.isNoGravity()) { + this.motY -= d0 / 4.0D; + } + + if (this.positionChanged && this.c(this.motX, this.motY + 0.6000000238418579D - this.locY + d1, this.motZ)) { + this.motY = 0.30000001192092896D; + } + } else if (this.dc()) { + if (this.motY > -0.5D) { + this.fallDistance = 1.0F; + } + + Vec3D vec3d = this.aN(); + float f6 = this.pitch * 0.017453292F; + + d2 = Math.sqrt(vec3d.x * vec3d.x + vec3d.z * vec3d.z); + double d3 = Math.sqrt(this.motX * this.motX + this.motZ * this.motZ); + double d4 = vec3d.b(); + float f7 = MathHelper.cos(f6); + + f7 = (float) ((double) f7 * (double) f7 * Math.min(1.0D, d4 / 0.4D)); + this.motY += d0 * (-1.0D + (double) f7 * 0.75D); + double d5; + + if (this.motY < 0.0D && d2 > 0.0D) { + d5 = this.motY * -0.1D * (double) f7; + this.motY += d5; + this.motX += vec3d.x * d5 / d2; + this.motZ += vec3d.z * d5 / d2; + } + + if (f6 < 0.0F && d2 > 0.0D) { + d5 = d3 * (double) (-MathHelper.sin(f6)) * 0.04D; + this.motY += d5 * 3.2D; + this.motX -= vec3d.x * d5 / d2; + this.motZ -= vec3d.z * d5 / d2; + } + + if (d2 > 0.0D) { + this.motX += (vec3d.x / d2 * d3 - this.motX) * 0.1D; + this.motZ += (vec3d.z / d2 * d3 - this.motZ) * 0.1D; + } + + this.motX *= 0.9900000095367432D; + this.motY *= 0.9800000190734863D; + this.motZ *= 0.9900000095367432D; + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + if (this.positionChanged && !this.world.isClientSide) { + d5 = Math.sqrt(this.motX * this.motX + this.motZ * this.motZ); + double d6 = d3 - d5; + float f8 = (float) (d6 * 10.0D - 3.0D); + + if (f8 > 0.0F) { + this.a(this.m((int) f8), 1.0F, 1.0F); + this.damageEntity(DamageSource.FLY_INTO_WALL, f8); + } + } + + if (this.onGround && !this.world.isClientSide) { + if (getFlag(7) && !CraftEventFactory.callToggleGlideEvent(this, false).isCancelled()) // CraftBukkit + this.setFlag(7, false); + } + } else { + float f9 = 0.91F; + BlockPosition.b blockposition_b = BlockPosition.b.d(this.locX, this.getBoundingBox().minY - 1.0D, this.locZ); + Throwable throwable = null; + + try { + if (this.onGround) { + f9 = this.world.getType(blockposition_b).getBlock().n() * 0.91F; + } + + f4 = 0.16277137F / (f9 * f9 * f9); + if (this.onGround) { + f3 = this.cK() * f4; + } else { + f3 = this.aU; + } + + this.a(f, f1, f2, f3); + f9 = 0.91F; + if (this.onGround) { + f9 = this.world.getType(blockposition_b.c(this.locX, this.getBoundingBox().minY - 1.0D, this.locZ)).getBlock().n() * 0.91F; + } + + if (this.z_()) { + float f10 = 0.15F; + + this.motX = MathHelper.a(this.motX, -0.15000000596046448D, 0.15000000596046448D); + this.motZ = MathHelper.a(this.motZ, -0.15000000596046448D, 0.15000000596046448D); + this.fallDistance = 0.0F; + if (this.motY < -0.15D) { + this.motY = -0.15D; + } + + boolean flag = this.isSneaking() && this instanceof EntityHuman; + + if (flag && this.motY < 0.0D) { + this.motY = 0.0D; + } + } + + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + if (this.positionChanged && this.z_()) { + this.motY = 0.2D; + } + + if (this.hasEffect(MobEffects.LEVITATION)) { + this.motY += (0.05D * (double) (this.getEffect(MobEffects.LEVITATION).getAmplifier() + 1) - this.motY) * 0.2D; + this.fallDistance = 0.0F; + } else { + blockposition_b.c(this.locX, 0.0D, this.locZ); + if (this.world.isClientSide && (!this.world.isLoaded(blockposition_b) || !this.world.getChunkAtWorldCoords(blockposition_b).y())) { + if (this.locY > 0.0D) { + this.motY = -0.1D; + } else { + this.motY = 0.0D; + } + } else if (!this.isNoGravity()) { + this.motY -= d0; + } + } + + this.motY *= 0.9800000190734863D; + this.motX *= (double) f9; + this.motZ *= (double) f9; + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + } + } + + this.aI = this.aJ; + d0 = this.locX - this.lastX; + d1 = this.locZ - this.lastZ; + d2 = this instanceof EntityBird ? this.locY - this.lastY : 0.0D; + f3 = MathHelper.sqrt(d0 * d0 + d2 * d2 + d1 * d1) * 4.0F; + if (f3 > 1.0F) { + f3 = 1.0F; + } + + this.aJ += (f3 - this.aJ) * 0.4F; + this.aK += this.aJ; + } + + public float cK() { + return this.bI; + } + + public void o(float f) { + this.bI = f; + } + + public boolean B(Entity entity) { + this.z(entity); + return false; + } + + public boolean isSleeping() { + return false; + } + + public void tick() { + super.tick(); + this.cV(); + this.o(); + if (!this.world.isClientSide) { + int i = this.getArrowCount(); + + if (i > 0) { + if (this.aA <= 0) { + this.aA = 20 * (30 - i); + } + + --this.aA; + if (this.aA <= 0) { + this.setArrowCount(i - 1); + } + } + + updateEntityEquipment(); // Paper - split into own method + + if (this.ticksLived % 20 == 0) { + this.getCombatTracker().g(); + } + + if (!this.glowing) { + boolean flag = this.hasEffect(MobEffects.GLOWING); + + if (this.getFlag(6) != flag) { + this.setFlag(6, flag); + } + } + } + + this.movementTick(); + double d0 = this.locX - this.lastX; + double d1 = this.locZ - this.lastZ; + float f = (float) (d0 * d0 + d1 * d1); + float f1 = this.aQ; + float f2 = 0.0F; + + this.aZ = this.ba; + float f3 = 0.0F; + + if (f > 0.0025000002F) { + f3 = 1.0F; + f2 = (float) Math.sqrt((double) f) * 3.0F; + float f4 = (float) MathHelper.c(d1, d0) * 57.295776F - 90.0F; + float f5 = MathHelper.e(MathHelper.g(this.yaw) - f4); + + if (95.0F < f5 && f5 < 265.0F) { + f1 = f4 - 180.0F; + } else { + f1 = f4; + } + } + + if (this.aG > 0.0F) { + f1 = this.yaw; + } + + if (!this.onGround) { + f3 = 0.0F; + } + + this.ba += (f3 - this.ba) * 0.3F; + this.world.methodProfiler.enter("headTurn"); + f2 = this.e(f1, f2); + this.world.methodProfiler.exit(); + this.world.methodProfiler.enter("rangeChecks"); + + while (this.yaw - this.lastYaw < -180.0F) { + this.lastYaw -= 360.0F; + } + + while (this.yaw - this.lastYaw >= 180.0F) { + this.lastYaw += 360.0F; + } + + while (this.aQ - this.aR < -180.0F) { + this.aR -= 360.0F; + } + + while (this.aQ - this.aR >= 180.0F) { + this.aR += 360.0F; + } + + while (this.pitch - this.lastPitch < -180.0F) { + this.lastPitch -= 360.0F; + } + + while (this.pitch - this.lastPitch >= 180.0F) { + this.lastPitch += 360.0F; + } + + while (this.aS - this.aT < -180.0F) { + this.aT -= 360.0F; + } + + while (this.aS - this.aT >= 180.0F) { + this.aT += 360.0F; + } + + this.world.methodProfiler.exit(); + this.bb += f2; + if (this.dc()) { + ++this.bv; + } else { + this.bv = 0; + } + } + + // Paper start - split into own method from above + public void updateEntityEquipment() { + EnumItemSlot[] aenumitemslot = EnumItemSlot.values(); + int j = aenumitemslot.length; + + for (int k = 0; k < j; ++k) { + EnumItemSlot enumitemslot = aenumitemslot[k]; + ItemStack itemstack; + + switch (enumitemslot.a()) { + case HAND: + itemstack = (ItemStack) this.bB.get(enumitemslot.b()); + break; + case ARMOR: + itemstack = (ItemStack) this.bC.get(enumitemslot.b()); + break; + default: + continue; + } + + ItemStack itemstack1 = this.getEquipment(enumitemslot); + + if (!ItemStack.matches(itemstack1, itemstack)) { + // Paper start - PlayerArmorChangeEvent + if (this instanceof EntityPlayer && enumitemslot.getType() == EnumItemSlot.Function.ARMOR) { + final org.bukkit.inventory.ItemStack oldItem = CraftItemStack.asBukkitCopy(itemstack); + final org.bukkit.inventory.ItemStack newItem = CraftItemStack.asBukkitCopy(itemstack1); + new PlayerArmorChangeEvent((Player) this.getBukkitEntity(), PlayerArmorChangeEvent.SlotType.valueOf(enumitemslot.name()), oldItem, newItem).callEvent(); + } + // Paper end + ((WorldServer) this.world).getTracker().a((Entity) this, (Packet) (new PacketPlayOutEntityEquipment(this.getId(), enumitemslot, itemstack1))); + if (!itemstack.isEmpty()) { + this.getAttributeMap().a(itemstack.a(enumitemslot)); + } + + if (!itemstack1.isEmpty()) { + this.getAttributeMap().b(itemstack1.a(enumitemslot)); + } + + switch (enumitemslot.a()) { + case HAND: + this.bB.set(enumitemslot.b(), itemstack1.isEmpty() ? ItemStack.a : itemstack1.cloneItemStack()); + break; + case ARMOR: + this.bC.set(enumitemslot.b(), itemstack1.isEmpty() ? ItemStack.a : itemstack1.cloneItemStack()); + } + } + } + } + // Paper end + + protected float e(float f, float f1) { + float f2 = MathHelper.g(f - this.aQ); + + this.aQ += f2 * 0.3F; + float f3 = MathHelper.g(this.yaw - this.aQ); + boolean flag = f3 < -90.0F || f3 >= 90.0F; + + if (f3 < -75.0F) { + f3 = -75.0F; + } + + if (f3 >= 75.0F) { + f3 = 75.0F; + } + + this.aQ = this.yaw - f3; + if (f3 * f3 > 2500.0F) { + this.aQ += f3 * 0.2F; + } + + if (flag) { + f1 *= -1.0F; + } + + return f1; + } + + public void movementTick() { + if (this.bJ > 0) { + --this.bJ; + } + + if (this.bl > 0 && !this.bT()) { + double d0 = this.locX + (this.bm - this.locX) / (double) this.bl; + double d1 = this.locY + (this.bn - this.locY) / (double) this.bl; + double d2 = this.locZ + (this.bo - this.locZ) / (double) this.bl; + double d3 = MathHelper.g(this.bp - (double) this.yaw); + + this.yaw = (float) ((double) this.yaw + d3 / (double) this.bl); + this.pitch = (float) ((double) this.pitch + (this.bq - (double) this.pitch) / (double) this.bl); + --this.bl; + this.setPosition(d0, d1, d2); + this.setYawPitch(this.yaw, this.pitch); + } else if (!this.cP()) { + this.motX *= 0.98D; + this.motY *= 0.98D; + this.motZ *= 0.98D; + } + + if (this.bs > 0) { + this.aS = (float) ((double) this.aS + MathHelper.g(this.br - (double) this.aS) / (double) this.bs); + --this.bs; + } + + if (Math.abs(this.motX) < 0.003D) { + this.motX = 0.0D; + } + + if (Math.abs(this.motY) < 0.003D) { + this.motY = 0.0D; + } + + if (Math.abs(this.motZ) < 0.003D) { + this.motZ = 0.0D; + } + + this.world.methodProfiler.enter("ai"); + if (this.isFrozen()) { + this.bg = false; + this.bh = 0.0F; + this.bj = 0.0F; + this.bk = 0.0F; + } else if (this.cP()) { + this.world.methodProfiler.enter("newAi"); + this.doTick(); + this.world.methodProfiler.exit(); + } + + this.world.methodProfiler.exit(); + this.world.methodProfiler.enter("jump"); + if (this.bg) { + if (this.W > 0.0D && (!this.onGround || this.W > 0.4D)) { + this.c(TagsFluid.WATER); + } else if (this.ax()) { + this.c(TagsFluid.LAVA); + } else if ((this.onGround || this.W > 0.0D && this.W <= 0.4D) && this.bJ == 0) { + this.cH(); + this.bJ = 10; + } + } else { + this.bJ = 0; + } + + this.world.methodProfiler.exit(); + this.world.methodProfiler.enter("travel"); + this.bh *= 0.98F; + this.bj *= 0.98F; + this.bk *= 0.9F; + this.n(); + AxisAlignedBB axisalignedbb = this.getBoundingBox(); + + this.a(this.bh, this.bi, this.bj); + this.world.methodProfiler.exit(); + this.world.methodProfiler.enter("push"); + if (this.bw > 0) { + --this.bw; + this.a(axisalignedbb, this.getBoundingBox()); + } + + this.cN(); + this.world.methodProfiler.exit(); + } + + private void n() { + boolean flag = this.getFlag(7); + + if (flag && !this.onGround && !this.isPassenger()) { + ItemStack itemstack = this.getEquipment(EnumItemSlot.CHEST); + + if (itemstack.getItem() == Items.ELYTRA && ItemElytra.e(itemstack)) { + flag = true; + if (!this.world.isClientSide && (this.bv + 1) % 20 == 0) { + itemstack.damage(1, this); + } + } else { + flag = false; + } + } else { + flag = false; + } + + if (!this.world.isClientSide) { + if (flag != this.getFlag(7) && !CraftEventFactory.callToggleGlideEvent(this, flag).isCancelled()) // CraftBukkit + this.setFlag(7, flag); + } + + } + + protected void doTick() {} + + protected void cN() { + List list = this.world.getEntities(this, this.getBoundingBox(), IEntitySelector.a(this)); + + if (!list.isEmpty()) { + int i = this.world.getGameRules().c("maxEntityCramming"); + int j; + + if (i > 0 && list.size() > i - 1 && this.random.nextInt(4) == 0) { + j = 0; + + for (int k = 0; k < list.size(); ++k) { + if (!((Entity) list.get(k)).isPassenger()) { + ++j; + } + } + + if (j > i - 1) { + this.damageEntity(DamageSource.CRAMMING, 6.0F); + } + } + + numCollisions = Math.max(0, numCollisions - world.paperConfig.maxCollisionsPerEntity); // Paper + for (j = 0; j < list.size() && numCollisions < world.paperConfig.maxCollisionsPerEntity; ++j) { // Paper + Entity entity = (Entity) list.get(j); + entity.numCollisions++; // Paper + numCollisions++; // Paper + + this.C(entity); + } + } + + } + + protected void a(AxisAlignedBB axisalignedbb, AxisAlignedBB axisalignedbb1) { + AxisAlignedBB axisalignedbb2 = axisalignedbb.b(axisalignedbb1); + List list = this.world.getEntities(this, axisalignedbb2); + + if (!list.isEmpty()) { + for (int i = 0; i < list.size(); ++i) { + Entity entity = (Entity) list.get(i); + + if (entity instanceof EntityLiving) { + this.d((EntityLiving) entity); + this.bw = 0; + this.motX *= -0.2D; + this.motY *= -0.2D; + this.motZ *= -0.2D; + break; + } + } + } else if (this.positionChanged) { + this.bw = 0; + } + + if (!this.world.isClientSide && this.bw <= 0) { + this.c(4, false); + } + + } + + protected void C(Entity entity) { + entity.collide(this); + } + + protected void d(EntityLiving entityliving) {} + + public void o(int i) { + this.bw = i; + if (!this.world.isClientSide) { + this.c(4, true); + } + + } + + public boolean isRiptiding() { + return ((Byte) this.datawatcher.get(EntityLiving.aw) & 4) != 0; + } + + // Paper start + public void stopRiding() { stopRiding(false); } + public void stopRiding(boolean suppressCancellation) { + // Paper end + Entity entity = this.getVehicle(); + + super.stopRiding(suppressCancellation); // Paper - suppress + if (entity != null && entity != this.getVehicle() && !this.world.isClientSide) { + this.A(entity); + } + + } + + public void aH() { + super.aH(); + this.aZ = this.ba; + this.ba = 0.0F; + this.fallDistance = 0.0F; + } + + public void o(boolean flag) { + this.bg = flag; + } + + public void receive(Entity entity, int i) { + if (!entity.dead && !this.world.isClientSide) { + EntityTracker entitytracker = ((WorldServer) this.world).getTracker(); + + if (entity instanceof EntityItem || entity instanceof EntityArrow || entity instanceof EntityExperienceOrb) { + entitytracker.a(entity, (Packet) (new PacketPlayOutCollect(entity.getId(), this.getId(), i))); + } + } + + } + + public boolean hasLineOfSight(Entity entity) { + return this.world.rayTrace(new Vec3D(this.locX, this.locY + (double) this.getHeadHeight(), this.locZ), new Vec3D(entity.locX, entity.locY + (double) entity.getHeadHeight(), entity.locZ), FluidCollisionOption.NEVER, true, false) == null; + } + + public float h(float f) { + return f == 1.0F ? this.aS : this.aT + (this.aS - this.aT) * f; + } + + public boolean cP() { + return !this.world.isClientSide; + } + + public boolean isInteractable() { + return !this.dead && this.collides; // CraftBukkit + } + + public boolean isCollidable() { + return this.isAlive() && !this.z_() && this.collides; // CraftBukkit + } + + protected void aA() { + this.velocityChanged = this.random.nextDouble() >= this.getAttributeInstance(GenericAttributes.c).getValue(); + } + + public float getHeadRotation() { + return this.aS; + } + + public void setHeadRotation(float f) { + this.aS = f; + } + + public void k(float f) { + this.aQ = f; + } + + public float getAbsorptionHearts() { + return this.bK; + } + + public void setAbsorptionHearts(float f) { + if (f < 0.0F || Float.isNaN(f)) { // Paper + f = 0.0F; + } + + this.bK = f; + } + + public void enterCombat() {} + + public void exitCombat() {} + + protected void cR() { + this.updateEffects = true; + } + + public abstract EnumMainHand getMainHand(); + + public boolean isHandRaised() { + return ((Byte) this.datawatcher.get(EntityLiving.aw) & 1) > 0; + } + + public EnumHand cU() { + return ((Byte) this.datawatcher.get(EntityLiving.aw) & 2) > 0 ? EnumHand.OFF_HAND : EnumHand.MAIN_HAND; + } + + protected void cV() { + if (this.isHandRaised()) { + if (this.b(this.cU()) == this.activeItem) { + if (this.cX() <= 25 && this.cX() % 4 == 0) { + this.b(this.activeItem, 5); + } + + if (--this.bu == 0 && !this.world.isClientSide) { + this.q(); + } + } else { + this.da(); + } + } + + } + + private void o() { + this.bP = this.bO; + if (this.isSwimming()) { + this.bO = Math.min(1.0F, this.bO + 0.09F); + } else { + this.bO = Math.max(0.0F, this.bO - 0.09F); + } + + } + + protected void c(int i, boolean flag) { + byte b0 = (Byte) this.datawatcher.get(EntityLiving.aw); + int j; + + if (flag) { + j = b0 | i; + } else { + j = b0 & ~i; + } + + this.datawatcher.set(EntityLiving.aw, (byte) j); + } + + public void c(EnumHand enumhand) { + ItemStack itemstack = this.b(enumhand); + + if (!itemstack.isEmpty() && !this.isHandRaised()) { + this.activeItem = itemstack; + this.bu = itemstack.k(); + if (!this.world.isClientSide) { + this.c(1, true); + this.c(2, enumhand == EnumHand.OFF_HAND); + } + + } + } + + public void a(DataWatcherObject datawatcherobject) { + super.a(datawatcherobject); + if (EntityLiving.aw.equals(datawatcherobject) && this.world.isClientSide) { + if (this.isHandRaised() && this.activeItem.isEmpty()) { + this.activeItem = this.b(this.cU()); + if (!this.activeItem.isEmpty()) { + this.bu = this.activeItem.k(); + } + } else if (!this.isHandRaised() && !this.activeItem.isEmpty()) { + this.activeItem = ItemStack.a; + this.bu = 0; + } + } + + } + + public void a(ArgumentAnchor.Anchor argumentanchor_anchor, Vec3D vec3d) { + super.a(argumentanchor_anchor, vec3d); + this.aT = this.aS; + this.aQ = this.aS; + this.aR = this.aQ; + } + + protected void b(ItemStack itemstack, int i) { + if (!itemstack.isEmpty() && this.isHandRaised()) { + if (itemstack.l() == EnumAnimation.DRINK) { + this.a(SoundEffects.ENTITY_GENERIC_DRINK, 0.5F, this.world.random.nextFloat() * 0.1F + 0.9F); + } + + if (itemstack.l() == EnumAnimation.EAT) { + this.a(itemstack, i); + this.a(SoundEffects.ENTITY_GENERIC_EAT, 0.5F + 0.5F * (float) this.random.nextInt(2), (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F); + } + + } + } + + private void a(ItemStack itemstack, int i) { + for (int j = 0; j < i; ++j) { + Vec3D vec3d = new Vec3D(((double) this.random.nextFloat() - 0.5D) * 0.1D, Math.random() * 0.1D + 0.1D, 0.0D); + + vec3d = vec3d.a(-this.pitch * 0.017453292F); + vec3d = vec3d.b(-this.yaw * 0.017453292F); + double d0 = (double) (-this.random.nextFloat()) * 0.6D - 0.3D; + Vec3D vec3d1 = new Vec3D(((double) this.random.nextFloat() - 0.5D) * 0.3D, d0, 0.6D); + + vec3d1 = vec3d1.a(-this.pitch * 0.017453292F); + vec3d1 = vec3d1.b(-this.yaw * 0.017453292F); + vec3d1 = vec3d1.add(this.locX, this.locY + (double) this.getHeadHeight(), this.locZ); + this.world.addParticle(new ParticleParamItem(Particles.C, itemstack), vec3d1.x, vec3d1.y, vec3d1.z, vec3d.x, vec3d.y + 0.05D, vec3d.z); + } + + } + + protected void q() { + if (!this.activeItem.isEmpty() && this.isHandRaised()) { + PlayerItemConsumeEvent event = null; // Paper + this.b(this.activeItem, 16); + // CraftBukkit start - fire PlayerItemConsumeEvent + ItemStack itemstack; + if (this instanceof EntityPlayer) { + org.bukkit.inventory.ItemStack craftItem = CraftItemStack.asBukkitCopy(this.activeItem); + event = new PlayerItemConsumeEvent((Player) this.getBukkitEntity(), craftItem); // Paper + world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + // Update client + ((EntityPlayer) this).getBukkitEntity().updateInventory(); + ((EntityPlayer) this).getBukkitEntity().updateScaledHealth(); + return; + } + + itemstack = (craftItem.equals(event.getItem())) ? this.activeItem.a(this.world, this) : CraftItemStack.asNMSCopy(event.getItem()).a(world, this); + } else { + itemstack = this.activeItem.a(this.world, this); + } + + // Paper start - save the default replacement item and change it if necessary + final ItemStack defaultReplacement = itemstack; + if (event != null && event.getReplacement() != null) { + itemstack = CraftItemStack.asNMSCopy(event.getReplacement()); + } + // Paper end + this.a(this.cU(), itemstack); + // CraftBukkit end + this.da(); + // Paper start - if the replacement is anything but the default, update the client inventory + if (this instanceof EntityPlayer && !com.google.common.base.Objects.equal(defaultReplacement, itemstack)) { + ((EntityPlayer) this).getBukkitEntity().updateInventory(); + } + // Paper end + } + + } + + public ItemStack cW() { + return this.activeItem; + } + + public int getItemUseRemainingTime() { return cX(); } // Paper - OBFHELPER + public int cX() { + return this.bu; + } + + public int getHandRaisedTime() { return cY(); } // Paper - OBFHELPER + public int cY() { + return this.isHandRaised() ? this.activeItem.k() - this.cX() : 0; + } + + public void clearActiveItem() { + if (!this.activeItem.isEmpty()) { + this.activeItem.a(this.world, this, this.cX()); + } + + this.da(); + } + + public void da() { + if (!this.world.isClientSide) { + this.c(1, false); + } + + this.activeItem = ItemStack.a; + this.bu = 0; + } + + public boolean isBlocking() { + if (this.isHandRaised() && !this.activeItem.isEmpty()) { + Item item = this.activeItem.getItem(); + + return item.d(this.activeItem) != EnumAnimation.BLOCK ? false : item.c(this.activeItem) - this.bu >= getShieldBlockingDelay(); // Paper - shieldBlockingDelay + } else { + return false; + } + } + + public boolean dc() { + return this.getFlag(7); + } + + public boolean j(double d0, double d1, double d2) { + double d3 = this.locX; + double d4 = this.locY; + double d5 = this.locZ; + + this.locX = d0; + this.locY = d1; + this.locZ = d2; + boolean flag = false; + BlockPosition blockposition = new BlockPosition(this); + World world = this.world; + Random random = this.getRandom(); + boolean flag1; + + if (world.isLoaded(blockposition)) { + flag1 = false; + + while (!flag1 && blockposition.getY() > 0) { + BlockPosition blockposition1 = blockposition.down(); + IBlockData iblockdata = world.getType(blockposition1); + + if (iblockdata.getMaterial().isSolid()) { + flag1 = true; + } else { + --this.locY; + blockposition = blockposition1; + } + } + + if (flag1) { + // CraftBukkit start - Teleport event + // this.enderTeleportTo(this.locX, this.locY, this.locZ); + EntityTeleportEvent teleport = new EntityTeleportEvent(this.getBukkitEntity(), new Location(this.world.getWorld(), d3, d4, d5), new Location(this.world.getWorld(), this.locX, this.locY, this.locZ)); + this.world.getServer().getPluginManager().callEvent(teleport); + if (!teleport.isCancelled()) { + Location to = teleport.getTo(); + this.enderTeleportTo(to.getX(), to.getY(), to.getZ()); + if (world.getCubes((Entity) this, this.getBoundingBox()) && !world.containsLiquid(this.getBoundingBox())) { + flag = true; + } + } + // CraftBukkit end + } + } + + if (!flag) { + this.enderTeleportTo(d3, d4, d5); + return false; + } else { + flag1 = true; + + for (int i = 0; i < 128; ++i) { + double d6 = (double) i / 127.0D; + float f = (random.nextFloat() - 0.5F) * 0.2F; + float f1 = (random.nextFloat() - 0.5F) * 0.2F; + float f2 = (random.nextFloat() - 0.5F) * 0.2F; + double d7 = d3 + (this.locX - d3) * d6 + (random.nextDouble() - 0.5D) * (double) this.width * 2.0D; + double d8 = d4 + (this.locY - d4) * d6 + random.nextDouble() * (double) this.length; + double d9 = d5 + (this.locZ - d5) * d6 + (random.nextDouble() - 0.5D) * (double) this.width * 2.0D; + + world.addParticle(Particles.K, d7, d8, d9, (double) f, (double) f1, (double) f2); + } + + if (this instanceof EntityCreature) { + ((EntityCreature) this).getNavigation().q(); + } + + return true; + } + } + + public boolean de() { + return true; + } + + public boolean df() { + return true; + } + + // Paper start + public MovingObjectPosition getRayTrace(int maxDistance) { + return getRayTrace(maxDistance, FluidCollisionOption.NEVER); + } + + public MovingObjectPosition getRayTrace(int maxDistance, FluidCollisionOption fluidCollisionOption) { + if (maxDistance < 1 || maxDistance > 120) { + throw new IllegalArgumentException("maxDistance must be between 1-120"); + } + + Vec3D start = new Vec3D(locX, locY + getHeadHeight(), locZ); + org.bukkit.util.Vector dir = getBukkitEntity().getLocation().getDirection().multiply(maxDistance); + Vec3D end = new Vec3D(start.x + dir.getX(), start.y + dir.getY(), start.z + dir.getZ()); + + return world.rayTrace(start, end, fluidCollisionOption); + } + + public MovingObjectPosition getTargetEntity(int maxDistance) { + if (maxDistance < 1 || maxDistance > 120) { + throw new IllegalArgumentException("maxDistance must be between 1-120"); + } + + Vec3D start = getEyePosition(1.0F); + Vec3D direction = getLookVec(); + Vec3D end = start.add(direction.x * maxDistance, direction.y * maxDistance, direction.z * maxDistance); + + List entityList = world.getEntities(this, getBoundingBox().expand(direction.x * maxDistance, direction.y * maxDistance, direction.z * maxDistance).grow(1.0D, 1.0D, 1.0D), IEntitySelector.notSpectator().and(Entity::isInteractable)); + + double distance = 0.0D; + MovingObjectPosition rayTraceResult = null; + + for (Entity entity : entityList) { + AxisAlignedBB aabb = entity.getBoundingBox().grow((double) entity.getCollisionBorderSize()); + MovingObjectPosition rayTrace = aabb.calculateIntercept(start, end); + + if (rayTrace != null) { + double distanceTo = start.distanceSquared(rayTrace.pos); + if (distanceTo < distance || distance == 0.0D) { + rayTraceResult = new MovingObjectPosition(entity, rayTrace.pos); + distance = distanceTo; + } + } + } + + return rayTraceResult; + } + + public int shieldBlockingDelay = world.paperConfig.shieldBlockingDelay; + + public int getShieldBlockingDelay() { + return shieldBlockingDelay; + } + + public void setShieldBlockingDelay(int shieldBlockingDelay) { + this.shieldBlockingDelay = shieldBlockingDelay; + } + // Paper end +} diff --git a/src/main/java/net/minecraft/server/EntityLlama.java b/src/main/java/net/minecraft/server/EntityLlama.java new file mode 100644 index 000000000000..82a32c61ed41 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityLlama.java @@ -0,0 +1,445 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public class EntityLlama extends EntityHorseChestedAbstract implements IRangedEntity { + + private static final DataWatcherObject bM = DataWatcher.a(EntityLlama.class, DataWatcherRegistry.b); + private static final DataWatcherObject bN = DataWatcher.a(EntityLlama.class, DataWatcherRegistry.b); + private static final DataWatcherObject bO = DataWatcher.a(EntityLlama.class, DataWatcherRegistry.b); + private boolean bP; + @Nullable + private EntityLlama bQ; + @Nullable + private EntityLlama bR; + + public EntityLlama(World world) { + super(EntityTypes.LLAMA, world); + this.setSize(0.9F, 1.87F); + } + + public void setStrength(int i) { + this.datawatcher.set(EntityLlama.bM, Math.max(1, Math.min(5, i))); + } + + private void eo() { + int i = this.random.nextFloat() < 0.04F ? 5 : 3; + + this.setStrength(1 + this.random.nextInt(i)); + } + + public int getStrength() { + return (Integer) this.datawatcher.get(EntityLlama.bM); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("Variant", this.getVariant()); + nbttagcompound.setInt("Strength", this.getStrength()); + if (!this.inventoryChest.getItem(1).isEmpty()) { + nbttagcompound.set("DecorItem", this.inventoryChest.getItem(1).save(new NBTTagCompound())); + } + + } + + public void a(NBTTagCompound nbttagcompound) { + this.setStrength(nbttagcompound.getInt("Strength")); + super.a(nbttagcompound); + this.setVariant(nbttagcompound.getInt("Variant")); + if (nbttagcompound.hasKeyOfType("DecorItem", 10)) { + this.inventoryChest.setItem(1, ItemStack.a(nbttagcompound.getCompound("DecorItem"))); + } + + this.dS(); + } + + protected void n() { + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(1, new PathfinderGoalTame(this, 1.2D)); + this.goalSelector.a(2, new PathfinderGoalLlamaFollow(this, 2.0999999046325684D)); + this.goalSelector.a(3, new PathfinderGoalArrowAttack(this, 1.25D, 40, 20.0F)); + this.goalSelector.a(3, new PathfinderGoalPanic(this, 1.2D)); + this.goalSelector.a(4, new PathfinderGoalBreed(this, 1.0D)); + this.goalSelector.a(5, new PathfinderGoalFollowParent(this, 1.0D)); + this.goalSelector.a(6, new PathfinderGoalRandomStrollLand(this, 0.7D)); + this.goalSelector.a(7, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new EntityLlama.c(this)); + this.targetSelector.a(2, new EntityLlama.a(this)); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.FOLLOW_RANGE).setValue(40.0D); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityLlama.bM, 0); + this.datawatcher.register(EntityLlama.bN, -1); + this.datawatcher.register(EntityLlama.bO, 0); + } + + public int getVariant() { + return MathHelper.clamp((Integer) this.datawatcher.get(EntityLlama.bO), 0, 3); + } + + public void setVariant(int i) { + this.datawatcher.set(EntityLlama.bO, i); + } + + protected int dA() { + return this.isCarryingChest() ? 2 + 3 * this.dH() : super.dA(); + } + + public void k(Entity entity) { + if (this.w(entity)) { + float f = MathHelper.cos(this.aQ * 0.017453292F); + float f1 = MathHelper.sin(this.aQ * 0.017453292F); + float f2 = 0.3F; + + entity.setPosition(this.locX + (double) (0.3F * f1), this.locY + this.aJ() + entity.aI(), this.locZ - (double) (0.3F * f)); + } + } + + public double aJ() { + return (double) this.length * 0.67D; + } + + public boolean dh() { + return false; + } + + protected boolean b(EntityHuman entityhuman, ItemStack itemstack) { + byte b0 = 0; + byte b1 = 0; + float f = 0.0F; + boolean flag = false; + Item item = itemstack.getItem(); + + if (item == Items.WHEAT) { + b0 = 10; + b1 = 3; + f = 2.0F; + } else if (item == Blocks.HAY_BLOCK.getItem()) { + b0 = 90; + b1 = 6; + f = 10.0F; + if (this.isTamed() && this.getAge() == 0 && this.dD()) { + flag = true; + this.f(entityhuman); + } + } + + if (this.getHealth() < this.getMaxHealth() && f > 0.0F) { + this.heal(f); + flag = true; + } + + if (this.isBaby() && b0 > 0) { + this.world.addParticle(Particles.z, this.locX + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width, this.locY + 0.5D + (double) (this.random.nextFloat() * this.length), this.locZ + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width, 0.0D, 0.0D, 0.0D); + if (!this.world.isClientSide) { + this.setAge(b0); + } + + flag = true; + } + + if (b1 > 0 && (flag || !this.isTamed()) && this.getTemper() < this.getMaxDomestication()) { + flag = true; + if (!this.world.isClientSide) { + this.r(b1); + } + } + + if (flag && !this.isSilent()) { + this.world.a((EntityHuman) null, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_LLAMA_EAT, this.bV(), 1.0F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F); + } + + return flag; + } + + protected boolean isFrozen() { + return this.getHealth() <= 0.0F || this.dN(); + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + Object object = super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + + this.eo(); + int i; + + if (object instanceof EntityLlama.b) { + i = ((EntityLlama.b) object).a; + } else { + i = this.random.nextInt(4); + object = new EntityLlama.b(i); + } + + this.setVariant(i); + return (GroupDataEntity) object; + } + + protected SoundEffect dB() { + return SoundEffects.ENTITY_LLAMA_ANGRY; + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_LLAMA_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_LLAMA_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_LLAMA_DEATH; + } + + protected void a(BlockPosition blockposition, IBlockData iblockdata) { + this.a(SoundEffects.ENTITY_LLAMA_STEP, 0.15F, 1.0F); + } + + protected void dC() { + this.a(SoundEffects.ENTITY_LLAMA_CHEST, 1.0F, (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F); + } + + public void dZ() { + SoundEffect soundeffect = this.dB(); + + if (soundeffect != null) { + this.a(soundeffect, this.cD(), this.cE()); + } + + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.aD; + } + + public int dH() { + return this.getStrength(); + } + + public boolean ef() { + return true; + } + + public boolean g(ItemStack itemstack) { + Item item = itemstack.getItem(); + + return TagsItem.CARPETS.isTagged(item); + } + + public boolean dU() { + return false; + } + + public void a(IInventory iinventory) { + EnumColor enumcolor = this.ej(); + + super.a(iinventory); + EnumColor enumcolor1 = this.ej(); + + if (this.ticksLived > 20 && enumcolor1 != null && enumcolor1 != enumcolor) { + this.a(SoundEffects.ENTITY_LLAMA_SWAG, 0.5F, 1.0F); + } + + } + + protected void dS() { + if (!this.world.isClientSide) { + super.dS(); + this.a(h(this.inventoryChest.getItem(1))); + } + } + + private void a(@Nullable EnumColor enumcolor) { + this.datawatcher.set(EntityLlama.bN, enumcolor == null ? -1 : enumcolor.getColorIndex()); + } + + @Nullable + private static EnumColor h(ItemStack itemstack) { + Block block = Block.asBlock(itemstack.getItem()); + + return block instanceof BlockCarpet ? ((BlockCarpet) block).d() : null; + } + + @Nullable + public EnumColor ej() { + int i = (Integer) this.datawatcher.get(EntityLlama.bN); + + return i == -1 ? null : EnumColor.fromColorIndex(i); + } + + public int getMaxDomestication() { + return 30; + } + + public boolean mate(EntityAnimal entityanimal) { + return entityanimal != this && entityanimal instanceof EntityLlama && this.eb() && ((EntityLlama) entityanimal).eb(); + } + + public EntityLlama createChild(EntityAgeable entityageable) { + EntityLlama entityllama = EntityTypes.LLAMA.create(world); // Paper + + this.a(entityageable, (EntityHorseAbstract) entityllama); + EntityLlama entityllama1 = (EntityLlama) entityageable; + int i = this.random.nextInt(Math.max(this.getStrength(), entityllama1.getStrength())) + 1; + + if (this.random.nextFloat() < 0.03F) { + ++i; + } + + entityllama.setStrength(i); + entityllama.setVariant(this.random.nextBoolean() ? this.getVariant() : entityllama1.getVariant()); + return entityllama; + } + + private void f(EntityLiving entityliving) { + EntityLlamaSpit entityllamaspit = new EntityLlamaSpit(this.world, this); + double d0 = entityliving.locX - this.locX; + double d1 = entityliving.getBoundingBox().minY + (double) (entityliving.length / 3.0F) - entityllamaspit.locY; + double d2 = entityliving.locZ - this.locZ; + float f = MathHelper.sqrt(d0 * d0 + d2 * d2) * 0.2F; + + entityllamaspit.shoot(d0, d1 + (double) f, d2, 1.5F, 10.0F); + this.world.a((EntityHuman) null, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_LLAMA_SPIT, this.bV(), 1.0F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F); + this.world.addEntity(entityllamaspit); + this.bP = true; + } + + private void B(boolean flag) { + this.bP = flag; + } + + public void c(float f, float f1) { + int i = MathHelper.f((f * 0.5F - 3.0F) * f1); + + if (i > 0) { + if (f >= 6.0F) { + this.damageEntity(DamageSource.FALL, (float) i); + if (this.isVehicle()) { + Iterator iterator = this.getAllPassengers().iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + entity.damageEntity(DamageSource.FALL, (float) i); + } + } + } + + IBlockData iblockdata = this.world.getType(new BlockPosition(this.locX, this.locY - 0.2D - (double) this.lastYaw, this.locZ)); + Block block = iblockdata.getBlock(); + + if (!iblockdata.isAir() && !this.isSilent()) { + SoundEffectType soundeffecttype = block.getStepSound(); + + this.world.a((EntityHuman) null, this.locX, this.locY, this.locZ, soundeffecttype.d(), this.bV(), soundeffecttype.a() * 0.5F, soundeffecttype.b() * 0.75F); + } + + } + } + + public void ek() { + if (this.bQ != null) { + this.bQ.bR = null; + } + + this.bQ = null; + } + + public void a(EntityLlama entityllama) { + this.bQ = entityllama; + this.bQ.bR = this; + } + + public boolean el() { + return this.bR != null; + } + + public boolean inCaravan() { return this.em(); } // Paper - OBFHELPER + public boolean em() { + return this.bQ != null; + } + + @Nullable + public EntityLlama en() { + return this.bQ; + } + + protected double dx() { + return 2.0D; + } + + protected void dX() { + if (!this.em() && this.isBaby()) { + super.dX(); + } + + } + + public boolean dY() { + return false; + } + + public void a(EntityLiving entityliving, float f) { + this.f(entityliving); + } + + public void s(boolean flag) {} + + static class a extends PathfinderGoalNearestAttackableTarget { + + public a(EntityLlama entityllama) { + super(entityllama, EntityWolf.class, 16, false, true, (Predicate) null); + } + + public boolean a() { + if (super.a() && this.d != null && !((EntityWolf) this.d).isTamed()) { + return true; + } else { + this.e.setGoalTarget((EntityLiving) null); + return false; + } + } + + protected double i() { + return super.i() * 0.25D; + } + } + + static class c extends PathfinderGoalHurtByTarget { + + public c(EntityLlama entityllama) { + super(entityllama, false); + } + + public boolean b() { + if (this.e instanceof EntityLlama) { + EntityLlama entityllama = (EntityLlama) this.e; + + if (entityllama.bP) { + entityllama.B(false); + return false; + } + } + + return super.b(); + } + } + + static class b implements GroupDataEntity { + + public int a; + + private b(int i) { + this.a = i; + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityLlamaSpit.java b/src/main/java/net/minecraft/server/EntityLlamaSpit.java new file mode 100644 index 000000000000..58e1ed795487 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityLlamaSpit.java @@ -0,0 +1,195 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.UUID; +import javax.annotation.Nullable; + +public class EntityLlamaSpit extends Entity implements IProjectile { + + public EntityLiving shooter; // CraftBukkit - type + private NBTTagCompound b; + + public EntityLlamaSpit(World world) { + super(EntityTypes.LLAMA_SPIT, world); + this.setSize(0.25F, 0.25F); + } + + public EntityLlamaSpit(World world, EntityLlama entityllama) { + this(world); + this.shooter = entityllama; + this.setPosition(entityllama.locX - (double) (entityllama.width + 1.0F) * 0.5D * (double) MathHelper.sin(entityllama.aQ * 0.017453292F), entityllama.locY + (double) entityllama.getHeadHeight() - 0.10000000149011612D, entityllama.locZ + (double) (entityllama.width + 1.0F) * 0.5D * (double) MathHelper.cos(entityllama.aQ * 0.017453292F)); + } + + public void tick() { + super.tick(); + if (this.b != null) { + this.f(); + } + + Vec3D vec3d = new Vec3D(this.locX, this.locY, this.locZ); + Vec3D vec3d1 = new Vec3D(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + MovingObjectPosition movingobjectposition = this.world.rayTrace(vec3d, vec3d1); + + vec3d = new Vec3D(this.locX, this.locY, this.locZ); + vec3d1 = new Vec3D(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + if (movingobjectposition != null) { + vec3d1 = new Vec3D(movingobjectposition.pos.x, movingobjectposition.pos.y, movingobjectposition.pos.z); + } + + Entity entity = this.a(vec3d, vec3d1); + + if (entity != null) { + movingobjectposition = new MovingObjectPosition(entity); + } + + if (movingobjectposition != null) { + this.a(movingobjectposition); + } + + this.locX += this.motX; + this.locY += this.motY; + this.locZ += this.motZ; + float f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + this.yaw = (float) (MathHelper.c(this.motX, this.motZ) * 57.2957763671875D); + + for (this.pitch = (float) (MathHelper.c(this.motY, (double) f) * 57.2957763671875D); this.pitch - this.lastPitch < -180.0F; this.lastPitch -= 360.0F) { + ; + } + + while (this.pitch - this.lastPitch >= 180.0F) { + this.lastPitch += 360.0F; + } + + while (this.yaw - this.lastYaw < -180.0F) { + this.lastYaw -= 360.0F; + } + + while (this.yaw - this.lastYaw >= 180.0F) { + this.lastYaw += 360.0F; + } + + this.pitch = this.lastPitch + (this.pitch - this.lastPitch) * 0.2F; + this.yaw = this.lastYaw + (this.yaw - this.lastYaw) * 0.2F; + float f1 = 0.99F; + float f2 = 0.06F; + + if (!this.world.a(this.getBoundingBox(), Material.AIR)) { + this.die(); + } else if (this.aq()) { + this.die(); + } else { + this.motX *= 0.9900000095367432D; + this.motY *= 0.9900000095367432D; + this.motZ *= 0.9900000095367432D; + if (!this.isNoGravity()) { + this.motY -= 0.05999999865889549D; + } + + this.setPosition(this.locX, this.locY, this.locZ); + } + } + + @Nullable + private Entity a(Vec3D vec3d, Vec3D vec3d1) { + Entity entity = null; + List list = this.world.getEntities(this, this.getBoundingBox().b(this.motX, this.motY, this.motZ).g(1.0D)); + double d0 = 0.0D; + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + Entity entity1 = (Entity) iterator.next(); + + if (entity1 != this.shooter) { + AxisAlignedBB axisalignedbb = entity1.getBoundingBox().g(0.30000001192092896D); + MovingObjectPosition movingobjectposition = axisalignedbb.b(vec3d, vec3d1); + + if (movingobjectposition != null) { + double d1 = vec3d.distanceSquared(movingobjectposition.pos); + + if (d1 < d0 || d0 == 0.0D) { + entity = entity1; + d0 = d1; + } + } + } + } + + return entity; + } + + public void shoot(double d0, double d1, double d2, float f, float f1) { + float f2 = MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + + d0 /= (double) f2; + d1 /= (double) f2; + d2 /= (double) f2; + d0 += this.random.nextGaussian() * 0.007499999832361937D * (double) f1; + d1 += this.random.nextGaussian() * 0.007499999832361937D * (double) f1; + d2 += this.random.nextGaussian() * 0.007499999832361937D * (double) f1; + d0 *= (double) f; + d1 *= (double) f; + d2 *= (double) f; + this.motX = d0; + this.motY = d1; + this.motZ = d2; + float f3 = MathHelper.sqrt(d0 * d0 + d2 * d2); + + this.yaw = (float) (MathHelper.c(d0, d2) * 57.2957763671875D); + this.pitch = (float) (MathHelper.c(d1, (double) f3) * 57.2957763671875D); + this.lastYaw = this.yaw; + this.lastPitch = this.pitch; + } + + public void a(MovingObjectPosition movingobjectposition) { + org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this, movingobjectposition); // Craftbukkit - Call event + if (movingobjectposition.entity != null && this.shooter != null) { + movingobjectposition.entity.damageEntity(DamageSource.a(this, (EntityLiving) this.shooter).c(), 1.0F); + } + + if (!this.world.isClientSide) { + this.die(); + } + + } + + protected void x_() {} + + protected void a(NBTTagCompound nbttagcompound) { + if (nbttagcompound.hasKeyOfType("Owner", 10)) { + this.b = nbttagcompound.getCompound("Owner"); + } + + } + + protected void b(NBTTagCompound nbttagcompound) { + if (this.shooter != null) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + UUID uuid = this.shooter.getUniqueID(); + + nbttagcompound1.a("OwnerUUID", uuid); + nbttagcompound.set("Owner", nbttagcompound1); + } + + } + + private void f() { + if (this.b != null && this.b.b("OwnerUUID")) { + UUID uuid = this.b.a("OwnerUUID"); + List list = this.world.a(EntityLlama.class, this.getBoundingBox().g(15.0D)); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityLlama entityllama = (EntityLlama) iterator.next(); + + if (entityllama.getUniqueID().equals(uuid)) { + this.shooter = entityllama; + break; + } + } + } + + this.b = null; + } +} diff --git a/src/main/java/net/minecraft/server/EntityMinecartAbstract.java b/src/main/java/net/minecraft/server/EntityMinecartAbstract.java new file mode 100644 index 000000000000..41e8a4b507a7 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityMinecartAbstract.java @@ -0,0 +1,866 @@ +package net.minecraft.server; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.Location; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.vehicle.VehicleDamageEvent; +import org.bukkit.event.vehicle.VehicleDestroyEvent; +import org.bukkit.event.vehicle.VehicleEntityCollisionEvent; +import org.bukkit.util.Vector; +// CraftBukkit end + +public abstract class EntityMinecartAbstract extends Entity implements INamableTileEntity { + + private static final DataWatcherObject a = DataWatcher.a(EntityMinecartAbstract.class, DataWatcherRegistry.b); + private static final DataWatcherObject b = DataWatcher.a(EntityMinecartAbstract.class, DataWatcherRegistry.b); + private static final DataWatcherObject c = DataWatcher.a(EntityMinecartAbstract.class, DataWatcherRegistry.c); + private static final DataWatcherObject d = DataWatcher.a(EntityMinecartAbstract.class, DataWatcherRegistry.b); + private static final DataWatcherObject e = DataWatcher.a(EntityMinecartAbstract.class, DataWatcherRegistry.b); + private static final DataWatcherObject f = DataWatcher.a(EntityMinecartAbstract.class, DataWatcherRegistry.i); + private boolean g; + private static final int[][][] h = new int[][][] { { { 0, 0, -1}, { 0, 0, 1}}, { { -1, 0, 0}, { 1, 0, 0}}, { { -1, -1, 0}, { 1, 0, 0}}, { { -1, 0, 0}, { 1, -1, 0}}, { { 0, 0, -1}, { 0, -1, 1}}, { { 0, -1, -1}, { 0, 0, 1}}, { { 0, 0, 1}, { 1, 0, 0}}, { { 0, 0, 1}, { -1, 0, 0}}, { { 0, 0, -1}, { -1, 0, 0}}, { { 0, 0, -1}, { 1, 0, 0}}}; + private int aw; + private double ax; + private double ay; + private double az; + private double aA; + private double aB; + + // CraftBukkit start + public boolean slowWhenEmpty = true; + private double derailedX = 0.5; + private double derailedY = 0.5; + private double derailedZ = 0.5; + private double flyingX = 0.949999988079071D; // Paper - restore vanilla precision + private double flyingY = 0.949999988079071D; // Paper - restore vanilla precision + private double flyingZ = 0.949999988079071D; // Paper - restore vanilla precision + public double maxSpeed = 0.4D; + // CraftBukkit end + + protected EntityMinecartAbstract(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.j = true; + this.setSize(0.98F, 0.7F); + } + + protected EntityMinecartAbstract(EntityTypes entitytypes, World world, double d0, double d1, double d2) { + this(entitytypes, world); + this.setPosition(d0, d1, d2); + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + this.lastX = d0; + this.lastY = d1; + this.lastZ = d2; + } + + public static EntityMinecartAbstract a(World world, double d0, double d1, double d2, EntityMinecartAbstract.EnumMinecartType entityminecartabstract_enumminecarttype) { + switch (entityminecartabstract_enumminecarttype) { + case CHEST: + return new EntityMinecartChest(world, d0, d1, d2); + case FURNACE: + return new EntityMinecartFurnace(world, d0, d1, d2); + case TNT: + return new EntityMinecartTNT(world, d0, d1, d2); + case SPAWNER: + return new EntityMinecartMobSpawner(world, d0, d1, d2); + case HOPPER: + return new EntityMinecartHopper(world, d0, d1, d2); + case COMMAND_BLOCK: + return new EntityMinecartCommandBlock(world, d0, d1, d2); + default: + return new EntityMinecartRideable(world, d0, d1, d2); + } + } + + protected boolean playStepSound() { + return false; + } + + protected void x_() { + this.datawatcher.register(EntityMinecartAbstract.a, 0); + this.datawatcher.register(EntityMinecartAbstract.b, 1); + this.datawatcher.register(EntityMinecartAbstract.c, 0.0F); + this.datawatcher.register(EntityMinecartAbstract.d, Block.getCombinedId(Blocks.AIR.getBlockData())); + this.datawatcher.register(EntityMinecartAbstract.e, 6); + this.datawatcher.register(EntityMinecartAbstract.f, false); + } + + @Nullable + public AxisAlignedBB j(Entity entity) { + return entity.isCollidable() ? entity.getBoundingBox() : null; + } + + public boolean isCollidable() { + return true; + } + + public double aJ() { + return 0.0D; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (!this.world.isClientSide && !this.dead) { + if (this.isInvulnerable(damagesource)) { + return false; + } else { + // CraftBukkit start - fire VehicleDamageEvent + Vehicle vehicle = (Vehicle) this.getBukkitEntity(); + org.bukkit.entity.Entity passenger = (damagesource.getEntity() == null) ? null : damagesource.getEntity().getBukkitEntity(); + + VehicleDamageEvent event = new VehicleDamageEvent(vehicle, passenger, f); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return true; + } + + f = (float) event.getDamage(); + // CraftBukkit end + this.k(-this.u()); + this.d(10); + this.aA(); + this.setDamage(this.getDamage() + f * 10.0F); + boolean flag = damagesource.getEntity() instanceof EntityHuman && ((EntityHuman) damagesource.getEntity()).abilities.canInstantlyBuild; + + if (flag || this.getDamage() > 40.0F) { + // CraftBukkit start + VehicleDestroyEvent destroyEvent = new VehicleDestroyEvent(vehicle, passenger); + this.world.getServer().getPluginManager().callEvent(destroyEvent); + + if (destroyEvent.isCancelled()) { + this.setDamage(40); // Maximize damage so this doesn't get triggered again right away + return true; + } + // CraftBukkit end + this.ejectPassengers(); + if (flag && !this.hasCustomName()) { + this.die(); + } else { + this.a(damagesource); + } + } + + return true; + } + } else { + return true; + } + } + + public void a(DamageSource damagesource) { + this.die(); + if (this.world.getGameRules().getBoolean("doEntityDrops")) { + ItemStack itemstack = new ItemStack(Items.MINECART); + + if (this.hasCustomName()) { + itemstack.a(this.getCustomName()); + } + + this.a_(itemstack); + } + + } + + public boolean isInteractable() { + return !this.dead; + } + + public EnumDirection getAdjustedDirection() { + return this.g ? this.getDirection().opposite().e() : this.getDirection().e(); + } + + public void tick() { + // CraftBukkit start + double prevX = this.locX; + double prevY = this.locY; + double prevZ = this.locZ; + float prevYaw = this.yaw; + float prevPitch = this.pitch; + // CraftBukkit end + + if (this.getType() > 0) { + this.d(this.getType() - 1); + } + + if (this.getDamage() > 0.0F) { + this.setDamage(this.getDamage() - 1.0F); + } + + // Paper start - Configurable nether ceiling damage + // Extracted to own function + /* + if (this.locY < -64.0D) { + this.aa(); + } + */ + this.checkAndDoHeightDamage(); + // Paper end + + int i; + + // CraftBukkit - handled in postTick + /* + if (!this.world.isClientSide && this.world instanceof WorldServer) { + this.world.methodProfiler.enter("portal"); + MinecraftServer minecraftserver = this.world.getMinecraftServer(); + + i = this.X(); + if (this.an) { + if (minecraftserver.getAllowNether()) { + if (!this.isPassenger() && this.ao++ >= i) { + this.ao = i; + this.portalCooldown = this.aQ(); + DimensionManager dimensionmanager; + + if (this.world.worldProvider.getDimensionManager() == DimensionManager.NETHER) { + dimensionmanager = DimensionManager.OVERWORLD; + } else { + dimensionmanager = DimensionManager.NETHER; + } + + this.a(dimensionmanager); + } + + this.an = false; + } + } else { + if (this.ao > 0) { + this.ao -= 4; + } + + if (this.ao < 0) { + this.ao = 0; + } + } + + if (this.portalCooldown > 0) { + --this.portalCooldown; + } + + this.world.methodProfiler.exit(); + } + */ + + if (this.world.isClientSide) { + if (this.aw > 0) { + double d0 = this.locX + (this.ax - this.locX) / (double) this.aw; + double d1 = this.locY + (this.ay - this.locY) / (double) this.aw; + double d2 = this.locZ + (this.az - this.locZ) / (double) this.aw; + double d3 = MathHelper.g(this.aA - (double) this.yaw); + + this.yaw = (float) ((double) this.yaw + d3 / (double) this.aw); + this.pitch = (float) ((double) this.pitch + (this.aB - (double) this.pitch) / (double) this.aw); + --this.aw; + this.setPosition(d0, d1, d2); + this.setYawPitch(this.yaw, this.pitch); + } else { + this.setPosition(this.locX, this.locY, this.locZ); + this.setYawPitch(this.yaw, this.pitch); + } + + } else { + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + if (!this.isNoGravity()) { + this.motY -= 0.03999999910593033D; + } + + int j = MathHelper.floor(this.locX); + + i = MathHelper.floor(this.locY); + int k = MathHelper.floor(this.locZ); + + if (this.world.getType(new BlockPosition(j, i - 1, k)).a(TagsBlock.RAILS)) { + --i; + } + + BlockPosition blockposition = new BlockPosition(j, i, k); + IBlockData iblockdata = this.world.getType(blockposition); + + if (iblockdata.a(TagsBlock.RAILS)) { + this.b(blockposition, iblockdata); + if (iblockdata.getBlock() == Blocks.ACTIVATOR_RAIL) { + this.a(j, i, k, (Boolean) iblockdata.get(BlockPoweredRail.POWERED)); + } + } else { + this.q(); + } + + this.checkBlockCollisions(); + this.pitch = 0.0F; + double d4 = this.lastX - this.locX; + double d5 = this.lastZ - this.locZ; + + if (d4 * d4 + d5 * d5 > 0.001D) { + this.yaw = (float) (MathHelper.c(d5, d4) * 180.0D / 3.141592653589793D); + if (this.g) { + this.yaw += 180.0F; + } + } + + double d6 = (double) MathHelper.g(this.yaw - this.lastYaw); + + if (d6 < -170.0D || d6 >= 170.0D) { + this.yaw += 180.0F; + this.g = !this.g; + } + + this.setYawPitch(this.yaw, this.pitch); + // CraftBukkit start + org.bukkit.World bworld = this.world.getWorld(); + Location from = new Location(bworld, prevX, prevY, prevZ, prevYaw, prevPitch); + Location to = new Location(bworld, this.locX, this.locY, this.locZ, this.yaw, this.pitch); + Vehicle vehicle = (Vehicle) this.getBukkitEntity(); + + this.world.getServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleUpdateEvent(vehicle)); + + if (!from.equals(to)) { + this.world.getServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleMoveEvent(vehicle, from, to)); + } + // CraftBukkit end + if (this.v() == EntityMinecartAbstract.EnumMinecartType.RIDEABLE && this.motX * this.motX + this.motZ * this.motZ > 0.01D) { + List list = this.world.getEntities(this, this.getBoundingBox().grow(0.20000000298023224D, 0.0D, 0.20000000298023224D), IEntitySelector.a(this)); + + if (!list.isEmpty()) { + for (int l = 0; l < list.size(); ++l) { + Entity entity = (Entity) list.get(l); + + if (!(entity instanceof EntityHuman) && !(entity instanceof EntityIronGolem) && !(entity instanceof EntityMinecartAbstract) && !this.isVehicle() && !entity.isPassenger()) { + // CraftBukkit start + VehicleEntityCollisionEvent collisionEvent = new VehicleEntityCollisionEvent(vehicle, entity.getBukkitEntity()); + this.world.getServer().getPluginManager().callEvent(collisionEvent); + + if (collisionEvent.isCancelled()) { + continue; + } + // CraftBukkit end + entity.startRiding(this); + } else { + // CraftBukkit start + VehicleEntityCollisionEvent collisionEvent = new VehicleEntityCollisionEvent(vehicle, entity.getBukkitEntity()); + this.world.getServer().getPluginManager().callEvent(collisionEvent); + + if (collisionEvent.isCancelled()) { + continue; + } + // CraftBukkit end + entity.collide(this); + } + } + } + } else { + Iterator iterator = this.world.getEntities(this, this.getBoundingBox().grow(0.20000000298023224D, 0.0D, 0.20000000298023224D)).iterator(); + + while (iterator.hasNext()) { + Entity entity1 = (Entity) iterator.next(); + + if (!this.w(entity1) && entity1.isCollidable() && entity1 instanceof EntityMinecartAbstract) { + // CraftBukkit start + VehicleEntityCollisionEvent collisionEvent = new VehicleEntityCollisionEvent(vehicle, entity1.getBukkitEntity()); + this.world.getServer().getPluginManager().callEvent(collisionEvent); + + if (collisionEvent.isCancelled()) { + continue; + } + // CraftBukkit end + entity1.collide(this); + } + } + } + + this.at(); + } + } + + protected double p() { + return this.maxSpeed; // CraftBukkit + } + + public void a(int i, int j, int k, boolean flag) {} + + protected void q() { + double d0 = this.p(); + + this.motX = MathHelper.a(this.motX, -d0, d0); + this.motZ = MathHelper.a(this.motZ, -d0, d0); + if (this.onGround) { + // CraftBukkit start - replace magic numbers with our variables + this.motX *= this.derailedX; + this.motY *= this.derailedY; + this.motZ *= this.derailedZ; + // CraftBukkit end + } + + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + if (!this.onGround) { + // CraftBukkit start - replace magic numbers with our variables + this.motX *= this.flyingX; + this.motY *= this.flyingY; + this.motZ *= this.flyingZ; + // CraftBukkit end + } + + } + + protected void b(BlockPosition blockposition, IBlockData iblockdata) { + this.fallDistance = 0.0F; + Vec3D vec3d = this.j(this.locX, this.locY, this.locZ); + + this.locY = (double) blockposition.getY(); + boolean flag = false; + boolean flag1 = false; + BlockMinecartTrackAbstract blockminecarttrackabstract = (BlockMinecartTrackAbstract) iblockdata.getBlock(); + + if (blockminecarttrackabstract == Blocks.POWERED_RAIL) { + flag = (Boolean) iblockdata.get(BlockPoweredRail.POWERED); + flag1 = !flag; + } + + double d0 = 0.0078125D; + BlockPropertyTrackPosition blockpropertytrackposition = (BlockPropertyTrackPosition) iblockdata.get(blockminecarttrackabstract.e()); + + switch (blockpropertytrackposition) { + case ASCENDING_EAST: + this.motX -= 0.0078125D; + ++this.locY; + break; + case ASCENDING_WEST: + this.motX += 0.0078125D; + ++this.locY; + break; + case ASCENDING_NORTH: + this.motZ += 0.0078125D; + ++this.locY; + break; + case ASCENDING_SOUTH: + this.motZ -= 0.0078125D; + ++this.locY; + } + + int[][] aint = EntityMinecartAbstract.h[blockpropertytrackposition.a()]; + double d1 = (double) (aint[1][0] - aint[0][0]); + double d2 = (double) (aint[1][2] - aint[0][2]); + double d3 = Math.sqrt(d1 * d1 + d2 * d2); + double d4 = this.motX * d1 + this.motZ * d2; + + if (d4 < 0.0D) { + d1 = -d1; + d2 = -d2; + } + + double d5 = Math.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + if (d5 > 2.0D) { + d5 = 2.0D; + } + + this.motX = d5 * d1 / d3; + this.motZ = d5 * d2 / d3; + Entity entity = this.bP().isEmpty() ? null : (Entity) this.bP().get(0); + double d6; + double d7; + double d8; + double d9; + + if (entity instanceof EntityHuman) { + d6 = (double) ((EntityHuman) entity).bj; + if (d6 > 0.0D) { + d7 = -Math.sin((double) (entity.yaw * 0.017453292F)); + d8 = Math.cos((double) (entity.yaw * 0.017453292F)); + d9 = this.motX * this.motX + this.motZ * this.motZ; + if (d9 < 0.01D) { + this.motX += d7 * 0.1D; + this.motZ += d8 * 0.1D; + flag1 = false; + } + } + } + + if (flag1) { + d6 = Math.sqrt(this.motX * this.motX + this.motZ * this.motZ); + if (d6 < 0.03D) { + this.motX *= 0.0D; + this.motY *= 0.0D; + this.motZ *= 0.0D; + } else { + this.motX *= 0.5D; + this.motY *= 0.0D; + this.motZ *= 0.5D; + } + } + + d6 = (double) blockposition.getX() + 0.5D + (double) aint[0][0] * 0.5D; + d7 = (double) blockposition.getZ() + 0.5D + (double) aint[0][2] * 0.5D; + d8 = (double) blockposition.getX() + 0.5D + (double) aint[1][0] * 0.5D; + d9 = (double) blockposition.getZ() + 0.5D + (double) aint[1][2] * 0.5D; + d1 = d8 - d6; + d2 = d9 - d7; + double d10; + double d11; + double d12; + + if (d1 == 0.0D) { + this.locX = (double) blockposition.getX() + 0.5D; + d10 = this.locZ - (double) blockposition.getZ(); + } else if (d2 == 0.0D) { + this.locZ = (double) blockposition.getZ() + 0.5D; + d10 = this.locX - (double) blockposition.getX(); + } else { + d11 = this.locX - d6; + d12 = this.locZ - d7; + d10 = (d11 * d1 + d12 * d2) * 2.0D; + } + + this.locX = d6 + d1 * d10; + this.locZ = d7 + d2 * d10; + this.setPosition(this.locX, this.locY, this.locZ); + d11 = this.motX; + d12 = this.motZ; + if (this.isVehicle()) { + d11 *= 0.75D; + d12 *= 0.75D; + } + + double d13 = this.p(); + + d11 = MathHelper.a(d11, -d13, d13); + d12 = MathHelper.a(d12, -d13, d13); + this.move(EnumMoveType.SELF, d11, 0.0D, d12); + if (aint[0][1] != 0 && MathHelper.floor(this.locX) - blockposition.getX() == aint[0][0] && MathHelper.floor(this.locZ) - blockposition.getZ() == aint[0][2]) { + this.setPosition(this.locX, this.locY + (double) aint[0][1], this.locZ); + } else if (aint[1][1] != 0 && MathHelper.floor(this.locX) - blockposition.getX() == aint[1][0] && MathHelper.floor(this.locZ) - blockposition.getZ() == aint[1][2]) { + this.setPosition(this.locX, this.locY + (double) aint[1][1], this.locZ); + } + + this.r(); + Vec3D vec3d1 = this.j(this.locX, this.locY, this.locZ); + + if (vec3d1 != null && vec3d != null) { + double d14 = (vec3d.y - vec3d1.y) * 0.05D; + + d5 = Math.sqrt(this.motX * this.motX + this.motZ * this.motZ); + if (d5 > 0.0D) { + this.motX = this.motX / d5 * (d5 + d14); + this.motZ = this.motZ / d5 * (d5 + d14); + } + + this.setPosition(this.locX, vec3d1.y, this.locZ); + } + + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locZ); + + if (i != blockposition.getX() || j != blockposition.getZ()) { + d5 = Math.sqrt(this.motX * this.motX + this.motZ * this.motZ); + this.motX = d5 * (double) (i - blockposition.getX()); + this.motZ = d5 * (double) (j - blockposition.getZ()); + } + + if (flag) { + double d15 = Math.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + if (d15 > 0.01D) { + double d16 = 0.06D; + + this.motX += this.motX / d15 * 0.06D; + this.motZ += this.motZ / d15 * 0.06D; + } else if (blockpropertytrackposition == BlockPropertyTrackPosition.EAST_WEST) { + if (this.world.getType(blockposition.west()).isOccluding()) { + this.motX = 0.02D; + } else if (this.world.getType(blockposition.east()).isOccluding()) { + this.motX = -0.02D; + } + } else if (blockpropertytrackposition == BlockPropertyTrackPosition.NORTH_SOUTH) { + if (this.world.getType(blockposition.north()).isOccluding()) { + this.motZ = 0.02D; + } else if (this.world.getType(blockposition.south()).isOccluding()) { + this.motZ = -0.02D; + } + } + } + + } + + protected void r() { + if (this.isVehicle() || !this.slowWhenEmpty) { // CraftBukkit - add !this.slowWhenEmpty + this.motX *= 0.996999979019165D; + this.motY *= 0.0D; + this.motZ *= 0.996999979019165D; + } else { + this.motX *= 0.9599999785423279D; + this.motY *= 0.0D; + this.motZ *= 0.9599999785423279D; + } + + } + + public void setPosition(double d0, double d1, double d2) { + this.locX = d0; + this.locY = d1; + this.locZ = d2; + float f = this.width / 2.0F; + float f1 = this.length; + + this.a(new AxisAlignedBB(d0 - (double) f, d1, d2 - (double) f, d0 + (double) f, d1 + (double) f1, d2 + (double) f)); + } + + @Nullable + public Vec3D j(double d0, double d1, double d2) { + int i = MathHelper.floor(d0); + int j = MathHelper.floor(d1); + int k = MathHelper.floor(d2); + + if (this.world.getType(new BlockPosition(i, j - 1, k)).a(TagsBlock.RAILS)) { + --j; + } + + IBlockData iblockdata = this.world.getType(new BlockPosition(i, j, k)); + + if (iblockdata.a(TagsBlock.RAILS)) { + BlockPropertyTrackPosition blockpropertytrackposition = (BlockPropertyTrackPosition) iblockdata.get(((BlockMinecartTrackAbstract) iblockdata.getBlock()).e()); + int[][] aint = EntityMinecartAbstract.h[blockpropertytrackposition.a()]; + double d3 = (double) i + 0.5D + (double) aint[0][0] * 0.5D; + double d4 = (double) j + 0.0625D + (double) aint[0][1] * 0.5D; + double d5 = (double) k + 0.5D + (double) aint[0][2] * 0.5D; + double d6 = (double) i + 0.5D + (double) aint[1][0] * 0.5D; + double d7 = (double) j + 0.0625D + (double) aint[1][1] * 0.5D; + double d8 = (double) k + 0.5D + (double) aint[1][2] * 0.5D; + double d9 = d6 - d3; + double d10 = (d7 - d4) * 2.0D; + double d11 = d8 - d5; + double d12; + + if (d9 == 0.0D) { + d12 = d2 - (double) k; + } else if (d11 == 0.0D) { + d12 = d0 - (double) i; + } else { + double d13 = d0 - d3; + double d14 = d2 - d5; + + d12 = (d13 * d9 + d14 * d11) * 2.0D; + } + + d0 = d3 + d9 * d12; + d1 = d4 + d10 * d12; + d2 = d5 + d11 * d12; + if (d10 < 0.0D) { + ++d1; + } + + if (d10 > 0.0D) { + d1 += 0.5D; + } + + return new Vec3D(d0, d1, d2); + } else { + return null; + } + } + + protected void a(NBTTagCompound nbttagcompound) { + if (nbttagcompound.getBoolean("CustomDisplayTile")) { + this.setDisplayBlock(GameProfileSerializer.d(nbttagcompound.getCompound("DisplayState"))); + this.setDisplayBlockOffset(nbttagcompound.getInt("DisplayOffset")); + } + + } + + protected void b(NBTTagCompound nbttagcompound) { + if (this.C()) { + nbttagcompound.setBoolean("CustomDisplayTile", true); + nbttagcompound.set("DisplayState", GameProfileSerializer.a(this.getDisplayBlock())); + nbttagcompound.setInt("DisplayOffset", this.getDisplayBlockOffset()); + } + + } + + public void collide(Entity entity) { + if (!this.world.isClientSide) { + if (!entity.noclip && !this.noclip) { + if (!this.w(entity)) { + // CraftBukkit start + VehicleEntityCollisionEvent collisionEvent = new VehicleEntityCollisionEvent((Vehicle) this.getBukkitEntity(), entity.getBukkitEntity()); + this.world.getServer().getPluginManager().callEvent(collisionEvent); + + if (collisionEvent.isCancelled()) { + return; + } + // CraftBukkit end + double d0 = entity.locX - this.locX; + double d1 = entity.locZ - this.locZ; + double d2 = d0 * d0 + d1 * d1; + + if (d2 >= 9.999999747378752E-5D) { + d2 = (double) MathHelper.sqrt(d2); + d0 /= d2; + d1 /= d2; + double d3 = 1.0D / d2; + + if (d3 > 1.0D) { + d3 = 1.0D; + } + + d0 *= d3; + d1 *= d3; + d0 *= 0.10000000149011612D; + d1 *= 0.10000000149011612D; + d0 *= (double) (1.0F - this.S); + d1 *= (double) (1.0F - this.S); + d0 *= 0.5D; + d1 *= 0.5D; + if (entity instanceof EntityMinecartAbstract) { + double d4 = entity.locX - this.locX; + double d5 = entity.locZ - this.locZ; + Vec3D vec3d = (new Vec3D(d4, 0.0D, d5)).a(); + Vec3D vec3d1 = (new Vec3D((double) MathHelper.cos(this.yaw * 0.017453292F), 0.0D, (double) MathHelper.sin(this.yaw * 0.017453292F))).a(); + double d6 = Math.abs(vec3d.b(vec3d1)); + + if (d6 < 0.800000011920929D) { + return; + } + + double d7 = entity.motX + this.motX; + double d8 = entity.motZ + this.motZ; + + if (((EntityMinecartAbstract) entity).v() == EntityMinecartAbstract.EnumMinecartType.FURNACE && this.v() != EntityMinecartAbstract.EnumMinecartType.FURNACE) { + this.motX *= 0.20000000298023224D; + this.motZ *= 0.20000000298023224D; + this.f(entity.motX - d0, 0.0D, entity.motZ - d1); + entity.motX *= 0.949999988079071D; + entity.motZ *= 0.949999988079071D; + } else if (((EntityMinecartAbstract) entity).v() != EntityMinecartAbstract.EnumMinecartType.FURNACE && this.v() == EntityMinecartAbstract.EnumMinecartType.FURNACE) { + entity.motX *= 0.20000000298023224D; + entity.motZ *= 0.20000000298023224D; + entity.f(this.motX + d0, 0.0D, this.motZ + d1); + this.motX *= 0.949999988079071D; + this.motZ *= 0.949999988079071D; + } else { + d7 /= 2.0D; + d8 /= 2.0D; + this.motX *= 0.20000000298023224D; + this.motZ *= 0.20000000298023224D; + this.f(d7 - d0, 0.0D, d8 - d1); + entity.motX *= 0.20000000298023224D; + entity.motZ *= 0.20000000298023224D; + entity.f(d7 + d0, 0.0D, d8 + d1); + } + } else { + this.f(-d0, 0.0D, -d1); + entity.f(d0 / 4.0D, 0.0D, d1 / 4.0D); + } + } + + } + } + } + } + + public void setDamage(float f) { + this.datawatcher.set(EntityMinecartAbstract.c, f); + } + + public float getDamage() { + return (Float) this.datawatcher.get(EntityMinecartAbstract.c); + } + + public void d(int i) { + this.datawatcher.set(EntityMinecartAbstract.a, i); + } + + public int getType() { + return (Integer) this.datawatcher.get(EntityMinecartAbstract.a); + } + + public void k(int i) { + this.datawatcher.set(EntityMinecartAbstract.b, i); + } + + public int u() { + return (Integer) this.datawatcher.get(EntityMinecartAbstract.b); + } + + public abstract EntityMinecartAbstract.EnumMinecartType v(); + + public IBlockData getDisplayBlock() { + return !this.C() ? this.z() : Block.getByCombinedId((Integer) this.getDataWatcher().get(EntityMinecartAbstract.d)); + } + + public IBlockData z() { + return Blocks.AIR.getBlockData(); + } + + public int getDisplayBlockOffset() { + return !this.C() ? this.B() : (Integer) this.getDataWatcher().get(EntityMinecartAbstract.e); + } + + public int B() { + return 6; + } + + public void setDisplayBlock(IBlockData iblockdata) { + this.getDataWatcher().set(EntityMinecartAbstract.d, Block.getCombinedId(iblockdata)); + this.a(true); + } + + public void setDisplayBlockOffset(int i) { + this.getDataWatcher().set(EntityMinecartAbstract.e, i); + this.a(true); + } + + public boolean C() { + return (Boolean) this.getDataWatcher().get(EntityMinecartAbstract.f); + } + + public void a(boolean flag) { + this.getDataWatcher().set(EntityMinecartAbstract.f, flag); + } + + public static enum EnumMinecartType { + + RIDEABLE(0), CHEST(1), FURNACE(2), TNT(3), SPAWNER(4), HOPPER(5), COMMAND_BLOCK(6); + + private static final EntityMinecartAbstract.EnumMinecartType[] h = (EntityMinecartAbstract.EnumMinecartType[]) Arrays.stream(values()).sorted(Comparator.comparingInt(EntityMinecartAbstract.EnumMinecartType::a)).toArray((i) -> { + return new EntityMinecartAbstract.EnumMinecartType[i]; + }); + private final int i; + + private EnumMinecartType(int i) { + this.i = i; + } + + public int a() { + return this.i; + } + } + + // CraftBukkit start - Methods for getting and setting flying and derailed velocity modifiers + public Vector getFlyingVelocityMod() { + return new Vector(flyingX, flyingY, flyingZ); + } + + public void setFlyingVelocityMod(Vector flying) { + flyingX = flying.getX(); + flyingY = flying.getY(); + flyingZ = flying.getZ(); + } + + public Vector getDerailedVelocityMod() { + return new Vector(derailedX, derailedY, derailedZ); + } + + public void setDerailedVelocityMod(Vector derailed) { + derailedX = derailed.getX(); + derailedY = derailed.getY(); + derailedZ = derailed.getZ(); + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/EntityMinecartCommandBlock.java b/src/main/java/net/minecraft/server/EntityMinecartCommandBlock.java new file mode 100644 index 000000000000..61f11c90d3e4 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityMinecartCommandBlock.java @@ -0,0 +1,103 @@ +package net.minecraft.server; + +public class EntityMinecartCommandBlock extends EntityMinecartAbstract { + + public static final DataWatcherObject COMMAND = DataWatcher.a(EntityMinecartCommandBlock.class, DataWatcherRegistry.d); + private static final DataWatcherObject b = DataWatcher.a(EntityMinecartCommandBlock.class, DataWatcherRegistry.e); + private final CommandBlockListenerAbstract c = new EntityMinecartCommandBlock.a(); + private int d; + + public EntityMinecartCommandBlock(World world) { + super(EntityTypes.COMMAND_BLOCK_MINECART, world); + } + + public EntityMinecartCommandBlock(World world, double d0, double d1, double d2) { + super(EntityTypes.COMMAND_BLOCK_MINECART, world, d0, d1, d2); + } + + protected void x_() { + super.x_(); + this.getDataWatcher().register(EntityMinecartCommandBlock.COMMAND, ""); + this.getDataWatcher().register(EntityMinecartCommandBlock.b, new ChatComponentText("")); + } + + protected void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.c.b(nbttagcompound); + this.getDataWatcher().set(EntityMinecartCommandBlock.COMMAND, this.getCommandBlock().getCommand()); + this.getDataWatcher().set(EntityMinecartCommandBlock.b, this.getCommandBlock().j()); + } + + protected void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + this.c.a(nbttagcompound); + } + + public EntityMinecartAbstract.EnumMinecartType v() { + return EntityMinecartAbstract.EnumMinecartType.COMMAND_BLOCK; + } + + public IBlockData z() { + return Blocks.COMMAND_BLOCK.getBlockData(); + } + + public CommandBlockListenerAbstract getCommandBlock() { + return this.c; + } + + public void a(int i, int j, int k, boolean flag) { + if (flag && this.ticksLived - this.d >= 4) { + this.getCommandBlock().a(this.world); + this.d = this.ticksLived; + } + + } + + public boolean b(EntityHuman entityhuman, EnumHand enumhand) { + this.c.a(entityhuman); + return true; + } + + public void a(DataWatcherObject datawatcherobject) { + super.a(datawatcherobject); + if (EntityMinecartCommandBlock.b.equals(datawatcherobject)) { + try { + this.c.c((IChatBaseComponent) this.getDataWatcher().get(EntityMinecartCommandBlock.b)); + } catch (Throwable throwable) { + ; + } + } else if (EntityMinecartCommandBlock.COMMAND.equals(datawatcherobject)) { + this.c.setCommand((String) this.getDataWatcher().get(EntityMinecartCommandBlock.COMMAND)); + } + + } + + public boolean bM() { + return true; + } + + public class a extends CommandBlockListenerAbstract { + + public a() {} + + public WorldServer d() { + return (WorldServer) EntityMinecartCommandBlock.this.world; + } + + public void e() { + EntityMinecartCommandBlock.this.getDataWatcher().set(EntityMinecartCommandBlock.COMMAND, this.getCommand()); + EntityMinecartCommandBlock.this.getDataWatcher().set(EntityMinecartCommandBlock.b, this.j()); + } + + public CommandListenerWrapper getWrapper() { + return new CommandListenerWrapper(this, new Vec3D(EntityMinecartCommandBlock.this.locX, EntityMinecartCommandBlock.this.locY, EntityMinecartCommandBlock.this.locZ), EntityMinecartCommandBlock.this.aO(), this.d(), 2, this.getName().getString(), EntityMinecartCommandBlock.this.getScoreboardDisplayName(), this.d().getMinecraftServer(), EntityMinecartCommandBlock.this); + } + + // CraftBukkit start + @Override + public org.bukkit.command.CommandSender getBukkitSender(CommandListenerWrapper wrapper) { + return (org.bukkit.craftbukkit.entity.CraftMinecartCommand) EntityMinecartCommandBlock.this.getBukkitEntity(); + } + // CraftBukkit end + } +} diff --git a/src/main/java/net/minecraft/server/EntityMinecartContainer.java b/src/main/java/net/minecraft/server/EntityMinecartContainer.java new file mode 100644 index 000000000000..e228fc8538b0 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityMinecartContainer.java @@ -0,0 +1,276 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.Random; +import javax.annotation.Nullable; +// CraftBukkit start +import java.util.List; +import org.bukkit.Location; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.InventoryHolder; +// CraftBukkit end + +public abstract class EntityMinecartContainer extends EntityMinecartAbstract implements ITileInventory, ILootable { + + private NonNullList items; + private boolean b; + private MinecraftKey c; public MinecraftKey getLootTableKey() { return c; } public void setLootTable(MinecraftKey key) { c = key; } // Paper - OBFHELPER + public long lootTableSeed; + + // CraftBukkit start + { lootableData = new com.destroystokyo.paper.loottable.PaperLootableInventoryData(new com.destroystokyo.paper.loottable.PaperMinecartLootableInventory(this)); } // Paper + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public List getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public InventoryHolder getOwner() { + org.bukkit.entity.Entity cart = getBukkitEntity(); + if(cart instanceof InventoryHolder) return (InventoryHolder) cart; + return null; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + // CraftBukkit end + + protected EntityMinecartContainer(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.items = NonNullList.a(this.getSize(), ItemStack.a); // CraftBukkit - SPIGOT-3513 + this.b = true; + } + + protected EntityMinecartContainer(EntityTypes entitytypes, double d0, double d1, double d2, World world) { + super(entitytypes, world, d0, d1, d2); + this.items = NonNullList.a(this.getSize(), ItemStack.a); // CraftBukkit - SPIGOT-3513 + this.b = true; + } + + public void a(DamageSource damagesource) { + super.a(damagesource); + if (this.world.getGameRules().getBoolean("doEntityDrops")) { + InventoryUtils.dropEntity(this.world, this, this); + } + + } + + public boolean P_() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + public ItemStack getItem(int i) { + this.f((EntityHuman) null); + return (ItemStack) this.items.get(i); + } + + public ItemStack splitStack(int i, int j) { + this.f((EntityHuman) null); + return ContainerUtil.a(this.items, i, j); + } + + public ItemStack splitWithoutUpdate(int i) { + this.f((EntityHuman) null); + ItemStack itemstack = (ItemStack) this.items.get(i); + + if (itemstack.isEmpty()) { + return ItemStack.a; + } else { + this.items.set(i, ItemStack.a); + return itemstack; + } + } + + public void setItem(int i, ItemStack itemstack) { + this.f((EntityHuman) null); + this.items.set(i, itemstack); + if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { + itemstack.setCount(this.getMaxStackSize()); + } + + } + + public boolean c(int i, ItemStack itemstack) { + if (i >= 0 && i < this.getSize()) { + this.setItem(i, itemstack); + return true; + } else { + return false; + } + } + + public void update() {} + + public boolean a(EntityHuman entityhuman) { + return this.dead ? false : entityhuman.h(this) <= 64.0D; + } + + public void startOpen(EntityHuman entityhuman) {} + + public void closeContainer(EntityHuman entityhuman) {} + + public boolean b(int i, ItemStack itemstack) { + return true; + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + @Nullable + public Entity a(DimensionManager dimensionmanager) { + this.b = false; + return super.a(dimensionmanager); + } + + public void die() { + if (this.b) { + InventoryUtils.dropEntity(this.world, this, this); + } + + super.die(); + } + + public void b(boolean flag) { + this.b = flag; + } + + protected void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + lootableData.saveNbt(nbttagcompound); // Paper + if (this.c != null) { + nbttagcompound.setString("LootTable", this.c.toString()); + if (this.lootTableSeed != 0L) { + nbttagcompound.setLong("LootTableSeed", this.lootTableSeed); + } + } if (true) { // Paper - Always save the items, Table may stick around + ContainerUtil.a(nbttagcompound, this.items); + } + + } + + protected void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + lootableData.loadNbt(nbttagcompound); // Paper + this.items = NonNullList.a(this.getSize(), ItemStack.a); + if (nbttagcompound.hasKeyOfType("LootTable", 8)) { + this.c = new MinecraftKey(nbttagcompound.getString("LootTable")); + this.lootTableSeed = nbttagcompound.getLong("LootTableSeed"); + } if (true) { // Paper - always load the items, table may still remain + ContainerUtil.b(nbttagcompound, this.items); + } + + } + + public boolean b(EntityHuman entityhuman, EnumHand enumhand) { + if (!this.world.isClientSide) { + entityhuman.openContainer(this); + } + + return true; + } + + protected void r() { + float f = 0.98F; + + if (this.c == null) { + int i = 15 - Container.b((IInventory) this); + + f += (float) i * 0.001F; + } + + this.motX *= (double) f; + this.motY *= 0.0D; + this.motZ *= (double) f; + } + + public int getProperty(int i) { + return 0; + } + + public void setProperty(int i, int j) {} + + public int h() { + return 0; + } + + public boolean isLocked() { + return false; + } + + public void setLock(ChestLock chestlock) {} + + public ChestLock getLock() { + return ChestLock.a; + } + + public void f(@Nullable EntityHuman entityhuman) { + if (lootableData.shouldReplenish(entityhuman) && this.world.getMinecraftServer() != null) { // Paper + LootTable loottable = this.world.getMinecraftServer().getLootTableRegistry().getLootTable(this.c); + + lootableData.processRefill(entityhuman); // Paper + Random random; + + if (this.lootTableSeed == 0L) { + random = new Random(); + } else { + random = new Random(this.lootTableSeed); + } + + LootTableInfo.Builder loottableinfo_builder = (new LootTableInfo.Builder((WorldServer) this.world)).position(new BlockPosition(this)); + + if (entityhuman != null) { + loottableinfo_builder.luck(entityhuman.dJ()); + } + + loottable.fillInventory(this, random, loottableinfo_builder.build()); + } + + } + + public void clear() { + this.f((EntityHuman) null); + this.items.clear(); + } + + public void a(MinecraftKey minecraftkey, long i) { + this.c = minecraftkey; + this.lootTableSeed = i; + } + + public MinecraftKey getLootTable() { + return this.c; + } +} diff --git a/src/main/java/net/minecraft/server/EntityMonster.java b/src/main/java/net/minecraft/server/EntityMonster.java new file mode 100644 index 000000000000..dc61263a3f1d --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityMonster.java @@ -0,0 +1,100 @@ +package net.minecraft.server; + +public abstract class EntityMonster extends EntityCreature implements IMonster { + + public org.bukkit.craftbukkit.entity.CraftMonster getBukkitMonster() { return (org.bukkit.craftbukkit.entity.CraftMonster) super.getBukkitEntity(); } // Paper + protected EntityMonster(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.b_ = 5; + } + + public SoundCategory getSoundCategory() { return bV(); } // Paper - OBFHELPER + public SoundCategory bV() { + return SoundCategory.HOSTILE; + } + + public void movementTick() { + this.cy(); + float f = this.az(); + + if (f > 0.5F) { + this.ticksFarFromPlayer += 2; + } + + super.movementTick(); + } + + public void tick() { + super.tick(); + if (!this.world.isClientSide && this.world.getDifficulty() == EnumDifficulty.PEACEFUL) { + this.die(); + } + + } + + protected SoundEffect ad() { + return SoundEffects.ENTITY_HOSTILE_SWIM; + } + + protected SoundEffect ae() { + return SoundEffects.ENTITY_HOSTILE_SPLASH; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + return this.isInvulnerable(damagesource) ? false : super.damageEntity(damagesource, f); + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_HOSTILE_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_HOSTILE_DEATH; + } + + protected SoundEffect m(int i) { + return i > 4 ? SoundEffects.ENTITY_HOSTILE_BIG_FALL : SoundEffects.ENTITY_HOSTILE_SMALL_FALL; + } + + public float a(BlockPosition blockposition, IWorldReader iworldreader) { + return 0.5F - iworldreader.A(blockposition); + } + + protected boolean K_() { + BlockPosition blockposition = new BlockPosition(this.locX, this.getBoundingBox().minY, this.locZ); + + if (this.world.getBrightness(EnumSkyBlock.SKY, blockposition) > this.random.nextInt(32)) { + return false; + } else { + // Paper start - optimized light check, returns faster + boolean passes; + if (this.world.Y()) { + final int orig = world.getSkylightSubtracted(); + world.setSkylightSubtracted(10); + passes = !this.world.isLightLevel(blockposition, this.random.nextInt(8)); + world.setSkylightSubtracted(orig); + } else { + passes = !this.world.isLightLevel(blockposition, this.random.nextInt(8)); + } + return passes; + // Paper end + } + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + return generatoraccess.getDifficulty() != EnumDifficulty.PEACEFUL && this.K_() && super.a(generatoraccess, flag); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeMap().b(GenericAttributes.ATTACK_DAMAGE); + } + + protected boolean isDropExperience() { + return true; + } + + public boolean c(EntityHuman entityhuman) { + return true; + } +} diff --git a/src/main/java/net/minecraft/server/EntityMushroomCow.java b/src/main/java/net/minecraft/server/EntityMushroomCow.java new file mode 100644 index 000000000000..638dbe978da2 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityMushroomCow.java @@ -0,0 +1,84 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityTransformEvent; +import org.bukkit.event.player.PlayerShearEntityEvent; +// CraftBukkit end + +public class EntityMushroomCow extends EntityCow { + + public EntityMushroomCow(World world) { + super(EntityTypes.MOOSHROOM, world); + this.setSize(0.9F, 1.4F); + this.bF = Blocks.MYCELIUM; + } + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (itemstack.getItem() == Items.BOWL && this.getAge() >= 0 && !entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + if (itemstack.isEmpty()) { + entityhuman.a(enumhand, new ItemStack(Items.MUSHROOM_STEW)); + } else if (!entityhuman.inventory.pickup(new ItemStack(Items.MUSHROOM_STEW))) { + entityhuman.drop(new ItemStack(Items.MUSHROOM_STEW), false); + } + + return true; + } else if (itemstack.getItem() == Items.SHEARS && this.getAge() >= 0) { + // CraftBukkit start + PlayerShearEntityEvent event = new PlayerShearEntityEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), this.getBukkitEntity()); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return false; + } + // CraftBukkit end + this.world.addParticle(Particles.u, this.locX, this.locY + (double) (this.length / 2.0F), this.locZ, 0.0D, 0.0D, 0.0D); + if (!this.world.isClientSide) { + // this.die(); // CraftBukkit - moved down + EntityCow entitycow = EntityTypes.COW.create(world); // Paper + + entitycow.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, this.pitch); + entitycow.setHealth(this.getHealth()); + entitycow.aQ = this.aQ; + if (this.hasCustomName()) { + entitycow.setCustomName(this.getCustomName()); + } + + // CraftBukkit start + if (CraftEventFactory.callEntityTransformEvent(this, entitycow, EntityTransformEvent.TransformReason.SHEARED).isCancelled()) { + return false; + } + if (!new com.destroystokyo.paper.event.entity.EntityTransformedEvent(this.getBukkitEntity(), entitycow.getBukkitEntity(), com.destroystokyo.paper.event.entity.EntityTransformedEvent.TransformedReason.SHEARED).callEvent()) return false; // Paper + this.world.addEntity(entitycow, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SHEARED); + + this.die(); // CraftBukkit - from above + // CraftBukkit end + + for (int i = 0; i < 5; ++i) { + this.world.addEntity(new EntityItem(this.world, this.locX, this.locY + (double) this.length, this.locZ, new ItemStack(Blocks.RED_MUSHROOM))); + } + + itemstack.damage(1, entityhuman); + this.a(SoundEffects.ENTITY_MOOSHROOM_SHEAR, 1.0F, 1.0F); + } + + return true; + } else { + return super.a(entityhuman, enumhand); + } + } + + public EntityMushroomCow createChild(EntityAgeable entityageable) { + return EntityTypes.MOOSHROOM.create(world); // Paper + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.T; + } +} diff --git a/src/main/java/net/minecraft/server/EntityOcelot.java b/src/main/java/net/minecraft/server/EntityOcelot.java new file mode 100644 index 000000000000..13c84bda8431 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityOcelot.java @@ -0,0 +1,250 @@ +package net.minecraft.server; + +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public class EntityOcelot extends EntityTameableAnimal { + + private static final RecipeItemStack bG = RecipeItemStack.a(Items.COD, Items.SALMON, Items.TROPICAL_FISH, Items.PUFFERFISH); + private static final DataWatcherObject bH = DataWatcher.a(EntityOcelot.class, DataWatcherRegistry.b); + private static final MinecraftKey bI = new MinecraftKey("cat"); + private PathfinderGoalAvoidTarget bJ; + private PathfinderGoalTempt bK; + public boolean spawnBonus = true; // Spigot + + public EntityOcelot(World world) { + super(EntityTypes.OCELOT, world); + this.setSize(0.6F, 0.7F); + } + + protected void n() { + this.goalSit = new PathfinderGoalSit(this); + this.bK = new PathfinderGoalTempt(this, 0.6D, EntityOcelot.bG, true); + this.goalSelector.a(1, new PathfinderGoalFloat(this)); + this.goalSelector.a(2, this.goalSit); + this.goalSelector.a(3, this.bK); + this.goalSelector.a(5, new PathfinderGoalFollowOwner(this, 1.0D, 10.0F, 5.0F)); + this.goalSelector.a(6, new PathfinderGoalJumpOnBlock(this, 0.8D)); + this.goalSelector.a(7, new PathfinderGoalLeapAtTarget(this, 0.3F)); + this.goalSelector.a(8, new PathfinderGoalOcelotAttack(this)); + this.goalSelector.a(9, new PathfinderGoalBreed(this, 0.8D)); + this.goalSelector.a(10, new PathfinderGoalRandomStrollLand(this, 0.8D, 1.0000001E-5F)); + this.goalSelector.a(11, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 10.0F)); + this.targetSelector.a(1, new PathfinderGoalRandomTargetNonTamed<>(this, EntityChicken.class, false, (Predicate) null)); + this.targetSelector.a(1, new PathfinderGoalRandomTargetNonTamed<>(this, EntityTurtle.class, false, EntityTurtle.bC)); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityOcelot.bH, 0); + } + + public void mobTick() { + if (this.getControllerMove().b()) { + double d0 = this.getControllerMove().c(); + + if (d0 == 0.6D) { + this.setSneaking(true); + this.setSprinting(false); + } else if (d0 == 1.33D) { + this.setSneaking(false); + this.setSprinting(true); + } else { + this.setSneaking(false); + this.setSprinting(false); + } + } else { + this.setSneaking(false); + this.setSprinting(false); + } + + } + + public boolean isTypeNotPersistent() { + return !this.isTamed() && !this.hasCustomName() && !this.isLeashed() /*&& this.ticksLived > 2400*/; // CraftBukkit - Paper (honor name and leash) + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(10.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.30000001192092896D); + } + + public void c(float f, float f1) {} + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("CatType", this.getCatType()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setCatType(nbttagcompound.getInt("CatType")); + } + + @Nullable + protected SoundEffect D() { + return this.isTamed() ? (this.isInLove() ? SoundEffects.ENTITY_CAT_PURR : (this.random.nextInt(4) == 0 ? SoundEffects.ENTITY_CAT_PURREOW : SoundEffects.ENTITY_CAT_AMBIENT)) : null; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_CAT_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_CAT_DEATH; + } + + protected float cD() { + return 0.4F; + } + + public boolean B(Entity entity) { + return entity.damageEntity(DamageSource.mobAttack(this), 3.0F); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else { + if (this.goalSit != null) { + // CraftBukkit - moved into EntityLiving.d(DamageSource, float) + // this.goalSit.setSitting(false); + } + + return super.damageEntity(damagesource, f); + } + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.V; + } + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (this.isTamed()) { + if (this.f((EntityLiving) entityhuman) && !this.world.isClientSide && !this.f(itemstack)) { + this.goalSit.setSitting(!this.isSitting()); + } + } else if ((this.bK == null || this.bK.g()) && EntityOcelot.bG.test(itemstack) && entityhuman.h(this) < 9.0D) { + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + if (!this.world.isClientSide) { + // CraftBukkit - added event call and isCancelled check + if (this.random.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, entityhuman).isCancelled()) { + this.c(entityhuman); + this.setCatType(1 + this.world.random.nextInt(3)); + this.s(true); + this.goalSit.setSitting(true); + this.world.broadcastEntityEffect(this, (byte) 7); + } else { + this.s(false); + this.world.broadcastEntityEffect(this, (byte) 6); + } + } + + return true; + } + + return super.a(entityhuman, enumhand); + } + + public EntityOcelot createChild(EntityAgeable entityageable) { + EntityOcelot entityocelot = EntityTypes.OCELOT.create(world); // Paper + + if (this.isTamed()) { + entityocelot.setOwnerUUID(this.getOwnerUUID()); + entityocelot.setTamed(true); + entityocelot.setCatType(this.getCatType()); + } + + return entityocelot; + } + + public boolean f(ItemStack itemstack) { + return EntityOcelot.bG.test(itemstack); + } + + public boolean mate(EntityAnimal entityanimal) { + if (entityanimal == this) { + return false; + } else if (!this.isTamed()) { + return false; + } else if (!(entityanimal instanceof EntityOcelot)) { + return false; + } else { + EntityOcelot entityocelot = (EntityOcelot) entityanimal; + + return !entityocelot.isTamed() ? false : this.isInLove() && entityocelot.isInLove(); + } + } + + public int getCatType() { + return (Integer) this.datawatcher.get(EntityOcelot.bH); + } + + public void setCatType(int i) { + this.datawatcher.set(EntityOcelot.bH, i); + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + return this.random.nextInt(3) != 0; + } + + public boolean a(IWorldReader iworldreader) { + if (iworldreader.a_(this, this.getBoundingBox()) && iworldreader.getCubes(this, this.getBoundingBox()) && !iworldreader.containsLiquid(this.getBoundingBox())) { + BlockPosition blockposition = new BlockPosition(this.locX, this.getBoundingBox().minY, this.locZ); + + if (blockposition.getY() < iworldreader.getSeaLevel()) { + return false; + } + + IBlockData iblockdata = iworldreader.getType(blockposition.down()); + Block block = iblockdata.getBlock(); + + if (block == Blocks.GRASS_BLOCK || iblockdata.a(TagsBlock.LEAVES)) { + return true; + } + } + + return false; + } + + public IChatBaseComponent getDisplayName() { + IChatBaseComponent ichatbasecomponent = this.getCustomName(); + + return (IChatBaseComponent) (ichatbasecomponent != null ? ichatbasecomponent : (this.isTamed() ? new ChatMessage(SystemUtils.a("entity", EntityOcelot.bI), new Object[0]) : super.getDisplayName())); + } + + protected void dz() { + if (this.bJ == null) { + this.bJ = new PathfinderGoalAvoidTarget<>(this, EntityHuman.class, 16.0F, 0.8D, 1.33D); + } + + this.goalSelector.a((PathfinderGoal) this.bJ); + if (!this.isTamed()) { + this.goalSelector.a(4, this.bJ); + } + + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + groupdataentity = super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + if (spawnBonus && this.getCatType() == 0 && this.world.random.nextInt(7) == 0) { // Spigot + for (int i = 0; i < 2; ++i) { + EntityOcelot entityocelot = EntityTypes.OCELOT.create(world); // Paper + + entityocelot.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, 0.0F); + entityocelot.setAgeRaw(-24000); + this.world.addEntity(entityocelot, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.OCELOT_BABY); // CraftBukkit - add SpawnReason + } + } + + return groupdataentity; + } +} diff --git a/src/main/java/net/minecraft/server/EntityPainting.java b/src/main/java/net/minecraft/server/EntityPainting.java new file mode 100644 index 000000000000..deceabf3fe49 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityPainting.java @@ -0,0 +1,98 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; + +public class EntityPainting extends EntityHanging { + + public Paintings art; + + public EntityPainting(World world) { + super(EntityTypes.PAINTING, world); + // CraftBukkit start - generate a non-null painting + List list = Lists.newArrayList(Paintings.a); + this.art = (Paintings) list.get(this.random.nextInt(list.size())); + // CraftBukkit end + } + + public EntityPainting(World world, BlockPosition blockposition, EnumDirection enumdirection) { + super(EntityTypes.PAINTING, world, blockposition); + List list = Lists.newArrayList(); + int i = 0; + Iterator iterator = IRegistry.MOTIVE.iterator(); + + Paintings paintings; + + while (iterator.hasNext()) { + paintings = (Paintings) iterator.next(); + this.art = paintings; + this.setDirection(enumdirection); + if (this.survives()) { + list.add(paintings); + int j = paintings.b() * paintings.c(); + + if (j > i) { + i = j; + } + } + } + + if (!list.isEmpty()) { + iterator = list.iterator(); + + while (iterator.hasNext()) { + paintings = (Paintings) iterator.next(); + if (paintings.b() * paintings.c() < i) { + iterator.remove(); + } + } + + this.art = (Paintings) list.get(this.random.nextInt(list.size())); + } + + this.setDirection(enumdirection); + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setString("Motive", IRegistry.MOTIVE.getKey(this.art).toString()); + super.b(nbttagcompound); + } + + public void a(NBTTagCompound nbttagcompound) { + this.art = (Paintings) IRegistry.MOTIVE.getOrDefault(MinecraftKey.a(nbttagcompound.getString("Motive"))); + super.a(nbttagcompound); + } + + public int getWidth() { + return this.art.b(); + } + + public int getHeight() { + return this.art.c(); + } + + public void a(@Nullable Entity entity) { + if (this.world.getGameRules().getBoolean("doEntityDrops")) { + this.a(SoundEffects.ENTITY_PAINTING_BREAK, 1.0F, 1.0F); + if (entity instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) entity; + + if (entityhuman.abilities.canInstantlyBuild) { + return; + } + } + + this.a((IMaterial) Items.PAINTING); + } + } + + public void m() { + this.a(SoundEffects.ENTITY_PAINTING_PLACE, 1.0F, 1.0F); + } + + public void setPositionRotation(double d0, double d1, double d2, float f, float f1) { + this.setPosition(d0, d1, d2); + } +} diff --git a/src/main/java/net/minecraft/server/EntityParrot.java b/src/main/java/net/minecraft/server/EntityParrot.java new file mode 100644 index 000000000000..6acad352071e --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityParrot.java @@ -0,0 +1,346 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public class EntityParrot extends EntityPerchable implements EntityBird { + + private static final DataWatcherObject bL = DataWatcher.a(EntityParrot.class, DataWatcherRegistry.b); + private static final Predicate bM = new Predicate() { + public boolean test(@Nullable EntityInsentient entityinsentient) { + return entityinsentient != null && EntityParrot.bP.containsKey(entityinsentient.P()); + } + }; + private static final Item bN = Items.COOKIE; + private static final Set bO = Sets.newHashSet(new Item[] { Items.WHEAT_SEEDS, Items.MELON_SEEDS, Items.PUMPKIN_SEEDS, Items.BEETROOT_SEEDS}); + private static final Map, SoundEffect> bP = (Map) SystemUtils.a(Maps.newHashMap(), (hashmap) -> { // CraftBukkit - decompile error + hashmap.put(EntityTypes.BLAZE, SoundEffects.ENTITY_PARROT_IMITATE_BLAZE); + hashmap.put(EntityTypes.CAVE_SPIDER, SoundEffects.ENTITY_PARROT_IMITATE_SPIDER); + hashmap.put(EntityTypes.CREEPER, SoundEffects.ENTITY_PARROT_IMITATE_CREEPER); + hashmap.put(EntityTypes.DROWNED, SoundEffects.ENTITY_PARROT_IMITATE_DROWNED); + hashmap.put(EntityTypes.ELDER_GUARDIAN, SoundEffects.ENTITY_PARROT_IMITATE_ELDER_GUARDIAN); + hashmap.put(EntityTypes.ENDER_DRAGON, SoundEffects.ENTITY_PARROT_IMITATE_ENDER_DRAGON); + hashmap.put(EntityTypes.ENDERMAN, SoundEffects.ENTITY_PARROT_IMITATE_ENDERMAN); + hashmap.put(EntityTypes.ENDERMITE, SoundEffects.ENTITY_PARROT_IMITATE_ENDERMITE); + hashmap.put(EntityTypes.EVOKER, SoundEffects.ENTITY_PARROT_IMITATE_EVOKER); + hashmap.put(EntityTypes.GHAST, SoundEffects.ENTITY_PARROT_IMITATE_GHAST); + hashmap.put(EntityTypes.HUSK, SoundEffects.ENTITY_PARROT_IMITATE_HUSK); + hashmap.put(EntityTypes.ILLUSIONER, SoundEffects.ENTITY_PARROT_IMITATE_ILLUSIONER); + hashmap.put(EntityTypes.MAGMA_CUBE, SoundEffects.ENTITY_PARROT_IMITATE_MAGMA_CUBE); + hashmap.put(EntityTypes.ZOMBIE_PIGMAN, SoundEffects.ENTITY_PARROT_IMITATE_ZOMBIE_PIGMAN); + hashmap.put(EntityTypes.PHANTOM, SoundEffects.ENTITY_PARROT_IMITATE_PHANTOM); + hashmap.put(EntityTypes.POLAR_BEAR, SoundEffects.ENTITY_PARROT_IMITATE_POLAR_BEAR); + hashmap.put(EntityTypes.SHULKER, SoundEffects.ENTITY_PARROT_IMITATE_SHULKER); + hashmap.put(EntityTypes.SILVERFISH, SoundEffects.ENTITY_PARROT_IMITATE_SILVERFISH); + hashmap.put(EntityTypes.SKELETON, SoundEffects.ENTITY_PARROT_IMITATE_SKELETON); + hashmap.put(EntityTypes.SLIME, SoundEffects.ENTITY_PARROT_IMITATE_SLIME); + hashmap.put(EntityTypes.SPIDER, SoundEffects.ENTITY_PARROT_IMITATE_SPIDER); + hashmap.put(EntityTypes.STRAY, SoundEffects.ENTITY_PARROT_IMITATE_STRAY); + hashmap.put(EntityTypes.VEX, SoundEffects.ENTITY_PARROT_IMITATE_VEX); + hashmap.put(EntityTypes.VINDICATOR, SoundEffects.ENTITY_PARROT_IMITATE_VINDICATOR); + hashmap.put(EntityTypes.WITCH, SoundEffects.ENTITY_PARROT_IMITATE_WITCH); + hashmap.put(EntityTypes.WITHER, SoundEffects.ENTITY_PARROT_IMITATE_WITHER); + hashmap.put(EntityTypes.WITHER_SKELETON, SoundEffects.ENTITY_PARROT_IMITATE_WITHER_SKELETON); + hashmap.put(EntityTypes.WOLF, SoundEffects.ENTITY_PARROT_IMITATE_WOLF); + hashmap.put(EntityTypes.ZOMBIE, SoundEffects.ENTITY_PARROT_IMITATE_ZOMBIE); + hashmap.put(EntityTypes.ZOMBIE_VILLAGER, SoundEffects.ENTITY_PARROT_IMITATE_ZOMBIE_VILLAGER); + }); + public float bG; + public float bH; + public float bI; + public float bJ; + public float bK = 1.0F; + private boolean bQ; + private BlockPosition bR; + + public EntityParrot(World world) { + super(EntityTypes.PARROT, world); + this.setSize(0.5F, 0.9F); + this.moveController = new ControllerMoveFlying(this); + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + this.setVariant(this.random.nextInt(5)); + return super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + } + + protected void n() { + this.goalSit = new PathfinderGoalSit(this); + this.goalSelector.a(0, new PathfinderGoalPanic(this, 1.25D)); + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(1, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(2, this.goalSit); + this.goalSelector.a(2, new PathfinderGoalFollowOwnerParrot(this, 1.0D, 5.0F, 1.0F)); + this.goalSelector.a(2, new PathfinderGoalRandomFly(this, 1.0D)); + this.goalSelector.a(3, new PathfinderGoalPerch(this)); + this.goalSelector.a(3, new PathfinderGoalFollowEntity(this, 1.0D, 3.0F, 7.0F)); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeMap().b(GenericAttributes.e); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(6.0D); + this.getAttributeInstance(GenericAttributes.e).setValue(0.4000000059604645D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.20000000298023224D); + } + + protected NavigationAbstract b(World world) { + NavigationFlying navigationflying = new NavigationFlying(this, world); + + navigationflying.a(false); + navigationflying.d(true); + navigationflying.b(true); + return navigationflying; + } + + public float getHeadHeight() { + return this.length * 0.6F; + } + + public void movementTick() { + b(this.world, (Entity) this); + if (this.bR == null || this.bR.distanceSquared(this.locX, this.locY, this.locZ) > 12.0D || this.world.getType(this.bR).getBlock() != Blocks.JUKEBOX) { + this.bQ = false; + this.bR = null; + } + + super.movementTick(); + this.dL(); + } + + private void dL() { + this.bJ = this.bG; + this.bI = this.bH; + this.bH = (float) ((double) this.bH + (double) (this.onGround ? -1 : 4) * 0.3D); + this.bH = MathHelper.a(this.bH, 0.0F, 1.0F); + if (!this.onGround && this.bK < 1.0F) { + this.bK = 1.0F; + } + + this.bK = (float) ((double) this.bK * 0.9D); + if (!this.onGround && this.motY < 0.0D) { + this.motY *= 0.6D; + } + + this.bG += this.bK * 2.0F; + } + + private static boolean b(World world, Entity entity) { + if (!entity.isSilent() && world.random.nextInt(50) == 0) { + List list = world.a(EntityInsentient.class, entity.getBoundingBox().g(20.0D), EntityParrot.bM); + + if (!list.isEmpty()) { + EntityInsentient entityinsentient = (EntityInsentient) list.get(world.random.nextInt(list.size())); + + if (!entityinsentient.isSilent()) { + SoundEffect soundeffect = a(entityinsentient.P()); + + world.a((EntityHuman) null, entity.locX, entity.locY, entity.locZ, soundeffect, entity.bV(), 0.7F, b(world.random)); + return true; + } + } + + return false; + } else { + return false; + } + } + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (!this.isTamed() && EntityParrot.bO.contains(itemstack.getItem())) { + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + if (!this.isSilent()) { + this.world.a((EntityHuman) null, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_PARROT_EAT, this.bV(), 1.0F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F); + } + + if (!this.world.isClientSide) { + if (this.random.nextInt(10) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, entityhuman).isCancelled()) { // CraftBukkit + this.c(entityhuman); + this.s(true); + this.world.broadcastEntityEffect(this, (byte) 7); + } else { + this.s(false); + this.world.broadcastEntityEffect(this, (byte) 6); + } + } + + return true; + } else if (itemstack.getItem() == EntityParrot.bN) { + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + this.addEffect(new MobEffect(MobEffects.POISON, 900), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.FOOD); // CraftBukkit + if (entityhuman.u() || !this.bl()) { + this.damageEntity(DamageSource.playerAttack(entityhuman), Float.MAX_VALUE); + } + + return true; + } else { + if (!this.world.isClientSide && !this.F_() && this.isTamed() && this.f((EntityLiving) entityhuman)) { + this.goalSit.setSitting(!this.isSitting()); + } + + return super.a(entityhuman, enumhand); + } + } + + public boolean f(ItemStack itemstack) { + return false; + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.getBoundingBox().minY); + int k = MathHelper.floor(this.locZ); + BlockPosition blockposition = new BlockPosition(i, j, k); + Block block = generatoraccess.getType(blockposition.down()).getBlock(); + + return block instanceof BlockLeaves || block == Blocks.GRASS || block instanceof BlockLogAbstract || block == Blocks.AIR && super.a(generatoraccess, flag); + } + + public void c(float f, float f1) {} + + protected void a(double d0, boolean flag, IBlockData iblockdata, BlockPosition blockposition) {} + + public boolean mate(EntityAnimal entityanimal) { + return false; + } + + @Nullable + public EntityAgeable createChild(EntityAgeable entityageable) { + return null; + } + + public static void a(World world, Entity entity) { + if (!entity.isSilent() && !b(world, entity) && world.random.nextInt(200) == 0) { + world.a((EntityHuman) null, entity.locX, entity.locY, entity.locZ, a(world.random), entity.bV(), 1.0F, b(world.random)); + } + + } + + public boolean B(Entity entity) { + return entity.damageEntity(DamageSource.mobAttack(this), 3.0F); + } + + @Nullable + public SoundEffect D() { + return a(this.random); + } + + private static SoundEffect a(Random random) { + if (random.nextInt(1000) == 0) { + List> list = Lists.newArrayList(EntityParrot.bP.keySet()); + + return a((EntityTypes) list.get(random.nextInt(list.size()))); + } else { + return SoundEffects.ENTITY_PARROT_AMBIENT; + } + } + + public static SoundEffect a(EntityTypes entitytypes) { + return (SoundEffect) EntityParrot.bP.getOrDefault(entitytypes, SoundEffects.ENTITY_PARROT_AMBIENT); + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_PARROT_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_PARROT_DEATH; + } + + protected void a(BlockPosition blockposition, IBlockData iblockdata) { + this.a(SoundEffects.ENTITY_PARROT_STEP, 0.15F, 1.0F); + } + + protected float e(float f) { + this.a(SoundEffects.ENTITY_PARROT_FLY, 0.15F, 1.0F); + return f + this.bH / 2.0F; + } + + protected boolean ah() { + return true; + } + + protected float cE() { + return b(this.random); + } + + private static float b(Random random) { + return (random.nextFloat() - random.nextFloat()) * 0.2F + 1.0F; + } + + public SoundCategory bV() { + return SoundCategory.NEUTRAL; + } + + public boolean isCollidable() { + return true; + } + + protected void C(Entity entity) { + if (!(entity instanceof EntityHuman)) { + super.C(entity); + } + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else { + if (this.goalSit != null) { + // CraftBukkit - moved into EntityLiving.d(DamageSource, float) + // this.goalSit.setSitting(false); + } + + return super.damageEntity(damagesource, f); + } + } + + public int getVariant() { + return MathHelper.clamp((Integer) this.datawatcher.get(EntityParrot.bL), 0, 4); + } + + public void setVariant(int i) { + this.datawatcher.set(EntityParrot.bL, i); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityParrot.bL, 0); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("Variant", this.getVariant()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setVariant(nbttagcompound.getInt("Variant")); + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.aE; + } + + public boolean F_() { + return !this.onGround; + } +} diff --git a/src/main/java/net/minecraft/server/EntityPhantom.java b/src/main/java/net/minecraft/server/EntityPhantom.java new file mode 100644 index 000000000000..f576264a8d07 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityPhantom.java @@ -0,0 +1,468 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; + +public class EntityPhantom extends EntityFlying implements IMonster { + + private static final DataWatcherObject a = DataWatcher.a(EntityPhantom.class, DataWatcherRegistry.b); + private Vec3D b; + private BlockPosition c; + private EntityPhantom.AttackPhase bC; + + public EntityPhantom(World world) { + super(EntityTypes.PHANTOM, world); + this.b = Vec3D.a; + this.c = BlockPosition.ZERO; + this.bC = EntityPhantom.AttackPhase.CIRCLE; + this.b_ = 5; + this.setSize(0.9F, 0.5F); + this.moveController = new EntityPhantom.g(this); + this.lookController = new EntityPhantom.f(this); + } + + protected EntityAIBodyControl o() { + return new EntityPhantom.d(this); + } + + protected void n() { + this.goalSelector.a(1, new EntityPhantom.c()); + this.goalSelector.a(2, new EntityPhantom.i()); + this.goalSelector.a(3, new EntityPhantom.e()); + this.targetSelector.a(1, new EntityPhantom.b()); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeMap().b(GenericAttributes.ATTACK_DAMAGE); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityPhantom.a, 0); + } + + public void setSize(int i) { + if (i < 0) { + i = 0; + } else if (i > 64) { + i = 64; + } + + this.datawatcher.set(EntityPhantom.a, i); + this.l(); + } + + public void l() { + int i = (Integer) this.datawatcher.get(EntityPhantom.a); + + this.setSize(0.9F + 0.2F * (float) i, 0.5F + 0.1F * (float) i); + this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue((double) (6 + i)); + } + + public int getSize() { + return (Integer) this.datawatcher.get(EntityPhantom.a); + } + + public float getHeadHeight() { + return this.length * 0.35F; + } + + public void a(DataWatcherObject datawatcherobject) { + if (EntityPhantom.a.equals(datawatcherobject)) { + this.l(); + } + + super.a(datawatcherobject); + } + + public void tick() { + super.tick(); + if (this.world.isClientSide) { + float f = MathHelper.cos((float) (this.getId() * 3 + this.ticksLived) * 0.13F + 3.1415927F); + float f1 = MathHelper.cos((float) (this.getId() * 3 + this.ticksLived + 1) * 0.13F + 3.1415927F); + + if (f > 0.0F && f1 <= 0.0F) { + this.world.a(this.locX, this.locY, this.locZ, SoundEffects.ENTITY_PHANTOM_FLAP, this.bV(), 0.95F + this.random.nextFloat() * 0.05F, 0.95F + this.random.nextFloat() * 0.05F, false); + } + + int i = this.getSize(); + float f2 = MathHelper.cos(this.yaw * 0.017453292F) * (1.3F + 0.21F * (float) i); + float f3 = MathHelper.sin(this.yaw * 0.017453292F) * (1.3F + 0.21F * (float) i); + float f4 = (0.3F + f * 0.45F) * ((float) i * 0.2F + 1.0F); + + this.world.addParticle(Particles.H, this.locX + (double) f2, this.locY + (double) f4, this.locZ + (double) f3, 0.0D, 0.0D, 0.0D); + this.world.addParticle(Particles.H, this.locX - (double) f2, this.locY + (double) f4, this.locZ - (double) f3, 0.0D, 0.0D, 0.0D); + } + + if (!this.world.isClientSide && this.world.getDifficulty() == EnumDifficulty.PEACEFUL) { + this.die(); + } + + } + + public void movementTick() { + if (this.dq()) { + this.setOnFire(8); + } + + super.movementTick(); + } + + protected void mobTick() { + super.mobTick(); + } + + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + this.c = (new BlockPosition(this)).up(5); + this.setSize(0); + return super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKey("AX")) { + this.c = new BlockPosition(nbttagcompound.getInt("AX"), nbttagcompound.getInt("AY"), nbttagcompound.getInt("AZ")); + } + + this.setSize(nbttagcompound.getInt("Size")); + // Paper start + if (nbttagcompound.hasUUID("Paper.SpawningEntity")) { + this.spawningEntity = nbttagcompound.getUUID("Paper.SpawningEntity"); + } + // Paper end + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("AX", this.c.getX()); + nbttagcompound.setInt("AY", this.c.getY()); + nbttagcompound.setInt("AZ", this.c.getZ()); + nbttagcompound.setInt("Size", this.getSize()); + // Paper start + if (this.spawningEntity != null) { + nbttagcompound.setUUID("Paper.SpawningEntity", this.spawningEntity); + } + // Paper end + } + + public SoundCategory bV() { + return SoundCategory.HOSTILE; + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_PHANTOM_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_PHANTOM_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_PHANTOM_DEATH; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.K; + } + + public EnumMonsterType getMonsterType() { + return EnumMonsterType.UNDEAD; + } + + protected float cD() { + return 1.0F; + } + + public boolean b(Class oclass) { + return true; + } + + // Paper start + java.util.UUID spawningEntity; + + public java.util.UUID getSpawningEntity() { + return spawningEntity; + } + // Paper end + + class b extends PathfinderGoal { + + private int b; + + private b() { + this.b = 20; + } + + public boolean a() { + if (this.b > 0) { + --this.b; + return false; + } else { + this.b = 60; + AxisAlignedBB axisalignedbb = EntityPhantom.this.getBoundingBox().grow(16.0D, 64.0D, 16.0D); + List list = EntityPhantom.this.world.a(EntityHuman.class, axisalignedbb); + + if (!list.isEmpty()) { + list.sort((entityhuman, entityhuman1) -> { + return entityhuman.locY > entityhuman1.locY ? -1 : 1; + }); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + if (PathfinderGoalTarget.a(EntityPhantom.this, entityhuman, false, false)) { + EntityPhantom.this.setGoalTarget(entityhuman, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER, true); // CraftBukkit - reason + return true; + } + } + } + + return false; + } + } + + public boolean b() { + return PathfinderGoalTarget.a(EntityPhantom.this, EntityPhantom.this.getGoalTarget(), false, false); + } + } + + class c extends PathfinderGoal { + + private int b; + + private c() {} + + public boolean a() { + return PathfinderGoalTarget.a(EntityPhantom.this, EntityPhantom.this.getGoalTarget(), false, false); + } + + public void c() { + this.b = 10; + EntityPhantom.this.bC = EntityPhantom.AttackPhase.CIRCLE; + this.g(); + } + + public void d() { + EntityPhantom.this.c = EntityPhantom.this.world.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING, EntityPhantom.this.c).up(10 + EntityPhantom.this.random.nextInt(20)); + } + + public void e() { + if (EntityPhantom.this.bC == EntityPhantom.AttackPhase.CIRCLE) { + --this.b; + if (this.b <= 0) { + EntityPhantom.this.bC = EntityPhantom.AttackPhase.SWOOP; + this.g(); + this.b = (8 + EntityPhantom.this.random.nextInt(4)) * 20; + EntityPhantom.this.a(SoundEffects.ENTITY_PHANTOM_SWOOP, 10.0F, 0.95F + EntityPhantom.this.random.nextFloat() * 0.1F); + } + } + + } + + private void g() { + EntityPhantom.this.c = (new BlockPosition(EntityPhantom.this.getGoalTarget())).up(20 + EntityPhantom.this.random.nextInt(20)); + if (EntityPhantom.this.c.getY() < EntityPhantom.this.world.getSeaLevel()) { + EntityPhantom.this.c = new BlockPosition(EntityPhantom.this.c.getX(), EntityPhantom.this.world.getSeaLevel() + 1, EntityPhantom.this.c.getZ()); + } + + } + } + + class i extends EntityPhantom.h { + + private i() { + super(); + } + + public boolean a() { + return EntityPhantom.this.getGoalTarget() != null && EntityPhantom.this.bC == EntityPhantom.AttackPhase.SWOOP; + } + + public boolean b() { + EntityLiving entityliving = EntityPhantom.this.getGoalTarget(); + + return entityliving == null ? false : (!entityliving.isAlive() ? false : (entityliving instanceof EntityHuman && (((EntityHuman) entityliving).isSpectator() || ((EntityHuman) entityliving).u()) ? false : this.a())); + } + + public void c() {} + + public void d() { + EntityPhantom.this.setGoalTarget((EntityLiving) null); + EntityPhantom.this.bC = EntityPhantom.AttackPhase.CIRCLE; + } + + public void e() { + EntityLiving entityliving = EntityPhantom.this.getGoalTarget(); + + EntityPhantom.this.b = new Vec3D(entityliving.locX, entityliving.locY + (double) entityliving.length * 0.5D, entityliving.locZ); + if (EntityPhantom.this.getBoundingBox().g(0.20000000298023224D).c(entityliving.getBoundingBox())) { + EntityPhantom.this.B(entityliving); + EntityPhantom.this.bC = EntityPhantom.AttackPhase.CIRCLE; + EntityPhantom.this.world.triggerEffect(1039, new BlockPosition(EntityPhantom.this), 0); + } else if (EntityPhantom.this.positionChanged || EntityPhantom.this.hurtTicks > 0) { + EntityPhantom.this.bC = EntityPhantom.AttackPhase.CIRCLE; + } + + } + } + + class e extends EntityPhantom.h { + + private float c; + private float d; + private float e; + private float f; + + private e() { + super(); + } + + public boolean a() { + return EntityPhantom.this.getGoalTarget() == null || EntityPhantom.this.bC == EntityPhantom.AttackPhase.CIRCLE; + } + + public void c() { + this.d = 5.0F + EntityPhantom.this.random.nextFloat() * 10.0F; + this.e = -4.0F + EntityPhantom.this.random.nextFloat() * 9.0F; + this.f = EntityPhantom.this.random.nextBoolean() ? 1.0F : -1.0F; + this.i(); + } + + public void e() { + if (EntityPhantom.this.random.nextInt(350) == 0) { + this.e = -4.0F + EntityPhantom.this.random.nextFloat() * 9.0F; + } + + if (EntityPhantom.this.random.nextInt(250) == 0) { + ++this.d; + if (this.d > 15.0F) { + this.d = 5.0F; + this.f = -this.f; + } + } + + if (EntityPhantom.this.random.nextInt(450) == 0) { + this.c = EntityPhantom.this.random.nextFloat() * 2.0F * 3.1415927F; + this.i(); + } + + if (this.g()) { + this.i(); + } + + if (EntityPhantom.this.b.y < EntityPhantom.this.locY && !EntityPhantom.this.world.isEmpty((new BlockPosition(EntityPhantom.this)).down(1))) { + this.e = Math.max(1.0F, this.e); + this.i(); + } + + if (EntityPhantom.this.b.y > EntityPhantom.this.locY && !EntityPhantom.this.world.isEmpty((new BlockPosition(EntityPhantom.this)).up(1))) { + this.e = Math.min(-1.0F, this.e); + this.i(); + } + + } + + private void i() { + if (BlockPosition.ZERO.equals(EntityPhantom.this.c)) { + EntityPhantom.this.c = new BlockPosition(EntityPhantom.this); + } + + this.c += this.f * 15.0F * 0.017453292F; + EntityPhantom.this.b = (new Vec3D(EntityPhantom.this.c)).add((double) (this.d * MathHelper.cos(this.c)), (double) (-4.0F + this.e), (double) (this.d * MathHelper.sin(this.c))); + } + } + + abstract class h extends PathfinderGoal { + + public h() { + this.a(1); + } + + protected boolean g() { + return EntityPhantom.this.b.c(EntityPhantom.this.locX, EntityPhantom.this.locY, EntityPhantom.this.locZ) < 4.0D; + } + } + + class f extends ControllerLook { + + public f(EntityInsentient entityinsentient) { + super(entityinsentient); + } + + public void a() {} + } + + class d extends EntityAIBodyControl { + + public d(EntityLiving entityliving) { + super(entityliving); + } + + public void a() { + EntityPhantom.this.aS = EntityPhantom.this.aQ; + EntityPhantom.this.aQ = EntityPhantom.this.yaw; + } + } + + class g extends ControllerMove { + + private float j = 0.1F; + + public g(EntityInsentient entityinsentient) { + super(entityinsentient); + } + + public void a() { + if (EntityPhantom.this.positionChanged) { + EntityPhantom.this.yaw += 180.0F; + this.j = 0.1F; + } + + float f = (float) (EntityPhantom.this.b.x - EntityPhantom.this.locX); + float f1 = (float) (EntityPhantom.this.b.y - EntityPhantom.this.locY); + float f2 = (float) (EntityPhantom.this.b.z - EntityPhantom.this.locZ); + double d0 = (double) MathHelper.c(f * f + f2 * f2); + double d1 = 1.0D - (double) MathHelper.e(f1 * 0.7F) / d0; + + f = (float) ((double) f * d1); + f2 = (float) ((double) f2 * d1); + d0 = (double) MathHelper.c(f * f + f2 * f2); + double d2 = (double) MathHelper.c(f * f + f2 * f2 + f1 * f1); + float f3 = EntityPhantom.this.yaw; + float f4 = (float) MathHelper.c((double) f2, (double) f); + float f5 = MathHelper.g(EntityPhantom.this.yaw + 90.0F); + float f6 = MathHelper.g(f4 * 57.295776F); + + EntityPhantom.this.yaw = MathHelper.c(f5, f6, 4.0F) - 90.0F; + EntityPhantom.this.aQ = EntityPhantom.this.yaw; + if (MathHelper.d(f3, EntityPhantom.this.yaw) < 3.0F) { + this.j = MathHelper.b(this.j, 1.8F, 0.005F * (1.8F / this.j)); + } else { + this.j = MathHelper.b(this.j, 0.2F, 0.025F); + } + + float f7 = (float) (-(MathHelper.c((double) (-f1), d0) * 57.2957763671875D)); + + EntityPhantom.this.pitch = f7; + float f8 = EntityPhantom.this.yaw + 90.0F; + double d3 = (double) (this.j * MathHelper.cos(f8 * 0.017453292F)) * Math.abs((double) f / d2); + double d4 = (double) (this.j * MathHelper.sin(f8 * 0.017453292F)) * Math.abs((double) f2 / d2); + double d5 = (double) (this.j * MathHelper.sin(f7 * 0.017453292F)) * Math.abs((double) f1 / d2); + + EntityPhantom.this.motX += (d3 - EntityPhantom.this.motX) * 0.2D; + EntityPhantom.this.motY += (d5 - EntityPhantom.this.motY) * 0.2D; + EntityPhantom.this.motZ += (d4 - EntityPhantom.this.motZ) * 0.2D; + } + } + + static enum AttackPhase { + + CIRCLE, SWOOP; + + private AttackPhase() {} + } +} diff --git a/src/main/java/net/minecraft/server/EntityPig.java b/src/main/java/net/minecraft/server/EntityPig.java new file mode 100644 index 000000000000..d1689dc33a38 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityPig.java @@ -0,0 +1,251 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityTransformEvent; +// CraftBukkit end + +public class EntityPig extends EntityAnimal { + + private static final DataWatcherObject bC = DataWatcher.a(EntityPig.class, DataWatcherRegistry.i); + private static final DataWatcherObject bD = DataWatcher.a(EntityPig.class, DataWatcherRegistry.b); + private static final RecipeItemStack bE = RecipeItemStack.a(Items.CARROT, Items.POTATO, Items.BEETROOT); + private boolean bG; + private int bH; + private int bI; + + public EntityPig(World world) { + super(EntityTypes.PIG, world); + this.setSize(0.9F, 0.9F); + } + + protected void n() { + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(1, new PathfinderGoalPanic(this, 1.25D)); + this.goalSelector.a(3, new PathfinderGoalBreed(this, 1.0D)); + this.goalSelector.a(4, new PathfinderGoalTempt(this, 1.2D, RecipeItemStack.a(Items.CARROT_ON_A_STICK), false)); + this.goalSelector.a(4, new PathfinderGoalTempt(this, 1.2D, false, EntityPig.bE)); + this.goalSelector.a(5, new PathfinderGoalFollowParent(this, 1.1D)); + this.goalSelector.a(6, new PathfinderGoalRandomStrollLand(this, 1.0D)); + this.goalSelector.a(7, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this)); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(10.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.25D); + } + + @Nullable + public Entity bO() { + return this.bP().isEmpty() ? null : (Entity) this.bP().get(0); + } + + public boolean dh() { + Entity entity = this.bO(); + + if (!(entity instanceof EntityHuman)) { + return false; + } else { + EntityHuman entityhuman = (EntityHuman) entity; + + return entityhuman.getItemInMainHand().getItem() == Items.CARROT_ON_A_STICK || entityhuman.getItemInOffHand().getItem() == Items.CARROT_ON_A_STICK; + } + } + + public void a(DataWatcherObject datawatcherobject) { + if (EntityPig.bD.equals(datawatcherobject) && this.world.isClientSide) { + this.bG = true; + this.bH = 0; + this.bI = (Integer) this.datawatcher.get(EntityPig.bD); + } + + super.a(datawatcherobject); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityPig.bC, false); + this.datawatcher.register(EntityPig.bD, 0); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("Saddle", this.hasSaddle()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setSaddle(nbttagcompound.getBoolean("Saddle")); + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_PIG_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_PIG_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_PIG_DEATH; + } + + protected void a(BlockPosition blockposition, IBlockData iblockdata) { + this.a(SoundEffects.ENTITY_PIG_STEP, 0.15F, 1.0F); + } + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + if (!super.a(entityhuman, enumhand)) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (itemstack.getItem() == Items.NAME_TAG) { + itemstack.a(entityhuman, (EntityLiving) this, enumhand); + return true; + } else if (this.hasSaddle() && !this.isVehicle()) { + if (!this.world.isClientSide) { + entityhuman.startRiding(this); + } + + return true; + } else if (itemstack.getItem() == Items.SADDLE) { + itemstack.a(entityhuman, (EntityLiving) this, enumhand); + return true; + } else { + return false; + } + } else { + return true; + } + } + + public void die(DamageSource damagesource) { + // super.die(damagesource); // CraftBukkit - Moved to end + if (!this.world.isClientSide) { + if (this.hasSaddle()) { + this.a((IMaterial) Items.SADDLE); + } + + } + super.die(damagesource); // CraftBukkit - Moved from above + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.L; + } + + public boolean hasSaddle() { + return (Boolean) this.datawatcher.get(EntityPig.bC); + } + + public void setSaddle(boolean flag) { + if (flag) { + this.datawatcher.set(EntityPig.bC, true); + } else { + this.datawatcher.set(EntityPig.bC, false); + } + + } + + public void onLightningStrike(EntityLightning entitylightning) { + if (!this.world.isClientSide && !this.dead) { + EntityPigZombie entitypigzombie = EntityTypes.ZOMBIE_PIGMAN.create(world); // Paper + + entitypigzombie.setSlot(EnumItemSlot.MAINHAND, new ItemStack(Items.GOLDEN_SWORD)); + entitypigzombie.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, this.pitch); + entitypigzombie.setNoAI(this.isNoAI()); + if (this.hasCustomName()) { + entitypigzombie.setCustomName(this.getCustomName()); + entitypigzombie.setCustomNameVisible(this.getCustomNameVisible()); + } + + // Paper start + if (CraftEventFactory.callEntityZapEvent(this, entitylightning, entitypigzombie).isCancelled()) { + return; + } + // Paper end + + // CraftBukkit start + if (CraftEventFactory.callPigZapEvent(this, entitylightning, entitypigzombie).isCancelled()) { + return; + } + // CraftBukkit - added a reason for spawning this creature + this.world.addEntity(entitypigzombie, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); + // CraftBukkit end + this.die(); + } + } + + public void a(float f, float f1, float f2) { + Entity entity = this.bP().isEmpty() ? null : (Entity) this.bP().get(0); + + if (this.isVehicle() && this.dh()) { + this.yaw = entity.yaw; + this.lastYaw = this.yaw; + this.pitch = entity.pitch * 0.5F; + this.setYawPitch(this.yaw, this.pitch); + this.aQ = this.yaw; + this.aS = this.yaw; + this.Q = 1.0F; + this.aU = this.cK() * 0.1F; + if (this.bG && this.bH++ > this.bI) { + this.bG = false; + } + + if (this.bT()) { + float f3 = (float) this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue() * 0.225F; + + if (this.bG) { + f3 += f3 * 1.15F * MathHelper.sin((float) this.bH / (float) this.bI * 3.1415927F); + } + + this.o(f3); + super.a(0.0F, 0.0F, 1.0F); + } else { + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + } + + this.aI = this.aJ; + double d0 = this.locX - this.lastX; + double d1 = this.locZ - this.lastZ; + float f4 = MathHelper.sqrt(d0 * d0 + d1 * d1) * 4.0F; + + if (f4 > 1.0F) { + f4 = 1.0F; + } + + this.aJ += (f4 - this.aJ) * 0.4F; + this.aK += this.aJ; + } else { + this.Q = 0.5F; + this.aU = 0.02F; + super.a(f, f1, f2); + } + } + + public boolean dz() { + if (this.bG) { + return false; + } else { + this.bG = true; + this.bH = 0; + this.bI = this.getRandom().nextInt(841) + 140; + this.getDataWatcher().set(EntityPig.bD, this.bI); + return true; + } + } + + public EntityPig createChild(EntityAgeable entityageable) { + return EntityTypes.PIG.create(world); // Paper + } + + public boolean f(ItemStack itemstack) { + return EntityPig.bE.test(itemstack); + } +} diff --git a/src/main/java/net/minecraft/server/EntityPigZombie.java b/src/main/java/net/minecraft/server/EntityPigZombie.java new file mode 100644 index 000000000000..56dab7667f50 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityPigZombie.java @@ -0,0 +1,206 @@ +package net.minecraft.server; + +import java.util.UUID; +import javax.annotation.Nullable; + +public class EntityPigZombie extends EntityZombie { + + private static final UUID a = UUID.fromString("49455A49-7EC5-45BA-B886-3B90B23A1718"); + private static final AttributeModifier b = (new AttributeModifier(EntityPigZombie.a, "Attacking speed boost", 0.05D, 0)).a(false); + public int angerLevel; + private int soundDelay; + private UUID hurtBy; + + public EntityPigZombie(World world) { + super(EntityTypes.ZOMBIE_PIGMAN, world); + this.fireProof = true; + } + + public void setLastDamager(@Nullable EntityLiving entityliving) { + super.setLastDamager(entityliving); + if (entityliving != null) { + this.hurtBy = entityliving.getUniqueID(); + } + + } + + protected void l() { + this.goalSelector.a(2, new PathfinderGoalZombieAttack(this, 1.0D, false)); + this.goalSelector.a(7, new PathfinderGoalRandomStrollLand(this, 1.0D)); + this.targetSelector.a(1, new EntityPigZombie.PathfinderGoalAngerOther(this)); + this.targetSelector.a(2, new EntityPigZombie.PathfinderGoalAnger(this)); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(EntityPigZombie.c).setValue(0.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.23000000417232513D); + this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue(5.0D); + } + + protected boolean dC() { + return false; + } + + protected void mobTick() { + AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED); + + if (this.dF()) { + if (!this.isBaby() && !attributeinstance.a(EntityPigZombie.b)) { + attributeinstance.b(EntityPigZombie.b); + } + + --this.angerLevel; + } else if (attributeinstance.a(EntityPigZombie.b)) { + attributeinstance.c(EntityPigZombie.b); + } + + if (this.soundDelay > 0 && --this.soundDelay == 0) { + this.a(SoundEffects.ENTITY_ZOMBIE_PIGMAN_ANGRY, this.cD() * 2.0F, ((this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F) * 1.8F); + } + + if (this.angerLevel > 0 && this.hurtBy != null && this.getLastDamager() == null) { + EntityHuman entityhuman = this.world.b(this.hurtBy); + + this.setLastDamager(entityhuman); + this.killer = entityhuman; + this.lastDamageByPlayerTime = this.cg(); + } + + super.mobTick(); + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + return generatoraccess.getDifficulty() != EnumDifficulty.PEACEFUL; + } + + public boolean a(IWorldReader iworldreader) { + return iworldreader.a_(this, this.getBoundingBox()) && iworldreader.getCubes(this, this.getBoundingBox()) && !iworldreader.containsLiquid(this.getBoundingBox()); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setShort("Anger", (short) this.angerLevel); + if (this.hurtBy != null) { + nbttagcompound.setString("HurtBy", this.hurtBy.toString()); + } else { + nbttagcompound.setString("HurtBy", ""); + } + + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.angerLevel = nbttagcompound.getShort("Anger"); + String s = nbttagcompound.getString("HurtBy"); + + if (!s.isEmpty()) { + this.hurtBy = UUID.fromString(s); + EntityHuman entityhuman = this.world.b(this.hurtBy); + + this.setLastDamager(entityhuman); + if (entityhuman != null) { + this.killer = entityhuman; + this.lastDamageByPlayerTime = this.cg(); + } + } + + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else { + Entity entity = damagesource.getEntity(); + + // CraftBukkit start + boolean result = super.damageEntity(damagesource, f); + + if (result && entity instanceof EntityHuman && !((EntityHuman) entity).u()) { + this.a(entity); + } + + return result; + // CraftBukkit end + } + } + + private void a(Entity entity) { + // CraftBukkit start + org.bukkit.event.entity.PigZombieAngerEvent event = new org.bukkit.event.entity.PigZombieAngerEvent((org.bukkit.entity.PigZombie) this.getBukkitEntity(), (entity == null) ? null : entity.getBukkitEntity(), 400 + this.random.nextInt(400)); + this.world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + this.angerLevel = event.getNewAnger(); + // CraftBukkit end + this.soundDelay = this.random.nextInt(40); + if (entity instanceof EntityLiving) { + this.setLastDamager((EntityLiving) entity); + } + + } + + public boolean dF() { + return this.angerLevel > 0; + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_ZOMBIE_PIGMAN_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_ZOMBIE_PIGMAN_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_ZOMBIE_PIGMAN_DEATH; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.au; + } + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + return false; + } + + protected void a(DifficultyDamageScaler difficultydamagescaler) { + this.setSlot(EnumItemSlot.MAINHAND, new ItemStack(Items.GOLDEN_SWORD)); + } + + protected ItemStack dB() { + return ItemStack.a; + } + + public boolean c(EntityHuman entityhuman) { + return this.dF(); + } + + static class PathfinderGoalAnger extends PathfinderGoalNearestAttackableTarget { + + public PathfinderGoalAnger(EntityPigZombie entitypigzombie) { + super(entitypigzombie, EntityHuman.class, true); + } + + public boolean a() { + return ((EntityPigZombie) this.e).dF() && super.a(); + } + } + + static class PathfinderGoalAngerOther extends PathfinderGoalHurtByTarget { + + public PathfinderGoalAngerOther(EntityPigZombie entitypigzombie) { + super(entitypigzombie, true); + } + + protected void a(EntityCreature entitycreature, EntityLiving entityliving) { + super.a(entitycreature, entityliving); + if (entitycreature instanceof EntityPigZombie) { + ((EntityPigZombie) entitycreature).a((Entity) entityliving); + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java new file mode 100644 index 000000000000..010749ddc37a --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -0,0 +1,1670 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.mojang.authlib.GameProfile; +import io.netty.buffer.Unpooled; +import io.netty.util.concurrent.Future; +import java.util.ArrayDeque; // Paper +import java.util.Collection; +import java.util.Deque; // Paper +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import com.google.common.base.Preconditions; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.WeatherType; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.player.PlayerChangedMainHandEvent; +import org.bukkit.event.player.PlayerGameModeChangeEvent; +import org.bukkit.event.player.PlayerLocaleChangeEvent; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.inventory.MainHand; +// CraftBukkit end + +public class EntityPlayer extends EntityHuman implements ICrafting { + + private static final Logger cc = LogManager.getLogger(); + public String locale = null; // CraftBukkit - lowercase // Paper - default to null + public long lastSave = MinecraftServer.currentTick; // Paper + public PlayerConnection playerConnection; + public final MinecraftServer server; + public final PlayerInteractManager playerInteractManager; + public double d; + public double e; + public final Deque removeQueue = new ArrayDeque<>(); // Paper + private final AdvancementDataPlayer cf; + private final ServerStatisticManager cg; + private float ch = Float.MIN_VALUE; + private int ci = Integer.MIN_VALUE; + private int cj = Integer.MIN_VALUE; + private int ck = Integer.MIN_VALUE; + private int cl = Integer.MIN_VALUE; + private int cm = Integer.MIN_VALUE; + private float lastHealthSent = -1.0E8F; + private int lastFoodSent = -99999999; + private boolean cp = true; + public int lastSentExp = -99999999; + public int invulnerableTicks = 60; + private EntityHuman.EnumChatVisibility cs; + private boolean ct = true; + private long cu = SystemUtils.getMonotonicMillis(); + private Entity spectatedEntity; private void setSpectatorTargetField(Entity e) { this.spectatedEntity = e; } // Paper - OBFHELPER + public boolean worldChangeInvuln; + private boolean cx; private void setHasSeenCredits(boolean has) { this.cx = has; } // Paper - OBFHELPER + private final RecipeBookServer recipeBook; + private Vec3D cz; + private int cA; + private boolean cB; + private Vec3D cC; + private int containerCounter; + public boolean f; + public int ping; + public boolean viewingCredits; + private int containerUpdateDelay; // Paper + public long loginTime; // Paper + // Paper start - cancellable death event + public boolean queueHealthUpdatePacket = false; + public net.minecraft.server.PacketPlayOutUpdateHealth queuedHealthUpdatePacket; + // Paper end + + // CraftBukkit start + public String displayName; + public IChatBaseComponent listName; + public org.bukkit.Location compassTarget; + public int newExp = 0; + public int newLevel = 0; + public int newTotalExp = 0; + public boolean keepLevel = false; + public double maxHealthCache; + public boolean joining = true; + public boolean sentListPacket = false; + public Integer clientViewDistance; + // CraftBukkit end + + public EntityPlayer(MinecraftServer minecraftserver, WorldServer worldserver, GameProfile gameprofile, PlayerInteractManager playerinteractmanager) { + super((World) worldserver, gameprofile); + playerinteractmanager.player = this; + this.playerInteractManager = playerinteractmanager; + this.server = minecraftserver; + this.recipeBook = new RecipeBookServer(minecraftserver.getCraftingManager()); + this.cg = minecraftserver.getPlayerList().getStatisticManager(this); + this.cf = minecraftserver.getPlayerList().h(this); + this.Q = 1.0F; + this.a(worldserver); + + // CraftBukkit start + this.displayName = this.getName(); + this.canPickUpLoot = true; + this.maxHealthCache = this.getMaxHealth(); + } + + // Yes, this doesn't match Vanilla, but it's the best we can do for now. + // If this is an issue, PRs are welcome + public final BlockPosition getSpawnPoint(WorldServer worldserver) { + BlockPosition blockposition = worldserver.getSpawn(); + + if (worldserver.worldProvider.g() && worldserver.getWorldData().getGameType() != EnumGamemode.ADVENTURE) { + int i = Math.max(0, this.server.a(worldserver)); + int j = MathHelper.floor(worldserver.getWorldBorder().b((double) blockposition.getX(), (double) blockposition.getZ())); + + if (j < i) { + i = j; + } + + if (j <= 1) { + i = 1; + } + + int k = (i * 2 + 1) * (i * 2 + 1); + int l = this.r(k); + int i1 = (new Random()).nextInt(k); + + for (int j1 = 0; j1 < k; ++j1) { + int k1 = (i1 + l * j1) % k; + int l1 = k1 % (i * 2 + 1); + int i2 = k1 / (i * 2 + 1); + BlockPosition blockposition1 = worldserver.o().a(blockposition.getX() + l1 - i, blockposition.getZ() + i2 - i, false); + + if (blockposition1 != null) { + return blockposition1; + } + } + } + + return blockposition; + } + // CraftBukkit end + + private void a(WorldServer worldserver) { + BlockPosition blockposition = worldserver.getSpawn(); + + if (worldserver.worldProvider.g() && worldserver.getWorldData().getGameType() != EnumGamemode.ADVENTURE) { + int i = Math.max(0, this.server.a(worldserver)); + int j = MathHelper.floor(worldserver.getWorldBorder().b((double) blockposition.getX(), (double) blockposition.getZ())); + + if (j < i) { + i = j; + } + + if (j <= 1) { + i = 1; + } + + int k = (i * 2 + 1) * (i * 2 + 1); + int l = this.r(k); + int i1 = (new Random()).nextInt(k); + + for (int j1 = 0; j1 < k; ++j1) { + int k1 = (i1 + l * j1) % k; + int l1 = k1 % (i * 2 + 1); + int i2 = k1 / (i * 2 + 1); + BlockPosition blockposition1 = worldserver.o().a(blockposition.getX() + l1 - i, blockposition.getZ() + i2 - i, false); + + if (blockposition1 != null) { + this.setPositionRotation(blockposition1, 0.0F, 0.0F); + if (worldserver.getCubes(this, this.getBoundingBox())) { + break; + } + } + } + } else { + this.setPositionRotation(blockposition, 0.0F, 0.0F); + + while (!worldserver.getCubes(this, this.getBoundingBox()) && this.locY < 255.0D) { + this.setPosition(this.locX, this.locY + 1.0D, this.locZ); + } + } + + } + + private int r(int i) { + return i <= 16 ? i - 1 : 17; + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (this.locY > 300) this.locY = 257; // Paper - bring down to a saner Y level if out of world + if (nbttagcompound.hasKeyOfType("playerGameType", 99)) { + if (this.bK().getForceGamemode()) { + this.playerInteractManager.setGameMode(this.bK().getGamemode()); + } else { + this.playerInteractManager.setGameMode(EnumGamemode.getById(nbttagcompound.getInt("playerGameType"))); + } + } + + if (nbttagcompound.hasKeyOfType("enteredNetherPosition", 10)) { + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("enteredNetherPosition"); + + this.cC = new Vec3D(nbttagcompound1.getDouble("x"), nbttagcompound1.getDouble("y"), nbttagcompound1.getDouble("z")); + } + + this.cx = nbttagcompound.getBoolean("seenCredits"); + if (nbttagcompound.hasKeyOfType("recipeBook", 10)) { + this.recipeBook.a(nbttagcompound.getCompound("recipeBook")); + } + this.getBukkitEntity().readExtraData(nbttagcompound); // CraftBukkit + + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("playerGameType", this.playerInteractManager.getGameMode().getId()); + nbttagcompound.setBoolean("seenCredits", this.cx); + if (this.cC != null) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.setDouble("x", this.cC.x); + nbttagcompound1.setDouble("y", this.cC.y); + nbttagcompound1.setDouble("z", this.cC.z); + nbttagcompound.set("enteredNetherPosition", nbttagcompound1); + } + + Entity entity = this.getRootVehicle(); + Entity entity1 = this.getVehicle(); + + // CraftBukkit start - handle non-persistent vehicles + boolean persistVehicle = true; + if (entity1 != null) { + Entity vehicle; + for (vehicle = entity1; vehicle != null; vehicle = vehicle.getVehicle()) { + if (!vehicle.persist) { + persistVehicle = false; + break; + } + } + } + + if (persistVehicle && entity1 != null && entity != this && entity.bR()) { + // CraftBukkit end + NBTTagCompound nbttagcompound2 = new NBTTagCompound(); + NBTTagCompound nbttagcompound3 = new NBTTagCompound(); + + entity.d(nbttagcompound3); + nbttagcompound2.a("Attach", entity1.getUniqueID()); + nbttagcompound2.set("Entity", nbttagcompound3); + nbttagcompound.set("RootVehicle", nbttagcompound2); + } + + nbttagcompound.set("recipeBook", this.recipeBook.e()); + this.getBukkitEntity().setExtraData(nbttagcompound); // CraftBukkit + } + + // CraftBukkit start - World fallback code, either respawn location or global spawn + public void spawnIn(World world) { + super.spawnIn(world); + if (world == null) { + this.dead = false; + BlockPosition position = null; + if (this.spawnWorld != null && !this.spawnWorld.equals("")) { + CraftWorld cworld = (CraftWorld) Bukkit.getServer().getWorld(this.spawnWorld); + if (cworld != null && this.getBed() != null) { + world = cworld.getHandle(); + position = EntityHuman.getBed(cworld.getHandle(), this.getBed(), false); + } + } + if (world == null || position == null) { + world = ((CraftWorld) Bukkit.getServer().getWorlds().get(0)).getHandle(); + position = world.getSpawn(); + } + this.world = world; + this.setPosition(position.getX() + 0.5, position.getY(), position.getZ() + 0.5); + } + this.dimension = ((WorldServer) this.world).dimension; + this.playerInteractManager.a((WorldServer) world); + } + // CraftBukkit end + + public void a(int i) { + float f = (float) this.getExpToLevel(); + float f1 = (f - 1.0F) / f; + + this.exp = MathHelper.a((float) i / f, 0.0F, f1); + this.lastSentExp = -1; + } + + public void b(int i) { + this.expLevel = i; + this.lastSentExp = -1; + } + + public void levelDown(int i) { + super.levelDown(i); + this.lastSentExp = -1; + } + + public void enchantDone(ItemStack itemstack, int i) { + super.enchantDone(itemstack, i); + this.lastSentExp = -1; + } + + public void syncInventory() { + this.activeContainer.addSlotListener(this); + } + + public void enterCombat() { + super.enterCombat(); + this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTER_COMBAT)); + } + + public void exitCombat() { + super.exitCombat(); + this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.END_COMBAT)); + } + + protected void a(IBlockData iblockdata) { + CriterionTriggers.d.a(this, iblockdata); + } + + protected ItemCooldown g() { + return new ItemCooldownPlayer(this); + } + + public void tick() { + // CraftBukkit start + if (this.joining) { + this.joining = false; + } + // CraftBukkit end + this.playerInteractManager.a(); + --this.invulnerableTicks; + if (this.noDamageTicks > 0) { + --this.noDamageTicks; + } + + // Paper start - Configurable container update tick rate + if (--containerUpdateDelay <= 0) { + this.activeContainer.b(); + containerUpdateDelay = world.paperConfig.containerUpdateTickRate; + } + // Paper end + if (!this.world.isClientSide && !this.activeContainer.canUse(this)) { + this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.CANT_USE); // Paper + this.activeContainer = this.defaultContainer; + } + + while (!this.removeQueue.isEmpty()) { + int i = Math.min(this.removeQueue.size(), Integer.MAX_VALUE); + int[] aint = new int[i]; + //Iterator iterator = this.removeQueue.iterator(); // Paper + int j = 0; + + // Paper start + /* while (iterator.hasNext() && j < i) { + aint[j++] = (Integer) iterator.next(); + iterator.remove(); + } */ + + Integer integer; + while (j < i && (integer = this.removeQueue.poll()) != null) { + aint[j++] = integer.intValue(); + } + // Paper end + + this.playerConnection.sendPacket(new PacketPlayOutEntityDestroy(aint)); + } + + Entity entity = this.getSpecatorTarget(); + + if (entity != this) { + if (entity.isAlive()) { + this.setLocation(entity.locX, entity.locY, entity.locZ, entity.yaw, entity.pitch); + this.server.getPlayerList().updateChunks(this); + if (this.isSneaking()) { + this.setSpectatorTarget(this); + } + } else { + this.setSpectatorTarget(this); + } + } + + CriterionTriggers.w.a(this); + if (this.cz != null) { + CriterionTriggers.u.a(this, this.cz, this.ticksLived - this.cA); + } + + this.cf.b(this); + } + + public void playerTick() { + try { + super.tick(); + + for (int i = 0; i < this.inventory.getSize(); ++i) { + ItemStack itemstack = this.inventory.getItem(i); + + if (itemstack.getItem().W_()) { + Packet packet = ((ItemWorldMapBase) itemstack.getItem()).a(itemstack, this.world, (EntityHuman) this); + + if (packet != null) { + this.playerConnection.sendPacket(packet); + } + } + } + + if (this.getHealth() != this.lastHealthSent || this.lastFoodSent != this.foodData.getFoodLevel() || this.foodData.getSaturationLevel() == 0.0F != this.cp) { + this.playerConnection.sendPacket(new PacketPlayOutUpdateHealth(this.getBukkitEntity().getScaledHealth(), this.foodData.getFoodLevel(), this.foodData.getSaturationLevel())); // CraftBukkit + this.lastHealthSent = this.getHealth(); + this.lastFoodSent = this.foodData.getFoodLevel(); + this.cp = this.foodData.getSaturationLevel() == 0.0F; + } + + if (this.getHealth() + this.getAbsorptionHearts() != this.ch) { + this.ch = this.getHealth() + this.getAbsorptionHearts(); + this.a(IScoreboardCriteria.HEALTH, MathHelper.f(this.ch)); + } + + if (this.foodData.getFoodLevel() != this.ci) { + this.ci = this.foodData.getFoodLevel(); + this.a(IScoreboardCriteria.FOOD, MathHelper.f((float) this.ci)); + } + + if (this.getAirTicks() != this.cj) { + this.cj = this.getAirTicks(); + this.a(IScoreboardCriteria.AIR, MathHelper.f((float) this.cj)); + } + + if (this.getArmorStrength() != this.ck) { + this.ck = this.getArmorStrength(); + this.a(IScoreboardCriteria.ARMOR, MathHelper.f((float) this.ck)); + } + + if (this.expTotal != this.cm) { + this.cm = this.expTotal; + this.a(IScoreboardCriteria.XP, MathHelper.f((float) this.cm)); + } + + // CraftBukkit start - Force max health updates + if (this.maxHealthCache != this.getMaxHealth()) { + this.getBukkitEntity().updateScaledHealth(); + } + // CraftBukkit end + + if (this.expLevel != this.cl) { + this.cl = this.expLevel; + this.a(IScoreboardCriteria.LEVEL, MathHelper.f((float) this.cl)); + } + + if (this.expTotal != this.lastSentExp) { + this.lastSentExp = this.expTotal; + this.playerConnection.sendPacket(new PacketPlayOutExperience(this.exp, this.expTotal, this.expLevel)); + } + + if (this.ticksLived % 20 == 0) { + CriterionTriggers.p.a(this); + } + + // CraftBukkit start - initialize oldLevel and fire PlayerLevelChangeEvent + if (this.oldLevel == -1) { + this.oldLevel = this.expLevel; + } + + if (this.oldLevel != this.expLevel) { + CraftEventFactory.callPlayerLevelChangeEvent(this.world.getServer().getPlayer((EntityPlayer) this), this.oldLevel, this.expLevel); + this.oldLevel = this.expLevel; + } + // CraftBukkit end + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Ticking player"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Player being ticked"); + + this.appendEntityCrashDetails(crashreportsystemdetails); + throw new ReportedException(crashreport); + } + } + + private void a(IScoreboardCriteria iscoreboardcriteria, int i) { + // CraftBukkit - Use our scores instead + this.world.getServer().getScoreboardManager().getScoreboardScores(iscoreboardcriteria, this.getName(), (scoreboardscore) -> { + scoreboardscore.setScore(i); + }); + } + + public void die(DamageSource damagesource) { + boolean flag = this.world.getGameRules().getBoolean("showDeathMessages"); + // CraftBukkit start - fire PlayerDeathEvent + if (this.dead) { + return; + } + java.util.List loot = new java.util.ArrayList(this.inventory.getSize()); + boolean keepInventory = this.world.getGameRules().getBoolean("keepInventory") || this.isSpectator(); + + if (!keepInventory) { + for (ItemStack item : this.inventory.getContents()) { + if (!item.isEmpty() && !EnchantmentManager.shouldNotDrop(item)) { + loot.add(CraftItemStack.asCraftMirror(item)); + } + } + } + + IChatBaseComponent defaultMessage = this.getCombatTracker().getDeathMessage(); + + String deathmessage = defaultMessage.getString(); + org.bukkit.event.entity.PlayerDeathEvent event = CraftEventFactory.callPlayerDeathEvent(this, loot, deathmessage, keepInventory); + // Paper start - cancellable death event + if (event.isCancelled()) { + // make compatible with plugins that might have already set the health in an event listener + if (this.getHealth() <= 0) { + this.setHealth((float) event.getReviveHealth()); + } + return; + } + // Paper end + + // SPIGOT-943 - only call if they have an inventory open + if (this.activeContainer != this.defaultContainer) { + this.closeInventory(); + } + + String deathMessage = event.getDeathMessage(); + + if (deathMessage != null && deathMessage.length() > 0 && flag) { // TODO: allow plugins to override? + IChatBaseComponent ichatbasecomponent; + if (deathMessage.equals(deathmessage)) { + ichatbasecomponent = this.getCombatTracker().getDeathMessage(); + } else { + ichatbasecomponent = org.bukkit.craftbukkit.util.CraftChatMessage.fromStringOrNull(deathMessage); + } + + this.playerConnection.a((Packet) (new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, ichatbasecomponent)), (future) -> { + if (!future.isSuccess()) { + boolean flag1 = true; + String s = ichatbasecomponent.a(256); + ChatMessage chatmessage = new ChatMessage("death.attack.message_too_long", new Object[] { (new ChatComponentText(s)).a(EnumChatFormat.YELLOW)}); + IChatBaseComponent ichatbasecomponent1 = (new ChatMessage("death.attack.even_more_magic", new Object[] { this.getScoreboardDisplayName()})).a((chatmodifier) -> { + chatmodifier.setChatHoverable(new ChatHoverable(ChatHoverable.EnumHoverAction.SHOW_TEXT, chatmessage)); + }); + + this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, ichatbasecomponent1)); + } + + }); + ScoreboardTeamBase scoreboardteambase = this.getScoreboardTeam(); + + if (scoreboardteambase != null && scoreboardteambase.getDeathMessageVisibility() != ScoreboardTeamBase.EnumNameTagVisibility.ALWAYS) { + if (scoreboardteambase.getDeathMessageVisibility() == ScoreboardTeamBase.EnumNameTagVisibility.HIDE_FOR_OTHER_TEAMS) { + this.server.getPlayerList().a((EntityHuman) this, ichatbasecomponent); + } else if (scoreboardteambase.getDeathMessageVisibility() == ScoreboardTeamBase.EnumNameTagVisibility.HIDE_FOR_OWN_TEAM) { + this.server.getPlayerList().b((EntityHuman) this, ichatbasecomponent); + } + } else { + this.server.getPlayerList().sendMessage(ichatbasecomponent); + } + } else { + this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED)); + } + + this.releaseShoulderEntities(); + // we clean the player's inventory after the EntityDeathEvent is called so plugins can get the exact state of the inventory. + if (!event.getKeepInventory()) { + this.inventory.clear(); + } + this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.DEATH); // Paper + this.setSpectatorTarget(this); // Remove spectated target + // CraftBukkit end + + // CraftBukkit - Get our scores instead + this.world.getServer().getScoreboardManager().getScoreboardScores(IScoreboardCriteria.DEATH_COUNT, this.getName(), ScoreboardScore::incrementScore); + EntityLiving entityliving = this.cv(); + + if (entityliving != null) { + this.b(StatisticList.ENTITY_KILLED_BY.b(entityliving.P())); + entityliving.a(this, this.be, damagesource); + } + + this.a(StatisticList.DEATHS); + this.a(StatisticList.CUSTOM.b(StatisticList.TIME_SINCE_DEATH)); + this.a(StatisticList.CUSTOM.b(StatisticList.TIME_SINCE_REST)); + this.extinguish(); + this.setFlag(0, false); + this.getCombatTracker().g(); + } + + public void a(Entity entity, int i, DamageSource damagesource) { + if (entity != this) { + super.a(entity, i, damagesource); + this.addScore(i); + String s = this.getName(); + String s1 = entity.getName(); + + // CraftBukkit - Get our scores instead + this.world.getServer().getScoreboardManager().getScoreboardScores(IScoreboardCriteria.TOTAL_KILL_COUNT, s, ScoreboardScore::incrementScore); + if (entity instanceof EntityHuman) { + this.a(StatisticList.PLAYER_KILLS); + // CraftBukkit - Get our scores instead + this.world.getServer().getScoreboardManager().getScoreboardScores(IScoreboardCriteria.PLAYER_KILL_COUNT, s, ScoreboardScore::incrementScore); + } else { + this.a(StatisticList.MOB_KILLS); + } + + this.a(s, s1, IScoreboardCriteria.m); + this.a(s1, s, IScoreboardCriteria.n); + CriterionTriggers.b.a(this, entity, damagesource); + } + } + + private void a(String s, String s1, IScoreboardCriteria[] aiscoreboardcriteria) { + ScoreboardTeam scoreboardteam = this.getScoreboard().getPlayerTeam(s1); + + if (scoreboardteam != null) { + int i = scoreboardteam.getColor().b(); + + if (i >= 0 && i < aiscoreboardcriteria.length) { + // CraftBukkit - Get our scores instead + this.world.getServer().getScoreboardManager().getScoreboardScores(aiscoreboardcriteria[i], s, ScoreboardScore::incrementScore); + } + } + + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else { + boolean flag = this.server.Q() && this.canPvP() && "fall".equals(damagesource.translationIndex); + + if (!flag && this.invulnerableTicks > 0 && damagesource != DamageSource.OUT_OF_WORLD) { + return false; + } else { + if (damagesource instanceof EntityDamageSource) { + Entity entity = damagesource.getEntity(); + + if (entity instanceof EntityHuman && !this.a((EntityHuman) entity)) { + return false; + } + + if (entity instanceof EntityArrow) { + EntityArrow entityarrow = (EntityArrow) entity; + Entity entity1 = entityarrow.getShooter(); + + if (entity1 instanceof EntityHuman && !this.a((EntityHuman) entity1)) { + return false; + } + } + } + // Paper start - cancellable death events + //return super.damageEntity(damagesource, f); + this.queueHealthUpdatePacket = true; + boolean damaged = super.damageEntity(damagesource, f); + this.queueHealthUpdatePacket = false; + if (this.queuedHealthUpdatePacket != null) { + this.playerConnection.sendPacket(this.queuedHealthUpdatePacket); + this.queuedHealthUpdatePacket = null; + } + return damaged; + // Paper end + } + } + } + + public boolean a(EntityHuman entityhuman) { + return !this.canPvP() ? false : super.a(entityhuman); + } + + private boolean canPvP() { + // CraftBukkit - this.server.getPvP() -> this.world.pvpMode + return this.world.pvpMode; + } + + @Nullable + public Entity a(DimensionManager dimensionmanager) { + if (this.isSleeping()) return this; // CraftBukkit - SPIGOT-3154 + // this.worldChangeInvuln = true; // CraftBukkit - Moved down and into PlayerList#changeDimension + if (this.dimension == DimensionManager.OVERWORLD && dimensionmanager == DimensionManager.NETHER) { + this.cC = new Vec3D(this.locX, this.locY, this.locZ); + } else if (this.dimension != DimensionManager.NETHER && dimensionmanager != DimensionManager.OVERWORLD) { + this.cC = null; + } + + if (this.dimension == DimensionManager.THE_END && dimensionmanager == DimensionManager.THE_END) { + this.worldChangeInvuln = true; // CraftBukkit - Moved down from above + this.world.kill(this); + if (!this.viewingCredits) { + this.viewingCredits = true; + if (world.paperConfig.disableEndCredits) this.setHasSeenCredits(true); // Paper - Toggle to always disable end credits + this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(4, this.cx ? 0.0F : 1.0F)); + this.cx = true; + } + + return this; + } else { + if (this.dimension == DimensionManager.OVERWORLD && dimensionmanager == DimensionManager.THE_END) { + dimensionmanager = DimensionManager.THE_END; + } + + // CraftBukkit start + TeleportCause cause = (this.dimension == DimensionManager.THE_END || dimensionmanager == DimensionManager.THE_END) ? TeleportCause.END_PORTAL : TeleportCause.NETHER_PORTAL; + this.server.getPlayerList().changeDimension(this, dimensionmanager, cause); // PAIL: check all this + // CraftBukkit end + this.playerConnection.sendPacket(new PacketPlayOutWorldEvent(1032, BlockPosition.ZERO, 0, false)); + this.lastSentExp = -1; + this.lastHealthSent = -1.0F; + this.lastFoodSent = -1; + return this; + } + } + + public boolean a(EntityPlayer entityplayer) { + return entityplayer.isSpectator() ? this.getSpecatorTarget() == this : (this.isSpectator() ? false : super.a(entityplayer)); + } + + private void a(TileEntity tileentity) { + if (tileentity != null) { + PacketPlayOutTileEntityData packetplayouttileentitydata = tileentity.getUpdatePacket(); + + if (packetplayouttileentitydata != null) { + this.playerConnection.sendPacket(packetplayouttileentitydata); + } + } + + } + + public void receive(Entity entity, int i) { + super.receive(entity, i); + this.activeContainer.b(); + } + + public EntityHuman.EnumBedResult a(BlockPosition blockposition) { + EntityHuman.EnumBedResult entityhuman_enumbedresult = super.a(blockposition); + + if (entityhuman_enumbedresult == EntityHuman.EnumBedResult.OK) { + this.a(StatisticList.SLEEP_IN_BED); + Packet packet = new PacketPlayOutBed(this, blockposition); + + this.getWorldServer().getTracker().a((Entity) this, (Packet) packet); + this.playerConnection.a(this.locX, this.locY, this.locZ, this.yaw, this.pitch); + this.playerConnection.sendPacket(packet); + CriterionTriggers.q.a(this); + } + + return entityhuman_enumbedresult; + } + + public void a(boolean flag, boolean flag1, boolean flag2) { + if (!this.sleeping) return; // CraftBukkit - Can't leave bed if not in one! + if (this.isSleeping()) { + this.getWorldServer().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(this, 2)); + } + + super.a(flag, flag1, flag2); + if (this.playerConnection != null) { + this.playerConnection.a(this.locX, this.locY, this.locZ, this.yaw, this.pitch); + } + + } + + public boolean a(Entity entity, boolean flag) { + Entity entity1 = this.getVehicle(); + + if (!super.a(entity, flag)) { + return false; + } else { + Entity entity2 = this.getVehicle(); + + if (entity2 != entity1 && this.playerConnection != null) { + this.playerConnection.a(this.locX, this.locY, this.locZ, this.yaw, this.pitch); + } + + return true; + } + } + + // Paper start + public void stopRiding() { stopRiding(false); } + public void stopRiding(boolean suppressCancellation) { + // paper end + Entity entity = this.getVehicle(); + + super.stopRiding(suppressCancellation); // Paper + Entity entity1 = this.getVehicle(); + + if (entity1 != entity && this.playerConnection != null) { + this.playerConnection.a(this.locX, this.locY, this.locZ, this.yaw, this.pitch); + } + // Paper start - "Fixes" an issue in which the vehicle player would not be notified that the passenger dismounted + if (entity instanceof EntityPlayer) { + WorldServer worldServer = (WorldServer) entity.getWorld(); + worldServer.tracker.untrackEntity(this); + worldServer.tracker.track(this); + } + // Paper end + + } + + public boolean isInvulnerable(DamageSource damagesource) { + return super.isInvulnerable(damagesource) || this.H(); + } + + protected void a(double d0, boolean flag, IBlockData iblockdata, BlockPosition blockposition) {} + + protected void b(BlockPosition blockposition) { + if (!this.isSpectator()) { + super.b(blockposition); + } + + } + + public void a(double d0, boolean flag) { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locY - 0.20000000298023224D); + int k = MathHelper.floor(this.locZ); + BlockPosition blockposition = new BlockPosition(i, j, k); + IBlockData iblockdata = this.world.getType(blockposition); + + if (iblockdata.isAir()) { + BlockPosition blockposition1 = blockposition.down(); + IBlockData iblockdata1 = this.world.getType(blockposition1); + Block block = iblockdata1.getBlock(); + + if (block instanceof BlockFence || block instanceof BlockCobbleWall || block instanceof BlockFenceGate) { + blockposition = blockposition1; + iblockdata = iblockdata1; + } + } + + super.a(d0, flag, iblockdata, blockposition); + } + + public void openSign(TileEntitySign tileentitysign) { + tileentitysign.a((EntityHuman) this); + this.playerConnection.sendPacket(new PacketPlayOutOpenSignEditor(tileentitysign.getPosition())); + } + + public int nextContainerCounter() { // CraftBukkit - void -> int + this.containerCounter = this.containerCounter % 100 + 1; + return containerCounter; // CraftBukkit + } + + public void openTileEntity(ITileEntityContainer itileentitycontainer) { + // CraftBukkit start - Inventory open hook + if (false && itileentitycontainer instanceof ILootable && ((ILootable) itileentitycontainer).getLootTable() != null && this.isSpectator()) { + this.a((new ChatMessage("container.spectatorCantOpen", new Object[0])).a(EnumChatFormat.RED), true); + } else { + boolean cancelled = itileentitycontainer instanceof ILootable && ((ILootable) itileentitycontainer).getLootTable()!= null && this.isSpectator(); + Container container = CraftEventFactory.callInventoryOpenEvent(this, itileentitycontainer.createContainer(this.inventory, this), cancelled); + if (container == null) { + return; + } + this.nextContainerCounter(); + this.activeContainer = container; + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, itileentitycontainer.getContainerName(), itileentitycontainer.getScoreboardDisplayName())); + // CraftBukkit end + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + } + } + + public void openContainer(IInventory iinventory) { + // CraftBukkit start - Inventory open hook + // Copied from below + boolean cancelled = false; + if (iinventory instanceof ITileInventory) { + ITileInventory itileinventory = (ITileInventory) iinventory; + cancelled = itileinventory.isLocked() && !this.a(itileinventory.getLock()) && !this.isSpectator(); + } + + Container container; + if (iinventory instanceof ITileEntityContainer) { + if (iinventory instanceof TileEntity) { + Preconditions.checkArgument(((TileEntity) iinventory).getWorld() != null, "Container must have world to be opened"); + } + container = ((ITileEntityContainer) iinventory).createContainer(this.inventory, this); + } else { + container = new ContainerChest(this.inventory, iinventory, this); + } + container = CraftEventFactory.callInventoryOpenEvent(this, container, cancelled); + if (container == null && !cancelled) { // Let pre-cancelled events fall through + iinventory.closeContainer(this); + return; + } + // CraftBukkit end + + if (iinventory instanceof ILootable && ((ILootable) iinventory).getLootTable() != null && this.isSpectator()) { + this.a((new ChatMessage("container.spectatorCantOpen", new Object[0])).a(EnumChatFormat.RED), true); + } else { + if (this.activeContainer != this.defaultContainer) { + this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.OPEN_NEW); // Paper + } + + if (iinventory instanceof ITileInventory) { + ITileInventory itileinventory = (ITileInventory) iinventory; + + if (itileinventory.isLocked() && !this.a(itileinventory.getLock()) && !this.isSpectator()) { + this.playerConnection.sendPacket(new PacketPlayOutChat(new ChatMessage("container.isLocked", new Object[] { iinventory.getScoreboardDisplayName()}), ChatMessageType.GAME_INFO)); + this.playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(SoundEffects.BLOCK_CHEST_LOCKED, SoundCategory.BLOCKS, this.locX, this.locY, this.locZ, 1.0F, 1.0F)); + iinventory.closeContainer(this); // CraftBukkit + return; + } + } + + this.nextContainerCounter(); + // CraftBukkit start + if (iinventory instanceof ITileEntityContainer) { + this.activeContainer = container; + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, ((ITileEntityContainer) iinventory).getContainerName(), iinventory.getScoreboardDisplayName(), iinventory.getSize())); + } else { + this.activeContainer = container; + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, "minecraft:container", iinventory.getScoreboardDisplayName(), iinventory.getSize())); + } + // CraftBukkit end + + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + } + } + + public void openTrade(IMerchant imerchant) { + // CraftBukkit start - Inventory open hook + Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerMerchant(this.inventory, imerchant, this.world)); + if (container == null) { + return; + } + // CraftBukkit end + this.nextContainerCounter(); + this.activeContainer = container; // CraftBukkit + // CraftBukkit start - moved down (SPIGOT-4619) + // this.activeContainer.windowId = this.containerCounter; + // this.activeContainer.addSlotListener(this); + // CraftBukkit end + + InventoryMerchant inventorymerchant = ((ContainerMerchant) this.activeContainer).d(); + IChatBaseComponent ichatbasecomponent = imerchant.getScoreboardDisplayName(); + + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, "minecraft:villager", ichatbasecomponent, inventorymerchant.getSize())); + // CraftBukkit start + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + // CraftBukkit end + MerchantRecipeList merchantrecipelist = imerchant.getOffers(this); + + if (merchantrecipelist != null) { + PacketDataSerializer packetdataserializer = new PacketDataSerializer(Unpooled.buffer()); + + packetdataserializer.writeInt(this.containerCounter); + merchantrecipelist.a(packetdataserializer); + this.playerConnection.sendPacket(new PacketPlayOutCustomPayload(PacketPlayOutCustomPayload.a, packetdataserializer)); + } + + } + + public void openHorseInventory(EntityHorseAbstract entityhorseabstract, IInventory iinventory) { + // CraftBukkit start - Inventory open hook + Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerHorse(this.inventory, iinventory, entityhorseabstract, this)); + if (container == null) { + iinventory.closeContainer(this); + return; + } + // CraftBukkit end + if (this.activeContainer != this.defaultContainer) { + this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.OPEN_NEW); // Paper + } + + this.nextContainerCounter(); + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, "EntityHorse", iinventory.getScoreboardDisplayName(), iinventory.getSize(), entityhorseabstract.getId())); + this.activeContainer = container; // CraftBukkit + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + } + + public void a(ItemStack itemstack, EnumHand enumhand) { + Item item = itemstack.getItem(); + + if (item == Items.WRITTEN_BOOK) { + PacketDataSerializer packetdataserializer = new PacketDataSerializer(Unpooled.buffer()); + + packetdataserializer.a((Enum) enumhand); + this.playerConnection.sendPacket(new PacketPlayOutCustomPayload(PacketPlayOutCustomPayload.c, packetdataserializer)); + } + + } + + public void a(TileEntityCommand tileentitycommand) { + tileentitycommand.c(true); + this.a((TileEntity) tileentitycommand); + } + + public void a(Container container, int i, ItemStack itemstack) { + if (!(container.getSlot(i) instanceof SlotResult)) { + if (container == this.defaultContainer) { + CriterionTriggers.e.a(this, this.inventory); + } + + if (!this.f) { + this.playerConnection.sendPacket(new PacketPlayOutSetSlot(container.windowId, i, itemstack)); + } + } + } + + public void updateInventory(Container container) { + this.a(container, container.a()); + } + + public void a(Container container, NonNullList nonnulllist) { + this.playerConnection.sendPacket(new PacketPlayOutWindowItems(container.windowId, nonnulllist)); + this.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.inventory.getCarried())); + // CraftBukkit start - Send a Set Slot to update the crafting result slot + if (java.util.EnumSet.of(InventoryType.CRAFTING,InventoryType.WORKBENCH).contains(container.getBukkitView().getType())) { + this.playerConnection.sendPacket(new PacketPlayOutSetSlot(container.windowId, 0, container.getSlot(0).getItem())); + } + // CraftBukkit end + } + + public void setContainerData(Container container, int i, int j) { + this.playerConnection.sendPacket(new PacketPlayOutWindowData(container.windowId, i, j)); + } + + public void setContainerData(Container container, IInventory iinventory) { + for (int i = 0; i < iinventory.h(); ++i) { + this.playerConnection.sendPacket(new PacketPlayOutWindowData(container.windowId, i, iinventory.getProperty(i))); + } + + } + + public void closeInventory() { + // Paper start + closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNKNOWN); + } + public void closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) { + CraftEventFactory.handleInventoryCloseEvent(this, reason); // CraftBukkit + // Paper end + this.playerConnection.sendPacket(new PacketPlayOutCloseWindow(this.activeContainer.windowId)); + this.m(); + } + + public void broadcastCarriedItem() { + if (!this.f) { + this.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.inventory.getCarried())); + } + } + + public void m() { + this.activeContainer.b((EntityHuman) this); + this.activeContainer = this.defaultContainer; + } + + public void a(float f, float f1, boolean flag, boolean flag1) { + if (this.isPassenger()) { + if (f >= -1.0F && f <= 1.0F) { + this.bh = f; + } + + if (f1 >= -1.0F && f1 <= 1.0F) { + this.bj = f1; + } + + this.bg = flag; + this.setSneaking(flag1); + } + + } + + public void a(Statistic statistic, int i) { + this.cg.b(this, statistic, i); + this.world.getServer().getScoreboardManager().getScoreboardScores(statistic, this.getName(), (scoreboardscore) -> { // CraftBukkit - Get our scores instead + scoreboardscore.addScore(i); + }); + } + + public void a(Statistic statistic) { + this.cg.setStatistic(this, statistic, 0); + this.world.getServer().getScoreboardManager().getScoreboardScores(statistic, this.getName(), ScoreboardScore::c); // CraftBukkit - Get our scores instead + } + + public int discoverRecipes(Collection collection) { + return this.recipeBook.a(collection, this); + } + + public void a(MinecraftKey[] aminecraftkey) { + List list = Lists.newArrayList(); + MinecraftKey[] aminecraftkey1 = aminecraftkey; + int i = aminecraftkey.length; + + for (int j = 0; j < i; ++j) { + MinecraftKey minecraftkey = aminecraftkey1[j]; + IRecipe irecipe = this.server.getCraftingManager().a(minecraftkey); + + if (irecipe != null) { + list.add(irecipe); + } + } + + this.discoverRecipes(list); + } + + public int undiscoverRecipes(Collection collection) { + return this.recipeBook.b(collection, this); + } + + public void giveExp(int i) { + super.giveExp(i); + this.lastSentExp = -1; + } + + public void n() { + this.cB = true; + this.ejectPassengers(); + + // Paper start - Workaround an issue where the vehicle doesn't track the passenger disconnection dismount. + if (this.isPassenger() && this.getVehicle() instanceof EntityPlayer) { + this.stopRiding(); + } + // Paper end + + if (this.sleeping) { + this.a(true, false, false); + } + + } + + public boolean o() { + return this.cB; + } + + public void triggerHealthUpdate() { + this.lastHealthSent = -1.0E8F; + this.lastSentExp = -1; // CraftBukkit - Added to reset + } + + // CraftBukkit start - Support multi-line messages + public void sendMessage(IChatBaseComponent[] ichatbasecomponent) { + for (IChatBaseComponent component : ichatbasecomponent) { + this.sendMessage(component); + } + } + // CraftBukkit end + + public void a(IChatBaseComponent ichatbasecomponent, boolean flag) { + this.playerConnection.sendPacket(new PacketPlayOutChat(ichatbasecomponent, flag ? ChatMessageType.GAME_INFO : ChatMessageType.CHAT)); + } + + protected void q() { + if (!this.activeItem.isEmpty() && this.isHandRaised()) { + this.playerConnection.sendPacket(new PacketPlayOutEntityStatus(this, (byte) 9)); + super.q(); + } + + } + + public void a(ArgumentAnchor.Anchor argumentanchor_anchor, Vec3D vec3d) { + super.a(argumentanchor_anchor, vec3d); + this.playerConnection.sendPacket(new PacketPlayOutLookAt(argumentanchor_anchor, vec3d.x, vec3d.y, vec3d.z)); + } + + public void a(ArgumentAnchor.Anchor argumentanchor_anchor, Entity entity, ArgumentAnchor.Anchor argumentanchor_anchor1) { + Vec3D vec3d = argumentanchor_anchor1.a(entity); + + super.a(argumentanchor_anchor, vec3d); + this.playerConnection.sendPacket(new PacketPlayOutLookAt(argumentanchor_anchor, entity, argumentanchor_anchor1)); + } + + public void copyFrom(EntityPlayer entityplayer, boolean flag) { + if (flag) { + this.inventory.a(entityplayer.inventory); + this.setHealth(entityplayer.getHealth()); + this.foodData = entityplayer.foodData; + this.expLevel = entityplayer.expLevel; + this.expTotal = entityplayer.expTotal; + this.exp = entityplayer.exp; + this.setScore(entityplayer.getScore()); + this.aq = entityplayer.aq; + this.ar = entityplayer.ar; + this.as = entityplayer.as; + } else if (this.world.getGameRules().getBoolean("keepInventory") || entityplayer.isSpectator()) { + this.inventory.a(entityplayer.inventory); + this.expLevel = entityplayer.expLevel; + this.expTotal = entityplayer.expTotal; + this.exp = entityplayer.exp; + this.setScore(entityplayer.getScore()); + } + + this.bZ = entityplayer.bZ; + this.enderChest = entityplayer.enderChest; + this.getDataWatcher().set(EntityPlayer.bx, entityplayer.getDataWatcher().get(EntityPlayer.bx)); + this.lastSentExp = -1; + this.lastHealthSent = -1.0F; + this.lastFoodSent = -1; + // this.recipeBook.a((RecipeBook) entityplayer.recipeBook); // CraftBukkit + // Paper start - Optimize remove queue - vanilla copies player objects, but CB doesn't. This method currently only + // Applies to the same player, so we need to not duplicate our removal queue. The rest of this method does "resetting" + // type logic so it does need to be called, maybe? This is silly. + //this.removeQueue.addAll(entityplayer.removeQueue); + if (this.removeQueue != entityplayer.removeQueue) { + this.removeQueue.addAll(entityplayer.removeQueue); + } + // Paper end + this.cx = entityplayer.cx; + this.cC = entityplayer.cC; + this.setShoulderEntityLeft(entityplayer.getShoulderEntityLeft()); + this.setShoulderEntityRight(entityplayer.getShoulderEntityRight()); + } + + protected void a(MobEffect mobeffect) { + super.a(mobeffect); + this.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.getId(), mobeffect)); + if (mobeffect.getMobEffect() == MobEffects.LEVITATION) { + this.cA = this.ticksLived; + this.cz = new Vec3D(this.locX, this.locY, this.locZ); + } + + CriterionTriggers.A.a(this); + } + + protected void a(MobEffect mobeffect, boolean flag) { + super.a(mobeffect, flag); + this.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.getId(), mobeffect)); + CriterionTriggers.A.a(this); + } + + protected void b(MobEffect mobeffect) { + super.b(mobeffect); + this.playerConnection.sendPacket(new PacketPlayOutRemoveEntityEffect(this.getId(), mobeffect.getMobEffect())); + if (mobeffect.getMobEffect() == MobEffects.LEVITATION) { + this.cz = null; + } + + CriterionTriggers.A.a(this); + } + + public void enderTeleportTo(double d0, double d1, double d2) { + this.playerConnection.a(d0, d1, d2, this.yaw, this.pitch); + } + + public void a(Entity entity) { + this.getWorldServer().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(entity, 4)); + } + + public void b(Entity entity) { + this.getWorldServer().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(entity, 5)); + } + + public void updateAbilities() { + if (this.playerConnection != null) { + this.playerConnection.sendPacket(new PacketPlayOutAbilities(this.abilities)); + this.C(); + } + } + + public WorldServer getWorldServer() { + return (WorldServer) this.world; + } + + public void a(EnumGamemode enumgamemode) { + // CraftBukkit start + if (enumgamemode == this.playerInteractManager.getGameMode()) { + return; + } + + PlayerGameModeChangeEvent event = new PlayerGameModeChangeEvent(getBukkitEntity(), GameMode.getByValue(enumgamemode.getId())); + world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + // CraftBukkit end + + this.playerInteractManager.setGameMode(enumgamemode); + this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(3, (float) enumgamemode.getId())); + if (enumgamemode == EnumGamemode.SPECTATOR) { + this.releaseShoulderEntities(); + this.stopRiding(); + } else { + this.setSpectatorTarget(this); + } + + this.updateAbilities(); + this.cR(); + } + + public boolean isSpectator() { + return this.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR; + } + + public boolean u() { + return this.playerInteractManager.getGameMode() == EnumGamemode.CREATIVE; + } + + public void sendMessage(IChatBaseComponent ichatbasecomponent) { + this.a(ichatbasecomponent, ChatMessageType.SYSTEM); + } + + public void a(IChatBaseComponent ichatbasecomponent, ChatMessageType chatmessagetype) { + this.playerConnection.a((Packet) (new PacketPlayOutChat(ichatbasecomponent, chatmessagetype)), (future) -> { + if (!future.isSuccess() && (chatmessagetype == ChatMessageType.GAME_INFO || chatmessagetype == ChatMessageType.SYSTEM)) { + boolean flag = true; + String s = ichatbasecomponent.a(256); + IChatBaseComponent ichatbasecomponent1 = (new ChatComponentText(s)).a(EnumChatFormat.YELLOW); + + this.playerConnection.sendPacket(new PacketPlayOutChat((new ChatMessage("multiplayer.message_not_delivered", new Object[] { ichatbasecomponent1})).a(EnumChatFormat.RED), ChatMessageType.SYSTEM)); + } + + }); + } + + public String v() { + String s = this.playerConnection.networkManager.getSocketAddress().toString(); + + s = s.substring(s.indexOf("/") + 1); + s = s.substring(0, s.indexOf(":")); + return s; + } + + public void a(PacketPlayInSettings packetplayinsettings) { + // CraftBukkit start + if (getMainHand() != packetplayinsettings.getMainHand()) { + PlayerChangedMainHandEvent event = new PlayerChangedMainHandEvent(getBukkitEntity(), getMainHand() == EnumMainHand.LEFT ? MainHand.LEFT : MainHand.RIGHT); + this.server.server.getPluginManager().callEvent(event); + } + if (this.locale == null || !this.locale.equals(packetplayinsettings.b())) { // Paper - check for null + PlayerLocaleChangeEvent event = new PlayerLocaleChangeEvent(getBukkitEntity(), packetplayinsettings.b()); + this.server.server.getPluginManager().callEvent(event); + } + this.clientViewDistance = packetplayinsettings.viewDistance; + // CraftBukkit end + // Paper start - add PlayerLocaleChangeEvent + // Since the field is initialized to null, this event should always fire the first time the packet is received + String oldLocale = this.locale; + this.locale = packetplayinsettings.b(); + if (!this.locale.equals(oldLocale)) { + new com.destroystokyo.paper.event.player.PlayerLocaleChangeEvent(this.getBukkitEntity(), oldLocale, this.locale).callEvent(); + } + // Paper end + this.cs = packetplayinsettings.d(); + this.ct = packetplayinsettings.e(); + this.getDataWatcher().set(EntityPlayer.bx, (byte) packetplayinsettings.f()); + this.getDataWatcher().set(EntityPlayer.by, (byte) (packetplayinsettings.getMainHand() == EnumMainHand.LEFT ? 0 : 1)); + } + + public EntityHuman.EnumChatVisibility getChatFlags() { + return this.cs; + } + + public void setResourcePack(String s, String s1) { + this.playerConnection.sendPacket(new PacketPlayOutResourcePackSend(s, s1)); + } + + protected int y() { + return this.server.a(this.getProfile()); + } + + public void resetIdleTimer() { + this.cu = SystemUtils.getMonotonicMillis(); + } + + public ServerStatisticManager getStatisticManager() { + return this.cg; + } + + public RecipeBookServer B() { + return this.recipeBook; + } + + public void c(Entity entity) { + if (entity instanceof EntityHuman) { + this.playerConnection.sendPacket(new PacketPlayOutEntityDestroy(new int[] { entity.getId()})); + } else { + this.removeQueue.add((Integer) entity.getId()); // CraftBukkit - decompile error + } + + } + + public void d(Entity entity) { + this.removeQueue.remove((Integer) entity.getId()); // CraftBukkit - decompile error + } + + protected void C() { + if (this.isSpectator()) { + this.cl(); + this.setInvisible(true); + } else { + super.C(); + } + + this.getWorldServer().getTracker().a(this); + } + + public Entity getSpecatorTarget() { + return (Entity) (this.spectatedEntity == null ? this : this.spectatedEntity); + } + + public void setSpectatorTarget(Entity newSpectatorTarget) { + // Paper start - Add PlayerStartSpectatingEntityEvent and PlayerStopSpectatingEntity Event + Entity entity1 = this.getSpecatorTarget(); + + if (newSpectatorTarget == null) { + newSpectatorTarget = this; + } + + if (entity1 == newSpectatorTarget) return; // new spec target is the current spec target + + if (newSpectatorTarget == this) { + com.destroystokyo.paper.event.player.PlayerStopSpectatingEntityEvent playerStopSpectatingEntityEvent = new com.destroystokyo.paper.event.player.PlayerStopSpectatingEntityEvent(this.getBukkitEntity(), entity1.getBukkitEntity()); + + if (!playerStopSpectatingEntityEvent.callEvent()) { + return; + } + } else { + com.destroystokyo.paper.event.player.PlayerStartSpectatingEntityEvent playerStartSpectatingEntityEvent = new com.destroystokyo.paper.event.player.PlayerStartSpectatingEntityEvent(this.getBukkitEntity(), entity1.getBukkitEntity(), newSpectatorTarget.getBukkitEntity()); + + if (!playerStartSpectatingEntityEvent.callEvent()) { + return; + } + } + + setSpectatorTargetField(newSpectatorTarget); + + this.playerConnection.sendPacket(new PacketPlayOutCamera(newSpectatorTarget)); + this.playerConnection.a(this.spectatedEntity.locX, this.spectatedEntity.locY, this.spectatedEntity.locZ, this.yaw, this.pitch, TeleportCause.SPECTATE); // CraftBukkit + // Paper end + } + + protected void E() { + if (this.portalCooldown > 0 && !this.worldChangeInvuln) { + --this.portalCooldown; + } + + } + + public void attack(Entity entity) { + if (this.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR) { + this.setSpectatorTarget(entity); + } else { + super.attack(entity); + } + + } + + public long F() { + return this.cu; + } + + @Nullable + public IChatBaseComponent getPlayerListName() { + return listName; // CraftBukkit + } + + public void a(EnumHand enumhand) { + super.a(enumhand); + this.dH(); + } + + public boolean H() { + return this.worldChangeInvuln; + } + + public void I() { + this.worldChangeInvuln = false; + } + + public void J() { + if (!CraftEventFactory.callToggleGlideEvent(this, true).isCancelled()) // CraftBukkit + this.setFlag(7, true); + } + + public void K() { + // CraftBukkit start + if (!CraftEventFactory.callToggleGlideEvent(this, false).isCancelled()) { + this.setFlag(7, true); + this.setFlag(7, false); + } + // CraftBukkit end + } + + public AdvancementDataPlayer getAdvancementData() { + return this.cf; + } + + @Nullable + public Vec3D M() { + return this.cC; + } + + // CraftBukkit start + public void a(WorldServer worldserver, double d0, double d1, double d2, float f, float f1) { + this.a(worldserver, d0, d1, d2, f, f1, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.UNKNOWN); + } + + public void a(WorldServer worldserver, double d0, double d1, double d2, float f, float f1, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) { + // CraftBukkit end + this.setSpectatorTarget(this); + this.stopRiding(); + /* CraftBukkit start - replace with bukkit handling for multi-world + if (worldserver == this.world) { + this.playerConnection.a(d0, d1, d2, f, f1); + } else { + WorldServer worldserver1 = this.getWorldServer(); + + this.dimension = worldserver.worldProvider.getDimensionManager(); + this.playerConnection.sendPacket(new PacketPlayOutRespawn(this.dimension, worldserver1.getDifficulty(), worldserver1.getWorldData().getType(), this.playerInteractManager.getGameMode())); + this.server.getPlayerList().f(this); + worldserver1.removeEntity(this); + this.dead = false; + this.setPositionRotation(d0, d1, d2, f, f1); + if (this.isAlive()) { + worldserver1.entityJoinedWorld(this, false); + worldserver.addEntity(this); + worldserver.entityJoinedWorld(this, false); + } + + this.spawnIn(worldserver); + this.server.getPlayerList().a(this, worldserver1); + this.playerConnection.a(d0, d1, d2, f, f1); + this.playerInteractManager.a(worldserver); + this.server.getPlayerList().b(this, worldserver); + this.server.getPlayerList().updateClient(this); + } + */ + this.getBukkitEntity().teleport(new Location(worldserver.getWorld(), d0, d1, d2, f, f1), cause); + // CraftBukkit end + + } + + // CraftBukkit start - Add per-player time and weather. + public long timeOffset = 0; + public boolean relativeTime = true; + + public long getPlayerTime() { + if (this.relativeTime) { + // Adds timeOffset to the current server time. + return this.world.getDayTime() + this.timeOffset; + } else { + // Adds timeOffset to the beginning of this day. + return this.world.getDayTime() - (this.world.getDayTime() % 24000) + this.timeOffset; + } + } + + public WeatherType weather = null; + + public WeatherType getPlayerWeather() { + return this.weather; + } + + public void setPlayerWeather(WeatherType type, boolean plugin) { + if (!plugin && this.weather != null) { + return; + } + + if (plugin) { + this.weather = type; + } + + if (type == WeatherType.DOWNFALL) { + this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(2, 0)); + } else { + this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(1, 0)); + } + } + + private float pluginRainPosition; + private float pluginRainPositionPrevious; + + public void updateWeather(float oldRain, float newRain, float oldThunder, float newThunder) { + if (this.weather == null) { + // Vanilla + if (oldRain != newRain) { + this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(7, newRain)); + } + } else { + // Plugin + if (pluginRainPositionPrevious != pluginRainPosition) { + this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(7, pluginRainPosition)); + } + } + + if (oldThunder != newThunder) { + if (weather == WeatherType.DOWNFALL || weather == null) { + this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(8, newThunder)); + } else { + this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(8, 0)); + } + } + } + + public void tickWeather() { + if (this.weather == null) return; + + pluginRainPositionPrevious = pluginRainPosition; + if (weather == WeatherType.DOWNFALL) { + pluginRainPosition += 0.01; + } else { + pluginRainPosition -= 0.01; + } + + pluginRainPosition = MathHelper.a(pluginRainPosition, 0.0F, 1.0F); + } + + public void resetPlayerWeather() { + this.weather = null; + this.setPlayerWeather(this.world.getWorldData().hasStorm() ? WeatherType.DOWNFALL : WeatherType.CLEAR, false); + } + + @Override + public String toString() { + return super.toString() + "(" + this.getName() + " at " + this.locX + "," + this.locY + "," + this.locZ + ")"; + } + + // SPIGOT-1903, MC-98153 + public void forceSetPositionRotation(double x, double y, double z, float yaw, float pitch) { + this.setPositionRotation(x, y, z, yaw, pitch); + this.playerConnection.syncPosition(); + } + + @Override + protected boolean isFrozen() { + return super.isFrozen() || (this.playerConnection != null && this.playerConnection.isDisconnected()); // Paper + } + + @Override + public Scoreboard getScoreboard() { + return getBukkitEntity().getScoreboard().getHandle(); + } + + public void reset() { + float exp = 0; + boolean keepInventory = this.world.getGameRules().getBoolean("keepInventory"); + + if (this.keepLevel || keepInventory) { + exp = this.exp; + this.newTotalExp = this.expTotal; + this.newLevel = this.expLevel; + } + + this.setHealth(this.getMaxHealth()); + this.setAirTicks(this.getMaxAirTicks()); // Paper + this.fireTicks = 0; + this.fallDistance = 0; + this.foodData = new FoodMetaData(this); + this.expLevel = this.newLevel; + this.expTotal = this.newTotalExp; + this.exp = 0; + this.deathTicks = 0; + this.setArrowCount(0); + this.removeAllEffects(org.bukkit.event.entity.EntityPotionEffectEvent.Cause.DEATH); + this.updateEffects = true; + this.activeContainer = this.defaultContainer; + this.killer = null; + this.lastDamager = null; + this.combatTracker = new CombatTracker(this); + this.lastSentExp = -1; + if (this.keepLevel || keepInventory) { + this.exp = exp; + } else { + this.giveExp(this.newExp); + } + this.keepLevel = false; + } + + @Override + public CraftPlayer getBukkitEntity() { + return (CraftPlayer) super.getBukkitEntity(); + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/EntityPolarBear.java b/src/main/java/net/minecraft/server/EntityPolarBear.java new file mode 100644 index 000000000000..dbb534c9cdc9 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityPolarBear.java @@ -0,0 +1,261 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public class EntityPolarBear extends EntityAnimal { + + private static final DataWatcherObject bC = DataWatcher.a(EntityPolarBear.class, DataWatcherRegistry.i); + private float bD; + private float bE; + private int bG; + + public EntityPolarBear(World world) { + super(EntityTypes.POLAR_BEAR, world); + this.setSize(1.3F, 1.4F); + } + + public EntityAgeable createChild(EntityAgeable entityageable) { + return EntityTypes.POLAR_BEAR.create(world); // Paper + } + + public boolean f(ItemStack itemstack) { + return false; + } + + protected void n() { + super.n(); + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(1, new EntityPolarBear.d()); + this.goalSelector.a(1, new EntityPolarBear.e()); + this.goalSelector.a(4, new PathfinderGoalFollowParent(this, 1.25D)); + this.goalSelector.a(5, new PathfinderGoalRandomStroll(this, 1.0D)); + this.goalSelector.a(6, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(7, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new EntityPolarBear.c()); + this.targetSelector.a(2, new EntityPolarBear.a()); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(30.0D); + this.getAttributeInstance(GenericAttributes.FOLLOW_RANGE).setValue(20.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.25D); + this.getAttributeMap().b(GenericAttributes.ATTACK_DAMAGE); + this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue(6.0D); + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.getBoundingBox().minY); + int k = MathHelper.floor(this.locZ); + BlockPosition blockposition = new BlockPosition(i, j, k); + BiomeBase biomebase = generatoraccess.getBiome(blockposition); + + return biomebase != Biomes.FROZEN_OCEAN && biomebase != Biomes.DEEP_FROZEN_OCEAN ? super.a(generatoraccess, flag) : generatoraccess.getLightLevel(blockposition, 0) > 8 && generatoraccess.getType(blockposition.down()).getBlock() == Blocks.ICE; + } + + protected SoundEffect D() { + return this.isBaby() ? SoundEffects.ENTITY_POLAR_BEAR_AMBIENT_BABY : SoundEffects.ENTITY_POLAR_BEAR_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_POLAR_BEAR_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_POLAR_BEAR_DEATH; + } + + protected void a(BlockPosition blockposition, IBlockData iblockdata) { + this.a(SoundEffects.ENTITY_POLAR_BEAR_STEP, 0.15F, 1.0F); + } + + protected void dy() { + if (this.bG <= 0) { + this.a(SoundEffects.ENTITY_POLAR_BEAR_WARNING, 1.0F, 1.0F); + this.bG = 40; + } + + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.M; + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityPolarBear.bC, false); + } + + public void tick() { + super.tick(); + if (this.world.isClientSide) { + this.bD = this.bE; + if (this.dz()) { + this.bE = MathHelper.a(this.bE + 1.0F, 0.0F, 6.0F); + } else { + this.bE = MathHelper.a(this.bE - 1.0F, 0.0F, 6.0F); + } + } + + if (this.bG > 0) { + --this.bG; + } + + } + + public boolean B(Entity entity) { + boolean flag = entity.damageEntity(DamageSource.mobAttack(this), (float) ((int) this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).getValue())); + + if (flag) { + this.a((EntityLiving) this, entity); + } + + return flag; + } + + public boolean dz() { + return (Boolean) this.datawatcher.get(EntityPolarBear.bC); + } + + public void s(boolean flag) { + this.datawatcher.set(EntityPolarBear.bC, flag); + } + + protected float cJ() { + return 0.98F; + } + + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + if (groupdataentity instanceof EntityPolarBear.b) { + if (((EntityPolarBear.b) groupdataentity).a) { + this.setAgeRaw(-24000); + } + } else { + EntityPolarBear.b entitypolarbear_b = new EntityPolarBear.b(); + + entitypolarbear_b.a = true; + groupdataentity = entitypolarbear_b; + } + + return (GroupDataEntity) groupdataentity; + } + + class e extends PathfinderGoalPanic { + + public e() { + super(EntityPolarBear.this, 2.0D); + } + + public boolean a() { + return !EntityPolarBear.this.isBaby() && !EntityPolarBear.this.isBurning() ? false : super.a(); + } + } + + class d extends PathfinderGoalMeleeAttack { + + public d() { + super(EntityPolarBear.this, 1.25D, true); + } + + protected void a(EntityLiving entityliving, double d0) { + double d1 = this.a(entityliving); + + if (d0 <= d1 && this.b <= 0) { + this.b = 20; + this.a.B(entityliving); + EntityPolarBear.this.s(false); + } else if (d0 <= d1 * 2.0D) { + if (this.b <= 0) { + EntityPolarBear.this.s(false); + this.b = 20; + } + + if (this.b <= 10) { + EntityPolarBear.this.s(true); + EntityPolarBear.this.dy(); + } + } else { + this.b = 20; + EntityPolarBear.this.s(false); + } + + } + + public void d() { + EntityPolarBear.this.s(false); + super.d(); + } + + protected double a(EntityLiving entityliving) { + return (double) (4.0F + entityliving.width); + } + } + + class a extends PathfinderGoalNearestAttackableTarget { + + public a() { + super(EntityPolarBear.this, EntityHuman.class, 20, true, true, (Predicate) null); + } + + public boolean a() { + if (EntityPolarBear.this.isBaby()) { + return false; + } else { + if (super.a()) { + List list = EntityPolarBear.this.world.a(EntityPolarBear.class, EntityPolarBear.this.getBoundingBox().grow(8.0D, 4.0D, 8.0D)); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityPolarBear entitypolarbear = (EntityPolarBear) iterator.next(); + + if (entitypolarbear.isBaby()) { + return true; + } + } + } + + EntityPolarBear.this.setGoalTarget((EntityLiving) null); + return false; + } + } + + protected double i() { + return super.i() * 0.5D; + } + } + + class c extends PathfinderGoalHurtByTarget { + + public c() { + super(EntityPolarBear.this, false); + } + + public void c() { + super.c(); + if (EntityPolarBear.this.isBaby()) { + this.g(); + this.d(); + } + + } + + protected void a(EntityCreature entitycreature, EntityLiving entityliving) { + if (entitycreature instanceof EntityPolarBear && !entitycreature.isBaby()) { + super.a(entitycreature, entityliving); + } + + } + } + + static class b implements GroupDataEntity { + + public boolean a; + + private b() {} + } +} diff --git a/src/main/java/net/minecraft/server/EntityPotion.java b/src/main/java/net/minecraft/server/EntityPotion.java new file mode 100644 index 000000000000..eec8ed72c27b --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityPotion.java @@ -0,0 +1,258 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +// CraftBukkit start +import java.util.HashMap; +import java.util.Map; +import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.entity.LivingEntity; +// CraftBukkit end + +public class EntityPotion extends EntityProjectile { + + private static final DataWatcherObject f = DataWatcher.a(EntityPotion.class, DataWatcherRegistry.g); + private static final Logger g = LogManager.getLogger(); + public static final Predicate e = EntityPotion::a; + + public EntityPotion(World world) { + super(EntityTypes.POTION, world); + } + + public EntityPotion(World world, EntityLiving entityliving, ItemStack itemstack) { + super(EntityTypes.POTION, entityliving, world); + this.setItem(itemstack); + } + + public EntityPotion(World world, double d0, double d1, double d2, ItemStack itemstack) { + super(EntityTypes.POTION, d0, d1, d2, world); + if (!itemstack.isEmpty()) { + this.setItem(itemstack); + } + + } + + protected void x_() { + this.getDataWatcher().register(EntityPotion.f, ItemStack.a); + } + + public ItemStack getItem() { + ItemStack itemstack = (ItemStack) this.getDataWatcher().get(EntityPotion.f); + + if (itemstack.getItem() != Items.SPLASH_POTION && itemstack.getItem() != Items.LINGERING_POTION) { + if (this.world != null) { + EntityPotion.g.error("ThrownPotion entity {} has no item?!", this.getId()); + } + + return new ItemStack(Items.SPLASH_POTION); + } else { + return itemstack; + } + } + + public void setItem(ItemStack itemstack) { + this.getDataWatcher().set(EntityPotion.f, itemstack); + } + + protected float f() { + return 0.05F; + } + + protected void a(MovingObjectPosition movingobjectposition) { + if (!this.world.isClientSide) { + ItemStack itemstack = this.getItem(); + PotionRegistry potionregistry = PotionUtil.d(itemstack); + List list = PotionUtil.getEffects(itemstack); + boolean flag = potionregistry == Potions.b && list.isEmpty(); + + if (movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.BLOCK && flag) { + BlockPosition blockposition = movingobjectposition.getBlockPosition().shift(movingobjectposition.direction); + + this.a(blockposition, movingobjectposition.direction); + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + + this.a(blockposition.shift(enumdirection), enumdirection); + } + } + + if (flag) { + this.l(); + } else if (true || !list.isEmpty()) { // CraftBukkit - Call event even if no effects to apply + if (this.isLingering()) { + this.a(itemstack, potionregistry); + } else { + this.a(movingobjectposition, list); + } + } + + int i = potionregistry.c() ? 2007 : 2002; + + this.world.triggerEffect(i, new BlockPosition(this), PotionUtil.c(itemstack)); + this.die(); + } + } + + private void l() { + AxisAlignedBB axisalignedbb = this.getBoundingBox().grow(4.0D, 2.0D, 4.0D); + List list = this.world.a(EntityLiving.class, axisalignedbb, EntityPotion.e); + + if (!list.isEmpty()) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityLiving entityliving = (EntityLiving) iterator.next(); + double d0 = this.h(entityliving); + + if (d0 < 16.0D && a(entityliving)) { + entityliving.damageEntity(DamageSource.DROWN, 1.0F); + } + } + } + + } + + private void a(MovingObjectPosition movingobjectposition, List list) { + AxisAlignedBB axisalignedbb = this.getBoundingBox().grow(4.0D, 2.0D, 4.0D); + List list1 = this.world.a(EntityLiving.class, axisalignedbb); + Map affected = new HashMap(); // CraftBukkit + + if (!list1.isEmpty()) { + Iterator iterator = list1.iterator(); + + while (iterator.hasNext()) { + EntityLiving entityliving = (EntityLiving) iterator.next(); + + if (entityliving.de()) { + double d0 = this.h(entityliving); + + if (d0 < 16.0D) { + double d1 = 1.0D - Math.sqrt(d0) / 4.0D; + + if (entityliving == movingobjectposition.entity) { + d1 = 1.0D; + } + + // CraftBukkit start + affected.put((LivingEntity) entityliving.getBukkitEntity(), d1); + } + } + } + } + + org.bukkit.event.entity.PotionSplashEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPotionSplashEvent(this, affected); + if (!event.isCancelled() && list != null && !list.isEmpty()) { // do not process effects if there are no effects to process + for (LivingEntity victim : event.getAffectedEntities()) { + if (!(victim instanceof CraftLivingEntity)) { + continue; + } + + EntityLiving entityliving = ((CraftLivingEntity) victim).getHandle(); + double d1 = event.getIntensity(victim); + // CraftBukkit end + + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator1.next(); + MobEffectList mobeffectlist = mobeffect.getMobEffect(); + // CraftBukkit start - Abide by PVP settings - for players only! + if (!this.world.pvpMode && this.getShooter() instanceof EntityPlayer && entityliving instanceof EntityPlayer && entityliving != this.getShooter()) { + int i = MobEffectList.getId(mobeffectlist); + // Block SLOWER_MOVEMENT, SLOWER_DIG, HARM, BLINDNESS, HUNGER, WEAKNESS and POISON potions + if (i == 2 || i == 4 || i == 7 || i == 15 || i == 17 || i == 18 || i == 19) { + continue; + } + } + // CraftBukkit end + + if (mobeffectlist.isInstant()) { + mobeffectlist.applyInstantEffect(this, this.getShooter(), entityliving, mobeffect.getAmplifier(), d1); + } else { + int i = (int) (d1 * (double) mobeffect.getDuration() + 0.5D); + + if (i > 20) { + entityliving.addEffect(new MobEffect(mobeffectlist, i, mobeffect.getAmplifier(), mobeffect.isAmbient(), mobeffect.isShowParticles()), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.POTION_SPLASH); // CraftBukkit + } + } + } + } + } + + } + + private void a(ItemStack itemstack, PotionRegistry potionregistry) { + EntityAreaEffectCloud entityareaeffectcloud = new EntityAreaEffectCloud(this.world, this.locX, this.locY, this.locZ); + + entityareaeffectcloud.setSource(this.getShooter()); + entityareaeffectcloud.setRadius(3.0F); + entityareaeffectcloud.setRadiusOnUse(-0.5F); + entityareaeffectcloud.setWaitTime(10); + entityareaeffectcloud.setRadiusPerTick(-entityareaeffectcloud.getRadius() / (float) entityareaeffectcloud.getDuration()); + entityareaeffectcloud.a(potionregistry); + Iterator iterator = PotionUtil.b(itemstack).iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + entityareaeffectcloud.a(new MobEffect(mobeffect)); + } + + NBTTagCompound nbttagcompound = itemstack.getTag(); + + if (nbttagcompound != null && nbttagcompound.hasKeyOfType("CustomPotionColor", 99)) { + entityareaeffectcloud.setColor(nbttagcompound.getInt("CustomPotionColor")); + } + + // CraftBukkit start + org.bukkit.event.entity.LingeringPotionSplashEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callLingeringPotionSplashEvent(this, entityareaeffectcloud); + if (!(event.isCancelled() || entityareaeffectcloud.dead)) { + this.world.addEntity(entityareaeffectcloud); + } else { + entityareaeffectcloud.dead = true; + } + // CraftBukkit end + } + + public boolean isLingering() { + return this.getItem().getItem() == Items.LINGERING_POTION; + } + + private void a(BlockPosition blockposition, EnumDirection enumdirection) { + if (this.world.getType(blockposition).getBlock() == Blocks.FIRE) { + this.world.douseFire((EntityHuman) null, blockposition.shift(enumdirection), enumdirection.opposite()); + } + + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + ItemStack itemstack = ItemStack.a(nbttagcompound.getCompound("Potion")); + + if (itemstack.isEmpty()) { + this.die(); + } else { + this.setItem(itemstack); + } + + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + ItemStack itemstack = this.getItem(); + + if (!itemstack.isEmpty()) { + nbttagcompound.set("Potion", itemstack.save(new NBTTagCompound())); + } + + } + + private static boolean a(EntityLiving entityliving) { + return entityliving instanceof EntityEnderman || entityliving instanceof EntityBlaze; + } +} diff --git a/src/main/java/net/minecraft/server/EntityProjectile.java b/src/main/java/net/minecraft/server/EntityProjectile.java new file mode 100644 index 000000000000..60ab1c751435 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityProjectile.java @@ -0,0 +1,273 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.UUID; +import javax.annotation.Nullable; + +public abstract class EntityProjectile extends Entity implements IProjectile { + + private int blockX; + private int blockY; + private int blockZ; + protected boolean inGround; + public int shake; + public EntityLiving shooter; + public UUID shooterId; + public Entity d; + private int aw; + + protected EntityProjectile(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.blockX = -1; + this.blockY = -1; + this.blockZ = -1; + this.setSize(0.25F, 0.25F); + } + + protected EntityProjectile(EntityTypes entitytypes, double d0, double d1, double d2, World world) { + this(entitytypes, world); + this.setPosition(d0, d1, d2); + } + + protected EntityProjectile(EntityTypes entitytypes, EntityLiving entityliving, World world) { + this(entitytypes, entityliving.locX, entityliving.locY + (double) entityliving.getHeadHeight() - 0.10000000149011612D, entityliving.locZ, world); + this.shooter = entityliving; + this.shooterId = entityliving.getUniqueID(); + this.projectileSource = (org.bukkit.entity.LivingEntity) entityliving.getBukkitEntity(); // CraftBukkit + } + + protected void x_() {} + + public void a(Entity entity, float f, float f1, float f2, float f3, float f4) { + float f5 = -MathHelper.sin(f1 * 0.017453292F) * MathHelper.cos(f * 0.017453292F); + float f6 = -MathHelper.sin((f + f2) * 0.017453292F); + float f7 = MathHelper.cos(f1 * 0.017453292F) * MathHelper.cos(f * 0.017453292F); + + this.shoot((double) f5, (double) f6, (double) f7, f3, f4); + this.motX += entity.motX; + this.motZ += entity.motZ; + if (!entity.onGround) { + this.motY += entity.motY; + } + + } + + public void shoot(double d0, double d1, double d2, float f, float f1) { + float f2 = MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + + d0 /= (double) f2; + d1 /= (double) f2; + d2 /= (double) f2; + d0 += this.random.nextGaussian() * 0.007499999832361937D * (double) f1; + d1 += this.random.nextGaussian() * 0.007499999832361937D * (double) f1; + d2 += this.random.nextGaussian() * 0.007499999832361937D * (double) f1; + d0 *= (double) f; + d1 *= (double) f; + d2 *= (double) f; + this.motX = d0; + this.motY = d1; + this.motZ = d2; + float f3 = MathHelper.sqrt(d0 * d0 + d2 * d2); + + this.yaw = (float) (MathHelper.c(d0, d2) * 57.2957763671875D); + this.pitch = (float) (MathHelper.c(d1, (double) f3) * 57.2957763671875D); + this.lastYaw = this.yaw; + this.lastPitch = this.pitch; + } + + public void tick() { + this.N = this.locX; + this.O = this.locY; + this.P = this.locZ; + super.tick(); + if (this.shake > 0) { + --this.shake; + } + + if (this.inGround) { + this.inGround = false; + this.motX *= (double) (this.random.nextFloat() * 0.2F); + this.motY *= (double) (this.random.nextFloat() * 0.2F); + this.motZ *= (double) (this.random.nextFloat() * 0.2F); + } + + Vec3D vec3d = new Vec3D(this.locX, this.locY, this.locZ); + Vec3D vec3d1 = new Vec3D(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + MovingObjectPosition movingobjectposition = this.world.rayTrace(vec3d, vec3d1); + + vec3d = new Vec3D(this.locX, this.locY, this.locZ); + vec3d1 = new Vec3D(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + if (movingobjectposition != null) { + vec3d1 = new Vec3D(movingobjectposition.pos.x, movingobjectposition.pos.y, movingobjectposition.pos.z); + } + + Entity entity = null; + List list = this.world.getEntities(this, this.getBoundingBox().b(this.motX, this.motY, this.motZ).g(1.0D)); + double d0 = 0.0D; + boolean flag = false; + + for (int i = 0; i < list.size(); ++i) { + Entity entity1 = (Entity) list.get(i); + + if (entity1.isInteractable()) { + if (entity1 == this.d) { + flag = true; + } else if (this.shooter != null && this.ticksLived < 2 && this.d == null && this.shooter == entity1) { // CraftBukkit - MC-88491 + this.d = entity1; + flag = true; + } else { + flag = false; + AxisAlignedBB axisalignedbb = entity1.getBoundingBox().g(0.30000001192092896D); + MovingObjectPosition movingobjectposition1 = axisalignedbb.b(vec3d, vec3d1); + + if (movingobjectposition1 != null) { + double d1 = vec3d.distanceSquared(movingobjectposition1.pos); + + if (d1 < d0 || d0 == 0.0D) { + entity = entity1; + d0 = d1; + } + } + } + } + } + + if (this.d != null) { + if (flag) { + this.aw = 2; + } else if (this.aw-- <= 0) { + this.d = null; + } + } + + if (entity != null) { + movingobjectposition = new MovingObjectPosition(entity); + } + + // Paper start - Call ProjectileCollideEvent + if (movingobjectposition != null && movingobjectposition.entity != null) { + com.destroystokyo.paper.event.entity.ProjectileCollideEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileCollideEvent(this, movingobjectposition); + if (event.isCancelled()) { + movingobjectposition = null; + } + } + // Paper end + + if (movingobjectposition != null) { + if (movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.BLOCK && this.world.getType(movingobjectposition.getBlockPosition()).getBlock() == Blocks.NETHER_PORTAL) { + this.e(movingobjectposition.getBlockPosition()); + } else { + this.a(movingobjectposition); + // CraftBukkit start + if (this.dead) { + org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this, movingobjectposition); + } + // CraftBukkit end + } + } + + this.locX += this.motX; + this.locY += this.motY; + this.locZ += this.motZ; + float f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + this.yaw = (float) (MathHelper.c(this.motX, this.motZ) * 57.2957763671875D); + + for (this.pitch = (float) (MathHelper.c(this.motY, (double) f) * 57.2957763671875D); this.pitch - this.lastPitch < -180.0F; this.lastPitch -= 360.0F) { + ; + } + + while (this.pitch - this.lastPitch >= 180.0F) { + this.lastPitch += 360.0F; + } + + while (this.yaw - this.lastYaw < -180.0F) { + this.lastYaw -= 360.0F; + } + + while (this.yaw - this.lastYaw >= 180.0F) { + this.lastYaw += 360.0F; + } + + this.pitch = this.lastPitch + (this.pitch - this.lastPitch) * 0.2F; + this.yaw = this.lastYaw + (this.yaw - this.lastYaw) * 0.2F; + float f1 = 0.99F; + float f2 = this.f(); + + if (this.isInWater()) { + for (int j = 0; j < 4; ++j) { + float f3 = 0.25F; + + this.world.addParticle(Particles.e, this.locX - this.motX * 0.25D, this.locY - this.motY * 0.25D, this.locZ - this.motZ * 0.25D, this.motX, this.motY, this.motZ); + } + + f1 = 0.8F; + } + + this.motX *= (double) f1; + this.motY *= (double) f1; + this.motZ *= (double) f1; + if (!this.isNoGravity()) { + this.motY -= (double) f2; + } + + this.setPosition(this.locX, this.locY, this.locZ); + } + + protected float f() { + return 0.03F; + } + + protected abstract void a(MovingObjectPosition movingobjectposition); + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setInt("xTile", this.blockX); + nbttagcompound.setInt("yTile", this.blockY); + nbttagcompound.setInt("zTile", this.blockZ); + nbttagcompound.setByte("shake", (byte) this.shake); + nbttagcompound.setByte("inGround", (byte) (this.inGround ? 1 : 0)); + if (this.shooterId != null) { + nbttagcompound.set("owner", GameProfileSerializer.a(this.shooterId)); + } + + } + + public void a(NBTTagCompound nbttagcompound) { + this.blockX = nbttagcompound.getInt("xTile"); + this.blockY = nbttagcompound.getInt("yTile"); + this.blockZ = nbttagcompound.getInt("zTile"); + this.shake = nbttagcompound.getByte("shake") & 255; + this.inGround = nbttagcompound.getByte("inGround") == 1; + this.shooter = null; + if (nbttagcompound.hasKeyOfType("owner", 10)) { + this.shooterId = GameProfileSerializer.b(nbttagcompound.getCompound("owner")); + } + if (this instanceof EntityEnderPearl && this.world != null && this.world.paperConfig.disableEnderpearlExploit) { this.shooterId = null; } // Paper - Don't store shooter name for pearls to block enderpearl travel exploit + + } + + @Nullable + public EntityLiving getShooter() { + if (this.shooter == null && this.shooterId != null && this.world instanceof WorldServer) { + Entity entity = ((WorldServer) this.world).getEntity(this.shooterId); + // Paper start - MC-50319 - shooter might be in another world (arrows through portals) + if (entity == null) { + for (WorldServer world : world.getMinecraftServer().getWorlds()) { + entity = world.getEntity(this.shooterId); + if (entity != null) { + break; + } + } + } + // Paper end + + if (entity instanceof EntityLiving) { + this.shooter = (EntityLiving) entity; + } else { + //this.shooterId = null; // Paper - don't unset shooterId + } + } + + return this.shooter; + } +} diff --git a/src/main/java/net/minecraft/server/EntityPufferFish.java b/src/main/java/net/minecraft/server/EntityPufferFish.java new file mode 100644 index 000000000000..5cc0e1f46ce0 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityPufferFish.java @@ -0,0 +1,204 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public class EntityPufferFish extends EntityFish { + + private static final DataWatcherObject a = DataWatcher.a(EntityPufferFish.class, DataWatcherRegistry.b); + private int b; + private int c; + private static final Predicate bC = (entityliving) -> { + return entityliving == null ? false : (entityliving instanceof EntityHuman && (((EntityHuman) entityliving).isSpectator() || ((EntityHuman) entityliving).u()) ? false : entityliving.getMonsterType() != EnumMonsterType.e); + }; + private float bD = -1.0F; + private float bE; + + public EntityPufferFish(World world) { + super(EntityTypes.PUFFERFISH, world); + this.setSize(0.7F, 0.7F); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityPufferFish.a, 0); + } + + public int getPuffState() { + return (Integer) this.datawatcher.get(EntityPufferFish.a); + } + + public void setPuffState(int i) { + this.datawatcher.set(EntityPufferFish.a, i); + this.d(i); + } + + private void d(int i) { + float f = 1.0F; + + if (i == 1) { + f = 0.7F; + } else if (i == 0) { + f = 0.5F; + } + + this.a(f); + } + + public final void setSize(float f, float f1) { + boolean flag = this.bD > 0.0F; + + this.bD = f; + this.bE = f1; + if (!flag) { + this.a(1.0F); + } + + } + + private void a(float f) { + super.setSize(this.bD * f, this.bE * f); + } + + public void a(DataWatcherObject datawatcherobject) { + this.d(this.getPuffState()); + super.a(datawatcherobject); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("PuffState", this.getPuffState()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setPuffState(nbttagcompound.getInt("PuffState")); + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.aF; + } + + protected ItemStack l() { + return new ItemStack(Items.PUFFERFISH_BUCKET); + } + + protected void n() { + super.n(); + this.goalSelector.a(1, new EntityPufferFish.a(this)); + } + + public void tick() { + if (this.isAlive() && !this.world.isClientSide) { + if (this.b > 0) { + if (this.getPuffState() == 0) { + this.a(SoundEffects.ENTITY_PUFFER_FISH_BLOW_UP, this.cD(), this.cE()); + this.setPuffState(1); + } else if (this.b > 40 && this.getPuffState() == 1) { + this.a(SoundEffects.ENTITY_PUFFER_FISH_BLOW_UP, this.cD(), this.cE()); + this.setPuffState(2); + } + + ++this.b; + } else if (this.getPuffState() != 0) { + if (this.c > 60 && this.getPuffState() == 2) { + this.a(SoundEffects.ENTITY_PUFFER_FISH_BLOW_OUT, this.cD(), this.cE()); + this.setPuffState(1); + } else if (this.c > 100 && this.getPuffState() == 1) { + this.a(SoundEffects.ENTITY_PUFFER_FISH_BLOW_OUT, this.cD(), this.cE()); + this.setPuffState(0); + } + + ++this.c; + } + } + + super.tick(); + } + + public void movementTick() { + super.movementTick(); + if (this.getPuffState() > 0) { + List list = this.world.a(EntityInsentient.class, this.getBoundingBox().g(0.3D), EntityPufferFish.bC); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityInsentient entityinsentient = (EntityInsentient) iterator.next(); + + if (entityinsentient.isAlive()) { + this.a(entityinsentient); + } + } + } + + } + + private void a(EntityInsentient entityinsentient) { + int i = this.getPuffState(); + + if (entityinsentient.damageEntity(DamageSource.mobAttack(this), (float) (1 + i))) { + entityinsentient.addEffect(new MobEffect(MobEffects.POISON, 60 * i, 0), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit + this.a(SoundEffects.ENTITY_PUFFER_FISH_STING, 1.0F, 1.0F); + } + + } + + public void d(EntityHuman entityhuman) { + int i = this.getPuffState(); + + if (entityhuman instanceof EntityPlayer && i > 0 && entityhuman.damageEntity(DamageSource.mobAttack(this), (float) (1 + i))) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutGameStateChange(9, 0.0F)); + entityhuman.addEffect(new MobEffect(MobEffects.POISON, 60 * i, 0), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit + } + + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_PUFFER_FISH_AMBIENT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_PUFFER_FISH_DEATH; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_PUFFER_FISH_HURT; + } + + protected SoundEffect dz() { + return SoundEffects.ENTITY_PUFFER_FISH_FLOP; + } + + static class a extends PathfinderGoal { + + private final EntityPufferFish a; + + public a(EntityPufferFish entitypufferfish) { + this.a = entitypufferfish; + } + + public boolean a() { + List list = this.a.world.a(EntityLiving.class, this.a.getBoundingBox().g(2.0D), EntityPufferFish.bC); + + return !list.isEmpty(); + } + + public void c() { + this.a.b = 1; + this.a.c = 0; + } + + public void d() { + this.a.b = 0; + } + + public boolean b() { + List list = this.a.world.a(EntityLiving.class, this.a.getBoundingBox().g(2.0D), EntityPufferFish.bC); + + return !list.isEmpty(); + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityRabbit.java b/src/main/java/net/minecraft/server/EntityRabbit.java new file mode 100644 index 000000000000..d6bac06a7a51 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityRabbit.java @@ -0,0 +1,536 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class EntityRabbit extends EntityAnimal { + + private static final DataWatcherObject bC = DataWatcher.a(EntityRabbit.class, DataWatcherRegistry.b); + private static final MinecraftKey bD = new MinecraftKey("killer_bunny"); + private int bE; + private int bG; + private boolean bH; + private int bI; + private int bJ; + + public EntityRabbit(World world) { + super(EntityTypes.RABBIT, world); + this.setSize(0.4F, 0.5F); + this.h = new EntityRabbit.ControllerJumpRabbit(this); + this.moveController = new EntityRabbit.ControllerMoveRabbit(this); + this.initializePathFinderGoals(); // CraftBukkit - moved code + } + + // CraftBukkit start - code from constructor + public void initializePathFinderGoals(){ + this.c(0.0D); + } + // CraftBukkit end + + protected void n() { + this.goalSelector.a(1, new PathfinderGoalFloat(this)); + this.goalSelector.a(1, new EntityRabbit.PathfinderGoalRabbitPanic(this, 2.2D)); + this.goalSelector.a(2, new PathfinderGoalBreed(this, 0.8D)); + this.goalSelector.a(3, new PathfinderGoalTempt(this, 1.0D, RecipeItemStack.a(Items.CARROT, Items.GOLDEN_CARROT, Blocks.DANDELION), false)); + this.goalSelector.a(4, new EntityRabbit.PathfinderGoalRabbitAvoidTarget<>(this, EntityHuman.class, 8.0F, 2.2D, 2.2D)); + this.goalSelector.a(4, new EntityRabbit.PathfinderGoalRabbitAvoidTarget<>(this, EntityWolf.class, 10.0F, 2.2D, 2.2D)); + this.goalSelector.a(4, new EntityRabbit.PathfinderGoalRabbitAvoidTarget<>(this, EntityMonster.class, 4.0F, 2.2D, 2.2D)); + this.goalSelector.a(5, new EntityRabbit.PathfinderGoalEatCarrots(this)); + this.goalSelector.a(6, new PathfinderGoalRandomStrollLand(this, 0.6D)); + this.goalSelector.a(11, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 10.0F)); + } + + protected float cG() { + if (!this.positionChanged && (!this.moveController.b() || this.moveController.e() <= this.locY + 0.5D)) { + PathEntity pathentity = this.navigation.m(); + + if (pathentity != null && pathentity.e() < pathentity.d()) { + Vec3D vec3d = pathentity.a((Entity) this); + + if (vec3d.y > this.locY + 0.5D) { + return 0.5F; + } + } + + return this.moveController.c() <= 0.6D ? 0.2F : 0.3F; + } else { + return 0.5F; + } + } + + protected void cH() { + super.cH(); + double d0 = this.moveController.c(); + + if (d0 > 0.0D) { + double d1 = this.motX * this.motX + this.motZ * this.motZ; + + if (d1 < 0.010000000000000002D) { + this.a(0.0F, 0.0F, 1.0F, 0.1F); + } + } + + if (!this.world.isClientSide) { + this.world.broadcastEntityEffect(this, (byte) 1); + } + + } + + public void c(double d0) { + this.getNavigation().a(d0); + this.moveController.a(this.moveController.d(), this.moveController.e(), this.moveController.f(), d0); + } + + public void o(boolean flag) { + super.o(flag); + if (flag) { + this.a(this.dz(), this.cD(), ((this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F) * 0.8F); + } + + } + + public void dy() { + this.o(true); + this.bG = 10; + this.bE = 0; + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityRabbit.bC, 0); + } + + public void mobTick() { + if (this.bI > 0) { + --this.bI; + } + + if (this.bJ > 0) { + this.bJ -= this.random.nextInt(3); + if (this.bJ < 0) { + this.bJ = 0; + } + } + + if (this.onGround) { + if (!this.bH) { + this.o(false); + this.dI(); + } + + if (this.getRabbitType() == 99 && this.bI == 0) { + EntityLiving entityliving = this.getGoalTarget(); + + if (entityliving != null && this.h(entityliving) < 16.0D) { + this.b(entityliving.locX, entityliving.locZ); + this.moveController.a(entityliving.locX, entityliving.locY, entityliving.locZ, this.moveController.c()); + this.dy(); + this.bH = true; + } + } + + EntityRabbit.ControllerJumpRabbit entityrabbit_controllerjumprabbit = (EntityRabbit.ControllerJumpRabbit) this.h; + + if (!entityrabbit_controllerjumprabbit.c()) { + if (this.moveController.b() && this.bI == 0) { + PathEntity pathentity = this.navigation.m(); + Vec3D vec3d = new Vec3D(this.moveController.d(), this.moveController.e(), this.moveController.f()); + + if (pathentity != null && pathentity.e() < pathentity.d()) { + vec3d = pathentity.a((Entity) this); + } + + this.b(vec3d.x, vec3d.z); + this.dy(); + } + } else if (!entityrabbit_controllerjumprabbit.d()) { + this.dB(); + } + } + + this.bH = this.onGround; + } + + public void av() {} + + private void b(double d0, double d1) { + this.yaw = (float) (MathHelper.c(d1 - this.locZ, d0 - this.locX) * 57.2957763671875D) - 90.0F; + } + + private void dB() { + ((EntityRabbit.ControllerJumpRabbit) this.h).a(true); + } + + private void dC() { + ((EntityRabbit.ControllerJumpRabbit) this.h).a(false); + } + + private void dH() { + if (this.moveController.c() < 2.2D) { + this.bI = 10; + } else { + this.bI = 1; + } + + } + + private void dI() { + this.dH(); + this.dC(); + } + + public void movementTick() { + super.movementTick(); + if (this.bE != this.bG) { + ++this.bE; + } else if (this.bG != 0) { + this.bE = 0; + this.bG = 0; + this.o(false); + } + + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(3.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.30000001192092896D); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("RabbitType", this.getRabbitType()); + nbttagcompound.setInt("MoreCarrotTicks", this.bJ); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setRabbitType(nbttagcompound.getInt("RabbitType")); + this.bJ = nbttagcompound.getInt("MoreCarrotTicks"); + } + + protected SoundEffect dz() { + return SoundEffects.ENTITY_RABBIT_JUMP; + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_RABBIT_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_RABBIT_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_RABBIT_DEATH; + } + + public boolean B(Entity entity) { + if (this.getRabbitType() == 99) { + this.a(SoundEffects.ENTITY_RABBIT_ATTACK, 1.0F, (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F); + return entity.damageEntity(DamageSource.mobAttack(this), 8.0F); + } else { + return entity.damageEntity(DamageSource.mobAttack(this), 3.0F); + } + } + + public SoundCategory bV() { + return this.getRabbitType() == 99 ? SoundCategory.HOSTILE : SoundCategory.NEUTRAL; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + return this.isInvulnerable(damagesource) ? false : super.damageEntity(damagesource, f); + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.I; + } + + private boolean a(Item item) { + return item == Items.CARROT || item == Items.GOLDEN_CARROT || item == Blocks.DANDELION.getItem(); + } + + public EntityRabbit createChild(EntityAgeable entityageable) { + EntityRabbit entityrabbit = EntityTypes.RABBIT.create(world); // Paper + int i = this.dJ(); + + if (this.random.nextInt(20) != 0) { + if (entityageable instanceof EntityRabbit && this.random.nextBoolean()) { + i = ((EntityRabbit) entityageable).getRabbitType(); + } else { + i = this.getRabbitType(); + } + } + + entityrabbit.setRabbitType(i); + return entityrabbit; + } + + public boolean f(ItemStack itemstack) { + return this.a(itemstack.getItem()); + } + + public int getRabbitType() { + return (Integer) this.datawatcher.get(EntityRabbit.bC); + } + + public void setRabbitType(int i) { + if (i == 99) { + this.getAttributeInstance(GenericAttributes.h).setValue(8.0D); + this.goalSelector.a(4, new EntityRabbit.PathfinderGoalKillerRabbitMeleeAttack(this)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, false, new Class[0])); + this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget<>(this, EntityHuman.class, true)); + this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget<>(this, EntityWolf.class, true)); + if (!this.hasCustomName()) { + this.setCustomName(new ChatMessage(SystemUtils.a("entity", EntityRabbit.bD), new Object[0])); + } + } + + this.datawatcher.set(EntityRabbit.bC, i); + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + Object object = super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + int i = this.dJ(); + boolean flag = false; + + if (object instanceof EntityRabbit.GroupDataRabbit) { + i = ((EntityRabbit.GroupDataRabbit) object).a; + flag = true; + } else { + object = new EntityRabbit.GroupDataRabbit(i); + } + + this.setRabbitType(i); + if (flag) { + this.setAgeRaw(-24000); + } + + return (GroupDataEntity) object; + } + + private int dJ() { + BiomeBase biomebase = this.world.getBiome(new BlockPosition(this)); + int i = this.random.nextInt(100); + + return biomebase.c() == BiomeBase.Precipitation.SNOW ? (i < 80 ? 1 : 3) : (biomebase.p() == BiomeBase.Geography.DESERT ? 4 : (i < 50 ? 0 : (i < 90 ? 5 : 2))); + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.getBoundingBox().minY); + int k = MathHelper.floor(this.locZ); + BlockPosition blockposition = new BlockPosition(i, j, k); + Block block = generatoraccess.getType(blockposition.down()).getBlock(); + + return block != Blocks.GRASS && block != Blocks.SNOW && block != Blocks.SAND ? super.a(generatoraccess, flag) : true; + } + + private boolean dK() { + return this.bJ == 0; + } + + static class PathfinderGoalKillerRabbitMeleeAttack extends PathfinderGoalMeleeAttack { + + public PathfinderGoalKillerRabbitMeleeAttack(EntityRabbit entityrabbit) { + super(entityrabbit, 1.4D, true); + } + + protected double a(EntityLiving entityliving) { + return (double) (4.0F + entityliving.width); + } + } + + static class PathfinderGoalRabbitPanic extends PathfinderGoalPanic { + + private final EntityRabbit f; + + public PathfinderGoalRabbitPanic(EntityRabbit entityrabbit, double d0) { + super(entityrabbit, d0); + this.f = entityrabbit; + } + + public void e() { + super.e(); + this.f.c(this.b); + } + } + + static class PathfinderGoalEatCarrots extends PathfinderGoalGotoTarget { + + private final EntityRabbit entity; + private boolean g; + private boolean h; + + public PathfinderGoalEatCarrots(EntityRabbit entityrabbit) { + super(entityrabbit, 0.699999988079071D, 16); + this.entity = entityrabbit; + } + + public boolean a() { + if (this.b <= 0) { + if (!this.entity.world.getGameRules().getBoolean("mobGriefing")) { + return false; + } + + this.h = false; + this.g = this.entity.dK(); + this.g = true; + } + + return super.a(); + } + + public boolean b() { + return this.h && super.b(); + } + + public void e() { + super.e(); + this.entity.getControllerLook().a((double) this.d.getX() + 0.5D, (double) (this.d.getY() + 1), (double) this.d.getZ() + 0.5D, 10.0F, (float) this.entity.K()); + if (this.k()) { + World world = this.entity.world; + BlockPosition blockposition = this.d.up(); + IBlockData iblockdata = world.getType(blockposition); + Block block = iblockdata.getBlock(); + + if (this.h && block instanceof BlockCarrots) { + Integer integer = (Integer) iblockdata.get(BlockCarrots.AGE); + + if (integer == 0) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.entity, blockposition, Blocks.AIR.getBlockData()).isCancelled()) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 2); + world.setAir(blockposition, true); + } else { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent( + this.entity, + blockposition, + iblockdata.set(BlockCarrots.AGE, integer - 1) + ).isCancelled()) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockCarrots.AGE, integer - 1), 2); + world.triggerEffect(2001, blockposition, Block.getCombinedId(iblockdata)); + } + + this.entity.bJ = 40; + } + + this.h = false; + this.b = 10; + } + + } + + protected boolean a(IWorldReader iworldreader, BlockPosition blockposition) { + Block block = iworldreader.getType(blockposition).getBlock(); + + if (block == Blocks.FARMLAND && this.g && !this.h) { + blockposition = blockposition.up(); + IBlockData iblockdata = iworldreader.getType(blockposition); + + block = iblockdata.getBlock(); + if (block instanceof BlockCarrots && ((BlockCarrots) block).w(iblockdata)) { + this.h = true; + return true; + } + } + + return false; + } + } + + static class PathfinderGoalRabbitAvoidTarget extends PathfinderGoalAvoidTarget { + + private final EntityRabbit c; + + public PathfinderGoalRabbitAvoidTarget(EntityRabbit entityrabbit, Class oclass, float f, double d0, double d1) { + super(entityrabbit, oclass, f, d0, d1); + this.c = entityrabbit; + } + + public boolean a() { + return this.c.getRabbitType() != 99 && super.a(); + } + } + + static class ControllerMoveRabbit extends ControllerMove { + + private final EntityRabbit i; + private double j; + + public ControllerMoveRabbit(EntityRabbit entityrabbit) { + super(entityrabbit); + this.i = entityrabbit; + } + + public void a() { + if (this.i.onGround && !this.i.bg && !((EntityRabbit.ControllerJumpRabbit) this.i.h).c()) { + this.i.c(0.0D); + } else if (this.b()) { + this.i.c(this.j); + } + + super.a(); + } + + public void a(double d0, double d1, double d2, double d3) { + if (this.i.isInWater()) { + d3 = 1.5D; + } + + super.a(d0, d1, d2, d3); + if (d3 > 0.0D) { + this.j = d3; + } + + } + } + + public class ControllerJumpRabbit extends ControllerJump { + + private final EntityRabbit c; + private boolean d; + + public ControllerJumpRabbit(EntityRabbit entityrabbit) { + super(entityrabbit); + this.c = entityrabbit; + } + + public boolean c() { + return this.a; + } + + public boolean d() { + return this.d; + } + + public void a(boolean flag) { + this.d = flag; + } + + public void b() { + if (this.a) { + this.c.dy(); + this.a = false; + } + + } + } + + public static class GroupDataRabbit implements GroupDataEntity { + + public int a; + + public GroupDataRabbit(int i) { + this.a = i; + } + } +} diff --git a/src/main/java/net/minecraft/server/EntitySelector.java b/src/main/java/net/minecraft/server/EntitySelector.java new file mode 100644 index 000000000000..9d92d4510c4c --- /dev/null +++ b/src/main/java/net/minecraft/server/EntitySelector.java @@ -0,0 +1,243 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public class EntitySelector { + + private final int a; + private final boolean b; + private final boolean c; + private final Predicate d; + private final CriterionConditionValue.c e; + private final Function f; + @Nullable + private final AxisAlignedBB g; + private final BiConsumer> h; + private final boolean i; + @Nullable + private final String j; + @Nullable + private final UUID k; + private final Class l; + private final boolean m; + + public EntitySelector(int i, boolean flag, boolean flag1, Predicate predicate, CriterionConditionValue.c criterionconditionvalue_c, Function function, @Nullable AxisAlignedBB axisalignedbb, BiConsumer> biconsumer, boolean flag2, @Nullable String s, @Nullable UUID uuid, Class oclass, boolean flag3) { + this.a = i; + this.b = flag; + this.c = flag1; + this.d = predicate; + this.e = criterionconditionvalue_c; + this.f = function; + this.g = axisalignedbb; + this.h = biconsumer; + this.i = flag2; + this.j = s; + this.k = uuid; + this.l = oclass; + this.m = flag3; + } + + public int a() { + return this.a; + } + + public boolean b() { + return this.b; + } + + public boolean c() { + return this.i; + } + + public boolean d() { + return this.c; + } + + private void e(CommandListenerWrapper commandlistenerwrapper) throws CommandSyntaxException { + if (this.m && !commandlistenerwrapper.hasPermission(2, "minecraft.command.selector")) { // CraftBukkit + throw ArgumentEntity.f.create(); + } + } + + public Entity a(CommandListenerWrapper commandlistenerwrapper) throws CommandSyntaxException { + this.e(commandlistenerwrapper); + List list = this.b(commandlistenerwrapper); + + if (list.isEmpty()) { + throw ArgumentEntity.d.create(); + } else if (list.size() > 1) { + throw ArgumentEntity.a.create(); + } else { + return (Entity) list.get(0); + } + } + + public List b(CommandListenerWrapper commandlistenerwrapper) throws CommandSyntaxException { + this.e(commandlistenerwrapper); + if (!this.b) { + return this.d(commandlistenerwrapper); + } else if (this.j != null) { + EntityPlayer entityplayer = commandlistenerwrapper.getServer().getPlayerList().getPlayer(this.j); + + return (List) (entityplayer == null ? Collections.emptyList() : Lists.newArrayList(new EntityPlayer[] { entityplayer})); + } else if (this.k != null) { + Iterator iterator = commandlistenerwrapper.getServer().getWorlds().iterator(); + + Entity entity; + + do { + if (!iterator.hasNext()) { + return Collections.emptyList(); + } + + WorldServer worldserver = (WorldServer) iterator.next(); + + entity = worldserver.getEntity(this.k); + } while (entity == null); + + return Lists.newArrayList(new Entity[] { entity}); + } else { + Vec3D vec3d = (Vec3D) this.f.apply(commandlistenerwrapper.getPosition()); + Predicate predicate = this.a(vec3d); + + if (this.i) { + return (List) (commandlistenerwrapper.getEntity() != null && predicate.test(commandlistenerwrapper.getEntity()) ? Lists.newArrayList(new Entity[] { commandlistenerwrapper.getEntity()}) : Collections.emptyList()); + } else { + List list = Lists.newArrayList(); + + if (this.d()) { + this.a(list, commandlistenerwrapper.getWorld(), vec3d, predicate); + } else { + Iterator iterator1 = commandlistenerwrapper.getServer().getWorlds().iterator(); + + while (iterator1.hasNext()) { + WorldServer worldserver1 = (WorldServer) iterator1.next(); + + this.a(list, worldserver1, vec3d, predicate); + } + } + + return this.a(vec3d, (List) list); + } + } + } + + private void a(List list, WorldServer worldserver, Vec3D vec3d, Predicate predicate) { + Class oclass; + + if (this.g != null) { + oclass = this.l; + AxisAlignedBB axisalignedbb = this.g.a(vec3d); + + predicate.getClass(); + list.addAll(worldserver.a(oclass, axisalignedbb, (java.util.function.Predicate) predicate::test)); // CraftBukkit - decompile error + } else { + oclass = this.l; + predicate.getClass(); + list.addAll(worldserver.a(oclass, predicate::test)); + } + + } + + public EntityPlayer c(CommandListenerWrapper commandlistenerwrapper) throws CommandSyntaxException { + this.e(commandlistenerwrapper); + List list = this.d(commandlistenerwrapper); + + if (list.size() != 1) { + throw ArgumentEntity.e.create(); + } else { + return (EntityPlayer) list.get(0); + } + } + + public List d(CommandListenerWrapper commandlistenerwrapper) throws CommandSyntaxException { + this.e(commandlistenerwrapper); + EntityPlayer entityplayer; + + if (this.j != null) { + entityplayer = commandlistenerwrapper.getServer().getPlayerList().getPlayer(this.j); + return (List) (entityplayer == null ? Collections.emptyList() : Lists.newArrayList(new EntityPlayer[] { entityplayer})); + } else if (this.k != null) { + entityplayer = commandlistenerwrapper.getServer().getPlayerList().a(this.k); + return (List) (entityplayer == null ? Collections.emptyList() : Lists.newArrayList(new EntityPlayer[] { entityplayer})); + } else { + Vec3D vec3d = (Vec3D) this.f.apply(commandlistenerwrapper.getPosition()); + Predicate predicate = this.a(vec3d); + + if (this.i) { + if (commandlistenerwrapper.getEntity() instanceof EntityPlayer) { + EntityPlayer entityplayer1 = (EntityPlayer) commandlistenerwrapper.getEntity(); + + if (predicate.test(entityplayer1)) { + return Lists.newArrayList(new EntityPlayer[] { entityplayer1}); + } + } + + return Collections.emptyList(); + } else { + Object object; + + if (this.d()) { + WorldServer worldserver = commandlistenerwrapper.getWorld(); + + predicate.getClass(); + object = worldserver.b(EntityPlayer.class, predicate::test); + } else { + object = Lists.newArrayList(); + Iterator iterator = commandlistenerwrapper.getServer().getPlayerList().v().iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer2 = (EntityPlayer) iterator.next(); + + if (predicate.test(entityplayer2)) { + ((List) object).add(entityplayer2); + } + } + } + + return this.a(vec3d, (List) object); + } + } + } + + private Predicate a(Vec3D vec3d) { + Predicate predicate = this.d; + + if (this.g != null) { + AxisAlignedBB axisalignedbb = this.g.a(vec3d); + + predicate = predicate.and((entity) -> { + return axisalignedbb.c(entity.getBoundingBox()); + }); + } + + if (!this.e.c()) { + predicate = predicate.and((entity) -> { + return this.e.a(entity.a(vec3d)); + }); + } + + return predicate; + } + + private List a(Vec3D vec3d, List list) { + if (list.size() > 1) { + this.h.accept(vec3d, list); + } + + return list.subList(0, Math.min(this.a, list.size())); + } + + public static IChatBaseComponent a(List list) { + return ChatComponentUtils.b(list, Entity::getScoreboardDisplayName); + } +} diff --git a/src/main/java/net/minecraft/server/EntitySheep.java b/src/main/java/net/minecraft/server/EntitySheep.java new file mode 100644 index 000000000000..c35d1eef431c --- /dev/null +++ b/src/main/java/net/minecraft/server/EntitySheep.java @@ -0,0 +1,300 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.Map; +import java.util.Random; +import java.util.stream.Collectors; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.event.entity.SheepRegrowWoolEvent; +import org.bukkit.event.player.PlayerShearEntityEvent; +import org.bukkit.inventory.InventoryView; +// CraftBukkit end + +public class EntitySheep extends EntityAnimal { + + private static final DataWatcherObject bC = DataWatcher.a(EntitySheep.class, DataWatcherRegistry.a); + private final InventoryCrafting container = new InventoryCrafting(new Container() { + public boolean canUse(EntityHuman entityhuman) { + return false; + } + + // CraftBukkit start + @Override + public InventoryView getBukkitView() { + return null; // TODO: O.O + } + // CraftBukkit end + }, 2, 1); + private static final Map bE = (Map) SystemUtils.a(Maps.newEnumMap(EnumColor.class), (enummap) -> { // CraftBukkit - decompile error + enummap.put(EnumColor.WHITE, Blocks.WHITE_WOOL); + enummap.put(EnumColor.ORANGE, Blocks.ORANGE_WOOL); + enummap.put(EnumColor.MAGENTA, Blocks.MAGENTA_WOOL); + enummap.put(EnumColor.LIGHT_BLUE, Blocks.LIGHT_BLUE_WOOL); + enummap.put(EnumColor.YELLOW, Blocks.YELLOW_WOOL); + enummap.put(EnumColor.LIME, Blocks.LIME_WOOL); + enummap.put(EnumColor.PINK, Blocks.PINK_WOOL); + enummap.put(EnumColor.GRAY, Blocks.GRAY_WOOL); + enummap.put(EnumColor.LIGHT_GRAY, Blocks.LIGHT_GRAY_WOOL); + enummap.put(EnumColor.CYAN, Blocks.CYAN_WOOL); + enummap.put(EnumColor.PURPLE, Blocks.PURPLE_WOOL); + enummap.put(EnumColor.BLUE, Blocks.BLUE_WOOL); + enummap.put(EnumColor.BROWN, Blocks.BROWN_WOOL); + enummap.put(EnumColor.GREEN, Blocks.GREEN_WOOL); + enummap.put(EnumColor.RED, Blocks.RED_WOOL); + enummap.put(EnumColor.BLACK, Blocks.BLACK_WOOL); + }); + private static final Map bG = Maps.newEnumMap((Map) Arrays.stream(EnumColor.values()).collect(Collectors.toMap((enumcolor) -> { + return enumcolor; + }, EntitySheep::c))); + private int bH; + private PathfinderGoalEatTile bI; + + private static float[] c(EnumColor enumcolor) { + if (enumcolor == EnumColor.WHITE) { + return new float[] { 0.9019608F, 0.9019608F, 0.9019608F}; + } else { + float[] afloat = enumcolor.d(); + float f = 0.75F; + + return new float[] { afloat[0] * 0.75F, afloat[1] * 0.75F, afloat[2] * 0.75F}; + } + } + + public EntitySheep(World world) { + super(EntityTypes.SHEEP, world); + this.setSize(0.9F, 1.3F); + } + + protected void n() { + this.bI = new PathfinderGoalEatTile(this); + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(1, new PathfinderGoalPanic(this, 1.25D)); + this.goalSelector.a(2, new PathfinderGoalBreed(this, 1.0D)); + this.goalSelector.a(3, new PathfinderGoalTempt(this, 1.1D, RecipeItemStack.a(Items.WHEAT), false)); + this.goalSelector.a(4, new PathfinderGoalFollowParent(this, 1.1D)); + this.goalSelector.a(5, this.bI); + this.goalSelector.a(6, new PathfinderGoalRandomStrollLand(this, 1.0D)); + this.goalSelector.a(7, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this)); + } + + protected void mobTick() { + this.bH = this.bI.g(); + super.mobTick(); + } + + public void movementTick() { + if (this.world.isClientSide) { + this.bH = Math.max(0, this.bH - 1); + } + + super.movementTick(); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(8.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.23000000417232513D); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntitySheep.bC, (byte) 0); + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + if (this.isSheared()) { + return LootTables.W; + } else { + switch (this.getColor()) { + case WHITE: + default: + return LootTables.X; + case ORANGE: + return LootTables.Y; + case MAGENTA: + return LootTables.Z; + case LIGHT_BLUE: + return LootTables.aa; + case YELLOW: + return LootTables.ab; + case LIME: + return LootTables.ac; + case PINK: + return LootTables.ad; + case GRAY: + return LootTables.ae; + case LIGHT_GRAY: + return LootTables.af; + case CYAN: + return LootTables.ag; + case PURPLE: + return LootTables.ah; + case BLUE: + return LootTables.ai; + case BROWN: + return LootTables.aj; + case GREEN: + return LootTables.ak; + case RED: + return LootTables.al; + case BLACK: + return LootTables.am; + } + } + } + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (itemstack.getItem() == Items.SHEARS && !this.isSheared() && !this.isBaby()) { + if (!this.world.isClientSide) { + // CraftBukkit start + PlayerShearEntityEvent event = new PlayerShearEntityEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), this.getBukkitEntity()); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return false; + } + // CraftBukkit end + + this.setSheared(true); + int i = 1 + this.random.nextInt(3); + + for (int j = 0; j < i; ++j) { + this.forceDrops = true; // CraftBukkit + EntityItem entityitem = this.a((IMaterial) EntitySheep.bE.get(this.getColor()), 1); + this.forceDrops = false; // CraftBukkit + + if (entityitem != null) { + entityitem.motY += (double) (this.random.nextFloat() * 0.05F); + entityitem.motX += (double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.1F); + entityitem.motZ += (double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.1F); + } + } + } + + itemstack.damage(1, entityhuman); + this.a(SoundEffects.ENTITY_SHEEP_SHEAR, 1.0F, 1.0F); + } + + return super.a(entityhuman, enumhand); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("Sheared", this.isSheared()); + nbttagcompound.setByte("Color", (byte) this.getColor().getColorIndex()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setSheared(nbttagcompound.getBoolean("Sheared")); + this.setColor(EnumColor.fromColorIndex(nbttagcompound.getByte("Color"))); + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_SHEEP_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_SHEEP_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_SHEEP_DEATH; + } + + protected void a(BlockPosition blockposition, IBlockData iblockdata) { + this.a(SoundEffects.ENTITY_SHEEP_STEP, 0.15F, 1.0F); + } + + public EnumColor getColor() { + return EnumColor.fromColorIndex((Byte) this.datawatcher.get(EntitySheep.bC) & 15); + } + + public void setColor(EnumColor enumcolor) { + byte b0 = (Byte) this.datawatcher.get(EntitySheep.bC); + + this.datawatcher.set(EntitySheep.bC, (byte) (b0 & 240 | enumcolor.getColorIndex() & 15)); + } + + public boolean isSheared() { + return ((Byte) this.datawatcher.get(EntitySheep.bC) & 16) != 0; + } + + public void setSheared(boolean flag) { + byte b0 = (Byte) this.datawatcher.get(EntitySheep.bC); + + if (flag) { + this.datawatcher.set(EntitySheep.bC, (byte) (b0 | 16)); + } else { + this.datawatcher.set(EntitySheep.bC, (byte) (b0 & -17)); + } + + } + + public static EnumColor a(Random random) { + int i = random.nextInt(100); + + return i < 5 ? EnumColor.BLACK : (i < 10 ? EnumColor.GRAY : (i < 15 ? EnumColor.LIGHT_GRAY : (i < 18 ? EnumColor.BROWN : (random.nextInt(500) == 0 ? EnumColor.PINK : EnumColor.WHITE)))); + } + + public EntitySheep createChild(EntityAgeable entityageable) { + EntitySheep entitysheep = (EntitySheep) entityageable; + EntitySheep entitysheep1 = EntityTypes.SHEEP.create(world); // Paper + + entitysheep1.setColor(this.a((EntityAnimal) this, (EntityAnimal) entitysheep)); + return entitysheep1; + } + + public void x() { + // CraftBukkit start + SheepRegrowWoolEvent event = new SheepRegrowWoolEvent((org.bukkit.entity.Sheep) this.getBukkitEntity()); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) return; + // CraftBukkit end + this.setSheared(false); + if (this.isBaby()) { + this.setAge(60); + } + + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + groupdataentity = super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + this.setColor(a(this.world.random)); + return groupdataentity; + } + + private EnumColor a(EntityAnimal entityanimal, EntityAnimal entityanimal1) { + EnumColor enumcolor = ((EntitySheep) entityanimal).getColor(); + EnumColor enumcolor1 = ((EntitySheep) entityanimal1).getColor(); + + this.container.setItem(0, new ItemStack(ItemDye.a(enumcolor))); + this.container.setItem(1, new ItemStack(ItemDye.a(enumcolor1))); + this.container.resultInventory = new InventoryCraftResult(); // CraftBukkit - add result slot for event + ItemStack itemstack = entityanimal.world.getCraftingManager().craft(this.container, ((EntitySheep) entityanimal).world); + Item item = itemstack.getItem(); + EnumColor enumcolor2; + + if (item instanceof ItemDye) { + enumcolor2 = ((ItemDye) item).d(); + } else { + enumcolor2 = this.world.random.nextBoolean() ? enumcolor : enumcolor1; + } + + return enumcolor2; + } + + public float getHeadHeight() { + return 0.95F * this.length; + } +} diff --git a/src/main/java/net/minecraft/server/EntityShulker.java b/src/main/java/net/minecraft/server/EntityShulker.java new file mode 100644 index 000000000000..5ce91c8f6f23 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityShulker.java @@ -0,0 +1,601 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import javax.annotation.Nullable; +// CraftBukkit start +import org.bukkit.Location; +import org.bukkit.event.entity.EntityTeleportEvent; +// CraftBukkit end + +public class EntityShulker extends EntityGolem implements IMonster { + + private static final UUID bD = UUID.fromString("7E0292F2-9434-48D5-A29F-9583AF7DF27F"); + private static final AttributeModifier bE = (new AttributeModifier(EntityShulker.bD, "Covered armor bonus", 20.0D, 0)).a(false); + protected static final DataWatcherObject a = DataWatcher.a(EntityShulker.class, DataWatcherRegistry.n); + protected static final DataWatcherObject> b = DataWatcher.a(EntityShulker.class, DataWatcherRegistry.m); + protected static final DataWatcherObject c = DataWatcher.a(EntityShulker.class, DataWatcherRegistry.a); + public static final DataWatcherObject COLOR = DataWatcher.a(EntityShulker.class, DataWatcherRegistry.a); + private float bF; + private float bG; + private BlockPosition bH; + private int bI; + + public EntityShulker(World world) { + super(EntityTypes.SHULKER, world); + this.setSize(1.0F, 1.0F); + this.aR = 180.0F; + this.aQ = 180.0F; + this.fireProof = true; + this.bH = null; + this.b_ = 5; + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + this.aQ = 180.0F; + this.aR = 180.0F; + this.yaw = 180.0F; + this.lastYaw = 180.0F; + this.aS = 180.0F; + this.aT = 180.0F; + return super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + } + + protected void n() { + this.goalSelector.a(1, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(4, new EntityShulker.a()); + this.goalSelector.a(7, new EntityShulker.e()); + this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, true, new Class[0])); + this.targetSelector.a(2, new EntityShulker.d(this)); + this.targetSelector.a(3, new EntityShulker.c(this)); + } + + protected boolean playStepSound() { + return false; + } + + public SoundCategory bV() { + return SoundCategory.HOSTILE; + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_SHULKER_AMBIENT; + } + + public void A() { + if (!this.dG()) { + super.A(); + } + + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_SHULKER_DEATH; + } + + protected SoundEffect d(DamageSource damagesource) { + return this.dG() ? SoundEffects.ENTITY_SHULKER_HURT_CLOSED : SoundEffects.ENTITY_SHULKER_HURT; + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityShulker.a, EnumDirection.DOWN); + this.datawatcher.register(EntityShulker.b, Optional.empty()); + this.datawatcher.register(EntityShulker.c, (byte) 0); + this.datawatcher.register(EntityShulker.COLOR, (byte) 16); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(30.0D); + } + + protected EntityAIBodyControl o() { + return new EntityShulker.b(this); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.datawatcher.set(EntityShulker.a, EnumDirection.fromType1(nbttagcompound.getByte("AttachFace"))); + this.datawatcher.set(EntityShulker.c, nbttagcompound.getByte("Peek")); + this.datawatcher.set(EntityShulker.COLOR, nbttagcompound.getByte("Color")); + if (nbttagcompound.hasKey("APX")) { + int i = nbttagcompound.getInt("APX"); + int j = nbttagcompound.getInt("APY"); + int k = nbttagcompound.getInt("APZ"); + + this.datawatcher.set(EntityShulker.b, Optional.of(new BlockPosition(i, j, k))); + } else { + this.datawatcher.set(EntityShulker.b, Optional.empty()); + } + + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setByte("AttachFace", (byte) ((EnumDirection) this.datawatcher.get(EntityShulker.a)).a()); + nbttagcompound.setByte("Peek", (Byte) this.datawatcher.get(EntityShulker.c)); + nbttagcompound.setByte("Color", (Byte) this.datawatcher.get(EntityShulker.COLOR)); + BlockPosition blockposition = this.dz(); + + if (blockposition != null) { + nbttagcompound.setInt("APX", blockposition.getX()); + nbttagcompound.setInt("APY", blockposition.getY()); + nbttagcompound.setInt("APZ", blockposition.getZ()); + } + + } + + public void tick() { + super.tick(); + BlockPosition blockposition = (BlockPosition) ((Optional) this.datawatcher.get(EntityShulker.b)).orElse((Object) null); + + if (blockposition == null && !this.world.isClientSide) { + blockposition = new BlockPosition(this); + this.datawatcher.set(EntityShulker.b, Optional.of(blockposition)); + } + + float f; + + if (this.isPassenger()) { + blockposition = null; + f = this.getVehicle().yaw; + this.yaw = f; + this.aQ = f; + this.aR = f; + this.bI = 0; + } else if (!this.world.isClientSide) { + IBlockData iblockdata = this.world.getType(blockposition); + + if (!iblockdata.isAir()) { + EnumDirection enumdirection; + + if (iblockdata.getBlock() == Blocks.MOVING_PISTON) { + enumdirection = (EnumDirection) iblockdata.get(BlockPiston.FACING); + if (this.world.isEmpty(blockposition.shift(enumdirection))) { + blockposition = blockposition.shift(enumdirection); + this.datawatcher.set(EntityShulker.b, Optional.of(blockposition)); + } else { + this.l(); + } + } else if (iblockdata.getBlock() == Blocks.PISTON_HEAD) { + enumdirection = (EnumDirection) iblockdata.get(BlockPistonExtension.FACING); + if (this.world.isEmpty(blockposition.shift(enumdirection))) { + blockposition = blockposition.shift(enumdirection); + this.datawatcher.set(EntityShulker.b, Optional.of(blockposition)); + } else { + this.l(); + } + } else { + this.l(); + } + } + + BlockPosition blockposition1 = blockposition.shift(this.dy()); + + if (!this.world.q(blockposition1)) { + boolean flag = false; + EnumDirection[] aenumdirection = EnumDirection.values(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection1 = aenumdirection[j]; + + blockposition1 = blockposition.shift(enumdirection1); + if (this.world.q(blockposition1)) { + this.datawatcher.set(EntityShulker.a, enumdirection1); + flag = true; + break; + } + } + + if (!flag) { + this.l(); + } + } + + BlockPosition blockposition2 = blockposition.shift(this.dy().opposite()); + + if (this.world.q(blockposition2)) { + this.l(); + } + } + + f = (float) this.dA() * 0.01F; + this.bF = this.bG; + if (this.bG > f) { + this.bG = MathHelper.a(this.bG - 0.05F, f, 1.0F); + } else if (this.bG < f) { + this.bG = MathHelper.a(this.bG + 0.05F, 0.0F, f); + } + + if (blockposition != null) { + if (this.world.isClientSide) { + if (this.bI > 0 && this.bH != null) { + --this.bI; + } else { + this.bH = blockposition; + } + } + + this.locX = (double) blockposition.getX() + 0.5D; + this.locY = (double) blockposition.getY(); + this.locZ = (double) blockposition.getZ() + 0.5D; + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + this.N = this.locX; + this.O = this.locY; + this.P = this.locZ; + double d0 = 0.5D - (double) MathHelper.sin((0.5F + this.bG) * 3.1415927F) * 0.5D; + double d1 = 0.5D - (double) MathHelper.sin((0.5F + this.bF) * 3.1415927F) * 0.5D; + double d2 = d0 - d1; + double d3 = 0.0D; + double d4 = 0.0D; + double d5 = 0.0D; + EnumDirection enumdirection2 = this.dy(); + + switch (enumdirection2) { + case DOWN: + this.a(new AxisAlignedBB(this.locX - 0.5D, this.locY, this.locZ - 0.5D, this.locX + 0.5D, this.locY + 1.0D + d0, this.locZ + 0.5D)); + d4 = d2; + break; + case UP: + this.a(new AxisAlignedBB(this.locX - 0.5D, this.locY - d0, this.locZ - 0.5D, this.locX + 0.5D, this.locY + 1.0D, this.locZ + 0.5D)); + d4 = -d2; + break; + case NORTH: + this.a(new AxisAlignedBB(this.locX - 0.5D, this.locY, this.locZ - 0.5D, this.locX + 0.5D, this.locY + 1.0D, this.locZ + 0.5D + d0)); + d5 = d2; + break; + case SOUTH: + this.a(new AxisAlignedBB(this.locX - 0.5D, this.locY, this.locZ - 0.5D - d0, this.locX + 0.5D, this.locY + 1.0D, this.locZ + 0.5D)); + d5 = -d2; + break; + case WEST: + this.a(new AxisAlignedBB(this.locX - 0.5D, this.locY, this.locZ - 0.5D, this.locX + 0.5D + d0, this.locY + 1.0D, this.locZ + 0.5D)); + d3 = d2; + break; + case EAST: + this.a(new AxisAlignedBB(this.locX - 0.5D - d0, this.locY, this.locZ - 0.5D, this.locX + 0.5D, this.locY + 1.0D, this.locZ + 0.5D)); + d3 = -d2; + } + + if (d2 > 0.0D) { + List list = this.world.getEntities(this, this.getBoundingBox()); + + if (!list.isEmpty()) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + if (!(entity instanceof EntityShulker) && !entity.noclip) { + entity.move(EnumMoveType.SHULKER, d3, d4, d5); + } + } + } + } + } + + } + + public void move(EnumMoveType enummovetype, double d0, double d1, double d2) { + if (enummovetype == EnumMoveType.SHULKER_BOX) { + this.l(); + } else { + super.move(enummovetype, d0, d1, d2); + } + + } + + public void setPosition(double d0, double d1, double d2) { + super.setPosition(d0, d1, d2); + if (this.datawatcher != null && this.ticksLived != 0) { + Optional optional = (Optional) this.datawatcher.get(EntityShulker.b); + Optional optional1 = Optional.of(new BlockPosition(d0, d1, d2)); + + if (!optional1.equals(optional)) { + this.datawatcher.set(EntityShulker.b, optional1); + this.datawatcher.set(EntityShulker.c, (byte) 0); + this.impulse = true; + } + + } + } + + protected boolean l() { + if (!this.isNoAI() && this.isAlive()) { + BlockPosition blockposition = new BlockPosition(this); + + for (int i = 0; i < 5; ++i) { + BlockPosition blockposition1 = blockposition.a(8 - this.random.nextInt(17), 8 - this.random.nextInt(17), 8 - this.random.nextInt(17)); + + if (blockposition1.getY() > 0 && this.world.isEmpty(blockposition1) && this.world.i((Entity) this) && this.world.getCubes(this, new AxisAlignedBB(blockposition1))) { + boolean flag = false; + EnumDirection[] aenumdirection = EnumDirection.values(); + int j = aenumdirection.length; + + for (int k = 0; k < j; ++k) { + EnumDirection enumdirection = aenumdirection[k]; + + if (this.world.q(blockposition1.shift(enumdirection))) { + // CraftBukkit start + EntityTeleportEvent teleport = new EntityTeleportEvent(this.getBukkitEntity(), this.getBukkitEntity().getLocation(), new Location(this.world.getWorld(), blockposition1.getX(), blockposition1.getY(), blockposition1.getZ())); + this.world.getServer().getPluginManager().callEvent(teleport); + if (!teleport.isCancelled()) { + Location to = teleport.getTo(); + blockposition1 = new BlockPosition(to.getX(), to.getY(), to.getZ()); + + this.datawatcher.set(EntityShulker.a, enumdirection); + flag = true; + } + // CraftBukkit end + break; + } + } + + if (flag) { + this.a(SoundEffects.ENTITY_SHULKER_TELEPORT, 1.0F, 1.0F); + this.datawatcher.set(EntityShulker.b, Optional.of(blockposition1)); + this.datawatcher.set(EntityShulker.c, (byte) 0); + this.setGoalTarget((EntityLiving) null); + return true; + } + } + } + + return false; + } else { + return true; + } + } + + public void movementTick() { + super.movementTick(); + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + this.aR = 180.0F; + this.aQ = 180.0F; + this.yaw = 180.0F; + } + + public void a(DataWatcherObject datawatcherobject) { + if (EntityShulker.b.equals(datawatcherobject) && this.world.isClientSide && !this.isPassenger()) { + BlockPosition blockposition = this.dz(); + + if (blockposition != null) { + if (this.bH == null) { + this.bH = blockposition; + } else { + this.bI = 6; + } + + this.locX = (double) blockposition.getX() + 0.5D; + this.locY = (double) blockposition.getY(); + this.locZ = (double) blockposition.getZ() + 0.5D; + if (valid) world.entityJoinedWorld(this, false); // CraftBukkit + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + this.N = this.locX; + this.O = this.locY; + this.P = this.locZ; + } + } + + super.a(datawatcherobject); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.dG()) { + Entity entity = damagesource.j(); + + if (entity instanceof EntityArrow) { + return false; + } + } + + if (super.damageEntity(damagesource, f)) { + if ((double) this.getHealth() < (double) this.getMaxHealth() * 0.5D && this.random.nextInt(4) == 0) { + this.l(); + } + + return true; + } else { + return false; + } + } + + private boolean dG() { + return this.dA() == 0; + } + + @Nullable + public AxisAlignedBB al() { + return this.isAlive() ? this.getBoundingBox() : null; + } + + public EnumDirection dy() { + return (EnumDirection) this.datawatcher.get(EntityShulker.a); + } + + @Nullable + public BlockPosition dz() { + return (BlockPosition) ((Optional) this.datawatcher.get(EntityShulker.b)).orElse((Object) null); + } + + public void g(@Nullable BlockPosition blockposition) { + this.datawatcher.set(EntityShulker.b, Optional.ofNullable(blockposition)); + } + + public int dA() { + return (Byte) this.datawatcher.get(EntityShulker.c); + } + + public void a(int i) { + if (!this.world.isClientSide) { + this.getAttributeInstance(GenericAttributes.h).c(EntityShulker.bE); + if (i == 0) { + this.getAttributeInstance(GenericAttributes.h).b(EntityShulker.bE); + this.a(SoundEffects.ENTITY_SHULKER_CLOSE, 1.0F, 1.0F); + } else { + this.a(SoundEffects.ENTITY_SHULKER_OPEN, 1.0F, 1.0F); + } + } + + this.datawatcher.set(EntityShulker.c, (byte) i); + } + + public float getHeadHeight() { + return 0.5F; + } + + public int K() { + return 180; + } + + public int L() { + return 180; + } + + public void collide(Entity entity) {} + + public float aM() { + return 0.0F; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.F; + } + + static class c extends PathfinderGoalNearestAttackableTarget { + + public c(EntityShulker entityshulker) { + super(entityshulker, EntityLiving.class, 10, true, false, (entityliving) -> { + return entityliving instanceof IMonster; + }); + } + + public boolean a() { + return this.e.getScoreboardTeam() == null ? false : super.a(); + } + + protected AxisAlignedBB a(double d0) { + EnumDirection enumdirection = ((EntityShulker) this.e).dy(); + + return enumdirection.k() == EnumDirection.EnumAxis.X ? this.e.getBoundingBox().grow(4.0D, d0, d0) : (enumdirection.k() == EnumDirection.EnumAxis.Z ? this.e.getBoundingBox().grow(d0, d0, 4.0D) : this.e.getBoundingBox().grow(d0, 4.0D, d0)); + } + } + + class d extends PathfinderGoalNearestAttackableTarget { + + public d(EntityShulker entityshulker) { + super(entityshulker, EntityHuman.class, true); + } + + public boolean a() { + return EntityShulker.this.world.getDifficulty() == EnumDifficulty.PEACEFUL ? false : super.a(); + } + + protected AxisAlignedBB a(double d0) { + EnumDirection enumdirection = ((EntityShulker) this.e).dy(); + + return enumdirection.k() == EnumDirection.EnumAxis.X ? this.e.getBoundingBox().grow(4.0D, d0, d0) : (enumdirection.k() == EnumDirection.EnumAxis.Z ? this.e.getBoundingBox().grow(d0, d0, 4.0D) : this.e.getBoundingBox().grow(d0, 4.0D, d0)); + } + } + + class a extends PathfinderGoal { + + private int b; + + public a() { + this.a(3); + } + + public boolean a() { + EntityLiving entityliving = EntityShulker.this.getGoalTarget(); + + return entityliving != null && entityliving.isAlive() ? EntityShulker.this.world.getDifficulty() != EnumDifficulty.PEACEFUL : false; + } + + public void c() { + this.b = 20; + EntityShulker.this.a(100); + } + + public void d() { + EntityShulker.this.a(0); + } + + public void e() { + if (EntityShulker.this.world.getDifficulty() != EnumDifficulty.PEACEFUL) { + --this.b; + EntityLiving entityliving = EntityShulker.this.getGoalTarget(); + + EntityShulker.this.getControllerLook().a(entityliving, 180.0F, 180.0F); + double d0 = EntityShulker.this.h(entityliving); + + if (d0 < 400.0D) { + if (this.b <= 0) { + this.b = 20 + EntityShulker.this.random.nextInt(10) * 20 / 2; + EntityShulkerBullet entityshulkerbullet = new EntityShulkerBullet(EntityShulker.this.world, EntityShulker.this, entityliving, EntityShulker.this.dy().k()); + + EntityShulker.this.world.addEntity(entityshulkerbullet); + EntityShulker.this.a(SoundEffects.ENTITY_SHULKER_SHOOT, 2.0F, (EntityShulker.this.random.nextFloat() - EntityShulker.this.random.nextFloat()) * 0.2F + 1.0F); + } + } else { + EntityShulker.this.setGoalTarget((EntityLiving) null); + } + + super.e(); + } + } + } + + class e extends PathfinderGoal { + + private int b; + + private e() {} + + public boolean a() { + return EntityShulker.this.getGoalTarget() == null && EntityShulker.this.random.nextInt(40) == 0; + } + + public boolean b() { + return EntityShulker.this.getGoalTarget() == null && this.b > 0; + } + + public void c() { + this.b = 20 * (1 + EntityShulker.this.random.nextInt(3)); + EntityShulker.this.a(30); + } + + public void d() { + if (EntityShulker.this.getGoalTarget() == null) { + EntityShulker.this.a(0); + } + + } + + public void e() { + --this.b; + } + } + + class b extends EntityAIBodyControl { + + public b(EntityLiving entityliving) { + super(entityliving); + } + + public void a() {} + } +} diff --git a/src/main/java/net/minecraft/server/EntityShulkerBullet.java b/src/main/java/net/minecraft/server/EntityShulkerBullet.java new file mode 100644 index 000000000000..4b95a3a67ff0 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityShulkerBullet.java @@ -0,0 +1,341 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; +import javax.annotation.Nullable; + +public class EntityShulkerBullet extends Entity { + + private EntityLiving shooter; + private Entity target; + @Nullable + private EnumDirection c; + private int d; + private double e; + private double f; + private double g; + @Nullable + private UUID h; + private BlockPosition aw; + @Nullable + private UUID ax; + private BlockPosition ay; + + public EntityShulkerBullet(World world) { + super(EntityTypes.SHULKER_BULLET, world); + this.setSize(0.3125F, 0.3125F); + this.noclip = true; + } + + public EntityShulkerBullet(World world, EntityLiving entityliving, Entity entity, EnumDirection.EnumAxis enumdirection_enumaxis) { + this(world); + this.shooter = entityliving; + BlockPosition blockposition = new BlockPosition(entityliving); + double d0 = (double) blockposition.getX() + 0.5D; + double d1 = (double) blockposition.getY() + 0.5D; + double d2 = (double) blockposition.getZ() + 0.5D; + + this.setPositionRotation(d0, d1, d2, this.yaw, this.pitch); + this.target = entity; + this.c = EnumDirection.UP; + this.a(enumdirection_enumaxis); + projectileSource = (org.bukkit.entity.LivingEntity) entityliving.getBukkitEntity(); // CraftBukkit + } + + // CraftBukkit start + public EntityLiving getShooter() { + return this.shooter; + } + + public void setShooter(EntityLiving e) { + this.shooter = e; + } + + public Entity getTarget() { + return this.target; + } + + public void setTarget(Entity e) { + this.target = e; + this.c = EnumDirection.UP; + this.a(EnumDirection.EnumAxis.X); + } + // CraftBukkit end + + public SoundCategory bV() { + return SoundCategory.HOSTILE; + } + + protected void b(NBTTagCompound nbttagcompound) { + BlockPosition blockposition; + NBTTagCompound nbttagcompound1; + + if (this.shooter != null) { + blockposition = new BlockPosition(this.shooter); + nbttagcompound1 = GameProfileSerializer.a(this.shooter.getUniqueID()); + nbttagcompound1.setInt("X", blockposition.getX()); + nbttagcompound1.setInt("Y", blockposition.getY()); + nbttagcompound1.setInt("Z", blockposition.getZ()); + nbttagcompound.set("Owner", nbttagcompound1); + } + + if (this.target != null) { + blockposition = new BlockPosition(this.target); + nbttagcompound1 = GameProfileSerializer.a(this.target.getUniqueID()); + nbttagcompound1.setInt("X", blockposition.getX()); + nbttagcompound1.setInt("Y", blockposition.getY()); + nbttagcompound1.setInt("Z", blockposition.getZ()); + nbttagcompound.set("Target", nbttagcompound1); + } + + if (this.c != null) { + nbttagcompound.setInt("Dir", this.c.a()); + } + + nbttagcompound.setInt("Steps", this.d); + nbttagcompound.setDouble("TXD", this.e); + nbttagcompound.setDouble("TYD", this.f); + nbttagcompound.setDouble("TZD", this.g); + } + + protected void a(NBTTagCompound nbttagcompound) { + this.d = nbttagcompound.getInt("Steps"); + this.e = nbttagcompound.getDouble("TXD"); + this.f = nbttagcompound.getDouble("TYD"); + this.g = nbttagcompound.getDouble("TZD"); + if (nbttagcompound.hasKeyOfType("Dir", 99)) { + this.c = EnumDirection.fromType1(nbttagcompound.getInt("Dir")); + } + + NBTTagCompound nbttagcompound1; + + if (nbttagcompound.hasKeyOfType("Owner", 10)) { + nbttagcompound1 = nbttagcompound.getCompound("Owner"); + this.h = GameProfileSerializer.b(nbttagcompound1); + this.aw = new BlockPosition(nbttagcompound1.getInt("X"), nbttagcompound1.getInt("Y"), nbttagcompound1.getInt("Z")); + } + + if (nbttagcompound.hasKeyOfType("Target", 10)) { + nbttagcompound1 = nbttagcompound.getCompound("Target"); + this.ax = GameProfileSerializer.b(nbttagcompound1); + this.ay = new BlockPosition(nbttagcompound1.getInt("X"), nbttagcompound1.getInt("Y"), nbttagcompound1.getInt("Z")); + } + + } + + protected void x_() {} + + private void a(@Nullable EnumDirection enumdirection) { + this.c = enumdirection; + } + + private void a(@Nullable EnumDirection.EnumAxis enumdirection_enumaxis) { + double d0 = 0.5D; + BlockPosition blockposition; + + if (this.target == null) { + blockposition = (new BlockPosition(this)).down(); + } else { + d0 = (double) this.target.length * 0.5D; + blockposition = new BlockPosition(this.target.locX, this.target.locY + d0, this.target.locZ); + } + + double d1 = (double) blockposition.getX() + 0.5D; + double d2 = (double) blockposition.getY() + d0; + double d3 = (double) blockposition.getZ() + 0.5D; + EnumDirection enumdirection = null; + + if (blockposition.g(this.locX, this.locY, this.locZ) >= 4.0D) { + BlockPosition blockposition1 = new BlockPosition(this); + List list = Lists.newArrayList(); + + if (enumdirection_enumaxis != EnumDirection.EnumAxis.X) { + if (blockposition1.getX() < blockposition.getX() && this.world.isEmpty(blockposition1.east())) { + list.add(EnumDirection.EAST); + } else if (blockposition1.getX() > blockposition.getX() && this.world.isEmpty(blockposition1.west())) { + list.add(EnumDirection.WEST); + } + } + + if (enumdirection_enumaxis != EnumDirection.EnumAxis.Y) { + if (blockposition1.getY() < blockposition.getY() && this.world.isEmpty(blockposition1.up())) { + list.add(EnumDirection.UP); + } else if (blockposition1.getY() > blockposition.getY() && this.world.isEmpty(blockposition1.down())) { + list.add(EnumDirection.DOWN); + } + } + + if (enumdirection_enumaxis != EnumDirection.EnumAxis.Z) { + if (blockposition1.getZ() < blockposition.getZ() && this.world.isEmpty(blockposition1.south())) { + list.add(EnumDirection.SOUTH); + } else if (blockposition1.getZ() > blockposition.getZ() && this.world.isEmpty(blockposition1.north())) { + list.add(EnumDirection.NORTH); + } + } + + enumdirection = EnumDirection.a(this.random); + if (list.isEmpty()) { + for (int i = 5; !this.world.isEmpty(blockposition1.shift(enumdirection)) && i > 0; --i) { + enumdirection = EnumDirection.a(this.random); + } + } else { + enumdirection = (EnumDirection) list.get(this.random.nextInt(list.size())); + } + + d1 = this.locX + (double) enumdirection.getAdjacentX(); + d2 = this.locY + (double) enumdirection.getAdjacentY(); + d3 = this.locZ + (double) enumdirection.getAdjacentZ(); + } + + this.a(enumdirection); + double d4 = d1 - this.locX; + double d5 = d2 - this.locY; + double d6 = d3 - this.locZ; + double d7 = (double) MathHelper.sqrt(d4 * d4 + d5 * d5 + d6 * d6); + + if (d7 == 0.0D) { + this.e = 0.0D; + this.f = 0.0D; + this.g = 0.0D; + } else { + this.e = d4 / d7 * 0.15D; + this.f = d5 / d7 * 0.15D; + this.g = d6 / d7 * 0.15D; + } + + this.impulse = true; + this.d = 10 + this.random.nextInt(5) * 10; + } + + public void tick() { + if (!this.world.isClientSide && this.world.getDifficulty() == EnumDifficulty.PEACEFUL) { + this.die(); + } else { + super.tick(); + if (!this.world.isClientSide) { + List list; + Iterator iterator; + EntityLiving entityliving; + + if (this.target == null && this.ax != null) { + list = this.world.a(EntityLiving.class, new AxisAlignedBB(this.ay.a(-2, -2, -2), this.ay.a(2, 2, 2))); + iterator = list.iterator(); + + while (iterator.hasNext()) { + entityliving = (EntityLiving) iterator.next(); + if (entityliving.getUniqueID().equals(this.ax)) { + this.target = entityliving; + break; + } + } + + this.ax = null; + } + + if (this.shooter == null && this.h != null) { + list = this.world.a(EntityLiving.class, new AxisAlignedBB(this.aw.a(-2, -2, -2), this.aw.a(2, 2, 2))); + iterator = list.iterator(); + + while (iterator.hasNext()) { + entityliving = (EntityLiving) iterator.next(); + if (entityliving.getUniqueID().equals(this.h)) { + this.shooter = entityliving; + break; + } + } + + this.h = null; + } + + if (this.target != null && this.target.isAlive() && (!(this.target instanceof EntityHuman) || !((EntityHuman) this.target).isSpectator())) { + this.e = MathHelper.a(this.e * 1.025D, -1.0D, 1.0D); + this.f = MathHelper.a(this.f * 1.025D, -1.0D, 1.0D); + this.g = MathHelper.a(this.g * 1.025D, -1.0D, 1.0D); + this.motX += (this.e - this.motX) * 0.2D; + this.motY += (this.f - this.motY) * 0.2D; + this.motZ += (this.g - this.motZ) * 0.2D; + } else if (!this.isNoGravity()) { + this.motY -= 0.04D; + } + + MovingObjectPosition movingobjectposition = ProjectileHelper.a(this, true, false, this.shooter); + + if (movingobjectposition != null) { + this.a(movingobjectposition); + } + } + + this.setPosition(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + ProjectileHelper.a(this, 0.5F); + if (this.world.isClientSide) { + this.world.addParticle(Particles.r, this.locX - this.motX, this.locY - this.motY + 0.15D, this.locZ - this.motZ, 0.0D, 0.0D, 0.0D); + } else if (this.target != null && !this.target.dead) { + if (this.d > 0) { + --this.d; + if (this.d == 0) { + this.a(this.c == null ? null : this.c.k()); + } + } + + if (this.c != null) { + BlockPosition blockposition = new BlockPosition(this); + EnumDirection.EnumAxis enumdirection_enumaxis = this.c.k(); + + if (this.world.q(blockposition.shift(this.c))) { + this.a(enumdirection_enumaxis); + } else { + BlockPosition blockposition1 = new BlockPosition(this.target); + + if (enumdirection_enumaxis == EnumDirection.EnumAxis.X && blockposition.getX() == blockposition1.getX() || enumdirection_enumaxis == EnumDirection.EnumAxis.Z && blockposition.getZ() == blockposition1.getZ() || enumdirection_enumaxis == EnumDirection.EnumAxis.Y && blockposition.getY() == blockposition1.getY()) { + this.a(enumdirection_enumaxis); + } + } + } + } + + } + } + + public boolean isBurning() { + return false; + } + + public float az() { + return 1.0F; + } + + protected void a(MovingObjectPosition movingobjectposition) { + org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this, movingobjectposition); // Craftbukkit - Call event + if (movingobjectposition.entity == null) { + ((WorldServer) this.world).a(Particles.u, this.locX, this.locY, this.locZ, 2, 0.2D, 0.2D, 0.2D, 0.0D); + this.a(SoundEffects.ENTITY_SHULKER_BULLET_HIT, 1.0F, 1.0F); + } else { + boolean flag = movingobjectposition.entity.damageEntity(DamageSource.a(this, this.shooter).c(), 4.0F); + + if (flag) { + this.a(this.shooter, movingobjectposition.entity); + if (movingobjectposition.entity instanceof EntityLiving) { + ((EntityLiving) movingobjectposition.entity).addEffect(new MobEffect(MobEffects.LEVITATION, 200), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit + } + } + } + + this.die(); + } + + public boolean isInteractable() { + return true; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (!this.world.isClientSide) { + this.a(SoundEffects.ENTITY_SHULKER_BULLET_HURT, 1.0F, 1.0F); + ((WorldServer) this.world).a(Particles.h, this.locX, this.locY, this.locZ, 15, 0.2D, 0.2D, 0.2D, 0.0D); + this.die(); + } + + return true; + } +} diff --git a/src/main/java/net/minecraft/server/EntitySilverfish.java b/src/main/java/net/minecraft/server/EntitySilverfish.java new file mode 100644 index 000000000000..ba40e03fccf5 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntitySilverfish.java @@ -0,0 +1,227 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; + +public class EntitySilverfish extends EntityMonster { + + private EntitySilverfish.PathfinderGoalSilverfishWakeOthers a; + + public EntitySilverfish(World world) { + super(EntityTypes.SILVERFISH, world); + this.setSize(0.4F, 0.3F); + } + + protected void n() { + this.a = new EntitySilverfish.PathfinderGoalSilverfishWakeOthers(this); + this.goalSelector.a(1, new PathfinderGoalFloat(this)); + this.goalSelector.a(3, this.a); + this.goalSelector.a(4, new PathfinderGoalMeleeAttack(this, 1.0D, false)); + this.goalSelector.a(5, new EntitySilverfish.PathfinderGoalSilverfishHideInBlock(this)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, true, new Class[0])); + this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget<>(this, EntityHuman.class, true)); + } + + public double aI() { + return 0.1D; + } + + public float getHeadHeight() { + return 0.1F; + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(8.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.25D); + this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue(1.0D); + } + + protected boolean playStepSound() { + return false; + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_SILVERFISH_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_SILVERFISH_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_SILVERFISH_DEATH; + } + + protected void a(BlockPosition blockposition, IBlockData iblockdata) { + this.a(SoundEffects.ENTITY_SILVERFISH_STEP, 0.15F, 1.0F); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else { + if ((damagesource instanceof EntityDamageSource || damagesource == DamageSource.MAGIC) && this.a != null) { + this.a.g(); + } + + return super.damageEntity(damagesource, f); + } + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.B; + } + + public void tick() { + this.aQ = this.yaw; + super.tick(); + } + + public void k(float f) { + this.yaw = f; + super.k(f); + } + + public float a(BlockPosition blockposition, IWorldReader iworldreader) { + return BlockMonsterEggs.k(iworldreader.getType(blockposition.down())) ? 10.0F : super.a(blockposition, iworldreader); + } + + protected boolean K_() { + return true; + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + if (super.a(generatoraccess, flag)) { + EntityHuman entityhuman = generatoraccess.b(this, 5.0D); + + return !(entityhuman != null && !entityhuman.affectsSpawning) && entityhuman == null; // Paper - Affects Spawning API + } else { + return false; + } + } + + public EnumMonsterType getMonsterType() { + return EnumMonsterType.ARTHROPOD; + } + + static class PathfinderGoalSilverfishHideInBlock extends PathfinderGoalRandomStroll { + + private EnumDirection h; + private boolean i; + + public PathfinderGoalSilverfishHideInBlock(EntitySilverfish entitysilverfish) { + super(entitysilverfish, 1.0D, 10); + this.a(1); + } + + public boolean a() { + if (this.a.getGoalTarget() != null) { + return false; + } else if (!this.a.getNavigation().p()) { + return false; + } else { + Random random = this.a.getRandom(); + + if (this.a.world.getGameRules().getBoolean("mobGriefing") && random.nextInt(10) == 0) { + this.h = EnumDirection.a(random); + BlockPosition blockposition = (new BlockPosition(this.a.locX, this.a.locY + 0.5D, this.a.locZ)).shift(this.h); + IBlockData iblockdata = this.a.world.getType(blockposition); + + if (BlockMonsterEggs.k(iblockdata)) { + this.i = true; + return true; + } + } + + this.i = false; + return super.a(); + } + } + + public boolean b() { + return this.i ? false : super.b(); + } + + public void c() { + if (!this.i) { + super.c(); + } else { + World world = this.a.world; + BlockPosition blockposition = (new BlockPosition(this.a.locX, this.a.locY + 0.5D, this.a.locZ)).shift(this.h); + IBlockData iblockdata = world.getType(blockposition); + + if (BlockMonsterEggs.k(iblockdata)) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.a, blockposition, BlockMonsterEggs.f(iblockdata.getBlock())).isCancelled()) { + return; + } + // CraftBukkit end + world.setTypeAndData(blockposition, BlockMonsterEggs.f(iblockdata.getBlock()), 3); + this.a.doSpawnEffect(); + this.a.die(); + } + + } + } + } + + static class PathfinderGoalSilverfishWakeOthers extends PathfinderGoal { + + private final EntitySilverfish silverfish; + private int b; + + public PathfinderGoalSilverfishWakeOthers(EntitySilverfish entitysilverfish) { + this.silverfish = entitysilverfish; + } + + public void g() { + if (this.b == 0) { + this.b = 20; + } + + } + + public boolean a() { + return this.b > 0; + } + + public void e() { + --this.b; + if (this.b <= 0) { + World world = this.silverfish.world; + Random random = this.silverfish.getRandom(); + BlockPosition blockposition = new BlockPosition(this.silverfish); + + for (int i = 0; i <= 5 && i >= -5; i = (i <= 0 ? 1 : 0) - i) { + for (int j = 0; j <= 10 && j >= -10; j = (j <= 0 ? 1 : 0) - j) { + for (int k = 0; k <= 10 && k >= -10; k = (k <= 0 ? 1 : 0) - k) { + BlockPosition blockposition1 = blockposition.a(j, i, k); + IBlockData iblockdata = world.getType(blockposition1); + Block block = iblockdata.getBlock(); + + if (block instanceof BlockMonsterEggs) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.silverfish, blockposition1, Blocks.AIR.getBlockData()).isCancelled()) { + continue; + } + // CraftBukkit end + if (world.getGameRules().getBoolean("mobGriefing")) { + world.setAir(blockposition1, true); + } else { + world.setTypeAndData(blockposition1, ((BlockMonsterEggs) block).d().getBlockData(), 3); + } + + if (random.nextBoolean()) { + return; + } + } + } + } + } + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/EntitySkeleton.java b/src/main/java/net/minecraft/server/EntitySkeleton.java new file mode 100644 index 000000000000..592fadd503b3 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntitySkeleton.java @@ -0,0 +1,64 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class EntitySkeleton extends EntitySkeletonAbstract { + + public EntitySkeleton(World world) { + super(EntityTypes.SKELETON, world); + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.av; + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_SKELETON_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_SKELETON_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_SKELETON_DEATH; + } + + SoundEffect l() { + return SoundEffects.ENTITY_SKELETON_STEP; + } + + public void die(DamageSource damagesource) { + // super.die(damagesource); // CraftBukkit + if (damagesource.getEntity() instanceof EntityCreeper) { + EntityCreeper entitycreeper = (EntityCreeper) damagesource.getEntity(); + + if (entitycreeper.isPowered() && entitycreeper.canCauseHeadDrop()) { + entitycreeper.setCausedHeadDrop(); + this.a((IMaterial) Items.SKELETON_SKULL); + } + } + super.die(damagesource); // CraftBukkit - moved from above + + } + + protected EntityArrow a(float f) { + ItemStack itemstack = this.getEquipment(EnumItemSlot.OFFHAND); + + if (itemstack.getItem() == Items.SPECTRAL_ARROW) { + EntitySpectralArrow entityspectralarrow = new EntitySpectralArrow(this.world, this); + + entityspectralarrow.a((EntityLiving) this, f); + return entityspectralarrow; + } else { + EntityArrow entityarrow = super.a(f); + + if (itemstack.getItem() == Items.TIPPED_ARROW && entityarrow instanceof EntityTippedArrow) { + ((EntityTippedArrow) entityarrow).b(itemstack); + } + + return entityarrow; + } + } +} diff --git a/src/main/java/net/minecraft/server/EntitySkeletonAbstract.java b/src/main/java/net/minecraft/server/EntitySkeletonAbstract.java new file mode 100644 index 000000000000..6e2ee04c77f3 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntitySkeletonAbstract.java @@ -0,0 +1,200 @@ +package net.minecraft.server; + +import java.time.LocalDate; +import java.time.temporal.ChronoField; +import javax.annotation.Nullable; + +public abstract class EntitySkeletonAbstract extends EntityMonster implements IRangedEntity { + + private static final DataWatcherObject a = DataWatcher.a(EntitySkeletonAbstract.class, DataWatcherRegistry.i); + private final PathfinderGoalBowShoot b = new PathfinderGoalBowShoot<>(this, 1.0D, 20, 15.0F); + private final PathfinderGoalMeleeAttack c = new PathfinderGoalMeleeAttack(this, 1.2D, false) { + public void d() { + super.d(); + EntitySkeletonAbstract.this.s(false); + } + + public void c() { + super.c(); + EntitySkeletonAbstract.this.s(true); + } + }; + + protected EntitySkeletonAbstract(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.setSize(0.6F, 1.99F); + this.dz(); + } + + protected void n() { + this.goalSelector.a(2, new PathfinderGoalRestrictSun(this)); + this.goalSelector.a(3, new PathfinderGoalFleeSun(this, 1.0D)); + this.goalSelector.a(3, new PathfinderGoalAvoidTarget<>(this, EntityWolf.class, 6.0F, 1.0D, 1.2D)); + this.goalSelector.a(5, new PathfinderGoalRandomStrollLand(this, 1.0D)); + this.goalSelector.a(6, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(6, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, false, new Class[0])); + this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget<>(this, EntityHuman.class, true)); + this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget<>(this, EntityIronGolem.class, true)); + this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget<>(this, EntityTurtle.class, 10, true, false, EntityTurtle.bC)); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.25D); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntitySkeletonAbstract.a, false); + } + + protected void a(BlockPosition blockposition, IBlockData iblockdata) { + this.a(this.l(), 0.15F, 1.0F); + } + + abstract SoundEffect l(); + + public EnumMonsterType getMonsterType() { + return EnumMonsterType.UNDEAD; + } + + public void movementTick() { + boolean flag = this.dq(); + + if (flag) { + ItemStack itemstack = this.getEquipment(EnumItemSlot.HEAD); + + if (!itemstack.isEmpty()) { + if (itemstack.e()) { + itemstack.setDamage(itemstack.getDamage() + this.random.nextInt(2)); + if (itemstack.getDamage() >= itemstack.h()) { + this.c(itemstack); + this.setSlot(EnumItemSlot.HEAD, ItemStack.a); + } + } + + flag = false; + } + + if (flag) { + this.setOnFire(8); + } + } + + super.movementTick(); + } + + public void aH() { + super.aH(); + if (this.getVehicle() instanceof EntityCreature) { + EntityCreature entitycreature = (EntityCreature) this.getVehicle(); + + this.aQ = entitycreature.aQ; + } + + } + + protected void a(DifficultyDamageScaler difficultydamagescaler) { + super.a(difficultydamagescaler); + this.setSlot(EnumItemSlot.MAINHAND, new ItemStack(Items.BOW)); + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + groupdataentity = super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + this.a(difficultydamagescaler); + this.b(difficultydamagescaler); + this.dz(); + this.p(this.random.nextFloat() < 0.55F * difficultydamagescaler.d()); + if (this.getEquipment(EnumItemSlot.HEAD).isEmpty()) { + LocalDate localdate = LocalDate.now(); + int i = localdate.get(ChronoField.DAY_OF_MONTH); + int j = localdate.get(ChronoField.MONTH_OF_YEAR); + + if (j == 10 && i == 31 && this.random.nextFloat() < 0.25F) { + this.setSlot(EnumItemSlot.HEAD, new ItemStack(this.random.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN)); + this.dropChanceArmor[EnumItemSlot.HEAD.b()] = 0.0F; + } + } + + return groupdataentity; + } + + public void dz() { + if (this.world != null && !this.world.isClientSide) { + this.goalSelector.a((PathfinderGoal) this.c); + this.goalSelector.a((PathfinderGoal) this.b); + ItemStack itemstack = this.getItemInMainHand(); + + if (itemstack.getItem() == Items.BOW) { + byte b0 = 20; + + if (this.world.getDifficulty() != EnumDifficulty.HARD) { + b0 = 40; + } + + this.b.b(b0); + this.goalSelector.a(4, this.b); + } else { + this.goalSelector.a(4, this.c); + } + + } + } + + public void a(EntityLiving entityliving, float f) { + EntityArrow entityarrow = this.a(f); + double d0 = entityliving.locX - this.locX; + double d1 = entityliving.getBoundingBox().minY + (double) (entityliving.length / 3.0F) - entityarrow.locY; + double d2 = entityliving.locZ - this.locZ; + double d3 = (double) MathHelper.sqrt(d0 * d0 + d2 * d2); + + entityarrow.shoot(d0, d1 + d3 * 0.20000000298023224D, d2, 1.6F, (float) (14 - this.world.getDifficulty().a() * 4)); + // CraftBukkit start + org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, this.getItemInMainHand(), this.getItemInOffHand(), entityarrow, 0.8F); // Paper + if (event.isCancelled()) { + event.getProjectile().remove(); + return; + } + + if (event.getProjectile() == entityarrow.getBukkitEntity()) { + world.addEntity(entityarrow); + } + // CraftBukkit end + this.a(SoundEffects.ENTITY_SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F)); + // this.world.addEntity(entityarrow); // CraftBukkit - moved up + } + + protected EntityArrow a(float f) { + EntityTippedArrow entitytippedarrow = new EntityTippedArrow(this.world, this); + + entitytippedarrow.a((EntityLiving) this, f); + return entitytippedarrow; + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.dz(); + } + + public void setSlot(EnumItemSlot enumitemslot, ItemStack itemstack) { + super.setSlot(enumitemslot, itemstack); + if (!this.world.isClientSide && enumitemslot == EnumItemSlot.MAINHAND) { + this.dz(); + } + + } + + public float getHeadHeight() { + return 1.74F; + } + + public double aI() { + return -0.6D; + } + + public void s(boolean flag) { + this.datawatcher.set(EntitySkeletonAbstract.a, flag); + } +} diff --git a/src/main/java/net/minecraft/server/EntitySkeletonWither.java b/src/main/java/net/minecraft/server/EntitySkeletonWither.java new file mode 100644 index 000000000000..88d323525c1a --- /dev/null +++ b/src/main/java/net/minecraft/server/EntitySkeletonWither.java @@ -0,0 +1,85 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class EntitySkeletonWither extends EntitySkeletonAbstract { + + public EntitySkeletonWither(World world) { + super(EntityTypes.WITHER_SKELETON, world); + this.setSize(0.7F, 2.4F); + this.fireProof = true; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.aw; + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_WITHER_SKELETON_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_WITHER_SKELETON_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_WITHER_SKELETON_DEATH; + } + + SoundEffect l() { + return SoundEffects.ENTITY_WITHER_SKELETON_STEP; + } + + public void die(DamageSource damagesource) { + // super.die(damagesource); // CraftBukkit + if (damagesource.getEntity() instanceof EntityCreeper) { + EntityCreeper entitycreeper = (EntityCreeper) damagesource.getEntity(); + + if (entitycreeper.isPowered() && entitycreeper.canCauseHeadDrop()) { + entitycreeper.setCausedHeadDrop(); + this.a((IMaterial) Items.WITHER_SKELETON_SKULL); + } + } + super.die(damagesource); // CraftBukkit - moved from above + + } + + protected void a(DifficultyDamageScaler difficultydamagescaler) { + this.setSlot(EnumItemSlot.MAINHAND, new ItemStack(Items.STONE_SWORD)); + } + + protected void b(DifficultyDamageScaler difficultydamagescaler) {} + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + GroupDataEntity groupdataentity1 = super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + + this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue(4.0D); + this.dz(); + return groupdataentity1; + } + + public float getHeadHeight() { + return 2.1F; + } + + public boolean B(Entity entity) { + if (!super.B(entity)) { + return false; + } else { + if (entity instanceof EntityLiving) { + ((EntityLiving) entity).addEffect(new MobEffect(MobEffects.WITHER, 200), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit + } + + return true; + } + } + + protected EntityArrow a(float f) { + EntityArrow entityarrow = super.a(f); + + entityarrow.setOnFire(100); + return entityarrow; + } +} diff --git a/src/main/java/net/minecraft/server/EntitySlice.java b/src/main/java/net/minecraft/server/EntitySlice.java new file mode 100644 index 000000000000..e2b75a09b86a --- /dev/null +++ b/src/main/java/net/minecraft/server/EntitySlice.java @@ -0,0 +1,133 @@ +package net.minecraft.server; + +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import java.util.AbstractSet; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class EntitySlice extends AbstractSet { + + private static final Set> a = Sets.newConcurrentHashSet(); // CraftBukkit + private final Map, List> b = Maps.newHashMap(); + private final Set> c = Sets.newIdentityHashSet(); + private final Class d; + private final List e = Lists.newArrayList(); + + public EntitySlice(Class oclass) { + this.d = oclass; + this.c.add(oclass); + this.b.put(oclass, this.e); + Iterator iterator = Lists.newArrayList(EntitySlice.a).iterator(); + + while (iterator.hasNext()) { + Class oclass1 = (Class) iterator.next(); + + this.a(oclass1); + } + + } + + protected void a(Class oclass) { + EntitySlice.a.add(oclass); + Iterator iterator = this.e.iterator(); // CraftBukkit - decompile error + + while (iterator.hasNext()) { + T t0 = iterator.next(); + + if (oclass.isAssignableFrom(t0.getClass())) { + this.a(t0, oclass); + } + } + + this.c.add(oclass); + } + + protected Class b(Class oclass) { + if (this.d.isAssignableFrom(oclass)) { + if (!this.c.contains(oclass)) { + this.a(oclass); + } + + return oclass; + } else { + throw new IllegalArgumentException("Don't know how to search for " + oclass); + } + } + + public boolean add(T t0) { + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + Class oclass = (Class) iterator.next(); + + if (oclass.isAssignableFrom(t0.getClass())) { + this.a(t0, oclass); + } + } + + return true; + } + + private void a(T t0, Class oclass) { + List list = (List) this.b.get(oclass); + + if (list == null) { + this.b.put(oclass, Lists.newArrayList(t0)); + } else { + list.add(t0); + } + + } + + public boolean remove(Object object) { + T t0 = (T) object; // CraftBukkit - decompile error + boolean flag = false; + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + Class oclass = (Class) iterator.next(); + + if (oclass.isAssignableFrom(t0.getClass())) { + List list = (List) this.b.get(oclass); + + if (list != null && list.remove(t0)) { + flag = true; + } + } + } + + return flag; + } + + public boolean contains(Object object) { + return Iterators.contains(this.c(object.getClass()).iterator(), object); + } + + public Iterable c(Class oclass) { + return () -> { + List list = (List) this.b.get(this.b(oclass)); + + if (list == null) { + return Collections.emptyIterator(); + } else { + Iterator iterator = list.iterator(); + + return Iterators.filter(iterator, oclass); + } + }; + } + + public Iterator iterator() { + return (Iterator) (this.e.isEmpty() ? Collections.emptyIterator() : Iterators.unmodifiableIterator(this.e.iterator())); + } + + public int size() { + return this.e.size(); + } +} diff --git a/src/main/java/net/minecraft/server/EntitySlime.java b/src/main/java/net/minecraft/server/EntitySlime.java new file mode 100644 index 000000000000..81966542ee1f --- /dev/null +++ b/src/main/java/net/minecraft/server/EntitySlime.java @@ -0,0 +1,522 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; +// Paper start +import com.destroystokyo.paper.event.entity.SlimeChangeDirectionEvent; +import com.destroystokyo.paper.event.entity.SlimeSwimEvent; +import com.destroystokyo.paper.event.entity.SlimeTargetLivingEntityEvent; +import com.destroystokyo.paper.event.entity.SlimeWanderEvent; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Slime; +// Paper end +// CraftBukkit start +import java.util.ArrayList; +import java.util.List; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityTransformEvent; +import org.bukkit.event.entity.SlimeSplitEvent; +// CraftBukkit end + +public class EntitySlime extends EntityInsentient implements IMonster { + + private static final DataWatcherObject bC = DataWatcher.a(EntitySlime.class, DataWatcherRegistry.b); + public float a; + public float b; + public float c; + private boolean bD; + + protected EntitySlime(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.moveController = new EntitySlime.ControllerMoveSlime(this); + } + + public EntitySlime(World world) { + this(EntityTypes.SLIME, world); + } + + protected void n() { + this.goalSelector.a(1, new EntitySlime.PathfinderGoalSlimeRandomJump(this)); + this.goalSelector.a(2, new EntitySlime.PathfinderGoalSlimeNearestPlayer(this)); + this.goalSelector.a(3, new EntitySlime.PathfinderGoalSlimeRandomDirection(this)); + this.goalSelector.a(5, new EntitySlime.PathfinderGoalSlimeIdle(this)); + this.targetSelector.a(1, new PathfinderGoalTargetNearestPlayer(this)); + this.targetSelector.a(3, new PathfinderGoalNearestAttackableTargetInsentient(this, EntityIronGolem.class)); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntitySlime.bC, 1); + } + + public void setSize(int i, boolean flag) { + this.datawatcher.set(EntitySlime.bC, i); + this.setSize(0.51000005F * (float) i, 0.51000005F * (float) i); + this.setPosition(this.locX, this.locY, this.locZ); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue((double) (i * i)); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue((double) (0.2F + 0.1F * (float) i)); + if (flag) { + this.setHealth(this.getMaxHealth()); + } + + this.b_ = i; + } + + public int getSize() { + return (Integer) this.datawatcher.get(EntitySlime.bC); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("Size", this.getSize() - 1); + nbttagcompound.setBoolean("wasOnGround", this.bD); + nbttagcompound.setBoolean("Paper.canWander", this.canWander); // Paper + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + int i = nbttagcompound.getInt("Size"); + + if (i < 0) { + i = 0; + } + + this.setSize(i + 1, false); + this.bD = nbttagcompound.getBoolean("wasOnGround"); + // Paper start - check exists before loading or this will be loaded as false + if (nbttagcompound.hasKey("Paper.canWander")) { + this.canWander = nbttagcompound.getBoolean("Paper.canWander"); + } + // Paper end + } + + public boolean dy() { + return this.getSize() <= 1; + } + + protected ParticleParam l() { + return Particles.D; + } + + public void tick() { + if (!this.world.isClientSide && this.world.getDifficulty() == EnumDifficulty.PEACEFUL && this.getSize() > 0) { + this.dead = true; + } + + this.b += (this.a - this.b) * 0.5F; + this.c = this.b; + super.tick(); + if (this.onGround && !this.bD) { + int i = this.getSize(); + + for (int j = 0; j < i * 8; ++j) { + float f = this.random.nextFloat() * 6.2831855F; + float f1 = this.random.nextFloat() * 0.5F + 0.5F; + float f2 = MathHelper.sin(f) * (float) i * 0.5F * f1; + float f3 = MathHelper.cos(f) * (float) i * 0.5F * f1; + World world = this.world; + ParticleParam particleparam = this.l(); + double d0 = this.locX + (double) f2; + double d1 = this.locZ + (double) f3; + + world.addParticle(particleparam, d0, this.getBoundingBox().minY, d1, 0.0D, 0.0D, 0.0D); + } + + this.a(this.dv(), this.cD(), ((this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F) / 0.8F); + this.a = -0.5F; + } else if (!this.onGround && this.bD) { + this.a = 1.0F; + } + + this.bD = this.onGround; + this.ds(); + } + + protected void ds() { + this.a *= 0.6F; + } + + protected int dr() { + return this.random.nextInt(20) + 10; + } + + public void a(DataWatcherObject datawatcherobject) { + if (EntitySlime.bC.equals(datawatcherobject)) { + int i = this.getSize(); + + this.setSize(0.51000005F * (float) i, 0.51000005F * (float) i); + this.yaw = this.aS; + this.aQ = this.aS; + if (this.isInWater() && this.random.nextInt(20) == 0) { + this.au(); + } + } + + super.a(datawatcherobject); + } + + public EntityTypes P() { + return (EntityTypes) super.P(); // CraftBukkit - decompile error + } + + public void die() { + int i = this.getSize(); + + if (!this.world.isClientSide && i > 1 && this.getHealth() <= 0.0F) { + int j = 2 + this.random.nextInt(3); + + // CraftBukkit start + SlimeSplitEvent event = new SlimeSplitEvent((org.bukkit.entity.Slime) this.getBukkitEntity(), j); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled() && event.getCount() > 0) { + j = event.getCount(); + } else { + super.die(); + return; + } + List slimes = new ArrayList<>(j); + // CraftBukkit end + + for (int k = 0; k < j; ++k) { + float f = ((float) (k % 2) - 0.5F) * (float) i / 4.0F; + float f1 = ((float) (k / 2) - 0.5F) * (float) i / 4.0F; + EntitySlime entityslime = (EntitySlime) this.P().a(this.world); + + if (this.hasCustomName()) { + entityslime.setCustomName(this.getCustomName()); + } + + if (this.isPersistent()) { + entityslime.di(); + } + + entityslime.setSize(i / 2, true); + entityslime.setPositionRotation(this.locX + (double) f, this.locY + 0.5D, this.locZ + (double) f1, this.random.nextFloat() * 360.0F, 0.0F); + + slimes.add(entityslime); // CraftBukkit + } + + // CraftBukkit start + if (CraftEventFactory.callEntityTransformEvent(this, slimes, EntityTransformEvent.TransformReason.SPLIT).isCancelled()) { + return; + } + for (EntityLiving living : slimes) { + this.world.addEntity(living, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SLIME_SPLIT); // CraftBukkit - SpawnReason + } + // CraftBukkit end + } + + super.die(); + } + + public void collide(Entity entity) { + super.collide(entity); + if (entity instanceof EntityIronGolem && this.dt()) { + this.f((EntityLiving) entity); + } + + } + + public void d(EntityHuman entityhuman) { + if (this.dt()) { + this.f((EntityLiving) entityhuman); + } + + } + + protected void f(EntityLiving entityliving) { + int i = this.getSize(); + + if (this.hasLineOfSight(entityliving) && this.h(entityliving) < 0.6D * (double) i * 0.6D * (double) i && entityliving.damageEntity(DamageSource.mobAttack(this), (float) this.du())) { + this.a(SoundEffects.ENTITY_SLIME_ATTACK, 1.0F, (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F); + this.a((EntityLiving) this, (Entity) entityliving); + } + + } + + public float getHeadHeight() { + return 0.625F * this.length; + } + + protected boolean dt() { + return !this.dy() && this.cP(); + } + + protected int du() { + return this.getSize(); + } + + protected SoundEffect d(DamageSource damagesource) { + return this.dy() ? SoundEffects.ENTITY_SLIME_HURT_SMALL : SoundEffects.ENTITY_SLIME_HURT; + } + + protected SoundEffect cs() { + return this.dy() ? SoundEffects.ENTITY_SLIME_DEATH_SMALL : SoundEffects.ENTITY_SLIME_DEATH; + } + + protected SoundEffect dv() { + return this.dy() ? SoundEffects.ENTITY_SLIME_SQUISH_SMALL : SoundEffects.ENTITY_SLIME_SQUISH; + } + + protected Item getLoot() { + return this.getSize() == 1 ? Items.SLIME_BALL : null; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return this.getSize() == 1 ? LootTables.ao : LootTables.a; + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + BlockPosition blockposition = new BlockPosition(MathHelper.floor(this.locX), 0, MathHelper.floor(this.locZ)); + + if (generatoraccess.getWorldData().getType() == WorldType.FLAT && this.random.nextInt(4) != 1) { + return false; + } else { + if (generatoraccess.getDifficulty() != EnumDifficulty.PEACEFUL) { + BiomeBase biomebase = generatoraccess.getBiome(blockposition); + + if (biomebase == Biomes.SWAMP && this.locY > 50.0D && this.locY < 70.0D && this.random.nextFloat() < 0.5F && this.random.nextFloat() < generatoraccess.ah() && generatoraccess.getLightLevel(new BlockPosition(this)) <= this.random.nextInt(8)) { + return super.a(generatoraccess, flag); + } + + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(blockposition); + boolean flag1 = world.paperConfig.allChunksAreSlimeChunks || SeededRandom.a(chunkcoordintpair.x, chunkcoordintpair.z, generatoraccess.getSeed(), world.spigotConfig.slimeSeed).nextInt(10) == 0; // Spigot // Paper - add allChunksAreSlime + + if (this.random.nextInt(10) == 0 && flag1 && this.locY < 40.0D) { + return super.a(generatoraccess, flag); + } + } + + return false; + } + } + + protected float cD() { + return 0.4F * (float) this.getSize(); + } + + public int K() { + return 0; + } + + protected boolean dz() { + return this.getSize() > 0; + } + + protected void cH() { + this.motY = 0.41999998688697815D; + this.impulse = true; + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + int i = this.random.nextInt(3); + + if (i < 2 && this.random.nextFloat() < 0.5F * difficultydamagescaler.d()) { + ++i; + } + + int j = 1 << i; + + this.setSize(j, true); + return super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + } + + protected SoundEffect dw() { + return this.dy() ? SoundEffects.ENTITY_SLIME_JUMP_SMALL : SoundEffects.ENTITY_SLIME_JUMP; + } + + static class PathfinderGoalSlimeIdle extends PathfinderGoal { + + private final EntitySlime a; + + public PathfinderGoalSlimeIdle(EntitySlime entityslime) { + this.a = entityslime; + this.a(5); + } + + public boolean a() { + return this.a.canWander && new SlimeWanderEvent((Slime) this.a.getBukkitEntity()).callEvent(); // Paper + } + + public void e() { + ((EntitySlime.ControllerMoveSlime) this.a.getControllerMove()).a(1.0D); + } + } + + static class PathfinderGoalSlimeRandomJump extends PathfinderGoal { + + private final EntitySlime a; + + public PathfinderGoalSlimeRandomJump(EntitySlime entityslime) { + this.a = entityslime; + this.a(5); + ((Navigation) entityslime.getNavigation()).d(true); + } + + public boolean a() { + return (this.a.isInWater() || this.a.ax()) && this.a.canWander && new SlimeSwimEvent((Slime) this.a.getBukkitEntity()).callEvent(); // Paper + } + + public void e() { + if (this.a.getRandom().nextFloat() < 0.8F) { + this.a.getControllerJump().a(); + } + + ((EntitySlime.ControllerMoveSlime) this.a.getControllerMove()).a(1.2D); + } + } + + static class PathfinderGoalSlimeRandomDirection extends PathfinderGoal { + + private final EntitySlime a; + private float b; + private int c; + + public PathfinderGoalSlimeRandomDirection(EntitySlime entityslime) { + this.a = entityslime; + this.a(2); + } + + public boolean a() { + return this.a.canWander && this.a.getGoalTarget() == null && (this.a.onGround || this.a.isInWater() || this.a.ax() || this.a.hasEffect(MobEffects.LEVITATION)); // Paper + } + + public void e() { + if (--this.c <= 0) { + this.c = 40 + this.a.getRandom().nextInt(60); + // Paper start + SlimeChangeDirectionEvent event = new SlimeChangeDirectionEvent((Slime) this.a.getBukkitEntity(), (float) this.a.getRandom().nextInt(360)); + if (!this.a.canWander || !event.callEvent()) return; + this.b = event.getNewYaw(); + // Paper end + } + + ((EntitySlime.ControllerMoveSlime) this.a.getControllerMove()).a(this.b, false); + } + } + + static class PathfinderGoalSlimeNearestPlayer extends PathfinderGoal { + + private final EntitySlime a; + private int b; + + public PathfinderGoalSlimeNearestPlayer(EntitySlime entityslime) { + this.a = entityslime; + this.a(2); + } + + public boolean a() { + EntityLiving entityliving = this.a.getGoalTarget(); + + // Paper start + if (entityliving == null || !entityliving.isAlive()) { + return false; + } + if (entityliving instanceof EntityHuman && ((EntityHuman) entityliving).abilities.isInvulnerable) { + return false; + } + return this.a.canWander && new SlimeTargetLivingEntityEvent((Slime) this.a.getBukkitEntity(), (LivingEntity) entityliving.getBukkitEntity()).callEvent(); + // Paper end + } + + public void c() { + this.b = 300; + super.c(); + } + + public boolean b() { + EntityLiving entityliving = this.a.getGoalTarget(); + + // Paper start + if (entityliving == null || !entityliving.isAlive()) { + return false; + } + if (entityliving instanceof EntityHuman && ((EntityHuman) entityliving).abilities.isInvulnerable) { + return false; + } + return --this.b > 0 && this.a.canWander && new SlimeTargetLivingEntityEvent((Slime) this.a.getBukkitEntity(), (LivingEntity) entityliving.getBukkitEntity()).callEvent(); + // Paper end + } + + public void e() { + this.a.a((Entity) this.a.getGoalTarget(), 10.0F, 10.0F); + ((EntitySlime.ControllerMoveSlime) this.a.getControllerMove()).a(this.a.yaw, this.a.dt()); + } + + // Paper start - clear timer and target when goal resets + public void d() { + this.b = 0; + this.a.setGoalTarget(null); + } + // Paper end + } + + static class ControllerMoveSlime extends ControllerMove { + + private float i; + private int j; + private final EntitySlime k; + private boolean l; + + public ControllerMoveSlime(EntitySlime entityslime) { + super(entityslime); + this.k = entityslime; + this.i = 180.0F * entityslime.yaw / 3.1415927F; + } + + public void a(float f, boolean flag) { + this.i = f; + this.l = flag; + } + + public void a(double d0) { + this.e = d0; + this.h = ControllerMove.Operation.MOVE_TO; + } + + public void a() { + this.a.yaw = this.a(this.a.yaw, this.i, 90.0F); + this.a.aS = this.a.yaw; + this.a.aQ = this.a.yaw; + if (this.h != ControllerMove.Operation.MOVE_TO) { + this.a.r(0.0F); + } else { + this.h = ControllerMove.Operation.WAIT; + if (this.a.onGround) { + this.a.o((float) (this.e * this.a.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue())); + if (this.j-- <= 0) { + this.j = this.k.dr(); + if (this.l) { + this.j /= 3; + } + + this.k.getControllerJump().a(); + if (this.k.dz()) { + this.k.a(this.k.dw(), this.k.cD(), ((this.k.getRandom().nextFloat() - this.k.getRandom().nextFloat()) * 0.2F + 1.0F) * 0.8F); + } + } else { + this.k.bh = 0.0F; + this.k.bj = 0.0F; + this.a.o(0.0F); + } + } else { + this.a.o((float) (this.e * this.a.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue())); + } + + } + } + } + + // Paper start + private boolean canWander = true; + public boolean canWander() { + return canWander; + } + + public void setWander(boolean canWander) { + this.canWander = canWander; + } + // Paper end +} diff --git a/src/main/java/net/minecraft/server/EntitySmallFireball.java b/src/main/java/net/minecraft/server/EntitySmallFireball.java new file mode 100644 index 000000000000..121921e35823 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntitySmallFireball.java @@ -0,0 +1,76 @@ +package net.minecraft.server; + +import org.bukkit.event.entity.EntityCombustByEntityEvent; // CraftBukkit + +public class EntitySmallFireball extends EntityFireball { + + public EntitySmallFireball(World world) { + super(EntityTypes.SMALL_FIREBALL, world, 0.3125F, 0.3125F); + } + + public EntitySmallFireball(World world, EntityLiving entityliving, double d0, double d1, double d2) { + super(EntityTypes.SMALL_FIREBALL, entityliving, d0, d1, d2, world, 0.3125F, 0.3125F); + // CraftBukkit start + if (this.shooter != null && this.shooter instanceof EntityInsentient) { + isIncendiary = this.world.getGameRules().getBoolean("mobGriefing"); + } + // CraftBukkit end + } + + public EntitySmallFireball(World world, double d0, double d1, double d2, double d3, double d4, double d5) { + super(EntityTypes.SMALL_FIREBALL, d0, d1, d2, d3, d4, d5, world, 0.3125F, 0.3125F); + } + + protected void a(MovingObjectPosition movingobjectposition) { + if (!this.world.isClientSide) { + boolean flag; + + if (movingobjectposition.entity != null) { + if (!movingobjectposition.entity.isFireProof()) { + // CraftBukkit start - Entity damage by entity event + combust event + if (isIncendiary) { + EntityCombustByEntityEvent event = new EntityCombustByEntityEvent((org.bukkit.entity.Projectile) this.getBukkitEntity(), movingobjectposition.entity.getBukkitEntity(), 5); + movingobjectposition.entity.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + movingobjectposition.entity.setOnFire(event.getDuration(), false); + } + } + // CraftBukkit end + flag = movingobjectposition.entity.damageEntity(DamageSource.fireball(this, this.shooter), 5.0F); + if (flag) { + this.a(this.shooter, movingobjectposition.entity); + } + } + } else { + flag = true; + if (this.shooter != null && this.shooter instanceof EntityInsentient) { + flag = this.world.getGameRules().getBoolean("mobGriefing"); + } + + // CraftBukkit start + if (isIncendiary) { + BlockPosition blockposition = movingobjectposition.getBlockPosition().shift(movingobjectposition.direction); + + if (this.world.isEmpty(blockposition)) { + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, blockposition, this).isCancelled()) { + this.world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); + } + // CraftBukkit end + } + } + } + + this.die(); + } + + } + + public boolean isInteractable() { + return false; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/EntitySnowman.java b/src/main/java/net/minecraft/server/EntitySnowman.java new file mode 100644 index 000000000000..6fdd8cc3a575 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntitySnowman.java @@ -0,0 +1,158 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.player.PlayerShearEntityEvent; +// CraftBukkit end + +public class EntitySnowman extends EntityGolem implements IRangedEntity { + + private static final DataWatcherObject a = DataWatcher.a(EntitySnowman.class, DataWatcherRegistry.a); + + public EntitySnowman(World world) { + super(EntityTypes.SNOW_GOLEM, world); + this.setSize(0.7F, 1.9F); + } + + protected void n() { + this.goalSelector.a(1, new PathfinderGoalArrowAttack(this, 1.25D, 20, 10.0F)); + this.goalSelector.a(2, new PathfinderGoalRandomStrollLand(this, 1.0D, 1.0000001E-5F)); + this.goalSelector.a(3, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 6.0F)); + this.goalSelector.a(4, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new PathfinderGoalNearestAttackableTarget<>(this, EntityInsentient.class, 10, true, false, IMonster.d)); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(4.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.20000000298023224D); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntitySnowman.a, (byte) 16); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("Pumpkin", this.hasPumpkin()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKey("Pumpkin")) { + this.setHasPumpkin(nbttagcompound.getBoolean("Pumpkin")); + } + + } + + public void movementTick() { + super.movementTick(); + if (!this.world.isClientSide) { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locY); + int k = MathHelper.floor(this.locZ); + + if (this.ap()) { + this.damageEntity(DamageSource.DROWN, 1.0F); + } + + if (this.world.getBiome(new BlockPosition(i, 0, k)).getAdjustedTemperature(new BlockPosition(i, j, k)) > 1.0F) { + this.damageEntity(CraftEventFactory.MELTING, 1.0F); // CraftBukkit - DamageSource.BURN -> CraftEventFactory.MELTING + } + + if (!this.world.getGameRules().getBoolean("mobGriefing")) { + return; + } + + IBlockData iblockdata = Blocks.SNOW.getBlockData(); + + for (int l = 0; l < 4; ++l) { + i = MathHelper.floor(this.locX + (double) ((float) (l % 2 * 2 - 1) * 0.25F)); + j = MathHelper.floor(this.locY); + k = MathHelper.floor(this.locZ + (double) ((float) (l / 2 % 2 * 2 - 1) * 0.25F)); + BlockPosition blockposition = new BlockPosition(i, j, k); + + if (this.world.getType(blockposition).isAir() && this.world.getBiome(blockposition).getAdjustedTemperature(blockposition) < 0.8F && iblockdata.canPlace(this.world, blockposition)) { + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this.world, blockposition, iblockdata, this); // CraftBukkit + } + } + } + + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.H; + } + + public void a(EntityLiving entityliving, float f) { + EntitySnowball entitysnowball = new EntitySnowball(this.world, this); + double d0 = entityliving.locY + (double) entityliving.getHeadHeight() - 1.100000023841858D; + double d1 = entityliving.locX - this.locX; + double d2 = d0 - entitysnowball.locY; + double d3 = entityliving.locZ - this.locZ; + float f1 = MathHelper.sqrt(d1 * d1 + d3 * d3) * 0.2F; + + entitysnowball.shoot(d1, d2 + (double) f1, d3, 1.6F, 12.0F); + this.a(SoundEffects.ENTITY_SNOW_GOLEM_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F)); + this.world.addEntity(entitysnowball); + } + + public float getHeadHeight() { + return 1.7F; + } + + protected boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (itemstack.getItem() == Items.SHEARS && this.hasPumpkin() && !this.world.isClientSide) { + // CraftBukkit start + PlayerShearEntityEvent event = new PlayerShearEntityEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), this.getBukkitEntity()); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return false; + } + // CraftBukkit end + + this.setHasPumpkin(false); + itemstack.damage(1, entityhuman); + } + + return super.a(entityhuman, enumhand); + } + + public boolean hasPumpkin() { + return ((Byte) this.datawatcher.get(EntitySnowman.a) & 16) != 0; + } + + public void setHasPumpkin(boolean flag) { + byte b0 = (Byte) this.datawatcher.get(EntitySnowman.a); + + if (flag) { + this.datawatcher.set(EntitySnowman.a, (byte) (b0 | 16)); + } else { + this.datawatcher.set(EntitySnowman.a, (byte) (b0 & -17)); + } + + } + + @Nullable + protected SoundEffect D() { + return SoundEffects.ENTITY_SNOW_GOLEM_AMBIENT; + } + + @Nullable + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_SNOW_GOLEM_HURT; + } + + @Nullable + protected SoundEffect cs() { + return SoundEffects.ENTITY_SNOW_GOLEM_DEATH; + } + + public void s(boolean flag) {} +} diff --git a/src/main/java/net/minecraft/server/EntitySpectralArrow.java b/src/main/java/net/minecraft/server/EntitySpectralArrow.java new file mode 100644 index 000000000000..13027fa3c3ac --- /dev/null +++ b/src/main/java/net/minecraft/server/EntitySpectralArrow.java @@ -0,0 +1,50 @@ +package net.minecraft.server; + +public class EntitySpectralArrow extends EntityArrow { + + public int duration = 200; + + public EntitySpectralArrow(World world) { + super(EntityTypes.SPECTRAL_ARROW, world); + } + + public EntitySpectralArrow(World world, EntityLiving entityliving) { + super(EntityTypes.SPECTRAL_ARROW, entityliving, world); + } + + public EntitySpectralArrow(World world, double d0, double d1, double d2) { + super(EntityTypes.SPECTRAL_ARROW, d0, d1, d2, world); + } + + public void tick() { + super.tick(); + if (this.world.isClientSide && !this.inGround) { + this.world.addParticle(Particles.B, this.locX, this.locY, this.locZ, 0.0D, 0.0D, 0.0D); + } + + } + + protected ItemStack getItemStack() { + return new ItemStack(Items.SPECTRAL_ARROW); + } + + protected void a(EntityLiving entityliving) { + super.a(entityliving); + MobEffect mobeffect = new MobEffect(MobEffects.GLOWING, this.duration, 0); + + entityliving.addEffect(mobeffect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ARROW); // CraftBukkit + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKey("Duration")) { + this.duration = nbttagcompound.getInt("Duration"); + } + + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("Duration", this.duration); + } +} diff --git a/src/main/java/net/minecraft/server/EntitySpider.java b/src/main/java/net/minecraft/server/EntitySpider.java new file mode 100644 index 000000000000..9ef1c9baf24e --- /dev/null +++ b/src/main/java/net/minecraft/server/EntitySpider.java @@ -0,0 +1,200 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; + +public class EntitySpider extends EntityMonster { + + private static final DataWatcherObject a = DataWatcher.a(EntitySpider.class, DataWatcherRegistry.a); + + protected EntitySpider(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.setSize(1.4F, 0.9F); + } + + public EntitySpider(World world) { + this(EntityTypes.SPIDER, world); + } + + protected void n() { + this.goalSelector.a(1, new PathfinderGoalFloat(this)); + this.goalSelector.a(3, new PathfinderGoalLeapAtTarget(this, 0.4F)); + this.goalSelector.a(4, new EntitySpider.PathfinderGoalSpiderMeleeAttack(this)); + this.goalSelector.a(5, new PathfinderGoalRandomStrollLand(this, 0.8D)); + this.goalSelector.a(6, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(6, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, false, new Class[0])); + this.targetSelector.a(2, new EntitySpider.PathfinderGoalSpiderNearestAttackableTarget<>(this, EntityHuman.class)); + this.targetSelector.a(3, new EntitySpider.PathfinderGoalSpiderNearestAttackableTarget<>(this, EntityIronGolem.class)); + } + + public double aJ() { + return (double) (this.length * 0.5F); + } + + protected NavigationAbstract b(World world) { + return new NavigationSpider(this, world); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntitySpider.a, (byte) 0); + } + + public void tick() { + super.tick(); + if (!this.world.isClientSide) { + this.a(this.positionChanged); + } + + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(16.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.30000001192092896D); + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_SPIDER_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_SPIDER_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_SPIDER_DEATH; + } + + protected void a(BlockPosition blockposition, IBlockData iblockdata) { + this.a(SoundEffects.ENTITY_SPIDER_STEP, 0.15F, 1.0F); + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.y; + } + + public boolean z_() { + return this.l(); + } + + public void bh() {} + + public EnumMonsterType getMonsterType() { + return EnumMonsterType.ARTHROPOD; + } + + public boolean d(MobEffect mobeffect) { + return mobeffect.getMobEffect() == MobEffects.POISON ? false : super.d(mobeffect); + } + + public boolean l() { + return ((Byte) this.datawatcher.get(EntitySpider.a) & 1) != 0; + } + + public void a(boolean flag) { + byte b0 = (Byte) this.datawatcher.get(EntitySpider.a); + + if (flag) { + b0 = (byte) (b0 | 1); + } else { + b0 &= -2; + } + + this.datawatcher.set(EntitySpider.a, b0); + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + Object object = super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + + if (this.world.random.nextInt(100) == 0) { + EntitySkeleton entityskeleton = EntityTypes.SKELETON.create(world); // Paper + + entityskeleton.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, 0.0F); + entityskeleton.prepare(difficultydamagescaler, (GroupDataEntity) null, (NBTTagCompound) null); + this.world.addEntity(entityskeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.JOCKEY); // CraftBukkit - add SpawnReason + entityskeleton.startRiding(this); + } + + if (object == null) { + object = new EntitySpider.GroupDataSpider(); + if (this.world.getDifficulty() == EnumDifficulty.HARD && this.world.random.nextFloat() < 0.1F * difficultydamagescaler.d()) { + ((EntitySpider.GroupDataSpider) object).a(this.world.random); + } + } + + if (object instanceof EntitySpider.GroupDataSpider) { + MobEffectList mobeffectlist = ((EntitySpider.GroupDataSpider) object).a; + + if (mobeffectlist != null) { + this.addEffect(new MobEffect(mobeffectlist, Integer.MAX_VALUE), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.SPIDER_SPAWN); // CraftBukkit + } + } + + return (GroupDataEntity) object; + } + + public float getHeadHeight() { + return 0.65F; + } + + static class PathfinderGoalSpiderNearestAttackableTarget extends PathfinderGoalNearestAttackableTarget { + + public PathfinderGoalSpiderNearestAttackableTarget(EntitySpider entityspider, Class oclass) { + super(entityspider, oclass, true); + } + + public boolean a() { + float f = this.e.az(); + + return f >= 0.5F ? false : super.a(); + } + } + + static class PathfinderGoalSpiderMeleeAttack extends PathfinderGoalMeleeAttack { + + public PathfinderGoalSpiderMeleeAttack(EntitySpider entityspider) { + super(entityspider, 1.0D, true); + } + + public boolean b() { + float f = this.a.az(); + + if (f >= 0.5F && this.a.getRandom().nextInt(100) == 0) { + this.a.setGoalTarget((EntityLiving) null); + return false; + } else { + return super.b(); + } + } + + protected double a(EntityLiving entityliving) { + return (double) (4.0F + entityliving.width); + } + } + + public static class GroupDataSpider implements GroupDataEntity { + + public MobEffectList a; + + public GroupDataSpider() {} + + public void a(Random random) { + int i = random.nextInt(5); + + if (i <= 1) { + this.a = MobEffects.FASTER_MOVEMENT; + } else if (i <= 2) { + this.a = MobEffects.INCREASE_DAMAGE; + } else if (i <= 3) { + this.a = MobEffects.REGENERATION; + } else if (i <= 4) { + this.a = MobEffects.INVISIBILITY; + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/EntitySquid.java b/src/main/java/net/minecraft/server/EntitySquid.java new file mode 100644 index 000000000000..ab79317a2d0b --- /dev/null +++ b/src/main/java/net/minecraft/server/EntitySquid.java @@ -0,0 +1,270 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class EntitySquid extends EntityWaterAnimal { + + public float a; + public float b; + public float c; + public float bC; + public float bD; + public float bE; + public float bF; + public float bG; + private float bH; + private float bI; + private float bJ; + private float bK; + private float bL; + private float bM; + + public EntitySquid(World world) { + super(EntityTypes.SQUID, world); + this.setSize(0.8F, 0.8F); + //this.random.setSeed((long) (1 + this.getId())); // Paper + this.bI = 1.0F / (this.random.nextFloat() + 1.0F) * 0.2F; + } + + protected void n() { + this.goalSelector.a(0, new EntitySquid.PathfinderGoalSquid(this)); + this.goalSelector.a(1, new EntitySquid.a()); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(10.0D); + } + + public float getHeadHeight() { + return this.length * 0.5F; + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_SQUID_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_SQUID_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_SQUID_DEATH; + } + + protected float cD() { + return 0.4F; + } + + protected boolean playStepSound() { + return false; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.ar; + } + + public void movementTick() { + super.movementTick(); + this.b = this.a; + this.bC = this.c; + this.bE = this.bD; + this.bG = this.bF; + this.bD += this.bI; + if ((double) this.bD > 6.283185307179586D) { + if (this.world.isClientSide) { + this.bD = 6.2831855F; + } else { + this.bD = (float) ((double) this.bD - 6.283185307179586D); + if (this.random.nextInt(10) == 0) { + this.bI = 1.0F / (this.random.nextFloat() + 1.0F) * 0.2F; + } + + this.world.broadcastEntityEffect(this, (byte) 19); + } + } + + if (this.aq()) { + float f; + + if (this.bD < 3.1415927F) { + f = this.bD / 3.1415927F; + this.bF = MathHelper.sin(f * f * 3.1415927F) * 3.1415927F * 0.25F; + if ((double) f > 0.75D) { + this.bH = 1.0F; + this.bJ = 1.0F; + } else { + this.bJ *= 0.8F; + } + } else { + this.bF = 0.0F; + this.bH *= 0.9F; + this.bJ *= 0.99F; + } + + if (!this.world.isClientSide) { + this.motX = (double) (this.bK * this.bH); + this.motY = (double) (this.bL * this.bH); + this.motZ = (double) (this.bM * this.bH); + } + + f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + this.aQ += (-((float) MathHelper.c(this.motX, this.motZ)) * 57.295776F - this.aQ) * 0.1F; + this.yaw = this.aQ; + this.c = (float) ((double) this.c + 3.141592653589793D * (double) this.bJ * 1.5D); + this.a += (-((float) MathHelper.c((double) f, this.motY)) * 57.295776F - this.a) * 0.1F; + } else { + this.bF = MathHelper.e(MathHelper.sin(this.bD)) * 3.1415927F * 0.25F; + if (!this.world.isClientSide) { + this.motX = 0.0D; + this.motZ = 0.0D; + if (this.hasEffect(MobEffects.LEVITATION)) { + this.motY += 0.05D * (double) (this.getEffect(MobEffects.LEVITATION).getAmplifier() + 1) - this.motY; + } else if (!this.isNoGravity()) { + this.motY -= 0.08D; + } + + this.motY *= 0.9800000190734863D; + } + + this.a = (float) ((double) this.a + (double) (-90.0F - this.a) * 0.02D); + } + + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (super.damageEntity(damagesource, f) && this.getLastDamager() != null) { + this.dy(); + return true; + } else { + return false; + } + } + + private Vec3D b(Vec3D vec3d) { + Vec3D vec3d1 = vec3d.a(this.b * 0.017453292F); + + vec3d1 = vec3d1.b(-this.aR * 0.017453292F); + return vec3d1; + } + + private void dy() { + this.a(SoundEffects.ENTITY_SQUID_SQUIRT, this.cD(), this.cE()); + Vec3D vec3d = this.b(new Vec3D(0.0D, -1.0D, 0.0D)).add(this.locX, this.locY, this.locZ); + + for (int i = 0; i < 30; ++i) { + Vec3D vec3d1 = this.b(new Vec3D((double) this.random.nextFloat() * 0.6D - 0.3D, -1.0D, (double) this.random.nextFloat() * 0.6D - 0.3D)); + Vec3D vec3d2 = vec3d1.a(0.3D + (double) (this.random.nextFloat() * 2.0F)); + + ((WorldServer) this.world).a(Particles.V, vec3d.x, vec3d.y + 0.5D, vec3d.z, 0, vec3d2.x, vec3d2.y, vec3d2.z, 0.10000000149011612D); + } + + } + + public void a(float f, float f1, float f2) { + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + // Paper - Make max spawn height configurable + final double maxHeight = world.paperConfig.squidMaxSpawnHeight > 0 ? world.paperConfig.squidMaxSpawnHeight : world.getSeaLevel(); + return this.locY > this.world.spigotConfig.squidSpawnRangeMin && this.locY < maxHeight; // Spigot + // Paper end + } + + public void c(float f, float f1, float f2) { + this.bK = f; + this.bL = f1; + this.bM = f2; + } + + public boolean l() { + return this.bK != 0.0F || this.bL != 0.0F || this.bM != 0.0F; + } + + class a extends PathfinderGoal { + + private int b; + + private a() {} + + public boolean a() { + EntityLiving entityliving = EntitySquid.this.getLastDamager(); + + return EntitySquid.this.isInWater() && entityliving != null ? EntitySquid.this.h(entityliving) < 100.0D : false; + } + + public void c() { + this.b = 0; + } + + public void e() { + ++this.b; + EntityLiving entityliving = EntitySquid.this.getLastDamager(); + + if (entityliving != null) { + Vec3D vec3d = new Vec3D(EntitySquid.this.locX - entityliving.locX, EntitySquid.this.locY - entityliving.locY, EntitySquid.this.locZ - entityliving.locZ); + IBlockData iblockdata = EntitySquid.this.world.getType(new BlockPosition(EntitySquid.this.locX + vec3d.x, EntitySquid.this.locY + vec3d.y, EntitySquid.this.locZ + vec3d.z)); + Fluid fluid = EntitySquid.this.world.getFluid(new BlockPosition(EntitySquid.this.locX + vec3d.x, EntitySquid.this.locY + vec3d.y, EntitySquid.this.locZ + vec3d.z)); + + if (fluid.a(TagsFluid.WATER) || iblockdata.isAir()) { + double d0 = vec3d.b(); + + if (d0 > 0.0D) { + vec3d.a(); + float f = 3.0F; + + if (d0 > 5.0D) { + f = (float) ((double) f - (d0 - 5.0D) / 5.0D); + } + + if (f > 0.0F) { + vec3d = vec3d.a((double) f); + } + } + + if (iblockdata.isAir()) { + vec3d = vec3d.a(0.0D, vec3d.y, 0.0D); + } + + EntitySquid.this.c((float) vec3d.x / 20.0F, (float) vec3d.y / 20.0F, (float) vec3d.z / 20.0F); + } + + if (this.b % 10 == 5) { + EntitySquid.this.world.addParticle(Particles.e, EntitySquid.this.locX, EntitySquid.this.locY, EntitySquid.this.locZ, 0.0D, 0.0D, 0.0D); + } + + } + } + } + + class PathfinderGoalSquid extends PathfinderGoal { + + private final EntitySquid b; + + public PathfinderGoalSquid(EntitySquid entitysquid) { + this.b = entitysquid; + } + + public boolean a() { + return true; + } + + public void e() { + int i = this.b.cj(); + + if (i > 100) { + this.b.c(0.0F, 0.0F, 0.0F); + } else if (this.b.getRandom().nextInt(50) == 0 || !this.b.inWater || !this.b.l()) { + float f = this.b.getRandom().nextFloat() * 6.2831855F; + float f1 = MathHelper.cos(f) * 0.2F; + float f2 = -0.1F + this.b.getRandom().nextFloat() * 0.2F; + float f3 = MathHelper.sin(f) * 0.2F; + + this.b.c(f1, f2, f3); + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityTNTPrimed.java b/src/main/java/net/minecraft/server/EntityTNTPrimed.java new file mode 100644 index 000000000000..e5170b4e6bc5 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityTNTPrimed.java @@ -0,0 +1,195 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; +import org.bukkit.event.entity.ExplosionPrimeEvent; // CraftBukkit + +public class EntityTNTPrimed extends Entity { + + private static final DataWatcherObject FUSE_TICKS = DataWatcher.a(EntityTNTPrimed.class, DataWatcherRegistry.b); + @Nullable + private EntityLiving source; + private int c; + public float yield = 4; // CraftBukkit - add field + public boolean isIncendiary = false; // CraftBukkit - add field + + public EntityTNTPrimed(World world) { + super(EntityTypes.TNT, world); + this.c = 80; + this.j = true; + this.fireProof = true; + this.setSize(0.98F, 0.98F); + } + + public EntityTNTPrimed(World world, double d0, double d1, double d2, @Nullable EntityLiving entityliving) { + this(world); + this.setPosition(d0, d1, d2); + float f = (float) (Math.random() * 6.2831854820251465D); + + this.motX = (double) (-((float) Math.sin((double) f)) * 0.02F); + this.motY = 0.20000000298023224D; + this.motZ = (double) (-((float) Math.cos((double) f)) * 0.02F); + this.setFuseTicks(80); + this.lastX = d0; + this.lastY = d1; + this.lastZ = d2; + this.source = entityliving; + } + + protected void x_() { + this.datawatcher.register(EntityTNTPrimed.FUSE_TICKS, 80); + } + + protected boolean playStepSound() { + return false; + } + + public boolean isInteractable() { + return !this.dead; + } + + public void tick() { + if (world.spigotConfig.currentPrimedTnt++ > world.spigotConfig.maxTntTicksPerTick) { return; } // Spigot + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + if (!this.isNoGravity()) { + this.motY -= 0.03999999910593033D; + } + + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + + // Paper start - Configurable TNT entity height nerf + if (this.world.paperConfig.entityTNTHeightNerf != 0 && this.locY > this.world.paperConfig.entityTNTHeightNerf) { + this.die(); + } + // Paper end + + this.motX *= 0.9800000190734863D; + this.motY *= 0.9800000190734863D; + this.motZ *= 0.9800000190734863D; + if (this.onGround) { + this.motX *= 0.699999988079071D; + this.motZ *= 0.699999988079071D; + this.motY *= -0.5D; + } + + --this.c; + if (this.c <= 0) { + // CraftBukkit start - Need to reverse the order of the explosion and the entity death so we have a location for the event + // this.die(); + if (!this.world.isClientSide) { + this.explode(); + } + this.die(); + // CraftBukkit end + } else { + this.at(); + this.world.addParticle(Particles.M, this.locX, this.locY + 0.5D, this.locZ, 0.0D, 0.0D, 0.0D); + } + + } + + private void explode() { + // CraftBukkit start + // float f = 4.0F; + + org.bukkit.craftbukkit.CraftServer server = this.world.getServer(); + ExplosionPrimeEvent event = new ExplosionPrimeEvent((org.bukkit.entity.Explosive) org.bukkit.craftbukkit.entity.CraftEntity.getEntity(server, this)); + server.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + this.world.createExplosion(this, this.locX, this.locY + (double) (this.length / 16.0F), this.locZ, event.getRadius(), event.getFire(), true); + } + // CraftBukkit end + } + + protected void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setShort("Fuse", (short) this.getFuseTicks()); + } + + protected void a(NBTTagCompound nbttagcompound) { + this.setFuseTicks(nbttagcompound.getShort("Fuse")); + // Paper start - Try and load origin location from the old NBT tags for backwards compatibility + if (nbttagcompound.hasKey("SourceLoc_x")) { + int srcX = nbttagcompound.getInt("SourceLoc_x"); + int srcY = nbttagcompound.getInt("SourceLoc_y"); + int srcZ = nbttagcompound.getInt("SourceLoc_z"); + origin = new org.bukkit.Location(world.getWorld(), srcX, srcY, srcZ); + } + // Paper end + } + + @Nullable + public EntityLiving getSource() { + return this.source; + } + + public float getHeadHeight() { + return 0.0F; + } + + public void setFuseTicks(int i) { + this.datawatcher.set(EntityTNTPrimed.FUSE_TICKS, i); + this.c = i; + } + + public void a(DataWatcherObject datawatcherobject) { + if (EntityTNTPrimed.FUSE_TICKS.equals(datawatcherobject)) { + this.c = this.i(); + } + + } + + public int i() { + return (Integer) this.datawatcher.get(EntityTNTPrimed.FUSE_TICKS); + } + + public int getFuseTicks() { + return this.c; + } + + // Paper start - Optional prevent TNT from moving in water + @Override + public boolean pushedByWater() { + return !world.paperConfig.preventTntFromMovingInWater && super.pushedByWater(); + } + + /** + * Author: Jedediah Smith + */ + @Override + public boolean doWaterMovement() { + if (!world.paperConfig.preventTntFromMovingInWater) return super.doWaterMovement(); + + // Preserve velocity while calling the super method + double oldMotX = this.motX; + double oldMotY = this.motY; + double oldMotZ = this.motZ; + + super.doWaterMovement(); + + this.motX = oldMotX; + this.motY = oldMotY; + this.motZ = oldMotZ; + + if (this.inWater) { + // Send position and velocity updates to nearby players on every tick while the TNT is in water. + // This does pretty well at keeping their clients in sync with the server. + EntityTrackerEntry ete = ((WorldServer) this.getWorld()).getTracker().trackedEntities.get(this.getId()); + if (ete != null) { + PacketPlayOutEntityVelocity velocityPacket = new PacketPlayOutEntityVelocity(this); + PacketPlayOutEntityTeleport positionPacket = new PacketPlayOutEntityTeleport(this); + + ete.trackedPlayers.stream() + .filter(viewer -> (viewer.locX - this.locX) * (viewer.locY - this.locY) * (viewer.locZ - this.locZ) < 16 * 16) + .forEach(viewer -> { + viewer.playerConnection.sendPacket(velocityPacket); + viewer.playerConnection.sendPacket(positionPacket); + }); + } + } + + return this.inWater; + } + // Paper end +} diff --git a/src/main/java/net/minecraft/server/EntityThrownExpBottle.java b/src/main/java/net/minecraft/server/EntityThrownExpBottle.java new file mode 100644 index 000000000000..e73dba09a69d --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityThrownExpBottle.java @@ -0,0 +1,46 @@ +package net.minecraft.server; + +public class EntityThrownExpBottle extends EntityProjectile { + + public EntityThrownExpBottle(World world) { + super(EntityTypes.EXPERIENCE_BOTTLE, world); + } + + public EntityThrownExpBottle(World world, EntityLiving entityliving) { + super(EntityTypes.EXPERIENCE_BOTTLE, entityliving, world); + } + + public EntityThrownExpBottle(World world, double d0, double d1, double d2) { + super(EntityTypes.EXPERIENCE_BOTTLE, d0, d1, d2, world); + } + + protected float f() { + return 0.07F; + } + + protected void a(MovingObjectPosition movingobjectposition) { + if (!this.world.isClientSide) { + // CraftBukkit - moved to after event + // this.world.triggerEffect(2002, new BlockPosition(this), PotionUtil.a(Potions.b)); + int i = 3 + this.world.random.nextInt(5) + this.world.random.nextInt(5); + + // CraftBukkit start + org.bukkit.event.entity.ExpBottleEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callExpBottleEvent(this, i); + i = event.getExperience(); + if (event.getShowEffect()) { + this.world.triggerEffect(2002, new BlockPosition(this), PotionUtil.a(Potions.b)); + } + // CraftBukkit end + + while (i > 0) { + int j = EntityExperienceOrb.getOrbValue(i); + + i -= j; + this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j, org.bukkit.entity.ExperienceOrb.SpawnReason.EXP_BOTTLE, getShooter(), this)); // Paper + } + + this.die(); + } + + } +} diff --git a/src/main/java/net/minecraft/server/EntityThrownTrident.java b/src/main/java/net/minecraft/server/EntityThrownTrident.java new file mode 100644 index 000000000000..d9107f24b96e --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityThrownTrident.java @@ -0,0 +1,174 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class EntityThrownTrident extends EntityArrow { + + private static final DataWatcherObject h = DataWatcher.a(EntityThrownTrident.class, DataWatcherRegistry.a); + public ItemStack trident; + private boolean ax; + public int g; + + public EntityThrownTrident(World world) { + super(EntityTypes.TRIDENT, world); + this.trident = new ItemStack(Items.TRIDENT); + } + + public EntityThrownTrident(World world, EntityLiving entityliving, ItemStack itemstack) { + super(EntityTypes.TRIDENT, entityliving, world); + this.trident = new ItemStack(Items.TRIDENT); + this.trident = itemstack.cloneItemStack(); + this.datawatcher.set(EntityThrownTrident.h, (byte) EnchantmentManager.f(itemstack)); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityThrownTrident.h, (byte) 0); + } + + public void tick() { + if (this.c > 4) { + this.ax = true; + } + + Entity entity = this.getShooter(); + + if ((this.ax || this.q()) && entity != null) { + byte b0 = (Byte) this.datawatcher.get(EntityThrownTrident.h); + + if (b0 > 0 && !this.r()) { + if (!this.world.isClientSide && this.fromPlayer == EntityArrow.PickupStatus.ALLOWED) { + this.a(this.getItemStack(), 0.1F); + } + + this.die(); + } else if (b0 > 0) { + this.o(true); + Vec3D vec3d = new Vec3D(entity.locX - this.locX, entity.locY + (double) entity.getHeadHeight() - this.locY, entity.locZ - this.locZ); + + this.locY += vec3d.y * 0.015D * (double) b0; + if (this.world.isClientSide) { + this.O = this.locY; + } + + vec3d = vec3d.a(); + double d0 = 0.05D * (double) b0; + + this.motX += vec3d.x * d0 - this.motX * 0.05D; + this.motY += vec3d.y * d0 - this.motY * 0.05D; + this.motZ += vec3d.z * d0 - this.motZ * 0.05D; + if (this.g == 0) { + this.a(SoundEffects.ITEM_TRIDENT_RETURN, 10.0F, 1.0F); + } + + ++this.g; + } + } + + super.tick(); + } + + private boolean r() { + Entity entity = this.getShooter(); + + return entity != null && entity.isAlive() ? !(entity instanceof EntityPlayer) || !((EntityPlayer) entity).isSpectator() : false; + } + + protected ItemStack getItemStack() { + return this.trident.cloneItemStack(); + } + + @Nullable + protected Entity a(Vec3D vec3d, Vec3D vec3d1) { + return this.ax ? null : super.a(vec3d, vec3d1); + } + + protected void b(MovingObjectPosition movingobjectposition) { + Entity entity = movingobjectposition.entity; + float f = 8.0F; + + if (entity instanceof EntityLiving) { + EntityLiving entityliving = (EntityLiving) entity; + + f += EnchantmentManager.a(this.trident, entityliving.getMonsterType()); + } + + Entity entity1 = this.getShooter(); + DamageSource damagesource = DamageSource.a(this, (Entity) (entity1 == null ? this : entity1)); + + this.ax = true; + SoundEffect soundeffect = SoundEffects.ITEM_TRIDENT_HIT; + + if (entity.damageEntity(damagesource, f) && entity instanceof EntityLiving) { + EntityLiving entityliving1 = (EntityLiving) entity; + + if (entity1 instanceof EntityLiving) { + EnchantmentManager.a(entityliving1, entity1); + EnchantmentManager.b((EntityLiving) entity1, (Entity) entityliving1); + } + + this.a(entityliving1); + } + + this.motX *= -0.009999999776482582D; + this.motY *= -0.10000000149011612D; + this.motZ *= -0.009999999776482582D; + float f1 = 1.0F; + + if (this.world.Y() && EnchantmentManager.h(this.trident)) { + BlockPosition blockposition = entity.getChunkCoordinates(); + + if (this.world.e(blockposition)) { + EntityLightning entitylightning = new EntityLightning(this.world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, false); + + entitylightning.d(entity1 instanceof EntityPlayer ? (EntityPlayer) entity1 : null); + ((WorldServer) this.world).strikeLightning(entitylightning, org.bukkit.event.weather.LightningStrikeEvent.Cause.TRIDENT); // CraftBukkit + soundeffect = SoundEffects.ITEM_TRIDENT_THUNDER; + f1 = 5.0F; + } + } + + this.a(soundeffect, f1, 1.0F); + } + + protected SoundEffect i() { + return SoundEffects.ITEM_TRIDENT_HIT_GROUND; + } + + public void d(EntityHuman entityhuman) { + Entity entity = this.getShooter(); + + if (entity == null || entity.getUniqueID() == entityhuman.getUniqueID()) { + super.d(entityhuman); + } + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKeyOfType("Trident", 10)) { + this.trident = ItemStack.a(nbttagcompound.getCompound("Trident")); + } + + this.ax = nbttagcompound.getBoolean("DealtDamage"); + this.datawatcher.set(EntityThrownTrident.h, (byte) EnchantmentManager.f(this.trident)); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.set("Trident", this.trident.save(new NBTTagCompound())); + nbttagcompound.setBoolean("DealtDamage", this.ax); + } + + protected void f() { + byte b0 = (Byte) this.datawatcher.get(EntityThrownTrident.h); + + if (this.fromPlayer != EntityArrow.PickupStatus.ALLOWED || b0 <= 0) { + super.f(); + } + + } + + protected float p() { + return 0.99F; + } +} diff --git a/src/main/java/net/minecraft/server/EntityTippedArrow.java b/src/main/java/net/minecraft/server/EntityTippedArrow.java new file mode 100644 index 000000000000..12d1fa121014 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityTippedArrow.java @@ -0,0 +1,230 @@ +package net.minecraft.server; + +import com.google.common.collect.Sets; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +public class EntityTippedArrow extends EntityArrow { + + private static final DataWatcherObject g = DataWatcher.a(EntityTippedArrow.class, DataWatcherRegistry.b); + private PotionRegistry potionRegistry; + public final Set effects; + private boolean hasColor; + + public EntityTippedArrow(World world) { + super(EntityTypes.ARROW, world); + this.potionRegistry = Potions.EMPTY; + this.effects = Sets.newHashSet(); + } + + public EntityTippedArrow(World world, double d0, double d1, double d2) { + super(EntityTypes.ARROW, d0, d1, d2, world); + this.potionRegistry = Potions.EMPTY; + this.effects = Sets.newHashSet(); + } + + public EntityTippedArrow(World world, EntityLiving entityliving) { + super(EntityTypes.ARROW, entityliving, world); + this.potionRegistry = Potions.EMPTY; + this.effects = Sets.newHashSet(); + } + + public void b(ItemStack itemstack) { + if (itemstack.getItem() == Items.TIPPED_ARROW) { + this.potionRegistry = PotionUtil.d(itemstack); + Collection collection = PotionUtil.b(itemstack); + + if (!collection.isEmpty()) { + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + this.effects.add(new MobEffect(mobeffect)); + } + } + + int i = c(itemstack); + + if (i == -1) { + this.s(); + } else { + this.setColor(i); + } + } else if (itemstack.getItem() == Items.ARROW) { + this.potionRegistry = Potions.EMPTY; + this.effects.clear(); + this.datawatcher.set(EntityTippedArrow.g, -1); + } + + } + + public static int c(ItemStack itemstack) { + NBTTagCompound nbttagcompound = itemstack.getTag(); + + return nbttagcompound != null && nbttagcompound.hasKeyOfType("CustomPotionColor", 99) ? nbttagcompound.getInt("CustomPotionColor") : -1; + } + + private void s() { + this.hasColor = false; + this.datawatcher.set(EntityTippedArrow.g, PotionUtil.a((Collection) PotionUtil.a(this.potionRegistry, (Collection) this.effects))); + } + + public void a(MobEffect mobeffect) { + this.effects.add(mobeffect); + this.getDataWatcher().set(EntityTippedArrow.g, PotionUtil.a((Collection) PotionUtil.a(this.potionRegistry, (Collection) this.effects))); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityTippedArrow.g, -1); + } + + public void tick() { + super.tick(); + if (this.world.isClientSide) { + if (this.inGround) { + if (this.c % 5 == 0) { + this.b(1); + } + } else { + this.b(2); + } + } else if (this.inGround && this.c != 0 && !this.effects.isEmpty() && this.c >= 600) { + this.world.broadcastEntityEffect(this, (byte) 0); + this.potionRegistry = Potions.EMPTY; + this.effects.clear(); + this.datawatcher.set(EntityTippedArrow.g, -1); + } + + } + + private void b(int i) { + int j = this.getColor(); + + if (j != -1 && i > 0) { + double d0 = (double) (j >> 16 & 255) / 255.0D; + double d1 = (double) (j >> 8 & 255) / 255.0D; + double d2 = (double) (j >> 0 & 255) / 255.0D; + + for (int k = 0; k < i; ++k) { + this.world.addParticle(Particles.s, this.locX + (this.random.nextDouble() - 0.5D) * (double) this.width, this.locY + this.random.nextDouble() * (double) this.length, this.locZ + (this.random.nextDouble() - 0.5D) * (double) this.width, d0, d1, d2); + } + + } + } + + // CraftBukkit start accessor methods + public void refreshEffects() { + this.getDataWatcher().set(EntityTippedArrow.g, Integer.valueOf(PotionUtil.a((Collection) PotionUtil.a(this.potionRegistry, (Collection) this.effects)))); + } + + public String getType() { + return IRegistry.POTION.getKey(this.potionRegistry).toString(); + } + + public void setType(String string) { + this.potionRegistry = IRegistry.POTION.get(new MinecraftKey(string)); + this.datawatcher.set(EntityTippedArrow.g, Integer.valueOf(PotionUtil.a((Collection) PotionUtil.a(this.potionRegistry, (Collection) this.effects)))); + } + + public boolean isTipped() { + return !(this.effects.isEmpty() && this.potionRegistry == Potions.EMPTY); + } + // CraftBukkit end + + public int getColor() { + return (Integer) this.datawatcher.get(EntityTippedArrow.g); + } + + public void setColor(int i) { + this.hasColor = true; + this.datawatcher.set(EntityTippedArrow.g, i); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + if (this.potionRegistry != Potions.EMPTY && this.potionRegistry != null) { + nbttagcompound.setString("Potion", IRegistry.POTION.getKey(this.potionRegistry).toString()); + } + + if (this.hasColor) { + nbttagcompound.setInt("Color", this.getColor()); + } + + if (!this.effects.isEmpty()) { + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = this.effects.iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + nbttaglist.add((NBTBase) mobeffect.a(new NBTTagCompound())); + } + + nbttagcompound.set("CustomPotionEffects", nbttaglist); + } + + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKeyOfType("Potion", 8)) { + this.potionRegistry = PotionUtil.c(nbttagcompound); + } + + Iterator iterator = PotionUtil.b(nbttagcompound).iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + this.a(mobeffect); + } + + if (nbttagcompound.hasKeyOfType("Color", 99)) { + this.setColor(nbttagcompound.getInt("Color")); + } else { + this.s(); + } + + } + + protected void a(EntityLiving entityliving) { + super.a(entityliving); + Iterator iterator = this.potionRegistry.a().iterator(); + + MobEffect mobeffect; + + while (iterator.hasNext()) { + mobeffect = (MobEffect) iterator.next(); + entityliving.addEffect(new MobEffect(mobeffect.getMobEffect(), Math.max(mobeffect.getDuration() / 8, 1), mobeffect.getAmplifier(), mobeffect.isAmbient(), mobeffect.isShowParticles()), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ARROW); // CraftBukkit + } + + if (!this.effects.isEmpty()) { + iterator = this.effects.iterator(); + + while (iterator.hasNext()) { + mobeffect = (MobEffect) iterator.next(); + entityliving.addEffect(mobeffect, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ARROW); // CraftBukkit + } + } + + } + + protected ItemStack getItemStack() { + if (this.effects.isEmpty() && this.potionRegistry == Potions.EMPTY) { + return new ItemStack(Items.ARROW); + } else { + ItemStack itemstack = new ItemStack(Items.TIPPED_ARROW); + + PotionUtil.a(itemstack, this.potionRegistry); + PotionUtil.a(itemstack, (Collection) this.effects); + if (this.hasColor) { + itemstack.getOrCreateTag().setInt("CustomPotionColor", this.getColor()); + } + + return itemstack; + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityTracker.java b/src/main/java/net/minecraft/server/EntityTracker.java new file mode 100644 index 000000000000..3854ae9769c9 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityTracker.java @@ -0,0 +1,302 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class EntityTracker { + + private static final Logger a = LogManager.getLogger(); + private final WorldServer world; + private final Set c = Sets.newHashSet(); + public final IntHashMap trackedEntities = new IntHashMap<>(); + private int trackingDistance; + + public EntityTracker(WorldServer worldserver) { + this.world = worldserver; + this.trackingDistance = PlayerChunkMap.getFurthestViewableBlock(worldserver.spigotConfig.viewDistance); // Spigot + } + + public static long a(double d0) { + return MathHelper.d(d0 * 4096.0D); + } + + public void track(Entity entity) { + if (entity instanceof EntityPlayer) { + this.addEntity(entity, 512, 2); + EntityPlayer entityplayer = (EntityPlayer) entity; + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + + if (entitytrackerentry.b() != entityplayer) { + entitytrackerentry.updatePlayer(entityplayer); + } + } + } else if (entity instanceof EntityFishingHook) { + this.addEntity(entity, 64, 5, true); + } else if (entity instanceof EntityArrow) { + this.addEntity(entity, 64, 20, false); + } else if (entity instanceof EntitySmallFireball) { + this.addEntity(entity, 64, 10, false); + } else if (entity instanceof EntityFireball) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntitySnowball) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityLlamaSpit) { + this.addEntity(entity, 64, 10, false); + } else if (entity instanceof EntityEnderPearl) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityEnderSignal) { + this.addEntity(entity, 64, 4, true); + } else if (entity instanceof EntityEgg) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityPotion) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityThrownExpBottle) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityFireworks) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityItem) { + this.addEntity(entity, 64, 20, true); + } else if (entity instanceof EntityMinecartAbstract) { + this.addEntity(entity, 80, 3, true); + } else if (entity instanceof EntityBoat) { + this.addEntity(entity, 80, 3, true); + } else if (entity instanceof EntitySquid) { + this.addEntity(entity, 64, 3, true); + } else if (entity instanceof EntityWither) { + this.addEntity(entity, 80, 3, false); + } else if (entity instanceof EntityShulkerBullet) { + this.addEntity(entity, 80, 3, true); + } else if (entity instanceof EntityBat) { + this.addEntity(entity, 80, 3, false); + } else if (entity instanceof EntityEnderDragon) { + this.addEntity(entity, 160, 3, true); + } else if (entity instanceof IAnimal) { + this.addEntity(entity, 80, 3, true); + } else if (entity instanceof EntityTNTPrimed) { + this.addEntity(entity, 160, 10, true); + } else if (entity instanceof EntityFallingBlock) { + this.addEntity(entity, 160, 20, true); + } else if (entity instanceof EntityHanging) { + this.addEntity(entity, 160, Integer.MAX_VALUE, false); + } else if (entity instanceof EntityArmorStand) { + this.addEntity(entity, 160, 3, true); + } else if (entity instanceof EntityExperienceOrb) { + this.addEntity(entity, 160, 20, true); + } else if (entity instanceof EntityAreaEffectCloud) { + this.addEntity(entity, 160, 10, true); // CraftBukkit + } else if (entity instanceof EntityEnderCrystal) { + this.addEntity(entity, 256, Integer.MAX_VALUE, false); + } else if (entity instanceof EntityEvokerFangs) { + this.addEntity(entity, 160, 2, false); + } + + } + + public void addEntity(Entity entity, int i, int j) { + this.addEntity(entity, i, j, false); + } + + public void addEntity(Entity entity, int originalRange, int j, boolean flag) { // Spigot + org.spigotmc.AsyncCatcher.catchOp( "entity track"); // Spigot + int i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, originalRange); // Spigot + try { + if (this.trackedEntities.b(entity.getId())) { + throw new IllegalStateException("Entity is already tracked!"); + } + + EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(entity, i, this.trackingDistance, j, flag); + + this.c.add(entitytrackerentry); + this.trackedEntities.a(entity.getId(), entitytrackerentry); + entitytrackerentry.scanPlayers(this.world.players); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Adding entity to track"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity To Track"); + + crashreportsystemdetails.a("Tracking range", (Object) (i + " blocks")); + crashreportsystemdetails.a("Update interval", () -> { + String s = "Once per " + j + " ticks"; + + if (j == Integer.MAX_VALUE) { + s = "Maximum (" + s + ")"; + } + + return s; + }); + entity.appendEntityCrashDetails(crashreportsystemdetails); + ((EntityTrackerEntry) this.trackedEntities.get(entity.getId())).b().appendEntityCrashDetails(crashreport.a("Entity That Is Already Tracked")); + + try { + throw new ReportedException(crashreport); + } catch (ReportedException reportedexception) { + EntityTracker.a.error("\"Silently\" catching entity tracking error.", reportedexception); + } + } + + } + + public void untrackEntity(Entity entity) { + org.spigotmc.AsyncCatcher.catchOp( "entity untrack"); // Spigot + if (entity instanceof EntityPlayer) { + EntityPlayer entityplayer = (EntityPlayer) entity; + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + + entitytrackerentry.a(entityplayer); + } + } + + EntityTrackerEntry entitytrackerentry1 = (EntityTrackerEntry) this.trackedEntities.d(entity.getId()); + + if (entitytrackerentry1 != null) { + this.c.remove(entitytrackerentry1); + entitytrackerentry1.a(); + } + + } + + public void updatePlayers() { + List list = Lists.newArrayList(); + Iterator iterator = this.c.iterator(); + world.timings.tracker1.startTiming(); // Paper + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + + entitytrackerentry.track(this.world.players); + if (entitytrackerentry.b) { + Entity entity = entitytrackerentry.b(); + + if (entity instanceof EntityPlayer) { + list.add((EntityPlayer) entity); + } + } + } + world.timings.tracker1.stopTiming(); // Paper + + world.timings.tracker2.startTiming(); // Paper + for (int i = 0; i < list.size(); ++i) { + EntityPlayer entityplayer = (EntityPlayer) list.get(i); + Iterator iterator1 = this.c.iterator(); + + while (iterator1.hasNext()) { + EntityTrackerEntry entitytrackerentry1 = (EntityTrackerEntry) iterator1.next(); + + if (entitytrackerentry1.b() != entityplayer) { + entitytrackerentry1.updatePlayer(entityplayer); + } + } + } + world.timings.tracker2.stopTiming(); // Paper + + } + + public void updatePlayer(EntityPlayer entityplayer) { a(entityplayer); } // Paper - OBFHELPER + public void a(EntityPlayer entityplayer) { + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + + if (entitytrackerentry.b() == entityplayer) { + entitytrackerentry.scanPlayers(this.world.players); + } else { + entitytrackerentry.updatePlayer(entityplayer); + } + } + + } + + public void a(Entity entity, Packet packet) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) this.trackedEntities.get(entity.getId()); + + if (entitytrackerentry != null) { + entitytrackerentry.broadcast(packet); + } + + } + + public void sendPacketToEntity(Entity entity, Packet packet) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) this.trackedEntities.get(entity.getId()); + + if (entitytrackerentry != null) { + entitytrackerentry.broadcastIncludingSelf(packet); + } + + } + + public void untrackPlayer(EntityPlayer entityplayer) { + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + + entitytrackerentry.clear(entityplayer); + } + + } + + public void a(EntityPlayer entityplayer, Chunk chunk) { + List list = Lists.newArrayList(); + List list1 = Lists.newArrayList(); + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + Entity entity = entitytrackerentry.b(); + + if (entity != entityplayer && entity.chunkX == chunk.locX && entity.chunkZ == chunk.locZ) { + entitytrackerentry.updatePlayer(entityplayer); + if (entity instanceof EntityInsentient && ((EntityInsentient) entity).getLeashHolder() != null) { + list.add(entity); + } + + if (!entity.bP().isEmpty()) { + list1.add(entity); + } + } + } + + Entity entity1; + + if (!list.isEmpty()) { + iterator = list.iterator(); + + while (iterator.hasNext()) { + entity1 = (Entity) iterator.next(); + entityplayer.playerConnection.sendPacket(new PacketPlayOutAttachEntity(entity1, ((EntityInsentient) entity1).getLeashHolder())); + } + } + + if (!list1.isEmpty()) { + iterator = list1.iterator(); + + while (iterator.hasNext()) { + entity1 = (Entity) iterator.next(); + entityplayer.playerConnection.sendPacket(new PacketPlayOutMount(entity1)); + } + } + + } + + public void a(int i) { + this.trackingDistance = (i - 1) * 16; + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + + entitytrackerentry.a(this.trackingDistance); + } + + } +} diff --git a/src/main/java/net/minecraft/server/EntityTrackerEntry.java b/src/main/java/net/minecraft/server/EntityTrackerEntry.java new file mode 100644 index 000000000000..2b8501143c9b --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityTrackerEntry.java @@ -0,0 +1,666 @@ +package net.minecraft.server; + +import com.google.common.collect.Sets; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerVelocityEvent; +// CraftBukkit end + +public class EntityTrackerEntry { + + private static final Logger c = LogManager.getLogger(); + private final Entity tracker; + private final int e; + private int f; + private final int g; + private long xLoc; + private long yLoc; + private long zLoc; + private int yRot; + private int xRot; + private int headYaw; + private double n; + private double o; + private double p; + public int a; + private double q; + private double r; + private double s; + private boolean isMoving; + private final boolean u; + private int v; + private List w = Collections.emptyList(); + private boolean x; + private boolean y; + public boolean b; + // Paper start + // Replace trackedPlayers Set with a Map. The value is true until the player receives + // their first update (which is forced to have absolute coordinates), false afterward. + public java.util.Map trackedPlayerMap = new java.util.HashMap(); + public Set trackedPlayers = trackedPlayerMap.keySet(); + + /** + * Requested in https://github.com/PaperMC/Paper/issues/1537 to allow intercepting packets + */ + public void sendPlayerPacket(EntityPlayer player, Packet packet) { + player.playerConnection.sendPacket(packet); + } + // Paper end + + public EntityTrackerEntry(Entity entity, int i, int j, int k, boolean flag) { + entity.tracker = this; // Paper + this.tracker = entity; + this.e = i; + this.f = j; + this.g = k; + this.u = flag; + this.xLoc = EntityTracker.a(entity.locX); + this.yLoc = EntityTracker.a(entity.locY); + this.zLoc = EntityTracker.a(entity.locZ); + this.yRot = MathHelper.d(entity.yaw * 256.0F / 360.0F); + this.xRot = MathHelper.d(entity.pitch * 256.0F / 360.0F); + this.headYaw = MathHelper.d(entity.getHeadRotation() * 256.0F / 360.0F); + this.y = entity.onGround; + } + + public boolean equals(Object object) { + return object instanceof EntityTrackerEntry ? ((EntityTrackerEntry) object).tracker.getId() == this.tracker.getId() : false; + } + + public int hashCode() { + return this.tracker.getId(); + } + + public void track(List list) { + this.b = false; + if (!this.isMoving || this.tracker.d(this.q, this.r, this.s) > 16.0D) { + this.q = this.tracker.locX; + this.r = this.tracker.locY; + this.s = this.tracker.locZ; + this.isMoving = true; + this.b = true; + this.scanPlayers(list); + } + + List list1 = this.tracker.bP(); + + if (!list1.equals(this.w)) { + this.w = list1; + this.broadcastIncludingSelf(new PacketPlayOutMount(this.tracker)); // CraftBukkit + } + + // PAIL : rename + if (this.tracker instanceof EntityItemFrame && this.a % 20 == 0) { // Paper + EntityItemFrame entityitemframe = (EntityItemFrame) this.tracker; + ItemStack itemstack = entityitemframe.getItem(); + + if (this.a % 10 == 0 && itemstack.getItem() instanceof ItemWorldMap) { // CraftBukkit - Moved this.a % 10 logic here so item frames do not enter the other blocks + WorldMap worldmap = ItemWorldMap.getSavedMap(itemstack, this.tracker.world); + Iterator iterator = this.trackedPlayers.iterator(); // CraftBukkit + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + EntityPlayer entityplayer = (EntityPlayer) entityhuman; + + worldmap.a((EntityHuman) entityplayer, itemstack); + Packet packet = ((ItemWorldMap) itemstack.getItem()).a(itemstack, this.tracker.world, (EntityHuman) entityplayer); + + if (packet != null) { + entityplayer.playerConnection.sendPacket(packet); + } + } + } + + this.d(); + } + + if (this.a % this.g == 0 || this.tracker.impulse || this.tracker.getDataWatcher().a()) { + int i; + + if (this.tracker.isPassenger()) { + i = MathHelper.d(this.tracker.yaw * 256.0F / 360.0F); + int j = MathHelper.d(this.tracker.pitch * 256.0F / 360.0F); + boolean flag = Math.abs(i - this.yRot) >= 1 || Math.abs(j - this.xRot) >= 1; + + if (flag) { + this.broadcast(new PacketPlayOutEntity.PacketPlayOutEntityLook(this.tracker.getId(), (byte) i, (byte) j, this.tracker.onGround)); + this.yRot = i; + this.xRot = j; + } + + this.xLoc = EntityTracker.a(this.tracker.locX); + this.yLoc = EntityTracker.a(this.tracker.locY); + this.zLoc = EntityTracker.a(this.tracker.locZ); + this.d(); + this.x = true; + } else { + ++this.v; + long k = EntityTracker.a(this.tracker.locX); + long l = EntityTracker.a(this.tracker.locY); + long i1 = EntityTracker.a(this.tracker.locZ); + int j1 = MathHelper.d(this.tracker.yaw * 256.0F / 360.0F); + int k1 = MathHelper.d(this.tracker.pitch * 256.0F / 360.0F); + long l1 = k - this.xLoc; + long i2 = l - this.yLoc; + long j2 = i1 - this.zLoc; + Packet packet1 = null; + boolean flag1 = l1 * l1 + i2 * i2 + j2 * j2 >= 128L || this.a % 60 == 0; + boolean flag2 = Math.abs(j1 - this.yRot) >= 1 || Math.abs(k1 - this.xRot) >= 1; + + if (this.a > 0 || this.tracker instanceof EntityArrow) { // Paper - Moved up + // CraftBukkit start - Code moved from below + if (flag1) { + this.xLoc = k; + this.yLoc = l; + this.zLoc = i1; + } + + if (flag2) { + this.yRot = j1; + this.xRot = k1; + } + // CraftBukkit end + + if (l1 >= -32768L && l1 < 32768L && i2 >= -32768L && i2 < 32768L && j2 >= -32768L && j2 < 32768L && this.v <= 400 && !this.x && this.y == this.tracker.onGround) { + if ((!flag1 || !flag2) && !(this.tracker instanceof EntityArrow)) { + if (flag1) { + packet1 = new PacketPlayOutEntity.PacketPlayOutRelEntityMove(this.tracker.getId(), l1, i2, j2, this.tracker.onGround); + } else if (flag2) { + packet1 = new PacketPlayOutEntity.PacketPlayOutEntityLook(this.tracker.getId(), (byte) j1, (byte) k1, this.tracker.onGround); + } + } else { + packet1 = new PacketPlayOutEntity.PacketPlayOutRelEntityMoveLook(this.tracker.getId(), l1, i2, j2, (byte) j1, (byte) k1, this.tracker.onGround); + } + } else { + this.y = this.tracker.onGround; + this.v = 0; + // CraftBukkit start - Refresh list of who can see a player before sending teleport packet + if (this.tracker instanceof EntityPlayer) { + this.scanPlayers(new java.util.ArrayList(this.trackedPlayers)); + } + // CraftBukkit end + this.c(); + packet1 = new PacketPlayOutEntityTeleport(this.tracker); + } + } + + boolean flag3 = this.u || this.tracker.impulse; + + if (this.tracker instanceof EntityLiving && ((EntityLiving) this.tracker).dc()) { + flag3 = true; + } + + if (flag3 && this.a > 0) { + double d0 = this.tracker.motX - this.n; + double d1 = this.tracker.motY - this.o; + double d2 = this.tracker.motZ - this.p; + double d3 = 0.02D; + double d4 = d0 * d0 + d1 * d1 + d2 * d2; + + if (d4 > 4.0E-4D || d4 > 0.0D && this.tracker.motX == 0.0D && this.tracker.motY == 0.0D && this.tracker.motZ == 0.0D) { + this.n = this.tracker.motX; + this.o = this.tracker.motY; + this.p = this.tracker.motZ; + this.broadcast(new PacketPlayOutEntityVelocity(this.tracker.getId(), this.n, this.o, this.p)); + } + } + + if (packet1 != null) { + // Paper start - ensure fresh viewers get an absolute position on their first update, + // since we can't be certain what position they received in the spawn packet. + if (packet1 instanceof PacketPlayOutEntityTeleport) { + this.broadcast((Packet) packet1); + } else { + PacketPlayOutEntityTeleport teleportPacket = null; + + for (java.util.Map.Entry viewer : trackedPlayerMap.entrySet()) { + if (viewer.getValue()) { + viewer.setValue(false); + if (teleportPacket == null) { + teleportPacket = new PacketPlayOutEntityTeleport(this.tracker); + } + sendPlayerPacket(viewer.getKey(), teleportPacket); + } else { + sendPlayerPacket(viewer.getKey(), (Packet) packet1); + } + } + } + // Paper end + } + + this.d(); + /* CraftBukkit start - Code moved up + if (flag1) { + this.xLoc = k; + this.yLoc = l; + this.zLoc = i1; + } + + if (flag2) { + this.yRot = j1; + this.xRot = k1; + } + // CraftBukkit end */ + + this.x = false; + } + + i = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F); + if (Math.abs(i - this.headYaw) >= 1) { + this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) i)); + this.headYaw = i; + } + + this.tracker.impulse = false; + } + + ++this.a; + if (this.tracker.velocityChanged) { + // CraftBukkit start - Create PlayerVelocity event + boolean cancelled = false; + + if (this.tracker instanceof EntityPlayer) { + Player player = (Player) this.tracker.getBukkitEntity(); + org.bukkit.util.Vector velocity = player.getVelocity(); + + PlayerVelocityEvent event = new PlayerVelocityEvent(player, velocity.clone()); + this.tracker.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + cancelled = true; + } else if (!velocity.equals(event.getVelocity())) { + player.setVelocity(event.getVelocity()); + } + } + + if (!cancelled) { + this.broadcastIncludingSelf(new PacketPlayOutEntityVelocity(this.tracker)); + } + // CraftBukkit end + this.tracker.velocityChanged = false; + } + + } + + private void d() { + DataWatcher datawatcher = this.tracker.getDataWatcher(); + + if (datawatcher.a()) { + this.broadcastIncludingSelf(new PacketPlayOutEntityMetadata(this.tracker.getId(), datawatcher, false)); + } + + if (this.tracker instanceof EntityLiving) { + AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap(); + Set set = attributemapserver.getAttributes(); + + if (!set.isEmpty()) { + // CraftBukkit start - Send scaled max health + if (this.tracker instanceof EntityPlayer) { + ((EntityPlayer) this.tracker).getBukkitEntity().injectScaledMaxHealth(set, false); + } + // CraftBukkit end + this.broadcastIncludingSelf(new PacketPlayOutUpdateAttributes(this.tracker.getId(), set)); + } + + set.clear(); + } + + } + + public void broadcast(Packet packet) { + Iterator iterator = this.trackedPlayers.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + entityplayer.playerConnection.sendPacket(packet); + } + + } + + public void broadcastIncludingSelf(Packet packet) { + this.broadcast(packet); + if (this.tracker instanceof EntityPlayer) { + ((EntityPlayer) this.tracker).playerConnection.sendPacket(packet); + } + + } + + public void a() { + Iterator iterator = this.trackedPlayers.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + this.tracker.c(entityplayer); + entityplayer.c(this.tracker); + } + + } + + public void a(EntityPlayer entityplayer) { + if (this.trackedPlayers.contains(entityplayer)) { + this.tracker.c(entityplayer); + entityplayer.c(this.tracker); + this.trackedPlayers.remove(entityplayer); + } + + } + + public void updatePlayer(EntityPlayer entityplayer) { + org.spigotmc.AsyncCatcher.catchOp( "player tracker update"); // Spigot + if (entityplayer != this.tracker) { + if (this.c(entityplayer)) { + if (!this.trackedPlayers.contains(entityplayer) && (this.e(entityplayer) || this.tracker.attachedToPlayer)) { + // CraftBukkit start - respect vanish API + if (this.tracker instanceof EntityPlayer) { + Player player = ((EntityPlayer) this.tracker).getBukkitEntity(); + if (!entityplayer.getBukkitEntity().canSee(player)) { + return; + } + } + + entityplayer.removeQueue.remove(Integer.valueOf(this.tracker.getId())); + // CraftBukkit end + this.trackedPlayerMap.put(entityplayer, true); // Paper + Packet packet = this.e(); + + entityplayer.playerConnection.sendPacket(packet); + if (!this.tracker.getDataWatcher().d()) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityMetadata(this.tracker.getId(), this.tracker.getDataWatcher(), true)); + } + + boolean flag = this.u; + + if (this.tracker instanceof EntityLiving) { + AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap(); + Collection collection = attributemapserver.c(); + + // CraftBukkit start - If sending own attributes send scaled health instead of current maximum health + if (this.tracker.getId() == entityplayer.getId()) { + ((EntityPlayer) this.tracker).getBukkitEntity().injectScaledMaxHealth(collection, false); + } + // CraftBukkit end + + if (!collection.isEmpty()) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(this.tracker.getId(), collection)); + } + + if (((EntityLiving) this.tracker).dc()) { + flag = true; + } + } + + this.n = this.tracker.motX; + this.o = this.tracker.motY; + this.p = this.tracker.motZ; + if (flag && !(packet instanceof PacketPlayOutSpawnEntityLiving)) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityVelocity(this.tracker.getId(), this.tracker.motX, this.tracker.motY, this.tracker.motZ)); + } + + if (this.tracker instanceof EntityLiving) { + EnumItemSlot[] aenumitemslot = EnumItemSlot.values(); + int i = aenumitemslot.length; + + for (int j = 0; j < i; ++j) { + EnumItemSlot enumitemslot = aenumitemslot[j]; + ItemStack itemstack = ((EntityLiving) this.tracker).getEquipment(enumitemslot); + + if (!itemstack.isEmpty()) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEquipment(this.tracker.getId(), enumitemslot, itemstack)); + } + } + } + + if (this.tracker instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) this.tracker; + + if (entityhuman.isSleeping()) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutBed(entityhuman, new BlockPosition(this.tracker))); + } + } + + // CraftBukkit start - Fix for nonsensical head yaw + this.headYaw = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F); + this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) headYaw)); + // CraftBukkit end + + if (this.tracker instanceof EntityLiving) { + EntityLiving entityliving = (EntityLiving) this.tracker; + Iterator iterator = entityliving.getEffects().iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.tracker.getId(), mobeffect)); + } + } + + if (!this.tracker.bP().isEmpty()) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker)); + } + + if (this.tracker.isPassenger()) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker.getVehicle())); + } + + this.tracker.b(entityplayer); + entityplayer.d(this.tracker); + updatePassengers(entityplayer); // Paper + } + } else if (this.trackedPlayers.contains(entityplayer)) { + this.trackedPlayers.remove(entityplayer); + this.tracker.c(entityplayer); + entityplayer.c(this.tracker); + updatePassengers(entityplayer); // Paper + } + + } + } + + public boolean c(EntityPlayer entityplayer) { + // Paper start + if (tracker.isPassenger()) { + return isTrackedBy(tracker.getVehicle(), entityplayer); + } else if (hasPassengerInRange(tracker, entityplayer)) { + return true; + } + + return isInRangeOfPlayer(entityplayer); + } + private static boolean hasPassengerInRange(Entity entity, EntityPlayer entityplayer) { + if (!entity.isVehicle()) { + return false; + } + for (Entity passenger : entity.passengers) { + if (passenger.tracker != null && passenger.tracker.isInRangeOfPlayer(entityplayer)) { + return true; + } + if (passenger.isVehicle()) { + if (hasPassengerInRange(passenger, entityplayer)) { + return true; + } + } + } + return false; + } + private static boolean isTrackedBy(Entity entity, EntityPlayer entityplayer) { + return entity == entityplayer || entity.tracker != null && entity.tracker.trackedPlayers.contains(entityplayer); + } + private void updatePassengers(EntityPlayer player) { + if (tracker.isVehicle()) { + tracker.passengers.forEach((e) -> { + if (e.tracker != null) { + e.tracker.updatePlayer(player); + } + }); + player.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker)); + } + } + private boolean isInRangeOfPlayer(EntityPlayer entityplayer) { + // Paper end + double d0 = entityplayer.locX - (double) this.xLoc / 4096.0D; + double d1 = entityplayer.locZ - (double) this.zLoc / 4096.0D; + int i = Math.min(this.e, (entityplayer.getViewDistance() - 1) * 16); // Paper - Use player view distance API + + return d0 >= (double) (-i) && d0 <= (double) i && d1 >= (double) (-i) && d1 <= (double) i && this.tracker.a(entityplayer); + } + + private boolean e(EntityPlayer entityplayer) { + return entityplayer.getWorldServer().getPlayerChunkMap().a(entityplayer, this.tracker.chunkX, this.tracker.chunkZ); + } + + public void scanPlayers(List list) { + for (int i = 0; i < list.size(); ++i) { + this.updatePlayer((EntityPlayer) list.get(i)); + } + + } + + private Packet e() { + if (this.tracker.dead) { + // CraftBukkit start - Remove useless error spam, just return + // EntityTrackerEntry.d.warn("Fetching addPacket for removed entity"); + return null; + // CraftBukkit end + } + + if (this.tracker instanceof EntityPlayer) { + return new PacketPlayOutNamedEntitySpawn((EntityHuman) this.tracker); + } else if (this.tracker instanceof IAnimal) { + this.headYaw = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F); + return new PacketPlayOutSpawnEntityLiving((EntityLiving) this.tracker); + } else if (this.tracker instanceof EntityPainting) { + return new PacketPlayOutSpawnEntityPainting((EntityPainting) this.tracker); + } else if (this.tracker instanceof EntityItem) { + return new PacketPlayOutSpawnEntity(this.tracker, 2, 1); + } else if (this.tracker instanceof EntityMinecartAbstract) { + EntityMinecartAbstract entityminecartabstract = (EntityMinecartAbstract) this.tracker; + + return new PacketPlayOutSpawnEntity(this.tracker, 10, entityminecartabstract.v().a()); + } else if (this.tracker instanceof EntityBoat) { + return new PacketPlayOutSpawnEntity(this.tracker, 1); + } else if (this.tracker instanceof EntityExperienceOrb) { + return new PacketPlayOutSpawnEntityExperienceOrb((EntityExperienceOrb) this.tracker); + } else if (this.tracker instanceof EntityFishingHook) { + EntityHuman entityhuman = ((EntityFishingHook) this.tracker).i(); + + return new PacketPlayOutSpawnEntity(this.tracker, 90, entityhuman == null ? this.tracker.getId() : entityhuman.getId()); + } else { + Entity entity; + + if (this.tracker instanceof EntitySpectralArrow) { + entity = ((EntitySpectralArrow) this.tracker).getShooter(); + return new PacketPlayOutSpawnEntity(this.tracker, 91, 1 + (entity == null ? this.tracker.getId() : entity.getId())); + } else if (this.tracker instanceof EntityTippedArrow) { + entity = ((EntityArrow) this.tracker).getShooter(); + return new PacketPlayOutSpawnEntity(this.tracker, 60, 1 + (entity == null ? this.tracker.getId() : entity.getId())); + } else if (this.tracker instanceof EntitySnowball) { + return new PacketPlayOutSpawnEntity(this.tracker, 61); + } else if (this.tracker instanceof EntityThrownTrident) { + entity = ((EntityArrow) this.tracker).getShooter(); + return new PacketPlayOutSpawnEntity(this.tracker, 94, 1 + (entity == null ? this.tracker.getId() : entity.getId())); + } else if (this.tracker instanceof EntityLlamaSpit) { + return new PacketPlayOutSpawnEntity(this.tracker, 68); + } else if (this.tracker instanceof EntityPotion) { + return new PacketPlayOutSpawnEntity(this.tracker, 73); + } else if (this.tracker instanceof EntityThrownExpBottle) { + return new PacketPlayOutSpawnEntity(this.tracker, 75); + } else if (this.tracker instanceof EntityEnderPearl) { + return new PacketPlayOutSpawnEntity(this.tracker, 65); + } else if (this.tracker instanceof EntityEnderSignal) { + return new PacketPlayOutSpawnEntity(this.tracker, 72); + } else if (this.tracker instanceof EntityFireworks) { + return new PacketPlayOutSpawnEntity(this.tracker, 76); + } else if (this.tracker instanceof EntityFireball) { + EntityFireball entityfireball = (EntityFireball) this.tracker; + byte b0 = 63; + + if (this.tracker instanceof EntitySmallFireball) { + b0 = 64; + } else if (this.tracker instanceof EntityDragonFireball) { + b0 = 93; + } else if (this.tracker instanceof EntityWitherSkull) { + b0 = 66; + } + + PacketPlayOutSpawnEntity packetplayoutspawnentity; + + if (entityfireball.shooter == null) { + packetplayoutspawnentity = new PacketPlayOutSpawnEntity(this.tracker, b0, 0); + } else { + packetplayoutspawnentity = new PacketPlayOutSpawnEntity(this.tracker, b0, ((EntityFireball) this.tracker).shooter.getId()); + } + + packetplayoutspawnentity.a((int) (entityfireball.dirX * 8000.0D)); + packetplayoutspawnentity.b((int) (entityfireball.dirY * 8000.0D)); + packetplayoutspawnentity.c((int) (entityfireball.dirZ * 8000.0D)); + return packetplayoutspawnentity; + } else if (this.tracker instanceof EntityShulkerBullet) { + PacketPlayOutSpawnEntity packetplayoutspawnentity1 = new PacketPlayOutSpawnEntity(this.tracker, 67, 0); + + packetplayoutspawnentity1.a((int) (this.tracker.motX * 8000.0D)); + packetplayoutspawnentity1.b((int) (this.tracker.motY * 8000.0D)); + packetplayoutspawnentity1.c((int) (this.tracker.motZ * 8000.0D)); + return packetplayoutspawnentity1; + } else if (this.tracker instanceof EntityEgg) { + return new PacketPlayOutSpawnEntity(this.tracker, 62); + } else if (this.tracker instanceof EntityEvokerFangs) { + return new PacketPlayOutSpawnEntity(this.tracker, 79); + } else if (this.tracker instanceof EntityTNTPrimed) { + return new PacketPlayOutSpawnEntity(this.tracker, 50); + } else if (this.tracker instanceof EntityEnderCrystal) { + return new PacketPlayOutSpawnEntity(this.tracker, 51); + } else if (this.tracker instanceof EntityFallingBlock) { + EntityFallingBlock entityfallingblock = (EntityFallingBlock) this.tracker; + + return new PacketPlayOutSpawnEntity(this.tracker, 70, Block.getCombinedId(entityfallingblock.getBlock())); + } else if (this.tracker instanceof EntityArmorStand) { + return new PacketPlayOutSpawnEntity(this.tracker, 78); + } else if (this.tracker instanceof EntityItemFrame) { + EntityItemFrame entityitemframe = (EntityItemFrame) this.tracker; + + return new PacketPlayOutSpawnEntity(this.tracker, 71, entityitemframe.direction.a(), entityitemframe.getBlockPosition()); + } else if (this.tracker instanceof EntityLeash) { + EntityLeash entityleash = (EntityLeash) this.tracker; + + return new PacketPlayOutSpawnEntity(this.tracker, 77, 0, entityleash.getBlockPosition()); + } else if (this.tracker instanceof EntityAreaEffectCloud) { + return new PacketPlayOutSpawnEntity(this.tracker, 3); + } else { + throw new IllegalArgumentException("Don't know how to add " + this.tracker.getClass() + "!"); + } + } + } + + public void clear(EntityPlayer entityplayer) { + org.spigotmc.AsyncCatcher.catchOp( "player tracker clear"); // Spigot + if (this.trackedPlayers.contains(entityplayer)) { + this.trackedPlayers.remove(entityplayer); + this.tracker.c(entityplayer); + entityplayer.c(this.tracker); + updatePassengers(entityplayer); // Paper + } + + } + + public Entity b() { + return this.tracker; + } + + public void a(int i) { + this.f = i; + } + + public void c() { + this.isMoving = false; + } +} diff --git a/src/main/java/net/minecraft/server/EntityTurtle.java b/src/main/java/net/minecraft/server/EntityTurtle.java new file mode 100644 index 000000000000..270b950820ed --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityTurtle.java @@ -0,0 +1,718 @@ +package net.minecraft.server; + +import com.google.common.collect.Sets; +import java.util.Random; +import java.util.Set; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public class EntityTurtle extends EntityAnimal { + + private static final DataWatcherObject bD = DataWatcher.a(EntityTurtle.class, DataWatcherRegistry.l); + private static final DataWatcherObject bE = DataWatcher.a(EntityTurtle.class, DataWatcherRegistry.i); + private static final DataWatcherObject bG = DataWatcher.a(EntityTurtle.class, DataWatcherRegistry.i); + private static final DataWatcherObject bH = DataWatcher.a(EntityTurtle.class, DataWatcherRegistry.l); + private static final DataWatcherObject bI = DataWatcher.a(EntityTurtle.class, DataWatcherRegistry.i); + private static final DataWatcherObject bJ = DataWatcher.a(EntityTurtle.class, DataWatcherRegistry.i); + private int bK; + public static final Predicate bC = (entity) -> { + return !(entity instanceof EntityLiving) ? false : ((EntityLiving) entity).isBaby() && !entity.isInWater(); + }; + + public EntityTurtle(World world) { + super(EntityTypes.TURTLE, world); + this.setSize(1.2F, 0.4F); + this.moveController = new EntityTurtle.e(this); + this.bF = Blocks.SAND; + this.Q = 1.0F; + } + + public void setHome(BlockPosition pos) { g(pos); } // Paper - OBFHELPER + public void g(BlockPosition blockposition) { + this.datawatcher.set(EntityTurtle.bD, blockposition); + } + + public BlockPosition getHome() { return dA(); } // Paper - OBFHELPER + private BlockPosition dA() { + return (BlockPosition) this.datawatcher.get(EntityTurtle.bD); + } + + public void setTravelPos(BlockPosition pos) { h(pos); } // Paper - OBFHELPER + private void h(BlockPosition blockposition) { + this.datawatcher.set(EntityTurtle.bH, blockposition); + } + + public BlockPosition getTravelPos() { return dB(); } // Paper - OBFHELPER + private BlockPosition dB() { + return (BlockPosition) this.datawatcher.get(EntityTurtle.bH); + } + + public boolean hasEgg() { return dy(); } // Paper - OBFHELPER + public boolean dy() { + return (Boolean) this.datawatcher.get(EntityTurtle.bE); + } + + public void setHasEgg(boolean hasEgg) { s(hasEgg); } // Paper - OBFHELPER + private void s(boolean flag) { + this.datawatcher.set(EntityTurtle.bE, flag); + } + + public boolean isDigging() { return dz(); } // Paper - OBFHELPER + public boolean dz() { + return (Boolean) this.datawatcher.get(EntityTurtle.bG); + } + + public void setDigging(boolean digging) { t(digging); } // Paper - OBFHELPER + private void t(boolean flag) { + this.bK = flag ? 1 : 0; + this.datawatcher.set(EntityTurtle.bG, flag); + } + + public boolean isGoingHome() { return dC(); } // Paper - OBFHELPER + private boolean dC() { + return (Boolean) this.datawatcher.get(EntityTurtle.bI); + } + + public void setGoingHome(boolean goingHome) { u(goingHome); } // Paper - OBFHELPER + private void u(boolean flag) { + this.datawatcher.set(EntityTurtle.bI, flag); + } + + public boolean isTravelling() { return dH(); } // Paper - OBFHELPER + private boolean dH() { + return (Boolean) this.datawatcher.get(EntityTurtle.bJ); + } + + public void setTravelling(boolean travelling) { v(travelling); } // Paper - OBFHELPER + private void v(boolean flag) { + this.datawatcher.set(EntityTurtle.bJ, flag); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityTurtle.bD, BlockPosition.ZERO); + this.datawatcher.register(EntityTurtle.bE, false); + this.datawatcher.register(EntityTurtle.bH, BlockPosition.ZERO); + this.datawatcher.register(EntityTurtle.bI, false); + this.datawatcher.register(EntityTurtle.bJ, false); + this.datawatcher.register(EntityTurtle.bG, false); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("HomePosX", this.dA().getX()); + nbttagcompound.setInt("HomePosY", this.dA().getY()); + nbttagcompound.setInt("HomePosZ", this.dA().getZ()); + nbttagcompound.setBoolean("HasEgg", this.dy()); + nbttagcompound.setInt("TravelPosX", this.dB().getX()); + nbttagcompound.setInt("TravelPosY", this.dB().getY()); + nbttagcompound.setInt("TravelPosZ", this.dB().getZ()); + } + + public void a(NBTTagCompound nbttagcompound) { + int i = nbttagcompound.getInt("HomePosX"); + int j = nbttagcompound.getInt("HomePosY"); + int k = nbttagcompound.getInt("HomePosZ"); + + this.g(new BlockPosition(i, j, k)); + super.a(nbttagcompound); + this.s(nbttagcompound.getBoolean("HasEgg")); + int l = nbttagcompound.getInt("TravelPosX"); + int i1 = nbttagcompound.getInt("TravelPosY"); + int j1 = nbttagcompound.getInt("TravelPosZ"); + + this.h(new BlockPosition(l, i1, j1)); + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + this.g(new BlockPosition(this.locX, this.locY, this.locZ)); + this.h(BlockPosition.ZERO); + return super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + BlockPosition blockposition = new BlockPosition(this.locX, this.getBoundingBox().minY, this.locZ); + + return blockposition.getY() < generatoraccess.getSeaLevel() + 4 && super.a(generatoraccess, flag); + } + + protected void n() { + this.goalSelector.a(0, new EntityTurtle.f(this, 1.2D)); + this.goalSelector.a(1, new EntityTurtle.a(this, 1.0D)); + this.goalSelector.a(1, new EntityTurtle.d(this, 1.0D)); + this.goalSelector.a(2, new EntityTurtle.i(this, 1.1D, Blocks.SEAGRASS.getItem())); + this.goalSelector.a(3, new EntityTurtle.c(this, 1.0D)); + this.goalSelector.a(4, new EntityTurtle.b(this, 1.0D)); + this.goalSelector.a(7, new EntityTurtle.j(this, 1.0D)); + this.goalSelector.a(8, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(9, new EntityTurtle.h(this, 1.0D, 100)); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(30.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.25D); + } + + public boolean bw() { + return false; + } + + public boolean ca() { + return true; + } + + public EnumMonsterType getMonsterType() { + return EnumMonsterType.e; + } + + public int z() { + return 200; + } + + @Nullable + protected SoundEffect D() { + return !this.isInWater() && this.onGround && !this.isBaby() ? SoundEffects.ENTITY_TURTLE_AMBIENT_LAND : super.D(); + } + + protected void d(float f) { + super.d(f * 1.5F); + } + + protected SoundEffect ad() { + return SoundEffects.ENTITY_TURTLE_SWIM; + } + + @Nullable + protected SoundEffect d(DamageSource damagesource) { + return this.isBaby() ? SoundEffects.ENTITY_TURTLE_HURT_BABY : SoundEffects.ENTITY_TURTLE_HURT; + } + + @Nullable + protected SoundEffect cs() { + return this.isBaby() ? SoundEffects.ENTITY_TURTLE_DEATH_BABY : SoundEffects.ENTITY_TURTLE_DEATH; + } + + protected void a(BlockPosition blockposition, IBlockData iblockdata) { + SoundEffect soundeffect = this.isBaby() ? SoundEffects.ENTITY_TURTLE_SHAMBLE_BABY : SoundEffects.ENTITY_TURTLE_SHAMBLE; + + this.a(soundeffect, 0.15F, 1.0F); + } + + public boolean dD() { + return super.dD() && !this.dy(); + } + + protected float ab() { + return this.L + 0.15F; + } + + public void a(boolean flag) { + this.a(flag ? 0.3F : 1.0F); + } + + protected NavigationAbstract b(World world) { + return new EntityTurtle.g(this, world); + } + + @Nullable + public EntityAgeable createChild(EntityAgeable entityageable) { + return EntityTypes.TURTLE.create(world); // Paper + } + + public boolean f(ItemStack itemstack) { + return itemstack.getItem() == Blocks.SEAGRASS.getItem(); + } + + public float a(BlockPosition blockposition, IWorldReader iworldreader) { + return !this.dC() && iworldreader.getFluid(blockposition).a(TagsFluid.WATER) ? 10.0F : super.a(blockposition, iworldreader); + } + + public void movementTick() { + super.movementTick(); + if (this.dz() && this.bK >= 1 && this.bK % 5 == 0) { + BlockPosition blockposition = new BlockPosition(this); + + if (this.world.getType(blockposition.down()).getBlock() == Blocks.SAND) { + this.world.triggerEffect(2001, blockposition, Block.getCombinedId(Blocks.SAND.getBlockData())); + } + } + + } + + protected void l() { + super.l(); + if (this.world.getGameRules().getBoolean("doMobLoot")) { + this.forceDrops = true; // CraftBukkit + this.a((IMaterial) Items.SCUTE, 1); + this.forceDrops = false; // CraftBukkit + } + + } + + public void a(float f, float f1, float f2) { + if (this.cP() && this.isInWater()) { + this.a(f, f1, f2, 0.1F); + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + this.motX *= 0.8999999761581421D; + this.motY *= 0.8999999761581421D; + this.motZ *= 0.8999999761581421D; + if (this.getGoalTarget() == null && (!this.dC() || this.c(this.dA()) >= 400.0D)) { + this.motY -= 0.005D; + } + } else { + super.a(f, f1, f2); + } + + } + + public boolean a(EntityHuman entityhuman) { + return false; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.aI; + } + + public void onLightningStrike(EntityLightning entitylightning) { + org.bukkit.craftbukkit.event.CraftEventFactory.entityDamage = entitylightning; // CraftBukkit + this.damageEntity(DamageSource.LIGHTNING, Float.MAX_VALUE); + org.bukkit.craftbukkit.event.CraftEventFactory.entityDamage = null; // CraftBukkit + } + + public void die(DamageSource damagesource) { + super.die(damagesource); + if (damagesource == DamageSource.LIGHTNING) { + this.a(new ItemStack(Items.BOWL, 1), 0.0F); + } + + } + + static class g extends NavigationGuardian { + + g(EntityTurtle entityturtle, World world) { + super(entityturtle, world); + } + + protected boolean b() { + return true; + } + + protected Pathfinder a() { + return new Pathfinder(new PathfinderTurtle()); + } + + public boolean a(BlockPosition blockposition) { + if (this.a instanceof EntityTurtle) { + EntityTurtle entityturtle = (EntityTurtle) this.a; + + if (entityturtle.dH()) { + return this.b.getType(blockposition).getBlock() == Blocks.WATER; + } + } + + return !this.b.getType(blockposition.down()).isAir(); + } + } + + static class e extends ControllerMove { + + private final EntityTurtle i; + + e(EntityTurtle entityturtle) { + super(entityturtle); + this.i = entityturtle; + } + + private void g() { + if (this.i.isInWater()) { + this.i.motY += 0.005D; + if (this.i.c(this.i.dA()) > 256.0D) { + this.i.o(Math.max(this.i.cK() / 2.0F, 0.08F)); + } + + if (this.i.isBaby()) { + this.i.o(Math.max(this.i.cK() / 3.0F, 0.06F)); + } + } else if (this.i.onGround) { + this.i.o(Math.max(this.i.cK() / 2.0F, 0.06F)); + } + + } + + public void a() { + this.g(); + if (this.h == ControllerMove.Operation.MOVE_TO && !this.i.getNavigation().p()) { + double d0 = this.b - this.i.locX; + double d1 = this.c - this.i.locY; + double d2 = this.d - this.i.locZ; + double d3 = (double) MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + + d1 /= d3; + float f = (float) (MathHelper.c(d2, d0) * 57.2957763671875D) - 90.0F; + + this.i.yaw = this.a(this.i.yaw, f, 90.0F); + this.i.aQ = this.i.yaw; + float f1 = (float) (this.e * this.i.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).getValue()); + + this.i.o(this.i.cK() + (f1 - this.i.cK()) * 0.125F); + this.i.motY += (double) this.i.cK() * d1 * 0.1D; + } else { + this.i.o(0.0F); + } + } + } + + static class c extends PathfinderGoalGotoTarget { + + private final EntityTurtle f; + + private c(EntityTurtle entityturtle, double d0) { + super(entityturtle, entityturtle.isBaby() ? 2.0D : d0, 24); + this.f = entityturtle; + this.e = -1; + } + + public boolean b() { + return !this.f.isInWater() && this.c <= 1200 && this.a(this.f.world, this.d); + } + + public boolean a() { + return this.f.isBaby() && !this.f.isInWater() ? super.a() : (!this.f.dC() && !this.f.isInWater() && !this.f.dy() ? super.a() : false); + } + + public int j() { + return 1; + } + + public boolean i() { + return this.c % 160 == 0; + } + + protected boolean a(IWorldReader iworldreader, BlockPosition blockposition) { + Block block = iworldreader.getType(blockposition).getBlock(); + + return block == Blocks.WATER; + } + } + + static class h extends PathfinderGoalRandomStroll { + + private final EntityTurtle h; + + private h(EntityTurtle entityturtle, double d0, int i) { + super(entityturtle, d0, i); + this.h = entityturtle; + } + + public boolean a() { + return !this.a.isInWater() && !this.h.dC() && !this.h.dy() ? super.a() : false; + } + } + + static class d extends PathfinderGoalGotoTarget { + + private final EntityTurtle f; + + d(EntityTurtle entityturtle, double d0) { + super(entityturtle, d0, 16); + this.f = entityturtle; + } + + public boolean a() { + return this.f.dy() && this.f.c(this.f.dA()) < 81.0D ? super.a() : false; + } + + public boolean b() { + return super.b() && this.f.dy() && this.f.c(this.f.dA()) < 81.0D; + } + + public void e() { + super.e(); + BlockPosition blockposition = new BlockPosition(this.f); + + if (!this.f.isInWater() && this.k()) { + if (this.f.bK < 1) { + this.f.setDigging(new com.destroystokyo.paper.event.entity.TurtleStartDiggingEvent((org.bukkit.entity.Turtle) this.f.getBukkitEntity(), MCUtil.toLocation(this.f.world, this.d)).callEvent()); // Paper + } else if (this.f.bK > 200) { + World world = this.f.world; + + // CraftBukkit start + // Paper start + int eggCount = this.f.random.nextInt(4) + 1; + com.destroystokyo.paper.event.entity.TurtleLayEggEvent layEggEvent = new com.destroystokyo.paper.event.entity.TurtleLayEggEvent((org.bukkit.entity.Turtle) this.f.getBukkitEntity(), MCUtil.toLocation(this.f.world, this.d.up()), eggCount); + if (layEggEvent.callEvent() && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.f, this.d.up(), Blocks.TURTLE_EGG.getBlockData().set(BlockTurtleEgg.b, layEggEvent.getEggCount())).isCancelled()) { + world.a((EntityHuman) null, blockposition, SoundEffects.ENTITY_TURTLE_LAY_EGG, SoundCategory.BLOCKS, 0.3F, 0.9F + world.random.nextFloat() * 0.2F); + world.setTypeAndData(this.d.up(), (IBlockData) Blocks.TURTLE_EGG.getBlockData().set(BlockTurtleEgg.b, layEggEvent.getEggCount()), 3); + } + // CraftBukkit end + this.f.s(false); + this.f.t(false); + this.f.d(600); + } + + if (this.f.dz()) { + this.f.bK++; + } + } + + } + + protected boolean a(IWorldReader iworldreader, BlockPosition blockposition) { + if (!iworldreader.isEmpty(blockposition.up())) { + return false; + } else { + Block block = iworldreader.getType(blockposition).getBlock(); + + return block == Blocks.SAND; + } + } + } + + static class a extends PathfinderGoalBreed { + + private final EntityTurtle d; + + a(EntityTurtle entityturtle, double d0) { + super(entityturtle, d0); + this.d = entityturtle; + } + + public boolean a() { + return super.a() && !this.d.dy(); + } + + protected void g() { + EntityPlayer entityplayer = this.animal.getBreedCause(); + + if (entityplayer == null && this.partner.getBreedCause() != null) { + entityplayer = this.partner.getBreedCause(); + } + + if (entityplayer != null) { + entityplayer.a(StatisticList.ANIMALS_BRED); + CriterionTriggers.o.a(entityplayer, this.animal, this.partner, (EntityAgeable) null); + } + + this.d.s(true); + this.animal.resetLove(); + this.partner.resetLove(); + Random random = this.animal.getRandom(); + + if (this.b.getGameRules().getBoolean("doMobLoot")) { + this.b.addEntity(new EntityExperienceOrb(this.b, this.animal.locX, this.animal.locY, this.animal.locZ, random.nextInt(7) + 1)); + } + + } + } + + static class i extends PathfinderGoal { + + private final EntityTurtle a; + private final double b; + private EntityHuman c; + private int d; + private final Set e; + + i(EntityTurtle entityturtle, double d0, Item item) { + this.a = entityturtle; + this.b = d0; + this.e = Sets.newHashSet(new Item[] { item}); + this.a(3); + } + + public boolean a() { + if (this.d > 0) { + --this.d; + return false; + } else { + this.c = this.a.world.findNearbyPlayer(this.a, 10.0D); + return this.c == null ? false : this.a(this.c.getItemInMainHand()) || this.a(this.c.getItemInOffHand()); + } + } + + private boolean a(ItemStack itemstack) { + return this.e.contains(itemstack.getItem()); + } + + public boolean b() { + return this.a(); + } + + public void d() { + this.c = null; + this.a.getNavigation().q(); + this.d = 100; + } + + public void e() { + this.a.getControllerLook().a(this.c, (float) (this.a.L() + 20), (float) this.a.K()); + if (this.a.h((Entity) this.c) < 6.25D) { + this.a.getNavigation().q(); + } else { + this.a.getNavigation().a((Entity) this.c, this.b); + } + + } + } + + static class b extends PathfinderGoal { + + private final EntityTurtle a; + private final double b; + private boolean c; + private int d; + + b(EntityTurtle entityturtle, double d0) { + this.a = entityturtle; + this.b = d0; + } + + public boolean a() { + return this.a.isBaby() ? false : (this.a.dy() ? true : (this.a.getRandom().nextInt(700) != 0 ? false : this.a.c(this.a.dA()) >= 4096.0D)) && new com.destroystokyo.paper.event.entity.TurtleGoHomeEvent((org.bukkit.entity.Turtle) this.a.getBukkitEntity()).callEvent(); // Paper; + } + + public void c() { + this.a.u(true); + this.c = false; + this.d = 0; + } + + public void d() { + this.a.u(false); + } + + public boolean b() { + return this.a.c(this.a.dA()) >= 49.0D && !this.c && this.d <= 600; + } + + public void e() { + BlockPosition blockposition = this.a.dA(); + boolean flag = this.a.c(blockposition) <= 256.0D; + + if (flag) { + ++this.d; + } + + if (this.a.getNavigation().p()) { + Vec3D vec3d = RandomPositionGenerator.a((EntityCreature) this.a, 16, 3, new Vec3D((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ()), 0.3141592741012573D); + + if (vec3d == null) { + vec3d = RandomPositionGenerator.a(this.a, 8, 7, new Vec3D((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ())); + } + + if (vec3d != null && !flag && this.a.world.getType(new BlockPosition(vec3d)).getBlock() != Blocks.WATER) { + vec3d = RandomPositionGenerator.a(this.a, 16, 5, new Vec3D((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ())); + } + + if (vec3d == null) { + this.c = true; + return; + } + + this.a.getNavigation().a(vec3d.x, vec3d.y, vec3d.z, this.b); + } + + } + } + + static class j extends PathfinderGoal { + + private final EntityTurtle a; + private final double b; + private boolean c; + + j(EntityTurtle entityturtle, double d0) { + this.a = entityturtle; + this.b = d0; + } + + public boolean a() { + return !this.a.dC() && !this.a.dy() && this.a.isInWater(); + } + + public void c() { + boolean flag = true; + boolean flag1 = true; + Random random = this.a.random; + int i = random.nextInt(1025) - 512; + int j = random.nextInt(9) - 4; + int k = random.nextInt(1025) - 512; + + if ((double) j + this.a.locY > (double) (this.a.world.getSeaLevel() - 1)) { + j = 0; + } + + BlockPosition blockposition = new BlockPosition((double) i + this.a.locX, (double) j + this.a.locY, (double) k + this.a.locZ); + + this.a.h(blockposition); + this.a.v(true); + this.c = false; + } + + public void e() { + if (this.a.getNavigation().p()) { + BlockPosition blockposition = this.a.dB(); + Vec3D vec3d = RandomPositionGenerator.a((EntityCreature) this.a, 16, 3, new Vec3D((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ()), 0.3141592741012573D); + + if (vec3d == null) { + vec3d = RandomPositionGenerator.a(this.a, 8, 7, new Vec3D((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ())); + } + + if (vec3d != null) { + int i = MathHelper.floor(vec3d.x); + int j = MathHelper.floor(vec3d.z); + boolean flag = true; + StructureBoundingBox structureboundingbox = new StructureBoundingBox(i - 34, 0, j - 34, i + 34, 0, j + 34); + + if (!this.a.world.a(structureboundingbox)) { + vec3d = null; + } + } + + if (vec3d == null) { + this.c = true; + return; + } + + this.a.getNavigation().a(vec3d.x, vec3d.y, vec3d.z, this.b); + } + + } + + public boolean b() { + return !this.a.getNavigation().p() && !this.c && !this.a.dC() && !this.a.isInLove() && !this.a.dy(); + } + + public void d() { + this.a.v(false); + super.d(); + } + } + + static class f extends PathfinderGoalPanic { + + f(EntityTurtle entityturtle, double d0) { + super(entityturtle, d0); + } + + public boolean a() { + if (this.a.getLastDamager() == null && !this.a.isBurning()) { + return false; + } else { + BlockPosition blockposition = this.a(this.a.world, this.a, 7, 4); + + if (blockposition != null) { + this.c = (double) blockposition.getX(); + this.d = (double) blockposition.getY(); + this.e = (double) blockposition.getZ(); + return true; + } else { + return this.g(); + } + } + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityTypes.java b/src/main/java/net/minecraft/server/EntityTypes.java new file mode 100644 index 000000000000..24ca3511948f --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityTypes.java @@ -0,0 +1,358 @@ +package net.minecraft.server; + +import com.mojang.datafixers.DataFixUtils; +import com.mojang.datafixers.types.Type; + +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class EntityTypes { + + private static final Logger aR = LogManager.getLogger(); + public static final EntityTypes AREA_EFFECT_CLOUD = a("area_effect_cloud", EntityTypes.a.a(EntityAreaEffectCloud.class, EntityAreaEffectCloud::new)); + public static final EntityTypes ARMOR_STAND = a("armor_stand", EntityTypes.a.a(EntityArmorStand.class, EntityArmorStand::new)); + public static final EntityTypes ARROW = a("arrow", EntityTypes.a.a(EntityTippedArrow.class, EntityTippedArrow::new)); + public static final EntityTypes BAT = a("bat", EntityTypes.a.a(EntityBat.class, EntityBat::new)); + public static final EntityTypes BLAZE = a("blaze", EntityTypes.a.a(EntityBlaze.class, EntityBlaze::new)); + public static final EntityTypes BOAT = a("boat", EntityTypes.a.a(EntityBoat.class, EntityBoat::new)); + public static final EntityTypes CAVE_SPIDER = a("cave_spider", EntityTypes.a.a(EntityCaveSpider.class, EntityCaveSpider::new)); + public static final EntityTypes CHICKEN = a("chicken", EntityTypes.a.a(EntityChicken.class, EntityChicken::new)); + public static final EntityTypes COD = a("cod", EntityTypes.a.a(EntityCod.class, EntityCod::new)); + public static final EntityTypes COW = a("cow", EntityTypes.a.a(EntityCow.class, EntityCow::new)); + public static final EntityTypes CREEPER = a("creeper", EntityTypes.a.a(EntityCreeper.class, EntityCreeper::new)); + public static final EntityTypes DONKEY = a("donkey", EntityTypes.a.a(EntityHorseDonkey.class, EntityHorseDonkey::new)); + public static final EntityTypes DOLPHIN = a("dolphin", EntityTypes.a.a(EntityDolphin.class, EntityDolphin::new)); + public static final EntityTypes DRAGON_FIREBALL = a("dragon_fireball", EntityTypes.a.a(EntityDragonFireball.class, EntityDragonFireball::new)); + public static final EntityTypes DROWNED = a("drowned", EntityTypes.a.a(EntityDrowned.class, EntityDrowned::new)); + public static final EntityTypes ELDER_GUARDIAN = a("elder_guardian", EntityTypes.a.a(EntityGuardianElder.class, EntityGuardianElder::new)); + public static final EntityTypes END_CRYSTAL = a("end_crystal", EntityTypes.a.a(EntityEnderCrystal.class, EntityEnderCrystal::new)); + public static final EntityTypes ENDER_DRAGON = a("ender_dragon", EntityTypes.a.a(EntityEnderDragon.class, EntityEnderDragon::new)); + public static final EntityTypes ENDERMAN = a("enderman", EntityTypes.a.a(EntityEnderman.class, EntityEnderman::new)); + public static final EntityTypes ENDERMITE = a("endermite", EntityTypes.a.a(EntityEndermite.class, EntityEndermite::new)); + public static final EntityTypes EVOKER_FANGS = a("evoker_fangs", EntityTypes.a.a(EntityEvokerFangs.class, EntityEvokerFangs::new)); + public static final EntityTypes EVOKER = a("evoker", EntityTypes.a.a(EntityEvoker.class, EntityEvoker::new)); + public static final EntityTypes EXPERIENCE_ORB = a("experience_orb", EntityTypes.a.a(EntityExperienceOrb.class, EntityExperienceOrb::new)); + public static final EntityTypes EYE_OF_ENDER = a("eye_of_ender", EntityTypes.a.a(EntityEnderSignal.class, EntityEnderSignal::new)); + public static final EntityTypes FALLING_BLOCK = a("falling_block", EntityTypes.a.a(EntityFallingBlock.class, EntityFallingBlock::new)); + public static final EntityTypes FIREWORK_ROCKET = a("firework_rocket", EntityTypes.a.a(EntityFireworks.class, EntityFireworks::new)); + public static final EntityTypes GHAST = a("ghast", EntityTypes.a.a(EntityGhast.class, EntityGhast::new)); + public static final EntityTypes GIANT = a("giant", EntityTypes.a.a(EntityGiantZombie.class, EntityGiantZombie::new)); + public static final EntityTypes GUARDIAN = a("guardian", EntityTypes.a.a(EntityGuardian.class, EntityGuardian::new)); + public static final EntityTypes HORSE = a("horse", EntityTypes.a.a(EntityHorse.class, EntityHorse::new)); + public static final EntityTypes HUSK = a("husk", EntityTypes.a.a(EntityZombieHusk.class, EntityZombieHusk::new)); + public static final EntityTypes ILLUSIONER = a("illusioner", EntityTypes.a.a(EntityIllagerIllusioner.class, EntityIllagerIllusioner::new)); + public static final EntityTypes ITEM = a("item", EntityTypes.a.a(EntityItem.class, EntityItem::new)); + public static final EntityTypes ITEM_FRAME = a("item_frame", EntityTypes.a.a(EntityItemFrame.class, EntityItemFrame::new)); + public static final EntityTypes FIREBALL = a("fireball", EntityTypes.a.a(EntityLargeFireball.class, EntityLargeFireball::new)); + public static final EntityTypes LEASH_KNOT = a("leash_knot", EntityTypes.a.a(EntityLeash.class, EntityLeash::new).b()); + public static final EntityTypes LLAMA = a("llama", EntityTypes.a.a(EntityLlama.class, EntityLlama::new)); + public static final EntityTypes LLAMA_SPIT = a("llama_spit", EntityTypes.a.a(EntityLlamaSpit.class, EntityLlamaSpit::new)); + public static final EntityTypes MAGMA_CUBE = a("magma_cube", EntityTypes.a.a(EntityMagmaCube.class, EntityMagmaCube::new)); + public static final EntityTypes MINECART = a("minecart", EntityTypes.a.a(EntityMinecartRideable.class, EntityMinecartRideable::new)); + public static final EntityTypes CHEST_MINECART = a("chest_minecart", EntityTypes.a.a(EntityMinecartChest.class, EntityMinecartChest::new)); + public static final EntityTypes COMMAND_BLOCK_MINECART = a("command_block_minecart", EntityTypes.a.a(EntityMinecartCommandBlock.class, EntityMinecartCommandBlock::new)); + public static final EntityTypes FURNACE_MINECART = a("furnace_minecart", EntityTypes.a.a(EntityMinecartFurnace.class, EntityMinecartFurnace::new)); + public static final EntityTypes HOPPER_MINECART = a("hopper_minecart", EntityTypes.a.a(EntityMinecartHopper.class, EntityMinecartHopper::new)); + public static final EntityTypes SPAWNER_MINECART = a("spawner_minecart", EntityTypes.a.a(EntityMinecartMobSpawner.class, EntityMinecartMobSpawner::new)); + public static final EntityTypes TNT_MINECART = a("tnt_minecart", EntityTypes.a.a(EntityMinecartTNT.class, EntityMinecartTNT::new)); + public static final EntityTypes MULE = a("mule", EntityTypes.a.a(EntityHorseMule.class, EntityHorseMule::new)); + public static final EntityTypes MOOSHROOM = a("mooshroom", EntityTypes.a.a(EntityMushroomCow.class, EntityMushroomCow::new)); + public static final EntityTypes OCELOT = a("ocelot", EntityTypes.a.a(EntityOcelot.class, EntityOcelot::new)); + public static final EntityTypes PAINTING = a("painting", EntityTypes.a.a(EntityPainting.class, EntityPainting::new)); + public static final EntityTypes PARROT = a("parrot", EntityTypes.a.a(EntityParrot.class, EntityParrot::new)); + public static final EntityTypes PIG = a("pig", EntityTypes.a.a(EntityPig.class, EntityPig::new)); + public static final EntityTypes PUFFERFISH = a("pufferfish", EntityTypes.a.a(EntityPufferFish.class, EntityPufferFish::new)); + public static final EntityTypes ZOMBIE_PIGMAN = a("zombie_pigman", EntityTypes.a.a(EntityPigZombie.class, EntityPigZombie::new)); + public static final EntityTypes POLAR_BEAR = a("polar_bear", EntityTypes.a.a(EntityPolarBear.class, EntityPolarBear::new)); + public static final EntityTypes TNT = a("tnt", EntityTypes.a.a(EntityTNTPrimed.class, EntityTNTPrimed::new)); + public static final EntityTypes RABBIT = a("rabbit", EntityTypes.a.a(EntityRabbit.class, EntityRabbit::new)); + public static final EntityTypes SALMON = a("salmon", EntityTypes.a.a(EntitySalmon.class, EntitySalmon::new)); + public static final EntityTypes SHEEP = a("sheep", EntityTypes.a.a(EntitySheep.class, EntitySheep::new)); + public static final EntityTypes SHULKER = a("shulker", EntityTypes.a.a(EntityShulker.class, EntityShulker::new)); + public static final EntityTypes SHULKER_BULLET = a("shulker_bullet", EntityTypes.a.a(EntityShulkerBullet.class, EntityShulkerBullet::new)); + public static final EntityTypes SILVERFISH = a("silverfish", EntityTypes.a.a(EntitySilverfish.class, EntitySilverfish::new)); + public static final EntityTypes SKELETON = a("skeleton", EntityTypes.a.a(EntitySkeleton.class, EntitySkeleton::new)); + public static final EntityTypes SKELETON_HORSE = a("skeleton_horse", EntityTypes.a.a(EntityHorseSkeleton.class, EntityHorseSkeleton::new)); + public static final EntityTypes SLIME = a("slime", EntityTypes.a.a(EntitySlime.class, EntitySlime::new)); + public static final EntityTypes SMALL_FIREBALL = a("small_fireball", EntityTypes.a.a(EntitySmallFireball.class, EntitySmallFireball::new)); + public static final EntityTypes SNOW_GOLEM = a("snow_golem", EntityTypes.a.a(EntitySnowman.class, EntitySnowman::new)); + public static final EntityTypes SNOWBALL = a("snowball", EntityTypes.a.a(EntitySnowball.class, EntitySnowball::new)); + public static final EntityTypes SPECTRAL_ARROW = a("spectral_arrow", EntityTypes.a.a(EntitySpectralArrow.class, EntitySpectralArrow::new)); + public static final EntityTypes SPIDER = a("spider", EntityTypes.a.a(EntitySpider.class, EntitySpider::new)); + public static final EntityTypes SQUID = a("squid", EntityTypes.a.a(EntitySquid.class, EntitySquid::new)); + public static final EntityTypes STRAY = a("stray", EntityTypes.a.a(EntitySkeletonStray.class, EntitySkeletonStray::new)); + public static final EntityTypes TROPICAL_FISH = a("tropical_fish", EntityTypes.a.a(EntityTropicalFish.class, EntityTropicalFish::new)); + public static final EntityTypes TURTLE = a("turtle", EntityTypes.a.a(EntityTurtle.class, EntityTurtle::new)); + public static final EntityTypes EGG = a("egg", EntityTypes.a.a(EntityEgg.class, EntityEgg::new)); + public static final EntityTypes ENDER_PEARL = a("ender_pearl", EntityTypes.a.a(EntityEnderPearl.class, EntityEnderPearl::new)); + public static final EntityTypes EXPERIENCE_BOTTLE = a("experience_bottle", EntityTypes.a.a(EntityThrownExpBottle.class, EntityThrownExpBottle::new)); + public static final EntityTypes POTION = a("potion", EntityTypes.a.a(EntityPotion.class, EntityPotion::new)); + public static final EntityTypes VEX = a("vex", EntityTypes.a.a(EntityVex.class, EntityVex::new)); + public static final EntityTypes VILLAGER = a("villager", EntityTypes.a.a(EntityVillager.class, EntityVillager::new)); + public static final EntityTypes IRON_GOLEM = a("iron_golem", EntityTypes.a.a(EntityIronGolem.class, EntityIronGolem::new)); + public static final EntityTypes VINDICATOR = a("vindicator", EntityTypes.a.a(EntityVindicator.class, EntityVindicator::new)); + public static final EntityTypes WITCH = a("witch", EntityTypes.a.a(EntityWitch.class, EntityWitch::new)); + public static final EntityTypes WITHER = a("wither", EntityTypes.a.a(EntityWither.class, EntityWither::new)); + public static final EntityTypes WITHER_SKELETON = a("wither_skeleton", EntityTypes.a.a(EntitySkeletonWither.class, EntitySkeletonWither::new)); + public static final EntityTypes WITHER_SKULL = a("wither_skull", EntityTypes.a.a(EntityWitherSkull.class, EntityWitherSkull::new)); + public static final EntityTypes WOLF = a("wolf", EntityTypes.a.a(EntityWolf.class, EntityWolf::new)); + public static final EntityTypes ZOMBIE = a("zombie", EntityTypes.a.a(EntityZombie.class, EntityZombie::new)); + public static final EntityTypes ZOMBIE_HORSE = a("zombie_horse", EntityTypes.a.a(EntityHorseZombie.class, EntityHorseZombie::new)); + public static final EntityTypes ZOMBIE_VILLAGER = a("zombie_villager", EntityTypes.a.a(EntityZombieVillager.class, EntityZombieVillager::new)); + public static final EntityTypes PHANTOM = a("phantom", EntityTypes.a.a(EntityPhantom.class, EntityPhantom::new)); + public static final EntityTypes LIGHTNING_BOLT = a("lightning_bolt", EntityTypes.a.a(EntityLightning.class).b()); + public static final EntityTypes PLAYER = a("player", EntityTypes.a.a(EntityHuman.class).b().a()); + public static final EntityTypes FISHING_BOBBER = a("fishing_bobber", EntityTypes.a.a(EntityFishingHook.class).b().a()); + public static final EntityTypes TRIDENT = a("trident", EntityTypes.a.a(EntityThrownTrident.class, EntityThrownTrident::new)); + private final Class aS; + private final Function aT; + private final boolean aU; + private final boolean aV; + @Nullable + private String aW; + @Nullable + private IChatBaseComponent aX; + @Nullable + private final Type aY; + + public static EntityTypes a(String s, EntityTypes.a entitytypes_a) { + EntityTypes entitytypes = entitytypes_a.a(s); + + // Paper start + if (clsToKeyMap == null ) clsToKeyMap = new java.util.HashMap<>(); + if (clsToTypeMap == null ) clsToTypeMap = new java.util.HashMap<>(); + + MinecraftKey key = new MinecraftKey(s); + Class entityClass = entitytypes_a.getEntityClass(); + IRegistry.ENTITY_TYPE.a(new MinecraftKey(s), entitytypes); // CraftBukkit - decompile error + clsToKeyMap.put(entityClass, key); + clsToTypeMap.put(entityClass, org.bukkit.entity.EntityType.fromName(s)); + return entitytypes; + } + public static Map, MinecraftKey> clsToKeyMap; + public static Map, org.bukkit.entity.EntityType> clsToTypeMap; + // Paper end + + @Nullable + public static MinecraftKey getName(EntityTypes entitytypes) { + return IRegistry.ENTITY_TYPE.getKey(entitytypes); + } + + @Nullable + public static EntityTypes a(String s) { + return (EntityTypes) IRegistry.ENTITY_TYPE.get(MinecraftKey.a(s)); + } + + public EntityTypes(Class oclass, Function function, boolean flag, boolean flag1, @Nullable Type type) { + this.aS = oclass; + this.aT = function; + this.aU = flag; + this.aV = flag1; + this.aY = type; + } + + @Nullable + public Entity a(World world, @Nullable ItemStack itemstack, @Nullable EntityHuman entityhuman, BlockPosition blockposition, boolean flag, boolean flag1) { + return this.a(world, itemstack == null ? null : itemstack.getTag(), itemstack != null && itemstack.hasName() ? itemstack.getName() : null, entityhuman, blockposition, flag, flag1); + } + + @Nullable + public T a(World world, @Nullable NBTTagCompound nbttagcompound, @Nullable IChatBaseComponent ichatbasecomponent, @Nullable EntityHuman entityhuman, BlockPosition blockposition, boolean flag, boolean flag1) { + // CraftBukkit start + return spawnCreature(world, nbttagcompound, ichatbasecomponent, entityhuman, blockposition, flag, flag1, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG); + } + + @Nullable + public T spawnCreature(World world, @Nullable NBTTagCompound nbttagcompound, @Nullable IChatBaseComponent ichatbasecomponent, @Nullable EntityHuman entityhuman, BlockPosition blockposition, boolean flag, boolean flag1, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) { + T t0 = this.b(world, nbttagcompound, ichatbasecomponent, entityhuman, blockposition, flag, flag1); + + return world.addEntity(t0, spawnReason) ? t0 : null; // Don't return an entity when CreatureSpawnEvent is canceled + // CraftBukkit end + } + + @Nullable + public T b(World world, @Nullable NBTTagCompound nbttagcompound, @Nullable IChatBaseComponent ichatbasecomponent, @Nullable EntityHuman entityhuman, BlockPosition blockposition, boolean flag, boolean flag1) { + T t0 = this.a(world); + + if (t0 == null) { + return null; + } else { + double d0; + + if (flag) { + t0.setPosition((double) blockposition.getX() + 0.5D, (double) (blockposition.getY() + 1), (double) blockposition.getZ() + 0.5D); + d0 = a(world, blockposition, flag1, t0.getBoundingBox()); + } else { + d0 = 0.0D; + } + + t0.setPositionRotation((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + d0, (double) blockposition.getZ() + 0.5D, MathHelper.g(world.random.nextFloat() * 360.0F), 0.0F); + if (t0 instanceof EntityInsentient) { + EntityInsentient entityinsentient = (EntityInsentient) t0; + + entityinsentient.aS = entityinsentient.yaw; + entityinsentient.aQ = entityinsentient.yaw; + entityinsentient.prepare(world.getDamageScaler(new BlockPosition(entityinsentient)), (GroupDataEntity) null, nbttagcompound); + entityinsentient.A(); + } + + if (ichatbasecomponent != null && t0 instanceof EntityLiving) { + t0.setCustomName(ichatbasecomponent); + } + + a(world, entityhuman, t0, nbttagcompound); + return t0; + } + } + + protected static double a(IWorldReader iworldreader, BlockPosition blockposition, boolean flag, AxisAlignedBB axisalignedbb) { + AxisAlignedBB axisalignedbb1 = new AxisAlignedBB(blockposition); + + if (flag) { + axisalignedbb1 = axisalignedbb1.b(0.0D, -1.0D, 0.0D); + } + + Stream stream = iworldreader.b((Entity) null, axisalignedbb1); + + return 1.0D + VoxelShapes.a(EnumDirection.EnumAxis.Y, axisalignedbb, stream, flag ? -2.0D : -1.0D); + } + + public static void a(World world, @Nullable EntityHuman entityhuman, @Nullable Entity entity, @Nullable NBTTagCompound nbttagcompound) { + if (nbttagcompound != null && nbttagcompound.hasKeyOfType("EntityTag", 10)) { + MinecraftServer minecraftserver = world.getMinecraftServer(); + + if (minecraftserver != null && entity != null) { + if (world.isClientSide || !entity.bM() || entityhuman != null && minecraftserver.getPlayerList().isOp(entityhuman.getProfile())) { + NBTTagCompound nbttagcompound1 = entity.save(new NBTTagCompound()); + UUID uuid = entity.getUniqueID(); + + nbttagcompound1.a(nbttagcompound.getCompound("EntityTag")); + entity.a(uuid); + entity.f(nbttagcompound1); + } + } + } + } + + public boolean isPersistable() { return a(); } // Paper - OBFHELPER + public boolean a() { + return this.aU; + } + + public boolean b() { + return this.aV; + } + + public Class c() { + return this.aS; + } + + public String d() { + if (this.aW == null) { + this.aW = SystemUtils.a("entity", IRegistry.ENTITY_TYPE.getKey(this)); + } + + return this.aW; + } + + public IChatBaseComponent e() { + if (this.aX == null) { + this.aX = new ChatMessage(this.d(), new Object[0]); + } + + return this.aX; + } + + @Nullable public T create(World world) { return a(world); } // Paper - OBFHELPER + @Nullable + public T a(World world) { + return this.aT.apply(world); // CraftBukkit - decompile error + } + + @Nullable + public static Entity a(World world, MinecraftKey minecraftkey) { + return a(world, (EntityTypes) IRegistry.ENTITY_TYPE.get(minecraftkey)); + } + + @Nullable + public static Entity a(NBTTagCompound nbttagcompound, World world) { + MinecraftKey minecraftkey = new MinecraftKey(nbttagcompound.getString("id")); + Entity entity = a(world, minecraftkey); + + if (entity == null) { + EntityTypes.aR.warn("Skipping Entity with id {}", minecraftkey); + } else { + entity.f(nbttagcompound); + } + + return entity; + } + + @Nullable + private static Entity a(World world, @Nullable EntityTypes entitytypes) { + return entitytypes == null ? null : entitytypes.a(world); + } + + public static class a { + + private final Class a; public Class getEntityClass() { return a; } // Paper - OBFHELPER + private final Function b; + private boolean c = true; + private boolean d = true; + + private a(Class oclass, Function function) { + this.a = oclass; + this.b = function; + } + + public static EntityTypes.a a(Class oclass, Function function) { + return new EntityTypes.a<>(oclass, function); + } + + public static EntityTypes.a a(Class oclass) { + return new EntityTypes.a<>(oclass, (world) -> { + return null; + }); + } + + public EntityTypes.a a() { + this.d = false; + return this; + } + + public EntityTypes.a b() { + this.c = false; + return this; + } + + public EntityTypes a(String s) { + Type type = null; + + if (this.c) { + try { + type = DataConverterRegistry.a().getSchema(DataFixUtils.makeKey(1631)).getChoiceType(DataConverterTypes.n, s); + } catch (IllegalStateException illegalstateexception) { + if (SharedConstants.b) { + throw illegalstateexception; + } + + EntityTypes.aR.warn("No data fixer registered for entity {}", s); + } + } + + return new EntityTypes<>(this.a, this.b, this.c, this.d, type); + } + } + + // Paper start + public static Set getEntityNameList() { + return IRegistry.ENTITY_TYPE.keySet(); + } + // Paper end +} diff --git a/src/main/java/net/minecraft/server/EntityVex.java b/src/main/java/net/minecraft/server/EntityVex.java new file mode 100644 index 000000000000..589b13f4ee80 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityVex.java @@ -0,0 +1,309 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; +import org.bukkit.event.entity.EntityTargetEvent; + +public class EntityVex extends EntityMonster { + + protected static final DataWatcherObject a = DataWatcher.a(EntityVex.class, DataWatcherRegistry.a); + private EntityInsentient b; + @Nullable + private BlockPosition c; + private boolean bC; + private int bD; + + public EntityVex(World world) { + super(EntityTypes.VEX, world); + this.fireProof = true; + this.moveController = new EntityVex.c(this); + this.setSize(0.4F, 0.8F); + this.b_ = 3; + } + + public void move(EnumMoveType enummovetype, double d0, double d1, double d2) { + super.move(enummovetype, d0, d1, d2); + this.checkBlockCollisions(); + } + + public void tick() { + this.noclip = true; + super.tick(); + this.noclip = false; + this.setNoGravity(true); + if (this.bC && --this.bD <= 0) { + this.bD = 20; + this.damageEntity(DamageSource.STARVE, 1.0F); + } + + } + + protected void n() { + super.n(); + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(4, new EntityVex.a()); + this.goalSelector.a(8, new EntityVex.d()); + this.goalSelector.a(9, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 3.0F, 1.0F)); + this.goalSelector.a(10, new PathfinderGoalLookAtPlayer(this, EntityInsentient.class, 8.0F)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, true, new Class[] { EntityVex.class})); + this.targetSelector.a(2, new EntityVex.b(this)); + this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget<>(this, EntityHuman.class, true)); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(14.0D); + this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue(4.0D); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityVex.a, (byte) 0); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKey("BoundX")) { + this.c = new BlockPosition(nbttagcompound.getInt("BoundX"), nbttagcompound.getInt("BoundY"), nbttagcompound.getInt("BoundZ")); + } + + if (nbttagcompound.hasKey("LifeTicks")) { + this.a(nbttagcompound.getInt("LifeTicks")); + } + + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + if (this.c != null) { + nbttagcompound.setInt("BoundX", this.c.getX()); + nbttagcompound.setInt("BoundY", this.c.getY()); + nbttagcompound.setInt("BoundZ", this.c.getZ()); + } + + if (this.bC) { + nbttagcompound.setInt("LifeTicks", this.bD); + } + + } + + public EntityInsentient getOwner() { return l(); } // Paper - OBFHELPER + public EntityInsentient l() { + return this.b; + } + + @Nullable + public BlockPosition dz() { + return this.c; + } + + public void g(@Nullable BlockPosition blockposition) { + this.c = blockposition; + } + + private boolean b(int i) { + byte b0 = (Byte) this.datawatcher.get(EntityVex.a); + + return (b0 & i) != 0; + } + + private void a(int i, boolean flag) { + byte b0 = (Byte) this.datawatcher.get(EntityVex.a); + int j; + + if (flag) { + j = b0 | i; + } else { + j = b0 & ~i; + } + + this.datawatcher.set(EntityVex.a, (byte) (j & 255)); + } + + public boolean dA() { + return this.b(1); + } + + public void a(boolean flag) { + this.a(1, flag); + } + + public void setOwner(EntityInsentient entityinsentient) { a(entityinsentient); } // Paper - OBFHELPER + public void a(EntityInsentient entityinsentient) { + this.b = entityinsentient; + } + + public void a(int i) { + this.bC = true; + this.bD = i; + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_VEX_AMBIENT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_VEX_DEATH; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_VEX_HURT; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.aG; + } + + public float az() { + return 1.0F; + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + this.a(difficultydamagescaler); + this.b(difficultydamagescaler); + return super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + } + + protected void a(DifficultyDamageScaler difficultydamagescaler) { + this.setSlot(EnumItemSlot.MAINHAND, new ItemStack(Items.IRON_SWORD)); + this.a(EnumItemSlot.MAINHAND, 0.0F); + } + + class b extends PathfinderGoalTarget { + + public b(EntityCreature entitycreature) { + super(entitycreature, false); + } + + public boolean a() { + return EntityVex.this.b != null && EntityVex.this.b.getGoalTarget() != null && this.a(EntityVex.this.b.getGoalTarget(), false); + } + + public void c() { + EntityVex.this.setGoalTarget(EntityVex.this.b.getGoalTarget(), EntityTargetEvent.TargetReason.OWNER_ATTACKED_TARGET, true); // CraftBukkit + super.c(); + } + } + + class d extends PathfinderGoal { + + public d() { + this.a(1); + } + + public boolean a() { + return !EntityVex.this.getControllerMove().b() && EntityVex.this.random.nextInt(7) == 0; + } + + public boolean b() { + return false; + } + + public void e() { + BlockPosition blockposition = EntityVex.this.dz(); + + if (blockposition == null) { + blockposition = new BlockPosition(EntityVex.this); + } + + for (int i = 0; i < 3; ++i) { + BlockPosition blockposition1 = blockposition.a(EntityVex.this.random.nextInt(15) - 7, EntityVex.this.random.nextInt(11) - 5, EntityVex.this.random.nextInt(15) - 7); + + if (EntityVex.this.world.isEmpty(blockposition1)) { + EntityVex.this.moveController.a((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.5D, (double) blockposition1.getZ() + 0.5D, 0.25D); + if (EntityVex.this.getGoalTarget() == null) { + EntityVex.this.getControllerLook().a((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.5D, (double) blockposition1.getZ() + 0.5D, 180.0F, 20.0F); + } + break; + } + } + + } + } + + class a extends PathfinderGoal { + + public a() { + this.a(1); + } + + public boolean a() { + return EntityVex.this.getGoalTarget() != null && !EntityVex.this.getControllerMove().b() && EntityVex.this.random.nextInt(7) == 0 ? EntityVex.this.h((Entity) EntityVex.this.getGoalTarget()) > 4.0D : false; + } + + public boolean b() { + return EntityVex.this.getControllerMove().b() && EntityVex.this.dA() && EntityVex.this.getGoalTarget() != null && EntityVex.this.getGoalTarget().isAlive(); + } + + public void c() { + EntityLiving entityliving = EntityVex.this.getGoalTarget(); + Vec3D vec3d = entityliving.i(1.0F); + + EntityVex.this.moveController.a(vec3d.x, vec3d.y, vec3d.z, 1.0D); + EntityVex.this.a(true); + EntityVex.this.a(SoundEffects.ENTITY_VEX_CHARGE, 1.0F, 1.0F); + } + + public void d() { + EntityVex.this.a(false); + } + + public void e() { + EntityLiving entityliving = EntityVex.this.getGoalTarget(); + + if (EntityVex.this.getBoundingBox().c(entityliving.getBoundingBox())) { + EntityVex.this.B(entityliving); + EntityVex.this.a(false); + } else { + double d0 = EntityVex.this.h((Entity) entityliving); + + if (d0 < 9.0D) { + Vec3D vec3d = entityliving.i(1.0F); + + EntityVex.this.moveController.a(vec3d.x, vec3d.y, vec3d.z, 1.0D); + } + } + + } + } + + class c extends ControllerMove { + + public c(EntityVex entityvex) { + super(entityvex); + } + + public void a() { + if (this.h == ControllerMove.Operation.MOVE_TO) { + double d0 = this.b - EntityVex.this.locX; + double d1 = this.c - EntityVex.this.locY; + double d2 = this.d - EntityVex.this.locZ; + double d3 = d0 * d0 + d1 * d1 + d2 * d2; + + d3 = (double) MathHelper.sqrt(d3); + if (d3 < EntityVex.this.getBoundingBox().a()) { + this.h = ControllerMove.Operation.WAIT; + EntityVex.this.motX *= 0.5D; + EntityVex.this.motY *= 0.5D; + EntityVex.this.motZ *= 0.5D; + } else { + EntityVex.this.motX += d0 / d3 * 0.05D * this.e; + EntityVex.this.motY += d1 / d3 * 0.05D * this.e; + EntityVex.this.motZ += d2 / d3 * 0.05D * this.e; + if (EntityVex.this.getGoalTarget() == null) { + EntityVex.this.yaw = -((float) MathHelper.c(EntityVex.this.motX, EntityVex.this.motZ)) * 57.295776F; + EntityVex.this.aQ = EntityVex.this.yaw; + } else { + double d4 = EntityVex.this.getGoalTarget().locX - EntityVex.this.locX; + double d5 = EntityVex.this.getGoalTarget().locZ - EntityVex.this.locZ; + + EntityVex.this.yaw = -((float) MathHelper.c(d4, d5)) * 57.295776F; + EntityVex.this.aQ = EntityVex.this.yaw; + } + } + + } + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityVillager.java b/src/main/java/net/minecraft/server/EntityVillager.java new file mode 100644 index 000000000000..40b3ffd8ca54 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityVillager.java @@ -0,0 +1,893 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.Locale; +import java.util.Random; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +// CraftBukkit start +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.entity.CraftVillager; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.inventory.CraftMerchantRecipe; +import org.bukkit.entity.Villager; +import org.bukkit.event.entity.EntityTransformEvent; +import org.bukkit.event.entity.VillagerAcquireTradeEvent; +import org.bukkit.event.entity.VillagerReplenishTradeEvent; +// CraftBukkit end + +public class EntityVillager extends EntityAgeable implements NPC, IMerchant { + + private static final Logger bC = LogManager.getLogger(); + private static final DataWatcherObject bD = DataWatcher.a(EntityVillager.class, DataWatcherRegistry.b); + private int profession; + private boolean bF; + private boolean bG; + private Village village; + @Nullable + private EntityHuman tradingPlayer; + @Nullable + public MerchantRecipeList trades; + private int bK; + private boolean bL; + private boolean bM; + public int riches; + private String bO; + public int careerId; + public int careerLevel; + private boolean bR; + private boolean bS; + public final InventorySubcontainer inventory; + private static final EntityVillager.IMerchantRecipeOption[][][][] bU = new EntityVillager.IMerchantRecipeOption[][][][] { { { { new EntityVillager.MerchantRecipeOptionBuy(Items.WHEAT, new EntityVillager.MerchantOptionRandomRange(18, 22)), new EntityVillager.MerchantRecipeOptionBuy(Items.POTATO, new EntityVillager.MerchantOptionRandomRange(15, 19)), new EntityVillager.MerchantRecipeOptionBuy(Items.CARROT, new EntityVillager.MerchantOptionRandomRange(15, 19)), new EntityVillager.MerchantRecipeOptionSell(Items.BREAD, new EntityVillager.MerchantOptionRandomRange(-4, -2))}, { new EntityVillager.MerchantRecipeOptionBuy(Blocks.PUMPKIN, new EntityVillager.MerchantOptionRandomRange(8, 13)), new EntityVillager.MerchantRecipeOptionSell(Items.PUMPKIN_PIE, new EntityVillager.MerchantOptionRandomRange(-3, -2))}, { new EntityVillager.MerchantRecipeOptionBuy(Blocks.MELON, new EntityVillager.MerchantOptionRandomRange(7, 12)), new EntityVillager.MerchantRecipeOptionSell(Items.APPLE, new EntityVillager.MerchantOptionRandomRange(-7, -5))}, { new EntityVillager.MerchantRecipeOptionSell(Items.COOKIE, new EntityVillager.MerchantOptionRandomRange(-10, -6)), new EntityVillager.MerchantRecipeOptionSell(Blocks.CAKE, new EntityVillager.MerchantOptionRandomRange(1, 1))}}, { { new EntityVillager.MerchantRecipeOptionBuy(Items.STRING, new EntityVillager.MerchantOptionRandomRange(15, 20)), new EntityVillager.MerchantRecipeOptionBuy(Items.COAL, new EntityVillager.MerchantOptionRandomRange(16, 24)), new EntityVillager.MerchantRecipeOptionProcess(Items.COD, new EntityVillager.MerchantOptionRandomRange(6, 6), Items.COOKED_COD, new EntityVillager.MerchantOptionRandomRange(6, 6)), new EntityVillager.MerchantRecipeOptionProcess(Items.SALMON, new EntityVillager.MerchantOptionRandomRange(6, 6), Items.COOKED_SALMON, new EntityVillager.MerchantOptionRandomRange(6, 6))}, { new EntityVillager.MerchantRecipeOptionEnchant(Items.FISHING_ROD, new EntityVillager.MerchantOptionRandomRange(7, 8))}}, { { new EntityVillager.MerchantRecipeOptionBuy(Blocks.WHITE_WOOL, new EntityVillager.MerchantOptionRandomRange(16, 22)), new EntityVillager.MerchantRecipeOptionSell(Items.SHEARS, new EntityVillager.MerchantOptionRandomRange(3, 4))}, { new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.WHITE_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.ORANGE_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.MAGENTA_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.LIGHT_BLUE_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.YELLOW_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.LIME_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.PINK_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.GRAY_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.LIGHT_GRAY_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.CYAN_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.PURPLE_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.BLUE_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.BROWN_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.GREEN_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.RED_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Blocks.BLACK_WOOL), new EntityVillager.MerchantOptionRandomRange(1, 2))}}, { { new EntityVillager.MerchantRecipeOptionBuy(Items.STRING, new EntityVillager.MerchantOptionRandomRange(15, 20)), new EntityVillager.MerchantRecipeOptionSell(Items.ARROW, new EntityVillager.MerchantOptionRandomRange(-12, -8))}, { new EntityVillager.MerchantRecipeOptionSell(Items.BOW, new EntityVillager.MerchantOptionRandomRange(2, 3)), new EntityVillager.MerchantRecipeOptionProcess(Blocks.GRAVEL, new EntityVillager.MerchantOptionRandomRange(10, 10), Items.FLINT, new EntityVillager.MerchantOptionRandomRange(6, 10))}}}, { { { new EntityVillager.MerchantRecipeOptionBuy(Items.PAPER, new EntityVillager.MerchantOptionRandomRange(24, 36)), new EntityVillager.MerchantRecipeOptionBook()}, { new EntityVillager.MerchantRecipeOptionBuy(Items.BOOK, new EntityVillager.MerchantOptionRandomRange(8, 10)), new EntityVillager.MerchantRecipeOptionSell(Items.COMPASS, new EntityVillager.MerchantOptionRandomRange(10, 12)), new EntityVillager.MerchantRecipeOptionSell(Blocks.BOOKSHELF, new EntityVillager.MerchantOptionRandomRange(3, 4))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.WRITTEN_BOOK, new EntityVillager.MerchantOptionRandomRange(2, 2)), new EntityVillager.MerchantRecipeOptionSell(Items.CLOCK, new EntityVillager.MerchantOptionRandomRange(10, 12)), new EntityVillager.MerchantRecipeOptionSell(Blocks.GLASS, new EntityVillager.MerchantOptionRandomRange(-5, -3))}, { new EntityVillager.MerchantRecipeOptionBook()}, { new EntityVillager.MerchantRecipeOptionBook()}, { new EntityVillager.MerchantRecipeOptionSell(Items.NAME_TAG, new EntityVillager.MerchantOptionRandomRange(20, 22))}}, { { new EntityVillager.MerchantRecipeOptionBuy(Items.PAPER, new EntityVillager.MerchantOptionRandomRange(24, 36))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.COMPASS, new EntityVillager.MerchantOptionRandomRange(1, 1))}, { new EntityVillager.MerchantRecipeOptionSell(Items.MAP, new EntityVillager.MerchantOptionRandomRange(7, 11))}, { new EntityVillager.h(new EntityVillager.MerchantOptionRandomRange(12, 20), "Monument", MapIcon.Type.MONUMENT), new EntityVillager.h(new EntityVillager.MerchantOptionRandomRange(16, 28), "Mansion", MapIcon.Type.MANSION)}}}, { { { new EntityVillager.MerchantRecipeOptionBuy(Items.ROTTEN_FLESH, new EntityVillager.MerchantOptionRandomRange(36, 40)), new EntityVillager.MerchantRecipeOptionBuy(Items.GOLD_INGOT, new EntityVillager.MerchantOptionRandomRange(8, 10))}, { new EntityVillager.MerchantRecipeOptionSell(Items.REDSTONE, new EntityVillager.MerchantOptionRandomRange(-4, -1)), new EntityVillager.MerchantRecipeOptionSell(new ItemStack(Items.LAPIS_LAZULI), new EntityVillager.MerchantOptionRandomRange(-2, -1))}, { new EntityVillager.MerchantRecipeOptionSell(Items.ENDER_PEARL, new EntityVillager.MerchantOptionRandomRange(4, 7)), new EntityVillager.MerchantRecipeOptionSell(Blocks.GLOWSTONE, new EntityVillager.MerchantOptionRandomRange(-3, -1))}, { new EntityVillager.MerchantRecipeOptionSell(Items.EXPERIENCE_BOTTLE, new EntityVillager.MerchantOptionRandomRange(3, 11))}}}, { { { new EntityVillager.MerchantRecipeOptionBuy(Items.COAL, new EntityVillager.MerchantOptionRandomRange(16, 24)), new EntityVillager.MerchantRecipeOptionSell(Items.IRON_HELMET, new EntityVillager.MerchantOptionRandomRange(4, 6))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.IRON_INGOT, new EntityVillager.MerchantOptionRandomRange(7, 9)), new EntityVillager.MerchantRecipeOptionSell(Items.IRON_CHESTPLATE, new EntityVillager.MerchantOptionRandomRange(10, 14))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.DIAMOND, new EntityVillager.MerchantOptionRandomRange(3, 4)), new EntityVillager.MerchantRecipeOptionEnchant(Items.DIAMOND_CHESTPLATE, new EntityVillager.MerchantOptionRandomRange(16, 19))}, { new EntityVillager.MerchantRecipeOptionSell(Items.CHAINMAIL_BOOTS, new EntityVillager.MerchantOptionRandomRange(5, 7)), new EntityVillager.MerchantRecipeOptionSell(Items.CHAINMAIL_LEGGINGS, new EntityVillager.MerchantOptionRandomRange(9, 11)), new EntityVillager.MerchantRecipeOptionSell(Items.CHAINMAIL_HELMET, new EntityVillager.MerchantOptionRandomRange(5, 7)), new EntityVillager.MerchantRecipeOptionSell(Items.CHAINMAIL_CHESTPLATE, new EntityVillager.MerchantOptionRandomRange(11, 15))}}, { { new EntityVillager.MerchantRecipeOptionBuy(Items.COAL, new EntityVillager.MerchantOptionRandomRange(16, 24)), new EntityVillager.MerchantRecipeOptionSell(Items.IRON_AXE, new EntityVillager.MerchantOptionRandomRange(6, 8))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.IRON_INGOT, new EntityVillager.MerchantOptionRandomRange(7, 9)), new EntityVillager.MerchantRecipeOptionEnchant(Items.IRON_SWORD, new EntityVillager.MerchantOptionRandomRange(9, 10))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.DIAMOND, new EntityVillager.MerchantOptionRandomRange(3, 4)), new EntityVillager.MerchantRecipeOptionEnchant(Items.DIAMOND_SWORD, new EntityVillager.MerchantOptionRandomRange(12, 15)), new EntityVillager.MerchantRecipeOptionEnchant(Items.DIAMOND_AXE, new EntityVillager.MerchantOptionRandomRange(9, 12))}}, { { new EntityVillager.MerchantRecipeOptionBuy(Items.COAL, new EntityVillager.MerchantOptionRandomRange(16, 24)), new EntityVillager.MerchantRecipeOptionEnchant(Items.IRON_SHOVEL, new EntityVillager.MerchantOptionRandomRange(5, 7))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.IRON_INGOT, new EntityVillager.MerchantOptionRandomRange(7, 9)), new EntityVillager.MerchantRecipeOptionEnchant(Items.IRON_PICKAXE, new EntityVillager.MerchantOptionRandomRange(9, 11))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.DIAMOND, new EntityVillager.MerchantOptionRandomRange(3, 4)), new EntityVillager.MerchantRecipeOptionEnchant(Items.DIAMOND_PICKAXE, new EntityVillager.MerchantOptionRandomRange(12, 15))}}}, { { { new EntityVillager.MerchantRecipeOptionBuy(Items.PORKCHOP, new EntityVillager.MerchantOptionRandomRange(14, 18)), new EntityVillager.MerchantRecipeOptionBuy(Items.CHICKEN, new EntityVillager.MerchantOptionRandomRange(14, 18))}, { new EntityVillager.MerchantRecipeOptionBuy(Items.COAL, new EntityVillager.MerchantOptionRandomRange(16, 24)), new EntityVillager.MerchantRecipeOptionSell(Items.COOKED_PORKCHOP, new EntityVillager.MerchantOptionRandomRange(-7, -5)), new EntityVillager.MerchantRecipeOptionSell(Items.COOKED_CHICKEN, new EntityVillager.MerchantOptionRandomRange(-8, -6))}}, { { new EntityVillager.MerchantRecipeOptionBuy(Items.LEATHER, new EntityVillager.MerchantOptionRandomRange(9, 12)), new EntityVillager.MerchantRecipeOptionSell(Items.LEATHER_LEGGINGS, new EntityVillager.MerchantOptionRandomRange(2, 4))}, { new EntityVillager.MerchantRecipeOptionEnchant(Items.LEATHER_CHESTPLATE, new EntityVillager.MerchantOptionRandomRange(7, 12))}, { new EntityVillager.MerchantRecipeOptionSell(Items.SADDLE, new EntityVillager.MerchantOptionRandomRange(8, 10))}}}, { new EntityVillager.IMerchantRecipeOption[0][]}}; + + public EntityVillager(World world) { + this(world, 0); + } + + public EntityVillager(World world, int i) { + super(EntityTypes.VILLAGER, world); + this.inventory = new InventorySubcontainer(new ChatComponentText("Items"), 8, (CraftVillager) this.getBukkitEntity()); // CraftBukkit add argument + this.setProfession(i); + this.setSize(0.6F, 1.95F); + ((Navigation) this.getNavigation()).a(true); + this.p(true); + } + + protected void n() { + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(1, new PathfinderGoalAvoidTarget<>(this, EntityZombie.class, 8.0F, 0.6D, 0.6D)); + this.goalSelector.a(1, new PathfinderGoalAvoidTarget<>(this, EntityEvoker.class, 12.0F, 0.8D, 0.8D)); + this.goalSelector.a(1, new PathfinderGoalAvoidTarget<>(this, EntityVindicator.class, 8.0F, 0.8D, 0.8D)); + this.goalSelector.a(1, new PathfinderGoalAvoidTarget<>(this, EntityVex.class, 8.0F, 0.6D, 0.6D)); + this.goalSelector.a(1, new PathfinderGoalTradeWithPlayer(this)); + this.goalSelector.a(1, new PathfinderGoalLookAtTradingPlayer(this)); + this.goalSelector.a(2, new PathfinderGoalMoveIndoors(this)); + this.goalSelector.a(3, new PathfinderGoalRestrictOpenDoor(this)); + this.goalSelector.a(4, new PathfinderGoalOpenDoor(this, true)); + this.goalSelector.a(5, new PathfinderGoalMoveTowardsRestriction(this, 0.6D)); + this.goalSelector.a(6, new PathfinderGoalMakeLove(this)); + this.goalSelector.a(7, new PathfinderGoalTakeFlower(this)); + this.goalSelector.a(9, new PathfinderGoalInteract(this, EntityHuman.class, 3.0F, 1.0F)); + this.goalSelector.a(9, new PathfinderGoalInteractVillagers(this)); + this.goalSelector.a(9, new PathfinderGoalRandomStrollLand(this, 0.6D)); + this.goalSelector.a(10, new PathfinderGoalLookAtPlayer(this, EntityInsentient.class, 8.0F)); + } + + private void dJ() { + if (!this.bS) { + this.bS = true; + if (this.isBaby()) { + this.goalSelector.a(8, new PathfinderGoalPlay(this, 0.32D)); + } else if (this.getProfession() == 0) { + this.goalSelector.a(6, new PathfinderGoalVillagerFarm(this, 0.6D)); + } + + } + } + + protected void l() { + if (this.getProfession() == 0) { + this.goalSelector.a(8, new PathfinderGoalVillagerFarm(this, 0.6D)); + } + + super.l(); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.5D); + } + + // Spigot Start + @Override + public void inactiveTick() { + // SPIGOT-3874 + if (world.spigotConfig.tickInactiveVillagers) { + // SPIGOT-3894 + Chunk startingChunk = this.world.getChunkIfLoaded(MathHelper.floor(this.locX) >> 4, MathHelper.floor(this.locZ) >> 4); + if (!(startingChunk != null && startingChunk.areNeighborsLoaded(1))) { + return; + } + this.mobTick(); // SPIGOT-3846 + } + super.inactiveTick(); + } + // Spigot End + + protected void mobTick() { + if (--this.profession <= 0) { + BlockPosition blockposition = new BlockPosition(this); + + this.world.af().a(blockposition); + this.profession = 70 + this.random.nextInt(50); + this.village = this.world.af().getClosestVillage(blockposition, 32); + if (this.village == null) { + this.dv(); + } else { + BlockPosition blockposition1 = this.village.a(); + + this.a(blockposition1, this.village.b()); + if (this.bR) { + this.bR = false; + this.village.b(5); + } + } + } + + if (!this.dB() && this.bK > 0) { + --this.bK; + if (this.bK <= 0) { + if (this.bL) { + Iterator iterator = this.trades.iterator(); + + while (iterator.hasNext()) { + MerchantRecipe merchantrecipe = (MerchantRecipe) iterator.next(); + + if (merchantrecipe.h()) { + // CraftBukkit start + int bonus = this.random.nextInt(6) + this.random.nextInt(6) + 2; + VillagerReplenishTradeEvent event = new VillagerReplenishTradeEvent((Villager) this.getBukkitEntity(), merchantrecipe.asBukkit(), bonus); + Bukkit.getPluginManager().callEvent(event); + if (!event.isCancelled()) { + merchantrecipe.a(event.getBonus()); + } + // CraftBukkit end + } + } + + this.populateTrades(); + this.bL = false; + if (this.village != null && this.bO != null) { + this.world.broadcastEntityEffect(this, (byte) 14); + this.village.a(this.bO, 1); + } + } + + this.addEffect(new MobEffect(MobEffects.REGENERATION, 200, 0), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.VILLAGER_TRADE); // CraftBukkit + } + } + + super.mobTick(); + } + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + boolean flag = itemstack.getItem() == Items.NAME_TAG; + + if (flag) { + itemstack.a(entityhuman, (EntityLiving) this, enumhand); + return true; + } else if (itemstack.getItem() != Items.VILLAGER_SPAWN_EGG && this.isAlive() && !this.dB() && !this.isBaby()) { + if (this.trades == null) { + this.populateTrades(); + } + + if (enumhand == EnumHand.MAIN_HAND) { + entityhuman.a(StatisticList.TALKED_TO_VILLAGER); + } + + if (!this.world.isClientSide && !this.trades.isEmpty()) { + this.setTradingPlayer(entityhuman); + entityhuman.openTrade(this); + } else if (this.trades.isEmpty()) { + return super.a(entityhuman, enumhand); + } + + return true; + } else { + return super.a(entityhuman, enumhand); + } + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityVillager.bD, 0); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("Profession", this.getProfession()); + nbttagcompound.setInt("Riches", this.riches); + nbttagcompound.setInt("Career", this.careerId); + nbttagcompound.setInt("CareerLevel", this.careerLevel); + nbttagcompound.setBoolean("Willing", this.bM); + if (this.trades != null) { + nbttagcompound.set("Offers", this.trades.a()); + } + + NBTTagList nbttaglist = new NBTTagList(); + + for (int i = 0; i < this.inventory.getSize(); ++i) { + ItemStack itemstack = this.inventory.getItem(i); + + if (!itemstack.isEmpty()) { + nbttaglist.add((NBTBase) itemstack.save(new NBTTagCompound())); + } + } + + nbttagcompound.set("Inventory", nbttaglist); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setProfession(nbttagcompound.getInt("Profession")); + this.riches = nbttagcompound.getInt("Riches"); + this.careerId = nbttagcompound.getInt("Career"); + this.careerLevel = nbttagcompound.getInt("CareerLevel"); + this.bM = nbttagcompound.getBoolean("Willing"); + if (nbttagcompound.hasKeyOfType("Offers", 10)) { + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Offers"); + + this.trades = new MerchantRecipeList(nbttagcompound1); + } + + NBTTagList nbttaglist = nbttagcompound.getList("Inventory", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + ItemStack itemstack = ItemStack.a(nbttaglist.getCompound(i)); + + if (!itemstack.isEmpty()) { + this.inventory.a(itemstack); + } + } + + this.p(true); + this.dJ(); + } + + public boolean isTypeNotPersistent() { + return false; + } + + protected SoundEffect D() { + return this.dB() ? SoundEffects.ENTITY_VILLAGER_TRADE : SoundEffects.ENTITY_VILLAGER_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_VILLAGER_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_VILLAGER_DEATH; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.aA; + } + + public void setProfession(int i) { + this.datawatcher.set(EntityVillager.bD, i); + } + + public int getProfession() { + return Math.max((Integer) this.datawatcher.get(EntityVillager.bD) % 6, 0); + } + + public boolean isInLove() { + return this.bF; + } + + public void s(boolean flag) { + this.bF = flag; + } + + public void t(boolean flag) { + this.bG = flag; + } + + public boolean dA() { + return this.bG; + } + + public void setLastDamager(@Nullable EntityLiving entityliving) { + super.setLastDamager(entityliving); + if (this.village != null && entityliving != null) { + this.village.a(entityliving); + if (entityliving instanceof EntityHuman) { + byte b0 = -1; + + if (this.isBaby()) { + b0 = -3; + } + + this.village.a(((EntityHuman) entityliving).getProfile().getName(), b0); + if (this.isAlive()) { + this.world.broadcastEntityEffect(this, (byte) 13); + } + } + } + + } + + public void die(DamageSource damagesource) { + if (this.village != null) { + Entity entity = damagesource.getEntity(); + + if (entity != null) { + if (entity instanceof EntityHuman) { + this.village.a(((EntityHuman) entity).getProfile().getName(), -2); + } else if (entity instanceof IMonster) { + this.village.h(); + } + } else { + EntityHuman entityhuman = this.world.findNearbyPlayer(this, 16.0D); + + if (entityhuman != null) { + this.village.h(); + } + } + } + + super.die(damagesource); + } + + public void setTradingPlayer(@Nullable EntityHuman entityhuman) { + this.tradingPlayer = entityhuman; + } + + @Nullable + public EntityHuman getTrader() { + return this.tradingPlayer; + } + + public boolean dB() { + return this.tradingPlayer != null; + } + + public boolean u(boolean flag) { + if (!this.bM && flag && this.dE()) { + boolean flag1 = false; + + for (int i = 0; i < this.inventory.getSize(); ++i) { + ItemStack itemstack = this.inventory.getItem(i); + + if (!itemstack.isEmpty()) { + if (itemstack.getItem() == Items.BREAD && itemstack.getCount() >= 3) { + flag1 = true; + this.inventory.splitStack(i, 3); + } else if ((itemstack.getItem() == Items.POTATO || itemstack.getItem() == Items.CARROT) && itemstack.getCount() >= 12) { + flag1 = true; + this.inventory.splitStack(i, 12); + } + } + + if (flag1) { + this.world.broadcastEntityEffect(this, (byte) 18); + this.bM = true; + break; + } + } + } + + return this.bM; + } + + public void v(boolean flag) { + this.bM = flag; + } + + public void a(MerchantRecipe merchantrecipe) { + merchantrecipe.increaseUses(); + this.a_ = -this.z(); + this.a(SoundEffects.ENTITY_VILLAGER_YES, this.cD(), this.cE()); + int i = 3 + this.random.nextInt(4); + + if (merchantrecipe.e() == 1 || this.random.nextInt(5) == 0) { + this.bK = 40; + this.bL = true; + this.bM = true; + if (this.tradingPlayer != null) { + this.bO = this.tradingPlayer.getProfile().getName(); + } else { + this.bO = null; + } + + i += 5; + } + + if (merchantrecipe.getBuyItem1().getItem() == Items.EMERALD) { + this.riches += merchantrecipe.getBuyItem1().getCount(); + } + + if (merchantrecipe.j()) { + this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY + 0.5D, this.locZ, i, org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, tradingPlayer, this)); // Paper + } + + if (this.tradingPlayer instanceof EntityPlayer) { + CriterionTriggers.s.a((EntityPlayer) this.tradingPlayer, this, merchantrecipe.getBuyItem3()); + } + + } + + public void a(ItemStack itemstack) { + if (!this.world.isClientSide && this.a_ > -this.z() + 20) { + this.a_ = -this.z(); + this.a(itemstack.isEmpty() ? SoundEffects.ENTITY_VILLAGER_NO : SoundEffects.ENTITY_VILLAGER_YES, this.cD(), this.cE()); + } + + } + + @Nullable + public MerchantRecipeList getOffers(EntityHuman entityhuman) { + if (this.trades == null) { + this.populateTrades(); + } + + return this.trades; + } + + public void populateTrades() { + EntityVillager.IMerchantRecipeOption[][][] aentityvillager_imerchantrecipeoption = EntityVillager.bU[this.getProfession()]; + + if (this.careerId != 0 && this.careerLevel != 0) { + ++this.careerLevel; + } else { + this.careerId = this.random.nextInt(aentityvillager_imerchantrecipeoption.length) + 1; + this.careerLevel = 1; + } + + if (this.trades == null) { + this.trades = new MerchantRecipeList(); + } + + int i = this.careerId - 1; + int j = this.careerLevel - 1; + + if (i >= 0 && i < aentityvillager_imerchantrecipeoption.length) { + EntityVillager.IMerchantRecipeOption[][] aentityvillager_imerchantrecipeoption1 = aentityvillager_imerchantrecipeoption[i]; + + if (j >= 0 && j < aentityvillager_imerchantrecipeoption1.length) { + EntityVillager.IMerchantRecipeOption[] aentityvillager_imerchantrecipeoption2 = aentityvillager_imerchantrecipeoption1[j]; + EntityVillager.IMerchantRecipeOption[] aentityvillager_imerchantrecipeoption3 = aentityvillager_imerchantrecipeoption2; + int k = aentityvillager_imerchantrecipeoption2.length; + + for (int l = 0; l < k; ++l) { + EntityVillager.IMerchantRecipeOption entityvillager_imerchantrecipeoption = aentityvillager_imerchantrecipeoption3[l]; + + // CraftBukkit start + // this is a hack. this must be done because otherwise, if + // mojang adds a new type of villager merchant option, it will need to + // have event handling added manually. this is better than having to do that. + MerchantRecipeList list = new MerchantRecipeList(); + entityvillager_imerchantrecipeoption.a(this, list, this.random); + for (MerchantRecipe recipe : list) { + VillagerAcquireTradeEvent event = new VillagerAcquireTradeEvent((Villager) getBukkitEntity(), recipe.asBukkit()); + Bukkit.getPluginManager().callEvent(event); + if (!event.isCancelled()) { + this.trades.add(CraftMerchantRecipe.fromBukkit(event.getRecipe()).toMinecraft()); + } + } + // CraftBukkit end + } + } + + } + } + + public World getWorld() { + return this.world; + } + + public BlockPosition getPosition() { + return new BlockPosition(this); + } + + public IChatBaseComponent getScoreboardDisplayName() { + ScoreboardTeamBase scoreboardteambase = this.getScoreboardTeam(); + IChatBaseComponent ichatbasecomponent = this.getCustomName(); + + if (ichatbasecomponent != null) { + return ScoreboardTeam.a(scoreboardteambase, ichatbasecomponent).a((chatmodifier) -> { + chatmodifier.setChatHoverable(this.bC()).setInsertion(this.bu()); + }); + } else { + if (this.trades == null) { + this.populateTrades(); + } + + String s = null; + + switch (this.getProfession()) { + case 0: + if (this.careerId == 1) { + s = "farmer"; + } else if (this.careerId == 2) { + s = "fisherman"; + } else if (this.careerId == 3) { + s = "shepherd"; + } else if (this.careerId == 4) { + s = "fletcher"; + } + break; + case 1: + if (this.careerId == 1) { + s = "librarian"; + } else if (this.careerId == 2) { + s = "cartographer"; + } + break; + case 2: + s = "cleric"; + break; + case 3: + if (this.careerId == 1) { + s = "armorer"; + } else if (this.careerId == 2) { + s = "weapon_smith"; + } else if (this.careerId == 3) { + s = "tool_smith"; + } + break; + case 4: + if (this.careerId == 1) { + s = "butcher"; + } else if (this.careerId == 2) { + s = "leatherworker"; + } + break; + case 5: + s = "nitwit"; + } + + if (s != null) { + IChatBaseComponent ichatbasecomponent1 = (new ChatMessage(this.P().d() + '.' + s, new Object[0])).a((chatmodifier) -> { + chatmodifier.setChatHoverable(this.bC()).setInsertion(this.bu()); + }); + + if (scoreboardteambase != null) { + ichatbasecomponent1.a(scoreboardteambase.getColor()); + } + + return ichatbasecomponent1; + } else { + return super.getScoreboardDisplayName(); + } + } + } + + public float getHeadHeight() { + return this.isBaby() ? 0.81F : 1.62F; + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + return this.a(difficultydamagescaler, groupdataentity, nbttagcompound, true); + } + + public GroupDataEntity a(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound, boolean flag) { + groupdataentity = super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + if (flag) { + this.setProfession(this.world.random.nextInt(6)); + } + + this.dJ(); + this.populateTrades(); + return groupdataentity; + } + + public void dC() { + this.bR = true; + } + + public EntityVillager createChild(EntityAgeable entityageable) { + EntityVillager entityvillager = EntityTypes.VILLAGER.create(world); // Paper + + entityvillager.prepare(this.world.getDamageScaler(new BlockPosition(entityvillager)), (GroupDataEntity) null, (NBTTagCompound) null); + return entityvillager; + } + + public boolean a(EntityHuman entityhuman) { + return false; + } + + public void onLightningStrike(EntityLightning entitylightning) { + if (!this.world.isClientSide && !this.dead) { + EntityWitch entitywitch = EntityTypes.WITCH.create(world); // Paper + + // Paper start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityZapEvent(this, entitylightning, entitywitch).isCancelled()) { + return; + } + // Paper end + + entitywitch.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, this.pitch); + entitywitch.prepare(this.world.getDamageScaler(new BlockPosition(entitywitch)), (GroupDataEntity) null, (NBTTagCompound) null); + entitywitch.setNoAI(this.isNoAI()); + if (this.hasCustomName()) { + entitywitch.setCustomName(this.getCustomName()); + entitywitch.setCustomNameVisible(this.getCustomNameVisible()); + } + + // CraftBukkit start + if (CraftEventFactory.callEntityTransformEvent(this, entitywitch, EntityTransformEvent.TransformReason.LIGHTNING).isCancelled()) { + return; + } + if (!new com.destroystokyo.paper.event.entity.EntityTransformedEvent(this.getBukkitEntity(), entitywitch.getBukkitEntity(), com.destroystokyo.paper.event.entity.EntityTransformedEvent.TransformedReason.LIGHTNING).callEvent()) return; // Paper + this.world.addEntity(entitywitch, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); + // CraftBukkit end + this.die(); + } + } + + public InventorySubcontainer dD() { + return this.inventory; + } + + protected void a(EntityItem entityitem) { + ItemStack itemstack = entityitem.getItemStack(); + Item item = itemstack.getItem(); + + if (this.a(item)) { + ItemStack itemstack1 = this.inventory.a(itemstack); + + if (itemstack1.isEmpty()) { + entityitem.die(); + } else { + itemstack.setCount(itemstack1.getCount()); + } + } + + } + + private boolean a(Item item) { + return item == Items.BREAD || item == Items.POTATO || item == Items.CARROT || item == Items.WHEAT || item == Items.WHEAT_SEEDS || item == Items.BEETROOT || item == Items.BEETROOT_SEEDS; + } + + public boolean dE() { + return this.p(1); + } + + public boolean dF() { + return this.p(2); + } + + public boolean dG() { + boolean flag = this.getProfession() == 0; + + return flag ? !this.p(5) : !this.p(1); + } + + private boolean p(int i) { + boolean flag = this.getProfession() == 0; + + for (int j = 0; j < this.inventory.getSize(); ++j) { + ItemStack itemstack = this.inventory.getItem(j); + Item item = itemstack.getItem(); + int k = itemstack.getCount(); + + if (item == Items.BREAD && k >= 3 * i || item == Items.POTATO && k >= 12 * i || item == Items.CARROT && k >= 12 * i || item == Items.BEETROOT && k >= 12 * i) { + return true; + } + + if (flag && item == Items.WHEAT && k >= 9 * i) { + return true; + } + } + + return false; + } + + public boolean dH() { + for (int i = 0; i < this.inventory.getSize(); ++i) { + Item item = this.inventory.getItem(i).getItem(); + + if (item == Items.WHEAT_SEEDS || item == Items.POTATO || item == Items.CARROT || item == Items.BEETROOT_SEEDS) { + return true; + } + } + + return false; + } + + public boolean c(int i, ItemStack itemstack) { + if (super.c(i, itemstack)) { + return true; + } else { + int j = i - 300; + + if (j >= 0 && j < this.inventory.getSize()) { + this.inventory.setItem(j, itemstack); + return true; + } else { + return false; + } + } + } + + static class MerchantRecipeOptionProcess implements EntityVillager.IMerchantRecipeOption { + + public ItemStack a; + public EntityVillager.MerchantOptionRandomRange b; + public ItemStack c; + public EntityVillager.MerchantOptionRandomRange d; + + public MerchantRecipeOptionProcess(IMaterial imaterial, EntityVillager.MerchantOptionRandomRange entityvillager_merchantoptionrandomrange, Item item, EntityVillager.MerchantOptionRandomRange entityvillager_merchantoptionrandomrange1) { + this.a = new ItemStack(imaterial); + this.b = entityvillager_merchantoptionrandomrange; + this.c = new ItemStack(item); + this.d = entityvillager_merchantoptionrandomrange1; + } + + public void a(IMerchant imerchant, MerchantRecipeList merchantrecipelist, Random random) { + int i = this.b.a(random); + int j = this.d.a(random); + + merchantrecipelist.add(new MerchantRecipe(new ItemStack(this.a.getItem(), i), new ItemStack(Items.EMERALD), new ItemStack(this.c.getItem(), j))); + } + } + + static class h implements EntityVillager.IMerchantRecipeOption { + + public EntityVillager.MerchantOptionRandomRange a; + public String b; + public MapIcon.Type c; + + public h(EntityVillager.MerchantOptionRandomRange entityvillager_merchantoptionrandomrange, String s, MapIcon.Type mapicon_type) { + this.a = entityvillager_merchantoptionrandomrange; + this.b = s; + this.c = mapicon_type; + } + + public void a(IMerchant imerchant, MerchantRecipeList merchantrecipelist, Random random) { + int i = this.a.a(random); + World world = imerchant.getWorld(); + if (!world.paperConfig.enableTreasureMaps) return; //Paper + BlockPosition blockposition = world.a(this.b, imerchant.getPosition(), 100, true); + + if (blockposition != null) { + ItemStack itemstack = ItemWorldMap.createFilledMapView(world, blockposition.getX(), blockposition.getZ(), (byte) 2, true, true); + + ItemWorldMap.applySepiaFilter(world, itemstack); + WorldMap.decorateMap(itemstack, blockposition, "+", this.c); + itemstack.a((IChatBaseComponent) (new ChatMessage("filled_map." + this.b.toLowerCase(Locale.ROOT), new Object[0]))); + merchantrecipelist.add(new MerchantRecipe(new ItemStack(Items.EMERALD, i), new ItemStack(Items.COMPASS), itemstack)); + } + + } + } + + static class MerchantRecipeOptionBook implements EntityVillager.IMerchantRecipeOption { + + public MerchantRecipeOptionBook() {} + + public void a(IMerchant imerchant, MerchantRecipeList merchantrecipelist, Random random) { + Enchantment enchantment = (Enchantment) IRegistry.ENCHANTMENT.a(random); + int i = MathHelper.nextInt(random, enchantment.getStartLevel(), enchantment.getMaxLevel()); + ItemStack itemstack = ItemEnchantedBook.a(new WeightedRandomEnchant(enchantment, i)); + int j = 2 + random.nextInt(5 + i * 10) + 3 * i; + + if (enchantment.isTreasure()) { + j *= 2; + } + + if (j > 64) { + j = 64; + } + + merchantrecipelist.add(new MerchantRecipe(new ItemStack(Items.BOOK), new ItemStack(Items.EMERALD, j), itemstack)); + } + } + + static class MerchantRecipeOptionEnchant implements EntityVillager.IMerchantRecipeOption { + + public ItemStack a; + public EntityVillager.MerchantOptionRandomRange b; + + public MerchantRecipeOptionEnchant(Item item, EntityVillager.MerchantOptionRandomRange entityvillager_merchantoptionrandomrange) { + this.a = new ItemStack(item); + this.b = entityvillager_merchantoptionrandomrange; + } + + public void a(IMerchant imerchant, MerchantRecipeList merchantrecipelist, Random random) { + int i = 1; + + if (this.b != null) { + i = this.b.a(random); + } + + ItemStack itemstack = new ItemStack(Items.EMERALD, i); + ItemStack itemstack1 = EnchantmentManager.a(random, new ItemStack(this.a.getItem()), 5 + random.nextInt(15), false); + + merchantrecipelist.add(new MerchantRecipe(itemstack, itemstack1)); + } + } + + static class MerchantRecipeOptionSell implements EntityVillager.IMerchantRecipeOption { + + public ItemStack a; + public EntityVillager.MerchantOptionRandomRange b; + + public MerchantRecipeOptionSell(Block block, EntityVillager.MerchantOptionRandomRange entityvillager_merchantoptionrandomrange) { + this(new ItemStack(block), entityvillager_merchantoptionrandomrange); + } + + public MerchantRecipeOptionSell(Item item, EntityVillager.MerchantOptionRandomRange entityvillager_merchantoptionrandomrange) { + this(new ItemStack(item), entityvillager_merchantoptionrandomrange); + } + + public MerchantRecipeOptionSell(ItemStack itemstack, EntityVillager.MerchantOptionRandomRange entityvillager_merchantoptionrandomrange) { + this.a = itemstack; + this.b = entityvillager_merchantoptionrandomrange; + } + + public void a(IMerchant imerchant, MerchantRecipeList merchantrecipelist, Random random) { + int i = 1; + + if (this.b != null) { + i = this.b.a(random); + } + + ItemStack itemstack; + ItemStack itemstack1; + + if (i < 0) { + itemstack = new ItemStack(Items.EMERALD); + itemstack1 = new ItemStack(this.a.getItem(), -i); + } else { + itemstack = new ItemStack(Items.EMERALD, i); + itemstack1 = new ItemStack(this.a.getItem()); + } + + merchantrecipelist.add(new MerchantRecipe(itemstack, itemstack1)); + } + } + + static class MerchantRecipeOptionBuy implements EntityVillager.IMerchantRecipeOption { + + public Item a; + public EntityVillager.MerchantOptionRandomRange b; + + public MerchantRecipeOptionBuy(IMaterial imaterial, EntityVillager.MerchantOptionRandomRange entityvillager_merchantoptionrandomrange) { + this.a = imaterial.getItem(); + this.b = entityvillager_merchantoptionrandomrange; + } + + public void a(IMerchant imerchant, MerchantRecipeList merchantrecipelist, Random random) { + ItemStack itemstack = new ItemStack(this.a, this.b == null ? 1 : this.b.a(random)); + + merchantrecipelist.add(new MerchantRecipe(itemstack, Items.EMERALD)); + } + } + + interface IMerchantRecipeOption { + + void a(IMerchant imerchant, MerchantRecipeList merchantrecipelist, Random random); + } + + static class MerchantOptionRandomRange extends Tuple { + + public MerchantOptionRandomRange(int i, int j) { + super(i, j); + if (j < i) { + EntityVillager.bC.warn("PriceRange({}, {}) invalid, {} smaller than {}", i, j, j, i); + } + + } + + public int a(Random random) { + return (Integer) this.a() >= (Integer) this.b() ? (Integer) this.a() : (Integer) this.a() + random.nextInt((Integer) this.b() - (Integer) this.a() + 1); + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityVindicator.java b/src/main/java/net/minecraft/server/EntityVindicator.java new file mode 100644 index 000000000000..4bbb4817b1e4 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityVindicator.java @@ -0,0 +1,120 @@ +package net.minecraft.server; + +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public class EntityVindicator extends EntityIllagerAbstract { + + private boolean b; public boolean isJohnny() { return b; } public void setJohnny(boolean johnny) { b = johnny; } // Paper - OBFHELPER + private static final Predicate c = (entity) -> { + return entity instanceof EntityLiving && ((EntityLiving) entity).df(); + }; + + public EntityVindicator(World world) { + super(EntityTypes.VINDICATOR, world); + this.setSize(0.6F, 1.95F); + } + + protected void n() { + super.n(); + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(4, new PathfinderGoalMeleeAttack(this, 1.0D, false)); + this.goalSelector.a(8, new PathfinderGoalRandomStroll(this, 0.6D)); + this.goalSelector.a(9, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 3.0F, 1.0F)); + this.goalSelector.a(10, new PathfinderGoalLookAtPlayer(this, EntityInsentient.class, 8.0F)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, true, new Class[] { EntityVindicator.class})); + this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget<>(this, EntityHuman.class, true)); + this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget<>(this, EntityVillager.class, true)); + this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget<>(this, EntityIronGolem.class, true)); + this.targetSelector.a(4, new EntityVindicator.a(this)); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.3499999940395355D); + this.getAttributeInstance(GenericAttributes.FOLLOW_RANGE).setValue(12.0D); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(24.0D); + this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue(5.0D); + } + + protected void x_() { + super.x_(); + } + + protected MinecraftKey getDefaultLootTable() { + return LootTables.aC; + } + + public void a(boolean flag) { + this.a(1, flag); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + if (this.b) { + nbttagcompound.setBoolean("Johnny", true); + } + + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKeyOfType("Johnny", 99)) { + this.b = nbttagcompound.getBoolean("Johnny"); + } + + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + GroupDataEntity groupdataentity1 = super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + + this.a(difficultydamagescaler); + this.b(difficultydamagescaler); + return groupdataentity1; + } + + protected void a(DifficultyDamageScaler difficultydamagescaler) { + this.setSlot(EnumItemSlot.MAINHAND, new ItemStack(Items.IRON_AXE)); + } + + protected void mobTick() { + super.mobTick(); + this.a(this.getGoalTarget() != null); + } + + public boolean r(Entity entity) { + return super.r(entity) ? true : (entity instanceof EntityLiving && ((EntityLiving) entity).getMonsterType() == EnumMonsterType.ILLAGER ? this.getScoreboardTeam() == null && entity.getScoreboardTeam() == null : false); + } + + public void setCustomName(@Nullable IChatBaseComponent ichatbasecomponent) { + super.setCustomName(ichatbasecomponent); + if (!this.b && ichatbasecomponent != null && ichatbasecomponent.getString().equals("Johnny")) { + this.b = true; + } + + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_VINDICATOR_AMBIENT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_VINDICATOR_DEATH; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_VINDICATOR_HURT; + } + + static class a extends PathfinderGoalNearestAttackableTarget { + + public a(EntityVindicator entityvindicator) { + super(entityvindicator, EntityLiving.class, 0, true, true, EntityVindicator.c); + } + + public boolean a() { + return ((EntityVindicator) this.e).b && super.a(); + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityWitch.java b/src/main/java/net/minecraft/server/EntityWitch.java new file mode 100644 index 000000000000..955e0e19b943 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityWitch.java @@ -0,0 +1,202 @@ +package net.minecraft.server; + +// Paper start +import com.destroystokyo.paper.event.entity.WitchReadyPotionEvent; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.Witch; +// Paper end + +import java.util.Iterator; +import java.util.List; +import java.util.UUID; +import javax.annotation.Nullable; + +public class EntityWitch extends EntityMonster implements IRangedEntity { + + private static final UUID a = UUID.fromString("5CD17E52-A79A-43D3-A529-90FDE04B181E"); + private static final AttributeModifier b = (new AttributeModifier(EntityWitch.a, "Drinking speed penalty", -0.25D, 0)).a(false); private static final AttributeModifier DRINKING_SPEED = b; // Paper - OBFHELPER + private static final DataWatcherObject c = DataWatcher.a(EntityWitch.class, DataWatcherRegistry.i); + private int bC; public int getPotionUseTimeLeft() { return bC; } public void setPotionUseTimeLeft(int timeLeft) { bC = timeLeft; } // Paper - OBFHELPER + + public EntityWitch(World world) { + super(EntityTypes.WITCH, world); + this.setSize(0.6F, 1.95F); + } + + protected void n() { + this.goalSelector.a(1, new PathfinderGoalFloat(this)); + this.goalSelector.a(2, new PathfinderGoalArrowAttack(this, 1.0D, 60, 10.0F)); + this.goalSelector.a(2, new PathfinderGoalRandomStrollLand(this, 1.0D)); + this.goalSelector.a(3, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(3, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, false, new Class[0])); + this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget<>(this, EntityHuman.class, true)); + } + + protected void x_() { + super.x_(); + this.getDataWatcher().register(EntityWitch.c, false); + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_WITCH_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_WITCH_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_WITCH_DEATH; + } + + public void setDrinkingPotion(boolean drinkingPotion) { a(drinkingPotion); } // Paper - OBFHELPER + public void a(boolean flag) { + this.getDataWatcher().set(EntityWitch.c, flag); + } + + public boolean isDrinkingPotion() { return l(); } // Paper - OBFHELPER + public boolean l() { + return (Boolean) this.getDataWatcher().get(EntityWitch.c); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(26.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.25D); + } + + public void movementTick() { + if (!this.world.isClientSide) { + if (this.l()) { + if (this.bC-- <= 0) { + this.a(false); + ItemStack itemstack = this.getItemInMainHand(); + + this.setSlot(EnumItemSlot.MAINHAND, ItemStack.a); + if (itemstack.getItem() == Items.POTION) { + // Paper start + com.destroystokyo.paper.event.entity.WitchConsumePotionEvent event = new com.destroystokyo.paper.event.entity.WitchConsumePotionEvent((org.bukkit.entity.Witch) this.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack)); + + List list = event.callEvent() ? PotionUtil.getEffects(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getPotion())) : null; + // Paper end + + if (list != null) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + this.addEffect(new MobEffect(mobeffect), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit + } + } + } + + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).c(EntityWitch.b); + } + } else { + PotionRegistry potionregistry = null; + + if (this.random.nextFloat() < 0.15F && this.a(TagsFluid.WATER) && !this.hasEffect(MobEffects.WATER_BREATHING)) { + potionregistry = Potions.x; + } else if (this.random.nextFloat() < 0.15F && (this.isBurning() || this.cr() != null && this.cr().p()) && !this.hasEffect(MobEffects.FIRE_RESISTANCE)) { + potionregistry = Potions.m; + } else if (this.random.nextFloat() < 0.05F && this.getHealth() < this.getMaxHealth()) { + potionregistry = Potions.z; + } else if (this.random.nextFloat() < 0.5F && this.getGoalTarget() != null && !this.hasEffect(MobEffects.FASTER_MOVEMENT) && this.getGoalTarget().h(this) > 121.0D) { + potionregistry = Potions.o; + } + + if (potionregistry != null) { + // Paper start - moved all this down into its own method + //this.setSlot(EnumItemSlot.MAINHAND, PotionUtil.a(new ItemStack(Items.POTION), potionregistry)); + //this.bC = this.getItemInMainHand().k(); + //this.a(true); + //this.world.a((EntityHuman) null, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_WITCH_DRINK, this.bV(), 1.0F, 0.8F + this.random.nextFloat() * 0.4F); + //AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED); + //attributeinstance.c(EntityWitch.b); + //attributeinstance.b(EntityWitch.b); + + setDrinkingPotion(PotionUtil.addPotionToItemStack(new ItemStack(Items.POTION), potionregistry)); + // Paper end + } + } + + if (this.random.nextFloat() < 7.5E-4F) { + this.world.broadcastEntityEffect(this, (byte) 15); + } + } + + super.movementTick(); + } + + // Paper start + public void setDrinkingPotion(ItemStack potion) { + setSlot(EnumItemSlot.MAINHAND, CraftItemStack.asNMSCopy(WitchReadyPotionEvent.process((Witch) getBukkitEntity(), CraftItemStack.asCraftMirror(potion)))); + setPotionUseTimeLeft(getItemInMainHand().getItemUseMaxDuration()); + setDrinkingPotion(true); + world.sendSoundEffect(null, locX, locY, locZ, SoundEffects.ENTITY_WITCH_DRINK, getSoundCategory(), 1.0F, 0.8F + random.nextFloat() * 0.4F); + AttributeInstance attributeinstance = getAttributeInstance(GenericAttributes.MOVEMENT_SPEED); + attributeinstance.removeModifier(EntityWitch.DRINKING_SPEED); + attributeinstance.addModifier(EntityWitch.DRINKING_SPEED); + } + // Paper end + + protected float applyMagicModifier(DamageSource damagesource, float f) { + f = super.applyMagicModifier(damagesource, f); + if (damagesource.getEntity() == this) { + f = 0.0F; + } + + if (damagesource.isMagic()) { + f = (float) ((double) f * 0.15D); + } + + return f; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.v; + } + + public void a(EntityLiving entityliving, float f) { + if (!this.l()) { + double d0 = entityliving.locY + (double) entityliving.getHeadHeight() - 1.100000023841858D; + double d1 = entityliving.locX + entityliving.motX - this.locX; + double d2 = d0 - this.locY; + double d3 = entityliving.locZ + entityliving.motZ - this.locZ; + float f1 = MathHelper.sqrt(d1 * d1 + d3 * d3); + PotionRegistry potionregistry = Potions.B; + + if (f1 >= 8.0F && !entityliving.hasEffect(MobEffects.SLOWER_MOVEMENT)) { + potionregistry = Potions.r; + } else if (entityliving.getHealth() >= 8.0F && !entityliving.hasEffect(MobEffects.POISON)) { + potionregistry = Potions.D; + } else if (f1 <= 3.0F && !entityliving.hasEffect(MobEffects.WEAKNESS) && this.random.nextFloat() < 0.25F) { + potionregistry = Potions.M; + } + + // Paper start + ItemStack potion = PotionUtil.a(new ItemStack(Items.SPLASH_POTION), potionregistry); + com.destroystokyo.paper.event.entity.WitchThrowPotionEvent event = new com.destroystokyo.paper.event.entity.WitchThrowPotionEvent((org.bukkit.entity.Witch) this.getBukkitEntity(), (org.bukkit.entity.LivingEntity) entityliving.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(potion)); + if (!event.callEvent()) { + return; + } + potion = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getPotion()); + EntityPotion entitypotion = new EntityPotion(this.world, this, potion); + // Paper end + + entitypotion.pitch -= -20.0F; + entitypotion.shoot(d1, d2 + (double) (f1 * 0.2F), d3, 0.75F, 8.0F); + this.world.a((EntityHuman) null, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_WITCH_THROW, this.bV(), 1.0F, 0.8F + this.random.nextFloat() * 0.4F); + this.world.addEntity(entitypotion); + } + } + + public float getHeadHeight() { + return 1.62F; + } + + public void s(boolean flag) {} +} diff --git a/src/main/java/net/minecraft/server/EntityWither.java b/src/main/java/net/minecraft/server/EntityWither.java new file mode 100644 index 000000000000..ed4ca8abb23b --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityWither.java @@ -0,0 +1,541 @@ +package net.minecraft.server; + +import com.google.common.collect.ImmutableList; +import java.util.List; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityRegainHealthEvent; +import org.bukkit.event.entity.EntityTargetEvent; +import org.bukkit.event.entity.ExplosionPrimeEvent; +// CraftBukkit end + +public class EntityWither extends EntityMonster implements IRangedEntity { + + private static final DataWatcherObject a = DataWatcher.a(EntityWither.class, DataWatcherRegistry.b); + private static final DataWatcherObject b = DataWatcher.a(EntityWither.class, DataWatcherRegistry.b); + private static final DataWatcherObject c = DataWatcher.a(EntityWither.class, DataWatcherRegistry.b); + private static final List> bC = ImmutableList.of(EntityWither.a, EntityWither.b, EntityWither.c); + private static final DataWatcherObject bD = DataWatcher.a(EntityWither.class, DataWatcherRegistry.b); + private final float[] bE = new float[2]; + private final float[] bF = new float[2]; + private final float[] bG = new float[2]; + private final float[] bH = new float[2]; + private final int[] bI = new int[2]; + private final int[] bJ = new int[2]; + private int bK; + public final BossBattleServer bossBattle; + private static final Predicate bM = (entity) -> { + return entity instanceof EntityLiving && ((EntityLiving) entity).getMonsterType() != EnumMonsterType.UNDEAD && ((EntityLiving) entity).df(); + }; + + public EntityWither(World world) { + super(EntityTypes.WITHER, world); + this.bossBattle = (BossBattleServer) (new BossBattleServer(this.getScoreboardDisplayName(), BossBattle.BarColor.PURPLE, BossBattle.BarStyle.PROGRESS)).setDarkenSky(true); + this.setHealth(this.getMaxHealth()); + this.setSize(0.9F, 3.5F); + this.fireProof = true; + ((Navigation) this.getNavigation()).d(true); + this.b_ = 50; + } + + protected void n() { + this.goalSelector.a(0, new EntityWither.a()); + this.goalSelector.a(2, new PathfinderGoalArrowAttack(this, 1.0D, 40, 20.0F)); + this.goalSelector.a(5, new PathfinderGoalRandomStrollLand(this, 1.0D)); + this.goalSelector.a(6, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(7, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, false, new Class[0])); + this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget<>(this, EntityInsentient.class, 0, false, false, EntityWither.bM)); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityWither.a, 0); + this.datawatcher.register(EntityWither.b, 0); + this.datawatcher.register(EntityWither.c, 0); + this.datawatcher.register(EntityWither.bD, 0); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("Invul", this.dz()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.d(nbttagcompound.getInt("Invul")); + if (this.hasCustomName()) { + this.bossBattle.a(this.getScoreboardDisplayName()); + } + + } + + public void setCustomName(@Nullable IChatBaseComponent ichatbasecomponent) { + super.setCustomName(ichatbasecomponent); + this.bossBattle.a(this.getScoreboardDisplayName()); + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_WITHER_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_WITHER_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_WITHER_DEATH; + } + + public void movementTick() { + this.motY *= 0.6000000238418579D; + double d0; + double d1; + double d2; + + if (!this.world.isClientSide && this.p(0) > 0) { + Entity entity = this.world.getEntity(this.p(0)); + + if (entity != null) { + if (this.locY < entity.locY || !this.dA() && this.locY < entity.locY + 5.0D) { + if (this.motY < 0.0D) { + this.motY = 0.0D; + } + + this.motY += (0.5D - this.motY) * 0.6000000238418579D; + } + + double d3 = entity.locX - this.locX; + + d0 = entity.locZ - this.locZ; + d1 = d3 * d3 + d0 * d0; + if (d1 > 9.0D) { + d2 = (double) MathHelper.sqrt(d1); + this.motX += (d3 / d2 * 0.5D - this.motX) * 0.6000000238418579D; + this.motZ += (d0 / d2 * 0.5D - this.motZ) * 0.6000000238418579D; + } + } + } + + if (this.motX * this.motX + this.motZ * this.motZ > 0.05000000074505806D) { + this.yaw = (float) MathHelper.c(this.motZ, this.motX) * 57.295776F - 90.0F; + } + + super.movementTick(); + + int i; + + for (i = 0; i < 2; ++i) { + this.bH[i] = this.bF[i]; + this.bG[i] = this.bE[i]; + } + + int j; + + for (i = 0; i < 2; ++i) { + j = this.p(i + 1); + Entity entity1 = null; + + if (j > 0) { + entity1 = this.world.getEntity(j); + } + + if (entity1 != null) { + d0 = this.q(i + 1); + d1 = this.r(i + 1); + d2 = this.s(i + 1); + double d4 = entity1.locX - d0; + double d5 = entity1.locY + (double) entity1.getHeadHeight() - d1; + double d6 = entity1.locZ - d2; + double d7 = (double) MathHelper.sqrt(d4 * d4 + d6 * d6); + float f = (float) (MathHelper.c(d6, d4) * 57.2957763671875D) - 90.0F; + float f1 = (float) (-(MathHelper.c(d5, d7) * 57.2957763671875D)); + + this.bE[i] = this.c(this.bE[i], f1, 40.0F); + this.bF[i] = this.c(this.bF[i], f, 10.0F); + } else { + this.bF[i] = this.c(this.bF[i], this.aQ, 10.0F); + } + } + + boolean flag = this.dA(); + + for (j = 0; j < 3; ++j) { + double d8 = this.q(j); + double d9 = this.r(j); + double d10 = this.s(j); + + this.world.addParticle(Particles.M, d8 + this.random.nextGaussian() * 0.30000001192092896D, d9 + this.random.nextGaussian() * 0.30000001192092896D, d10 + this.random.nextGaussian() * 0.30000001192092896D, 0.0D, 0.0D, 0.0D); + if (flag && this.world.random.nextInt(4) == 0) { + this.world.addParticle(Particles.s, d8 + this.random.nextGaussian() * 0.30000001192092896D, d9 + this.random.nextGaussian() * 0.30000001192092896D, d10 + this.random.nextGaussian() * 0.30000001192092896D, 0.699999988079071D, 0.699999988079071D, 0.5D); + } + } + + if (this.dz() > 0) { + for (j = 0; j < 3; ++j) { + this.world.addParticle(Particles.s, this.locX + this.random.nextGaussian(), this.locY + (double) (this.random.nextFloat() * 3.3F), this.locZ + this.random.nextGaussian(), 0.699999988079071D, 0.699999988079071D, 0.8999999761581421D); + } + } + + } + + protected void mobTick() { + int i; + + if (this.dz() > 0) { + i = this.dz() - 1; + if (i <= 0) { + // CraftBukkit start + // this.world.createExplosion(this, this.locX, this.locY + (double) this.getHeadHeight(), this.locZ, 7.0F, false, this.world.getGameRules().getBoolean("mobGriefing")); + ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), 7.0F, false); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + this.world.createExplosion(this, this.locX, this.locY + (double) this.getHeadHeight(), this.locZ, event.getRadius(), event.getFire(), this.world.getGameRules().getBoolean("mobGriefing")); + } + // CraftBukkit end + + // CraftBukkit start - Use relative location for far away sounds + // this.world.a(1023, new BlockPosition(this), 0); + // Paper start + //int viewDistance = ((WorldServer) this.world).spigotConfig.viewDistance * 16; // Paper - updated to use worlds actual view distance incase we have to uncomment this due to removal of player view distance API + for (EntityHuman human : world.players) { + EntityPlayer player = (EntityPlayer) human; + int viewDistance = player.getViewDistance(); + // Paper end + double deltaX = this.locX - player.locX; + double deltaZ = this.locZ - player.locZ; + double distanceSquared = deltaX * deltaX + deltaZ * deltaZ; + if ( world.spigotConfig.witherSpawnSoundRadius > 0 && distanceSquared > world.spigotConfig.witherSpawnSoundRadius * world.spigotConfig.witherSpawnSoundRadius ) continue; // Spigot + if (distanceSquared > viewDistance * viewDistance) { + double deltaLength = Math.sqrt(distanceSquared); + double relativeX = player.locX + (deltaX / deltaLength) * viewDistance; + double relativeZ = player.locZ + (deltaZ / deltaLength) * viewDistance; + player.playerConnection.sendPacket(new PacketPlayOutWorldEvent(1023, new BlockPosition((int) relativeX, (int) this.locY, (int) relativeZ), 0, true)); + } else { + player.playerConnection.sendPacket(new PacketPlayOutWorldEvent(1023, new BlockPosition((int) this.locX, (int) this.locY, (int) this.locZ), 0, true)); + } + } + // CraftBukkit end + } + + this.d(i); + if (this.ticksLived % 10 == 0) { + this.heal(10.0F, EntityRegainHealthEvent.RegainReason.WITHER_SPAWN); // CraftBukkit + } + + } else { + super.mobTick(); + + int j; + + for (i = 1; i < 3; ++i) { + if (this.ticksLived >= this.bI[i - 1]) { + this.bI[i - 1] = this.ticksLived + 10 + this.random.nextInt(10); + if (this.world.getDifficulty() == EnumDifficulty.NORMAL || this.world.getDifficulty() == EnumDifficulty.HARD) { + int k = i - 1; + int l = this.bJ[i - 1]; + + this.bJ[k] = this.bJ[i - 1] + 1; + if (l > 15) { + float f = 10.0F; + float f1 = 5.0F; + double d0 = MathHelper.a(this.random, this.locX - 10.0D, this.locX + 10.0D); + double d1 = MathHelper.a(this.random, this.locY - 5.0D, this.locY + 5.0D); + double d2 = MathHelper.a(this.random, this.locZ - 10.0D, this.locZ + 10.0D); + + this.a(i + 1, d0, d1, d2, true); + this.bJ[i - 1] = 0; + } + } + + j = this.p(i); + if (j > 0) { + Entity entity = this.world.getEntity(j); + + if (entity != null && entity.isAlive() && this.h(entity) <= 900.0D && this.hasLineOfSight(entity)) { + if (entity instanceof EntityHuman && ((EntityHuman) entity).abilities.isInvulnerable) { + this.a(i, 0); + } else { + this.a(i + 1, (EntityLiving) entity); + this.bI[i - 1] = this.ticksLived + 40 + this.random.nextInt(20); + this.bJ[i - 1] = 0; + } + } else { + this.a(i, 0); + } + } else { + List list = this.world.a(EntityLiving.class, this.getBoundingBox().grow(20.0D, 8.0D, 20.0D), EntityWither.bM.and(IEntitySelector.f)); + + for (int i1 = 0; i1 < 10 && !list.isEmpty(); ++i1) { + EntityLiving entityliving = (EntityLiving) list.get(this.random.nextInt(list.size())); + + if (entityliving != this && entityliving.isAlive() && this.hasLineOfSight(entityliving)) { + if (entityliving instanceof EntityHuman) { + if (!((EntityHuman) entityliving).abilities.isInvulnerable) { + if (CraftEventFactory.callEntityTargetLivingEvent(this, entityliving, EntityTargetEvent.TargetReason.CLOSEST_PLAYER).isCancelled()) continue; // CraftBukkit + this.a(i, entityliving.getId()); + } + } else { + if (CraftEventFactory.callEntityTargetLivingEvent(this, entityliving, EntityTargetEvent.TargetReason.CLOSEST_ENTITY).isCancelled()) continue; // CraftBukkit + this.a(i, entityliving.getId()); + } + break; + } + + list.remove(entityliving); + } + } + } + } + + if (this.getGoalTarget() != null) { + this.a(0, this.getGoalTarget().getId()); + } else { + this.a(0, 0); + } + + if (this.bK > 0) { + --this.bK; + if (this.bK == 0 && this.world.getGameRules().getBoolean("mobGriefing")) { + i = MathHelper.floor(this.locY); + j = MathHelper.floor(this.locX); + int j1 = MathHelper.floor(this.locZ); + boolean flag = false; + + for (int k1 = -1; k1 <= 1; ++k1) { + for (int l1 = -1; l1 <= 1; ++l1) { + for (int i2 = 0; i2 <= 3; ++i2) { + int j2 = j + k1; + int k2 = i + i2; + int l2 = j1 + l1; + BlockPosition blockposition = new BlockPosition(j2, k2, l2); + IBlockData iblockdata = this.world.getType(blockposition); + Block block = iblockdata.getBlock(); + + if (!iblockdata.isAir() && a(block)) { + // CraftBukkit start + if (CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, Blocks.AIR.getBlockData()).isCancelled()) { + continue; + } + // CraftBukkit end + flag = this.world.setAir(blockposition, true) || flag; + } + } + } + } + + if (flag) { + this.world.a((EntityHuman) null, 1022, new BlockPosition(this), 0); + } + } + } + + if (this.ticksLived % 20 == 0) { + this.heal(1.0F, EntityRegainHealthEvent.RegainReason.REGEN); // CraftBukkit + } + + this.bossBattle.setProgress(this.getHealth() / this.getMaxHealth()); + } + } + + public static boolean a(Block block) { + return block != Blocks.BEDROCK && block != Blocks.END_PORTAL && block != Blocks.END_PORTAL_FRAME && block != Blocks.COMMAND_BLOCK && block != Blocks.REPEATING_COMMAND_BLOCK && block != Blocks.CHAIN_COMMAND_BLOCK && block != Blocks.BARRIER && block != Blocks.STRUCTURE_BLOCK && block != Blocks.STRUCTURE_VOID && block != Blocks.MOVING_PISTON && block != Blocks.END_GATEWAY; + } + + public void l() { + this.d(220); + this.setHealth(this.getMaxHealth() / 3.0F); + } + + public void bh() {} + + public void b(EntityPlayer entityplayer) { + super.b(entityplayer); + this.bossBattle.addPlayer(entityplayer); + } + + public void c(EntityPlayer entityplayer) { + super.c(entityplayer); + this.bossBattle.removePlayer(entityplayer); + } + + private double q(int i) { + if (i <= 0) { + return this.locX; + } else { + float f = (this.aQ + (float) (180 * (i - 1))) * 0.017453292F; + float f1 = MathHelper.cos(f); + + return this.locX + (double) f1 * 1.3D; + } + } + + private double r(int i) { + return i <= 0 ? this.locY + 3.0D : this.locY + 2.2D; + } + + private double s(int i) { + if (i <= 0) { + return this.locZ; + } else { + float f = (this.aQ + (float) (180 * (i - 1))) * 0.017453292F; + float f1 = MathHelper.sin(f); + + return this.locZ + (double) f1 * 1.3D; + } + } + + private float c(float f, float f1, float f2) { + float f3 = MathHelper.g(f1 - f); + + if (f3 > f2) { + f3 = f2; + } + + if (f3 < -f2) { + f3 = -f2; + } + + return f + f3; + } + + private void a(int i, EntityLiving entityliving) { + this.a(i, entityliving.locX, entityliving.locY + (double) entityliving.getHeadHeight() * 0.5D, entityliving.locZ, i == 0 && this.random.nextFloat() < 0.001F); + } + + private void a(int i, double d0, double d1, double d2, boolean flag) { + this.world.a((EntityHuman) null, 1024, new BlockPosition(this), 0); + double d3 = this.q(i); + double d4 = this.r(i); + double d5 = this.s(i); + double d6 = d0 - d3; + double d7 = d1 - d4; + double d8 = d2 - d5; + EntityWitherSkull entitywitherskull = new EntityWitherSkull(this.world, this, d6, d7, d8); + + if (flag) { + entitywitherskull.setCharged(true); + } + + entitywitherskull.locY = d4; + entitywitherskull.locX = d3; + entitywitherskull.locZ = d5; + this.world.addEntity(entitywitherskull); + } + + public void a(EntityLiving entityliving, float f) { + this.a(0, entityliving); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else if (damagesource != DamageSource.DROWN && !(damagesource.getEntity() instanceof EntityWither)) { + if (this.dz() > 0 && damagesource != DamageSource.OUT_OF_WORLD) { + return false; + } else { + Entity entity; + + if (this.dA()) { + entity = damagesource.j(); + if (entity instanceof EntityArrow) { + return false; + } + } + + entity = damagesource.getEntity(); + if (entity != null && !(entity instanceof EntityHuman) && entity instanceof EntityLiving && ((EntityLiving) entity).getMonsterType() == this.getMonsterType()) { + return false; + } else { + if (this.bK <= 0) { + this.bK = 20; + } + + for (int i = 0; i < this.bJ.length; ++i) { + this.bJ[i] += 3; + } + + return super.damageEntity(damagesource, f); + } + } + } else { + return false; + } + } + + protected void dropDeathLoot(boolean flag, int i) { + EntityItem entityitem = this.a((IMaterial) Items.NETHER_STAR); + + if (entityitem != null) { + entityitem.s(); + } + + } + + protected void I() { + this.ticksFarFromPlayer = 0; + } + + public void c(float f, float f1) {} + + public boolean addEffect(MobEffect mobeffect) { + return false; + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(300.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.6000000238418579D); + this.getAttributeInstance(GenericAttributes.FOLLOW_RANGE).setValue(40.0D); + this.getAttributeInstance(GenericAttributes.h).setValue(4.0D); + } + + public int dz() { + return (Integer) this.datawatcher.get(EntityWither.bD); + } + + public void d(int i) { + this.datawatcher.set(EntityWither.bD, i); + } + + public int p(int i) { + return (Integer) this.datawatcher.get((DataWatcherObject) EntityWither.bC.get(i)); + } + + public void a(int i, int j) { + this.datawatcher.set((DataWatcherObject) EntityWither.bC.get(i), j); + } + + public boolean dA() { + return this.getHealth() <= this.getMaxHealth() / 2.0F; + } + + public EnumMonsterType getMonsterType() { + return EnumMonsterType.UNDEAD; + } + + protected boolean n(Entity entity) { + return false; + } + + public boolean bm() { + return false; + } + + public void s(boolean flag) {} + + class a extends PathfinderGoal { + + public a() { + this.a(7); + } + + public boolean a() { + return EntityWither.this.dz() > 0; + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityWitherSkull.java b/src/main/java/net/minecraft/server/EntityWitherSkull.java new file mode 100644 index 000000000000..ea1ab45f36cd --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityWitherSkull.java @@ -0,0 +1,100 @@ +package net.minecraft.server; + +import org.bukkit.event.entity.ExplosionPrimeEvent; // CraftBukkit + +public class EntityWitherSkull extends EntityFireball { + + private static final DataWatcherObject e = DataWatcher.a(EntityWitherSkull.class, DataWatcherRegistry.i); + + public EntityWitherSkull(World world) { + super(EntityTypes.WITHER_SKULL, world, 0.3125F, 0.3125F); + } + + public EntityWitherSkull(World world, EntityLiving entityliving, double d0, double d1, double d2) { + super(EntityTypes.WITHER_SKULL, entityliving, d0, d1, d2, world, 0.3125F, 0.3125F); + } + + protected float k() { + return this.isCharged() ? 0.73F : super.k(); + } + + public boolean isBurning() { + return false; + } + + public float a(Explosion explosion, IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, Fluid fluid, float f) { + return this.isCharged() && EntityWither.a(iblockdata.getBlock()) ? Math.min(0.8F, f) : f; + } + + protected void a(MovingObjectPosition movingobjectposition) { + if (!this.world.isClientSide) { + if (movingobjectposition.entity != null) { + // Spigot start + boolean didDamage = false; + if (this.shooter != null) { + didDamage = movingobjectposition.entity.damageEntity(DamageSource.projectile(this, shooter), 8.0F); + if (didDamage) { // CraftBukkit + if (movingobjectposition.entity.isAlive()) { + this.a(this.shooter, movingobjectposition.entity); + } else { + this.shooter.heal(5.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.WITHER); // CraftBukkit + } + } + } else { + didDamage = movingobjectposition.entity.damageEntity(DamageSource.MAGIC, 5.0F); + } + + if (didDamage && movingobjectposition.entity instanceof EntityLiving) { + // Spigot end + byte b0 = 0; + + if (this.world.getDifficulty() == EnumDifficulty.NORMAL) { + b0 = 10; + } else if (this.world.getDifficulty() == EnumDifficulty.HARD) { + b0 = 40; + } + + if (b0 > 0) { + ((EntityLiving) movingobjectposition.entity).addEffect(new MobEffect(MobEffects.WITHER, 20 * b0, 1), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit + } + } + } + + // CraftBukkit start + // this.world.createExplosion(this, this.locX, this.locY, this.locZ, 1.0F, false, this.world.getGameRules().getBoolean("mobGriefing")); + ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), 1.0F, false); + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + this.world.createExplosion(this, this.locX, this.locY, this.locZ, event.getRadius(), event.getFire(), this.world.getGameRules().getBoolean("mobGriefing")); + } + // CraftBukkit end + this.die(); + } + + } + + public boolean isInteractable() { + return false; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + return false; + } + + protected void x_() { + this.datawatcher.register(EntityWitherSkull.e, false); + } + + public boolean isCharged() { + return (Boolean) this.datawatcher.get(EntityWitherSkull.e); + } + + public void setCharged(boolean flag) { + this.datawatcher.set(EntityWitherSkull.e, flag); + } + + protected boolean f() { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/EntityWolf.java b/src/main/java/net/minecraft/server/EntityWolf.java new file mode 100644 index 000000000000..46d8e0a1f4c8 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityWolf.java @@ -0,0 +1,425 @@ +package net.minecraft.server; + +import java.util.UUID; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityTargetEvent.TargetReason; +// CraftBukkit end + +public class EntityWolf extends EntityTameableAnimal { + + private static final DataWatcherObject DATA_HEALTH = DataWatcher.a(EntityWolf.class, DataWatcherRegistry.c); + private static final DataWatcherObject bH = DataWatcher.a(EntityWolf.class, DataWatcherRegistry.i); + private static final DataWatcherObject bI = DataWatcher.a(EntityWolf.class, DataWatcherRegistry.b); + private float bJ; + private float bK; + private boolean bL; + private boolean bM; + private float bN; + private float bO; + + public EntityWolf(World world) { + super(EntityTypes.WOLF, world); + this.setSize(0.6F, 0.85F); + this.setTamed(false); + } + + protected void n() { + this.goalSit = new PathfinderGoalSit(this); + this.goalSelector.a(1, new PathfinderGoalFloat(this)); + this.goalSelector.a(2, this.goalSit); + this.goalSelector.a(3, new EntityWolf.a<>(this, EntityLlama.class, 24.0F, 1.5D, 1.5D)); + this.goalSelector.a(4, new PathfinderGoalLeapAtTarget(this, 0.4F)); + this.goalSelector.a(5, new PathfinderGoalMeleeAttack(this, 1.0D, true)); + this.goalSelector.a(6, new PathfinderGoalFollowOwner(this, 1.0D, 10.0F, 2.0F)); + this.goalSelector.a(7, new PathfinderGoalBreed(this, 1.0D)); + this.goalSelector.a(8, new PathfinderGoalRandomStrollLand(this, 1.0D)); + this.goalSelector.a(9, new PathfinderGoalBeg(this, 8.0F)); + this.goalSelector.a(10, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(10, new PathfinderGoalRandomLookaround(this)); + this.targetSelector.a(1, new PathfinderGoalOwnerHurtByTarget(this)); + this.targetSelector.a(2, new PathfinderGoalOwnerHurtTarget(this)); + this.targetSelector.a(3, new PathfinderGoalHurtByTarget(this, true, new Class[0])); + this.targetSelector.a(4, new PathfinderGoalRandomTargetNonTamed<>(this, EntityAnimal.class, false, (entityanimal) -> { + return entityanimal instanceof EntitySheep || entityanimal instanceof EntityRabbit; + })); + this.targetSelector.a(4, new PathfinderGoalRandomTargetNonTamed<>(this, EntityTurtle.class, false, EntityTurtle.bC)); + this.targetSelector.a(5, new PathfinderGoalNearestAttackableTarget<>(this, EntitySkeletonAbstract.class, false)); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.30000001192092896D); + if (this.isTamed()) { + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(20.0D); + } else { + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(8.0D); + } + + this.getAttributeMap().b(GenericAttributes.ATTACK_DAMAGE).setValue(2.0D); + } + + // CraftBukkit - add overriden version + @Override + public boolean setGoalTarget(EntityLiving entityliving, org.bukkit.event.entity.EntityTargetEvent.TargetReason reason, boolean fire) { + if (!super.setGoalTarget(entityliving, reason, fire)) { + return false; + } + entityliving = getGoalTarget(); + if (entityliving == null) { + this.setAngry(false); + } else if (!this.isTamed()) { + this.setAngry(true); + } + return true; + } + // CraftBukkit end + + public void setGoalTarget(@Nullable EntityLiving entityliving) { + super.setGoalTarget(entityliving); + if (entityliving == null) { + this.setAngry(false); + } else if (!this.isTamed()) { + this.setAngry(true); + } + + } + + protected void mobTick() { + this.datawatcher.set(EntityWolf.DATA_HEALTH, this.getHealth()); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityWolf.DATA_HEALTH, this.getHealth()); + this.datawatcher.register(EntityWolf.bH, false); + this.datawatcher.register(EntityWolf.bI, EnumColor.RED.getColorIndex()); + } + + protected void a(BlockPosition blockposition, IBlockData iblockdata) { + this.a(SoundEffects.ENTITY_WOLF_STEP, 0.15F, 1.0F); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setBoolean("Angry", this.isAngry()); + nbttagcompound.setByte("CollarColor", (byte) this.getCollarColor().getColorIndex()); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setAngry(nbttagcompound.getBoolean("Angry")); + if (nbttagcompound.hasKeyOfType("CollarColor", 99)) { + this.setCollarColor(EnumColor.fromColorIndex(nbttagcompound.getInt("CollarColor"))); + } + + } + + protected SoundEffect D() { + return this.isAngry() ? SoundEffects.ENTITY_WOLF_GROWL : (this.random.nextInt(3) == 0 ? (this.isTamed() && (Float) this.datawatcher.get(EntityWolf.DATA_HEALTH) < 10.0F ? SoundEffects.ENTITY_WOLF_WHINE : SoundEffects.ENTITY_WOLF_PANT) : SoundEffects.ENTITY_WOLF_AMBIENT); + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_WOLF_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_WOLF_DEATH; + } + + protected float cD() { + return 0.4F; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.U; + } + + public void movementTick() { + super.movementTick(); + if (!this.world.isClientSide && this.bL && !this.bM && !this.dr() && this.onGround) { + this.bM = true; + this.bN = 0.0F; + this.bO = 0.0F; + this.world.broadcastEntityEffect(this, (byte) 8); + } + + if (!this.world.isClientSide && this.getGoalTarget() == null && this.isAngry()) { + this.setAngry(false); + } + + } + + public void tick() { + super.tick(); + this.bK = this.bJ; + if (this.dL()) { + this.bJ += (1.0F - this.bJ) * 0.4F; + } else { + this.bJ += (0.0F - this.bJ) * 0.4F; + } + + if (this.ap()) { + this.bL = true; + this.bM = false; + this.bN = 0.0F; + this.bO = 0.0F; + } else if ((this.bL || this.bM) && this.bM) { + if (this.bN == 0.0F) { + this.a(SoundEffects.ENTITY_WOLF_SHAKE, this.cD(), (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F); + } + + this.bO = this.bN; + this.bN += 0.05F; + if (this.bO >= 2.0F) { + this.bL = false; + this.bM = false; + this.bO = 0.0F; + this.bN = 0.0F; + } + + if (this.bN > 0.4F) { + float f = (float) this.getBoundingBox().minY; + int i = (int) (MathHelper.sin((this.bN - 0.4F) * 3.1415927F) * 7.0F); + + for (int j = 0; j < i; ++j) { + float f1 = (this.random.nextFloat() * 2.0F - 1.0F) * this.width * 0.5F; + float f2 = (this.random.nextFloat() * 2.0F - 1.0F) * this.width * 0.5F; + + this.world.addParticle(Particles.R, this.locX + (double) f1, (double) (f + 0.8F), this.locZ + (double) f2, this.motX, this.motY, this.motZ); + } + } + } + + } + + public float getHeadHeight() { + return this.length * 0.8F; + } + + public int K() { + return this.isSitting() ? 20 : super.K(); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else { + Entity entity = damagesource.getEntity(); + + if (this.goalSit != null) { + // CraftBukkit - moved into EntityLiving.d(DamageSource, float) + // this.goalSit.setSitting(false); + } + + if (entity != null && !(entity instanceof EntityHuman) && !(entity instanceof EntityArrow)) { + f = (f + 1.0F) / 2.0F; + } + + return super.damageEntity(damagesource, f); + } + } + + public boolean B(Entity entity) { + boolean flag = entity.damageEntity(DamageSource.mobAttack(this), (float) ((int) this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).getValue())); + + if (flag) { + this.a((EntityLiving) this, entity); + } + + return flag; + } + + public void setTamed(boolean flag) { + super.setTamed(flag); + if (flag) { + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(20.0D); + } else { + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(8.0D); + } + + this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue(4.0D); + } + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + Item item = itemstack.getItem(); + + if (this.isTamed()) { + if (!itemstack.isEmpty()) { + if (item instanceof ItemFood) { + ItemFood itemfood = (ItemFood) item; + + if (itemfood.d() && (Float) this.datawatcher.get(EntityWolf.DATA_HEALTH) < 20.0F) { + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + this.heal((float) itemfood.getNutrition(itemstack), org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.EATING); // CraftBukkit + return true; + } + } else if (item instanceof ItemDye) { + EnumColor enumcolor = ((ItemDye) item).d(); + + if (enumcolor != this.getCollarColor()) { + this.setCollarColor(enumcolor); + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + return true; + } + } + } + + if (this.f((EntityLiving) entityhuman) && !this.world.isClientSide && !this.f(itemstack)) { + this.goalSit.setSitting(!this.isSitting()); + this.bg = false; + this.navigation.q(); + this.setGoalTarget((EntityLiving) null, TargetReason.FORGOT_TARGET, true); // CraftBukkit - reason + } + } else if (item == Items.BONE && !this.isAngry()) { + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + if (!this.world.isClientSide) { + // CraftBukkit - added event call and isCancelled check. + if (this.random.nextInt(3) == 0 && !CraftEventFactory.callEntityTameEvent(this, entityhuman).isCancelled()) { + this.c(entityhuman); + this.navigation.q(); + this.setGoalTarget((EntityLiving) null); + this.goalSit.setSitting(true); + this.setHealth(this.getMaxHealth()); // CraftBukkit - 20.0 -> getMaxHealth() + this.s(true); + this.world.broadcastEntityEffect(this, (byte) 7); + } else { + this.s(false); + this.world.broadcastEntityEffect(this, (byte) 6); + } + } + + return true; + } + + return super.a(entityhuman, enumhand); + } + + public boolean f(ItemStack itemstack) { + Item item = itemstack.getItem(); + + return item instanceof ItemFood && ((ItemFood) item).d(); + } + + public int dg() { + return 8; + } + + public boolean isAngry() { + return ((Byte) this.datawatcher.get(EntityWolf.bC) & 2) != 0; + } + + public void setAngry(boolean flag) { + byte b0 = (Byte) this.datawatcher.get(EntityWolf.bC); + + if (flag) { + this.datawatcher.set(EntityWolf.bC, (byte) (b0 | 2)); + } else { + this.datawatcher.set(EntityWolf.bC, (byte) (b0 & -3)); + } + + } + + public EnumColor getCollarColor() { + return EnumColor.fromColorIndex((Integer) this.datawatcher.get(EntityWolf.bI)); + } + + public void setCollarColor(EnumColor enumcolor) { + this.datawatcher.set(EntityWolf.bI, enumcolor.getColorIndex()); + } + + public EntityWolf createChild(EntityAgeable entityageable) { + EntityWolf entitywolf = EntityTypes.WOLF.create(world); // Paper + UUID uuid = this.getOwnerUUID(); + + if (uuid != null) { + entitywolf.setOwnerUUID(uuid); + entitywolf.setTamed(true); + } + + return entitywolf; + } + + public void w(boolean flag) { + this.datawatcher.set(EntityWolf.bH, flag); + } + + public boolean mate(EntityAnimal entityanimal) { + if (entityanimal == this) { + return false; + } else if (!this.isTamed()) { + return false; + } else if (!(entityanimal instanceof EntityWolf)) { + return false; + } else { + EntityWolf entitywolf = (EntityWolf) entityanimal; + + return !entitywolf.isTamed() ? false : (entitywolf.isSitting() ? false : this.isInLove() && entitywolf.isInLove()); + } + } + + public boolean dL() { + return (Boolean) this.datawatcher.get(EntityWolf.bH); + } + + public boolean a(EntityLiving entityliving, EntityLiving entityliving1) { + if (!(entityliving instanceof EntityCreeper) && !(entityliving instanceof EntityGhast)) { + if (entityliving instanceof EntityWolf) { + EntityWolf entitywolf = (EntityWolf) entityliving; + + if (entitywolf.isTamed() && entitywolf.getOwner() == entityliving1) { + return false; + } + } + + return entityliving instanceof EntityHuman && entityliving1 instanceof EntityHuman && !((EntityHuman) entityliving1).a((EntityHuman) entityliving) ? false : !(entityliving instanceof EntityHorseAbstract) || !((EntityHorseAbstract) entityliving).isTamed(); + } else { + return false; + } + } + + public boolean a(EntityHuman entityhuman) { + return !this.isAngry() && super.a(entityhuman); + } + + class a extends PathfinderGoalAvoidTarget { + + private final EntityWolf d; + + public a(EntityWolf entitywolf, Class oclass, float f, double d0, double d1) { + super(entitywolf, oclass, f, d0, d1); + this.d = entitywolf; + } + + public boolean a() { + return super.a() && this.b instanceof EntityLlama ? !this.d.isTamed() && this.a((EntityLlama) this.b) : false; + } + + private boolean a(EntityLlama entityllama) { + return entityllama.getStrength() >= EntityWolf.this.random.nextInt(5); + } + + public void c() { + EntityWolf.this.setGoalTarget((EntityLiving) null); + super.c(); + } + + public void e() { + EntityWolf.this.setGoalTarget((EntityLiving) null); + super.e(); + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityZombie.java b/src/main/java/net/minecraft/server/EntityZombie.java new file mode 100644 index 000000000000..bf2bed0029b0 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityZombie.java @@ -0,0 +1,608 @@ +package net.minecraft.server; + +import java.time.LocalDate; +import java.time.temporal.ChronoField; +import java.util.List; +import java.util.UUID; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.EntityCombustByEntityEvent; +import org.bukkit.event.entity.EntityTargetEvent; +import org.bukkit.event.entity.EntityTransformEvent; +// CraftBukkit end + +public class EntityZombie extends EntityMonster { + + protected static final IAttribute c = (new AttributeRanged((IAttribute) null, "zombie.spawnReinforcements", 0.0D, 0.0D, 1.0D)).a("Spawn Reinforcements Chance"); + private static final UUID a = UUID.fromString("B9766B59-9566-4402-BC1F-2EE2A276D836"); + private final AttributeModifier babyModifier = new AttributeModifier(EntityZombie.a, "Baby speed boost", world.paperConfig.babyZombieMovementSpeed, 1); // Paper - Remove static - Make baby speed configurable + private static final DataWatcherObject bC = DataWatcher.a(EntityZombie.class, DataWatcherRegistry.i); + private static final DataWatcherObject bD = DataWatcher.a(EntityZombie.class, DataWatcherRegistry.b); + private static final DataWatcherObject bE = DataWatcher.a(EntityZombie.class, DataWatcherRegistry.i); private static final DataWatcherObject armsRaised = bE; // Paper - OBFHELPER + public static final DataWatcherObject DROWN_CONVERTING = DataWatcher.a(EntityZombie.class, DataWatcherRegistry.i); private static final DataWatcherObject drowning = DROWN_CONVERTING; // Paper - OBFHELPER + private final PathfinderGoalBreakDoor bG; + private boolean bH; + private int bI; + public int drownedConversionTime; + private float bK; + private float bL; + private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field + private boolean shouldBurnInDay = true; // Paper + + public EntityZombie(EntityTypes entitytypes, World world) { + super(entitytypes, world); + this.bG = new PathfinderGoalBreakDoor(this); + this.bK = -1.0F; + this.setSize(0.6F, 1.95F); + } + + public EntityZombie(World world) { + this(EntityTypes.ZOMBIE, world); + } + + protected void n() { + this.goalSelector.a(4, new EntityZombie.a(Blocks.TURTLE_EGG, this, 1.0D, 3)); + this.goalSelector.a(5, new PathfinderGoalMoveTowardsRestriction(this, 1.0D)); + this.goalSelector.a(8, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this)); + this.l(); + } + + protected void l() { + this.goalSelector.a(2, new PathfinderGoalZombieAttack(this, 1.0D, false)); + this.goalSelector.a(6, new PathfinderGoalMoveThroughVillage(this, 1.0D, false)); + this.goalSelector.a(7, new PathfinderGoalRandomStrollLand(this, 1.0D)); + this.targetSelector.a(1, new PathfinderGoalHurtByTarget(this, true, new Class[] { EntityPigZombie.class})); + this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget<>(this, EntityHuman.class, true)); + if ( world.spigotConfig.zombieAggressiveTowardsVillager ) this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget<>(this, EntityVillager.class, false)); // Spigot + this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget<>(this, EntityIronGolem.class, true)); + this.targetSelector.a(5, new PathfinderGoalNearestAttackableTarget<>(this, EntityTurtle.class, 10, true, false, EntityTurtle.bC)); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.FOLLOW_RANGE).setValue(35.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.23000000417232513D); + this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue(3.0D); + this.getAttributeInstance(GenericAttributes.h).setValue(2.0D); + this.getAttributeMap().b(EntityZombie.c).setValue(this.random.nextDouble() * 0.10000000149011612D); + } + + protected void x_() { + super.x_(); + this.getDataWatcher().register(EntityZombie.bC, false); + this.getDataWatcher().register(EntityZombie.bD, 0); + this.getDataWatcher().register(EntityZombie.bE, false); + this.getDataWatcher().register(EntityZombie.DROWN_CONVERTING, false); + } + + public boolean isDrowning() { return isDrownConverting(); } // Paper - OBFHELPER + public boolean isDrownConverting() { + return (Boolean) this.getDataWatcher().get(EntityZombie.DROWN_CONVERTING); + } + + public void setArmsRaised(boolean raised) { s(raised); } // Paper - OBFHELPER + public void s(boolean flag) { + this.getDataWatcher().set(EntityZombie.bE, flag); + } + + // Paper start + public boolean isArmsRaised() { + return ((Boolean) this.getDataWatcher().get(EntityZombie.armsRaised)).booleanValue(); + } + // Paper end + + public boolean dH() { + return this.bH; + } + + public void t(boolean flag) { + if (this.dz()) { + if (this.bH != flag) { + this.bH = flag; + ((Navigation) this.getNavigation()).a(flag); + if (flag) { + this.goalSelector.a(1, this.bG); + } else { + this.goalSelector.a((PathfinderGoal) this.bG); + } + } + } else if (this.bH) { + this.goalSelector.a((PathfinderGoal) this.bG); + this.bH = false; + } + + } + + protected boolean dz() { + return true; + } + + public boolean isBaby() { + return (Boolean) this.getDataWatcher().get(EntityZombie.bC); + } + + protected int getExpValue(EntityHuman entityhuman) { + if (this.isBaby()) { + this.b_ = (int) ((float) this.b_ * 2.5F); + } + + return super.getExpValue(entityhuman); + } + + public void setBaby(boolean flag) { + this.getDataWatcher().set(EntityZombie.bC, flag); + if (this.world != null && !this.world.isClientSide) { + AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED); + + attributeinstance.c(this.babyModifier); // Paper + if (flag) { + attributeinstance.b(this.babyModifier); // Paper + } + } + + this.v(flag); + } + + public void a(DataWatcherObject datawatcherobject) { + if (EntityZombie.bC.equals(datawatcherobject)) { + this.v(this.isBaby()); + } + + super.a(datawatcherobject); + } + + protected boolean dC() { + return true; + } + + public void tick() { + if (!this.world.isClientSide) { + // CraftBukkit start - Use wall time instead of ticks for conversion + if (this.isDrownConverting() && this.isAlive()) { + int elapsedTicks = MinecraftServer.currentTick - this.lastTick; + this.lastTick = MinecraftServer.currentTick; + this.drownedConversionTime -= elapsedTicks; + // CraftBukkit end + if (this.drownedConversionTime < 0) { + this.dE(); + } + } else if (this.dC()) { + if (this.a(TagsFluid.WATER)) { + ++this.bI; + if (this.bI >= 600) { + this.startDrownedConversion(300); + this.lastTick = MinecraftServer.currentTick; // Paper - Make sure this is set at start of process - GH-1887 + } + } else { + this.bI = -1; + } + } + } + + super.tick(); + } + + public void movementTick() { + boolean flag = this.L_() && this.dq(); + + if (flag) { + ItemStack itemstack = this.getEquipment(EnumItemSlot.HEAD); + + if (!itemstack.isEmpty()) { + if (itemstack.e()) { + itemstack.setDamage(itemstack.getDamage() + this.random.nextInt(2)); + if (itemstack.getDamage() >= itemstack.h()) { + this.c(itemstack); + this.setSlot(EnumItemSlot.HEAD, ItemStack.a); + } + } + + flag = false; + } + + if (flag) { + this.setOnFire(8); + } + } + + super.movementTick(); + } + + public void startDrownedConversion(int i) { + this.drownedConversionTime = i; + this.getDataWatcher().set(EntityZombie.DROWN_CONVERTING, true); + } + + // Paper start + public void stopDrowning() { + this.drownedConversionTime = -1; + this.getDataWatcher().set(EntityZombie.drowning, Boolean.valueOf(false)); + } + // Paper end + + protected void dE() { + this.a((EntityZombie) EntityTypes.DROWNED.create(world)); // Paper + this.world.a((EntityHuman) null, 1040, new BlockPosition((int) this.locX, (int) this.locY, (int) this.locZ), 0); + } + + protected void a(EntityZombie entityzombie) { + if (!this.world.isClientSide && !this.dead) { + entityzombie.u(this); + entityzombie.a(this.dj(), this.dH(), this.isBaby(), this.isNoAI()); + EnumItemSlot[] aenumitemslot = EnumItemSlot.values(); + int i = aenumitemslot.length; + + for (int j = 0; j < i; ++j) { + EnumItemSlot enumitemslot = aenumitemslot[j]; + ItemStack itemstack = this.getEquipment(enumitemslot); + + if (!itemstack.isEmpty()) { + entityzombie.setSlot(enumitemslot, itemstack); + entityzombie.a(enumitemslot, this.c(enumitemslot)); + } + } + + if (this.hasCustomName()) { + entityzombie.setCustomName(this.getCustomName()); + entityzombie.setCustomNameVisible(this.getCustomNameVisible()); + } + + // CraftBukkit start + if (CraftEventFactory.callEntityTransformEvent(this, entityzombie, EntityTransformEvent.TransformReason.DROWNED).isCancelled()) { + return; + } + // CraftBukkit end + if (!new com.destroystokyo.paper.event.entity.EntityTransformedEvent(this.getBukkitEntity(), entityzombie.getBukkitEntity(), com.destroystokyo.paper.event.entity.EntityTransformedEvent.TransformedReason.DROWNED).callEvent()) return; // Paper + this.world.addEntity(entityzombie, CreatureSpawnEvent.SpawnReason.DROWNED); // CraftBukkit - added spawn reason + this.die(); + } + } + + public boolean shouldBurnInDay() { return L_(); } // Paper - OBFHELPER + protected boolean L_() { + return shouldBurnInDay; // Paper + } + + // Paper start + public void setShouldBurnInDay(boolean shouldBurnInDay) { + this.shouldBurnInDay = shouldBurnInDay; + } + // Paper end + + public boolean damageEntity(DamageSource damagesource, float f) { + if (super.damageEntity(damagesource, f)) { + EntityLiving entityliving = this.getGoalTarget(); + + if (entityliving == null && damagesource.getEntity() instanceof EntityLiving) { + entityliving = (EntityLiving) damagesource.getEntity(); + } + + if (entityliving != null && this.world.getDifficulty() == EnumDifficulty.HARD && (double) this.random.nextFloat() < this.getAttributeInstance(EntityZombie.c).getValue() && this.world.getGameRules().getBoolean("doMobSpawning")) { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locY); + int k = MathHelper.floor(this.locZ); + EntityZombie entityzombie = EntityTypes.ZOMBIE.create(world); // Paper + + for (int l = 0; l < 50; ++l) { + int i1 = i + MathHelper.nextInt(this.random, 7, 40) * MathHelper.nextInt(this.random, -1, 1); + int j1 = j + MathHelper.nextInt(this.random, 7, 40) * MathHelper.nextInt(this.random, -1, 1); + int k1 = k + MathHelper.nextInt(this.random, 7, 40) * MathHelper.nextInt(this.random, -1, 1); + + if (this.world.getType(new BlockPosition(i1, j1 - 1, k1)).q() && !this.world.isLightLevel(new BlockPosition(i1, j1, k1), 10)) { // Paper + entityzombie.setPosition((double) i1, (double) j1, (double) k1); + if (!this.world.isPlayerNearby((double) i1, (double) j1, (double) k1, 7.0D) && this.world.a_(entityzombie, entityzombie.getBoundingBox()) && this.world.getCubes(entityzombie, entityzombie.getBoundingBox()) && !this.world.containsLiquid(entityzombie.getBoundingBox())) { + this.world.addEntity(entityzombie, CreatureSpawnEvent.SpawnReason.REINFORCEMENTS); // CraftBukkit + entityzombie.setGoalTarget(entityliving, EntityTargetEvent.TargetReason.REINFORCEMENT_TARGET, true); // CraftBukkit + entityzombie.prepare(this.world.getDamageScaler(new BlockPosition(entityzombie)), (GroupDataEntity) null, (NBTTagCompound) null); + this.getAttributeInstance(EntityZombie.c).b(new AttributeModifier("Zombie reinforcement caller charge", -0.05000000074505806D, 0)); + entityzombie.getAttributeInstance(EntityZombie.c).b(new AttributeModifier("Zombie reinforcement callee charge", -0.05000000074505806D, 0)); + break; + } + } + } + } + + return true; + } else { + return false; + } + } + + public boolean B(Entity entity) { + boolean flag = super.B(entity); + + if (flag) { + float f = this.world.getDamageScaler(new BlockPosition(this)).b(); + + if (this.getItemInMainHand().isEmpty() && this.isBurning() && this.random.nextFloat() < f * 0.3F) { + // CraftBukkit start + EntityCombustByEntityEvent event = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), 2 * (int) f); // PAIL: fixme + this.world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + entity.setOnFire(event.getDuration(), false); + } + // CraftBukkit end + } + } + + return flag; + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_ZOMBIE_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_ZOMBIE_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_ZOMBIE_DEATH; + } + + protected SoundEffect dA() { + return SoundEffects.ENTITY_ZOMBIE_STEP; + } + + protected void a(BlockPosition blockposition, IBlockData iblockdata) { + this.a(this.dA(), 0.15F, 1.0F); + } + + public EnumMonsterType getMonsterType() { + return EnumMonsterType.UNDEAD; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.at; + } + + protected void a(DifficultyDamageScaler difficultydamagescaler) { + super.a(difficultydamagescaler); + if (this.random.nextFloat() < (this.world.getDifficulty() == EnumDifficulty.HARD ? 0.05F : 0.01F)) { + int i = this.random.nextInt(3); + + if (i == 0) { + this.setSlot(EnumItemSlot.MAINHAND, new ItemStack(Items.IRON_SWORD)); + } else { + this.setSlot(EnumItemSlot.MAINHAND, new ItemStack(Items.IRON_SHOVEL)); + } + } + + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + if (this.isBaby()) { + nbttagcompound.setBoolean("IsBaby", true); + } + + nbttagcompound.setBoolean("CanBreakDoors", this.dH()); + nbttagcompound.setInt("InWaterTime", this.isInWater() ? this.bI : -1); + nbttagcompound.setInt("DrownedConversionTime", this.isDrownConverting() ? this.drownedConversionTime : -1); + nbttagcompound.setBoolean("Paper.ShouldBurnInDay", shouldBurnInDay); // Paper + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.getBoolean("IsBaby")) { + this.setBaby(true); + } + + this.t(nbttagcompound.getBoolean("CanBreakDoors")); + this.bI = nbttagcompound.getInt("InWaterTime"); + if (nbttagcompound.hasKeyOfType("DrownedConversionTime", 99) && nbttagcompound.getInt("DrownedConversionTime") > -1) { + this.startDrownedConversion(nbttagcompound.getInt("DrownedConversionTime")); + } + // Paper start + if (nbttagcompound.hasKey("Paper.ShouldBurnInDay")) { + shouldBurnInDay = nbttagcompound.getBoolean("Paper.ShouldBurnInDay"); + } + // Paper end + } + + public void b(EntityLiving entityliving) { + super.b(entityliving); + if ((this.world.getDifficulty() == EnumDifficulty.NORMAL || this.world.getDifficulty() == EnumDifficulty.HARD) && entityliving instanceof EntityVillager) { + if (this.world.getDifficulty() != EnumDifficulty.HARD && this.random.nextBoolean()) { + return; + } + + EntityVillager entityvillager = (EntityVillager) entityliving; + EntityZombieVillager entityzombievillager = EntityTypes.ZOMBIE_VILLAGER.create(world); // Paper + + entityzombievillager.u(entityvillager); + // this.world.kill(entityvillager); // CraftBukkit - moved down + entityzombievillager.prepare(this.world.getDamageScaler(new BlockPosition(entityzombievillager)), new EntityZombie.GroupDataZombie(false), (NBTTagCompound) null); + entityzombievillager.setProfession(entityvillager.getProfession()); + entityzombievillager.setBaby(entityvillager.isBaby()); + entityzombievillager.setNoAI(entityvillager.isNoAI()); + if (entityvillager.hasCustomName()) { + entityzombievillager.setCustomName(entityvillager.getCustomName()); + entityzombievillager.setCustomNameVisible(entityvillager.getCustomNameVisible()); + } + + // CraftBukkit start + if (CraftEventFactory.callEntityTransformEvent(this, entityzombievillager, EntityTransformEvent.TransformReason.INFECTION).isCancelled()) { + return; + } + if (!new com.destroystokyo.paper.event.entity.EntityTransformedEvent(this.getBukkitEntity(), entityvillager.getBukkitEntity(), com.destroystokyo.paper.event.entity.EntityTransformedEvent.TransformedReason.INFECTED).callEvent()) return; // Paper + this.world.kill(entityvillager); // CraftBukkit - from above + this.world.addEntity(entityzombievillager, CreatureSpawnEvent.SpawnReason.INFECTION); // CraftBukkit - add SpawnReason + // CraftBukkit end + this.world.a((EntityHuman) null, 1026, new BlockPosition(this), 0); + } + + } + + public float getHeadHeight() { + float f = 1.74F; + + if (this.isBaby()) { + f = (float) ((double) f - 0.81D); + } + + return f; + } + + protected boolean d(ItemStack itemstack) { + return itemstack.getItem() == Items.EGG && this.isBaby() && this.isPassenger() ? false : super.d(itemstack); + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + Object object = super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + float f = difficultydamagescaler.d(); + + this.p(this.random.nextFloat() < 0.55F * f); + if (object == null) { + object = new EntityZombie.GroupDataZombie(this.world.random.nextFloat() < 0.05F); + } + + if (object instanceof EntityZombie.GroupDataZombie) { + EntityZombie.GroupDataZombie entityzombie_groupdatazombie = (EntityZombie.GroupDataZombie) object; + + if (entityzombie_groupdatazombie.a) { + this.setBaby(true); + if ((double) this.world.random.nextFloat() < 0.05D) { + List list = this.world.a(EntityChicken.class, this.getBoundingBox().grow(5.0D, 3.0D, 5.0D), IEntitySelector.c); + + if (!list.isEmpty()) { + EntityChicken entitychicken = (EntityChicken) list.get(0); + + entitychicken.s(true); + this.startRiding(entitychicken); + } + } else if ((double) this.world.random.nextFloat() < 0.05D) { + EntityChicken entitychicken1 = EntityTypes.CHICKEN.create(world); // Paper + + entitychicken1.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, 0.0F); + entitychicken1.prepare(difficultydamagescaler, (GroupDataEntity) null, (NBTTagCompound) null); + entitychicken1.s(true); + this.world.addEntity(entitychicken1, CreatureSpawnEvent.SpawnReason.MOUNT); // CraftBukkit + this.startRiding(entitychicken1); + } + } + + this.t(this.dz() && this.random.nextFloat() < f * 0.1F); + this.a(difficultydamagescaler); + this.b(difficultydamagescaler); + } + + if (this.getEquipment(EnumItemSlot.HEAD).isEmpty()) { + LocalDate localdate = LocalDate.now(); + int i = localdate.get(ChronoField.DAY_OF_MONTH); + int j = localdate.get(ChronoField.MONTH_OF_YEAR); + + if (j == 10 && i == 31 && this.random.nextFloat() < 0.25F) { + this.setSlot(EnumItemSlot.HEAD, new ItemStack(this.random.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN)); + this.dropChanceArmor[EnumItemSlot.HEAD.b()] = 0.0F; + } + } + + this.a(f); + return (GroupDataEntity) object; + } + + protected void a(boolean flag, boolean flag1, boolean flag2, boolean flag3) { + this.p(flag); + this.t(this.dz() && flag1); + this.a(this.world.getDamageScaler(new BlockPosition(this)).d()); + this.setBaby(flag2); + this.setNoAI(flag3); + } + + protected void a(float f) { + this.getAttributeInstance(GenericAttributes.c).b(new AttributeModifier("Random spawn bonus", this.random.nextDouble() * 0.05000000074505806D, 0)); + double d0 = this.random.nextDouble() * 1.5D * (double) f; + + if (d0 > 1.0D) { + this.getAttributeInstance(GenericAttributes.FOLLOW_RANGE).b(new AttributeModifier("Random zombie-spawn bonus", d0, 2)); + } + + if (this.random.nextFloat() < f * 0.05F) { + this.getAttributeInstance(EntityZombie.c).b(new AttributeModifier("Leader zombie bonus", this.random.nextDouble() * 0.25D + 0.5D, 0)); + this.getAttributeInstance(GenericAttributes.maxHealth).b(new AttributeModifier("Leader zombie bonus", this.random.nextDouble() * 3.0D + 1.0D, 2)); + this.t(this.dz()); + } + + } + + public void v(boolean flag) { + this.v(flag ? 0.5F : 1.0F); + } + + public final void setSize(float f, float f1) { + boolean flag = this.bK > 0.0F && this.bL > 0.0F; + + this.bK = f; + this.bL = f1; + if (!flag) { + this.v(1.0F); + } + + } + + protected final void v(float f) { + super.setSize(this.bK * f, this.bL * f); + } + + public double aI() { + return this.isBaby() ? 0.0D : -0.45D; + } + + public void die(DamageSource damagesource) { + // super.die(damagesource); // CraftBukkit + if (damagesource.getEntity() instanceof EntityCreeper) { + EntityCreeper entitycreeper = (EntityCreeper) damagesource.getEntity(); + + if (entitycreeper.isPowered() && entitycreeper.canCauseHeadDrop()) { + entitycreeper.setCausedHeadDrop(); + ItemStack itemstack = this.dB(); + + if (!itemstack.isEmpty()) { + this.a_(itemstack); + } + } + } + super.die(damagesource); // CraftBukkit - moved from above + + } + + protected ItemStack dB() { + return new ItemStack(Items.ZOMBIE_HEAD); + } + + class a extends PathfinderGoalRemoveBlock { + + a(Block block, EntityCreature entitycreature, double d0, int i) { + super(block, entitycreature, d0, i); + } + + public void a(GeneratorAccess generatoraccess, BlockPosition blockposition) { + generatoraccess.a((EntityHuman) null, blockposition, SoundEffects.ENTITY_ZOMBIE_DESTROY_EGG, SoundCategory.HOSTILE, 0.5F, 0.9F + EntityZombie.this.random.nextFloat() * 0.2F); + } + + public void a(World world, BlockPosition blockposition) { + world.a((EntityHuman) null, blockposition, SoundEffects.ENTITY_TURTLE_EGG_BREAK, SoundCategory.BLOCKS, 0.7F, 0.9F + world.random.nextFloat() * 0.2F); + } + + public double g() { + return 1.3D; + } + } + + public class GroupDataZombie implements GroupDataEntity { + + public boolean a; + + private GroupDataZombie(boolean flag) { + this.a = flag; + } + } +} diff --git a/src/main/java/net/minecraft/server/EntityZombieHusk.java b/src/main/java/net/minecraft/server/EntityZombieHusk.java new file mode 100644 index 000000000000..0cca7b6d518b --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityZombieHusk.java @@ -0,0 +1,64 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class EntityZombieHusk extends EntityZombie { + + public EntityZombieHusk(World world) { + super(EntityTypes.HUSK, world); + } + + public boolean a(GeneratorAccess generatoraccess, boolean flag) { + return super.a(generatoraccess, flag) && (flag || generatoraccess.e(new BlockPosition(this))); + } + + protected boolean L_() { + return false; + } + + protected SoundEffect D() { + return SoundEffects.ENTITY_HUSK_AMBIENT; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_HUSK_HURT; + } + + protected SoundEffect cs() { + return SoundEffects.ENTITY_HUSK_DEATH; + } + + protected SoundEffect dA() { + return SoundEffects.ENTITY_HUSK_STEP; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.ay; + } + + public boolean B(Entity entity) { + boolean flag = super.B(entity); + + if (flag && this.getItemInMainHand().isEmpty() && entity instanceof EntityLiving) { + float f = this.world.getDamageScaler(new BlockPosition(this)).b(); + + ((EntityLiving) entity).addEffect(new MobEffect(MobEffects.HUNGER, 140 * (int) f), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit + } + + return flag; + } + + protected boolean dC() { + return true; + } + + protected void dE() { + this.a(EntityTypes.ZOMBIE.create(world)); // Paper + this.world.a((EntityHuman) null, 1041, new BlockPosition((int) this.locX, (int) this.locY, (int) this.locZ), 0); + } + + protected ItemStack dB() { + return ItemStack.a; + } +} diff --git a/src/main/java/net/minecraft/server/EntityZombieVillager.java b/src/main/java/net/minecraft/server/EntityZombieVillager.java new file mode 100644 index 000000000000..fb96743af286 --- /dev/null +++ b/src/main/java/net/minecraft/server/EntityZombieVillager.java @@ -0,0 +1,213 @@ +package net.minecraft.server; + +import java.util.UUID; +import javax.annotation.Nullable; +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityTransformEvent; +// CraftBukkit end + +public class EntityZombieVillager extends EntityZombie { + + public static final DataWatcherObject CONVERTING = DataWatcher.a(EntityZombieVillager.class, DataWatcherRegistry.i); + private static final DataWatcherObject b = DataWatcher.a(EntityZombieVillager.class, DataWatcherRegistry.b); + public int conversionTime; + private UUID bD; + private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field + + public EntityZombieVillager(World world) { + super(EntityTypes.ZOMBIE_VILLAGER, world); + } + + protected void x_() { + super.x_(); + this.datawatcher.register(EntityZombieVillager.CONVERTING, false); + this.datawatcher.register(EntityZombieVillager.b, 0); + } + + public void setProfession(int i) { + this.datawatcher.set(EntityZombieVillager.b, i); + } + + public int getProfession() { + return Math.max((Integer) this.datawatcher.get(EntityZombieVillager.b) % 6, 0); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("Profession", this.getProfession()); + nbttagcompound.setInt("ConversionTime", this.isConverting() ? this.conversionTime : -1); + if (this.bD != null) { + nbttagcompound.a("ConversionPlayer", this.bD); + } + + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setProfession(nbttagcompound.getInt("Profession")); + if (nbttagcompound.hasKeyOfType("ConversionTime", 99) && nbttagcompound.getInt("ConversionTime") > -1) { + this.startConversion(nbttagcompound.b("ConversionPlayer") ? nbttagcompound.a("ConversionPlayer") : null, nbttagcompound.getInt("ConversionTime")); + } + + } + + @Nullable + public GroupDataEntity prepare(DifficultyDamageScaler difficultydamagescaler, @Nullable GroupDataEntity groupdataentity, @Nullable NBTTagCompound nbttagcompound) { + this.setProfession(this.world.random.nextInt(6)); + return super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); + } + + public void tick() { + if (!this.world.isClientSide && this.isConverting() && this.isAlive()) { // CraftBukkit + int i = this.dK(); + // CraftBukkit start - Use wall time instead of ticks for villager conversion + int elapsedTicks = MinecraftServer.currentTick - this.lastTick; + this.lastTick = MinecraftServer.currentTick; + i *= elapsedTicks; + // CraftBukkit end + + this.conversionTime -= i; + if (this.conversionTime <= 0) { + this.dJ(); + } + } + + super.tick(); + } + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (itemstack.getItem() == Items.GOLDEN_APPLE && this.hasEffect(MobEffects.WEAKNESS)) { + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + if (!this.world.isClientSide) { + this.startConversion(entityhuman.getUniqueID(), this.random.nextInt(2401) + 3600); + } + + return true; + } else { + return false; + } + } + + protected boolean dC() { + return false; + } + + public boolean isTypeNotPersistent() { + return !this.isConverting(); + } + + public boolean isConverting() { + return (Boolean) this.getDataWatcher().get(EntityZombieVillager.CONVERTING); + } + + public void startConversion(@Nullable UUID uuid, int i) { + this.bD = uuid; + this.conversionTime = i; + this.getDataWatcher().set(EntityZombieVillager.CONVERTING, true); + // CraftBukkit start + this.removeEffect(MobEffects.WEAKNESS, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONVERSION); + this.addEffect(new MobEffect(MobEffects.INCREASE_DAMAGE, i, Math.min(this.world.getDifficulty().a() - 1, 0)), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONVERSION); + // CraftBukkit end + this.world.broadcastEntityEffect(this, (byte) 16); + } + + protected void dJ() { + EntityVillager entityvillager = EntityTypes.VILLAGER.create(world); // Paper + + entityvillager.u(this); + entityvillager.setProfession(this.getProfession()); + entityvillager.a(this.world.getDamageScaler(new BlockPosition(entityvillager)), (GroupDataEntity) null, (NBTTagCompound) null, false); + entityvillager.dC(); + if (this.isBaby()) { + entityvillager.setAgeRaw(-24000); + } + + // this.world.kill(this); // CraftBukkit - moved down + entityvillager.setNoAI(this.isNoAI()); + if (this.hasCustomName()) { + entityvillager.setCustomName(this.getCustomName()); + entityvillager.setCustomNameVisible(this.getCustomNameVisible()); + } + + // CraftBukkit start + if (CraftEventFactory.callEntityTransformEvent(this, entityvillager, EntityTransformEvent.TransformReason.CURED).isCancelled()) { + return; + } + if (!new com.destroystokyo.paper.event.entity.EntityTransformedEvent(this.getBukkitEntity(), entityvillager.getBukkitEntity(), com.destroystokyo.paper.event.entity.EntityTransformedEvent.TransformedReason.CURED).callEvent()) return; // Paper + this.world.kill(this); // CraftBukkit - from above + this.world.addEntity(entityvillager, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CURED); // CraftBukkit - add SpawnReason + // CraftBukkit end + if (this.bD != null) { + EntityHuman entityhuman = this.world.b(this.bD); + + if (entityhuman instanceof EntityPlayer) { + CriterionTriggers.r.a((EntityPlayer) entityhuman, this, entityvillager); + } + } + + entityvillager.addEffect(new MobEffect(MobEffects.CONFUSION, 200, 0), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONVERSION); // CraftBukkit + this.world.a((EntityHuman) null, 1027, new BlockPosition((int) this.locX, (int) this.locY, (int) this.locZ), 0); + } + + protected int dK() { + int i = 1; + + if (this.random.nextFloat() < 0.01F) { + int j = 0; + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + for (int k = (int) this.locX - 4; k < (int) this.locX + 4 && j < 14; ++k) { + for (int l = (int) this.locY - 4; l < (int) this.locY + 4 && j < 14; ++l) { + for (int i1 = (int) this.locZ - 4; i1 < (int) this.locZ + 4 && j < 14; ++i1) { + Block block = this.world.getType(blockposition_mutableblockposition.c(k, l, i1)).getBlock(); + + if (block == Blocks.IRON_BARS || block instanceof BlockBed) { + if (this.random.nextFloat() < 0.3F) { + ++i; + } + + ++j; + } + } + } + } + } + + return i; + } + + protected float cE() { + return this.isBaby() ? (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 2.0F : (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F; + } + + public SoundEffect D() { + return SoundEffects.ENTITY_ZOMBIE_VILLAGER_AMBIENT; + } + + public SoundEffect d(DamageSource damagesource) { + return SoundEffects.ENTITY_ZOMBIE_VILLAGER_HURT; + } + + public SoundEffect cs() { + return SoundEffects.ENTITY_ZOMBIE_VILLAGER_DEATH; + } + + public SoundEffect dA() { + return SoundEffects.ENTITY_ZOMBIE_VILLAGER_STEP; + } + + @Nullable + protected MinecraftKey getDefaultLootTable() { + return LootTables.az; + } + + protected ItemStack dB() { + return ItemStack.a; + } +} diff --git a/src/main/java/net/minecraft/server/EnumCreatureType.java b/src/main/java/net/minecraft/server/EnumCreatureType.java new file mode 100644 index 000000000000..42f6a6a93a80 --- /dev/null +++ b/src/main/java/net/minecraft/server/EnumCreatureType.java @@ -0,0 +1,36 @@ +package net.minecraft.server; + +public enum EnumCreatureType { + + MONSTER(IMonster.class, 70, false, false), CREATURE(EntityAnimal.class, 10, true, true), AMBIENT(EntityAmbient.class, 15, true, false), WATER_CREATURE(EntityWaterAnimal.class, 15, true, false); + + private final Class e; + private final int f; + private final boolean g; + private final boolean h; + + private EnumCreatureType(Class oclass, int i, boolean flag, boolean flag1) { + this.e = oclass; + this.f = i; + this.g = flag; + this.h = flag1; + } + + public boolean matches(Entity entity) { return innerClass().isAssignableFrom(entity.getClass()); } // Paper + public Class innerClass() { return this.a(); } // Paper - OBFHELPER + public Class a() { + return this.e; + } + + public int b() { + return this.f; + } + + public boolean c() { + return this.g; + } + + public boolean d() { + return this.h; + } +} diff --git a/src/main/java/net/minecraft/server/EnumDirection.java b/src/main/java/net/minecraft/server/EnumDirection.java new file mode 100644 index 000000000000..ce71811271ff --- /dev/null +++ b/src/main/java/net/minecraft/server/EnumDirection.java @@ -0,0 +1,349 @@ +package net.minecraft.server; + +import com.google.common.collect.Iterators; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Iterator; +import java.util.Map; +import java.util.Random; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import javax.annotation.Nullable; + +public enum EnumDirection implements INamable { + + DOWN(0, 1, -1, "down", EnumDirection.EnumAxisDirection.NEGATIVE, EnumDirection.EnumAxis.Y, new BaseBlockPosition(0, -1, 0)), UP(1, 0, -1, "up", EnumDirection.EnumAxisDirection.POSITIVE, EnumDirection.EnumAxis.Y, new BaseBlockPosition(0, 1, 0)), NORTH(2, 3, 2, "north", EnumDirection.EnumAxisDirection.NEGATIVE, EnumDirection.EnumAxis.Z, new BaseBlockPosition(0, 0, -1)), SOUTH(3, 2, 0, "south", EnumDirection.EnumAxisDirection.POSITIVE, EnumDirection.EnumAxis.Z, new BaseBlockPosition(0, 0, 1)), WEST(4, 5, 1, "west", EnumDirection.EnumAxisDirection.NEGATIVE, EnumDirection.EnumAxis.X, new BaseBlockPosition(-1, 0, 0)), EAST(5, 4, 3, "east", EnumDirection.EnumAxisDirection.POSITIVE, EnumDirection.EnumAxis.X, new BaseBlockPosition(1, 0, 0)); + + private final int g; + private final int h; + private final int i; + private final String j; + private final EnumDirection.EnumAxis k; + private final EnumDirection.EnumAxisDirection l; + private final BaseBlockPosition m; + private static final EnumDirection[] n = values(); + private static final Map o = (Map) Arrays.stream(EnumDirection.n).collect(Collectors.toMap(EnumDirection::j, (enumdirection) -> { + return enumdirection; + })); + private static final EnumDirection[] p = (EnumDirection[]) Arrays.stream(EnumDirection.n).sorted(Comparator.comparingInt((enumdirection) -> { + return enumdirection.g; + })).toArray((i) -> { + return new EnumDirection[i]; + }); + private static final EnumDirection[] q = (EnumDirection[]) Arrays.stream(EnumDirection.n).filter((enumdirection) -> { + return enumdirection.k().c(); + }).sorted(Comparator.comparingInt((enumdirection) -> { + return enumdirection.i; + })).toArray((i) -> { + return new EnumDirection[i]; + }); + + private EnumDirection(int i, int j, int k, String s, EnumDirection.EnumAxisDirection enumdirection_enumaxisdirection, EnumDirection.EnumAxis enumdirection_enumaxis, BaseBlockPosition baseblockposition) { + this.g = i; + this.i = k; + this.h = j; + this.j = s; + this.k = enumdirection_enumaxis; + this.l = enumdirection_enumaxisdirection; + this.m = baseblockposition; + } + + public static EnumDirection[] a(Entity entity) { + float f = entity.g(1.0F) * 0.017453292F; + float f1 = -entity.h(1.0F) * 0.017453292F; + float f2 = MathHelper.sin(f); + float f3 = MathHelper.cos(f); + float f4 = MathHelper.sin(f1); + float f5 = MathHelper.cos(f1); + boolean flag = f4 > 0.0F; + boolean flag1 = f2 < 0.0F; + boolean flag2 = f5 > 0.0F; + float f6 = flag ? f4 : -f4; + float f7 = flag1 ? -f2 : f2; + float f8 = flag2 ? f5 : -f5; + float f9 = f6 * f3; + float f10 = f8 * f3; + EnumDirection enumdirection = flag ? EnumDirection.EAST : EnumDirection.WEST; + EnumDirection enumdirection1 = flag1 ? EnumDirection.UP : EnumDirection.DOWN; + EnumDirection enumdirection2 = flag2 ? EnumDirection.SOUTH : EnumDirection.NORTH; + + return f6 > f8 ? (f7 > f9 ? a(enumdirection1, enumdirection, enumdirection2) : (f10 > f7 ? a(enumdirection, enumdirection2, enumdirection1) : a(enumdirection, enumdirection1, enumdirection2))) : (f7 > f10 ? a(enumdirection1, enumdirection2, enumdirection) : (f9 > f7 ? a(enumdirection2, enumdirection, enumdirection1) : a(enumdirection2, enumdirection1, enumdirection))); + } + + private static EnumDirection[] a(EnumDirection enumdirection, EnumDirection enumdirection1, EnumDirection enumdirection2) { + return new EnumDirection[] { enumdirection, enumdirection1, enumdirection2, enumdirection2.opposite(), enumdirection1.opposite(), enumdirection.opposite()}; + } + + public int a() { + return this.g; + } + + public int get2DRotationValue() { + return this.i; + } + + public final EnumDirection.EnumAxisDirection getAxisDirection() { return c(); } // Paper - OBFHELPER + public EnumDirection.EnumAxisDirection c() { + return this.l; + } + + public EnumDirection opposite() { + return fromType1(this.h); + } + + public final EnumDirection rotateY() { return e(); } // Paper - OBFHELPER + public EnumDirection e() { + switch (this) { + case NORTH: + return EnumDirection.EAST; + case EAST: + return EnumDirection.SOUTH; + case SOUTH: + return EnumDirection.WEST; + case WEST: + return EnumDirection.NORTH; + default: + throw new IllegalStateException("Unable to get Y-rotated facing of " + this); + } + } + + public EnumDirection f() { + switch (this) { + case NORTH: + return EnumDirection.WEST; + case EAST: + return EnumDirection.NORTH; + case SOUTH: + return EnumDirection.EAST; + case WEST: + return EnumDirection.SOUTH; + default: + throw new IllegalStateException("Unable to get CCW facing of " + this); + } + } + + public int getAdjacentX() { + return this.k == EnumDirection.EnumAxis.X ? this.l.a() : 0; + } + + public int getAdjacentY() { + return this.k == EnumDirection.EnumAxis.Y ? this.l.a() : 0; + } + + public int getAdjacentZ() { + return this.k == EnumDirection.EnumAxis.Z ? this.l.a() : 0; + } + + public String j() { + return this.j; + } + + public EnumDirection.EnumAxis k() { + return this.k; + } + + public static EnumDirection fromType1(int i) { + return EnumDirection.p[MathHelper.a(i % EnumDirection.p.length)]; + } + + public static EnumDirection fromType2(int i) { + return EnumDirection.q[MathHelper.a(i % EnumDirection.q.length)]; + } + + public static EnumDirection fromAngle(double d0) { + return fromType2(MathHelper.floor(d0 / 90.0D + 0.5D) & 3); + } + + public static EnumDirection a(EnumDirection.EnumAxis enumdirection_enumaxis, EnumDirection.EnumAxisDirection enumdirection_enumaxisdirection) { + switch (enumdirection_enumaxis) { + case X: + return enumdirection_enumaxisdirection == EnumDirection.EnumAxisDirection.POSITIVE ? EnumDirection.EAST : EnumDirection.WEST; + case Y: + return enumdirection_enumaxisdirection == EnumDirection.EnumAxisDirection.POSITIVE ? EnumDirection.UP : EnumDirection.DOWN; + case Z: + default: + return enumdirection_enumaxisdirection == EnumDirection.EnumAxisDirection.POSITIVE ? EnumDirection.SOUTH : EnumDirection.NORTH; + } + } + + public float l() { + return (float) ((this.i & 3) * 90); + } + + public static EnumDirection a(Random random) { + return values()[random.nextInt(values().length)]; + } + + public static EnumDirection a(double d0, double d1, double d2) { + return a((float) d0, (float) d1, (float) d2); + } + + public static EnumDirection a(float f, float f1, float f2) { + EnumDirection enumdirection = EnumDirection.NORTH; + float f3 = Float.MIN_VALUE; + EnumDirection[] aenumdirection = EnumDirection.n; + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection1 = aenumdirection[j]; + float f4 = f * (float) enumdirection1.m.getX() + f1 * (float) enumdirection1.m.getY() + f2 * (float) enumdirection1.m.getZ(); + + if (f4 > f3) { + f3 = f4; + enumdirection = enumdirection1; + } + } + + return enumdirection; + } + + public String toString() { + return this.j; + } + + public String getName() { + return this.j; + } + + public static EnumDirection a(EnumDirection.EnumAxisDirection enumdirection_enumaxisdirection, EnumDirection.EnumAxis enumdirection_enumaxis) { + EnumDirection[] aenumdirection = values(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + + if (enumdirection.c() == enumdirection_enumaxisdirection && enumdirection.k() == enumdirection_enumaxis) { + return enumdirection; + } + } + + throw new IllegalArgumentException("No such direction: " + enumdirection_enumaxisdirection + " " + enumdirection_enumaxis); + } + + public static enum EnumDirectionLimit implements Iterable, Predicate { + + HORIZONTAL(new EnumDirection[] { EnumDirection.NORTH, EnumDirection.EAST, EnumDirection.SOUTH, EnumDirection.WEST}, new EnumDirection.EnumAxis[] { EnumDirection.EnumAxis.X, EnumDirection.EnumAxis.Z}), VERTICAL(new EnumDirection[] { EnumDirection.UP, EnumDirection.DOWN}, new EnumDirection.EnumAxis[] { EnumDirection.EnumAxis.Y}); + + private final EnumDirection[] c; + private final EnumDirection.EnumAxis[] d; + + private EnumDirectionLimit(EnumDirection[] aenumdirection, EnumDirection.EnumAxis[] aenumdirection_enumaxis) { + this.c = aenumdirection; + this.d = aenumdirection_enumaxis; + } + + public EnumDirection a(Random random) { + return this.c[random.nextInt(this.c.length)]; + } + + public boolean test(@Nullable EnumDirection enumdirection) { + return enumdirection != null && enumdirection.k().d() == this; + } + + public Iterator iterator() { + return Iterators.forArray(this.c); + } + } + + public static enum EnumAxisDirection { + + POSITIVE(1, "Towards positive"), NEGATIVE(-1, "Towards negative"); + + private final int c; + private final String d; + + private EnumAxisDirection(int i, String s) { + this.c = i; + this.d = s; + } + + public final int getOffset() { return a(); } // Paper - OBFHELPER + public int a() { + return this.c; + } + + public String toString() { + return this.d; + } + } + + public static enum EnumAxis implements Predicate, INamable { + + X("x") { + public int a(int i, int j, int k) { + return i; + } + + public double a(double d0, double d1, double d2) { + return d0; + } + }, + Y("y") { + public int a(int i, int j, int k) { + return j; + } + + public double a(double d0, double d1, double d2) { + return d1; + } + }, + Z("z") { + public int a(int i, int j, int k) { + return k; + } + + public double a(double d0, double d1, double d2) { + return d2; + } + }; + + private static final Map d = (Map) Arrays.stream(values()).collect(Collectors.toMap(EnumDirection.EnumAxis::a, (enumdirection_enumaxis) -> { + return enumdirection_enumaxis; + })); + private final String e; + + private EnumAxis(String s) { + this.e = s; + } + + public String a() { + return this.e; + } + + public boolean b() { + return this == EnumDirection.EnumAxis.Y; + } + + public boolean c() { + return this == EnumDirection.EnumAxis.X || this == EnumDirection.EnumAxis.Z; + } + + public String toString() { + return this.e; + } + + public boolean test(@Nullable EnumDirection enumdirection) { + return enumdirection != null && enumdirection.k() == this; + } + + public EnumDirection.EnumDirectionLimit d() { + switch (this) { + case X: + case Z: + return EnumDirection.EnumDirectionLimit.HORIZONTAL; + case Y: + return EnumDirection.EnumDirectionLimit.VERTICAL; + default: + throw new Error("Someone's been tampering with the universe!"); + } + } + + public String getName() { + return this.e; + } + + public abstract int a(int i, int j, int k); + + public abstract double a(double d0, double d1, double d2); + } +} diff --git a/src/main/java/net/minecraft/server/EnumItemSlot.java b/src/main/java/net/minecraft/server/EnumItemSlot.java new file mode 100644 index 000000000000..8f4b5dca94d9 --- /dev/null +++ b/src/main/java/net/minecraft/server/EnumItemSlot.java @@ -0,0 +1,57 @@ +package net.minecraft.server; + +public enum EnumItemSlot { + + MAINHAND(EnumItemSlot.Function.HAND, 0, 0, "mainhand"), OFFHAND(EnumItemSlot.Function.HAND, 1, 5, "offhand"), FEET(EnumItemSlot.Function.ARMOR, 0, 1, "feet"), LEGS(EnumItemSlot.Function.ARMOR, 1, 2, "legs"), CHEST(EnumItemSlot.Function.ARMOR, 2, 3, "chest"), HEAD(EnumItemSlot.Function.ARMOR, 3, 4, "head"); + + private final EnumItemSlot.Function g; + private final int h; + private final int i; + private final String j; + + private EnumItemSlot(EnumItemSlot.Function enumitemslot_function, int i, int j, String s) { + this.g = enumitemslot_function; + this.h = i; + this.i = j; + this.j = s; + } + + public EnumItemSlot.Function getType() { return this.a(); } // Paper - OBFHELPER + public EnumItemSlot.Function a() { + return this.g; + } + + public int b() { + return this.h; + } + + public int c() { + return this.i; + } + + public String getSlotName() { + return this.j; + } + + public static EnumItemSlot fromName(String s) { + EnumItemSlot[] aenumitemslot = values(); + int i = aenumitemslot.length; + + for (int j = 0; j < i; ++j) { + EnumItemSlot enumitemslot = aenumitemslot[j]; + + if (enumitemslot.getSlotName().equals(s)) { + return enumitemslot; + } + } + + throw new IllegalArgumentException("Invalid slot '" + s + "'"); + } + + public static enum Function { + + HAND, ARMOR; + + private Function() {} + } +} diff --git a/src/main/java/net/minecraft/server/ExpirableListEntry.java b/src/main/java/net/minecraft/server/ExpirableListEntry.java new file mode 100644 index 000000000000..d0e0dc3a5b92 --- /dev/null +++ b/src/main/java/net/minecraft/server/ExpirableListEntry.java @@ -0,0 +1,97 @@ +package net.minecraft.server; + +import com.google.gson.JsonObject; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import javax.annotation.Nullable; + +public abstract class ExpirableListEntry extends JsonListEntry { + + public static final SimpleDateFormat a = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + protected final Date b; + protected final String c; + protected final Date d; + protected final String e; + + public ExpirableListEntry(T t0, @Nullable Date date, @Nullable String s, @Nullable Date date1, @Nullable String s1) { + super(t0); + this.b = date == null ? new Date() : date; + this.c = s == null ? "(Unknown)" : s; + this.d = date1; + this.e = s1 == null ? "Banned by an operator." : s1; + } + + protected ExpirableListEntry(T t0, JsonObject jsonobject) { + super(checkExpiry(t0, jsonobject), jsonobject); + + Date date; + + try { + date = jsonobject.has("created") ? ExpirableListEntry.a.parse(jsonobject.get("created").getAsString()) : new Date(); + } catch (ParseException parseexception) { + date = new Date(); + } + + this.b = date; + this.c = jsonobject.has("source") ? jsonobject.get("source").getAsString() : "(Unknown)"; + + Date date1; + + try { + date1 = jsonobject.has("expires") ? ExpirableListEntry.a.parse(jsonobject.get("expires").getAsString()) : null; + } catch (ParseException parseexception1) { + date1 = null; + } + + this.d = date1; + this.e = jsonobject.has("reason") ? jsonobject.get("reason").getAsString() : "Banned by an operator."; + } + + public String getSource() { + return this.c; + } + + public Date getExpires() { + return this.d; + } + + public String getReason() { + return this.e; + } + + public abstract IChatBaseComponent e(); + + boolean hasExpired() { + return this.d == null ? false : this.d.before(new Date()); + } + + protected void a(JsonObject jsonobject) { + jsonobject.addProperty("created", ExpirableListEntry.a.format(this.b)); + jsonobject.addProperty("source", this.c); + jsonobject.addProperty("expires", this.d == null ? "forever" : ExpirableListEntry.a.format(this.d)); + jsonobject.addProperty("reason", this.e); + } + + // CraftBukkit start + public Date getCreated() { + return this.b; + } + + private static T checkExpiry(T object, JsonObject jsonobject) { + Date expires = null; + + try { + expires = jsonobject.has("expires") ? a.parse(jsonobject.get("expires").getAsString()) : null; + } catch (ParseException ex) { + // Guess we don't have a date + } + + if (expires == null || expires.after(new Date())) { + return object; + } else { + return null; + } + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/ExpiringMap.java b/src/main/java/net/minecraft/server/ExpiringMap.java new file mode 100644 index 000000000000..bf6095137a65 --- /dev/null +++ b/src/main/java/net/minecraft/server/ExpiringMap.java @@ -0,0 +1,220 @@ +package net.minecraft.server; + +import it.unimi.dsi.fastutil.longs.Long2LongLinkedOpenHashMap; +import it.unimi.dsi.fastutil.longs.Long2LongMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectCollection; +import it.unimi.dsi.fastutil.objects.ObjectIterator; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.LongFunction; + +public class ExpiringMap extends Long2ObjectMaps.SynchronizedMap { // paper - synchronize accesss + private final int a; + private final Long2LongMap ttl = new Long2LongLinkedOpenHashMap(); // Paper + private static final boolean DEBUG_EXPIRING_MAP = Boolean.getBoolean("debug.expiringmap"); + + public ExpiringMap(int i, int j) { + super(new Long2ObjectOpenHashMap<>(i)); // Paper + this.a = j; + } + + // Paper start + private void setAccess(long i) { a(i); } // Paper - OBFHELPER + private void a(long i) { + synchronized (this.sync) { + long j = System.currentTimeMillis(); // Paper + this.ttl.put(i, j); + if (!registered) { + registered = true; + MinecraftServer.getServer().expiringMaps.add(this); + } + } + } + + @Override + public T compute(long l, BiFunction biFunction) { + setAccess(l); + return super.compute(l, biFunction); + } + + @Override + public T putIfAbsent(long l, T t) { + setAccess(l); + return super.putIfAbsent(l, t); + } + + @Override + public T computeIfPresent(long l, BiFunction biFunction) { + setAccess(l); + return super.computeIfPresent(l, biFunction); + } + + @Override + public T computeIfAbsent(long l, LongFunction longFunction) { + setAccess(l); + return super.computeIfAbsent(l, longFunction); + } + + + @Override + public boolean replace(long l, T t, T v1) { + setAccess(l); + return super.replace(l, t, v1); + } + + @Override + public T replace(long l, T t) { + setAccess(l); + return super.replace(l, t); + } + + @Override + public T putIfAbsent(Long aLong, T t) { + setAccess(aLong); + return super.putIfAbsent(aLong, t); + } + + @Override + public boolean replace(Long aLong, T t, T v1) { + setAccess(aLong); + return super.replace(aLong, t, v1); + } + + @Override + public T replace(Long aLong, T t) { + setAccess(aLong); + return super.replace(aLong, t); + } + + @Override + public T computeIfAbsent(Long aLong, Function function) { + setAccess(aLong); + return super.computeIfAbsent(aLong, function); + } + + @Override + public T computeIfPresent(Long aLong, BiFunction biFunction) { + setAccess(aLong); + return super.computeIfPresent(aLong, biFunction); + } + + @Override + public T compute(Long aLong, BiFunction biFunction) { + setAccess(aLong); + return super.compute(aLong, biFunction); + } + + @Override + public void clear() { + synchronized (this.sync) { + ttl.clear(); + super.clear(); + } + } + + private boolean registered = false; + + // Break clean to its own method to be ticked + boolean clean() { + synchronized (this.sync) { + long now = System.currentTimeMillis(); + ObjectIterator objectiterator = this.ttl.long2LongEntrySet().iterator(); // Paper + + while (objectiterator.hasNext()) { + Long2LongMap.Entry entry = objectiterator.next(); // Paper + T object = super.get(entry.getLongKey()); // Paper + if (now - entry.getLongValue() <= (long) this.a) { + break; + } + + if (object != null && this.a(object)) { + super.remove(entry.getLongKey()); + objectiterator.remove(); + } + } + int ttlSize = this.ttl.size(); + int thisSize = this.size(); + if (ttlSize < thisSize) { + if (DEBUG_EXPIRING_MAP) { + MinecraftServer.LOGGER.warn("WARNING: ExpiringMap desync (ttl:" + ttlSize + " < actual:" + thisSize + ")"); + } + try { + for (Entry entry : this.long2ObjectEntrySet()) { + ttl.putIfAbsent(entry.getLongKey(), now); + } + } catch (Exception ignored) { + } // Ignore any como's + } else if (ttlSize > this.size()) { + if (DEBUG_EXPIRING_MAP) { + MinecraftServer.LOGGER.warn("WARNING: ExpiringMap desync (ttl:" + ttlSize + " > actual:" + thisSize + ")"); + } + try { + this.ttl.long2LongEntrySet().removeIf(entry -> !this.containsKey(entry.getLongKey())); + } catch (Exception ignored) { + } // Ignore any como's + } + if (isEmpty()) { + registered = false; + return true; + } + return false; + } + // Paper end + } + + protected boolean a(T var1) { + return true; + } + + public T put(long i, T object) { + this.a(i); + return (T)super.put(i, object); + } + + public T put(Long olong, T object) { + this.a(olong); + return (T)super.put(olong, object); + } + + public T get(long i) { + // Paper start - don't setAccess unless a hit + T t = super.get(i); + if (t != null) { + this.setAccess(i); + } + return t; + // Paper end + } + + public void putAll(Map var1) { + throw new RuntimeException("Not implemented"); + } + + public T remove(long var1) { + throw new RuntimeException("Not implemented"); + } + + public T remove(Object var1) { + throw new RuntimeException("Not implemented"); + } + + // Paper start + /* + // CraftBukkit start + @Override + public T computeIfAbsent(long l, LongFunction lf) { + this.ttl.put(l, SystemUtils.getMonotonicMillis()); // Paper + return super.computeIfAbsent(l, lf); + } + + @Override + public ObjectCollection values() { + cleanup(); + return super.values(); + } + // CraftBukkit end + */ // Paper end +} diff --git a/src/main/java/net/minecraft/server/Explosion.java b/src/main/java/net/minecraft/server/Explosion.java new file mode 100644 index 000000000000..5591ccb3b0b5 --- /dev/null +++ b/src/main/java/net/minecraft/server/Explosion.java @@ -0,0 +1,397 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.Location; +import org.bukkit.event.block.BlockExplodeEvent; +// CraftBukkit end + +public class Explosion { + + private final boolean a; + private final boolean b; + private final Random c = new Random(); + private final World world; + private final double posX; + private final double posY; + private final double posZ; + public final Entity source; + private final float size; + private DamageSource j; + private final List blocks = Lists.newArrayList(); + private final Map l = Maps.newHashMap(); + public boolean wasCanceled = false; // CraftBukkit - add field + + public Explosion(World world, @Nullable Entity entity, double d0, double d1, double d2, float f, boolean flag, boolean flag1) { + this.world = world; + this.source = entity; + this.size = (float) Math.max(f, 0.0); // CraftBukkit - clamp bad values + this.posX = d0; + this.posY = d1; + this.posZ = d2; + this.a = flag; + this.b = flag1; + this.j = DamageSource.explosion(this); + } + + public void a() { + // CraftBukkit start + if (this.size < 0.1F) { + return; + } + // CraftBukkit end + Set set = Sets.newHashSet(); + boolean flag = true; + + int i; + int j; + + for (int k = 0; k < 16; ++k) { + for (i = 0; i < 16; ++i) { + for (j = 0; j < 16; ++j) { + if (k == 0 || k == 15 || i == 0 || i == 15 || j == 0 || j == 15) { + double d0 = (double) ((float) k / 15.0F * 2.0F - 1.0F); + double d1 = (double) ((float) i / 15.0F * 2.0F - 1.0F); + double d2 = (double) ((float) j / 15.0F * 2.0F - 1.0F); + double d3 = Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + + d0 /= d3; + d1 /= d3; + d2 /= d3; + float f = this.size * (0.7F + this.world.random.nextFloat() * 0.6F); + double d4 = this.posX; + double d5 = this.posY; + double d6 = this.posZ; + + for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) { + BlockPosition blockposition = new BlockPosition(d4, d5, d6); + IBlockData iblockdata = this.world.getType(blockposition); + Fluid fluid = this.world.getFluid(blockposition); + + if (!iblockdata.isAir() || !fluid.e()) { + float f2 = Math.max(iblockdata.getBlock().getDurability(), fluid.l()); + + if (this.source != null) { + f2 = this.source.a(this, this.world, blockposition, iblockdata, fluid, f2); + } + + f -= (f2 + 0.3F) * 0.3F; + } + + if (f > 0.0F && (this.source == null || this.source.a(this, this.world, blockposition, iblockdata, f)) && blockposition.getY() < 256 && blockposition.getY() >= 0) { // CraftBukkit - don't wrap explosions + set.add(blockposition); + } + + d4 += d0 * 0.30000001192092896D; + d5 += d1 * 0.30000001192092896D; + d6 += d2 * 0.30000001192092896D; + } + } + } + } + } + + this.blocks.addAll(set); + float f3 = this.size * 2.0F; + + i = MathHelper.floor(this.posX - (double) f3 - 1.0D); + j = MathHelper.floor(this.posX + (double) f3 + 1.0D); + int l = MathHelper.floor(this.posY - (double) f3 - 1.0D); + int i1 = MathHelper.floor(this.posY + (double) f3 + 1.0D); + int j1 = MathHelper.floor(this.posZ - (double) f3 - 1.0D); + int k1 = MathHelper.floor(this.posZ + (double) f3 + 1.0D); + // Paper start - Fix lag from explosions processing dead entities + List list = this.world.getEntities(this.source, new AxisAlignedBB((double) i, (double) l, (double) j1, (double) j, (double) i1, (double) k1), new com.google.common.base.Predicate() { + @Override + public boolean apply(Entity entity) { + return IEntitySelector.canAITarget().test(entity) && !entity.dead; + } + }); + // Paper end + Vec3D vec3d = new Vec3D(this.posX, this.posY, this.posZ); + + for (int l1 = 0; l1 < list.size(); ++l1) { + Entity entity = (Entity) list.get(l1); + + if (!entity.bL()) { + double d7 = entity.e(this.posX, this.posY, this.posZ) / (double) f3; + + if (d7 <= 1.0D) { + double d8 = entity.locX - this.posX; + double d9 = entity.locY + (double) entity.getHeadHeight() - this.posY; + double d10 = entity.locZ - this.posZ; + double d11 = (double) MathHelper.sqrt(d8 * d8 + d9 * d9 + d10 * d10); + + if (d11 != 0.0D) { + d8 /= d11; + d9 /= d11; + d10 /= d11; + double d12 = this.getBlockDensity(vec3d, entity.getBoundingBox()); // Paper - Optimize explosions + double d13 = (1.0D - d7) * d12; + + // CraftBukkit start + // entity.damageEntity(this.b(), (float) ((int) ((d13 * d13 + d13) / 2.0D * 7.0D * (double) f3 + 1.0D))); + CraftEventFactory.entityDamage = source; + entity.forceExplosionKnockback = false; + boolean wasDamaged = entity.damageEntity(this.b(), (float) ((int) ((d13 * d13 + d13) / 2.0D * 7.0D * (double) f3 + 1.0D))); + CraftEventFactory.entityDamage = null; + if (!wasDamaged && !(entity instanceof EntityTNTPrimed || entity instanceof EntityFallingBlock) && !entity.forceExplosionKnockback) { + continue; + } + // CraftBukkit end + double d14 = d13; + + if (entity instanceof EntityLiving) { + d14 = entity instanceof EntityHuman && world.paperConfig.disableExplosionKnockback ? 0 : EnchantmentProtection.a((EntityLiving) entity, d13); // Paper - Disable explosion knockback + } + + entity.motX += d8 * d14; + entity.motY += d9 * d14; + entity.motZ += d10 * d14; + if (entity instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) entity; + + if (!entityhuman.isSpectator() && (!entityhuman.u() && !world.paperConfig.disableExplosionKnockback || !entityhuman.abilities.isFlying)) { // Paper - Disable explosion knockback + this.l.put(entityhuman, new Vec3D(d8 * d13, d9 * d13, d10 * d13)); + } + } + } + } + } + } + + } + + public void a(boolean flag) { + this.world.a((EntityHuman) null, this.posX, this.posY, this.posZ, SoundEffects.ENTITY_GENERIC_EXPLODE, SoundCategory.BLOCKS, 4.0F, (1.0F + (this.world.random.nextFloat() - this.world.random.nextFloat()) * 0.2F) * 0.7F); + if (this.size >= 2.0F && this.b) { + this.world.addParticle(Particles.t, this.posX, this.posY, this.posZ, 1.0D, 0.0D, 0.0D); + } else { + this.world.addParticle(Particles.u, this.posX, this.posY, this.posZ, 1.0D, 0.0D, 0.0D); + } + + Iterator iterator; + BlockPosition blockposition; + + if (this.b) { + // CraftBukkit start + org.bukkit.World bworld = this.world.getWorld(); + org.bukkit.entity.Entity explode = this.source == null ? null : this.source.getBukkitEntity(); + Location location = new Location(bworld, this.posX, this.posY, this.posZ); + + List blockList = Lists.newArrayList(); + for (int i1 = this.blocks.size() - 1; i1 >= 0; i1--) { + BlockPosition cpos = (BlockPosition) this.blocks.get(i1); + org.bukkit.block.Block bblock = bworld.getBlockAt(cpos.getX(), cpos.getY(), cpos.getZ()); + if (bblock.getType() != org.bukkit.Material.AIR) { + blockList.add(bblock); + } + } + + boolean cancelled; + List bukkitBlocks; + float yield; + + if (explode != null) { + EntityExplodeEvent event = new EntityExplodeEvent(explode, location, blockList, 1.0F / this.size); + this.world.getServer().getPluginManager().callEvent(event); + cancelled = event.isCancelled(); + bukkitBlocks = event.blockList(); + yield = event.getYield(); + } else { + BlockExplodeEvent event = new BlockExplodeEvent(location.getBlock(), blockList, 1.0F / this.size); + this.world.getServer().getPluginManager().callEvent(event); + cancelled = event.isCancelled(); + bukkitBlocks = event.blockList(); + yield = event.getYield(); + } + + this.blocks.clear(); + + for (org.bukkit.block.Block bblock : bukkitBlocks) { + BlockPosition coords = new BlockPosition(bblock.getX(), bblock.getY(), bblock.getZ()); + blocks.add(coords); + } + + if (cancelled) { + this.wasCanceled = true; + return; + } + // CraftBukkit end + iterator = this.blocks.iterator(); + + while (iterator.hasNext()) { + blockposition = (BlockPosition) iterator.next(); + IBlockData iblockdata = this.world.getType(blockposition); + Block block = iblockdata.getBlock(); + + if (flag) { + double d0 = (double) ((float) blockposition.getX() + this.world.random.nextFloat()); + double d1 = (double) ((float) blockposition.getY() + this.world.random.nextFloat()); + double d2 = (double) ((float) blockposition.getZ() + this.world.random.nextFloat()); + double d3 = d0 - this.posX; + double d4 = d1 - this.posY; + double d5 = d2 - this.posZ; + double d6 = (double) MathHelper.sqrt(d3 * d3 + d4 * d4 + d5 * d5); + + d3 /= d6; + d4 /= d6; + d5 /= d6; + double d7 = 0.5D / (d6 / (double) this.size + 0.1D); + + d7 *= (double) (this.world.random.nextFloat() * this.world.random.nextFloat() + 0.3F); + d3 *= d7; + d4 *= d7; + d5 *= d7; + this.world.addParticle(Particles.J, (d0 + this.posX) / 2.0D, (d1 + this.posY) / 2.0D, (d2 + this.posZ) / 2.0D, d3, d4, d5); + this.world.addParticle(Particles.M, d0, d1, d2, d3, d4, d5); + } + + if (!iblockdata.isAir()) { + if (block.a(this)) { + // CraftBukkit - add yield + iblockdata.dropNaturally(this.world, blockposition, yield, 0); + } + + this.world.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 3); + block.wasExploded(this.world, blockposition, this); + } + } + } + + if (this.a) { + iterator = this.blocks.iterator(); + + while (iterator.hasNext()) { + blockposition = (BlockPosition) iterator.next(); + if (this.world.getType(blockposition).isAir() && this.world.getType(blockposition.down()).f(this.world, blockposition.down()) && this.c.nextInt(3) == 0) { + // CraftBukkit start - Ignition by explosion + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this).isCancelled()) { + this.world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); + } + // CraftBukkit end + } + } + } + + } + + public DamageSource b() { + return this.j; + } + + public void a(DamageSource damagesource) { + this.j = damagesource; + } + + public Map c() { + return this.l; + } + + @Nullable + public EntityLiving getSource() { + // CraftBukkit start - obtain Fireball shooter for explosion tracking + return this.source == null ? null : (this.source instanceof EntityTNTPrimed ? ((EntityTNTPrimed) this.source).getSource() : (this.source instanceof EntityLiving ? (EntityLiving) this.source : (this.source instanceof EntityFireball ? ((EntityFireball) this.source).shooter : null))); + // CraftBukkit end + } + + public void clearBlocks() { + this.blocks.clear(); + } + + public List getBlocks() { + return this.blocks; + } + + // Paper start - Optimize explosions + private float getBlockDensity(Vec3D vec3d, AxisAlignedBB aabb) { + if (!this.world.paperConfig.optimizeExplosions) { + return this.world.a(vec3d, aabb); + } + CacheKey key = new CacheKey(this, aabb); + Float blockDensity = this.world.explosionDensityCache.get(key); + if (blockDensity == null) { + blockDensity = this.world.a(vec3d, aabb); + this.world.explosionDensityCache.put(key, blockDensity); + } + + return blockDensity; + } + + static class CacheKey { + private final World world; + private final double posX, posY, posZ; + private final double minX, minY, minZ; + private final double maxX, maxY, maxZ; + + public CacheKey(Explosion explosion, AxisAlignedBB aabb) { + this.world = explosion.world; + this.posX = explosion.posX; + this.posY = explosion.posY; + this.posZ = explosion.posZ; + this.minX = aabb.minX; + this.minY = aabb.minY; + this.minZ = aabb.minZ; + this.maxX = aabb.maxX; + this.maxY = aabb.maxY; + this.maxZ = aabb.maxZ; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CacheKey cacheKey = (CacheKey) o; + + if (Double.compare(cacheKey.posX, posX) != 0) return false; + if (Double.compare(cacheKey.posY, posY) != 0) return false; + if (Double.compare(cacheKey.posZ, posZ) != 0) return false; + if (Double.compare(cacheKey.minX, minX) != 0) return false; + if (Double.compare(cacheKey.minY, minY) != 0) return false; + if (Double.compare(cacheKey.minZ, minZ) != 0) return false; + if (Double.compare(cacheKey.maxX, maxX) != 0) return false; + if (Double.compare(cacheKey.maxY, maxY) != 0) return false; + if (Double.compare(cacheKey.maxZ, maxZ) != 0) return false; + return world.equals(cacheKey.world); + } + + @Override + public int hashCode() { + int result; + long temp; + result = world.hashCode(); + temp = Double.doubleToLongBits(posX); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(posY); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(posZ); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(minX); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(minY); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(minZ); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(maxX); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(maxY); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(maxZ); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } + } + // Paper end +} diff --git a/src/main/java/net/minecraft/server/FileIOThread.java b/src/main/java/net/minecraft/server/FileIOThread.java new file mode 100644 index 000000000000..570624600d18 --- /dev/null +++ b/src/main/java/net/minecraft/server/FileIOThread.java @@ -0,0 +1,84 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Collections; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class FileIOThread implements Runnable { + + private static final Logger a = LogManager.getLogger(); + private static final FileIOThread b = new FileIOThread(); + private final List c = Collections.synchronizedList(Lists.newArrayList()); private List getThreadedIOQueue() { return c; } // Paper - OBFHELPER + private volatile long d; + private volatile long e; + private volatile boolean f; + + private FileIOThread() { + Thread thread = new Thread(this, "File IO Thread"); + + thread.setUncaughtExceptionHandler(new ThreadNamedUncaughtExceptionHandler(FileIOThread.a)); + thread.setPriority(1); + thread.start(); + } + + public static FileIOThread a() { + return FileIOThread.b; + } + + public void run() { + while (true) { + this.c(); + } + } + + private void c() { + for (int i = 0; i < this.c.size(); ++i) { + IAsyncChunkSaver iasyncchunksaver = (IAsyncChunkSaver) this.c.get(i); + boolean flag; + + //synchronized (iasyncchunksaver) { // Paper - remove synchronized + flag = iasyncchunksaver.a(); + //} // Paper + + if (!flag) { + this.c.remove(i--); + ++this.e; + } + + if (com.destroystokyo.paper.PaperConfig.enableFileIOThreadSleep) { // Paper + try { + Thread.sleep(this.f ? 0L : 1L); // Paper + } catch (InterruptedException interruptedexception) { + interruptedexception.printStackTrace(); + }} // Paper + } + + if (this.c.isEmpty()) { + try { + Thread.sleep(25L); + } catch (InterruptedException interruptedexception1) { + interruptedexception1.printStackTrace(); + } + } + + } + + public void a(IAsyncChunkSaver iasyncchunksaver) { + if (!this.c.contains(iasyncchunksaver)) { + ++this.d; + this.c.add(iasyncchunksaver); + } + } + + public void b() throws InterruptedException { + this.f = true; + + while(!this.getThreadedIOQueue().isEmpty()) { // Paper - check actual list size + Thread.sleep(10L); + } + + this.f = false; + } +} diff --git a/src/main/java/net/minecraft/server/FluidTypeFlowing.java b/src/main/java/net/minecraft/server/FluidTypeFlowing.java new file mode 100644 index 000000000000..bbffeae3d0bd --- /dev/null +++ b/src/main/java/net/minecraft/server/FluidTypeFlowing.java @@ -0,0 +1,525 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.objects.Object2ByteLinkedOpenHashMap; +import it.unimi.dsi.fastutil.shorts.Short2BooleanMap; +import it.unimi.dsi.fastutil.shorts.Short2BooleanOpenHashMap; +import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; +import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +// CraftBukkit start +import org.bukkit.block.BlockFace; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.block.BlockFromToEvent; +import org.bukkit.event.block.FluidLevelChangeEvent; +// CraftBukkit end + +public abstract class FluidTypeFlowing extends FluidType { + + public static final BlockStateBoolean FALLING = BlockProperties.h; + public static final BlockStateInteger LEVEL = BlockProperties.ag; + private static final ThreadLocal> e = ThreadLocal.withInitial(() -> { + Object2ByteLinkedOpenHashMap object2bytelinkedopenhashmap = new Object2ByteLinkedOpenHashMap(200) { + protected void rehash(int i) {} + }; + + object2bytelinkedopenhashmap.defaultReturnValue((byte) 127); + return object2bytelinkedopenhashmap; + }); + + public FluidTypeFlowing() {} + + protected void a(BlockStateList.a blockstatelist_a) { + blockstatelist_a.a(FluidTypeFlowing.FALLING); + } + + public Vec3D a(IWorldReader iworldreader, BlockPosition blockposition, Fluid fluid) { + double d0 = 0.0D; + double d1 = 0.0D; + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + Vec3D vec3d; + + try { + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + + blockposition_b.g(blockposition).c(enumdirection); + Fluid fluid1 = iworldreader.getFluid(blockposition_b); + + if (this.g(fluid1)) { + float f = fluid1.getHeight(); + float f1 = 0.0F; + + if (f == 0.0F) { + if (!iworldreader.getType(blockposition_b).getMaterial().isSolid()) { + Fluid fluid2 = iworldreader.getFluid(blockposition_b.down()); + + if (this.g(fluid2)) { + f = fluid2.getHeight(); + if (f > 0.0F) { + f1 = fluid.getHeight() - (f - 0.8888889F); + } + } + } + } else if (f > 0.0F) { + f1 = fluid.getHeight() - f; + } + + if (f1 != 0.0F) { + d0 += (double) ((float) enumdirection.getAdjacentX() * f1); + d1 += (double) ((float) enumdirection.getAdjacentZ() * f1); + } + } + } + + Vec3D vec3d1 = new Vec3D(d0, 0.0D, d1); + + if ((Boolean) fluid.get(FluidTypeFlowing.FALLING)) { + Iterator iterator1 = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator1.hasNext()) { + EnumDirection enumdirection1 = (EnumDirection) iterator1.next(); + + blockposition_b.g(blockposition).c(enumdirection1); + if (this.a((IBlockAccess) iworldreader, (BlockPosition) blockposition_b, enumdirection1) || this.a((IBlockAccess) iworldreader, blockposition_b.up(), enumdirection1)) { + vec3d1 = vec3d1.a().add(0.0D, -6.0D, 0.0D); + break; + } + } + } + + vec3d = vec3d1.a(); + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + + return vec3d; + } + + private boolean g(Fluid fluid) { + return fluid.e() || fluid.c().a((FluidType) this); + } + + protected boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + IBlockData iblockdata = iblockaccess.getType(blockposition); + Block block = iblockdata.getBlock(); + Fluid fluid = iblockaccess.getFluid(blockposition); + + if (fluid.c().a((FluidType) this)) { + return false; + } else if (enumdirection == EnumDirection.UP) { + return true; + } else if (iblockdata.getMaterial() == Material.ICE) { + return false; + } else { + boolean flag = Block.b(block) || block instanceof BlockStairs; + + return !flag && iblockdata.c(iblockaccess, blockposition, enumdirection) == EnumBlockFaceShape.SOLID; + } + } + + protected void a(GeneratorAccess generatoraccess, BlockPosition blockposition, Fluid fluid) { + if (!fluid.e()) { + IBlockData iblockdata = generatoraccess.getType(blockposition); + BlockPosition blockposition1 = blockposition.down(); + IBlockData iblockdata1 = generatoraccess.getType(blockposition1); + Fluid fluid1 = this.a((IWorldReader) generatoraccess, blockposition1, iblockdata1); + + if (this.a(generatoraccess, blockposition, iblockdata, EnumDirection.DOWN, blockposition1, iblockdata1, generatoraccess.getFluid(blockposition1), fluid1.c())) { + // CraftBukkit start + org.bukkit.block.Block source = CraftBlock.at(generatoraccess, blockposition); + BlockFromToEvent event = new BlockFromToEvent(source, BlockFace.DOWN); + generatoraccess.getMinecraftWorld().getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + // CraftBukkit end + this.a(generatoraccess, blockposition1, iblockdata1, EnumDirection.DOWN, fluid1); + if (this.a((IWorldReader) generatoraccess, blockposition) >= 3) { + this.a(generatoraccess, blockposition, fluid, iblockdata); + } + } else if (fluid.d() || !this.a((IBlockAccess) generatoraccess, fluid1.c(), blockposition, iblockdata, blockposition1, iblockdata1)) { + this.a(generatoraccess, blockposition, fluid, iblockdata); + } + + } + } + + private void a(GeneratorAccess generatoraccess, BlockPosition blockposition, Fluid fluid, IBlockData iblockdata) { + int i = fluid.g() - this.c((IWorldReader) generatoraccess); + + if ((Boolean) fluid.get(FluidTypeFlowing.FALLING)) { + i = 7; + } + + if (i > 0) { + Map map = this.b(generatoraccess, blockposition, iblockdata); + Iterator iterator = map.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + EnumDirection enumdirection = (EnumDirection) entry.getKey(); + Fluid fluid1 = (Fluid) entry.getValue(); + BlockPosition blockposition1 = blockposition.shift(enumdirection); + IBlockData iblockdata1 = generatoraccess.getTypeIfLoaded(blockposition1); // Paper + if (iblockdata1 == null) continue; // Paper + + if (this.a(generatoraccess, blockposition, iblockdata, enumdirection, blockposition1, iblockdata1, generatoraccess.getFluid(blockposition1), fluid1.c())) { + // CraftBukkit start + org.bukkit.block.Block source = CraftBlock.at(generatoraccess, blockposition); + BlockFromToEvent event = new BlockFromToEvent(source, org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(enumdirection)); + generatoraccess.getMinecraftWorld().getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + continue; + } + // CraftBukkit end + this.a(generatoraccess, blockposition1, iblockdata1, enumdirection, fluid1); + } + } + + } + } + + protected Fluid a(IWorldReader iworldreader, BlockPosition blockposition, IBlockData iblockdata) { + int i = 0; + int j = 0; + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + BlockPosition blockposition1 = blockposition.shift(enumdirection); + IBlockData iblockdata1 = iworldreader.getTypeIfLoaded(blockposition1); // Paper + if (iblockdata1 == null) continue; // Paper + Fluid fluid = iblockdata1.s(); + + if (fluid.c().a((FluidType) this) && this.a(enumdirection, (IBlockAccess) iworldreader, blockposition, iblockdata, blockposition1, iblockdata1)) { + if (fluid.d()) { + ++j; + } + + i = Math.max(i, fluid.g()); + } + } + + if (this.g() && j >= 2) { + IBlockData iblockdata2 = iworldreader.getType(blockposition.down()); + Fluid fluid1 = iblockdata2.s(); + + if (iblockdata2.getMaterial().isBuildable() || this.h(fluid1)) { + return this.a(false); + } + } + + BlockPosition blockposition2 = blockposition.up(); + IBlockData iblockdata3 = iworldreader.getType(blockposition2); + Fluid fluid2 = iblockdata3.s(); + + if (!fluid2.e() && fluid2.c().a((FluidType) this) && this.a(EnumDirection.UP, (IBlockAccess) iworldreader, blockposition, iblockdata, blockposition2, iblockdata3)) { + return this.a(8, true); + } else { + int k = i - this.c(iworldreader); + + return k <= 0 ? FluidTypes.EMPTY.i() : this.a(k, false); + } + } + + private boolean a(EnumDirection enumdirection, IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, BlockPosition blockposition1, IBlockData iblockdata1) { + Object2ByteLinkedOpenHashMap object2bytelinkedopenhashmap; + + if (!iblockdata.getBlock().s() && !iblockdata1.getBlock().s()) { + object2bytelinkedopenhashmap = (Object2ByteLinkedOpenHashMap) FluidTypeFlowing.e.get(); + } else { + object2bytelinkedopenhashmap = null; + } + + Block.a block_a; + + if (object2bytelinkedopenhashmap != null) { + block_a = new Block.a(iblockdata, iblockdata1, enumdirection); + byte b0 = object2bytelinkedopenhashmap.getAndMoveToFirst(block_a); + + if (b0 != 127) { + return b0 != 0; + } + } else { + block_a = null; + } + + VoxelShape voxelshape = iblockdata.getCollisionShape(iblockaccess, blockposition); + VoxelShape voxelshape1 = iblockdata1.getCollisionShape(iblockaccess, blockposition1); + boolean flag = !VoxelShapes.b(voxelshape, voxelshape1, enumdirection); + + if (object2bytelinkedopenhashmap != null) { + if (object2bytelinkedopenhashmap.size() == 200) { + object2bytelinkedopenhashmap.removeLastByte(); + } + + object2bytelinkedopenhashmap.putAndMoveToFirst(block_a, (byte) (flag ? 1 : 0)); + } + + return flag; + } + + public abstract FluidType e(); + + public Fluid a(int i, boolean flag) { + return (Fluid) ((Fluid) this.e().i().set(FluidTypeFlowing.LEVEL, i)).set(FluidTypeFlowing.FALLING, flag); + } + + public abstract FluidType f(); + + public Fluid a(boolean flag) { + return (Fluid) this.f().i().set(FluidTypeFlowing.FALLING, flag); + } + + protected abstract boolean g(); + + protected void a(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, EnumDirection enumdirection, Fluid fluid) { + if (iblockdata.getBlock() instanceof IFluidContainer) { + ((IFluidContainer) iblockdata.getBlock()).place(generatoraccess, blockposition, iblockdata, fluid); + } else { + if (!iblockdata.isAir()) { + this.a(generatoraccess, blockposition, iblockdata); + } + + generatoraccess.setTypeAndData(blockposition, fluid.i(), 3); + } + + } + + protected abstract void a(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata); + + private static short a(BlockPosition blockposition, BlockPosition blockposition1) { + int i = blockposition1.getX() - blockposition.getX(); + int j = blockposition1.getZ() - blockposition.getZ(); + + return (short) ((i + 128 & 255) << 8 | j + 128 & 255); + } + + protected int a(IWorldReader iworldreader, BlockPosition blockposition, int i, EnumDirection enumdirection, IBlockData iblockdata, BlockPosition blockposition1, Short2ObjectMap> short2objectmap, Short2BooleanMap short2booleanmap) { + int j = 1000; + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection1 = (EnumDirection) iterator.next(); + + if (enumdirection1 != enumdirection) { + BlockPosition blockposition2 = blockposition.shift(enumdirection1); + short short0 = a(blockposition1, blockposition2); + // Paper start - avoid loading chunks + Pair pair = short2objectmap.get(short0); + if (pair == null) { + IBlockData iblockdatax = iworldreader.getTypeIfLoaded(blockposition2); + if (iblockdatax == null) { + continue; + } + + pair = Pair.of(iblockdatax, iblockdatax.s()); + short2objectmap.put(short0, pair); + + } + // Paper end + + IBlockData iblockdata1 = (IBlockData) pair.getFirst(); + Fluid fluid = (Fluid) pair.getSecond(); + + if (this.a(iworldreader, this.e(), blockposition, iblockdata, enumdirection1, blockposition2, iblockdata1, fluid)) { + boolean flag = short2booleanmap.computeIfAbsent(short0, (k) -> { + BlockPosition blockposition3 = blockposition2.down(); + IBlockData iblockdata2 = iworldreader.getType(blockposition3); + + return this.a((IBlockAccess) iworldreader, this.e(), blockposition2, iblockdata1, blockposition3, iblockdata2); + }); + + if (flag) { + return i; + } + + if (i < this.b(iworldreader)) { + int k = this.a(iworldreader, blockposition2, i + 1, enumdirection1.opposite(), iblockdata1, blockposition1, short2objectmap, short2booleanmap); + + if (k < j) { + j = k; + } + } + } + } + } + + return j; + } + + private boolean a(IBlockAccess iblockaccess, FluidType fluidtype, BlockPosition blockposition, IBlockData iblockdata, BlockPosition blockposition1, IBlockData iblockdata1) { + return !this.a(EnumDirection.DOWN, iblockaccess, blockposition, iblockdata, blockposition1, iblockdata1) ? false : (iblockdata1.s().c().a((FluidType) this) ? true : this.a(iblockaccess, blockposition1, iblockdata1, fluidtype)); + } + + private boolean a(IBlockAccess iblockaccess, FluidType fluidtype, BlockPosition blockposition, IBlockData iblockdata, EnumDirection enumdirection, BlockPosition blockposition1, IBlockData iblockdata1, Fluid fluid) { + return !this.h(fluid) && this.a(enumdirection, iblockaccess, blockposition, iblockdata, blockposition1, iblockdata1) && this.a(iblockaccess, blockposition1, iblockdata1, fluidtype); + } + + private boolean h(Fluid fluid) { + return fluid.c().a((FluidType) this) && fluid.d(); + } + + protected abstract int b(IWorldReader iworldreader); + + private int a(IWorldReader iworldreader, BlockPosition blockposition) { + int i = 0; + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + BlockPosition blockposition1 = blockposition.shift(enumdirection); + Fluid fluid = iworldreader.getFluid(blockposition1); + + if (this.h(fluid)) { + ++i; + } + } + + return i; + } + + protected Map b(IWorldReader iworldreader, BlockPosition blockposition, IBlockData iblockdata) { + int i = 1000; + Map map = Maps.newEnumMap(EnumDirection.class); + Short2ObjectMap> short2objectmap = new Short2ObjectOpenHashMap(); + Short2BooleanOpenHashMap short2booleanopenhashmap = new Short2BooleanOpenHashMap(); + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + BlockPosition blockposition1 = blockposition.shift(enumdirection); + short short0 = a(blockposition, blockposition1); + // Paper start + Pair pair = (Pair) short2objectmap.get(short0); + if (pair == null) { + IBlockData iblockdatax = iworldreader.getTypeIfLoaded(blockposition1); + if (iblockdatax == null) continue; + + pair = Pair.of(iblockdatax, iblockdatax.s()); + short2objectmap.put(short0, pair); + } + // Paper end + IBlockData iblockdata1 = (IBlockData) pair.getFirst(); + Fluid fluid = (Fluid) pair.getSecond(); + Fluid fluid1 = this.a(iworldreader, blockposition1, iblockdata1); + + if (this.a(iworldreader, fluid1.c(), blockposition, iblockdata, enumdirection, blockposition1, iblockdata1, fluid)) { + BlockPosition blockposition2 = blockposition1.down(); + boolean flag = short2booleanopenhashmap.computeIfAbsent(short0, (j) -> { + IBlockData iblockdata2 = iworldreader.getType(blockposition2); + + return this.a((IBlockAccess) iworldreader, this.e(), blockposition1, iblockdata1, blockposition2, iblockdata2); + }); + int j; + + if (flag) { + j = 0; + } else { + j = this.a(iworldreader, blockposition1, 1, enumdirection.opposite(), iblockdata1, blockposition, short2objectmap, short2booleanopenhashmap); + } + + if (j < i) { + map.clear(); + } + + if (j <= i) { + map.put(enumdirection, fluid1); + i = j; + } + } + } + + return map; + } + + private boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, FluidType fluidtype) { + Block block = iblockdata.getBlock(); + + if (block instanceof IFluidContainer) { + return ((IFluidContainer) block).canPlace(iblockaccess, blockposition, iblockdata, fluidtype); + } else if (!(block instanceof BlockDoor) && block != Blocks.SIGN && block != Blocks.LADDER && block != Blocks.SUGAR_CANE && block != Blocks.BUBBLE_COLUMN) { + Material material = iblockdata.getMaterial(); + + return material != Material.PORTAL && material != Material.STRUCTURE_VOID && material != Material.WATER_PLANT && material != Material.REPLACEABLE_WATER_PLANT ? !material.isSolid() : false; + } else { + return false; + } + } + + protected boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, EnumDirection enumdirection, BlockPosition blockposition1, IBlockData iblockdata1, Fluid fluid, FluidType fluidtype) { + return fluid.a(fluidtype, enumdirection) && this.a(enumdirection, iblockaccess, blockposition, iblockdata, blockposition1, iblockdata1) && this.a(iblockaccess, blockposition1, iblockdata1, fluidtype); + } + + protected abstract int c(IWorldReader iworldreader); + + protected int a(World world, Fluid fluid, Fluid fluid1) { + return this.a((IWorldReader) world); + } + + public void a(World world, BlockPosition blockposition, Fluid fluid) { + if (!fluid.d()) { + Fluid fluid1 = this.a((IWorldReader) world, blockposition, world.getType(blockposition)); + int i = this.a(world, fluid, fluid1); + + if (fluid1.e()) { + fluid = fluid1; + // CraftBukkit start + FluidLevelChangeEvent event = CraftEventFactory.callFluidLevelChangeEvent(world, blockposition, Blocks.AIR.getBlockData()); + if (event.isCancelled()) { + return; + } + world.setTypeAndData(blockposition, ((CraftBlockData) event.getNewData()).getState(), 3); + // CraftBukkit end + } else if (!fluid1.equals(fluid)) { + fluid = fluid1; + IBlockData iblockdata = fluid1.i(); + // CraftBukkit start + FluidLevelChangeEvent event = CraftEventFactory.callFluidLevelChangeEvent(world, blockposition, iblockdata); + if (event.isCancelled()) { + return; + } + world.setTypeAndData(blockposition, ((CraftBlockData) event.getNewData()).getState(), 2); + // CraftBukkit end + world.getFluidTickList().a(blockposition, fluid1.c(), i); + world.applyPhysics(blockposition, iblockdata.getBlock()); + } + } + + this.a((GeneratorAccess) world, blockposition, fluid); + } + + protected static int e(Fluid fluid) { + return fluid.d() ? 0 : 8 - Math.min(fluid.g(), 8) + ((Boolean) fluid.get(FluidTypeFlowing.FALLING) ? 8 : 0); + } + + public float a(Fluid fluid) { + return (float) fluid.g() / 9.0F; + } +} diff --git a/src/main/java/net/minecraft/server/FluidTypeLava.java b/src/main/java/net/minecraft/server/FluidTypeLava.java new file mode 100644 index 000000000000..8bb4f3b78e87 --- /dev/null +++ b/src/main/java/net/minecraft/server/FluidTypeLava.java @@ -0,0 +1,210 @@ +package net.minecraft.server; + +import java.util.Random; + +public abstract class FluidTypeLava extends FluidTypeFlowing { + + public FluidTypeLava() {} + + public FluidType e() { + return FluidTypes.FLOWING_LAVA; + } + + public FluidType f() { + return FluidTypes.LAVA; + } + + public Item b() { + return Items.LAVA_BUCKET; + } + + public void b(World world, BlockPosition blockposition, Fluid fluid, Random random) { + if (world.getGameRules().getBoolean("doFireTick")) { + int i = random.nextInt(3); + + if (i > 0) { + BlockPosition blockposition1 = blockposition; + + for (int j = 0; j < i; ++j) { + blockposition1 = blockposition1.a(random.nextInt(3) - 1, 1, random.nextInt(3) - 1); + if (!world.p(blockposition1)) { + return; + } + + IBlockData iblockdata = world.getType(blockposition1); + + if (iblockdata.isAir()) { + if (this.a((IWorldReader) world, blockposition1)) { + // CraftBukkit start - Prevent lava putting something on fire + if (world.getType(blockposition1) != Blocks.FIRE) { + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, blockposition1, blockposition).isCancelled()) { + continue; + } + } + // CraftBukkit end + world.setTypeUpdate(blockposition1, Blocks.FIRE.getBlockData()); + return; + } + } else if (iblockdata.getMaterial().isSolid()) { + return; + } + } + } else { + for (int k = 0; k < 3; ++k) { + BlockPosition blockposition2 = blockposition.a(random.nextInt(3) - 1, 0, random.nextInt(3) - 1); + + if (!world.p(blockposition2)) { + return; + } + + if (world.isEmpty(blockposition2.up()) && this.b(world, blockposition2)) { + // CraftBukkit start - Prevent lava putting something on fire + BlockPosition up = blockposition2.up(); + if (world.getType(up) != Blocks.FIRE) { + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, up, blockposition).isCancelled()) { + continue; + } + } + // CraftBukkit end + world.setTypeUpdate(blockposition2.up(), Blocks.FIRE.getBlockData()); + } + } + } + + } + } + + private boolean a(IWorldReader iworldreader, BlockPosition blockposition) { + EnumDirection[] aenumdirection = EnumDirection.values(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + + if (this.b(iworldreader, blockposition.shift(enumdirection))) { + return true; + } + } + + return false; + } + + private boolean b(IWorldReader iworldreader, BlockPosition blockposition) { + return blockposition.getY() >= 0 && blockposition.getY() < 256 && !iworldreader.isLoaded(blockposition) ? false : iworldreader.getType(blockposition).getMaterial().isBurnable(); + } + + protected void a(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata) { + this.a(generatoraccess, blockposition); + } + + public int b(IWorldReader iworldreader) { + return iworldreader.o().isNether() ? 4 : 2; + } + + public IBlockData b(Fluid fluid) { + return (IBlockData) Blocks.LAVA.getBlockData().set(BlockFluids.LEVEL, e(fluid)); + } + + public boolean a(FluidType fluidtype) { + return fluidtype == FluidTypes.LAVA || fluidtype == FluidTypes.FLOWING_LAVA; + } + + public int c(IWorldReader iworldreader) { + return iworldreader.o().isNether() ? 1 : 2; + } + + public boolean a(Fluid fluid, FluidType fluidtype, EnumDirection enumdirection) { + return fluid.getHeight() >= 0.44444445F && fluidtype.a(TagsFluid.WATER); + } + + public int a(IWorldReader iworldreader) { + return iworldreader.o().h() ? 10 : 30; + } + + public int a(World world, Fluid fluid, Fluid fluid1) { + int i = this.a((IWorldReader) world); + + if (!fluid.e() && !fluid1.e() && !(Boolean) fluid.get(FluidTypeLava.FALLING) && !(Boolean) fluid1.get(FluidTypeLava.FALLING) && fluid1.getHeight() > fluid.getHeight() && world.m().nextInt(4) != 0) { + i *= 4; + } + + return i; + } + + protected void a(GeneratorAccess generatoraccess, BlockPosition blockposition) { + double d0 = (double) blockposition.getX(); + double d1 = (double) blockposition.getY(); + double d2 = (double) blockposition.getZ(); + + generatoraccess.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_LAVA_EXTINGUISH, SoundCategory.BLOCKS, 0.5F, 2.6F + (generatoraccess.m().nextFloat() - generatoraccess.m().nextFloat()) * 0.8F); + + for (int i = 0; i < 8; ++i) { + generatoraccess.addParticle(Particles.F, d0 + Math.random(), d1 + 1.2D, d2 + Math.random(), 0.0D, 0.0D, 0.0D); + } + + } + + protected boolean g() { + return false; + } + + protected void a(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, EnumDirection enumdirection, Fluid fluid) { + if (enumdirection == EnumDirection.DOWN) { + Fluid fluid1 = generatoraccess.getFluid(blockposition); + + if (this.a(TagsFluid.LAVA) && fluid1.a(TagsFluid.WATER)) { + if (iblockdata.getBlock() instanceof BlockFluids) { + // CraftBukkit start + if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(generatoraccess.getMinecraftWorld(), blockposition, Blocks.STONE.getBlockData(), 3)) { + return; + } + // CraftBukkit end + } + + this.a(generatoraccess, blockposition); + return; + } + } + + super.a(generatoraccess, blockposition, iblockdata, enumdirection, fluid); + } + + protected boolean k() { + return true; + } + + protected float d() { + return 100.0F; + } + + public static class a extends FluidTypeLava { + + public a() {} + + protected void a(BlockStateList.a blockstatelist_a) { + super.a(blockstatelist_a); + blockstatelist_a.a(FluidTypeLava.a.LEVEL); + } + + public int d(Fluid fluid) { + return (Integer) fluid.get(FluidTypeLava.a.LEVEL); + } + + public boolean c(Fluid fluid) { + return false; + } + } + + public static class b extends FluidTypeLava { + + public b() {} + + public int d(Fluid fluid) { + return 8; + } + + public boolean c(Fluid fluid) { + return true; + } + } +} diff --git a/src/main/java/net/minecraft/server/FoodMetaData.java b/src/main/java/net/minecraft/server/FoodMetaData.java new file mode 100644 index 000000000000..aed3606bdcb4 --- /dev/null +++ b/src/main/java/net/minecraft/server/FoodMetaData.java @@ -0,0 +1,130 @@ +package net.minecraft.server; + +public class FoodMetaData { + + public int foodLevel = 20; + public float saturationLevel = 5.0F; + public float exhaustionLevel; + private int foodTickTimer; + private EntityHuman entityhuman; // CraftBukkit + private int e = 20; + + public FoodMetaData() { throw new AssertionError("Whoopsie, we missed the bukkit."); } // CraftBukkit start - throw an error + + // CraftBukkit start - added EntityHuman constructor + public FoodMetaData(EntityHuman entityhuman) { + org.apache.commons.lang.Validate.notNull(entityhuman); + this.entityhuman = entityhuman; + } + // CraftBukkit end + + public void eat(int i, float f) { + this.foodLevel = Math.min(i + this.foodLevel, 20); + this.saturationLevel = Math.min(this.saturationLevel + (float) i * f * 2.0F, (float) this.foodLevel); + } + + public void a(ItemFood itemfood, ItemStack itemstack) { + // CraftBukkit start + int oldFoodLevel = foodLevel; + + org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(entityhuman, itemfood.getNutrition(itemstack) + oldFoodLevel); + + if (!event.isCancelled()) { + this.eat(event.getFoodLevel() - oldFoodLevel, itemfood.getSaturationModifier(itemstack)); + } + + ((EntityPlayer) entityhuman).getBukkitEntity().sendHealthUpdate(); + // CraftBukkit end + } + + public void a(EntityHuman entityhuman) { + EnumDifficulty enumdifficulty = entityhuman.world.getDifficulty(); + + this.e = this.foodLevel; + if (this.exhaustionLevel > 4.0F) { + this.exhaustionLevel -= 4.0F; + if (this.saturationLevel > 0.0F) { + this.saturationLevel = Math.max(this.saturationLevel - 1.0F, 0.0F); + } else if (enumdifficulty != EnumDifficulty.PEACEFUL) { + // CraftBukkit start + org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(entityhuman, Math.max(this.foodLevel - 1, 0)); + + if (!event.isCancelled()) { + this.foodLevel = event.getFoodLevel(); + } + + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutUpdateHealth(((EntityPlayer) entityhuman).getBukkitEntity().getScaledHealth(), this.foodLevel, this.saturationLevel)); + // CraftBukkit end + } + } + + boolean flag = entityhuman.world.getGameRules().getBoolean("naturalRegeneration"); + + if (flag && this.saturationLevel > 0.0F && entityhuman.dx() && this.foodLevel >= 20) { + ++this.foodTickTimer; + if (this.foodTickTimer >= 10) { + float f = Math.min(this.saturationLevel, 6.0F); + + entityhuman.heal(f / 6.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.SATIATED, true); // CraftBukkit - added RegainReason // Paper - This is fast regen + this.a(f); + this.foodTickTimer = 0; + } + } else if (flag && this.foodLevel >= 18 && entityhuman.dx()) { + ++this.foodTickTimer; + if (this.foodTickTimer >= 80) { + entityhuman.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.SATIATED); // CraftBukkit - added RegainReason + this.a(entityhuman.world.spigotConfig.regenExhaustion); // Spigot - Change to use configurable value + this.foodTickTimer = 0; + } + } else if (this.foodLevel <= 0) { + ++this.foodTickTimer; + if (this.foodTickTimer >= 80) { + if (entityhuman.getHealth() > 10.0F || enumdifficulty == EnumDifficulty.HARD || entityhuman.getHealth() > 1.0F && enumdifficulty == EnumDifficulty.NORMAL) { + entityhuman.damageEntity(DamageSource.STARVE, 1.0F); + } + + this.foodTickTimer = 0; + } + } else { + this.foodTickTimer = 0; + } + + } + + public void a(NBTTagCompound nbttagcompound) { + if (nbttagcompound.hasKeyOfType("foodLevel", 99)) { + this.foodLevel = nbttagcompound.getInt("foodLevel"); + this.foodTickTimer = nbttagcompound.getInt("foodTickTimer"); + this.saturationLevel = nbttagcompound.getFloat("foodSaturationLevel"); + this.exhaustionLevel = nbttagcompound.getFloat("foodExhaustionLevel"); + } + + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setInt("foodLevel", this.foodLevel); + nbttagcompound.setInt("foodTickTimer", this.foodTickTimer); + nbttagcompound.setFloat("foodSaturationLevel", this.saturationLevel); + nbttagcompound.setFloat("foodExhaustionLevel", this.exhaustionLevel); + } + + public int getFoodLevel() { + return this.foodLevel; + } + + public boolean c() { + return this.foodLevel < 20; + } + + public void a(float f) { + this.exhaustionLevel = Math.min(this.exhaustionLevel + f, 40.0F); + } + + public float getSaturationLevel() { + return this.saturationLevel; + } + + public void a(int i) { + this.foodLevel = i; + } +} diff --git a/src/main/java/net/minecraft/server/FurnaceRecipe.java b/src/main/java/net/minecraft/server/FurnaceRecipe.java new file mode 100644 index 000000000000..ed18b60b6966 --- /dev/null +++ b/src/main/java/net/minecraft/server/FurnaceRecipe.java @@ -0,0 +1,129 @@ +package net.minecraft.server; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +// CraftBukkit start +import java.util.ArrayList; +import java.util.List; +import org.bukkit.craftbukkit.inventory.CraftFurnaceRecipe; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.inventory.CraftRecipe; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.inventory.Recipe; +// CraftBukkit end + +public class FurnaceRecipe implements IRecipe { + + private final MinecraftKey key; + private final String group; + private final RecipeItemStack ingredient; + private final ItemStack result; + private final float experience; + private final int cookingTime; + + public FurnaceRecipe(MinecraftKey minecraftkey, String s, RecipeItemStack recipeitemstack, ItemStack itemstack, float f, int i) { + this.key = minecraftkey; + this.group = s; + this.ingredient = recipeitemstack; + this.result = itemstack; + this.experience = f; + this.cookingTime = i; + } + + public boolean a(IInventory iinventory, World world) { + return iinventory instanceof TileEntityFurnace && this.ingredient.test(iinventory.getItem(0)); + } + + public ItemStack craftItem(IInventory iinventory) { + return this.result.cloneItemStack(); + } + + public RecipeSerializer a() { + return RecipeSerializers.p; + } + + public NonNullList e() { + NonNullList nonnulllist = NonNullList.a(); + + nonnulllist.add(this.ingredient); + return nonnulllist; + } + + public float g() { + return this.experience; + } + + public ItemStack d() { + return this.result; + } + + public int h() { + return this.cookingTime; + } + + public MinecraftKey getKey() { + return this.key; + } + + @Override + public Recipe toBukkitRecipe() { + CraftItemStack result = CraftItemStack.asCraftMirror(this.result); + + CraftFurnaceRecipe recipe = new CraftFurnaceRecipe(CraftNamespacedKey.fromMinecraft(this.key), result, CraftRecipe.toBukkit(this.ingredient), this.experience, this.cookingTime); + recipe.setGroup(this.group); + + return recipe; + } + + public static class a implements RecipeSerializer { + + public a() {} + + public FurnaceRecipe a(MinecraftKey minecraftkey, JsonObject jsonobject) { + String s = ChatDeserializer.a(jsonobject, "group", ""); + RecipeItemStack recipeitemstack; + + if (ChatDeserializer.d(jsonobject, "ingredient")) { + recipeitemstack = RecipeItemStack.a((JsonElement) ChatDeserializer.u(jsonobject, "ingredient")); + } else { + recipeitemstack = RecipeItemStack.a((JsonElement) ChatDeserializer.t(jsonobject, "ingredient")); + } + + String s1 = ChatDeserializer.h(jsonobject, "result"); + Item item = (Item) IRegistry.ITEM.get(new MinecraftKey(s1)); + + if (item != null) { + ItemStack itemstack = new ItemStack(item); + float f = ChatDeserializer.a(jsonobject, "experience", 0.0F); + int i = ChatDeserializer.a(jsonobject, "cookingtime", 200); + + return new FurnaceRecipe(minecraftkey, s, recipeitemstack, itemstack, f, i); + } else { + throw new IllegalStateException(s1 + " did not exist"); + } + } + + public FurnaceRecipe a(MinecraftKey minecraftkey, PacketDataSerializer packetdataserializer) { + String s = packetdataserializer.e(32767); + RecipeItemStack recipeitemstack = RecipeItemStack.b(packetdataserializer); + ItemStack itemstack = packetdataserializer.k(); + float f = packetdataserializer.readFloat(); + int i = packetdataserializer.g(); + + return new FurnaceRecipe(minecraftkey, s, recipeitemstack, itemstack, f, i); + } + + public void a(PacketDataSerializer packetdataserializer, FurnaceRecipe furnacerecipe) { + packetdataserializer.a(furnacerecipe.group); + furnacerecipe.ingredient.a(packetdataserializer); + packetdataserializer.a(furnacerecipe.result); + packetdataserializer.writeFloat(furnacerecipe.experience); + packetdataserializer.d(furnacerecipe.cookingTime); + } + + public String a() { + return "smelting"; + } + } +} diff --git a/src/main/java/net/minecraft/server/GameProfileBanEntry.java b/src/main/java/net/minecraft/server/GameProfileBanEntry.java new file mode 100644 index 000000000000..cb47697a041c --- /dev/null +++ b/src/main/java/net/minecraft/server/GameProfileBanEntry.java @@ -0,0 +1,64 @@ +package net.minecraft.server; + +import com.google.gson.JsonObject; +import com.mojang.authlib.GameProfile; +import java.util.Date; +import java.util.Objects; +import java.util.UUID; +import javax.annotation.Nullable; + +public class GameProfileBanEntry extends ExpirableListEntry { + + public GameProfileBanEntry(GameProfile gameprofile) { + this(gameprofile, (Date) null, (String) null, (Date) null, (String) null); + } + + public GameProfileBanEntry(GameProfile gameprofile, @Nullable Date date, @Nullable String s, @Nullable Date date1, @Nullable String s1) { + super(gameprofile, date, s, date1, s1); // Spigot + } + + public GameProfileBanEntry(JsonObject jsonobject) { + super(b(jsonobject), jsonobject); + } + + protected void a(JsonObject jsonobject) { + if (this.getKey() != null) { + jsonobject.addProperty("uuid", ((GameProfile) this.getKey()).getId() == null ? "" : ((GameProfile) this.getKey()).getId().toString()); + jsonobject.addProperty("name", ((GameProfile) this.getKey()).getName()); + super.a(jsonobject); + } + } + + public IChatBaseComponent e() { + GameProfile gameprofile = (GameProfile) this.getKey(); + + return new ChatComponentText(gameprofile.getName() != null ? gameprofile.getName() : Objects.toString(gameprofile.getId(), "(Unknown)")); + } + + private static GameProfile b(JsonObject jsonobject) { + // Spigot start + // this whole method has to be reworked to account for the fact Bukkit only accepts UUID bans and gives no way for usernames to be stored! + UUID uuid = null; + String name = null; + if (jsonobject.has("uuid")) { + String s = jsonobject.get("uuid").getAsString(); + + try { + uuid = UUID.fromString(s); + } catch (Throwable throwable) { + } + + } + if ( jsonobject.has("name")) + { + name = jsonobject.get("name").getAsString(); + } + if ( uuid != null || name != null ) + { + return new GameProfile( uuid, name ); + } else { + return null; + } + // Spigot End + } +} diff --git a/src/main/java/net/minecraft/server/GenericAttributes.java b/src/main/java/net/minecraft/server/GenericAttributes.java new file mode 100644 index 000000000000..90367a53ea7e --- /dev/null +++ b/src/main/java/net/minecraft/server/GenericAttributes.java @@ -0,0 +1,122 @@ +package net.minecraft.server; + +import java.util.Collection; +import java.util.Iterator; +import java.util.UUID; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class GenericAttributes { + + private static final Logger k = LogManager.getLogger(); + // Spigot start + public static final IAttribute maxHealth = (new AttributeRanged((IAttribute) null, "generic.maxHealth", 20.0D, 0.0D, org.spigotmc.SpigotConfig.maxHealth)).a("Max Health").a(true); + public static final IAttribute FOLLOW_RANGE = (new AttributeRanged((IAttribute) null, "generic.followRange", 32.0D, 0.0D, 2048.0D)).a("Follow Range"); + public static final IAttribute c = (new AttributeRanged((IAttribute) null, "generic.knockbackResistance", 0.0D, 0.0D, 1.0D)).a("Knockback Resistance"); + public static final IAttribute MOVEMENT_SPEED = (new AttributeRanged((IAttribute) null, "generic.movementSpeed", 0.699999988079071D, 0.0D, org.spigotmc.SpigotConfig.movementSpeed)).a("Movement Speed").a(true); + public static final IAttribute e = (new AttributeRanged((IAttribute) null, "generic.flyingSpeed", 0.4000000059604645D, 0.0D, 1024.0D)).a("Flying Speed").a(true); + public static final IAttribute ATTACK_DAMAGE = new AttributeRanged((IAttribute) null, "generic.attackDamage", 2.0D, 0.0D, org.spigotmc.SpigotConfig.attackDamage); + public static final IAttribute g = (new AttributeRanged((IAttribute) null, "generic.attackSpeed", 4.0D, 0.0D, 1024.0D)).a(true); + public static final IAttribute h = (new AttributeRanged((IAttribute) null, "generic.armor", 0.0D, 0.0D, 30.0D)).a(true); + public static final IAttribute i = (new AttributeRanged((IAttribute) null, "generic.armorToughness", 0.0D, 0.0D, 20.0D)).a(true); + public static final IAttribute j = (new AttributeRanged((IAttribute) null, "generic.luck", 0.0D, -1024.0D, 1024.0D)).a(true); + // Spigot end + + public static NBTTagList a(AttributeMapBase attributemapbase) { + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = attributemapbase.a().iterator(); + + while (iterator.hasNext()) { + AttributeInstance attributeinstance = (AttributeInstance) iterator.next(); + + nbttaglist.add((NBTBase) a(attributeinstance)); + } + + return nbttaglist; + } + + private static NBTTagCompound a(AttributeInstance attributeinstance) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + IAttribute iattribute = attributeinstance.getAttribute(); + + nbttagcompound.setString("Name", iattribute.getName()); + nbttagcompound.setDouble("Base", attributeinstance.b()); + Collection collection = attributeinstance.c(); + + if (collection != null && !collection.isEmpty()) { + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + AttributeModifier attributemodifier = (AttributeModifier) iterator.next(); + + if (attributemodifier.e()) { + nbttaglist.add((NBTBase) a(attributemodifier)); + } + } + + nbttagcompound.set("Modifiers", nbttaglist); + } + + return nbttagcompound; + } + + public static NBTTagCompound a(AttributeModifier attributemodifier) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setString("Name", attributemodifier.b()); + nbttagcompound.setDouble("Amount", attributemodifier.d()); + nbttagcompound.setInt("Operation", attributemodifier.c()); + nbttagcompound.a("UUID", attributemodifier.a()); + return nbttagcompound; + } + + public static void a(AttributeMapBase attributemapbase, NBTTagList nbttaglist) { + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist.getCompound(i); + AttributeInstance attributeinstance = attributemapbase.a(nbttagcompound.getString("Name")); + + if (attributeinstance == null) { + GenericAttributes.k.warn("Ignoring unknown attribute '{}'", nbttagcompound.getString("Name")); + } else { + a(attributeinstance, nbttagcompound); + } + } + + } + + private static void a(AttributeInstance attributeinstance, NBTTagCompound nbttagcompound) { + attributeinstance.setValue(nbttagcompound.getDouble("Base")); + if (nbttagcompound.hasKeyOfType("Modifiers", 9)) { + NBTTagList nbttaglist = nbttagcompound.getList("Modifiers", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + AttributeModifier attributemodifier = a(nbttaglist.getCompound(i)); + + if (attributemodifier != null) { + AttributeModifier attributemodifier1 = attributeinstance.a(attributemodifier.a()); + + if (attributemodifier1 != null) { + attributeinstance.c(attributemodifier1); + } + + attributeinstance.b(attributemodifier); + } + } + } + + } + + @Nullable + public static AttributeModifier a(NBTTagCompound nbttagcompound) { + UUID uuid = nbttagcompound.a("UUID"); + + try { + return new AttributeModifier(uuid, nbttagcompound.getString("Name"), nbttagcompound.getDouble("Amount"), nbttagcompound.getInt("Operation")); + } catch (Exception exception) { + GenericAttributes.k.warn("Unable to create attribute: {}", exception.getMessage()); + return null; + } + } +} diff --git a/src/main/java/net/minecraft/server/HandshakeListener.java b/src/main/java/net/minecraft/server/HandshakeListener.java new file mode 100644 index 000000000000..2c594b4378b8 --- /dev/null +++ b/src/main/java/net/minecraft/server/HandshakeListener.java @@ -0,0 +1,139 @@ +package net.minecraft.server; + +// CraftBukkit start +import java.net.InetAddress; +import java.util.HashMap; +// CraftBukkit end + +public class HandshakeListener implements PacketHandshakingInListener { + + private static final com.google.gson.Gson gson = new com.google.gson.Gson(); // Spigot + // CraftBukkit start - add fields + private static final HashMap throttleTracker = new HashMap(); + private static int throttleCounter = 0; + // CraftBukkit end + + private final MinecraftServer a; + private final NetworkManager b; + private NetworkManager getNetworkManager() { return b; } // Paper - OBFHELPER + + public HandshakeListener(MinecraftServer minecraftserver, NetworkManager networkmanager) { + this.a = minecraftserver; + this.b = networkmanager; + } + + public void a(PacketHandshakingInSetProtocol packethandshakinginsetprotocol) { + switch (packethandshakinginsetprotocol.b()) { + case LOGIN: + this.b.setProtocol(EnumProtocol.LOGIN); + ChatMessage chatmessage; + + // CraftBukkit start - Connection throttle + try { + long currentTime = System.currentTimeMillis(); + long connectionThrottle = MinecraftServer.getServer().server.getConnectionThrottle(); + InetAddress address = ((java.net.InetSocketAddress) this.b.getSocketAddress()).getAddress(); + + synchronized (throttleTracker) { + if (throttleTracker.containsKey(address) && !"127.0.0.1".equals(address.getHostAddress()) && currentTime - throttleTracker.get(address) < connectionThrottle) { + throttleTracker.put(address, currentTime); + chatmessage = new ChatMessage(com.destroystokyo.paper.PaperConfig.connectionThrottleKickMessage); // Paper - Configurable connection throttle kick message + this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage)); + this.b.close(chatmessage); + return; + } + + throttleTracker.put(address, currentTime); + throttleCounter++; + if (throttleCounter > 200) { + throttleCounter = 0; + + // Cleanup stale entries + java.util.Iterator iter = throttleTracker.entrySet().iterator(); + while (iter.hasNext()) { + java.util.Map.Entry entry = (java.util.Map.Entry) iter.next(); + if (entry.getValue() > connectionThrottle) { + iter.remove(); + } + } + } + } + } catch (Throwable t) { + org.apache.logging.log4j.LogManager.getLogger().debug("Failed to check connection throttle", t); + } + // CraftBukkit end + + if (packethandshakinginsetprotocol.c() > 404) { + chatmessage = new ChatMessage( java.text.MessageFormat.format( org.spigotmc.SpigotConfig.outdatedServerMessage.replaceAll("'", "''"), "1.13.2" ) ); // Spigot + this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage)); + this.b.close(chatmessage); + } else if (packethandshakinginsetprotocol.c() < 404) { + chatmessage = new ChatMessage( java.text.MessageFormat.format( org.spigotmc.SpigotConfig.outdatedClientMessage.replaceAll("'", "''"), "1.13.2" ) ); // Spigot + this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage)); + this.b.close(chatmessage); + } else { + this.b.setPacketListener(new LoginListener(this.a, this.b)); + // Paper start - handshake event + boolean proxyLogicEnabled = org.spigotmc.SpigotConfig.bungee; + boolean handledByEvent = false; + // Try and handle the handshake through the event + if (com.destroystokyo.paper.event.player.PlayerHandshakeEvent.getHandlerList().getRegisteredListeners().length != 0) { // Hello? Can you hear me? + com.destroystokyo.paper.event.player.PlayerHandshakeEvent event = new com.destroystokyo.paper.event.player.PlayerHandshakeEvent(packethandshakinginsetprotocol.hostname, !proxyLogicEnabled); + if (event.callEvent()) { + // If we've failed somehow, let the client know so and go no further. + if (event.isFailed()) { + chatmessage = new ChatMessage(event.getFailMessage()); + this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage)); + this.b.close(chatmessage); + return; + } + + packethandshakinginsetprotocol.hostname = event.getServerHostname(); + this.b.socketAddress = new java.net.InetSocketAddress(event.getSocketAddressHostname(), ((java.net.InetSocketAddress) this.b.getSocketAddress()).getPort()); + this.b.spoofedUUID = event.getUniqueId(); + this.b.spoofedProfile = gson.fromJson(event.getPropertiesJson(), com.mojang.authlib.properties.Property[].class); + handledByEvent = true; // Hooray, we did it! + } + } + // Don't try and handle default logic if it's been handled by the event. + if (!handledByEvent && proxyLogicEnabled) { + // Paper end + // Spigot Start + //if (org.spigotmc.SpigotConfig.bungee) { // Paper - comment out, we check above! + String[] split = packethandshakinginsetprotocol.hostname.split("\00"); + if ( split.length == 3 || split.length == 4 ) { + packethandshakinginsetprotocol.hostname = split[0]; + b.socketAddress = new java.net.InetSocketAddress(split[1], ((java.net.InetSocketAddress) b.getSocketAddress()).getPort()); + b.spoofedUUID = com.mojang.util.UUIDTypeAdapter.fromString( split[2] ); + } else + { + chatmessage = new ChatMessage("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!"); + this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage)); + this.b.close(chatmessage); + return; + } + if ( split.length == 4 ) + { + b.spoofedProfile = gson.fromJson(split[3], com.mojang.authlib.properties.Property[].class); + } + } + // Spigot End + ((LoginListener) this.b.i()).hostname = packethandshakinginsetprotocol.hostname + ":" + packethandshakinginsetprotocol.port; // CraftBukkit - set hostname + } + break; + case STATUS: + this.b.setProtocol(EnumProtocol.STATUS); + this.b.setPacketListener(new PacketStatusListener(this.a, this.b)); + break; + default: + throw new UnsupportedOperationException("Invalid intention " + packethandshakinginsetprotocol.b()); + } + + // Paper start - NetworkClient implementation + this.getNetworkManager().protocolVersion = packethandshakinginsetprotocol.getProtocolVersion(); + this.getNetworkManager().virtualHost = com.destroystokyo.paper.network.PaperNetworkClient.prepareVirtualHost(packethandshakinginsetprotocol.hostname, packethandshakinginsetprotocol.port); + // Paper end + } + + public void a(IChatBaseComponent ichatbasecomponent) {} +} diff --git a/src/main/java/net/minecraft/server/IBlockData.java b/src/main/java/net/minecraft/server/IBlockData.java new file mode 100644 index 000000000000..aa3547690adb --- /dev/null +++ b/src/main/java/net/minecraft/server/IBlockData.java @@ -0,0 +1,295 @@ +package net.minecraft.server; + +import it.unimi.dsi.fastutil.objects.Object2ByteMap; +import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap; +import java.util.Random; + +public interface IBlockData extends IBlockDataHolder { + + ThreadLocal> a = ThreadLocal.withInitial(() -> { + Object2ByteOpenHashMap object2byteopenhashmap = new Object2ByteOpenHashMap(); + + object2byteopenhashmap.defaultReturnValue((byte) 127); + return object2byteopenhashmap; + }); + ThreadLocal> b = ThreadLocal.withInitial(() -> { + Object2ByteOpenHashMap object2byteopenhashmap = new Object2ByteOpenHashMap(); + + object2byteopenhashmap.defaultReturnValue((byte) 127); + return object2byteopenhashmap; + }); + ThreadLocal> c = ThreadLocal.withInitial(() -> { + Object2ByteOpenHashMap object2byteopenhashmap = new Object2ByteOpenHashMap(); + + object2byteopenhashmap.defaultReturnValue((byte) 127); + return object2byteopenhashmap; + }); + + Block getBlock(); + + org.bukkit.craftbukkit.block.data.CraftBlockData createCraftBlockData(); // Paper - property for converting IBlockData to CraftBlockData, should only be used by CraftBlockData#fromData and should return a clone + + default Material getMaterial() { + return this.getBlock().n(this); + } + + default boolean a(Entity entity) { + return this.getBlock().a(this, entity); + } + + default boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) { + Block block = this.getBlock(); + Object2ByteMap object2bytemap = block.s() ? null : (Object2ByteMap) IBlockData.a.get(); + + if (object2bytemap != null) { + byte b0 = object2bytemap.getByte(this); + + if (b0 != object2bytemap.defaultReturnValue()) { + return b0 != 0; + } + } + + boolean flag = block.a_(this, iblockaccess, blockposition); + + if (object2bytemap != null) { + object2bytemap.put(this, (byte) (flag ? 1 : 0)); + } + + return flag; + } + + default int b(IBlockAccess iblockaccess, BlockPosition blockposition) { + Block block = this.getBlock(); + Object2ByteMap object2bytemap = block.s() ? null : (Object2ByteMap) IBlockData.b.get(); + + if (object2bytemap != null) { + byte b0 = object2bytemap.getByte(this); + + if (b0 != object2bytemap.defaultReturnValue()) { + return b0; + } + } + + int i = block.j(this, iblockaccess, blockposition); + + if (object2bytemap != null) { + object2bytemap.put(this, (byte) Math.min(i, iblockaccess.K())); + } + + return i; + } + + default int e() { + return this.getBlock().m(this); + } + + default boolean isAir() { + return this.getBlock().e(this); + } + + default boolean c(IBlockAccess iblockaccess, BlockPosition blockposition) { + return this.getBlock().k(this, iblockaccess, blockposition); + } + + default MaterialMapColor d(IBlockAccess iblockaccess, BlockPosition blockposition) { + return this.getBlock().c(this, iblockaccess, blockposition); + } + + default IBlockData a(EnumBlockRotation enumblockrotation) { + return this.getBlock().a(this, enumblockrotation); + } + + default IBlockData a(EnumBlockMirror enumblockmirror) { + return this.getBlock().a(this, enumblockmirror); + } + + default boolean g() { + return this.getBlock().a(this); + } + + default EnumRenderType i() { + return this.getBlock().c(this); + } + + default boolean k() { + return this.getBlock().o(this); + } + + default boolean isOccluding() { + return this.getBlock().isOccluding(this); + } + + default boolean isPowerSource() { + return this.getBlock().isPowerSource(this); + } + + default int a(IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return this.getBlock().a(this, iblockaccess, blockposition, enumdirection); + } + + default boolean isComplexRedstone() { + return this.getBlock().isComplexRedstone(this); + } + + default int a(World world, BlockPosition blockposition) { + return this.getBlock().a(this, world, blockposition); + } + + default float e(IBlockAccess iblockaccess, BlockPosition blockposition) { + return this.getBlock().d(this, iblockaccess, blockposition); + } + + default float getDamage(EntityHuman entityhuman, IBlockAccess iblockaccess, BlockPosition blockposition) { + return this.getBlock().getDamage(this, entityhuman, iblockaccess, blockposition); + } + + default int b(IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return this.getBlock().b(this, iblockaccess, blockposition, enumdirection); + } + + default EnumPistonReaction getPushReaction() { + return this.getBlock().getPushReaction(this); + } + + default boolean f(IBlockAccess iblockaccess, BlockPosition blockposition) { + Block block = this.getBlock(); + Object2ByteMap object2bytemap = block.s() ? null : (Object2ByteMap) IBlockData.c.get(); + + if (object2bytemap != null) { + byte b0 = object2bytemap.getByte(this); + + if (b0 != object2bytemap.defaultReturnValue()) { + return b0 != 0; + } + } + + boolean flag = block.i(this, iblockaccess, blockposition); + + if (object2bytemap != null) { + object2bytemap.put(this, (byte) (flag ? 1 : 0)); + } + + return flag; + } + + default boolean p() { + return this.getBlock().f(this); + } + + default VoxelShape getShape(IBlockAccess iblockaccess, BlockPosition blockposition) { + return this.getBlock().a(this, iblockaccess, blockposition); + } + + default VoxelShape getCollisionShape(IBlockAccess iblockaccess, BlockPosition blockposition) { + return this.getBlock().f(this, iblockaccess, blockposition); + } + + default VoxelShape i(IBlockAccess iblockaccess, BlockPosition blockposition) { + return this.getBlock().g(this, iblockaccess, blockposition); + } + + default VoxelShape j(IBlockAccess iblockaccess, BlockPosition blockposition) { + return this.getBlock().h(this, iblockaccess, blockposition); + } + + default boolean q() { + return this.getBlock().r(this); + } + + default Vec3D k(IBlockAccess iblockaccess, BlockPosition blockposition) { + return this.getBlock().l(this, iblockaccess, blockposition); + } + + default boolean a(World world, BlockPosition blockposition, int i, int j) { + return this.getBlock().a(this, world, blockposition, i, j); + } + + default void doPhysics(World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + this.getBlock().doPhysics(this, world, blockposition, block, blockposition1); + } + + default void a(GeneratorAccess generatoraccess, BlockPosition blockposition, int i) { + this.getBlock().a(this, generatoraccess, blockposition, i); + } + + default void b(GeneratorAccess generatoraccess, BlockPosition blockposition, int i) { + this.getBlock().b(this, generatoraccess, blockposition, i); + } + + default void onPlace(World world, BlockPosition blockposition, IBlockData iblockdata) { + this.getBlock().onPlace(this, world, blockposition, iblockdata); + } + + default void remove(World world, BlockPosition blockposition, IBlockData iblockdata, boolean flag) { + this.getBlock().remove(this, world, blockposition, iblockdata, flag); + } + + default void a(World world, BlockPosition blockposition, Random random) { + this.getBlock().a(this, world, blockposition, random); + } + + default void b(World world, BlockPosition blockposition, Random random) { + this.getBlock().b(this, world, blockposition, random); + } + + default void a(World world, BlockPosition blockposition, Entity entity) { + this.getBlock().a(this, world, blockposition, entity); + } + + default void dropNaturally(World world, BlockPosition blockPosition, int i) { a(world, blockPosition, i);} // Paper - OBFHELPER + default void a(World world, BlockPosition blockposition, int i) { + this.dropNaturally(world, blockposition, 1.0F, i); + } + + default void dropNaturally(World world, BlockPosition blockposition, float f, int i) { + this.getBlock().dropNaturally(this, world, blockposition, f, i); + } + + default boolean interact(World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + return this.getBlock().interact(this, world, blockposition, entityhuman, enumhand, enumdirection, f, f1, f2); + } + + default void attack(World world, BlockPosition blockposition, EntityHuman entityhuman) { + this.getBlock().attack(this, world, blockposition, entityhuman); + } + + default boolean r() { + return this.getBlock().q(this); + } + + default EnumBlockFaceShape c(IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return this.getBlock().a(iblockaccess, this, blockposition, enumdirection); + } + + default IBlockData updateState(EnumDirection enumdirection, IBlockData iblockdata, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) { + return this.getBlock().updateState(this, enumdirection, iblockdata, generatoraccess, blockposition, blockposition1); + } + + default boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) { + return this.getBlock().a(this, iblockaccess, blockposition, pathmode); + } + + default boolean a(BlockActionContext blockactioncontext) { + return this.getBlock().a(this, blockactioncontext); + } + + default boolean canPlace(IWorldReader iworldreader, BlockPosition blockposition) { + return this.getBlock().canPlace(this, iworldreader, blockposition); + } + + default boolean l(IBlockAccess iblockaccess, BlockPosition blockposition) { + return this.getBlock().e(this, iblockaccess, blockposition); + } + + default boolean a(Tag tag) { + return this.getBlock().a(tag); + } + + default Fluid s() { + return this.getBlock().h(this); + } + + default boolean t() { + return this.getBlock().isTicking(this); + } +} diff --git a/src/main/java/net/minecraft/server/IChatBaseComponent.java b/src/main/java/net/minecraft/server/IChatBaseComponent.java new file mode 100644 index 000000000000..48fecffdfe57 --- /dev/null +++ b/src/main/java/net/minecraft/server/IChatBaseComponent.java @@ -0,0 +1,439 @@ +package net.minecraft.server; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.stream.JsonReader; +import com.mojang.brigadier.Message; +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; +import java.util.function.Consumer; +import java.util.stream.Stream; +import javax.annotation.Nullable; + +public interface IChatBaseComponent extends Message, Iterable { + + IChatBaseComponent setChatModifier(ChatModifier chatmodifier); + + ChatModifier getChatModifier(); + + default IChatBaseComponent a(String s) { + return this.addSibling(new ChatComponentText(s)); + } + + IChatBaseComponent addSibling(IChatBaseComponent ichatbasecomponent); + + String getText(); + + default String getString() { + StringBuilder stringbuilder = new StringBuilder(); + + this.c().forEach((ichatbasecomponent) -> { + stringbuilder.append(ichatbasecomponent.getText()); + }); + return stringbuilder.toString(); + } + + default String a(int i) { + StringBuilder stringbuilder = new StringBuilder(); + Iterator iterator = this.c().iterator(); + + while (iterator.hasNext()) { + int j = i - stringbuilder.length(); + + if (j <= 0) { + break; + } + + String s = ((IChatBaseComponent) iterator.next()).getText(); + + stringbuilder.append(s.length() <= j ? s : s.substring(0, j)); + } + + return stringbuilder.toString(); + } + + default String e() { + StringBuilder stringbuilder = new StringBuilder(); + String s = ""; + Iterator iterator = this.c().iterator(); + + while (iterator.hasNext()) { + IChatBaseComponent ichatbasecomponent = (IChatBaseComponent) iterator.next(); + String s1 = ichatbasecomponent.getText(); + + if (!s1.isEmpty()) { + String s2 = ichatbasecomponent.getChatModifier().k(); + + if (!s2.equals(s)) { + if (!s.isEmpty()) { + stringbuilder.append(EnumChatFormat.RESET); + } + + stringbuilder.append(s2); + s = s2; + } + + stringbuilder.append(s1); + } + } + + if (!s.isEmpty()) { + stringbuilder.append(EnumChatFormat.RESET); + } + + return stringbuilder.toString(); + } + + List a(); + + Stream c(); + + default Stream f() { + return this.c().map(IChatBaseComponent::b); + } + + default Iterator iterator() { + return this.f().iterator(); + } + + IChatBaseComponent g(); + + default IChatBaseComponent h() { + IChatBaseComponent ichatbasecomponent = this.g(); + + ichatbasecomponent.setChatModifier(this.getChatModifier().clone()); + Iterator iterator = this.a().iterator(); + + while (iterator.hasNext()) { + IChatBaseComponent ichatbasecomponent1 = (IChatBaseComponent) iterator.next(); + + ichatbasecomponent.addSibling(ichatbasecomponent1.h()); + } + + return ichatbasecomponent; + } + + default IChatBaseComponent a(Consumer consumer) { + consumer.accept(this.getChatModifier()); + return this; + } + + default IChatBaseComponent a(EnumChatFormat... aenumchatformat) { + EnumChatFormat[] aenumchatformat1 = aenumchatformat; + int i = aenumchatformat.length; + + for (int j = 0; j < i; ++j) { + EnumChatFormat enumchatformat = aenumchatformat1[j]; + + this.a(enumchatformat); + } + + return this; + } + + default IChatBaseComponent a(EnumChatFormat enumchatformat) { + ChatModifier chatmodifier = this.getChatModifier(); + + if (enumchatformat.d()) { + chatmodifier.setColor(enumchatformat); + } + + if (enumchatformat.isFormat()) { + switch (enumchatformat) { + case OBFUSCATED: + chatmodifier.setRandom(true); + break; + case BOLD: + chatmodifier.setBold(true); + break; + case STRIKETHROUGH: + chatmodifier.setStrikethrough(true); + break; + case UNDERLINE: + chatmodifier.setUnderline(true); + break; + case ITALIC: + chatmodifier.setItalic(true); + } + } + + return this; + } + + static IChatBaseComponent b(IChatBaseComponent ichatbasecomponent) { + IChatBaseComponent ichatbasecomponent1 = ichatbasecomponent.g(); + + ichatbasecomponent1.setChatModifier(ichatbasecomponent.getChatModifier().n()); + return ichatbasecomponent1; + } + + public static class ChatSerializer implements JsonDeserializer, JsonSerializer { + + private static final Gson a = (Gson) SystemUtils.a(() -> { + GsonBuilder gsonbuilder = new GsonBuilder(); + + gsonbuilder.registerTypeHierarchyAdapter(IChatBaseComponent.class, new IChatBaseComponent.ChatSerializer()); + gsonbuilder.registerTypeHierarchyAdapter(ChatModifier.class, new ChatModifier.ChatModifierSerializer()); + gsonbuilder.registerTypeAdapterFactory(new ChatTypeAdapterFactory()); + return gsonbuilder.create(); + }); + private static final Field b = (Field) SystemUtils.a(() -> { + try { + new JsonReader(new StringReader("")); + Field field = JsonReader.class.getDeclaredField("pos"); + + field.setAccessible(true); + return field; + } catch (NoSuchFieldException nosuchfieldexception) { + throw new IllegalStateException("Couldn't get field 'pos' for JsonReader", nosuchfieldexception); + } + }); + private static final Field c = (Field) SystemUtils.a(() -> { + try { + new JsonReader(new StringReader("")); + Field field = JsonReader.class.getDeclaredField("lineStart"); + + field.setAccessible(true); + return field; + } catch (NoSuchFieldException nosuchfieldexception) { + throw new IllegalStateException("Couldn't get field 'lineStart' for JsonReader", nosuchfieldexception); + } + }); + + public ChatSerializer() {} + + public IChatBaseComponent deserialize(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws JsonParseException { + if (jsonelement.isJsonPrimitive()) { + return new ChatComponentText(jsonelement.getAsString()); + } else if (!jsonelement.isJsonObject()) { + if (jsonelement.isJsonArray()) { + JsonArray jsonarray = jsonelement.getAsJsonArray(); + IChatBaseComponent ichatbasecomponent = null; + Iterator iterator = jsonarray.iterator(); + + while (iterator.hasNext()) { + JsonElement jsonelement1 = (JsonElement) iterator.next(); + IChatBaseComponent ichatbasecomponent1 = this.deserialize(jsonelement1, jsonelement1.getClass(), jsondeserializationcontext); + + if (ichatbasecomponent == null) { + ichatbasecomponent = ichatbasecomponent1; + } else { + ichatbasecomponent.addSibling(ichatbasecomponent1); + } + } + + return ichatbasecomponent; + } else { + throw new JsonParseException("Don't know how to turn " + jsonelement + " into a Component"); + } + } else { + JsonObject jsonobject = jsonelement.getAsJsonObject(); + Object object; + + if (jsonobject.has("text")) { + object = new ChatComponentText(jsonobject.get("text").getAsString()); + } else if (jsonobject.has("translate")) { + String s = jsonobject.get("translate").getAsString(); + + if (jsonobject.has("with")) { + JsonArray jsonarray1 = jsonobject.getAsJsonArray("with"); + Object[] aobject = new Object[jsonarray1.size()]; + + for (int i = 0; i < aobject.length; ++i) { + aobject[i] = this.deserialize(jsonarray1.get(i), type, jsondeserializationcontext); + if (aobject[i] instanceof ChatComponentText) { + ChatComponentText chatcomponenttext = (ChatComponentText) aobject[i]; + + if (chatcomponenttext.getChatModifier().g() && chatcomponenttext.a().isEmpty()) { + aobject[i] = chatcomponenttext.i(); + } + } + } + + object = new ChatMessage(s, aobject); + } else { + object = new ChatMessage(s, new Object[0]); + } + } else if (jsonobject.has("score")) { + JsonObject jsonobject1 = jsonobject.getAsJsonObject("score"); + + if (!jsonobject1.has("name") || !jsonobject1.has("objective")) { + throw new JsonParseException("A score component needs a least a name and an objective"); + } + + object = new ChatComponentScore(ChatDeserializer.h(jsonobject1, "name"), ChatDeserializer.h(jsonobject1, "objective")); + if (jsonobject1.has("value")) { + ((ChatComponentScore) object).b(ChatDeserializer.h(jsonobject1, "value")); + } + } else if (jsonobject.has("selector")) { + object = new ChatComponentSelector(ChatDeserializer.h(jsonobject, "selector")); + } else { + if (!jsonobject.has("keybind")) { + throw new JsonParseException("Don't know how to turn " + jsonelement + " into a Component"); + } + + object = new ChatComponentKeybind(ChatDeserializer.h(jsonobject, "keybind")); + } + + if (jsonobject.has("extra")) { + JsonArray jsonarray2 = jsonobject.getAsJsonArray("extra"); + + if (jsonarray2.size() <= 0) { + throw new JsonParseException("Unexpected empty array of components"); + } + + for (int j = 0; j < jsonarray2.size(); ++j) { + ((IChatBaseComponent) object).addSibling(this.deserialize(jsonarray2.get(j), type, jsondeserializationcontext)); + } + } + + ((IChatBaseComponent) object).setChatModifier((ChatModifier) jsondeserializationcontext.deserialize(jsonelement, ChatModifier.class)); + return (IChatBaseComponent) object; + } + } + + private void a(ChatModifier chatmodifier, JsonObject jsonobject, JsonSerializationContext jsonserializationcontext) { + JsonElement jsonelement = jsonserializationcontext.serialize(chatmodifier); + + if (jsonelement.isJsonObject()) { + JsonObject jsonobject1 = (JsonObject) jsonelement; + Iterator iterator = jsonobject1.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + + jsonobject.add((String) entry.getKey(), (JsonElement) entry.getValue()); + } + } + + } + + public JsonElement serialize(IChatBaseComponent ichatbasecomponent, Type type, JsonSerializationContext jsonserializationcontext) { + JsonObject jsonobject = new JsonObject(); + + if (!ichatbasecomponent.getChatModifier().g()) { + this.a(ichatbasecomponent.getChatModifier(), jsonobject, jsonserializationcontext); + } + + if (!ichatbasecomponent.a().isEmpty()) { + JsonArray jsonarray = new JsonArray(); + Iterator iterator = ichatbasecomponent.a().iterator(); + + while (iterator.hasNext()) { + IChatBaseComponent ichatbasecomponent1 = (IChatBaseComponent) iterator.next(); + + jsonarray.add(this.serialize(ichatbasecomponent1, ichatbasecomponent1.getClass(), jsonserializationcontext)); + } + + jsonobject.add("extra", jsonarray); + } + + if (ichatbasecomponent instanceof ChatComponentText) { + jsonobject.addProperty("text", ((ChatComponentText) ichatbasecomponent).i()); + } else if (ichatbasecomponent instanceof ChatMessage) { + ChatMessage chatmessage = (ChatMessage) ichatbasecomponent; + + jsonobject.addProperty("translate", chatmessage.k()); + if (chatmessage.l() != null && chatmessage.l().length > 0) { + JsonArray jsonarray1 = new JsonArray(); + Object[] aobject = chatmessage.l(); + int i = aobject.length; + + for (int j = 0; j < i; ++j) { + Object object = aobject[j]; + + if (object instanceof IChatBaseComponent) { + jsonarray1.add(this.serialize((IChatBaseComponent) object, object.getClass(), jsonserializationcontext)); + } else { + jsonarray1.add(new JsonPrimitive(String.valueOf(object))); + } + } + + jsonobject.add("with", jsonarray1); + } + } else if (ichatbasecomponent instanceof ChatComponentScore) { + ChatComponentScore chatcomponentscore = (ChatComponentScore) ichatbasecomponent; + JsonObject jsonobject1 = new JsonObject(); + + jsonobject1.addProperty("name", chatcomponentscore.i()); + jsonobject1.addProperty("objective", chatcomponentscore.k()); + jsonobject1.addProperty("value", chatcomponentscore.getText()); + jsonobject.add("score", jsonobject1); + } else if (ichatbasecomponent instanceof ChatComponentSelector) { + ChatComponentSelector chatcomponentselector = (ChatComponentSelector) ichatbasecomponent; + + jsonobject.addProperty("selector", chatcomponentselector.i()); + } else { + if (!(ichatbasecomponent instanceof ChatComponentKeybind)) { + throw new IllegalArgumentException("Don't know how to serialize " + ichatbasecomponent + " as a Component"); + } + + ChatComponentKeybind chatcomponentkeybind = (ChatComponentKeybind) ichatbasecomponent; + + jsonobject.addProperty("keybind", chatcomponentkeybind.j()); + } + + return jsonobject; + } + + public static String a(IChatBaseComponent ichatbasecomponent) { + return IChatBaseComponent.ChatSerializer.a.toJson(ichatbasecomponent); + } + + public static JsonElement b(IChatBaseComponent ichatbasecomponent) { + return IChatBaseComponent.ChatSerializer.a.toJsonTree(ichatbasecomponent); + } + + @Nullable public static IChatBaseComponent jsonToComponent(String json) { return a(json);} // Paper - OBFHELPER + @Nullable + public static IChatBaseComponent a(String s) { + return (IChatBaseComponent) ChatDeserializer.a(IChatBaseComponent.ChatSerializer.a, s, IChatBaseComponent.class, false); + } + + @Nullable + public static IChatBaseComponent a(JsonElement jsonelement) { + return (IChatBaseComponent) IChatBaseComponent.ChatSerializer.a.fromJson(jsonelement, IChatBaseComponent.class); + } + + @Nullable + public static IChatBaseComponent b(String s) { + return (IChatBaseComponent) ChatDeserializer.a(IChatBaseComponent.ChatSerializer.a, s, IChatBaseComponent.class, true); + } + + public static IChatBaseComponent a(com.mojang.brigadier.StringReader com_mojang_brigadier_stringreader) { + try { + JsonReader jsonreader = new JsonReader(new StringReader(com_mojang_brigadier_stringreader.getRemaining())); + + jsonreader.setLenient(false); + IChatBaseComponent ichatbasecomponent = (IChatBaseComponent) IChatBaseComponent.ChatSerializer.a.getAdapter(IChatBaseComponent.class).read(jsonreader); + + com_mojang_brigadier_stringreader.setCursor(com_mojang_brigadier_stringreader.getCursor() + a(jsonreader)); + return ichatbasecomponent; + } catch (IOException ioexception) { + throw new JsonParseException(ioexception); + } + } + + private static int a(JsonReader jsonreader) { + try { + return IChatBaseComponent.ChatSerializer.b.getInt(jsonreader) - IChatBaseComponent.ChatSerializer.c.getInt(jsonreader) + 1; + } catch (IllegalAccessException illegalaccessexception) { + throw new IllegalStateException("Couldn't read position of JsonReader", illegalaccessexception); + } + } + } +} diff --git a/src/main/java/net/minecraft/server/IChunkLoader.java b/src/main/java/net/minecraft/server/IChunkLoader.java new file mode 100644 index 000000000000..dfb45cc4eac1 --- /dev/null +++ b/src/main/java/net/minecraft/server/IChunkLoader.java @@ -0,0 +1,22 @@ +package net.minecraft.server; + +import java.io.IOException; +import java.util.function.Consumer; +import javax.annotation.Nullable; + +public interface IChunkLoader { + + void loadEntities(NBTTagCompound nbttagcompound, Chunk chunk); // Paper - Async Chunks + Object[] loadChunk(GeneratorAccess generatoraccess, int i, int j, Consumer consumer) throws IOException; // Paper - Async Chunks + @Nullable + Chunk a(GeneratorAccess generatoraccess, int i, int j, Consumer consumer) throws IOException; + + @Nullable + ProtoChunk b(GeneratorAccess generatoraccess, int i, int j, Consumer consumer) throws IOException; + + void saveChunk(World world, IChunkAccess ichunkaccess) throws IOException, ExceptionWorldConflict; + + void saveChunk(World world, IChunkAccess ichunkaccess, boolean unloaded) throws IOException, ExceptionWorldConflict; // Spigot + + void b(); +} diff --git a/src/main/java/net/minecraft/server/ICommandListener.java b/src/main/java/net/minecraft/server/ICommandListener.java new file mode 100644 index 000000000000..f0ed7a5f1847 --- /dev/null +++ b/src/main/java/net/minecraft/server/ICommandListener.java @@ -0,0 +1,14 @@ +package net.minecraft.server; + +public interface ICommandListener { + + void sendMessage(IChatBaseComponent ichatbasecomponent); + + boolean a(); + + boolean b(); + + boolean B_(); + + org.bukkit.command.CommandSender getBukkitSender(CommandListenerWrapper wrapper); // CraftBukkit +} diff --git a/src/main/java/net/minecraft/server/IDataManager.java b/src/main/java/net/minecraft/server/IDataManager.java new file mode 100644 index 000000000000..2333fbbd907d --- /dev/null +++ b/src/main/java/net/minecraft/server/IDataManager.java @@ -0,0 +1,34 @@ +package net.minecraft.server; + +import com.mojang.datafixers.DataFixer; +import java.io.File; +import javax.annotation.Nullable; + +public interface IDataManager { + + @Nullable + WorldData getWorldData(); + + void checkSession() throws ExceptionWorldConflict; + + IChunkLoader createChunkLoader(WorldProvider worldprovider); + + void saveWorldData(WorldData worlddata, NBTTagCompound nbttagcompound); + + void saveWorldData(WorldData worlddata); + + IPlayerFileData getPlayerFileData(); + + void a(); + + File getDirectory(); + + @Nullable + File getDataFile(DimensionManager dimensionmanager, String s); + + DefinedStructureManager h(); + + DataFixer i(); + + java.util.UUID getUUID(); // CraftBukkit +} diff --git a/src/main/java/net/minecraft/server/IEntitySelector.java b/src/main/java/net/minecraft/server/IEntitySelector.java new file mode 100644 index 000000000000..71f08d53c733 --- /dev/null +++ b/src/main/java/net/minecraft/server/IEntitySelector.java @@ -0,0 +1,97 @@ +package net.minecraft.server; + +import com.google.common.base.Predicates; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public final class IEntitySelector { + + public static final Predicate a = Entity::isAlive; + public static final Predicate b = EntityLiving::isAlive; + public static final Predicate c = (entity) -> { + return entity.isAlive() && !entity.isVehicle() && !entity.isPassenger(); + }; + public static final Predicate isInventory() { return d; } // Paper - OBFHELPER + public static final Predicate d = (entity) -> { + return entity instanceof IInventory && entity.isAlive(); + }; + public static Predicate canAITarget() { return e; } // Paper - OBFHELPER + public static final Predicate e = (entity) -> { + return !(entity instanceof EntityHuman) || !((EntityHuman) entity).isSpectator() && !((EntityHuman) entity).u(); + }; + public static Predicate notSpectator() { return f; } // Paper - OBFHELPER + public static final Predicate f = (entity) -> { + return !(entity instanceof EntityHuman) || !((EntityHuman) entity).isSpectator(); + }; + + public static Predicate a(double d0, double d1, double d2, double d3) { + double d4 = d3 * d3; + + return (entity) -> { + return entity != null && entity.d(d0, d1, d2) <= d4; + }; + } + + public static Predicate a(Entity entity) { + ScoreboardTeamBase scoreboardteambase = entity.getScoreboardTeam(); + ScoreboardTeamBase.EnumTeamPush scoreboardteambase_enumteampush = scoreboardteambase == null ? ScoreboardTeamBase.EnumTeamPush.ALWAYS : scoreboardteambase.getCollisionRule(); + + return (Predicate) (scoreboardteambase_enumteampush == ScoreboardTeamBase.EnumTeamPush.NEVER ? Predicates.alwaysFalse() : IEntitySelector.f.and((entity1) -> { + if (!entity1.isCollidable()) { + return false; + } else if (entity.world.isClientSide && (!(entity1 instanceof EntityHuman) || !((EntityHuman) entity1).dn())) { + return false; + } else { + ScoreboardTeamBase scoreboardteambase1 = entity1.getScoreboardTeam(); + ScoreboardTeamBase.EnumTeamPush scoreboardteambase_enumteampush1 = scoreboardteambase1 == null ? ScoreboardTeamBase.EnumTeamPush.ALWAYS : scoreboardteambase1.getCollisionRule(); + + if (scoreboardteambase_enumteampush1 == ScoreboardTeamBase.EnumTeamPush.NEVER) { + return false; + } else { + boolean flag = scoreboardteambase != null && scoreboardteambase.isAlly(scoreboardteambase1); + + return (scoreboardteambase_enumteampush == ScoreboardTeamBase.EnumTeamPush.PUSH_OWN_TEAM || scoreboardteambase_enumteampush1 == ScoreboardTeamBase.EnumTeamPush.PUSH_OWN_TEAM) && flag ? false : scoreboardteambase_enumteampush != ScoreboardTeamBase.EnumTeamPush.PUSH_OTHER_TEAMS && scoreboardteambase_enumteampush1 != ScoreboardTeamBase.EnumTeamPush.PUSH_OTHER_TEAMS || flag; + } + } + })); + } + + public static Predicate b(Entity entity) { + return (entity1) -> { + while (true) { + if (entity1.isPassenger()) { + entity1 = entity1.getVehicle(); + if (entity1 != entity) { + continue; + } + + return false; + } + + return true; + } + }; + } + + public static class EntitySelectorEquipable implements Predicate { + + private final ItemStack a; + + public EntitySelectorEquipable(ItemStack itemstack) { + this.a = itemstack; + } + + public boolean test(@Nullable Entity entity) { + if (!entity.isAlive()) { + return false; + } else if (!(entity instanceof EntityLiving)) { + return false; + } else { + EntityLiving entityliving = (EntityLiving) entity; + EnumItemSlot enumitemslot = EntityInsentient.e(this.a); + + return !entityliving.getEquipment(enumitemslot).isEmpty() ? false : (entityliving instanceof EntityInsentient ? ((EntityInsentient) entityliving).dj() : (entityliving instanceof EntityArmorStand ? !((EntityArmorStand) entityliving).c(enumitemslot) : entityliving instanceof EntityHuman)); + } + } + } +} diff --git a/src/main/java/net/minecraft/server/IInventory.java b/src/main/java/net/minecraft/server/IInventory.java new file mode 100644 index 000000000000..bdec27c9d3b4 --- /dev/null +++ b/src/main/java/net/minecraft/server/IInventory.java @@ -0,0 +1,71 @@ +package net.minecraft.server; + +import org.bukkit.craftbukkit.entity.CraftHumanEntity; // CraftBukkit + +public interface IInventory extends INamableTileEntity { + + int getSize(); + + boolean P_(); + + ItemStack getItem(int i); + + ItemStack splitStack(int i, int j); + + ItemStack splitWithoutUpdate(int i); + + void setItem(int i, ItemStack itemstack); + + int getMaxStackSize(); + + void update(); + + boolean a(EntityHuman entityhuman); + + void startOpen(EntityHuman entityhuman); + + void closeContainer(EntityHuman entityhuman); + + boolean b(int i, ItemStack itemstack); + + int getProperty(int i); + + void setProperty(int i, int j); + + int h(); + + void clear(); + + default int n() { + return 0; + } + + default int U_() { + return 0; + } + + // CraftBukkit start + java.util.List getContents(); + + void onOpen(CraftHumanEntity who); + + void onClose(CraftHumanEntity who); + + java.util.List getViewers(); + + org.bukkit.inventory.InventoryHolder getOwner(); + + void setMaxStackSize(int size); + + org.bukkit.Location getLocation(); + + default IRecipe getCurrentRecipe() { + return null; + } + + default void setCurrentRecipe(IRecipe recipe) { + } + + int MAX_STACK = 64; + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/IRangedEntity.java b/src/main/java/net/minecraft/server/IRangedEntity.java new file mode 100644 index 000000000000..b763bd11dd07 --- /dev/null +++ b/src/main/java/net/minecraft/server/IRangedEntity.java @@ -0,0 +1,8 @@ +package net.minecraft.server; + +public interface IRangedEntity { + + void a(EntityLiving entityliving, float f); default void rangedAttack(EntityLiving entityliving, float f) { a(entityliving, f); } // Paper - OBFHELPER + + void s(boolean flag); default void setChargingAttack(boolean flag) { s(flag); } // Paper - OBFHELPER +} diff --git a/src/main/java/net/minecraft/server/IRecipe.java b/src/main/java/net/minecraft/server/IRecipe.java new file mode 100644 index 000000000000..0075fe09e946 --- /dev/null +++ b/src/main/java/net/minecraft/server/IRecipe.java @@ -0,0 +1,38 @@ +package net.minecraft.server; + +public interface IRecipe { + + boolean a(IInventory iinventory, World world); + + ItemStack craftItem(IInventory iinventory); + + ItemStack d(); + + default NonNullList b(IInventory iinventory) { + NonNullList nonnulllist = NonNullList.a(iinventory.getSize(), ItemStack.a); + + for (int i = 0; i < nonnulllist.size(); ++i) { + Item item = iinventory.getItem(i).getItem(); + + if (item.p()) { + nonnulllist.set(i, new ItemStack(item.o())); + } + } + + return nonnulllist; + } + + default NonNullList e() { + return NonNullList.a(); + } + + default boolean c() { + return false; + } + + MinecraftKey getKey(); + + RecipeSerializer a(); + + org.bukkit.inventory.Recipe toBukkitRecipe(); // CraftBukkit +} diff --git a/src/main/java/net/minecraft/server/IWorldReader.java b/src/main/java/net/minecraft/server/IWorldReader.java new file mode 100644 index 000000000000..cf53118ccb34 --- /dev/null +++ b/src/main/java/net/minecraft/server/IWorldReader.java @@ -0,0 +1,363 @@ +package net.minecraft.server; + +import java.util.Collections; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import javax.annotation.Nullable; + +public interface IWorldReader extends IBlockAccess { + + boolean isEmpty(BlockPosition blockposition); + + BiomeBase getBiome(BlockPosition blockposition); + + int getBrightness(EnumSkyBlock enumskyblock, BlockPosition blockposition); + + default boolean z(BlockPosition blockposition) { + if (blockposition.getY() >= this.getSeaLevel()) { + return this.e(blockposition); + } else { + BlockPosition blockposition1 = new BlockPosition(blockposition.getX(), this.getSeaLevel(), blockposition.getZ()); + + if (!this.e(blockposition1)) { + return false; + } else { + for (blockposition1 = blockposition1.down(); blockposition1.getY() > blockposition.getY(); blockposition1 = blockposition1.down()) { + IBlockData iblockdata = this.getType(blockposition1); + + if (iblockdata.b(this, blockposition1) > 0 && !iblockdata.getMaterial().isLiquid()) { + return false; + } + } + + return true; + } + } + } + + int getLightLevel(BlockPosition blockposition, int i); + // Paper start + default @Nullable + IBlockData getTypeIfLoaded(BlockPosition var1) { + return isLoaded(var1) ? getType(var1) : null; + } + + default @Nullable + Block getBlockIfLoaded(BlockPosition var1) { + return isLoaded(var1) ? getType(var1).getBlock() : null; + } + + default @Nullable + Material getMaterialIfLoaded(BlockPosition var1) { + return isLoaded(var1) ? getType(var1).getMaterial() : null; + } + // Paper end + + boolean isChunkLoaded(int i, int j, boolean flag); + + boolean e(BlockPosition blockposition); + + default BlockPosition getHighestBlockYAt(HeightMap.Type heightmap_type, BlockPosition blockposition) { + return new BlockPosition(blockposition.getX(), this.a(heightmap_type, blockposition.getX(), blockposition.getZ()), blockposition.getZ()); + } + + int a(HeightMap.Type heightmap_type, int i, int j); + + default float A(BlockPosition blockposition) { + return this.o().i()[this.getLightLevel(blockposition)]; + } + + @Nullable + default EntityHuman findNearbyPlayer(Entity entity, double d0) { + return this.a(entity.locX, entity.locY, entity.locZ, d0, false); + } + + @Nullable + default EntityHuman b(Entity entity, double d0) { + return this.a(entity.locX, entity.locY, entity.locZ, d0, true); + } + + @Nullable + default EntityHuman a(double d0, double d1, double d2, double d3, boolean flag) { + Predicate predicate = flag ? IEntitySelector.e : IEntitySelector.f; + + return this.a(d0, d1, d2, d3, predicate); + } + + @Nullable + EntityHuman a(double d0, double d1, double d2, double d3, Predicate predicate); + + int c(); + + WorldBorder getWorldBorder(); + + boolean a(@Nullable Entity entity, VoxelShape voxelshape); + + int a(BlockPosition blockposition, EnumDirection enumdirection); + + boolean e(); + + int getSeaLevel(); + + default boolean a(IBlockData iblockdata, BlockPosition blockposition) { + VoxelShape voxelshape = iblockdata.getCollisionShape(this, blockposition); + + return voxelshape.isEmpty() || this.a((Entity) null, voxelshape.a((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ())); + } + + default boolean a_(@Nullable Entity entity, AxisAlignedBB axisalignedbb) { + return this.a(entity, VoxelShapes.a(axisalignedbb)); + } + + default Stream a(VoxelShape voxelshape, VoxelShape voxelshape1, boolean flag) { + int i = MathHelper.floor(voxelshape.b(EnumDirection.EnumAxis.X)) - 1; + int j = MathHelper.f(voxelshape.c(EnumDirection.EnumAxis.X)) + 1; + int k = MathHelper.floor(voxelshape.b(EnumDirection.EnumAxis.Y)) - 1; + int l = MathHelper.f(voxelshape.c(EnumDirection.EnumAxis.Y)) + 1; + int i1 = MathHelper.floor(voxelshape.b(EnumDirection.EnumAxis.Z)) - 1; + int j1 = MathHelper.f(voxelshape.c(EnumDirection.EnumAxis.Z)) + 1; + WorldBorder worldborder = this.getWorldBorder(); + boolean flag1 = worldborder.b() < (double) i && (double) j < worldborder.d() && worldborder.c() < (double) i1 && (double) j1 < worldborder.e(); + VoxelShapeBitSet voxelshapebitset = new VoxelShapeBitSet(j - i, l - k, j1 - i1); + Predicate predicate = (voxelshape2) -> { + return !voxelshape2.isEmpty() && VoxelShapes.c(voxelshape, voxelshape2, OperatorBoolean.AND); + }; + Stream stream = StreamSupport.stream(BlockPosition.MutableBlockPosition.b(i, k, i1, j - 1, l - 1, j1 - 1).spliterator(), false).map((blockposition_mutableblockposition) -> { + int k1 = blockposition_mutableblockposition.getX(); + int l1 = blockposition_mutableblockposition.getY(); + int i2 = blockposition_mutableblockposition.getZ(); + boolean flag2 = k1 == i || k1 == j - 1; + boolean flag3 = l1 == k || l1 == l - 1; + boolean flag4 = i2 == i1 || i2 == j1 - 1; + + if ((!flag2 || !flag3) && (!flag3 || !flag4) && (!flag4 || !flag2) && this.isLoaded(blockposition_mutableblockposition)) { + VoxelShape voxelshape2; + + if (flag && !flag1 && !worldborder.a((BlockPosition) blockposition_mutableblockposition)) { + voxelshape2 = VoxelShapes.b(); + } else { + voxelshape2 = this.getType(blockposition_mutableblockposition).getCollisionShape(this, blockposition_mutableblockposition); + } + + VoxelShape voxelshape3 = voxelshape1.a((double) (-k1), (double) (-l1), (double) (-i2)); + + if (VoxelShapes.c(voxelshape3, voxelshape2, OperatorBoolean.AND)) { + return VoxelShapes.a(); + } else if (voxelshape2 == VoxelShapes.b()) { + voxelshapebitset.a(k1 - i, l1 - k, i2 - i1, true, true); + return VoxelShapes.a(); + } else { + return voxelshape2.a((double) k1, (double) l1, (double) i2); + } + } else { + return VoxelShapes.a(); + } + }).filter(predicate); + + return Stream.concat(stream, Stream.generate(() -> { + return new VoxelShapeWorldRegion(voxelshapebitset, i, k, i1); + }).limit(1L).filter(predicate)); + } + + default Stream a(@Nullable Entity entity, AxisAlignedBB axisalignedbb, double d0, double d1, double d2) { + return this.a(entity, axisalignedbb, Collections.emptySet(), d0, d1, d2); + } + + default Stream a(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set set, double d0, double d1, double d2) { + double d3 = 1.0E-7D; + VoxelShape voxelshape = VoxelShapes.a(axisalignedbb); + VoxelShape voxelshape1 = VoxelShapes.a(axisalignedbb.d(d0 > 0.0D ? -1.0E-7D : 1.0E-7D, d1 > 0.0D ? -1.0E-7D : 1.0E-7D, d2 > 0.0D ? -1.0E-7D : 1.0E-7D)); + VoxelShape voxelshape2 = VoxelShapes.b(VoxelShapes.a(axisalignedbb.b(d0, d1, d2).g(1.0E-7D)), voxelshape1, OperatorBoolean.ONLY_FIRST); + + return this.a(entity, voxelshape2, voxelshape, set); + } + + default Stream b(@Nullable Entity entity, AxisAlignedBB axisalignedbb) { + return this.a(entity, VoxelShapes.a(axisalignedbb), VoxelShapes.a(), Collections.emptySet()); + } + + default Stream a(@Nullable Entity entity, VoxelShape voxelshape, VoxelShape voxelshape1, Set set) { + boolean flag = entity != null && entity.bG(); + boolean flag1 = entity != null && this.i(entity); + + if (entity != null && flag == flag1) { + entity.n(!flag1); + } + + return this.a(voxelshape, voxelshape1, flag1); + } + + default boolean i(Entity entity) { + WorldBorder worldborder = this.getWorldBorder(); + double d0 = worldborder.b(); + double d1 = worldborder.c(); + double d2 = worldborder.d(); + double d3 = worldborder.e(); + + if (entity.bG()) { + ++d0; + ++d1; + --d2; + --d3; + } else { + --d0; + --d1; + ++d2; + ++d3; + } + + return entity.locX > d0 && entity.locX < d2 && entity.locZ > d1 && entity.locZ < d3; + } + + default boolean a(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Set set) { + return this.a(entity, VoxelShapes.a(axisalignedbb), VoxelShapes.a(), set).allMatch(VoxelShape::isEmpty); + } + + default boolean getCubes(@Nullable Entity entity, AxisAlignedBB axisalignedbb) { + return this.a(entity, axisalignedbb, Collections.emptySet()); + } + + default boolean B(BlockPosition blockposition) { + return this.getFluid(blockposition).a(TagsFluid.WATER); + } + + default boolean containsLiquid(AxisAlignedBB axisalignedbb) { + int i = MathHelper.floor(axisalignedbb.minX); + int j = MathHelper.f(axisalignedbb.maxX); + int k = MathHelper.floor(axisalignedbb.minY); + int l = MathHelper.f(axisalignedbb.maxY); + int i1 = MathHelper.floor(axisalignedbb.minZ); + int j1 = MathHelper.f(axisalignedbb.maxZ); + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + for (int k1 = i; k1 < j; ++k1) { + for (int l1 = k; l1 < l; ++l1) { + for (int i2 = i1; i2 < j1; ++i2) { + IBlockData iblockdata = this.getType(blockposition_b.c(k1, l1, i2)); + + if (!iblockdata.s().e()) { + boolean flag = true; + + return flag; + } + } + } + } + + return false; + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + } + + default int getLightLevel(BlockPosition blockposition) { + return this.d(blockposition, this.c()); + } + + default int d(BlockPosition blockposition, int i) { + if (blockposition.getX() >= -30000000 && blockposition.getZ() >= -30000000 && blockposition.getX() < 30000000 && blockposition.getZ() < 30000000) { + if (this.getType(blockposition).c(this, blockposition)) { + int j = this.getLightLevel(blockposition.up(), i); + int k = this.getLightLevel(blockposition.east(), i); + int l = this.getLightLevel(blockposition.west(), i); + int i1 = this.getLightLevel(blockposition.south(), i); + int j1 = this.getLightLevel(blockposition.north(), i); + + if (k > j) { + j = k; + } + + if (l > j) { + j = l; + } + + if (i1 > j) { + j = i1; + } + + if (j1 > j) { + j = j1; + } + + return j; + } else { + return this.getLightLevel(blockposition, i); + } + } else { + return 15; + } + } + + default boolean isLoaded(BlockPosition blockposition) { + return this.b(blockposition, true); + } + + default boolean b(BlockPosition blockposition, boolean flag) { + return this.isChunkLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4, flag); + } + + default boolean areChunksLoaded(BlockPosition blockposition, int i) { + return this.areChunksLoaded(blockposition, i, true); + } + + default boolean areChunksLoaded(BlockPosition blockposition, int i, boolean flag) { + return this.isAreaLoaded(blockposition.getX() - i, blockposition.getY() - i, blockposition.getZ() - i, blockposition.getX() + i, blockposition.getY() + i, blockposition.getZ() + i, flag); + } + + default boolean areChunksLoadedBetween(BlockPosition blockposition, BlockPosition blockposition1) { + return this.areChunksLoadedBetween(blockposition, blockposition1, true); + } + + default boolean areChunksLoadedBetween(BlockPosition blockposition, BlockPosition blockposition1, boolean flag) { + return this.isAreaLoaded(blockposition.getX(), blockposition.getY(), blockposition.getZ(), blockposition1.getX(), blockposition1.getY(), blockposition1.getZ(), flag); + } + + default boolean a(StructureBoundingBox structureboundingbox) { + return this.a(structureboundingbox, true); + } + + default boolean a(StructureBoundingBox structureboundingbox, boolean flag) { + return this.isAreaLoaded(structureboundingbox.a, structureboundingbox.b, structureboundingbox.c, structureboundingbox.d, structureboundingbox.e, structureboundingbox.f, flag); + } + + default boolean isAreaLoaded(int i, int j, int k, int l, int i1, int j1, boolean flag) { + if (i1 >= 0 && j < 256) { + i >>= 4; + k >>= 4; + l >>= 4; + j1 >>= 4; + + for (int k1 = i; k1 <= l; ++k1) { + for (int l1 = k; l1 <= j1; ++l1) { + if (!this.isChunkLoaded(k1, l1, flag)) { + return false; + } + } + } + + return true; + } else { + return false; + } + } + + WorldProvider o(); +} diff --git a/src/main/java/net/minecraft/server/IWorldWriter.java b/src/main/java/net/minecraft/server/IWorldWriter.java new file mode 100644 index 000000000000..028e9fe25f3e --- /dev/null +++ b/src/main/java/net/minecraft/server/IWorldWriter.java @@ -0,0 +1,16 @@ +package net.minecraft.server; + +public interface IWorldWriter { + + boolean setTypeAndData(BlockPosition blockposition, IBlockData iblockdata, int i); + + boolean addEntity(Entity entity); + + boolean addEntity(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason); // CraftBukkit + + boolean setAir(BlockPosition blockposition); + + void a(EnumSkyBlock enumskyblock, BlockPosition blockposition, int i); + + boolean setAir(BlockPosition blockposition, boolean flag); +} diff --git a/src/main/java/net/minecraft/server/InventoryCraftResult.java b/src/main/java/net/minecraft/server/InventoryCraftResult.java new file mode 100644 index 000000000000..ce8fa1c97581 --- /dev/null +++ b/src/main/java/net/minecraft/server/InventoryCraftResult.java @@ -0,0 +1,137 @@ +package net.minecraft.server; + +import java.util.Iterator; +import javax.annotation.Nullable; +// CraftBukkit start +import org.bukkit.Location; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class InventoryCraftResult implements IInventory, RecipeHolder { + + private final NonNullList items; + private IRecipe b; + + // CraftBukkit start + private int maxStack = MAX_STACK; + + public java.util.List getContents() { + return this.items; + } + + public org.bukkit.inventory.InventoryHolder getOwner() { + return null; // Result slots don't get an owner + } + + // Don't need a transaction; the InventoryCrafting keeps track of it for us + public void onOpen(CraftHumanEntity who) {} + public void onClose(CraftHumanEntity who) {} + public java.util.List getViewers() { + return new java.util.ArrayList(); + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + + @Override + public Location getLocation() { + return null; + } + // CraftBukkit end + + public InventoryCraftResult() { + this.items = NonNullList.a(1, ItemStack.a); + } + + public int getSize() { + return 1; + } + + public boolean P_() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + public ItemStack getItem(int i) { + return (ItemStack) this.items.get(0); + } + + public IChatBaseComponent getDisplayName() { + return new ChatComponentText("Result"); + } + + public boolean hasCustomName() { + return false; + } + + @Nullable + public IChatBaseComponent getCustomName() { + return null; + } + + public ItemStack splitStack(int i, int j) { + return ContainerUtil.a(this.items, 0); + } + + public ItemStack splitWithoutUpdate(int i) { + return ContainerUtil.a(this.items, 0); + } + + public void setItem(int i, ItemStack itemstack) { + this.items.set(0, itemstack); + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public void update() {} + + public boolean a(EntityHuman entityhuman) { + return true; + } + + public void startOpen(EntityHuman entityhuman) {} + + public void closeContainer(EntityHuman entityhuman) {} + + public boolean b(int i, ItemStack itemstack) { + return true; + } + + public int getProperty(int i) { + return 0; + } + + public void setProperty(int i, int j) {} + + public int h() { + return 0; + } + + public void clear() { + this.items.clear(); + } + + public void a(@Nullable IRecipe irecipe) { + this.b = irecipe; + } + + @Nullable + public IRecipe i() { + return this.b; + } +} diff --git a/src/main/java/net/minecraft/server/InventoryCrafting.java b/src/main/java/net/minecraft/server/InventoryCrafting.java new file mode 100644 index 000000000000..e9c798e0c3f5 --- /dev/null +++ b/src/main/java/net/minecraft/server/InventoryCrafting.java @@ -0,0 +1,191 @@ +package net.minecraft.server; + +import java.util.Iterator; +import javax.annotation.Nullable; +// CraftBukkit start +import java.util.List; +import org.bukkit.Location; + +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +// CraftBukkit end + +public class InventoryCrafting implements IInventory, AutoRecipeOutput { + + private final NonNullList items; + private final int b; + private final int c; + public final Container container; + + // CraftBukkit start - add fields + public List transaction = new java.util.ArrayList(); + private IRecipe currentRecipe; + public IInventory resultInventory; + private EntityHuman owner; + private int maxStack = MAX_STACK; + + public List getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public InventoryType getInvType() { + return items.size() == 4 ? InventoryType.CRAFTING : InventoryType.WORKBENCH; + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public org.bukkit.inventory.InventoryHolder getOwner() { + return (owner == null) ? null : owner.getBukkitEntity(); + } + + public void setMaxStackSize(int size) { + maxStack = size; + resultInventory.setMaxStackSize(size); + } + + @Override + public Location getLocation() { + return owner.getBukkitEntity().getLocation(); + } + + @Override + public IRecipe getCurrentRecipe() { + return currentRecipe; + } + + @Override + public void setCurrentRecipe(IRecipe currentRecipe) { + this.currentRecipe = currentRecipe; + } + + public InventoryCrafting(Container container, int i, int j, EntityHuman player) { + this(container, i, j); + this.owner = player; + } + // CraftBukkit end + + public InventoryCrafting(Container container, int i, int j) { + this.items = NonNullList.a(i * j, ItemStack.a); + this.container = container; + this.b = i; + this.c = j; + } + + public int getSize() { + return this.items.size(); + } + + public boolean P_() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + public ItemStack getItem(int i) { + return i >= this.getSize() ? ItemStack.a : (ItemStack) this.items.get(i); + } + + public IChatBaseComponent getDisplayName() { + return new ChatMessage("container.crafting", new Object[0]); + } + + public boolean hasCustomName() { + return false; + } + + @Nullable + public IChatBaseComponent getCustomName() { + return null; + } + + public ItemStack splitWithoutUpdate(int i) { + return ContainerUtil.a(this.items, i); + } + + public ItemStack splitStack(int i, int j) { + ItemStack itemstack = ContainerUtil.a(this.items, i, j); + + if (!itemstack.isEmpty()) { + this.container.a((IInventory) this); + } + + return itemstack; + } + + public void setItem(int i, ItemStack itemstack) { + this.items.set(i, itemstack); + this.container.a((IInventory) this); + } + + public int getMaxStackSize() { + return 64; + } + + public void update() {} + + public boolean a(EntityHuman entityhuman) { + return true; + } + + public void startOpen(EntityHuman entityhuman) {} + + public void closeContainer(EntityHuman entityhuman) {} + + public boolean b(int i, ItemStack itemstack) { + return true; + } + + public int getProperty(int i) { + return 0; + } + + public void setProperty(int i, int j) {} + + public int h() { + return 0; + } + + public void clear() { + this.items.clear(); + } + + public int n() { + return this.c; + } + + public int U_() { + return this.b; + } + + public void a(AutoRecipeStackManager autorecipestackmanager) { + Iterator iterator = this.items.iterator(); + + while (iterator.hasNext()) { + ItemStack itemstack = (ItemStack) iterator.next(); + + autorecipestackmanager.a(itemstack); + } + + } +} diff --git a/src/main/java/net/minecraft/server/InventoryEnderChest.java b/src/main/java/net/minecraft/server/InventoryEnderChest.java new file mode 100644 index 000000000000..f50bae0123fa --- /dev/null +++ b/src/main/java/net/minecraft/server/InventoryEnderChest.java @@ -0,0 +1,88 @@ +package net.minecraft.server; + +import org.bukkit.Location; +import org.bukkit.inventory.InventoryHolder; + +public class InventoryEnderChest extends InventorySubcontainer { + + private TileEntityEnderChest a; public TileEntityEnderChest getTileEntity() { return a; } // Paper - OBFHELPER + // CraftBukkit start + private final EntityHuman owner; + + public InventoryHolder getBukkitOwner() { + return owner.getBukkitEntity(); + } + + @Override + public Location getLocation() { + if (getTileEntity() == null) return null; // Paper - return null if there is no TE bound (opened by plugin) + return new Location(this.a.getWorld().getWorld(), this.a.getPosition().getX(), this.a.getPosition().getY(), this.a.getPosition().getZ()); + } + + public InventoryEnderChest(EntityHuman owner) { + super(new ChatMessage("container.enderchest", new Object[0]), 27); + this.owner = owner; + // CraftBukkit end + } + + public void a(TileEntityEnderChest tileentityenderchest) { + this.a = tileentityenderchest; + } + + public void a(NBTTagList nbttaglist) { + int i; + + for (i = 0; i < this.getSize(); ++i) { + this.setItem(i, ItemStack.a); + } + + for (i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist.getCompound(i); + int j = nbttagcompound.getByte("Slot") & 255; + + if (j >= 0 && j < this.getSize()) { + this.setItem(j, ItemStack.a(nbttagcompound)); + } + } + + } + + public NBTTagList i() { + NBTTagList nbttaglist = new NBTTagList(); + + for (int i = 0; i < this.getSize(); ++i) { + ItemStack itemstack = this.getItem(i); + + if (!itemstack.isEmpty()) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setByte("Slot", (byte) i); + itemstack.save(nbttagcompound); + nbttaglist.add((NBTBase) nbttagcompound); + } + } + + return nbttaglist; + } + + public boolean a(EntityHuman entityhuman) { + return this.a != null && !this.a.a(entityhuman) ? false : super.a(entityhuman); + } + + public void startOpen(EntityHuman entityhuman) { + if (this.a != null) { + this.a.c(); + } + + super.startOpen(entityhuman); + } + + public void closeContainer(EntityHuman entityhuman) { + if (this.a != null) { + this.a.d(); + } + + super.closeContainer(entityhuman); + this.a = null; + } +} diff --git a/src/main/java/net/minecraft/server/InventoryHorseChest.java b/src/main/java/net/minecraft/server/InventoryHorseChest.java new file mode 100644 index 000000000000..b9dd2ad5ac88 --- /dev/null +++ b/src/main/java/net/minecraft/server/InventoryHorseChest.java @@ -0,0 +1,10 @@ +package net.minecraft.server; + +public class InventoryHorseChest extends InventorySubcontainer { + + // CraftBukkit start + public InventoryHorseChest(IChatBaseComponent ichatbasecomponent, int i, EntityHorseAbstract owner) { + super(ichatbasecomponent, i, (org.bukkit.entity.AbstractHorse) owner.getBukkitEntity()); + // CraftBukkit end + } +} diff --git a/src/main/java/net/minecraft/server/InventoryLargeChest.java b/src/main/java/net/minecraft/server/InventoryLargeChest.java new file mode 100644 index 000000000000..cfc3bef8ff52 --- /dev/null +++ b/src/main/java/net/minecraft/server/InventoryLargeChest.java @@ -0,0 +1,189 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; +// CraftBukkit start +import java.util.ArrayList; +import java.util.List; +import org.bukkit.Location; + +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class InventoryLargeChest implements ITileInventory { + + private final IChatBaseComponent a; + public final ITileInventory left; + public final ITileInventory right; + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + + public List getContents() { + List result = new ArrayList(this.getSize()); + for (int i = 0; i < this.getSize(); i++) { + result.add(this.getItem(i)); + } + return result; + } + + public void onOpen(CraftHumanEntity who) { + this.left.onOpen(who); + this.right.onOpen(who); + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + this.left.onClose(who); + this.right.onClose(who); + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public org.bukkit.inventory.InventoryHolder getOwner() { + return null; // This method won't be called since CraftInventoryDoubleChest doesn't defer to here + } + + public void setMaxStackSize(int size) { + this.left.setMaxStackSize(size); + this.right.setMaxStackSize(size); + } + + @Override + public Location getLocation() { + return left.getLocation(); // TODO: right? + } + // CraftBukkit end + + public InventoryLargeChest(IChatBaseComponent ichatbasecomponent, ITileInventory itileinventory, ITileInventory itileinventory1) { + this.a = ichatbasecomponent; + if (itileinventory == null) { + itileinventory = itileinventory1; + } + + if (itileinventory1 == null) { + itileinventory1 = itileinventory; + } + + this.left = itileinventory; + this.right = itileinventory1; + if (itileinventory.isLocked()) { + itileinventory1.setLock(itileinventory.getLock()); + } else if (itileinventory1.isLocked()) { + itileinventory.setLock(itileinventory1.getLock()); + } + + } + + public int getSize() { + return this.left.getSize() + this.right.getSize(); + } + + public boolean P_() { + return this.left.P_() && this.right.P_(); + } + + public boolean a(IInventory iinventory) { + return this.left == iinventory || this.right == iinventory; + } + + public IChatBaseComponent getDisplayName() { + return this.left.hasCustomName() ? this.left.getDisplayName() : (this.right.hasCustomName() ? this.right.getDisplayName() : this.a); + } + + public boolean hasCustomName() { + return this.left.hasCustomName() || this.right.hasCustomName(); + } + + @Nullable + public IChatBaseComponent getCustomName() { + return this.left.hasCustomName() ? this.left.getCustomName() : this.right.getCustomName(); + } + + public ItemStack getItem(int i) { + return i >= this.left.getSize() ? this.right.getItem(i - this.left.getSize()) : this.left.getItem(i); + } + + public ItemStack splitStack(int i, int j) { + return i >= this.left.getSize() ? this.right.splitStack(i - this.left.getSize(), j) : this.left.splitStack(i, j); + } + + public ItemStack splitWithoutUpdate(int i) { + return i >= this.left.getSize() ? this.right.splitWithoutUpdate(i - this.left.getSize()) : this.left.splitWithoutUpdate(i); + } + + public void setItem(int i, ItemStack itemstack) { + if (i >= this.left.getSize()) { + this.right.setItem(i - this.left.getSize(), itemstack); + } else { + this.left.setItem(i, itemstack); + } + + } + + public int getMaxStackSize() { + return Math.min(this.left.getMaxStackSize(), this.right.getMaxStackSize()); // CraftBukkit - check both sides + } + + public void update() { + this.left.update(); + this.right.update(); + } + + public boolean a(EntityHuman entityhuman) { + return this.left.a(entityhuman) && this.right.a(entityhuman); + } + + public void startOpen(EntityHuman entityhuman) { + this.left.startOpen(entityhuman); + this.right.startOpen(entityhuman); + } + + public void closeContainer(EntityHuman entityhuman) { + this.left.closeContainer(entityhuman); + this.right.closeContainer(entityhuman); + } + + public boolean b(int i, ItemStack itemstack) { + return true; + } + + public int getProperty(int i) { + return 0; + } + + public void setProperty(int i, int j) {} + + public int h() { + return 0; + } + + public boolean isLocked() { + return this.left.isLocked() || this.right.isLocked(); + } + + public void setLock(ChestLock chestlock) { + this.left.setLock(chestlock); + this.right.setLock(chestlock); + } + + public ChestLock getLock() { + return this.left.getLock(); + } + + public String getContainerName() { + return this.left.getContainerName(); + } + + public Container createContainer(PlayerInventory playerinventory, EntityHuman entityhuman) { + return new ContainerChest(playerinventory, this, entityhuman); + } + + public void clear() { + this.left.clear(); + this.right.clear(); + } +} diff --git a/src/main/java/net/minecraft/server/InventoryMerchant.java b/src/main/java/net/minecraft/server/InventoryMerchant.java new file mode 100644 index 000000000000..7335f8c22fc1 --- /dev/null +++ b/src/main/java/net/minecraft/server/InventoryMerchant.java @@ -0,0 +1,215 @@ +package net.minecraft.server; + +import java.util.Iterator; +import javax.annotation.Nullable; +// CraftBukkit start +import java.util.List; +import org.bukkit.Location; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.entity.CraftVillager; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class InventoryMerchant implements IInventory { + + private final IMerchant merchant; + private final NonNullList itemsInSlots; + private final EntityHuman player; + private MerchantRecipe recipe; + public int selectedIndex; + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public List getContents() { + return this.itemsInSlots; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int i) { + maxStack = i; + } + + public org.bukkit.inventory.InventoryHolder getOwner() { + return (merchant instanceof EntityVillager) ? (CraftVillager) ((EntityVillager) this.merchant).getBukkitEntity() : null; + } + + @Override + public Location getLocation() { + return (merchant instanceof EntityVillager) ? ((EntityVillager) this.merchant).getBukkitEntity().getLocation() : null; + } + // CraftBukkit end + + public InventoryMerchant(EntityHuman entityhuman, IMerchant imerchant) { + this.itemsInSlots = NonNullList.a(3, ItemStack.a); + this.player = entityhuman; + this.merchant = imerchant; + } + + public int getSize() { + return this.itemsInSlots.size(); + } + + public boolean P_() { + Iterator iterator = this.itemsInSlots.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + public ItemStack getItem(int i) { + return (ItemStack) this.itemsInSlots.get(i); + } + + public ItemStack splitStack(int i, int j) { + ItemStack itemstack = (ItemStack) this.itemsInSlots.get(i); + + if (i == 2 && !itemstack.isEmpty()) { + return ContainerUtil.a(this.itemsInSlots, i, itemstack.getCount()); + } else { + ItemStack itemstack1 = ContainerUtil.a(this.itemsInSlots, i, j); + + if (!itemstack1.isEmpty() && this.e(i)) { + this.i(); + } + + return itemstack1; + } + } + + private boolean e(int i) { + return i == 0 || i == 1; + } + + public ItemStack splitWithoutUpdate(int i) { + return ContainerUtil.a(this.itemsInSlots, i); + } + + public void setItem(int i, ItemStack itemstack) { + this.itemsInSlots.set(i, itemstack); + if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { + itemstack.setCount(this.getMaxStackSize()); + } + + if (this.e(i)) { + this.i(); + } + + } + + public IChatBaseComponent getDisplayName() { + return merchant.getScoreboardDisplayName(); // CraftBukkit + } + + public boolean hasCustomName() { + return false; + } + + @Nullable + public IChatBaseComponent getCustomName() { + return null; + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public boolean a(EntityHuman entityhuman) { + return this.merchant.getTrader() == entityhuman; + } + + public void startOpen(EntityHuman entityhuman) {} + + public void closeContainer(EntityHuman entityhuman) {} + + public boolean b(int i, ItemStack itemstack) { + return true; + } + + public void update() { + this.i(); + } + + public void i() { + this.recipe = null; + ItemStack itemstack = (ItemStack) this.itemsInSlots.get(0); + ItemStack itemstack1 = (ItemStack) this.itemsInSlots.get(1); + + if (itemstack.isEmpty()) { + itemstack = itemstack1; + itemstack1 = ItemStack.a; + } + + if (itemstack.isEmpty()) { + this.setItem(2, ItemStack.a); + } else { + MerchantRecipeList merchantrecipelist = this.merchant.getOffers(this.player); + + if (merchantrecipelist != null) { + MerchantRecipe merchantrecipe = merchantrecipelist.a(itemstack, itemstack1, this.selectedIndex); + + if (merchantrecipe != null && !merchantrecipe.h()) { + this.recipe = merchantrecipe; + this.setItem(2, merchantrecipe.getBuyItem3().cloneItemStack()); + } else if (!itemstack1.isEmpty()) { + merchantrecipe = merchantrecipelist.a(itemstack1, itemstack, this.selectedIndex); + if (merchantrecipe != null && !merchantrecipe.h()) { + this.recipe = merchantrecipe; + this.setItem(2, merchantrecipe.getBuyItem3().cloneItemStack()); + } else { + this.setItem(2, ItemStack.a); + } + } else { + this.setItem(2, ItemStack.a); + } + } + + this.merchant.a(this.getItem(2)); + } + + } + + public MerchantRecipe getRecipe() { + return this.recipe; + } + + public void d(int i) { + this.selectedIndex = i; + this.i(); + } + + public int getProperty(int i) { + return 0; + } + + public void setProperty(int i, int j) {} + + public int h() { + return 0; + } + + public void clear() { + this.itemsInSlots.clear(); + } +} diff --git a/src/main/java/net/minecraft/server/InventorySubcontainer.java b/src/main/java/net/minecraft/server/InventorySubcontainer.java new file mode 100644 index 000000000000..126929eea52e --- /dev/null +++ b/src/main/java/net/minecraft/server/InventorySubcontainer.java @@ -0,0 +1,235 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; + +// CraftBukkit start +import java.util.List; +import org.bukkit.Location; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class InventorySubcontainer implements IInventory, AutoRecipeOutput { + + private final IChatBaseComponent a; + private final int b; + public final NonNullList items; + private List d; + private IChatBaseComponent e; + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + protected org.bukkit.inventory.InventoryHolder bukkitOwner; + + public List getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int i) { + maxStack = i; + } + + public org.bukkit.inventory.InventoryHolder getOwner() { + return bukkitOwner; + } + + @Override + public Location getLocation() { + return null; + } + + public InventorySubcontainer(IChatBaseComponent ichatbasecomponent, int i) { + this(ichatbasecomponent, i, null); + } + + public InventorySubcontainer(IChatBaseComponent ichatbasecomponent, int i, org.bukkit.inventory.InventoryHolder owner) { + this.bukkitOwner = owner; + // CraftBukkit end + this.a = ichatbasecomponent; + this.b = i; + this.items = NonNullList.a(i, ItemStack.a); + } + + public void a(IInventoryListener iinventorylistener) { + if (this.d == null) { + this.d = Lists.newArrayList(); + } + + this.d.add(iinventorylistener); + } + + public void b(IInventoryListener iinventorylistener) { + this.d.remove(iinventorylistener); + } + + public ItemStack getItem(int i) { + return i >= 0 && i < this.items.size() ? (ItemStack) this.items.get(i) : ItemStack.a; + } + + public ItemStack splitStack(int i, int j) { + ItemStack itemstack = ContainerUtil.a(this.items, i, j); + + if (!itemstack.isEmpty()) { + this.update(); + } + + return itemstack; + } + + public ItemStack a(ItemStack itemstack) { + ItemStack itemstack1 = itemstack.cloneItemStack(); + + for (int i = 0; i < this.b; ++i) { + ItemStack itemstack2 = this.getItem(i); + + if (itemstack2.isEmpty()) { + this.setItem(i, itemstack1); + this.update(); + return ItemStack.a; + } + + if (ItemStack.c(itemstack2, itemstack1)) { + int j = Math.min(this.getMaxStackSize(), itemstack2.getMaxStackSize()); + int k = Math.min(itemstack1.getCount(), j - itemstack2.getCount()); + + if (k > 0) { + itemstack2.add(k); + itemstack1.subtract(k); + if (itemstack1.isEmpty()) { + this.update(); + return ItemStack.a; + } + } + } + } + + if (itemstack1.getCount() != itemstack.getCount()) { + this.update(); + } + + return itemstack1; + } + + public ItemStack splitWithoutUpdate(int i) { + ItemStack itemstack = (ItemStack) this.items.get(i); + + if (itemstack.isEmpty()) { + return ItemStack.a; + } else { + this.items.set(i, ItemStack.a); + return itemstack; + } + } + + public void setItem(int i, ItemStack itemstack) { + this.items.set(i, itemstack); + if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) { + itemstack.setCount(this.getMaxStackSize()); + } + + this.update(); + } + + public int getSize() { + return this.b; + } + + public boolean P_() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + public IChatBaseComponent getDisplayName() { + return this.e != null ? this.e : this.a; + } + + @Nullable + public IChatBaseComponent getCustomName() { + return this.e; + } + + public boolean hasCustomName() { + return this.e != null; + } + + public void a(@Nullable IChatBaseComponent ichatbasecomponent) { + this.e = ichatbasecomponent; + } + + public int getMaxStackSize() { + return 64; + } + + public void update() { + if (this.d != null) { + for (int i = 0; i < this.d.size(); ++i) { + ((IInventoryListener) this.d.get(i)).a(this); + } + } + + } + + public boolean a(EntityHuman entityhuman) { + return true; + } + + public void startOpen(EntityHuman entityhuman) {} + + public void closeContainer(EntityHuman entityhuman) {} + + public boolean b(int i, ItemStack itemstack) { + return true; + } + + public int getProperty(int i) { + return 0; + } + + public void setProperty(int i, int j) {} + + public int h() { + return 0; + } + + public void clear() { + this.items.clear(); + } + + public void a(AutoRecipeStackManager autorecipestackmanager) { + Iterator iterator = this.items.iterator(); + + while (iterator.hasNext()) { + ItemStack itemstack = (ItemStack) iterator.next(); + + autorecipestackmanager.b(itemstack); + } + + } +} diff --git a/src/main/java/net/minecraft/server/ItemArmor.java b/src/main/java/net/minecraft/server/ItemArmor.java new file mode 100644 index 000000000000..49cb5808257d --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemArmor.java @@ -0,0 +1,127 @@ +package net.minecraft.server; + +import com.google.common.collect.Multimap; +import java.util.List; +import java.util.UUID; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseArmorEvent; +// CraftBukkit end + +public class ItemArmor extends Item { + + private static final UUID[] k = new UUID[] { UUID.fromString("845DB27C-C624-495F-8C9F-6020A9A58B6B"), UUID.fromString("D8499B04-0E66-4726-AB29-64469D734E0D"), UUID.fromString("9F3D476D-C118-4544-8365-64846904B48E"), UUID.fromString("2AD3F246-FEE1-4E67-B886-69FD380BB150")}; + public static final IDispenseBehavior a = new DispenseBehaviorItem() { + protected ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + ItemStack itemstack1 = ItemArmor.a(isourceblock, itemstack); + + return itemstack1.isEmpty() ? super.a(isourceblock, itemstack) : itemstack1; + } + }; + protected final EnumItemSlot b; + protected final int c; + protected final float d; + protected final ArmorMaterial e; + + public static ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING)); + List list = isourceblock.getWorld().a(EntityLiving.class, new AxisAlignedBB(blockposition), IEntitySelector.f.and(new IEntitySelector.EntitySelectorEquipable(itemstack))); + + if (list.isEmpty()) { + return ItemStack.a; + } else { + EntityLiving entityliving = (EntityLiving) list.get(0); + EnumItemSlot enumitemslot = EntityInsentient.e(itemstack); + ItemStack itemstack1 = itemstack.cloneAndSubtract(1); + // CraftBukkit start + World world = isourceblock.getWorld(); + org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + + BlockDispenseArmorEvent event = new BlockDispenseArmorEvent(block, craftItem.clone(), (org.bukkit.craftbukkit.entity.CraftLivingEntity) entityliving.bukkitEntity); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + itemstack.add(1); + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + itemstack.add(1); + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != ItemArmor.a) { + idispensebehavior.dispense(isourceblock, eventStack); + return itemstack; + } + } + // CraftBukkit end + + entityliving.setSlot(enumitemslot, itemstack1); + if (entityliving instanceof EntityInsentient) { + ((EntityInsentient) entityliving).a(enumitemslot, 2.0F); + ((EntityInsentient) entityliving).di(); + } + + return itemstack; + } + } + + public ItemArmor(ArmorMaterial armormaterial, EnumItemSlot enumitemslot, Item.Info item_info) { + super(item_info.b(armormaterial.a(enumitemslot))); + this.e = armormaterial; + this.b = enumitemslot; + this.c = armormaterial.b(enumitemslot); + this.d = armormaterial.e(); + BlockDispenser.a((IMaterial) this, ItemArmor.a); + } + + public EnumItemSlot b() { + return this.b; + } + + public int c() { + return this.e.a(); + } + + public ArmorMaterial d() { + return this.e; + } + + public boolean a(ItemStack itemstack, ItemStack itemstack1) { + return this.e.c().test(itemstack1) || super.a(itemstack, itemstack1); + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + EnumItemSlot enumitemslot = EntityInsentient.e(itemstack); + ItemStack itemstack1 = entityhuman.getEquipment(enumitemslot); + + if (itemstack1.isEmpty()) { + entityhuman.setSlot(enumitemslot, itemstack.cloneItemStack()); + itemstack.setCount(0); + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, itemstack); + } else { + return new InteractionResultWrapper<>(EnumInteractionResult.FAIL, itemstack); + } + } + + public Multimap a(EnumItemSlot enumitemslot) { + Multimap multimap = super.a(enumitemslot); + + if (enumitemslot == this.b) { + multimap.put(GenericAttributes.h.getName(), new AttributeModifier(ItemArmor.k[enumitemslot.b()], "Armor modifier", (double) this.c, 0)); + multimap.put(GenericAttributes.i.getName(), new AttributeModifier(ItemArmor.k[enumitemslot.b()], "Armor toughness", (double) this.d, 0)); + } + + return multimap; + } + + public int e() { + return this.c; + } +} diff --git a/src/main/java/net/minecraft/server/ItemArmorStand.java b/src/main/java/net/minecraft/server/ItemArmorStand.java new file mode 100644 index 000000000000..4dd0e39ec3d2 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemArmorStand.java @@ -0,0 +1,73 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.Random; + +public class ItemArmorStand extends Item { + + public ItemArmorStand(Item.Info item_info) { + super(item_info); + } + + public EnumInteractionResult a(ItemActionContext itemactioncontext) { + EnumDirection enumdirection = itemactioncontext.getClickedFace(); + + if (enumdirection == EnumDirection.DOWN) { + return EnumInteractionResult.FAIL; + } else { + World world = itemactioncontext.getWorld(); + BlockActionContext blockactioncontext = new BlockActionContext(itemactioncontext); + BlockPosition blockposition = blockactioncontext.getClickPosition(); + BlockPosition blockposition1 = blockposition.up(); + + if (blockactioncontext.b() && world.getType(blockposition1).a(blockactioncontext)) { + double d0 = (double) blockposition.getX(); + double d1 = (double) blockposition.getY(); + double d2 = (double) blockposition.getZ(); + List list = world.getEntities((Entity) null, new AxisAlignedBB(d0, d1, d2, d0 + 1.0D, d1 + 2.0D, d2 + 1.0D)); + + if (!list.isEmpty()) { + return EnumInteractionResult.FAIL; + } else { + ItemStack itemstack = itemactioncontext.getItemStack(); + + if (!world.isClientSide) { + world.setAir(blockposition); + world.setAir(blockposition1); + EntityArmorStand entityarmorstand = EntityTypes.ARMOR_STAND.create(world); // Paper + float f = (float) MathHelper.d((MathHelper.g(itemactioncontext.h() - 180.0F) + 22.5F) / 45.0F) * 45.0F; + + entityarmorstand.setPositionRotation(d0 + 0.5D, d1, d2 + 0.5D, f, 0.0F); + this.a(entityarmorstand, world.random); + EntityTypes.a(world, itemactioncontext.getEntity(), entityarmorstand, itemstack.getTag()); + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPlaceEvent(itemactioncontext, entityarmorstand).isCancelled()) { + return EnumInteractionResult.FAIL; + } + // CraftBukkit end + world.addEntity(entityarmorstand); + world.a((EntityHuman) null, entityarmorstand.locX, entityarmorstand.locY, entityarmorstand.locZ, SoundEffects.ENTITY_ARMOR_STAND_PLACE, SoundCategory.BLOCKS, 0.75F, 0.8F); + } + + itemstack.subtract(1); + return EnumInteractionResult.SUCCESS; + } + } else { + return EnumInteractionResult.FAIL; + } + } + } + + private void a(EntityArmorStand entityarmorstand, Random random) { + Vector3f vector3f = entityarmorstand.r(); + float f = random.nextFloat() * 5.0F; + float f1 = random.nextFloat() * 20.0F - 10.0F; + Vector3f vector3f1 = new Vector3f(vector3f.getX() + f, vector3f.getY() + f1, vector3f.getZ()); + + entityarmorstand.setHeadPose(vector3f1); + vector3f = entityarmorstand.s(); + f = random.nextFloat() * 10.0F - 5.0F; + vector3f1 = new Vector3f(vector3f.getX(), vector3f.getY() + f, vector3f.getZ()); + entityarmorstand.setBodyPose(vector3f1); + } +} diff --git a/src/main/java/net/minecraft/server/ItemBlock.java b/src/main/java/net/minecraft/server/ItemBlock.java new file mode 100644 index 000000000000..49ad201c6e93 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemBlock.java @@ -0,0 +1,141 @@ +package net.minecraft.server; + +import java.util.Map; +import javax.annotation.Nullable; +// CraftBukkit start +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.event.block.BlockCanBuildEvent; +// CraftBukkit end + +public class ItemBlock extends Item { + + @Deprecated + private final Block a; + + public ItemBlock(Block block, Item.Info item_info) { + super(item_info); + this.a = block; + } + + public EnumInteractionResult a(ItemActionContext itemactioncontext) { + return this.a(new BlockActionContext(itemactioncontext)); + } + + public EnumInteractionResult a(BlockActionContext blockactioncontext) { + if (!blockactioncontext.b()) { + return EnumInteractionResult.FAIL; + } else { + IBlockData iblockdata = this.b(blockactioncontext); + + if (iblockdata == null) { + return EnumInteractionResult.FAIL; + } else if (!this.a(blockactioncontext, iblockdata)) { + return EnumInteractionResult.FAIL; + } else { + BlockPosition blockposition = blockactioncontext.getClickPosition(); + World world = blockactioncontext.getWorld(); + EntityHuman entityhuman = blockactioncontext.getEntity(); + ItemStack itemstack = blockactioncontext.getItemStack(); + IBlockData iblockdata1 = world.getType(blockposition); + Block block = iblockdata1.getBlock(); + + if (block == iblockdata.getBlock()) { + this.a(blockposition, world, entityhuman, itemstack, iblockdata1); + block.postPlace(world, blockposition, iblockdata1, entityhuman, itemstack); + if (entityhuman instanceof EntityPlayer) { + CriterionTriggers.y.a((EntityPlayer) entityhuman, blockposition, itemstack); + } + } + + SoundEffectType soundeffecttype = block.getStepSound(); + + // world.a(entityhuman, blockposition, soundeffecttype.e(), SoundCategory.BLOCKS, (soundeffecttype.a() + 1.0F) / 2.0F, soundeffecttype.b() * 0.8F); // CraftBukkit - SPIGOT-1288 + itemstack.subtract(1); + return EnumInteractionResult.SUCCESS; + } + } + } + + protected boolean a(BlockPosition blockposition, World world, @Nullable EntityHuman entityhuman, ItemStack itemstack, IBlockData iblockdata) { + return a(world, entityhuman, blockposition, itemstack); + } + + @Nullable + protected IBlockData b(BlockActionContext blockactioncontext) { + IBlockData iblockdata = this.getBlock().getPlacedState(blockactioncontext); + + return iblockdata != null && this.b(blockactioncontext, iblockdata) ? iblockdata : null; + } + + protected boolean b(BlockActionContext blockactioncontext, IBlockData iblockdata) { + // CraftBukkit start - store default return + final World world = blockactioncontext.getWorld(); // Paper + boolean defaultReturn = iblockdata.canPlace(world, blockactioncontext.getClickPosition()) && world.a(iblockdata, blockactioncontext.getClickPosition()) && world.checkNoVisiblePlayerCollisions(blockactioncontext.getEntity(), iblockdata.getCollisionShape(world, blockactioncontext.getClickPosition())); // Paper - Use our entity search + org.bukkit.entity.Player player = (blockactioncontext.getEntity() instanceof EntityPlayer) ? (org.bukkit.entity.Player) blockactioncontext.getEntity().getBukkitEntity() : null; + + BlockCanBuildEvent event = new BlockCanBuildEvent(CraftBlock.at(blockactioncontext.getWorld(), blockactioncontext.getClickPosition()), player, CraftBlockData.fromData(iblockdata), defaultReturn); + blockactioncontext.getWorld().getServer().getPluginManager().callEvent(event); + + return event.isBuildable(); + // CraftBukkit end + } + + protected boolean a(BlockActionContext blockactioncontext, IBlockData iblockdata) { + return blockactioncontext.getWorld().setTypeAndData(blockactioncontext.getClickPosition(), iblockdata, 11); + } + + public static boolean a(World world, @Nullable EntityHuman entityhuman, BlockPosition blockposition, ItemStack itemstack) { + MinecraftServer minecraftserver = world.getMinecraftServer(); + + if (minecraftserver == null) { + return false; + } else { + NBTTagCompound nbttagcompound = itemstack.b("BlockEntityTag"); + + if (nbttagcompound != null) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity != null) { + if (!world.isClientSide && tileentity.isFilteredNBT() && (entityhuman == null || !(entityhuman.isCreativeAndOp() || (entityhuman.abilities.canInstantlyBuild && entityhuman.getBukkitEntity().hasPermission("minecraft.nbt.place"))))) { // Spigot - add permission + return false; + } + + NBTTagCompound nbttagcompound1 = tileentity.save(new NBTTagCompound()); + NBTTagCompound nbttagcompound2 = nbttagcompound1.clone(); + + nbttagcompound1.a(nbttagcompound); + nbttagcompound1.setInt("x", blockposition.getX()); + nbttagcompound1.setInt("y", blockposition.getY()); + nbttagcompound1.setInt("z", blockposition.getZ()); + if (!nbttagcompound1.equals(nbttagcompound2)) { + tileentity.load(nbttagcompound1); + tileentity.update(); + return true; + } + } + } + + return false; + } + } + + public String getName() { + return this.getBlock().m(); + } + + public void a(CreativeModeTab creativemodetab, NonNullList nonnulllist) { + if (this.a(creativemodetab)) { + this.getBlock().a(creativemodetab, nonnulllist); + } + + } + + public Block getBlock() { + return this.a; + } + + public void a(Map map, Item item) { + map.put(this.getBlock(), item); + } +} diff --git a/src/main/java/net/minecraft/server/ItemBoat.java b/src/main/java/net/minecraft/server/ItemBoat.java new file mode 100644 index 000000000000..9af24b906d61 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemBoat.java @@ -0,0 +1,87 @@ +package net.minecraft.server; + +import java.util.List; + +public class ItemBoat extends Item { + + private final EntityBoat.EnumBoatType a; + + public ItemBoat(EntityBoat.EnumBoatType entityboat_enumboattype, Item.Info item_info) { + super(item_info); + this.a = entityboat_enumboattype; + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + float f = 1.0F; + float f1 = entityhuman.lastPitch + (entityhuman.pitch - entityhuman.lastPitch) * 1.0F; + float f2 = entityhuman.lastYaw + (entityhuman.yaw - entityhuman.lastYaw) * 1.0F; + double d0 = entityhuman.lastX + (entityhuman.locX - entityhuman.lastX) * 1.0D; + double d1 = entityhuman.lastY + (entityhuman.locY - entityhuman.lastY) * 1.0D + (double) entityhuman.getHeadHeight(); + double d2 = entityhuman.lastZ + (entityhuman.locZ - entityhuman.lastZ) * 1.0D; + Vec3D vec3d = new Vec3D(d0, d1, d2); + float f3 = MathHelper.cos(-f2 * 0.017453292F - 3.1415927F); + float f4 = MathHelper.sin(-f2 * 0.017453292F - 3.1415927F); + float f5 = -MathHelper.cos(-f1 * 0.017453292F); + float f6 = MathHelper.sin(-f1 * 0.017453292F); + float f7 = f4 * f5; + float f8 = f3 * f5; + double d3 = 5.0D; + Vec3D vec3d1 = vec3d.add((double) f7 * 5.0D, (double) f6 * 5.0D, (double) f8 * 5.0D); + MovingObjectPosition movingobjectposition = world.rayTrace(vec3d, vec3d1, FluidCollisionOption.ALWAYS); + + if (movingobjectposition == null) { + return new InteractionResultWrapper<>(EnumInteractionResult.PASS, itemstack); + } else { + Vec3D vec3d2 = entityhuman.f(1.0F); + boolean flag = false; + List list = world.getEntities(entityhuman, entityhuman.getBoundingBox().b(vec3d2.x * 5.0D, vec3d2.y * 5.0D, vec3d2.z * 5.0D).g(1.0D)); + + for (int i = 0; i < list.size(); ++i) { + Entity entity = (Entity) list.get(i); + + if (entity.isInteractable()) { + AxisAlignedBB axisalignedbb = entity.getBoundingBox().g((double) entity.aM()); + + if (axisalignedbb.b(vec3d)) { + flag = true; + } + } + } + + if (flag) { + return new InteractionResultWrapper<>(EnumInteractionResult.PASS, itemstack); + } else if (movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.BLOCK) { + // CraftBukkit start - Boat placement + org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(entityhuman, org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK, movingobjectposition.getBlockPosition(), movingobjectposition.direction, itemstack, enumhand); + + if (event.isCancelled()) { + return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack); + } + // CraftBukkit end + BlockPosition blockposition = movingobjectposition.getBlockPosition(); + Block block = world.getType(blockposition).getBlock(); + EntityBoat entityboat = new EntityBoat(world, movingobjectposition.pos.x, movingobjectposition.pos.y, movingobjectposition.pos.z); + + entityboat.setType(this.a); + entityboat.yaw = entityhuman.yaw; + if (!world.getCubes(entityboat, entityboat.getBoundingBox().g(-0.1D))) { + return new InteractionResultWrapper<>(EnumInteractionResult.FAIL, itemstack); + } else { + if (!world.isClientSide) { + if (!world.addEntity(entityboat)) return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack); // CraftBukkit + } + + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + entityhuman.b(StatisticList.ITEM_USED.b(this)); + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, itemstack); + } + } else { + return new InteractionResultWrapper<>(EnumInteractionResult.PASS, itemstack); + } + } + } +} diff --git a/src/main/java/net/minecraft/server/ItemBow.java b/src/main/java/net/minecraft/server/ItemBow.java new file mode 100644 index 000000000000..f8dbc3c40073 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemBow.java @@ -0,0 +1,167 @@ +package net.minecraft.server; + +import org.bukkit.craftbukkit.inventory.CraftItemStack; + +public class ItemBow extends Item { + + public ItemBow(Item.Info item_info) { + super(item_info); + // CraftBukkit start - obfuscator went a little crazy + /* + this.a(new MinecraftKey("pull"), (itemstack, world, entityliving) -> { + return entityliving == null ? 0.0F : (entityliving.cW().getItem() != Items.BOW ? 0.0F : (float) (itemstack.k() - entityliving.cX()) / 20.0F); + }); + this.a(new MinecraftKey("pulling"), (itemstack, world, entityliving) -> { + return entityliving != null && entityliving.isHandRaised() && entityliving.cW() == itemstack ? 1.0F : 0.0F; + }); + */ + // CraftBukkit end + } + + private ItemStack a(EntityHuman entityhuman, ItemStack bow) { // Paper + if (this.e_(entityhuman, bow, entityhuman.b(EnumHand.OFF_HAND))) { // Paper + return entityhuman.b(EnumHand.OFF_HAND); + } else if (this.e_(entityhuman, bow, entityhuman.b(EnumHand.MAIN_HAND))) { + return entityhuman.b(EnumHand.MAIN_HAND); + } else { + for (int i = 0; i < entityhuman.inventory.getSize(); ++i) { + ItemStack itemstack = entityhuman.inventory.getItem(i); + + if (this.e_(entityhuman, bow, itemstack)) { + return itemstack; + } + } + + return ItemStack.a; + } + } + + // Paper start + protected boolean e_(EntityHuman player, ItemStack bow, ItemStack itemstack) { + return itemstack.getItem() instanceof ItemArrow && ( + !(player instanceof EntityPlayer) || + new com.destroystokyo.paper.event.player.PlayerReadyArrowEvent( + ((EntityPlayer) player).getBukkitEntity(), + CraftItemStack.asCraftMirror(bow), + CraftItemStack.asCraftMirror(itemstack) + ).callEvent()); + // Paper end + } + + public void a(ItemStack itemstack, World world, EntityLiving entityliving, int i) { + if (entityliving instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) entityliving; + boolean flag = entityhuman.abilities.canInstantlyBuild || EnchantmentManager.getEnchantmentLevel(Enchantments.ARROW_INFINITE, itemstack) > 0; + ItemStack itemstack1 = this.a(entityhuman, itemstack); // Paper + + if (!itemstack1.isEmpty() || flag) { + if (itemstack1.isEmpty()) { + itemstack1 = new ItemStack(Items.ARROW); + } + + int j = this.c(itemstack) - i; + float f = a(j); + + if ((double) f >= 0.1D) { + boolean flag1 = flag && itemstack1.getItem() == Items.ARROW; + + boolean consumeArrow = true; // Paper + if (!world.isClientSide) { + ItemArrow itemarrow = (ItemArrow) ((ItemArrow) (itemstack1.getItem() instanceof ItemArrow ? itemstack1.getItem() : Items.ARROW)); + EntityArrow entityarrow = itemarrow.a(world, itemstack1, (EntityLiving) entityhuman); + + entityarrow.a(entityhuman, entityhuman.pitch, entityhuman.yaw, 0.0F, f * 3.0F, 1.0F); + if (f == 1.0F) { + entityarrow.setCritical(true); + } + + int k = EnchantmentManager.getEnchantmentLevel(Enchantments.ARROW_DAMAGE, itemstack); + + if (k > 0) { + entityarrow.setDamage(entityarrow.getDamage() + (double) k * 0.5D + 0.5D); + } + + int l = EnchantmentManager.getEnchantmentLevel(Enchantments.ARROW_KNOCKBACK, itemstack); + + if (l > 0) { + entityarrow.setKnockbackStrength(l); + } + + if (EnchantmentManager.getEnchantmentLevel(Enchantments.ARROW_FIRE, itemstack) > 0) { + entityarrow.setOnFire(100); + } + // CraftBukkit start + org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(entityhuman, itemstack, itemstack1, entityarrow, f); // Paper + if (event.isCancelled()) { + event.getProjectile().remove(); + return; + } + // CraftBukkit end + + itemstack.damage(1, entityhuman); + consumeArrow = event.getConsumeArrow(); // Paper + if (!consumeArrow || flag1 || (entityhuman.abilities.canInstantlyBuild && ((itemstack1.getItem() == Items.SPECTRAL_ARROW) || (itemstack1.getItem() == Items.TIPPED_ARROW)))) { // Paper - add !consumeArrow + entityarrow.fromPlayer = EntityArrow.PickupStatus.CREATIVE_ONLY; + } + + // CraftBukkit start + if (event.getProjectile() == entityarrow.getBukkitEntity()) { + if (!world.addEntity(entityarrow)) { + if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); + } + return; + } + } + // CraftBukkit end + } + + world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_ARROW_SHOOT, SoundCategory.PLAYERS, 1.0F, 1.0F / (ItemBow.i.nextFloat() * 0.4F + 1.2F) + f * 0.5F); + if (!flag1 && !entityhuman.abilities.canInstantlyBuild && consumeArrow) { // Paper + itemstack1.subtract(1); + if (itemstack1.isEmpty()) { + entityhuman.inventory.f(itemstack1); + } + } + + entityhuman.b(StatisticList.ITEM_USED.b(this)); + } + } + } + } + + public static float a(int i) { + float f = (float) i / 20.0F; + + f = (f * f + f * 2.0F) / 3.0F; + if (f > 1.0F) { + f = 1.0F; + } + + return f; + } + + public int c(ItemStack itemstack) { + return 72000; + } + + public EnumAnimation d(ItemStack itemstack) { + return EnumAnimation.BOW; + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + boolean flag = !this.a(entityhuman, itemstack).isEmpty(); // Paper + + if (!entityhuman.abilities.canInstantlyBuild && !flag) { + return flag ? new InteractionResultWrapper<>(EnumInteractionResult.PASS, itemstack) : new InteractionResultWrapper<>(EnumInteractionResult.FAIL, itemstack); + } else { + entityhuman.c(enumhand); + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, itemstack); + } + } + + public int c() { + return 1; + } +} diff --git a/src/main/java/net/minecraft/server/ItemBucket.java b/src/main/java/net/minecraft/server/ItemBucket.java new file mode 100644 index 000000000000..a064897fc442 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemBucket.java @@ -0,0 +1,182 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.DummyGeneratorAccess; +import org.bukkit.event.player.PlayerBucketEmptyEvent; +import org.bukkit.event.player.PlayerBucketFillEvent; +// CraftBukkit end + +public class ItemBucket extends Item { + + public final FluidType fluidType; + + public ItemBucket(FluidType fluidtype, Item.Info item_info) { + super(item_info); + this.fluidType = fluidtype; + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + MovingObjectPosition movingobjectposition = this.a(world, entityhuman, this.fluidType == FluidTypes.EMPTY); + + if (movingobjectposition == null) { + return new InteractionResultWrapper<>(EnumInteractionResult.PASS, itemstack); + } else if (movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.BLOCK) { + BlockPosition blockposition = movingobjectposition.getBlockPosition(); + + if (world.a(entityhuman, blockposition) && entityhuman.a(blockposition, movingobjectposition.direction, itemstack)) { + IBlockData iblockdata; + + if (this.fluidType == FluidTypes.EMPTY) { + iblockdata = world.getType(blockposition); + if (iblockdata.getBlock() instanceof IFluidSource) { + // CraftBukkit start + FluidType dummyFluid = ((IFluidSource) iblockdata.getBlock()).removeFluid(DummyGeneratorAccess.INSTANCE, blockposition, iblockdata); + PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent(entityhuman, blockposition.getX(), blockposition.getY(), blockposition.getZ(), null, itemstack, dummyFluid.b(), enumhand); // Paper - add enumHand + + if (event.isCancelled()) { + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); // SPIGOT-4541 + return new InteractionResultWrapper(EnumInteractionResult.FAIL, itemstack); + } + // CraftBukkit end + FluidType fluidtype = ((IFluidSource) iblockdata.getBlock()).removeFluid(world, blockposition, iblockdata); + + if (fluidtype != FluidTypes.EMPTY) { + entityhuman.b(StatisticList.ITEM_USED.b(this)); + entityhuman.a(fluidtype.a(TagsFluid.LAVA) ? SoundEffects.ITEM_BUCKET_FILL_LAVA : SoundEffects.ITEM_BUCKET_FILL, 1.0F, 1.0F); + ItemStack itemstack1 = this.a(itemstack, entityhuman, fluidtype.b(), event.getItemStack()); // CraftBukkit + + if (!world.isClientSide) { + CriterionTriggers.j.a((EntityPlayer) entityhuman, new ItemStack(fluidtype.b())); + } + + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, itemstack1); + } + } + + return new InteractionResultWrapper<>(EnumInteractionResult.FAIL, itemstack); + } else { + iblockdata = world.getType(blockposition); + BlockPosition blockposition1 = this.a(iblockdata, blockposition, movingobjectposition); + + if (this.a(entityhuman, world, blockposition1, movingobjectposition, movingobjectposition.direction, blockposition, itemstack, enumhand)) { // CraftBukkit // Paper - add enumHand + this.a(world, itemstack, blockposition1); + if (entityhuman instanceof EntityPlayer) { + CriterionTriggers.y.a((EntityPlayer) entityhuman, blockposition1, itemstack); + } + + entityhuman.b(StatisticList.ITEM_USED.b(this)); + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, this.a(itemstack, entityhuman)); + } else { + return new InteractionResultWrapper<>(EnumInteractionResult.FAIL, itemstack); + } + } + } else { + return new InteractionResultWrapper<>(EnumInteractionResult.FAIL, itemstack); + } + } else { + return new InteractionResultWrapper<>(EnumInteractionResult.PASS, itemstack); + } + } + + private BlockPosition a(IBlockData iblockdata, BlockPosition blockposition, MovingObjectPosition movingobjectposition) { + return iblockdata.getBlock() instanceof IFluidContainer ? blockposition : movingobjectposition.getBlockPosition().shift(movingobjectposition.direction); + } + + protected ItemStack a(ItemStack itemstack, EntityHuman entityhuman) { + return !entityhuman.abilities.canInstantlyBuild ? new ItemStack(Items.BUCKET) : itemstack; + } + + public void a(World world, ItemStack itemstack, BlockPosition blockposition) {} + + // CraftBukkit - added ob.ItemStack result - TODO: Is this... the right way to handle this? + private ItemStack a(ItemStack itemstack, EntityHuman entityhuman, Item item, org.bukkit.inventory.ItemStack result) { + if (entityhuman.abilities.canInstantlyBuild) { + return itemstack; + } else { + itemstack.subtract(1); + if (itemstack.isEmpty()) { + // CraftBukkit start + return CraftItemStack.asNMSCopy(result); + } else { + if (!entityhuman.inventory.pickup(CraftItemStack.asNMSCopy(result))) { + entityhuman.drop(CraftItemStack.asNMSCopy(result), false); + // CraftBukkit end + } + + return itemstack; + } + } + } + + // CraftBukkit start + public boolean a(@Nullable EntityHuman entityhuman, World world, BlockPosition blockposition, @Nullable MovingObjectPosition movingobjectposition) { + return a(entityhuman, world, blockposition, movingobjectposition, null, null, null); + } + + public boolean a(EntityHuman entityhuman, World world, BlockPosition blockposition, @Nullable MovingObjectPosition movingobjectposition, EnumDirection enumdirection, BlockPosition clicked, ItemStack itemstack) { + // Paper start - add enumHand + return a(entityhuman, world, blockposition, movingobjectposition, enumdirection, clicked, itemstack, null); + } + + public boolean a(EntityHuman entityhuman, World world, BlockPosition blockposition, @Nullable MovingObjectPosition movingobjectposition, EnumDirection enumdirection, BlockPosition clicked, ItemStack itemstack, EnumHand enumhand) { + // Paper end + // CraftBukkit end + if (!(this.fluidType instanceof FluidTypeFlowing)) { + return false; + } else { + IBlockData iblockdata = world.getType(blockposition); + Material material = iblockdata.getMaterial(); + boolean flag = !material.isBuildable(); + boolean flag1 = material.isReplaceable(); + + if (!world.isEmpty(blockposition) && !flag && !flag1 && (!(iblockdata.getBlock() instanceof IFluidContainer) || !((IFluidContainer) iblockdata.getBlock()).canPlace(world, blockposition, iblockdata, this.fluidType))) { + return movingobjectposition == null ? false : this.a(entityhuman, world, movingobjectposition.getBlockPosition().shift(movingobjectposition.direction), (MovingObjectPosition) null, enumdirection, clicked, itemstack, enumhand); // CraftBukkit // Paper - add enumhand + } else { + // CraftBukkit start + if (entityhuman != null) { + PlayerBucketEmptyEvent event = CraftEventFactory.callPlayerBucketEmptyEvent(entityhuman, clicked.getX(), clicked.getY(), clicked.getZ(), enumdirection, itemstack, enumhand); // Paper - add enumHand + if (event.isCancelled()) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutBlockChange(world, blockposition)); // SPIGOT-4238: needed when looking through entity + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); // SPIGOT-4541 + return false; + } + } + // CraftBukkit end + if (world.worldProvider.isNether() && this.fluidType.a(TagsFluid.WATER)) { + int i = blockposition.getX(); + int j = blockposition.getY(); + int k = blockposition.getZ(); + + world.a(entityhuman, blockposition, SoundEffects.BLOCK_FIRE_EXTINGUISH, SoundCategory.BLOCKS, 0.5F, 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F); + + for (int l = 0; l < 8; ++l) { + world.addParticle(Particles.F, (double) i + Math.random(), (double) j + Math.random(), (double) k + Math.random(), 0.0D, 0.0D, 0.0D); + } + } else if (iblockdata.getBlock() instanceof IFluidContainer) { + if (((IFluidContainer) iblockdata.getBlock()).place(world, blockposition, iblockdata, ((FluidTypeFlowing) this.fluidType).a(false))) { + this.a(entityhuman, (GeneratorAccess) world, blockposition); + } + } else { + if (!world.isClientSide && (flag || flag1) && !material.isLiquid()) { + world.setAir(blockposition, true); + } + + this.a(entityhuman, (GeneratorAccess) world, blockposition); + world.setTypeAndData(blockposition, this.fluidType.i().i(), 11); + } + + return true; + } + } + } + + protected void a(@Nullable EntityHuman entityhuman, GeneratorAccess generatoraccess, BlockPosition blockposition) { + SoundEffect soundeffect = this.fluidType.a(TagsFluid.LAVA) ? SoundEffects.ITEM_BUCKET_EMPTY_LAVA : SoundEffects.ITEM_BUCKET_EMPTY; + + generatoraccess.a(entityhuman, blockposition, soundeffect, SoundCategory.BLOCKS, 1.0F, 1.0F); + } +} diff --git a/src/main/java/net/minecraft/server/ItemChorusFruit.java b/src/main/java/net/minecraft/server/ItemChorusFruit.java new file mode 100644 index 000000000000..413ea313a8d6 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemChorusFruit.java @@ -0,0 +1,60 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; +// CraftBukkit end + +public class ItemChorusFruit extends ItemFood { + + public ItemChorusFruit(int i, float f, Item.Info item_info) { + super(i, f, false, item_info); + } + + public ItemStack a(ItemStack itemstack, World world, EntityLiving entityliving) { + ItemStack itemstack1 = super.a(itemstack, world, entityliving); + + if (!world.isClientSide) { + double d0 = entityliving.locX; + double d1 = entityliving.locY; + double d2 = entityliving.locZ; + + for (int i = 0; i < 16; ++i) { + double d3 = entityliving.locX + (entityliving.getRandom().nextDouble() - 0.5D) * 16.0D; + double d4 = MathHelper.a(entityliving.locY + (double) (entityliving.getRandom().nextInt(16) - 8), 0.0D, (double) (world.ab() - 1)); + double d5 = entityliving.locZ + (entityliving.getRandom().nextDouble() - 0.5D) * 16.0D; + + // CraftBukkit start + if (entityliving instanceof EntityPlayer) { + Player player = ((EntityPlayer) entityliving).getBukkitEntity(); + PlayerTeleportEvent teleEvent = new PlayerTeleportEvent(player, player.getLocation(), new Location(player.getWorld(), d3, d4, d5), PlayerTeleportEvent.TeleportCause.CHORUS_FRUIT); + world.getServer().getPluginManager().callEvent(teleEvent); + if (teleEvent.isCancelled()) { + break; + } + d3 = teleEvent.getTo().getX(); + d4 = teleEvent.getTo().getY(); + d5 = teleEvent.getTo().getZ(); + } + // CraftBukkit end + + if (entityliving.isPassenger()) { + entityliving.stopRiding(); + } + + if (entityliving.j(d3, d4, d5)) { + world.a((EntityHuman) null, d0, d1, d2, SoundEffects.ITEM_CHORUS_FRUIT_TELEPORT, SoundCategory.PLAYERS, 1.0F, 1.0F); + entityliving.a(SoundEffects.ITEM_CHORUS_FRUIT_TELEPORT, 1.0F, 1.0F); + break; + } + } + + if (entityliving instanceof EntityHuman) { + ((EntityHuman) entityliving).getCooldownTracker().a(this, 20); + } + } + + return itemstack1; + } +} diff --git a/src/main/java/net/minecraft/server/ItemDebugStick.java b/src/main/java/net/minecraft/server/ItemDebugStick.java new file mode 100644 index 000000000000..9095d582e976 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemDebugStick.java @@ -0,0 +1,83 @@ +package net.minecraft.server; + +import java.util.Collection; +import javax.annotation.Nullable; + +public class ItemDebugStick extends Item { + + public ItemDebugStick(Item.Info item_info) { + super(item_info); + } + + public boolean a(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman) { + if (!world.isClientSide) { + this.a(entityhuman, iblockdata, world, blockposition, false, entityhuman.b(EnumHand.MAIN_HAND)); + } + + return false; + } + + public EnumInteractionResult a(ItemActionContext itemactioncontext) { + EntityHuman entityhuman = itemactioncontext.getEntity(); + World world = itemactioncontext.getWorld(); + + if (!world.isClientSide && entityhuman != null) { + BlockPosition blockposition = itemactioncontext.getClickPosition(); + + this.a(entityhuman, world.getType(blockposition), world, blockposition, true, itemactioncontext.getItemStack()); + } + + return EnumInteractionResult.SUCCESS; + } + + private void a(EntityHuman entityhuman, IBlockData iblockdata, GeneratorAccess generatoraccess, BlockPosition blockposition, boolean flag, ItemStack itemstack) { + if (entityhuman.isCreativeAndOp() || (entityhuman.abilities.canInstantlyBuild && entityhuman.getBukkitEntity().hasPermission("minecraft.debugstick"))) { + Block block = iblockdata.getBlock(); + BlockStateList blockstatelist = block.getStates(); + Collection> collection = blockstatelist.d(); + String s = IRegistry.BLOCK.getKey(block).toString(); + + if (collection.isEmpty()) { + a(entityhuman, (IChatBaseComponent) (new ChatMessage(this.getName() + ".empty", new Object[] { s}))); + } else { + NBTTagCompound nbttagcompound = itemstack.a("DebugProperty"); + String s1 = nbttagcompound.getString(s); + IBlockState iblockstate = blockstatelist.a(s1); + + if (flag) { + if (iblockstate == null) { + iblockstate = (IBlockState) collection.iterator().next(); + } + + IBlockData iblockdata1 = a(iblockdata, iblockstate, entityhuman.isSneaking()); + + generatoraccess.setTypeAndData(blockposition, iblockdata1, 18); + a(entityhuman, (IChatBaseComponent) (new ChatMessage(this.getName() + ".update", new Object[] { iblockstate.a(), a(iblockdata1, iblockstate)}))); + } else { + iblockstate = (IBlockState) a((Iterable) collection, (Object) iblockstate, entityhuman.isSneaking()); + String s2 = iblockstate.a(); + + nbttagcompound.setString(s, s2); + a(entityhuman, (IChatBaseComponent) (new ChatMessage(this.getName() + ".select", new Object[] { s2, a(iblockdata, iblockstate)}))); + } + + } + } + } + + private static > IBlockData a(IBlockData iblockdata, IBlockState iblockstate, boolean flag) { + return (IBlockData) iblockdata.set(iblockstate, a(iblockstate.d(), iblockdata.get(iblockstate), flag)); + } + + private static T a(Iterable iterable, @Nullable T t0, boolean flag) { + return flag ? SystemUtils.b(iterable, t0) : SystemUtils.a(iterable, t0); + } + + private static void a(EntityHuman entityhuman, IChatBaseComponent ichatbasecomponent) { + ((EntityPlayer) entityhuman).a(ichatbasecomponent, ChatMessageType.GAME_INFO); + } + + private static > String a(IBlockData iblockdata, IBlockState iblockstate) { + return iblockstate.a(iblockdata.get(iblockstate)); + } +} diff --git a/src/main/java/net/minecraft/server/ItemDye.java b/src/main/java/net/minecraft/server/ItemDye.java new file mode 100644 index 000000000000..88f6ee46ef9e --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemDye.java @@ -0,0 +1,51 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import java.util.Map; + +import org.bukkit.event.entity.SheepDyeWoolEvent; // CraftBukkit + +public class ItemDye extends Item { + + private static final Map a = Maps.newEnumMap(EnumColor.class); + private final EnumColor b; + + public ItemDye(EnumColor enumcolor, Item.Info item_info) { + super(item_info); + this.b = enumcolor; + ItemDye.a.put(enumcolor, this); + } + + public boolean a(ItemStack itemstack, EntityHuman entityhuman, EntityLiving entityliving, EnumHand enumhand) { + if (entityliving instanceof EntitySheep) { + EntitySheep entitysheep = (EntitySheep) entityliving; + + if (!entitysheep.isSheared() && entitysheep.getColor() != this.b) { + // CraftBukkit start + byte bColor = (byte) this.b.getColorIndex(); + SheepDyeWoolEvent event = new SheepDyeWoolEvent((org.bukkit.entity.Sheep) entitysheep.getBukkitEntity(), org.bukkit.DyeColor.getByWoolData(bColor)); + entitysheep.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return false; + } + + entitysheep.setColor(EnumColor.fromColorIndex((byte) event.getColor().getWoolData())); + // CraftBukkit end + itemstack.subtract(1); + } + + return true; + } else { + return false; + } + } + + public EnumColor d() { + return this.b; + } + + public static ItemDye a(EnumColor enumcolor) { + return (ItemDye) ItemDye.a.get(enumcolor); + } +} diff --git a/src/main/java/net/minecraft/server/ItemEgg.java b/src/main/java/net/minecraft/server/ItemEgg.java new file mode 100644 index 000000000000..5f3926d8308c --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemEgg.java @@ -0,0 +1,45 @@ +package net.minecraft.server; + +public class ItemEgg extends Item { + + public ItemEgg(Item.Info item_info) { + super(item_info); + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + /* // Paper start - moved down + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_EGG_THROW, SoundCategory.PLAYERS, 0.5F, 0.4F / (ItemEgg.i.nextFloat() * 0.4F + 0.8F)); + */ // Paper end + if (!world.isClientSide) { + EntityEgg entityegg = new EntityEgg(world, entityhuman); + + entityegg.a(entityhuman, entityhuman.pitch, entityhuman.yaw, 0.0F, 1.5F, 1.0F); + // Paper start + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) entityegg.getBukkitEntity()); + if (event.callEvent() && world.addEntity(entityegg)) { + if (event.shouldConsume() && !entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } else if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); + } + + world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_EGG_THROW, SoundCategory.PLAYERS, 0.5F, 0.4F / (Entity.SHARED_RANDOM.nextFloat() * 0.4F + 0.8F)); + } else { + if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); + } + return new InteractionResultWrapper(EnumInteractionResult.FAIL, itemstack); + } + // Paper end + } + + entityhuman.b(StatisticList.ITEM_USED.b(this)); + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, itemstack); + } +} diff --git a/src/main/java/net/minecraft/server/ItemEndCrystal.java b/src/main/java/net/minecraft/server/ItemEndCrystal.java new file mode 100644 index 000000000000..4914a40dedc3 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemEndCrystal.java @@ -0,0 +1,55 @@ +package net.minecraft.server; + +import java.util.List; + +public class ItemEndCrystal extends Item { + + public ItemEndCrystal(Item.Info item_info) { + super(item_info); + } + + public EnumInteractionResult a(ItemActionContext itemactioncontext) { + World world = itemactioncontext.getWorld(); + BlockPosition blockposition = itemactioncontext.getClickPosition(); + IBlockData iblockdata = world.getType(blockposition); + + if (iblockdata.getBlock() != Blocks.OBSIDIAN && iblockdata.getBlock() != Blocks.BEDROCK) { + return EnumInteractionResult.FAIL; + } else { + BlockPosition blockposition1 = blockposition.up(); + + if (!world.isEmpty(blockposition1)) { + return EnumInteractionResult.FAIL; + } else { + double d0 = (double) blockposition1.getX(); + double d1 = (double) blockposition1.getY(); + double d2 = (double) blockposition1.getZ(); + List list = world.getEntities((Entity) null, new AxisAlignedBB(d0, d1, d2, d0 + 1.0D, d1 + 2.0D, d2 + 1.0D)); + + if (!list.isEmpty()) { + return EnumInteractionResult.FAIL; + } else { + if (!world.isClientSide) { + EntityEnderCrystal entityendercrystal = new EntityEnderCrystal(world, d0 + 0.5D, d1, d2 + 0.5D); + + entityendercrystal.setShowingBottom(false); + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPlaceEvent(itemactioncontext, entityendercrystal).isCancelled()) { + return EnumInteractionResult.FAIL; + } + // CraftBukkit end + world.addEntity(entityendercrystal); + if (world.worldProvider instanceof WorldProviderTheEnd) { + EnderDragonBattle enderdragonbattle = ((WorldProviderTheEnd) world.worldProvider).r(); + + enderdragonbattle.e(); + } + } + + itemactioncontext.getItemStack().subtract(1); + return EnumInteractionResult.SUCCESS; + } + } + } + } +} diff --git a/src/main/java/net/minecraft/server/ItemEnderPearl.java b/src/main/java/net/minecraft/server/ItemEnderPearl.java new file mode 100644 index 000000000000..719210da151a --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemEnderPearl.java @@ -0,0 +1,50 @@ +package net.minecraft.server; + +public class ItemEnderPearl extends Item { + + public ItemEnderPearl(Item.Info item_info) { + super(item_info); + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + // CraftBukkit start - change order + if (!world.isClientSide) { + EntityEnderPearl entityenderpearl = new EntityEnderPearl(world, entityhuman); + + entityenderpearl.a(entityhuman, entityhuman.pitch, entityhuman.yaw, 0.0F, 1.5F, 1.0F); + // Paper start + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) entityenderpearl.getBukkitEntity()); + if (event.callEvent() && world.addEntity(entityenderpearl)) { + if (event.shouldConsume() && !entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } else if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); + } + + world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_ENDER_PEARL_THROW, SoundCategory.NEUTRAL, 0.5F, 0.4F / (Entity.SHARED_RANDOM.nextFloat() * 0.4F + 0.8F)); + entityhuman.getCooldownTracker().a(this, 20); + } else { + // Paper end + if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); + } + return new InteractionResultWrapper(EnumInteractionResult.FAIL, itemstack); + } + } + + // Paper start - moved up + //if (!entityhuman.abilities.canInstantlyBuild) { + // itemstack.subtract(1); + //} + // + //world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_ENDER_PEARL_THROW, SoundCategory.NEUTRAL, 0.5F, 0.4F / (ItemEnderPearl.i.nextFloat() * 0.4F + 0.8F)); + //entityhuman.getCooldownTracker().a(this, 20); + // // CraftBukkit end + // Paper end + + entityhuman.b(StatisticList.ITEM_USED.b(this)); + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, itemstack); + } +} diff --git a/src/main/java/net/minecraft/server/ItemExpBottle.java b/src/main/java/net/minecraft/server/ItemExpBottle.java new file mode 100644 index 000000000000..23b06169e282 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemExpBottle.java @@ -0,0 +1,44 @@ +package net.minecraft.server; + +public class ItemExpBottle extends Item { + + public ItemExpBottle(Item.Info item_info) { + super(item_info); + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + /* // Paper start + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_EXPERIENCE_BOTTLE_THROW, SoundCategory.NEUTRAL, 0.5F, 0.4F / (ItemExpBottle.i.nextFloat() * 0.4F + 0.8F)); + */ // Paper end + if (!world.isClientSide) { + EntityThrownExpBottle entitythrownexpbottle = new EntityThrownExpBottle(world, entityhuman); + + entitythrownexpbottle.a(entityhuman, entityhuman.pitch, entityhuman.yaw, -20.0F, 0.7F, 1.0F); + // Paper start + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) entitythrownexpbottle.getBukkitEntity()); + if (event.callEvent() && world.addEntity(entitythrownexpbottle)) { + if (event.shouldConsume() && !entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } else if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); + } + + world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_EXPERIENCE_BOTTLE_THROW, SoundCategory.NEUTRAL, 0.5F, 0.4F / (Entity.SHARED_RANDOM.nextFloat() * 0.4F + 0.8F)); + } else { + if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); + } + return new InteractionResultWrapper(EnumInteractionResult.FAIL, itemstack); + } + // Paper end + } + + entityhuman.b(StatisticList.ITEM_USED.b(this)); + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, itemstack); + } +} diff --git a/src/main/java/net/minecraft/server/ItemFireball.java b/src/main/java/net/minecraft/server/ItemFireball.java new file mode 100644 index 000000000000..37a481e1e0fc --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemFireball.java @@ -0,0 +1,34 @@ +package net.minecraft.server; + +public class ItemFireball extends Item { + + public ItemFireball(Item.Info item_info) { + super(item_info); + } + + public EnumInteractionResult a(ItemActionContext itemactioncontext) { + World world = itemactioncontext.getWorld(); + + if (world.isClientSide) { + return EnumInteractionResult.SUCCESS; + } else { + BlockPosition blockposition = itemactioncontext.getClickPosition().shift(itemactioncontext.getClickedFace()); + + if (world.getType(blockposition).isAir()) { + // CraftBukkit start - fire BlockIgniteEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, blockposition, org.bukkit.event.block.BlockIgniteEvent.IgniteCause.FIREBALL, itemactioncontext.getEntity()).isCancelled()) { + if (!itemactioncontext.getEntity().abilities.canInstantlyBuild) { + itemactioncontext.getItemStack().subtract(1); + } + return EnumInteractionResult.PASS; + } + // CraftBukkit end + world.a((EntityHuman) null, blockposition, SoundEffects.ITEM_FIRECHARGE_USE, SoundCategory.BLOCKS, 1.0F, (ItemFireball.i.nextFloat() - ItemFireball.i.nextFloat()) * 0.2F + 1.0F); + world.setTypeUpdate(blockposition, ((BlockFire) Blocks.FIRE).a((IBlockAccess) world, blockposition)); + } + + itemactioncontext.getItemStack().subtract(1); + return EnumInteractionResult.SUCCESS; + } + } +} diff --git a/src/main/java/net/minecraft/server/ItemFireworks.java b/src/main/java/net/minecraft/server/ItemFireworks.java new file mode 100644 index 000000000000..13938775bae0 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemFireworks.java @@ -0,0 +1,75 @@ +package net.minecraft.server; + +import java.util.Arrays; +import java.util.Comparator; + +public class ItemFireworks extends Item { + + public ItemFireworks(Item.Info item_info) { + super(item_info); + } + + public EnumInteractionResult a(ItemActionContext itemactioncontext) { + World world = itemactioncontext.getWorld(); + + if (!world.isClientSide) { + BlockPosition blockposition = itemactioncontext.getClickPosition(); + ItemStack itemstack = itemactioncontext.getItemStack(); + EntityFireworks entityfireworks = new EntityFireworks(world, (double) ((float) blockposition.getX() + itemactioncontext.m()), (double) ((float) blockposition.getY() + itemactioncontext.n()), (double) ((float) blockposition.getZ() + itemactioncontext.o()), itemstack); + entityfireworks.spawningEntity = itemactioncontext.b.getUniqueID(); // Paper + + world.addEntity(entityfireworks); + itemstack.subtract(1); + } + + return EnumInteractionResult.SUCCESS; + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + if (entityhuman.dc()) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (!world.isClientSide) { + EntityFireworks entityfireworks = new EntityFireworks(world, itemstack, entityhuman); + entityfireworks.spawningEntity = entityhuman.getUniqueID(); // Paper + + // Paper start + com.destroystokyo.paper.event.player.PlayerElytraBoostEvent event = new com.destroystokyo.paper.event.player.PlayerElytraBoostEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Firework) entityfireworks.getBukkitEntity()); + if (event.callEvent() && world.addEntity(entityfireworks)) { + if (event.shouldConsume() && !entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } else ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); + } else if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); + // Paper end + } + } + + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, entityhuman.b(enumhand)); + } else { + return new InteractionResultWrapper<>(EnumInteractionResult.PASS, entityhuman.b(enumhand)); + } + } + + public static enum EffectType { + + SMALL_BALL(0, "small_ball"), LARGE_BALL(1, "large_ball"), STAR(2, "star"), CREEPER(3, "creeper"), BURST(4, "burst"); + + private static final ItemFireworks.EffectType[] f = (ItemFireworks.EffectType[]) Arrays.stream(values()).sorted(Comparator.comparingInt((itemfireworks_effecttype) -> { + return itemfireworks_effecttype.g; + })).toArray((i) -> { + return new ItemFireworks.EffectType[i]; + }); + private final int g; + private final String h; + + private EffectType(int i, String s) { + this.g = i; + this.h = s; + } + + public int a() { + return this.g; + } + } +} diff --git a/src/main/java/net/minecraft/server/ItemFish.java b/src/main/java/net/minecraft/server/ItemFish.java new file mode 100644 index 000000000000..a43606fb32cc --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemFish.java @@ -0,0 +1,86 @@ +package net.minecraft.server; + +public class ItemFish extends ItemFood { + + private final boolean a; + private final ItemFish.EnumFish b; + + public ItemFish(ItemFish.EnumFish itemfish_enumfish, boolean flag, Item.Info item_info) { + super(0, 0.0F, false, item_info); + this.b = itemfish_enumfish; + this.a = flag; + } + + public int getNutrition(ItemStack itemstack) { + ItemFish.EnumFish itemfish_enumfish = ItemFish.EnumFish.a(itemstack); + + return this.a && itemfish_enumfish.e() ? itemfish_enumfish.c() : itemfish_enumfish.a(); + } + + public float getSaturationModifier(ItemStack itemstack) { + return this.a && this.b.e() ? this.b.d() : this.b.b(); + } + + protected void a(ItemStack itemstack, World world, EntityHuman entityhuman) { + ItemFish.EnumFish itemfish_enumfish = ItemFish.EnumFish.a(itemstack); + + if (itemfish_enumfish == ItemFish.EnumFish.PUFFERFISH) { + // CraftBukkit start + entityhuman.addEffect(new MobEffect(MobEffects.POISON, 1200, 3), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.FOOD); + entityhuman.addEffect(new MobEffect(MobEffects.HUNGER, 300, 2), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.FOOD); + entityhuman.addEffect(new MobEffect(MobEffects.CONFUSION, 300, 1), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.FOOD); + // CraftBukkit end + } + + super.a(itemstack, world, entityhuman); + } + + public static enum EnumFish { + + COD(2, 0.1F, 5, 0.6F), SALMON(2, 0.1F, 6, 0.8F), TROPICAL_FISH(1, 0.1F), PUFFERFISH(1, 0.1F); + + private final int e; + private final float f; + private final int g; + private final float h; + private final boolean i; + + private EnumFish(int i, float f, int j, float f1) { + this.e = i; + this.f = f; + this.g = j; + this.h = f1; + this.i = j != 0; + } + + private EnumFish(int i, float f) { + this(i, f, 0, 0.0F); + } + + public int a() { + return this.e; + } + + public float b() { + return this.f; + } + + public int c() { + return this.g; + } + + public float d() { + return this.h; + } + + public boolean e() { + return this.i; + } + + public static ItemFish.EnumFish a(ItemStack itemstack) { + Item item = itemstack.getItem(); + + return item instanceof ItemFish ? ((ItemFish) item).b : ItemFish.EnumFish.COD; + } + } +} diff --git a/src/main/java/net/minecraft/server/ItemFishingRod.java b/src/main/java/net/minecraft/server/ItemFishingRod.java new file mode 100644 index 000000000000..0c2305977f0a --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemFishingRod.java @@ -0,0 +1,78 @@ +package net.minecraft.server; + +import org.bukkit.event.player.PlayerFishEvent; // CraftBukkit + +public class ItemFishingRod extends Item { + + public ItemFishingRod(Item.Info item_info) { + super(item_info); + // CraftBukkit start - obfuscator went a little crazy + /* + this.a(new MinecraftKey("cast"), (itemstack, world, entityliving) -> { + if (entityliving == null) { + return 0.0F; + } else { + boolean flag = entityliving.getItemInMainHand() == itemstack; + boolean flag1 = entityliving.getItemInOffHand() == itemstack; + + if (entityliving.getItemInMainHand().getItem() instanceof ItemFishingRod) { + flag1 = false; + } + + return (flag || flag1) && entityliving instanceof EntityHuman && ((EntityHuman) entityliving).hookedFish != null ? 1.0F : 0.0F; + } + }); + */ + // CraftBukkit end + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (entityhuman.hookedFish != null) { + int i = entityhuman.hookedFish.b(itemstack); + + itemstack.damage(i, entityhuman); + entityhuman.a(enumhand); + world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_FISHING_BOBBER_RETRIEVE, SoundCategory.NEUTRAL, 1.0F, 0.4F / (ItemFishingRod.i.nextFloat() * 0.4F + 0.8F)); + } else { + // world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_FISHING_BOBBER_THROW, SoundCategory.NEUTRAL, 0.5F, 0.4F / (ItemFishingRod.i.nextFloat() * 0.4F + 0.8F)); + if (!world.isClientSide) { + EntityFishingHook entityfishinghook = new EntityFishingHook(world, entityhuman); + int j = EnchantmentManager.c(itemstack); + + if (j > 0) { + entityfishinghook.a(j); + } + + int k = EnchantmentManager.b(itemstack); + + if (k > 0) { + entityfishinghook.b(k); + } + + // CraftBukkit start + PlayerFishEvent playerFishEvent = new PlayerFishEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), null, (org.bukkit.entity.FishHook) entityfishinghook.getBukkitEntity(), PlayerFishEvent.State.FISHING); + world.getServer().getPluginManager().callEvent(playerFishEvent); + + if (playerFishEvent.isCancelled()) { + entityhuman.hookedFish = null; + return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack); + } + world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_FISHING_BOBBER_THROW, SoundCategory.NEUTRAL, 0.5F, 0.4F / (ItemFishingRod.i.nextFloat() * 0.4F + 0.8F)); + // CraftBukkit end + + world.addEntity(entityfishinghook); + } + + entityhuman.a(enumhand); + entityhuman.b(StatisticList.ITEM_USED.b(this)); + } + + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, itemstack); + } + + public int c() { + return 1; + } +} diff --git a/src/main/java/net/minecraft/server/ItemFlintAndSteel.java b/src/main/java/net/minecraft/server/ItemFlintAndSteel.java new file mode 100644 index 000000000000..3d58bf2d59ff --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemFlintAndSteel.java @@ -0,0 +1,58 @@ +package net.minecraft.server; + +import java.util.Iterator; + +public class ItemFlintAndSteel extends Item { + + public ItemFlintAndSteel(Item.Info item_info) { + super(item_info); + } + + public EnumInteractionResult a(ItemActionContext itemactioncontext) { + EntityHuman entityhuman = itemactioncontext.getEntity(); + World world = itemactioncontext.getWorld(); + BlockPosition blockposition = itemactioncontext.getClickPosition().shift(itemactioncontext.getClickedFace()); + + if (a((GeneratorAccess) world, blockposition)) { + // CraftBukkit start - Store the clicked block + if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, blockposition, org.bukkit.event.block.BlockIgniteEvent.IgniteCause.FLINT_AND_STEEL, entityhuman).isCancelled()) { + itemactioncontext.getItemStack().damage(1, entityhuman); + return EnumInteractionResult.PASS; + } + // CraftBukkit end + world.a(entityhuman, blockposition, SoundEffects.ITEM_FLINTANDSTEEL_USE, SoundCategory.BLOCKS, 1.0F, ItemFlintAndSteel.i.nextFloat() * 0.4F + 0.8F); + IBlockData iblockdata = ((BlockFire) Blocks.FIRE).a((IBlockAccess) world, blockposition); + + world.setTypeAndData(blockposition, iblockdata, 11); + ItemStack itemstack = itemactioncontext.getItemStack(); + + if (entityhuman instanceof EntityPlayer) { + CriterionTriggers.y.a((EntityPlayer) entityhuman, blockposition, itemstack); + } + + if (entityhuman != null) { + itemstack.damage(1, entityhuman); + } + + return EnumInteractionResult.SUCCESS; + } else { + return EnumInteractionResult.FAIL; + } + } + + public static boolean a(GeneratorAccess generatoraccess, BlockPosition blockposition) { + IBlockData iblockdata = ((BlockFire) Blocks.FIRE).a((IBlockAccess) generatoraccess, blockposition); + boolean flag = false; + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + + if (generatoraccess.getType(blockposition.shift(enumdirection)).getBlock() == Blocks.OBSIDIAN && ((BlockPortal) Blocks.NETHER_PORTAL).b(generatoraccess, blockposition) != null) { + flag = true; + } + } + + return generatoraccess.isEmpty(blockposition) && (iblockdata.canPlace(generatoraccess, blockposition) || flag); + } +} diff --git a/src/main/java/net/minecraft/server/ItemFood.java b/src/main/java/net/minecraft/server/ItemFood.java new file mode 100644 index 000000000000..762b6f4a1004 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemFood.java @@ -0,0 +1,90 @@ +package net.minecraft.server; + +public class ItemFood extends Item { + + private final int a; + private final float b; + private final boolean c; + private boolean d; + private boolean e; + private MobEffect k; + private float l; + + public ItemFood(int i, float f, boolean flag, Item.Info item_info) { + super(item_info); + this.a = i; + this.c = flag; + this.b = f; + } + + public ItemStack a(ItemStack itemstack, World world, EntityLiving entityliving) { + if (entityliving instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) entityliving; + + entityhuman.getFoodData().a(this, itemstack); + world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_PLAYER_BURP, SoundCategory.PLAYERS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F); + this.a(itemstack, world, entityhuman); + entityhuman.b(StatisticList.ITEM_USED.b(this)); + if (entityhuman instanceof EntityPlayer) { + CriterionTriggers.z.a((EntityPlayer) entityhuman, itemstack); + } + } + + itemstack.subtract(1); + return itemstack; + } + + protected void a(ItemStack itemstack, World world, EntityHuman entityhuman) { + if (!world.isClientSide && this.k != null && world.random.nextFloat() < this.l) { + entityhuman.addEffect(new MobEffect(this.k), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.FOOD); // CraftBukkit + } + + } + + public int c(ItemStack itemstack) { + return this.e ? 16 : 32; + } + + public EnumAnimation d(ItemStack itemstack) { + return EnumAnimation.EAT; + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (entityhuman.q(this.d)) { + entityhuman.c(enumhand); + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, itemstack); + } else { + return new InteractionResultWrapper<>(EnumInteractionResult.FAIL, itemstack); + } + } + + public int getNutrition(ItemStack itemstack) { + return this.a; + } + + public float getSaturationModifier(ItemStack itemstack) { + return this.b; + } + + public boolean d() { + return this.c; + } + + public ItemFood a(MobEffect mobeffect, float f) { + this.k = mobeffect; + this.l = f; + return this; + } + + public ItemFood e() { + this.d = true; + return this; + } + + public ItemFood f() { + this.e = true; + return this; + } +} diff --git a/src/main/java/net/minecraft/server/ItemGoldenApple.java b/src/main/java/net/minecraft/server/ItemGoldenApple.java new file mode 100644 index 000000000000..e81fa8dfdac9 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemGoldenApple.java @@ -0,0 +1,18 @@ +package net.minecraft.server; + +public class ItemGoldenApple extends ItemFood { + + public ItemGoldenApple(int i, float f, boolean flag, Item.Info item_info) { + super(i, f, flag, item_info); + } + + protected void a(ItemStack itemstack, World world, EntityHuman entityhuman) { + if (!world.isClientSide) { + // CraftBukkit start + entityhuman.addEffect(new MobEffect(MobEffects.REGENERATION, 100, 1), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.FOOD); + entityhuman.addEffect(new MobEffect(MobEffects.ABSORBTION, 2400, 0), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.FOOD); + // CraftBukkit end + } + + } +} diff --git a/src/main/java/net/minecraft/server/ItemGoldenAppleEnchanted.java b/src/main/java/net/minecraft/server/ItemGoldenAppleEnchanted.java new file mode 100644 index 000000000000..0a572d068802 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemGoldenAppleEnchanted.java @@ -0,0 +1,20 @@ +package net.minecraft.server; + +public class ItemGoldenAppleEnchanted extends ItemFood { + + public ItemGoldenAppleEnchanted(int i, float f, boolean flag, Item.Info item_info) { + super(i, f, flag, item_info); + } + + protected void a(ItemStack itemstack, World world, EntityHuman entityhuman) { + if (!world.isClientSide) { + // CraftBukkit start + entityhuman.addEffect(new MobEffect(MobEffects.REGENERATION, 400, 1), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.FOOD); + entityhuman.addEffect(new MobEffect(MobEffects.RESISTANCE, 6000, 0), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.FOOD); + entityhuman.addEffect(new MobEffect(MobEffects.FIRE_RESISTANCE, 6000, 0), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.FOOD); + entityhuman.addEffect(new MobEffect(MobEffects.ABSORBTION, 2400, 3), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.FOOD); + // CraftBukkit end + } + + } +} diff --git a/src/main/java/net/minecraft/server/ItemHanging.java b/src/main/java/net/minecraft/server/ItemHanging.java new file mode 100644 index 000000000000..922baf36b510 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemHanging.java @@ -0,0 +1,63 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; +// CraftBukkit start +import org.bukkit.entity.Player; +import org.bukkit.event.hanging.HangingPlaceEvent; +// CraftBukkit end + +public class ItemHanging extends Item { + + private final Class a; + + public ItemHanging(Class oclass, Item.Info item_info) { + super(item_info); + this.a = oclass; + } + + public EnumInteractionResult a(ItemActionContext itemactioncontext) { + BlockPosition blockposition = itemactioncontext.getClickPosition(); + EnumDirection enumdirection = itemactioncontext.getClickedFace(); + BlockPosition blockposition1 = blockposition.shift(enumdirection); + EntityHuman entityhuman = itemactioncontext.getEntity(); + + if (entityhuman != null && !this.a(entityhuman, enumdirection, itemactioncontext.getItemStack(), blockposition1)) { + return EnumInteractionResult.FAIL; + } else { + World world = itemactioncontext.getWorld(); + EntityHanging entityhanging = this.a(world, blockposition1, enumdirection); + + if (entityhanging != null && entityhanging.survives()) { + if (!world.isClientSide) { + // CraftBukkit start - fire HangingPlaceEvent + Player who = (itemactioncontext.getEntity() == null) ? null : (Player) itemactioncontext.getEntity().getBukkitEntity(); + org.bukkit.block.Block blockClicked = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + org.bukkit.block.BlockFace blockFace = org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(enumdirection); + + HangingPlaceEvent event = new HangingPlaceEvent((org.bukkit.entity.Hanging) entityhanging.getBukkitEntity(), who, blockClicked, blockFace); + world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return EnumInteractionResult.FAIL; + } + // CraftBukkit end + entityhanging.m(); + world.addEntity(entityhanging); + } + + itemactioncontext.getItemStack().subtract(1); + } + + return EnumInteractionResult.SUCCESS; + } + } + + protected boolean a(EntityHuman entityhuman, EnumDirection enumdirection, ItemStack itemstack, BlockPosition blockposition) { + return !enumdirection.k().b() && entityhuman.a(blockposition, enumdirection, itemstack); + } + + @Nullable + private EntityHanging a(World world, BlockPosition blockposition, EnumDirection enumdirection) { + return (EntityHanging) (this.a == EntityPainting.class ? new EntityPainting(world, blockposition, enumdirection) : (this.a == EntityItemFrame.class ? new EntityItemFrame(world, blockposition, enumdirection) : null)); + } +} diff --git a/src/main/java/net/minecraft/server/ItemLeash.java b/src/main/java/net/minecraft/server/ItemLeash.java new file mode 100644 index 000000000000..defab40af8fd --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemLeash.java @@ -0,0 +1,73 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +import org.bukkit.event.hanging.HangingPlaceEvent; // CraftBukkit + +public class ItemLeash extends Item { + + public ItemLeash(Item.Info item_info) { + super(item_info); + } + + public EnumInteractionResult a(ItemActionContext itemactioncontext) { + World world = itemactioncontext.getWorld(); + BlockPosition blockposition = itemactioncontext.getClickPosition(); + Block block = world.getType(blockposition).getBlock(); + + if (block instanceof BlockFence) { + EntityHuman entityhuman = itemactioncontext.getEntity(); + + if (!world.isClientSide && entityhuman != null) { + a(entityhuman, world, blockposition); + } + + return EnumInteractionResult.SUCCESS; + } else { + return EnumInteractionResult.PASS; + } + } + + public static boolean a(EntityHuman entityhuman, World world, BlockPosition blockposition) { + EntityLeash entityleash = EntityLeash.b(world, blockposition); + boolean flag = false; + double d0 = 7.0D; + int i = blockposition.getX(); + int j = blockposition.getY(); + int k = blockposition.getZ(); + List list = world.a(EntityInsentient.class, new AxisAlignedBB((double) i - 7.0D, (double) j - 7.0D, (double) k - 7.0D, (double) i + 7.0D, (double) j + 7.0D, (double) k + 7.0D)); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityInsentient entityinsentient = (EntityInsentient) iterator.next(); + + if (entityinsentient.isLeashed() && entityinsentient.getLeashHolder() == entityhuman) { + if (entityleash == null) { + entityleash = EntityLeash.a(world, blockposition); + + // CraftBukkit start - fire HangingPlaceEvent + HangingPlaceEvent event = new HangingPlaceEvent((org.bukkit.entity.Hanging) entityleash.getBukkitEntity(), entityhuman != null ? (org.bukkit.entity.Player) entityhuman.getBukkitEntity() : null, world.getWorld().getBlockAt(i, j, k), org.bukkit.block.BlockFace.SELF); + world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + entityleash.die(); + return false; + } + // CraftBukkit end + } + + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerLeashEntityEvent(entityinsentient, entityleash, entityhuman).isCancelled()) { + continue; + } + // CraftBukkit end + + entityinsentient.setLeashHolder(entityleash, true); + flag = true; + } + } + + return flag; + } +} diff --git a/src/main/java/net/minecraft/server/ItemLingeringPotion.java b/src/main/java/net/minecraft/server/ItemLingeringPotion.java new file mode 100644 index 000000000000..8d882b81bb44 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemLingeringPotion.java @@ -0,0 +1,48 @@ +package net.minecraft.server; + +public class ItemLingeringPotion extends ItemPotion { + + public ItemLingeringPotion(Item.Info item_info) { + super(item_info); + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + /* // Paper start + ItemStack itemstack1 = entityhuman.abilities.canInstantlyBuild ? itemstack.cloneItemStack() : itemstack.cloneAndSubtract(1); + + world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_LINGERING_POTION_THROW, SoundCategory.NEUTRAL, 0.5F, 0.4F / (ItemLingeringPotion.i.nextFloat() * 0.4F + 0.8F)); + */ // Paper end + if (!world.isClientSide) { + // Paper start - ensure stack count matches vanilla behavior without modifying original stack yet + ItemStack itemstack1 = itemstack.cloneItemStack(); + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack1.setCount(1); + } + // Paper end + EntityPotion entitypotion = new EntityPotion(world, entityhuman, itemstack1); + + entitypotion.a(entityhuman, entityhuman.pitch, entityhuman.yaw, -20.0F, 0.5F, 1.0F); + // Paper start + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) entitypotion.getBukkitEntity()); + if (event.callEvent() && world.addEntity(entitypotion)) { + if (event.shouldConsume() && !entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } else if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); + } + + world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_LINGERING_POTION_THROW, SoundCategory.NEUTRAL, 0.5F, 0.4F / (Entity.SHARED_RANDOM.nextFloat() * 0.4F + 0.8F)); + } else { + if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); + } + return new InteractionResultWrapper(EnumInteractionResult.FAIL, itemstack); + } + // Paper end + } + + entityhuman.b(StatisticList.ITEM_USED.b(this)); + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, itemstack); + } +} diff --git a/src/main/java/net/minecraft/server/ItemMilkBucket.java b/src/main/java/net/minecraft/server/ItemMilkBucket.java new file mode 100644 index 000000000000..9bae2c570178 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemMilkBucket.java @@ -0,0 +1,40 @@ +package net.minecraft.server; + +public class ItemMilkBucket extends Item { + + public ItemMilkBucket(Item.Info item_info) { + super(item_info); + } + + public ItemStack a(ItemStack itemstack, World world, EntityLiving entityliving) { + if (entityliving instanceof EntityPlayer) { + EntityPlayer entityplayer = (EntityPlayer) entityliving; + + CriterionTriggers.z.a(entityplayer, itemstack); + entityplayer.b(StatisticList.ITEM_USED.b(this)); + } + + if (entityliving instanceof EntityHuman && !((EntityHuman) entityliving).abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + if (!world.isClientSide) { + entityliving.removeAllEffects(org.bukkit.event.entity.EntityPotionEffectEvent.Cause.MILK); // CraftBukkit + } + + return itemstack.isEmpty() ? new ItemStack(Items.BUCKET) : itemstack; + } + + public int c(ItemStack itemstack) { + return 32; + } + + public EnumAnimation d(ItemStack itemstack) { + return EnumAnimation.DRINK; + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + entityhuman.c(enumhand); + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, entityhuman.b(enumhand)); + } +} diff --git a/src/main/java/net/minecraft/server/ItemMinecart.java b/src/main/java/net/minecraft/server/ItemMinecart.java new file mode 100644 index 000000000000..671cfde80b93 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemMinecart.java @@ -0,0 +1,133 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockDispenseEvent; +// CraftBukkit end + +public class ItemMinecart extends Item { + + private static final IDispenseBehavior a = new DispenseBehaviorItem() { + private final DispenseBehaviorItem a = new DispenseBehaviorItem(); + + public ItemStack a(ISourceBlock isourceblock, ItemStack itemstack) { + EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING); + World world = isourceblock.getWorld(); + double d0 = isourceblock.getX() + (double) enumdirection.getAdjacentX() * 1.125D; + double d1 = Math.floor(isourceblock.getY()) + (double) enumdirection.getAdjacentY(); + double d2 = isourceblock.getZ() + (double) enumdirection.getAdjacentZ() * 1.125D; + BlockPosition blockposition = isourceblock.getBlockPosition().shift(enumdirection); + IBlockData iblockdata = world.getType(blockposition); + BlockPropertyTrackPosition blockpropertytrackposition = iblockdata.getBlock() instanceof BlockMinecartTrackAbstract ? (BlockPropertyTrackPosition) iblockdata.get(((BlockMinecartTrackAbstract) iblockdata.getBlock()).e()) : BlockPropertyTrackPosition.NORTH_SOUTH; + double d3; + + if (iblockdata.a(TagsBlock.RAILS)) { + if (blockpropertytrackposition.c()) { + d3 = 0.6D; + } else { + d3 = 0.1D; + } + } else { + if (!iblockdata.isAir() || !world.getType(blockposition.down()).a(TagsBlock.RAILS)) { + return this.a.dispense(isourceblock, itemstack); + } + + IBlockData iblockdata1 = world.getType(blockposition.down()); + BlockPropertyTrackPosition blockpropertytrackposition1 = iblockdata1.getBlock() instanceof BlockMinecartTrackAbstract ? (BlockPropertyTrackPosition) iblockdata1.get(((BlockMinecartTrackAbstract) iblockdata1.getBlock()).e()) : BlockPropertyTrackPosition.NORTH_SOUTH; + + if (enumdirection != EnumDirection.DOWN && blockpropertytrackposition1.c()) { + d3 = -0.4D; + } else { + d3 = -0.9D; + } + } + + // CraftBukkit start + // EntityMinecartAbstract entityminecartabstract = EntityMinecartAbstract.a(world, d0, d1 + d3, d2, ((ItemMinecart) itemstack.getItem()).b); + ItemStack itemstack1 = itemstack.cloneAndSubtract(1); + org.bukkit.block.Block block2 = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); + + BlockDispenseEvent event = new BlockDispenseEvent(block2, craftItem.clone(), new org.bukkit.util.Vector(d0, d1 + d3, d2)); + if (!BlockDispenser.eventFired) { + world.getServer().getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + itemstack.add(1); + return itemstack; + } + + if (!event.getItem().equals(craftItem)) { + itemstack.add(1); + // Chain to handler for new item + ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); + IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem()); + if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) { + idispensebehavior.dispense(isourceblock, eventStack); + return itemstack; + } + } + + itemstack1 = CraftItemStack.asNMSCopy(event.getItem()); + EntityMinecartAbstract entityminecartabstract = EntityMinecartAbstract.a(world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), ((ItemMinecart) itemstack1.getItem()).b); + + if (itemstack.hasName()) { + entityminecartabstract.setCustomName(itemstack.getName()); + } + + if (!world.addEntity(entityminecartabstract)) itemstack.add(1); + // itemstack.subtract(1); // CraftBukkit - handled during event processing + // CraftBukkit end + return itemstack; + } + + protected void a(ISourceBlock isourceblock) { + isourceblock.getWorld().triggerEffect(1000, isourceblock.getBlockPosition(), 0); + } + }; + private final EntityMinecartAbstract.EnumMinecartType b; + + public ItemMinecart(EntityMinecartAbstract.EnumMinecartType entityminecartabstract_enumminecarttype, Item.Info item_info) { + super(item_info); + this.b = entityminecartabstract_enumminecarttype; + BlockDispenser.a((IMaterial) this, ItemMinecart.a); + } + + public EnumInteractionResult a(ItemActionContext itemactioncontext) { + World world = itemactioncontext.getWorld(); + BlockPosition blockposition = itemactioncontext.getClickPosition(); + IBlockData iblockdata = world.getType(blockposition); + + if (!iblockdata.a(TagsBlock.RAILS)) { + return EnumInteractionResult.FAIL; + } else { + ItemStack itemstack = itemactioncontext.getItemStack(); + + if (!world.isClientSide) { + BlockPropertyTrackPosition blockpropertytrackposition = iblockdata.getBlock() instanceof BlockMinecartTrackAbstract ? (BlockPropertyTrackPosition) iblockdata.get(((BlockMinecartTrackAbstract) iblockdata.getBlock()).e()) : BlockPropertyTrackPosition.NORTH_SOUTH; + double d0 = 0.0D; + + if (blockpropertytrackposition.c()) { + d0 = 0.5D; + } + + EntityMinecartAbstract entityminecartabstract = EntityMinecartAbstract.a(world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.0625D + d0, (double) blockposition.getZ() + 0.5D, this.b); + + if (itemstack.hasName()) { + entityminecartabstract.setCustomName(itemstack.getName()); + } + + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPlaceEvent(itemactioncontext, entityminecartabstract).isCancelled()) { + return EnumInteractionResult.FAIL; + } + // CraftBukkit end + if (!world.addEntity(entityminecartabstract)) return EnumInteractionResult.PASS; // CraftBukkit + } + + itemstack.subtract(1); + return EnumInteractionResult.SUCCESS; + } + } +} diff --git a/src/main/java/net/minecraft/server/ItemPotion.java b/src/main/java/net/minecraft/server/ItemPotion.java new file mode 100644 index 000000000000..6584ee48524a --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemPotion.java @@ -0,0 +1,86 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +public class ItemPotion extends Item { + + public ItemPotion(Item.Info item_info) { + super(item_info); + } + + public ItemStack a(ItemStack itemstack, World world, EntityLiving entityliving) { + EntityHuman entityhuman = entityliving instanceof EntityHuman ? (EntityHuman) entityliving : null; + + if (entityhuman == null || !entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + if (entityhuman instanceof EntityPlayer) { + CriterionTriggers.z.a((EntityPlayer) entityhuman, itemstack); + } + + if (!world.isClientSide) { + List list = PotionUtil.getEffects(itemstack); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + if (mobeffect.getMobEffect().isInstant()) { + mobeffect.getMobEffect().applyInstantEffect(entityhuman, entityhuman, entityliving, mobeffect.getAmplifier(), 1.0D); + } else { + entityliving.addEffect(new MobEffect(mobeffect), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.POTION_DRINK); // CraftBukkit + } + } + } + + if (entityhuman != null) { + entityhuman.b(StatisticList.ITEM_USED.b(this)); + } + + if (entityhuman == null || !entityhuman.abilities.canInstantlyBuild) { + if (itemstack.isEmpty()) { + return new ItemStack(Items.GLASS_BOTTLE); + } + + if (entityhuman != null) { + entityhuman.inventory.pickup(new ItemStack(Items.GLASS_BOTTLE)); + } + } + + return itemstack; + } + + public int c(ItemStack itemstack) { + return 32; + } + + public EnumAnimation d(ItemStack itemstack) { + return EnumAnimation.DRINK; + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + entityhuman.c(enumhand); + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, entityhuman.b(enumhand)); + } + + public String h(ItemStack itemstack) { + return PotionUtil.d(itemstack).b(this.getName() + ".effect."); + } + + public void a(CreativeModeTab creativemodetab, NonNullList nonnulllist) { + if (this.a(creativemodetab)) { + Iterator iterator = IRegistry.POTION.iterator(); + + while (iterator.hasNext()) { + PotionRegistry potionregistry = (PotionRegistry) iterator.next(); + + if (potionregistry != Potions.EMPTY) { + nonnulllist.add(PotionUtil.a(new ItemStack(this), potionregistry)); + } + } + } + + } +} diff --git a/src/main/java/net/minecraft/server/ItemRecord.java b/src/main/java/net/minecraft/server/ItemRecord.java new file mode 100644 index 000000000000..ddae1f516514 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemRecord.java @@ -0,0 +1,57 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import java.util.List; +import java.util.Map; +import java.util.Random; + +public class ItemRecord extends Item { + + private static final Map a = Maps.newHashMap(); + private static final List b = Lists.newArrayList(); + private final int c; + private final SoundEffect d; + + protected ItemRecord(int i, SoundEffect soundeffect, Item.Info item_info) { + super(item_info); + this.c = i; + this.d = soundeffect; + ItemRecord.a.put(this.d, this); + ItemRecord.b.add(this); + } + + public static ItemRecord a(Random random) { + return (ItemRecord) ItemRecord.b.get(random.nextInt(ItemRecord.b.size())); + } + + public EnumInteractionResult a(ItemActionContext itemactioncontext) { + World world = itemactioncontext.getWorld(); + BlockPosition blockposition = itemactioncontext.getClickPosition(); + IBlockData iblockdata = world.getType(blockposition); + + if (iblockdata.getBlock() == Blocks.JUKEBOX && !(Boolean) iblockdata.get(BlockJukeBox.HAS_RECORD)) { + ItemStack itemstack = itemactioncontext.getItemStack(); + + if (!world.isClientSide) { + if (true) return EnumInteractionResult.SUCCESS; // CraftBukkit - handled in ItemStack + ((BlockJukeBox) Blocks.JUKEBOX).a((GeneratorAccess) world, blockposition, iblockdata, itemstack); + world.a((EntityHuman) null, 1010, blockposition, Item.getId(this)); + itemstack.subtract(1); + EntityHuman entityhuman = itemactioncontext.getEntity(); + + if (entityhuman != null) { + entityhuman.a(StatisticList.PLAY_RECORD); + } + } + + return EnumInteractionResult.SUCCESS; + } else { + return EnumInteractionResult.PASS; + } + } + + public int d() { + return this.c; + } +} diff --git a/src/main/java/net/minecraft/server/ItemSkullPlayer.java b/src/main/java/net/minecraft/server/ItemSkullPlayer.java new file mode 100644 index 000000000000..a21ec22a9939 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemSkullPlayer.java @@ -0,0 +1,65 @@ +package net.minecraft.server; + +import com.mojang.authlib.GameProfile; +import java.util.UUID; +import org.apache.commons.lang3.StringUtils; + +public class ItemSkullPlayer extends ItemBlockWallable { + + public ItemSkullPlayer(Block block, Block block1, Item.Info item_info) { + super(block, block1, item_info); + } + + public IChatBaseComponent i(ItemStack itemstack) { + if (itemstack.getItem() == Items.PLAYER_HEAD && itemstack.hasTag()) { + String s = null; + NBTTagCompound nbttagcompound = itemstack.getTag(); + + if (nbttagcompound.hasKeyOfType("SkullOwner", 8)) { + s = nbttagcompound.getString("SkullOwner"); + } else if (nbttagcompound.hasKeyOfType("SkullOwner", 10)) { + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("SkullOwner"); + + if (nbttagcompound1.hasKeyOfType("Name", 8)) { + s = nbttagcompound1.getString("Name"); + } + } + + if (s != null) { + return new ChatMessage(this.getName() + ".named", new Object[] { s}); + } + } + + return super.i(itemstack); + } + + public boolean a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKeyOfType("SkullOwner", 8) && !StringUtils.isBlank(nbttagcompound.getString("SkullOwner"))) { + GameProfile gameprofile = new GameProfile((UUID) null, nbttagcompound.getString("SkullOwner")); + + // Spigot start + TileEntitySkull.b(gameprofile, new com.google.common.base.Predicate() { + + @Override + public boolean apply(GameProfile gameprofile) { + nbttagcompound.set("SkullOwner", GameProfileSerializer.serialize(new NBTTagCompound(), gameprofile)); + return false; + } + }, false); + // Spigot end + return true; + } else { + // CraftBukkit start + NBTTagList textures = nbttagcompound.getCompound("SkullOwner").getCompound("Properties").getList("textures", 10); // Safe due to method contracts + for (int i = 0; i < textures.size(); i++) { + if (textures.get(i) instanceof NBTTagCompound && !((NBTTagCompound) textures.get(i)).hasKeyOfType("Signature", 8) && ((NBTTagCompound) textures.get(i)).getString("Value").trim().isEmpty()) { + nbttagcompound.remove("SkullOwner"); + break; + } + } + // CraftBukkit end + return false; + } + } +} diff --git a/src/main/java/net/minecraft/server/ItemSnowball.java b/src/main/java/net/minecraft/server/ItemSnowball.java new file mode 100644 index 000000000000..95194ccdc87a --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemSnowball.java @@ -0,0 +1,47 @@ +package net.minecraft.server; + +public class ItemSnowball extends Item { + + public ItemSnowball(Item.Info item_info) { + super(item_info); + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + // CraftBukkit start - moved down + /* + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_SNOWBALL_THROW, SoundCategory.NEUTRAL, 0.5F, 0.4F / (ItemSnowball.i.nextFloat() * 0.4F + 0.8F)); + */ + if (!world.isClientSide) { + EntitySnowball entitysnowball = new EntitySnowball(world, entityhuman); + + entitysnowball.a(entityhuman, entityhuman.pitch, entityhuman.yaw, 0.0F, 1.5F, 1.0F); + // Paper start + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) entitysnowball.getBukkitEntity()); + if (event.callEvent() && world.addEntity(entitysnowball)) { + if (event.shouldConsume() && !entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } else if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); + } + + world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_SNOWBALL_THROW, SoundCategory.NEUTRAL, 0.5F, 0.4F / (Entity.SHARED_RANDOM.nextFloat() * 0.4F + 0.8F)); + } else { + if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); + } + return new InteractionResultWrapper(EnumInteractionResult.FAIL, itemstack); + } + // Paper end + } + // CraftBukkit end + + entityhuman.b(StatisticList.ITEM_USED.b(this)); + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, itemstack); + } +} diff --git a/src/main/java/net/minecraft/server/ItemSplashPotion.java b/src/main/java/net/minecraft/server/ItemSplashPotion.java new file mode 100644 index 000000000000..2bddefb802d9 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemSplashPotion.java @@ -0,0 +1,48 @@ +package net.minecraft.server; + +public class ItemSplashPotion extends ItemPotion { + + public ItemSplashPotion(Item.Info item_info) { + super(item_info); + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + /* // Paper start + ItemStack itemstack1 = entityhuman.abilities.canInstantlyBuild ? itemstack.cloneItemStack() : itemstack.cloneAndSubtract(1); + + world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_SPLASH_POTION_THROW, SoundCategory.PLAYERS, 0.5F, 0.4F / (ItemSplashPotion.i.nextFloat() * 0.4F + 0.8F)); + */ // Paper end + if (!world.isClientSide) { + // Paper start - ensure stack count matches vanilla behavior without modifying original stack yet + ItemStack itemstack1 = itemstack.cloneItemStack(); + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack1.setCount(1); + } + // Paper end + EntityPotion entitypotion = new EntityPotion(world, entityhuman, itemstack1); + + entitypotion.a(entityhuman, entityhuman.pitch, entityhuman.yaw, -20.0F, 0.5F, 1.0F); + // Paper start + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), (org.bukkit.entity.Projectile) entitypotion.getBukkitEntity()); + if (event.callEvent() && world.addEntity(entitypotion)) { + if (event.shouldConsume() && !entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } else if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); + } + + world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.ENTITY_SPLASH_POTION_THROW, SoundCategory.PLAYERS, 0.5F, 0.4F / (Entity.SHARED_RANDOM.nextFloat() * 0.4F + 0.8F)); + } else { + if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); + } + return new InteractionResultWrapper(EnumInteractionResult.FAIL, itemstack); + } + // Paper end + } + + entityhuman.b(StatisticList.ITEM_USED.b(this)); + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, itemstack); + } +} diff --git a/src/main/java/net/minecraft/server/ItemStack.java b/src/main/java/net/minecraft/server/ItemStack.java new file mode 100644 index 000000000000..7827f692af02 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemStack.java @@ -0,0 +1,894 @@ +package net.minecraft.server; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.gson.JsonParseException; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Collections; +import java.util.Comparator; +import java.util.Locale; +import java.util.Objects; +import java.util.Random; +import java.util.function.Predicate; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import com.mojang.datafixers.Dynamic; +import java.util.List; +import java.util.Map; + +import org.bukkit.Location; +import org.bukkit.TreeType; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.block.CraftBlockState; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockFertilizeEvent; +import org.bukkit.event.player.PlayerItemDamageEvent; +import org.bukkit.event.world.StructureGrowEvent; +// CraftBukkit end + +public final class ItemStack { + + private static final Logger c = LogManager.getLogger(); + public static final ItemStack a = new ItemStack((Item) null); + public static final DecimalFormat b = D(); + private int count; + private int e; + @Deprecated + private Item item; + NBTTagCompound tag; // Paper -> package private + private boolean h; + private EntityItemFrame i; + private ShapeDetectorBlock j; + private boolean k; + private ShapeDetectorBlock l; + private boolean m; + + private static DecimalFormat D() { + DecimalFormat decimalformat = new DecimalFormat("#.##"); + + decimalformat.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ROOT)); + return decimalformat; + } + // Paper start + private static final java.util.Comparator enchantSorter = java.util.Comparator.comparing(o -> o.getString("id")); + private void processEnchantOrder(NBTTagCompound tag) { + if (tag == null || !tag.hasKeyOfType("Enchantments", 9)) { + return; + } + NBTTagList list = tag.getList("Enchantments", 10); + if (list.size() < 2) { + return; + } + try { + //noinspection unchecked + list.sort((Comparator) enchantSorter); // Paper + } catch (Exception ignored) {} + } + // Paper end + + public ItemStack(IMaterial imaterial) { + this(imaterial, 1); + } + + public ItemStack(IMaterial imaterial, int i) { + this.item = imaterial == null ? null : imaterial.getItem(); + this.count = i; + this.E(); + } + + // Called to run this stack through the data converter to handle older storage methods and serialized items + public void convertStack() { + if (false && MinecraftServer.getServer() != null) { // Skip for now, causes more issues than it solves + // Don't convert some things - both the old and new data values are valid + // Conversion would make getting then impossible + if (this.item == Blocks.PUMPKIN.getItem() || this.item == Blocks.GRASS.getItem() || this.item == Blocks.SNOW.getItem()) { + return; + } + + NBTTagCompound savedStack = new NBTTagCompound(); + this.save(savedStack); + savedStack = (NBTTagCompound) MinecraftServer.getServer().dataConverterManager.update(DataConverterTypes.ITEM_STACK, new Dynamic(DynamicOpsNBT.a, savedStack), -1, CraftMagicNumbers.INSTANCE.getDataVersion()).getValue(); + this.load(savedStack); + } + } + + private void E() { + if (this.h && this == ItemStack.a) throw new AssertionError("TRAP"); // CraftBukkit + this.h = false; + this.h = this.isEmpty(); + } + + // CraftBukkit - break into own method + private void load(NBTTagCompound nbttagcompound) { + Item item = (Item) IRegistry.ITEM.get(new MinecraftKey(nbttagcompound.getString("id"))); + + this.item = item == null ? Items.AIR : item; + this.count = nbttagcompound.getByte("Count"); + if (nbttagcompound.hasKeyOfType("tag", 10)) { + // CraftBukkit start - make defensive copy as this data may be coming from the save thread + this.tag = (NBTTagCompound) nbttagcompound.getCompound("tag").clone(); + processEnchantOrder(this.tag); // Paper + this.getItem().a(this.tag); + // CraftBukkit end + } + + if (this.getItem().usesDurability()) { + this.setDamage(this.getDamage()); + } + } + + private ItemStack(NBTTagCompound nbttagcompound) { + this.load(nbttagcompound); + // CraftBukkit end + this.E(); + } + + public static ItemStack a(NBTTagCompound nbttagcompound) { + try { + return new ItemStack(nbttagcompound); + } catch (RuntimeException runtimeexception) { + ItemStack.c.debug("Tried to load invalid item: {}", nbttagcompound, runtimeexception); + return ItemStack.a; + } + } + + public boolean isEmpty() { + return this == ItemStack.a || this.item == null || this.item == Items.AIR || this.count <= 0; // Paper + } + + public ItemStack cloneAndSubtract(int i) { + int j = Math.min(i, this.count); + ItemStack itemstack = this.cloneItemStack(); + + itemstack.setCount(j); + this.subtract(j); + return itemstack; + } + + public Item getItem() { + return this.h ? Items.AIR : this.item; + } + + public EnumInteractionResult placeItem(ItemActionContext itemactioncontext, EnumHand enumhand) { // CraftBukkit - add hand + EntityHuman entityhuman = itemactioncontext.getEntity(); + BlockPosition blockposition = itemactioncontext.getClickPosition(); + ShapeDetectorBlock shapedetectorblock = new ShapeDetectorBlock(itemactioncontext.getWorld(), blockposition, false); + + if (entityhuman != null && !entityhuman.abilities.mayBuild && !this.b(itemactioncontext.getWorld().F(), shapedetectorblock)) { + return EnumInteractionResult.PASS; + } else { + // CraftBukkit start - handle all block place event logic here + NBTTagCompound oldData = this.getTagClone(); + int oldCount = this.getCount(); + World world = itemactioncontext.getWorld(); + + if (!(this.getItem() instanceof ItemBucket)) { // if not bucket + world.captureBlockStates = true; + // special case bonemeal + if (this.getItem() == Items.BONE_MEAL) { + world.captureTreeGeneration = true; + } + } + Item item = this.getItem(); + EnumInteractionResult enuminteractionresult = item.a(itemactioncontext); + NBTTagCompound newData = this.getTagClone(); + int newCount = this.getCount(); + this.setCount(oldCount); + this.setTagClone(oldData); + world.captureBlockStates = false; + if (enuminteractionresult == EnumInteractionResult.SUCCESS && world.captureTreeGeneration && world.capturedBlockStates.size() > 0) { + world.captureTreeGeneration = false; + Location location = new Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ()); + TreeType treeType = BlockSapling.treeType; + BlockSapling.treeType = null; + List blocks = (List) world.capturedBlockStates.clone(); + world.capturedBlockStates.clear(); + StructureGrowEvent structureEvent = null; + if (treeType != null) { + boolean isBonemeal = getItem() == Items.BONE_MEAL; + structureEvent = new StructureGrowEvent(location, treeType, isBonemeal, (Player) entityhuman.getBukkitEntity(), blocks); + org.bukkit.Bukkit.getPluginManager().callEvent(structureEvent); + } + + BlockFertilizeEvent fertilizeEvent = new BlockFertilizeEvent(CraftBlock.at(world, blockposition), (Player) entityhuman.getBukkitEntity(), blocks); + fertilizeEvent.setCancelled(structureEvent != null && structureEvent.isCancelled()); + org.bukkit.Bukkit.getPluginManager().callEvent(fertilizeEvent); + + if (!fertilizeEvent.isCancelled()) { + // Change the stack to its new contents if it hasn't been tampered with. + if (this.getCount() == oldCount && Objects.equals(this.tag, oldData)) { + this.setTag(newData); + this.setCount(newCount); + } + for (BlockState blockstate : blocks) { + blockstate.update(true); + } + } + + return enuminteractionresult; + } + world.captureTreeGeneration = false; + + if (entityhuman != null && enuminteractionresult == EnumInteractionResult.SUCCESS) { + org.bukkit.event.block.BlockPlaceEvent placeEvent = null; + List blocks = (List) world.capturedBlockStates.clone(); + world.capturedBlockStates.clear(); + if (blocks.size() > 1) { + placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockMultiPlaceEvent(world, entityhuman, enumhand, blocks, blockposition.getX(), blockposition.getY(), blockposition.getZ()); + } else if (blocks.size() == 1) { + placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(world, entityhuman, enumhand, blocks.get(0), blockposition.getX(), blockposition.getY(), blockposition.getZ()); + } + + if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) { + enuminteractionresult = EnumInteractionResult.FAIL; // cancel placement + // PAIL: Remove this when MC-99075 fixed + placeEvent.getPlayer().updateInventory(); + + // Paper start + for (Map.Entry e : world.capturedTileEntities.entrySet()) { + if (e.getValue() instanceof TileEntityLootable) { + ((TileEntityLootable) e.getValue()).setLootTable(null); + } + } + // Paper end + + // revert back all captured blocks + for (BlockState blockstate : blocks) { + blockstate.update(true, false); + } + + // Brute force all possible updates + BlockPosition placedPos = ((CraftBlock) placeEvent.getBlock()).getPosition(); + for (EnumDirection dir : EnumDirection.values()) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutBlockChange(world, placedPos.shift(dir))); + } + } else { + // Change the stack to its new contents if it hasn't been tampered with. + if (this.getCount() == oldCount && Objects.equals(this.tag, oldData)) { + this.setTag(newData); + this.setCount(newCount); + } + + for (Map.Entry e : world.capturedTileEntities.entrySet()) { + world.setTileEntity(e.getKey(), e.getValue()); + } + + for (BlockState blockstate : blocks) { + int updateFlag = ((CraftBlockState) blockstate).getFlag(); + IBlockData oldBlock = ((CraftBlockState) blockstate).getHandle(); + BlockPosition newblockposition = ((CraftBlockState) blockstate).getPosition(); + IBlockData block = world.getType(newblockposition); + + if (!(block.getBlock() instanceof BlockTileEntity)) { // Containers get placed automatically + block.getBlock().onPlace(block, world, newblockposition, oldBlock); + } + + world.notifyAndUpdatePhysics(newblockposition, null, oldBlock, block, world.getType(newblockposition), updateFlag); // send null chunk as chunk.k() returns false by this point + } + + // Special case juke boxes as they update their tile entity. Copied from ItemRecord. + // PAIL: checkme on updates. + if (this.item instanceof ItemRecord) { + ((BlockJukeBox) Blocks.JUKEBOX).a(world, blockposition, world.getType(blockposition), this); + world.a((EntityHuman) null, 1010, blockposition, Item.getId(this.item)); + this.subtract(1); + entityhuman.a(StatisticList.PLAY_RECORD); + } + + if (this.item == Items.WITHER_SKELETON_SKULL) { // Special case skulls to allow wither spawns to be cancelled + BlockPosition bp = blockposition; + if (!world.getType(blockposition).getMaterial().isReplaceable()) { + if (!world.getType(blockposition).getMaterial().isBuildable()) { + bp = null; + } else { + bp = bp.shift(itemactioncontext.getClickedFace()); + } + } + if (bp != null) { + TileEntity te = world.getTileEntity(bp); + if (te instanceof TileEntitySkull) { + BlockWitherSkull.a(world, bp, (TileEntitySkull) te); + } + } + } + + // SPIGOT-1288 - play sound stripped from ItemBlock + if (this.item instanceof ItemBlock) { + SoundEffectType soundeffecttype = ((ItemBlock) this.item).getBlock().getStepSound(); + world.a(entityhuman, blockposition, soundeffecttype.e(), SoundCategory.BLOCKS, (soundeffecttype.a() + 1.0F) / 2.0F, soundeffecttype.b() * 0.8F); + } + + entityhuman.b(StatisticList.ITEM_USED.b(item)); + } + } + world.capturedTileEntities.clear(); + world.capturedBlockStates.clear(); + // CraftBukkit end + + return enuminteractionresult; + } + } + + public float a(IBlockData iblockdata) { + return this.getItem().getDestroySpeed(this, iblockdata); + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + return this.getItem().a(world, entityhuman, enumhand); + } + + public ItemStack a(World world, EntityLiving entityliving) { + return this.getItem().a(this, world, entityliving); + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + MinecraftKey minecraftkey = IRegistry.ITEM.getKey(this.getItem()); + + nbttagcompound.setString("id", minecraftkey == null ? "minecraft:air" : minecraftkey.toString()); + nbttagcompound.setByte("Count", (byte) this.count); + if (this.tag != null) { + nbttagcompound.set("tag", this.tag.clone()); // CraftBukkit - make defensive copy, data is going to another thread + } + + return nbttagcompound; + } + + public int getMaxStackSize() { + return this.getItem().getMaxStackSize(); + } + + public boolean isStackable() { + return this.getMaxStackSize() > 1 && (!this.e() || !this.f()); + } + + public boolean e() { + if (!this.h && this.getItem().getMaxDurability() > 0) { + NBTTagCompound nbttagcompound = this.getTag(); + + return nbttagcompound == null || !nbttagcompound.getBoolean("Unbreakable"); + } else { + return false; + } + } + + public boolean f() { + return this.e() && this.getDamage() > 0; + } + + public int getDamage() { + return this.tag == null ? 0 : this.tag.getInt("Damage"); + } + + public void setDamage(int i) { + this.getOrCreateTag().setInt("Damage", Math.max(0, i)); + } + + public int h() { + return this.getItem().getMaxDurability(); + } + + public boolean isDamaged(int i, Random random, @Nullable EntityPlayer entityplayer) { + if (!this.e()) { + return false; + } else { + int j; + + if (i > 0) { + j = EnchantmentManager.getEnchantmentLevel(Enchantments.DURABILITY, this); + int k = 0; + + for (int l = 0; j > 0 && l < i; ++l) { + if (EnchantmentDurability.a(this, j, random)) { + ++k; + } + } + + i -= k; + // CraftBukkit start + if (entityplayer != null) { + PlayerItemDamageEvent event = new PlayerItemDamageEvent(entityplayer.getBukkitEntity(), CraftItemStack.asCraftMirror(this), i); + event.getPlayer().getServer().getPluginManager().callEvent(event); + + if (i != event.getDamage() || event.isCancelled()) { + event.getPlayer().updateInventory(); + } + if (event.isCancelled()) { + return false; + } + + i = event.getDamage(); + } + // CraftBukkit end + if (i <= 0) { + return false; + } + } + + if (entityplayer != null && i != 0) { + CriterionTriggers.t.a(entityplayer, this, this.getDamage() + i); + } + + j = this.getDamage() + i; + this.setDamage(j); + return j >= this.h(); + } + } + + public void damage(int i, EntityLiving entityliving) { + if (!(entityliving instanceof EntityHuman) || !((EntityHuman) entityliving).abilities.canInstantlyBuild) { + if (this.e()) { + if (this.isDamaged(i, entityliving.getRandom(), entityliving instanceof EntityPlayer ? (EntityPlayer) entityliving : null)) { + entityliving.c(this); + Item item = this.getItem(); + // CraftBukkit start - Check for item breaking + if (this.count == 1 && entityliving instanceof EntityHuman) { + org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemBreakEvent((EntityHuman) entityliving, this); + } + // CraftBukkit end + + this.subtract(1); + if (entityliving instanceof EntityHuman) { + ((EntityHuman) entityliving).b(StatisticList.ITEM_BROKEN.b(item)); + } + + this.setDamage(0); + } + + } + } + } + + public void a(EntityLiving entityliving, EntityHuman entityhuman) { + Item item = this.getItem(); + + if (item.a(this, entityliving, (EntityLiving) entityhuman)) { + entityhuman.b(StatisticList.ITEM_USED.b(item)); + } + + } + + public void a(World world, IBlockData iblockdata, BlockPosition blockposition, EntityHuman entityhuman) { + Item item = this.getItem(); + + if (item.a(this, world, iblockdata, blockposition, entityhuman)) { + entityhuman.b(StatisticList.ITEM_USED.b(item)); + } + + } + + public boolean b(IBlockData iblockdata) { + return this.getItem().canDestroySpecialBlock(iblockdata); + } + + public boolean a(EntityHuman entityhuman, EntityLiving entityliving, EnumHand enumhand) { + return this.getItem().a(this, entityhuman, entityliving, enumhand); + } + + public ItemStack cloneItemStack() { return cloneItemStack(false); } // Paper + public ItemStack cloneItemStack(boolean origItem) { // Paper + ItemStack itemstack = new ItemStack(origItem ? this.item : this.getItem(), this.count); // Paper + + itemstack.d(this.B()); + if (this.tag != null) { + itemstack.tag = this.tag.clone(); + } + + return itemstack; + } + + public static boolean equals(ItemStack itemstack, ItemStack itemstack1) { + return itemstack.isEmpty() && itemstack1.isEmpty() ? true : (!itemstack.isEmpty() && !itemstack1.isEmpty() ? (itemstack.tag == null && itemstack1.tag != null ? false : itemstack.tag == null || itemstack.tag.equals(itemstack1.tag)) : false); + } + + public static boolean matches(ItemStack itemstack, ItemStack itemstack1) { + return itemstack.isEmpty() && itemstack1.isEmpty() ? true : (!itemstack.isEmpty() && !itemstack1.isEmpty() ? itemstack.c(itemstack1) : false); + } + + private boolean c(ItemStack itemstack) { + return this.count != itemstack.count ? false : (this.getItem() != itemstack.getItem() ? false : (this.tag == null && itemstack.tag != null ? false : this.tag == null || this.tag.equals(itemstack.tag))); + } + + public static boolean c(ItemStack itemstack, ItemStack itemstack1) { + return itemstack == itemstack1 ? true : (!itemstack.isEmpty() && !itemstack1.isEmpty() ? itemstack.doMaterialsMatch(itemstack1) : false); + } + + public static boolean d(ItemStack itemstack, ItemStack itemstack1) { + return itemstack == itemstack1 ? true : (!itemstack.isEmpty() && !itemstack1.isEmpty() ? itemstack.b(itemstack1) : false); + } + + public boolean doMaterialsMatch(ItemStack itemstack) { + return !itemstack.isEmpty() && this.getItem() == itemstack.getItem(); + } + + public boolean b(ItemStack itemstack) { + return !this.e() ? this.doMaterialsMatch(itemstack) : !itemstack.isEmpty() && this.getItem() == itemstack.getItem(); + } + + public String j() { + return this.getItem().h(this); + } + + public String toString() { + return this.count + "x" + this.getItem().getName(); + } + + public void a(World world, Entity entity, int i, boolean flag) { + if (this.e > 0) { + --this.e; + } + + if (this.getItem() != null) { + this.getItem().a(this, world, entity, i, flag); + } + + } + + public void a(World world, EntityHuman entityhuman, int i) { + entityhuman.a(StatisticList.ITEM_CRAFTED.b(this.getItem()), i); + this.getItem().b(this, world, entityhuman); + } + + public int getItemUseMaxDuration() { return k(); } // Paper - OBFHELPER + public int k() { + return this.getItem().c(this); + } + + public EnumAnimation l() { + return this.getItem().d(this); + } + + public void a(World world, EntityLiving entityliving, int i) { + this.getItem().a(this, world, entityliving, i); + } + + public boolean hasTag() { + return !this.h && this.tag != null && !this.tag.isEmpty(); + } + + @Nullable + public NBTTagCompound getTag() { + return this.tag; + } + + // CraftBukkit start + @Nullable + private NBTTagCompound getTagClone() { + return this.tag == null ? null : this.tag.clone(); + } + + private void setTagClone(@Nullable NBTTagCompound nbtttagcompound) { + this.setTag(nbtttagcompound == null ? null : nbtttagcompound.clone()); + } + // CraftBukkit end + + public NBTTagCompound getOrCreateTag() { + if (this.tag == null) { + this.setTag(new NBTTagCompound()); + } + + return this.tag; + } + + public NBTTagCompound a(String s) { + if (this.tag != null && this.tag.hasKeyOfType(s, 10)) { + return this.tag.getCompound(s); + } else { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + this.a(s, (NBTBase) nbttagcompound); + return nbttagcompound; + } + } + + @Nullable + public NBTTagCompound b(String s) { + return this.tag != null && this.tag.hasKeyOfType(s, 10) ? this.tag.getCompound(s) : null; + } + + public void c(String s) { + if (this.tag != null && this.tag.hasKey(s)) { + this.tag.remove(s); + if (this.tag.isEmpty()) { + this.tag = null; + } + } + + } + + public NBTTagList getEnchantments() { + return this.tag != null ? this.tag.getList("Enchantments", 10) : new NBTTagList(); + } + + // Paper start - (this is just a good no conflict location) + public org.bukkit.inventory.ItemStack asBukkitMirror() { + return CraftItemStack.asCraftMirror(this); + } + public org.bukkit.inventory.ItemStack asBukkitCopy() { + return CraftItemStack.asCraftMirror(this.cloneItemStack()); + } + public static ItemStack fromBukkitCopy(org.bukkit.inventory.ItemStack itemstack) { + return CraftItemStack.asNMSCopy(itemstack); + } + // Paper end + public void setTag(@Nullable NBTTagCompound nbttagcompound) { + this.tag = nbttagcompound; + processEnchantOrder(this.tag); // Paper + } + + public IChatBaseComponent getName() { + NBTTagCompound nbttagcompound = this.b("display"); + + if (nbttagcompound != null && nbttagcompound.hasKeyOfType("Name", 8)) { + try { + IChatBaseComponent ichatbasecomponent = IChatBaseComponent.ChatSerializer.a(nbttagcompound.getString("Name")); + + if (ichatbasecomponent != null) { + return ichatbasecomponent; + } + + nbttagcompound.remove("Name"); + } catch (JsonParseException jsonparseexception) { + nbttagcompound.remove("Name"); + } + } + + return this.getItem().i(this); + } + + public ItemStack a(@Nullable IChatBaseComponent ichatbasecomponent) { + NBTTagCompound nbttagcompound = this.a("display"); + + if (ichatbasecomponent != null) { + nbttagcompound.setString("Name", IChatBaseComponent.ChatSerializer.a(ichatbasecomponent)); + } else { + nbttagcompound.remove("Name"); + } + + return this; + } + + public void r() { + NBTTagCompound nbttagcompound = this.b("display"); + + if (nbttagcompound != null) { + nbttagcompound.remove("Name"); + if (nbttagcompound.isEmpty()) { + this.c("display"); + } + } + + if (this.tag != null && this.tag.isEmpty()) { + this.tag = null; + } + + } + + public boolean hasName() { + NBTTagCompound nbttagcompound = this.b("display"); + + return nbttagcompound != null && nbttagcompound.hasKeyOfType("Name", 8); + } + + public EnumItemRarity u() { + return this.getItem().j(this); + } + + public boolean canEnchant() { + return !this.getItem().a(this) ? false : !this.hasEnchantments(); + } + + public void addEnchantment(Enchantment enchantment, int i) { + this.getOrCreateTag(); + if (!this.tag.hasKeyOfType("Enchantments", 9)) { + this.tag.set("Enchantments", new NBTTagList()); + } + + NBTTagList nbttaglist = this.tag.getList("Enchantments", 10); + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setString("id", String.valueOf(IRegistry.ENCHANTMENT.getKey(enchantment))); + nbttagcompound.setShort("lvl", (short) ((byte) i)); + nbttaglist.add((NBTBase) nbttagcompound); + processEnchantOrder(nbttagcompound); // Paper + } + + public boolean hasEnchantments() { + return this.tag != null && this.tag.hasKeyOfType("Enchantments", 9) ? !this.tag.getList("Enchantments", 10).isEmpty() : false; + } + + public void getOrCreateTagAndSet(String s, NBTBase nbtbase) { a(s, nbtbase);} // Paper - OBFHELPER + public void a(String s, NBTBase nbtbase) { + this.getOrCreateTag().set(s, nbtbase); + } + + public boolean x() { + return this.i != null; + } + + public void a(@Nullable EntityItemFrame entityitemframe) { + this.i = entityitemframe; + } + + @Nullable + public EntityItemFrame y() { + return this.h ? null : this.i; + } + + public int getRepairCost() { + return this.hasTag() && this.tag.hasKeyOfType("RepairCost", 3) ? this.tag.getInt("RepairCost") : 0; + } + + public void setRepairCost(int i) { + // CraftBukkit start - remove RepairCost tag when 0 (SPIGOT-3945) + if (i == 0) { + if (this.hasTag()) { + this.tag.remove("RepairCost"); + } + return; + } + // CraftBukkit end + this.getOrCreateTag().setInt("RepairCost", i); + } + + public Multimap a(EnumItemSlot enumitemslot) { + Object object; + + if (this.hasTag() && this.tag.hasKeyOfType("AttributeModifiers", 9)) { + object = HashMultimap.create(); + NBTTagList nbttaglist = this.tag.getList("AttributeModifiers", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist.getCompound(i); + AttributeModifier attributemodifier = GenericAttributes.a(nbttagcompound); + + if (attributemodifier != null && (!nbttagcompound.hasKeyOfType("Slot", 8) || nbttagcompound.getString("Slot").equals(enumitemslot.getSlotName())) && attributemodifier.a().getLeastSignificantBits() != 0L && attributemodifier.a().getMostSignificantBits() != 0L) { + ((Multimap) object).put(nbttagcompound.getString("AttributeName"), attributemodifier); + } + } + } else { + object = this.getItem().a(enumitemslot); + } + + return (Multimap) object; + } + + public void a(String s, AttributeModifier attributemodifier, @Nullable EnumItemSlot enumitemslot) { + this.getOrCreateTag(); + if (!this.tag.hasKeyOfType("AttributeModifiers", 9)) { + this.tag.set("AttributeModifiers", new NBTTagList()); + } + + NBTTagList nbttaglist = this.tag.getList("AttributeModifiers", 10); + NBTTagCompound nbttagcompound = GenericAttributes.a(attributemodifier); + + nbttagcompound.setString("AttributeName", s); + if (enumitemslot != null) { + nbttagcompound.setString("Slot", enumitemslot.getSlotName()); + } + + nbttaglist.add((NBTBase) nbttagcompound); + } + + // CraftBukkit start + @Deprecated + public void setItem(Item item) { + this.item = item; + } + // CraftBukkit end + + public IChatBaseComponent A() { + IChatBaseComponent ichatbasecomponent = (new ChatComponentText("")).addSibling(this.getName()); + + if (this.hasName()) { + ichatbasecomponent.a(EnumChatFormat.ITALIC); + } + + IChatBaseComponent ichatbasecomponent1 = ChatComponentUtils.a(ichatbasecomponent); + + if (!this.h) { + NBTTagCompound nbttagcompound = this.save(new NBTTagCompound()); + + ichatbasecomponent1.a(this.u().e).a((chatmodifier) -> { + chatmodifier.setChatHoverable(new ChatHoverable(ChatHoverable.EnumHoverAction.SHOW_ITEM, new ChatComponentText(nbttagcompound.toString()))); + }); + } + + return ichatbasecomponent1; + } + + private static boolean a(ShapeDetectorBlock shapedetectorblock, @Nullable ShapeDetectorBlock shapedetectorblock1) { + return shapedetectorblock1 != null && shapedetectorblock.a() == shapedetectorblock1.a() ? (shapedetectorblock.b() == null && shapedetectorblock1.b() == null ? true : (shapedetectorblock.b() != null && shapedetectorblock1.b() != null ? Objects.equals(shapedetectorblock.b().save(new NBTTagCompound()), shapedetectorblock1.b().save(new NBTTagCompound())) : false)) : false; + } + + public boolean a(TagRegistry tagregistry, ShapeDetectorBlock shapedetectorblock) { + if (a(shapedetectorblock, this.j)) { + return this.k; + } else { + this.j = shapedetectorblock; + if (this.hasTag() && this.tag.hasKeyOfType("CanDestroy", 9)) { + NBTTagList nbttaglist = this.tag.getList("CanDestroy", 8); + + for (int i = 0; i < nbttaglist.size(); ++i) { + String s = nbttaglist.getString(i); + + try { + Predicate predicate = ArgumentBlockPredicate.a().parse(new StringReader(s)).create(tagregistry); + + if (predicate.test(shapedetectorblock)) { + this.k = true; + return true; + } + } catch (CommandSyntaxException commandsyntaxexception) { + ; + } + } + } + + this.k = false; + return false; + } + } + + public boolean b(TagRegistry tagregistry, ShapeDetectorBlock shapedetectorblock) { + if (a(shapedetectorblock, this.l)) { + return this.m; + } else { + this.l = shapedetectorblock; + if (this.hasTag() && this.tag.hasKeyOfType("CanPlaceOn", 9)) { + NBTTagList nbttaglist = this.tag.getList("CanPlaceOn", 8); + + for (int i = 0; i < nbttaglist.size(); ++i) { + String s = nbttaglist.getString(i); + + try { + Predicate predicate = ArgumentBlockPredicate.a().parse(new StringReader(s)).create(tagregistry); + + if (predicate.test(shapedetectorblock)) { + this.m = true; + return true; + } + } catch (CommandSyntaxException commandsyntaxexception) { + ; + } + } + } + + this.m = false; + return false; + } + } + + public int B() { + return this.e; + } + + public void d(int i) { + this.e = i; + } + + public int getCount() { + return this.h ? 0 : this.count; + } + + public void setCount(int i) { + this.count = i; + this.E(); + } + + public void add(int i) { + this.setCount(this.count + i); + } + + public void subtract(int i) { + this.add(-i); + } +} diff --git a/src/main/java/net/minecraft/server/ItemTrident.java b/src/main/java/net/minecraft/server/ItemTrident.java new file mode 100644 index 000000000000..40b8eb20374d --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemTrident.java @@ -0,0 +1,149 @@ +package net.minecraft.server; + +import com.google.common.collect.Multimap; + +public class ItemTrident extends Item { + + public ItemTrident(Item.Info item_info) { + super(item_info); + // CraftBukkit start - obfuscator went a little crazy + /* + this.a(new MinecraftKey("throwing"), (itemstack, world, entityliving) -> { + return entityliving != null && entityliving.isHandRaised() && entityliving.cW() == itemstack ? 1.0F : 0.0F; + }); + */ + // CraftBukkit end + } + + public boolean a(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman) { + return !entityhuman.u(); + } + + public EnumAnimation d(ItemStack itemstack) { + return EnumAnimation.SPEAR; + } + + public int c(ItemStack itemstack) { + return 72000; + } + + public void a(ItemStack itemstack, World world, EntityLiving entityliving, int i) { + if (entityliving instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) entityliving; + int j = this.c(itemstack) - i; + + if (j >= 10) { + int k = EnchantmentManager.g(itemstack); + + if (k <= 0 || entityhuman.ao()) { + if (!world.isClientSide) { + // itemstack.damage(1, entityhuman); // CraftBukkit - moved down + if (k == 0) { + EntityThrownTrident entitythrowntrident = new EntityThrownTrident(world, entityhuman, itemstack); + + entitythrowntrident.a(entityhuman, entityhuman.pitch, entityhuman.yaw, 0.0F, 2.5F + (float) k * 0.5F, 1.0F); + if (entityhuman.abilities.canInstantlyBuild) { + entitythrowntrident.fromPlayer = EntityArrow.PickupStatus.CREATIVE_ONLY; + } + + // CraftBukkit start + if (!world.addEntity(entitythrowntrident)) { + if (entityhuman instanceof EntityPlayer) { + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); + } + return; + } + + itemstack.damage(1, entityhuman); + entitythrowntrident.trident = itemstack.cloneItemStack(); // SPIGOT-4511 update since damage call moved + // CraftBukkit end + + if (!entityhuman.abilities.canInstantlyBuild) { + entityhuman.inventory.f(itemstack); + } + } + } + + entityhuman.b(StatisticList.ITEM_USED.b(this)); + SoundEffect soundeffect = SoundEffects.ITEM_TRIDENT_THROW; + + if (k > 0) { + // CraftBukkit start + org.bukkit.event.player.PlayerRiptideEvent event = new org.bukkit.event.player.PlayerRiptideEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack)); + event.getPlayer().getServer().getPluginManager().callEvent(event); + // CraftBukkit end + float f = entityhuman.yaw; + float f1 = entityhuman.pitch; + float f2 = -MathHelper.sin(f * 0.017453292F) * MathHelper.cos(f1 * 0.017453292F); + float f3 = -MathHelper.sin(f1 * 0.017453292F); + float f4 = MathHelper.cos(f * 0.017453292F) * MathHelper.cos(f1 * 0.017453292F); + float f5 = MathHelper.c(f2 * f2 + f3 * f3 + f4 * f4); + float f6 = 3.0F * ((1.0F + (float) k) / 4.0F); + + f2 *= f6 / f5; + f3 *= f6 / f5; + f4 *= f6 / f5; + entityhuman.f((double) f2, (double) f3, (double) f4); + if (k >= 3) { + soundeffect = SoundEffects.ITEM_TRIDENT_RIPTIDE_3; + } else if (k == 2) { + soundeffect = SoundEffects.ITEM_TRIDENT_RIPTIDE_2; + } else { + soundeffect = SoundEffects.ITEM_TRIDENT_RIPTIDE_1; + } + + entityhuman.o(20); + if (entityhuman.onGround) { + float f7 = 1.1999999F; + + entityhuman.move(EnumMoveType.SELF, 0.0D, 1.1999999284744263D, 0.0D); + } + } + + world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, soundeffect, SoundCategory.PLAYERS, 1.0F, 1.0F); + } + } + } + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (itemstack.getDamage() >= itemstack.h()) { + return new InteractionResultWrapper<>(EnumInteractionResult.FAIL, itemstack); + } else if (EnchantmentManager.g(itemstack) > 0 && !entityhuman.ao()) { + return new InteractionResultWrapper<>(EnumInteractionResult.FAIL, itemstack); + } else { + entityhuman.c(enumhand); + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, itemstack); + } + } + + public boolean a(ItemStack itemstack, EntityLiving entityliving, EntityLiving entityliving1) { + itemstack.damage(1, entityliving1); + return true; + } + + public boolean a(ItemStack itemstack, World world, IBlockData iblockdata, BlockPosition blockposition, EntityLiving entityliving) { + if ((double) iblockdata.e(world, blockposition) != 0.0D) { + itemstack.damage(2, entityliving); + } + + return true; + } + + public Multimap a(EnumItemSlot enumitemslot) { + Multimap multimap = super.a(enumitemslot); + + if (enumitemslot == EnumItemSlot.MAINHAND) { + multimap.put(GenericAttributes.ATTACK_DAMAGE.getName(), new AttributeModifier(ItemTrident.g, "Tool modifier", 8.0D, 0)); + multimap.put(GenericAttributes.g.getName(), new AttributeModifier(ItemTrident.h, "Tool modifier", -2.9000000953674316D, 0)); + } + + return multimap; + } + + public int c() { + return 1; + } +} diff --git a/src/main/java/net/minecraft/server/ItemWaterLily.java b/src/main/java/net/minecraft/server/ItemWaterLily.java new file mode 100644 index 000000000000..fdeb5411d312 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemWaterLily.java @@ -0,0 +1,59 @@ +package net.minecraft.server; + +public class ItemWaterLily extends ItemBlock { + + public ItemWaterLily(Block block, Item.Info item_info) { + super(block, item_info); + } + + public EnumInteractionResult a(ItemActionContext itemactioncontext) { + return EnumInteractionResult.PASS; + } + + public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + MovingObjectPosition movingobjectposition = this.a(world, entityhuman, true); + + if (movingobjectposition == null) { + return new InteractionResultWrapper<>(EnumInteractionResult.PASS, itemstack); + } else { + if (movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.BLOCK) { + BlockPosition blockposition = movingobjectposition.getBlockPosition(); + + if (!world.a(entityhuman, blockposition) || !entityhuman.a(blockposition.shift(movingobjectposition.direction), movingobjectposition.direction, itemstack)) { + return new InteractionResultWrapper<>(EnumInteractionResult.FAIL, itemstack); + } + + BlockPosition blockposition1 = blockposition.up(); + IBlockData iblockdata = world.getType(blockposition); + Material material = iblockdata.getMaterial(); + Fluid fluid = world.getFluid(blockposition); + + if ((fluid.c() == FluidTypes.WATER || material == Material.ICE) && world.isEmpty(blockposition1)) { + // CraftBukkit start - special case for handling block placement with water lilies + org.bukkit.block.BlockState blockstate = org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(world, blockposition1); + world.setTypeAndData(blockposition1, Blocks.LILY_PAD.getBlockData(), 11); + org.bukkit.event.block.BlockPlaceEvent placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(world, entityhuman, enumhand, blockstate, blockposition.getX(), blockposition.getY(), blockposition.getZ()); + if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) { + blockstate.update(true, false); + return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack); + } + // CraftBukkit end + if (entityhuman instanceof EntityPlayer) { + CriterionTriggers.y.a((EntityPlayer) entityhuman, blockposition1, itemstack); + } + + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + } + + entityhuman.b(StatisticList.ITEM_USED.b(this)); + world.a(entityhuman, blockposition, SoundEffects.BLOCK_LILY_PAD_PLACE, SoundCategory.BLOCKS, 1.0F, 1.0F); + return new InteractionResultWrapper<>(EnumInteractionResult.SUCCESS, itemstack); + } + } + + return new InteractionResultWrapper<>(EnumInteractionResult.FAIL, itemstack); + } + } +} diff --git a/src/main/java/net/minecraft/server/ItemWorldMap.java b/src/main/java/net/minecraft/server/ItemWorldMap.java new file mode 100644 index 000000000000..c0540ca9b375 --- /dev/null +++ b/src/main/java/net/minecraft/server/ItemWorldMap.java @@ -0,0 +1,366 @@ +package net.minecraft.server; + +import com.google.common.collect.Iterables; +import com.google.common.collect.LinkedHashMultiset; +import com.google.common.collect.Multiset; +import com.google.common.collect.Multisets; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.Bukkit; +import org.bukkit.event.server.MapInitializeEvent; +// CraftBukkit end + +public class ItemWorldMap extends ItemWorldMapBase { + + public ItemWorldMap(Item.Info item_info) { + super(item_info); + } + + public static ItemStack createFilledMapView(World world, int i, int j, byte b0, boolean flag, boolean flag1) { + ItemStack itemstack = new ItemStack(Items.FILLED_MAP); + + a(itemstack, world, i, j, b0, flag, flag1, ((WorldServer) world).dimension); // CraftBukkit - fixes Bukkit multiworld maps + return itemstack; + } + + @Nullable + public static WorldMap getSavedMap(ItemStack itemstack, World world) { + WorldMap worldmap = a((GeneratorAccess) world, "map_" + e(itemstack)); + + if (worldmap == null && !world.isClientSide) { + worldmap = a(itemstack, world, world.getWorldData().b(), world.getWorldData().d(), 3, false, false, ((WorldServer) world).dimension); // CraftBukkit - fixes Bukkit multiworld maps + } + + return worldmap; + } + + public static int e(ItemStack itemstack) { + NBTTagCompound nbttagcompound = itemstack.getTag(); + + return nbttagcompound != null && nbttagcompound.hasKeyOfType("map", 99) ? nbttagcompound.getInt("map") : -1; // CraftBukkit - make new maps for no tag + } + + private static WorldMap a(ItemStack itemstack, World world, int i, int j, int k, boolean flag, boolean flag1, DimensionManager dimensionmanager) { + World worldMain = world.getServer().getServer().getWorldServer(DimensionManager.OVERWORLD); // CraftBukkit - store reference to primary world + int l = worldMain.a(DimensionManager.OVERWORLD, "map"); // CraftBukkit - use primary world for maps + WorldMap worldmap = new WorldMap("map_" + l); + + worldmap.a(i, j, k, flag, flag1, dimensionmanager); + worldMain.a(DimensionManager.OVERWORLD, worldmap.getId(), (PersistentBase) worldmap); // CraftBukkit - use primary world for maps + itemstack.getOrCreateTag().setInt("map", l); + + // CraftBukkit start + MapInitializeEvent event = new MapInitializeEvent(worldmap.mapView); + Bukkit.getServer().getPluginManager().callEvent(event); + // CraftBukkit end + return worldmap; + } + + @Nullable + public static WorldMap a(GeneratorAccess generatoraccess, String s) { + // CraftBukkit start - use primary world for maps and call event + WorldMap worldmap = (WorldMap) MinecraftServer.getServer().getWorldServer(DimensionManager.OVERWORLD).a(DimensionManager.OVERWORLD, (id) -> { + // We only get here when the data file exists, but is not a valid map + WorldMap newMap = new WorldMap(id); + MapInitializeEvent event = new MapInitializeEvent(newMap.mapView); + Bukkit.getServer().getPluginManager().callEvent(event); + return newMap; + }, s); + return worldmap; + // CraftBukkit end + } + + public void a(World world, Entity entity, WorldMap worldmap) { + // CraftBukkit - world.worldProvider -> ((WorldServer) world) + if (((WorldServer) world).dimension == worldmap.map && entity instanceof EntityHuman) { + int i = 1 << worldmap.scale; + int j = worldmap.centerX; + int k = worldmap.centerZ; + int l = MathHelper.floor(entity.locX - (double) j) / i + 64; + int i1 = MathHelper.floor(entity.locZ - (double) k) / i + 64; + int j1 = 128 / i; + + if (world.worldProvider.h()) { + j1 /= 2; + } + + WorldMap.WorldMapHumanTracker worldmap_worldmaphumantracker = worldmap.a((EntityHuman) entity); + + ++worldmap_worldmaphumantracker.b; + boolean flag = false; + + for (int k1 = l - j1 + 1; k1 < l + j1; ++k1) { + if ((k1 & 15) == (worldmap_worldmaphumantracker.b & 15) || flag) { + flag = false; + double d0 = 0.0D; + + for (int l1 = i1 - j1 - 1; l1 < i1 + j1; ++l1) { + if (k1 >= 0 && l1 >= -1 && k1 < 128 && l1 < 128) { + int i2 = k1 - l; + int j2 = l1 - i1; + boolean flag1 = i2 * i2 + j2 * j2 > (j1 - 2) * (j1 - 2); + int k2 = (j / i + k1 - 64) * i; + int l2 = (k / i + l1 - 64) * i; + Multiset multiset = LinkedHashMultiset.create(); + Chunk chunk = world.getChunkAtWorldCoords(new BlockPosition(k2, 0, l2)); + + if (!chunk.isEmpty()) { + int i3 = k2 & 15; + int j3 = l2 & 15; + int k3 = 0; + double d1 = 0.0D; + + if (world.worldProvider.h()) { + int l3 = k2 + l2 * 231871; + + l3 = l3 * l3 * 31287121 + l3 * 11; + if ((l3 >> 20 & 1) == 0) { + multiset.add(Blocks.DIRT.getBlockData().d(world, BlockPosition.ZERO), 10); + } else { + multiset.add(Blocks.STONE.getBlockData().d(world, BlockPosition.ZERO), 100); + } + + d1 = 100.0D; + } else { + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + for (int i4 = 0; i4 < i; ++i4) { + for (int j4 = 0; j4 < i; ++j4) { + int k4 = chunk.a(HeightMap.Type.WORLD_SURFACE, i4 + i3, j4 + j3) + 1; + IBlockData iblockdata; + + if (k4 > 1) { + do { + --k4; + iblockdata = chunk.getBlockData(i4 + i3, k4, j4 + j3); + blockposition_mutableblockposition.c((chunk.locX << 4) + i4 + i3, k4, (chunk.locZ << 4) + j4 + j3); + } while (iblockdata.d(world, blockposition_mutableblockposition) == MaterialMapColor.b && k4 > 0); + + if (k4 > 0 && !iblockdata.s().e()) { + int l4 = k4 - 1; + + IBlockData iblockdata1; + + do { + iblockdata1 = chunk.getBlockData(i4 + i3, l4--, j4 + j3); + ++k3; + } while (l4 > 0 && !iblockdata1.s().e()); + + iblockdata = this.a(world, iblockdata, (BlockPosition) blockposition_mutableblockposition); + } + } else { + iblockdata = Blocks.BEDROCK.getBlockData(); + } + + worldmap.a(world, (chunk.locX << 4) + i4 + i3, (chunk.locZ << 4) + j4 + j3); + d1 += (double) k4 / (double) (i * i); + multiset.add(iblockdata.d(world, blockposition_mutableblockposition)); + } + } + } + + k3 /= i * i; + double d2 = (d1 - d0) * 4.0D / (double) (i + 4) + ((double) (k1 + l1 & 1) - 0.5D) * 0.4D; + byte b0 = 1; + + if (d2 > 0.6D) { + b0 = 2; + } + + if (d2 < -0.6D) { + b0 = 0; + } + + MaterialMapColor materialmapcolor = (MaterialMapColor) Iterables.getFirst(Multisets.copyHighestCountFirst(multiset), MaterialMapColor.b); + + if (materialmapcolor == MaterialMapColor.n) { + d2 = (double) k3 * 0.1D + (double) (k1 + l1 & 1) * 0.2D; + b0 = 1; + if (d2 < 0.5D) { + b0 = 2; + } + + if (d2 > 0.9D) { + b0 = 0; + } + } + + d0 = d1; + if (l1 >= 0 && i2 * i2 + j2 * j2 < j1 * j1 && (!flag1 || (k1 + l1 & 1) != 0)) { + byte b1 = worldmap.colors[k1 + l1 * 128]; + byte b2 = (byte) (materialmapcolor.ac * 4 + b0); + + if (b1 != b2) { + worldmap.colors[k1 + l1 * 128] = b2; + worldmap.flagDirty(k1, l1); + flag = true; + } + } + } + } + } + } + } + + } + } + + private IBlockData a(World world, IBlockData iblockdata, BlockPosition blockposition) { + Fluid fluid = iblockdata.s(); + + return !fluid.e() && !Block.a(iblockdata.getCollisionShape(world, blockposition), EnumDirection.UP) ? fluid.i() : iblockdata; + } + + private static boolean a(BiomeBase[] abiomebase, int i, int j, int k) { + return abiomebase[j * i + k * i * 128 * i].h() >= 0.0F; + } + + public static void applySepiaFilter(World world, ItemStack itemstack) { + WorldMap worldmap = getSavedMap(itemstack, world); + + if (worldmap != null) { + // CraftBukkit - world.worldProvider -> ((WorldServer) world) + if (((WorldServer) world).dimension == worldmap.map) { + int i = 1 << worldmap.scale; + int j = worldmap.centerX; + int k = worldmap.centerZ; + BiomeBase[] abiomebase = world.getChunkProvider().getChunkGenerator().getWorldChunkManager().a((j / i - 64) * i, (k / i - 64) * i, 128 * i, 128 * i, false); + + for (int l = 0; l < 128; ++l) { + for (int i1 = 0; i1 < 128; ++i1) { + if (l > 0 && i1 > 0 && l < 127 && i1 < 127) { + BiomeBase biomebase = abiomebase[l * i + i1 * i * 128 * i]; + int j1 = 8; + + if (a(abiomebase, i, l - 1, i1 - 1)) { + --j1; + } + + if (a(abiomebase, i, l - 1, i1 + 1)) { + --j1; + } + + if (a(abiomebase, i, l - 1, i1)) { + --j1; + } + + if (a(abiomebase, i, l + 1, i1 - 1)) { + --j1; + } + + if (a(abiomebase, i, l + 1, i1 + 1)) { + --j1; + } + + if (a(abiomebase, i, l + 1, i1)) { + --j1; + } + + if (a(abiomebase, i, l, i1 - 1)) { + --j1; + } + + if (a(abiomebase, i, l, i1 + 1)) { + --j1; + } + + int k1 = 3; + MaterialMapColor materialmapcolor = MaterialMapColor.b; + + if (biomebase.h() < 0.0F) { + materialmapcolor = MaterialMapColor.q; + if (j1 > 7 && i1 % 2 == 0) { + k1 = (l + (int) (MathHelper.sin((float) i1 + 0.0F) * 7.0F)) / 8 % 5; + if (k1 == 3) { + k1 = 1; + } else if (k1 == 4) { + k1 = 0; + } + } else if (j1 > 7) { + materialmapcolor = MaterialMapColor.b; + } else if (j1 > 5) { + k1 = 1; + } else if (j1 > 3) { + k1 = 0; + } else if (j1 > 1) { + k1 = 0; + } + } else if (j1 > 0) { + materialmapcolor = MaterialMapColor.B; + if (j1 > 3) { + k1 = 1; + } else { + k1 = 3; + } + } + + if (materialmapcolor != MaterialMapColor.b) { + worldmap.colors[l + i1 * 128] = (byte) (materialmapcolor.ac * 4 + k1); + worldmap.flagDirty(l, i1); + } + } + } + } + + } + } + } + + public void a(ItemStack itemstack, World world, Entity entity, int i, boolean flag) { + if (!world.isClientSide) { + WorldMap worldmap = getSavedMap(itemstack, world); + + if (entity instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) entity; + + worldmap.a(entityhuman, itemstack); + } + + if (flag || entity instanceof EntityHuman && ((EntityHuman) entity).getItemInOffHand() == itemstack) { + this.a(world, entity, worldmap); + } + + } + } + + @Nullable + public Packet a(ItemStack itemstack, World world, EntityHuman entityhuman) { + return getSavedMap(itemstack, world).a(itemstack, world, entityhuman); + } + + public void b(ItemStack itemstack, World world, EntityHuman entityhuman) { + NBTTagCompound nbttagcompound = itemstack.getTag(); + + if (nbttagcompound != null && nbttagcompound.hasKeyOfType("map_scale_direction", 99)) { + a(itemstack, world, nbttagcompound.getInt("map_scale_direction")); + nbttagcompound.remove("map_scale_direction"); + } + + } + + protected static void a(ItemStack itemstack, World world, int i) { + WorldMap worldmap = getSavedMap(itemstack, world); + + if (worldmap != null) { + a(itemstack, world, worldmap.centerX, worldmap.centerZ, MathHelper.clamp(worldmap.scale + i, 0, 4), worldmap.track, worldmap.unlimitedTracking, worldmap.map); + } + + } + + public EnumInteractionResult a(ItemActionContext itemactioncontext) { + IBlockData iblockdata = itemactioncontext.getWorld().getType(itemactioncontext.getClickPosition()); + + if (iblockdata.a(TagsBlock.BANNERS)) { + if (!itemactioncontext.g.isClientSide) { + WorldMap worldmap = getSavedMap(itemactioncontext.getItemStack(), itemactioncontext.getWorld()); + + worldmap.a((GeneratorAccess) itemactioncontext.getWorld(), itemactioncontext.getClickPosition()); + } + + return EnumInteractionResult.SUCCESS; + } else { + return super.a(itemactioncontext); + } + } +} diff --git a/src/main/java/net/minecraft/server/JsonList.java b/src/main/java/net/minecraft/server/JsonList.java new file mode 100644 index 000000000000..949039f89a88 --- /dev/null +++ b/src/main/java/net/minecraft/server/JsonList.java @@ -0,0 +1,241 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.io.Files; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.Reader; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; +import org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class JsonList> { + + protected static final Logger a = LogManager.getLogger(); + protected final Gson b; + private final File c; + // Paper - replace HashMap is ConcurrentHashMap + private final Map d = Maps.newConcurrentMap(); private final Map getBackingMap() { return this.d; } // Paper - OBFHELPER + private boolean e = true; + private static final ParameterizedType f = new ParameterizedType() { + public Type[] getActualTypeArguments() { + return new Type[] { JsonListEntry.class}; + } + + public Type getRawType() { + return List.class; + } + + public Type getOwnerType() { + return null; + } + }; + + public JsonList(File file) { + this.c = file; + GsonBuilder gsonbuilder = (new GsonBuilder()).setPrettyPrinting(); + + gsonbuilder.registerTypeHierarchyAdapter(JsonListEntry.class, new JsonList.JsonListEntrySerializer()); + this.b = gsonbuilder.create(); + } + + public boolean isEnabled() { + return this.e; + } + + public void setEnabled(boolean flag) { a(flag); } // Paper - OBFHeLPER + public void a(boolean flag) { + this.e = flag; + } + + public File c() { + return this.c; + } + + public void add(V v0) { + this.d.put(this.a(v0.getKey()), v0); + + try { + this.save(); + } catch (IOException ioexception) { + JsonList.a.warn("Could not save the list after adding a user.", ioexception); + } + + } + + @Nullable + public V get(K k0) { + // Paper start + // this.h(); + // return (V) this.d.get(this.a(k0)); // CraftBukkit - fix decompile error + return (V) this.getBackingMap().computeIfPresent(this.getMappingKey(k0), (k, v) -> { + return v.hasExpired() ? null : v; + }); + // Paper end + } + + public void remove(K k0) { + this.d.remove(this.a(k0)); + + try { + this.save(); + } catch (IOException ioexception) { + JsonList.a.warn("Could not save the list after removing a user.", ioexception); + } + + } + + public void b(JsonListEntry jsonlistentry) { + this.remove(jsonlistentry.getKey()); + } + + public String[] getEntries() { + return (String[]) this.d.keySet().toArray(new String[this.d.size()]); + } + + // CraftBukkit start + public Collection getValues() { + return this.d.values(); + } + // CraftBukkit end + + public boolean isEmpty() { + // return this.d.size() < 1; // Paper + return this.getBackingMap().isEmpty(); // Paper - readability is the goal. As an aside, isEmpty() uses only sumCount() and a comparison. size() uses sumCount(), casts, and boolean logic + } + + protected final String getMappingKey(K k0) { return a(k0); } // Paper - OBFHELPER + protected String a(K k0) { + return k0.toString(); + } + + protected boolean d(K k0) { + return this.d.containsKey(this.a(k0)); + } + + private void removeStaleEntries() { h(); } // Paper - OBFHELPER + private void h() { + /*List list = Lists.newArrayList(); + Iterator iterator = this.d.values().iterator(); + + while (iterator.hasNext()) { + V v0 = (V) iterator.next(); // CraftBukkit - decompile error + + if (v0.hasExpired()) { + list.add(v0.getKey()); + } + } + + iterator = list.iterator(); + + while (iterator.hasNext()) { + K k0 = (K) iterator.next(); // CraftBukkit - decompile error + + this.d.remove(this.a(k0)); + }*/ + + this.getBackingMap().values().removeIf(JsonListEntry::hasExpired); + // Paper end + } + + protected JsonListEntry a(JsonObject jsonobject) { + return new JsonListEntry<>((K) null, jsonobject); // CraftBukkit - decompile error + } + + public Collection e() { + return this.d.values(); + } + + public void save() throws IOException { + this.removeStaleEntries(); // Paper - remove expired values before saving + Collection collection = this.d.values(); + String s = this.b.toJson(collection); + BufferedWriter bufferedwriter = null; + + try { + bufferedwriter = Files.newWriter(this.c, StandardCharsets.UTF_8); + bufferedwriter.write(s); + } finally { + IOUtils.closeQuietly(bufferedwriter); + } + + } + + public void load() throws FileNotFoundException { + if (this.c.exists()) { + BufferedReader bufferedreader = null; + + try { + bufferedreader = Files.newReader(this.c, StandardCharsets.UTF_8); + Collection> collection = (Collection) ChatDeserializer.a(this.b, (Reader) bufferedreader, (Type) JsonList.f); + + if (collection != null) { + this.d.clear(); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + JsonListEntry jsonlistentry = (JsonListEntry) iterator.next(); + + if (jsonlistentry.getKey() != null) { + this.d.put(this.a((K) jsonlistentry.getKey()), (V) jsonlistentry); // CraftBukkit - fix decompile error + } + } + } + // Spigot Start + } catch ( com.google.gson.JsonParseException ex ) + { + org.bukkit.Bukkit.getLogger().log( java.util.logging.Level.WARNING, "Unable to read file " + this.c + ", backing it up to {0}.backup and creating new copy.", ex ); + File backup = new File( this.c + ".backup" ); + this.c.renameTo( backup ); + this.c.delete(); + // Spigot End + } finally { + IOUtils.closeQuietly(bufferedreader); + } + + } + } + + class JsonListEntrySerializer implements JsonDeserializer>, JsonSerializer> { + + private JsonListEntrySerializer() {} + + public JsonElement serialize(JsonListEntry jsonlistentry, Type type, JsonSerializationContext jsonserializationcontext) { + JsonObject jsonobject = new JsonObject(); + + jsonlistentry.a(jsonobject); + return jsonobject; + } + + public JsonListEntry deserialize(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws JsonParseException { + if (jsonelement.isJsonObject()) { + JsonObject jsonobject = jsonelement.getAsJsonObject(); + + return JsonList.this.a(jsonobject); + } else { + return null; + } + } + } +} diff --git a/src/main/java/net/minecraft/server/KeyedObject.java b/src/main/java/net/minecraft/server/KeyedObject.java new file mode 100644 index 000000000000..743142d0303f --- /dev/null +++ b/src/main/java/net/minecraft/server/KeyedObject.java @@ -0,0 +1,9 @@ +package net.minecraft.server; + +public interface KeyedObject { + MinecraftKey getMinecraftKey(); + default String getMinecraftKeyString() { + MinecraftKey key = getMinecraftKey(); + return key != null ? key.toString() : null; + } +} diff --git a/src/main/java/net/minecraft/server/LegacyPingHandler.java b/src/main/java/net/minecraft/server/LegacyPingHandler.java new file mode 100644 index 000000000000..2a30af7defd0 --- /dev/null +++ b/src/main/java/net/minecraft/server/LegacyPingHandler.java @@ -0,0 +1,241 @@ +package net.minecraft.server; + +import com.destroystokyo.paper.network.PaperLegacyStatusClient; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class LegacyPingHandler extends ChannelInboundHandlerAdapter { + + private static final Logger a = LogManager.getLogger(); + private final ServerConnection b; + private ByteBuf buf; // Paper + + public LegacyPingHandler(ServerConnection serverconnection) { + this.b = serverconnection; + } + + public void channelRead(ChannelHandlerContext channelhandlercontext, Object object) throws Exception { + ByteBuf bytebuf = (ByteBuf) object; + + // Paper start - Make legacy ping handler more reliable + if (this.buf != null) { + try { + readLegacy1_6(channelhandlercontext, bytebuf); + } finally { + bytebuf.release(); + } + return; + } + // Paper end + bytebuf.markReaderIndex(); + boolean flag = true; + + try { + if (bytebuf.readUnsignedByte() != 254) { + return; + } + + InetSocketAddress inetsocketaddress = (InetSocketAddress) channelhandlercontext.channel().remoteAddress(); + MinecraftServer minecraftserver = this.b.d(); + int i = bytebuf.readableBytes(); + String s; + //org.bukkit.event.server.ServerListPingEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callServerListPingEvent(minecraftserver.server, inetsocketaddress.getAddress(), minecraftserver.getMotd(), minecraftserver.getPlayerCount(), minecraftserver.getMaxPlayers()); // CraftBukkit // Paper + com.destroystokyo.paper.event.server.PaperServerListPingEvent event; // Paper + + switch (i) { + case 0: + LegacyPingHandler.a.debug("Ping: (<1.3.x) from {}:{}", inetsocketaddress.getAddress(), inetsocketaddress.getPort()); + // Paper start - Call PaperServerListPingEvent and use results + event = PaperLegacyStatusClient.processRequest(minecraftserver, inetsocketaddress, 39, null); + if (event == null) { + channelhandlercontext.close(); + break; + } + s = String.format("%s\u00a7%d\u00a7%d", PaperLegacyStatusClient.getUnformattedMotd(event), event.getNumPlayers(), event.getMaxPlayers()); + this.a(channelhandlercontext, this.a(s)); + break; + case 1: + if (bytebuf.readUnsignedByte() != 1) { + return; + } + + LegacyPingHandler.a.debug("Ping: (1.4-1.5.x) from {}:{}", inetsocketaddress.getAddress(), inetsocketaddress.getPort()); + // Paper start - Call PaperServerListPingEvent and use results + event = PaperLegacyStatusClient.processRequest(minecraftserver, inetsocketaddress, 127, null); // Paper + if (event == null) { + channelhandlercontext.close(); + break; + } + s = String.format("\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", new Object[] { event.getProtocolVersion(), minecraftserver.getVersion(), event.getMotd(), event.getNumPlayers(), event.getMaxPlayers()}); // CraftBukkit + // Paper end + this.a(channelhandlercontext, this.a(s)); + break; + default: + // Paper start - Replace with improved version below + if (bytebuf.readUnsignedByte() != 0x01 || bytebuf.readUnsignedByte() != 0xFA) return; + readLegacy1_6(channelhandlercontext, bytebuf); + /* + boolean flag1 = bytebuf.readUnsignedByte() == 1; + + flag1 &= bytebuf.readUnsignedByte() == 250; + flag1 &= "MC|PingHost".equals(new String(bytebuf.readBytes(bytebuf.readShort() * 2).array(), StandardCharsets.UTF_16BE)); + int j = bytebuf.readUnsignedShort(); + + flag1 &= bytebuf.readUnsignedByte() >= 73; + flag1 &= 3 + bytebuf.readBytes(bytebuf.readShort() * 2).array().length + 4 == j; + flag1 &= bytebuf.readInt() <= 65535; + flag1 &= bytebuf.readableBytes() == 0; + if (!flag1) { + return; + } + + LegacyPingHandler.a.debug("Ping: (1.6) from {}:{}", inetsocketaddress.getAddress(), inetsocketaddress.getPort()); + String s1 = String.format("\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", 127, minecraftserver.getVersion(), event.getMotd(), event.getNumPlayers(), event.getMaxPlayers()); // CraftBukkit + ByteBuf bytebuf1 = this.a(s1); + + try { + this.a(channelhandlercontext, bytebuf1); + } finally { + bytebuf1.release(); + } + */ // Paper end - Replace with improved version below + } + + bytebuf.release(); + flag = false; + } catch (RuntimeException runtimeexception) { + ; + } finally { + if (flag) { + bytebuf.resetReaderIndex(); + channelhandlercontext.channel().pipeline().remove("legacy_query"); + channelhandlercontext.fireChannelRead(object); + } + + } + + } + + // Paper start + private static String readLegacyString(ByteBuf buf) { + int size = buf.readShort() * Character.BYTES; + if (!buf.isReadable(size)) { + return null; + } + + String result = buf.toString(buf.readerIndex(), size, StandardCharsets.UTF_16BE); + buf.skipBytes(size); // toString doesn't increase readerIndex automatically + return result; + } + + private void readLegacy1_6(ChannelHandlerContext ctx, ByteBuf part) { + ByteBuf buf = this.buf; + + if (buf == null) { + this.buf = buf = ctx.alloc().buffer(); + buf.markReaderIndex(); + } else { + buf.resetReaderIndex(); + } + + buf.writeBytes(part); + + if (!buf.isReadable(Short.BYTES + Short.BYTES + Byte.BYTES + Short.BYTES + Integer.BYTES)) { + return; + } + + String s = readLegacyString(buf); + if (s == null) { + return; + } + + if (!s.equals("MC|PingHost")) { + removeHandler(ctx); + return; + } + + if (!buf.isReadable(Short.BYTES) || !buf.isReadable(buf.readShort())) { + return; + } + + MinecraftServer server = this.b.d(); + int protocolVersion = buf.readByte(); + String host = readLegacyString(buf); + if (host == null) { + removeHandler(ctx); + return; + } + int port = buf.readInt(); + + if (buf.isReadable()) { + removeHandler(ctx); + return; + } + + buf.release(); + this.buf = null; + + a.debug("Ping: (1.6) from {}", ctx.channel().remoteAddress()); + + InetSocketAddress virtualHost = com.destroystokyo.paper.network.PaperNetworkClient.prepareVirtualHost(host, port); + com.destroystokyo.paper.event.server.PaperServerListPingEvent event = PaperLegacyStatusClient.processRequest( + server, (InetSocketAddress) ctx.channel().remoteAddress(), protocolVersion, virtualHost); + if (event == null) { + ctx.close(); + return; + } + + String response = String.format("\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", event.getProtocolVersion(), event.getVersion(), + PaperLegacyStatusClient.getMotd(event), event.getNumPlayers(), event.getMaxPlayers()); + this.a(ctx, this.a(response)); + } + + private void removeHandler(ChannelHandlerContext ctx) { + ByteBuf buf = this.buf; + this.buf = null; + + buf.resetReaderIndex(); + ctx.pipeline().remove(this); + ctx.fireChannelRead(buf); + } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) { + if (this.buf != null) { + this.buf.release(); + this.buf = null; + } + } + // Paper end + + private void a(ChannelHandlerContext channelhandlercontext, ByteBuf bytebuf) { + channelhandlercontext.pipeline().firstContext().writeAndFlush(bytebuf).addListener(ChannelFutureListener.CLOSE); + } + + private ByteBuf a(String s) { + ByteBuf bytebuf = Unpooled.buffer(); + + bytebuf.writeByte(255); + char[] achar = s.toCharArray(); + + bytebuf.writeShort(achar.length); + char[] achar1 = achar; + int i = achar.length; + + for (int j = 0; j < i; ++j) { + char c0 = achar1[j]; + + bytebuf.writeChar(c0); + } + + return bytebuf; + } +} diff --git a/src/main/java/net/minecraft/server/LocaleLanguage.java b/src/main/java/net/minecraft/server/LocaleLanguage.java new file mode 100644 index 000000000000..4c5556a09619 --- /dev/null +++ b/src/main/java/net/minecraft/server/LocaleLanguage.java @@ -0,0 +1,70 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Pattern; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class LocaleLanguage { + + private static final Logger a = LogManager.getLogger(); + private static final Pattern b = Pattern.compile("%(\\d+\\$)?[\\d\\.]*[df]"); + private static final LocaleLanguage c = new LocaleLanguage(); + private final Map d = Maps.newHashMap(); + private long e; + + public LocaleLanguage() { + try { + InputStream inputstream = LocaleLanguage.class.getResourceAsStream("/assets/minecraft/lang/en_us.json"); + JsonElement jsonelement = (JsonElement) (new Gson()).fromJson(new InputStreamReader(inputstream, StandardCharsets.UTF_8), JsonElement.class); + JsonObject jsonobject = ChatDeserializer.m(jsonelement, "strings"); + Iterator iterator = jsonobject.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + String s = LocaleLanguage.b.matcher(ChatDeserializer.a((JsonElement) entry.getValue(), (String) entry.getKey())).replaceAll("%$1s"); + + this.d.put(entry.getKey(), s); + } + + this.e = SystemUtils.getMonotonicMillis(); + } catch (JsonParseException jsonparseexception) { + LocaleLanguage.a.error("Couldn't read strings from /assets/minecraft/lang/en_us.json", jsonparseexception); + } + + } + + public static LocaleLanguage getInstance() { return a(); } // Paper - OBFHELPER + public static LocaleLanguage a() { + return LocaleLanguage.c; + } + + public synchronized String translateKey(String key) { return a(key); } // Paper - OBFHELPER + public synchronized String a(String s) { + return this.c(s); + } + + private String c(String s) { + String s1 = (String) this.d.get(s); + + return s1 == null ? s : s1; + } + + public synchronized boolean b(String s) { + return this.d.containsKey(s); + } + + public long b() { + return this.e; + } +} diff --git a/src/main/java/net/minecraft/server/LoginListener.java b/src/main/java/net/minecraft/server/LoginListener.java new file mode 100644 index 000000000000..dfe7a029f8c1 --- /dev/null +++ b/src/main/java/net/minecraft/server/LoginListener.java @@ -0,0 +1,386 @@ +package net.minecraft.server; + +import com.destroystokyo.paper.profile.CraftPlayerProfile; +import com.destroystokyo.paper.profile.PlayerProfile; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.exceptions.AuthenticationUnavailableException; +import io.netty.channel.ChannelFuture; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.security.PrivateKey; +import java.util.Arrays; +import java.util.Random; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.Nullable; +import javax.crypto.SecretKey; +import org.apache.commons.lang3.Validate; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.util.Waitable; +import org.bukkit.event.player.AsyncPlayerPreLoginEvent; +import org.bukkit.event.player.PlayerPreLoginEvent; +// CraftBukkit end + +public class LoginListener implements PacketLoginInListener, ITickable { + + private static final AtomicInteger b = new AtomicInteger(0); + private static final Logger c = LogManager.getLogger(); + private static final Random random = new Random(); + private final byte[] e = new byte[4]; + private final MinecraftServer server; + public final NetworkManager networkManager; + private LoginListener.EnumProtocolState g; public final LoginListener.EnumProtocolState getLoginState() { return this.g; }; // Paper - OBFHELPER + private int h; + private GameProfile i; private void setGameProfile(GameProfile profile) { i = profile; } public final GameProfile getGameProfile() { return i; } // Paper - OBFHELPER + private final String j; + private SecretKey loginKey; + private EntityPlayer l; + public String hostname = ""; // CraftBukkit - add field + private int velocityLoginMessageId = -1; // Paper - Velocity support + + public LoginListener(MinecraftServer minecraftserver, NetworkManager networkmanager) { + this.g = LoginListener.EnumProtocolState.HELLO; + this.j = ""; + this.server = minecraftserver; + this.networkManager = networkmanager; + LoginListener.random.nextBytes(this.e); + } + + public void tick() { + // Paper start - Do not allow logins while the server is shutting down + if (!MinecraftServer.getServer().isRunning()) { + this.disconnect(new ChatMessage(org.spigotmc.SpigotConfig.restartMessage)); + return; + } + // Paper end + if (this.g == LoginListener.EnumProtocolState.READY_TO_ACCEPT) { + // Paper start - prevent logins to be processed even though disconnect was called + if (networkManager.isConnected()) { + this.b(); + } + // Paper end + } else if (this.g == LoginListener.EnumProtocolState.DELAY_ACCEPT) { + EntityPlayer entityplayer = this.server.getPlayerList().a(this.i.getId()); + + if (entityplayer == null) { + this.g = LoginListener.EnumProtocolState.READY_TO_ACCEPT; + this.server.getPlayerList().a(this.networkManager, this.l); + this.l = null; + } + } + + if (this.h++ == 600) { + this.disconnect(new ChatMessage("multiplayer.disconnect.slow_login", new Object[0])); + } + + } + + // CraftBukkit start + @Deprecated + public void disconnect(String s) { + try { + IChatBaseComponent ichatbasecomponent = new ChatComponentText(s); + LoginListener.c.info("Disconnecting {}: {}", this.c(), s); + this.networkManager.sendPacket(new PacketLoginOutDisconnect(ichatbasecomponent)); + this.networkManager.close(ichatbasecomponent); + } catch (Exception exception) { + LoginListener.c.error("Error whilst disconnecting player", exception); + } + } + // CraftBukkit end + + public void disconnect(IChatBaseComponent ichatbasecomponent) { + try { + LoginListener.c.info("Disconnecting {}: {}", this.c(), ichatbasecomponent.getString()); + this.networkManager.sendPacket(new PacketLoginOutDisconnect(ichatbasecomponent)); + this.networkManager.close(ichatbasecomponent); + } catch (Exception exception) { + LoginListener.c.error("Error whilst disconnecting player", exception); + } + + } + + // Paper start - Cache authenticator threads + private static final AtomicInteger threadId = new AtomicInteger(0); + private static final java.util.concurrent.ExecutorService authenticatorPool = java.util.concurrent.Executors.newCachedThreadPool( + r -> new Thread(r, "User Authenticator #" + threadId.incrementAndGet()) + ); + // Paper end + // Spigot start + public void initUUID() + { + UUID uuid; + if ( networkManager.spoofedUUID != null ) + { + uuid = networkManager.spoofedUUID; + } else + { + uuid = EntityHuman.getOfflineUUID( this.i.getName() ); + } + + this.i = new GameProfile( uuid, this.i.getName() ); + + if (networkManager.spoofedProfile != null) + { + for ( com.mojang.authlib.properties.Property property : networkManager.spoofedProfile ) + { + this.i.getProperties().put( property.getName(), property ); + } + } + } + // Spigot end + + public void b() { + // Spigot start - Moved to initUUID + /* + if (!this.i.isComplete()) { + this.i = this.a(this.i); + } + */ + // Spigot end + + // CraftBukkit start - fire PlayerLoginEvent + EntityPlayer s = this.server.getPlayerList().attemptLogin(this, this.i, hostname); + + if (s == null) { + // this.disconnect(ichatbasecomponent); + // CraftBukkit end + } else { + this.g = LoginListener.EnumProtocolState.ACCEPTED; + if (this.server.aw() >= 0 && !this.networkManager.isLocal()) { + this.networkManager.sendPacket(new PacketLoginOutSetCompression(this.server.aw()), (channelfuture) -> { + this.networkManager.setCompressionLevel(this.server.aw()); + }); + } + + this.networkManager.sendPacket(new PacketLoginOutSuccess(this.i)); + EntityPlayer entityplayer = this.server.getPlayerList().a(this.i.getId()); + + if (entityplayer != null) { + this.g = LoginListener.EnumProtocolState.DELAY_ACCEPT; + this.l = this.server.getPlayerList().processLogin(this.i, s); // CraftBukkit - add player reference + } else { + this.server.getPlayerList().a(this.networkManager, this.server.getPlayerList().processLogin(this.i, s)); // CraftBukkit - add player reference + } + } + + } + + public void a(IChatBaseComponent ichatbasecomponent) { + LoginListener.c.info("{} lost connection: {}", this.c(), ichatbasecomponent.getString()); + } + + public String c() { + return this.i != null ? this.i + " (" + this.networkManager.getSocketAddress() + ")" : String.valueOf(this.networkManager.getSocketAddress()); + } + + public void a(PacketLoginInStart packetlogininstart) { + Validate.validState(this.g == LoginListener.EnumProtocolState.HELLO, "Unexpected hello packet", new Object[0]); + this.i = packetlogininstart.b(); + if (this.server.getOnlineMode() && !this.networkManager.isLocal()) { + this.g = LoginListener.EnumProtocolState.KEY; + this.networkManager.sendPacket(new PacketLoginOutEncryptionBegin("", this.server.E().getPublic(), this.e)); + } else { + // Paper start - Velocity support + if (com.destroystokyo.paper.PaperConfig.velocitySupport) { + this.velocityLoginMessageId = java.util.concurrent.ThreadLocalRandom.current().nextInt(); + PacketLoginOutCustomPayload packet = new PacketLoginOutCustomPayload(this.velocityLoginMessageId, com.destroystokyo.paper.proxy.VelocityProxy.PLAYER_INFO_CHANNEL, new PacketDataSerializer(io.netty.buffer.Unpooled.EMPTY_BUFFER)); + this.networkManager.sendPacket(packet); + return; + } + // Paper end + // Spigot start + // Paper start - Cache authenticator threads + authenticatorPool.execute(new Runnable() { + @Override + public void run() { + try { + initUUID(); + new LoginHandler().fireEvents(); + } catch (Exception ex) { + disconnect("Failed to verify username!"); + server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + i.getName(), ex); + } + } + }); + // Paper end + // Spigot end + } + + } + + public void a(PacketLoginInEncryptionBegin packetlogininencryptionbegin) { + Validate.validState(this.g == LoginListener.EnumProtocolState.KEY, "Unexpected key packet", new Object[0]); + PrivateKey privatekey = this.server.E().getPrivate(); + + if (!Arrays.equals(this.e, packetlogininencryptionbegin.b(privatekey))) { + throw new IllegalStateException("Invalid nonce!"); + } else { + this.loginKey = packetlogininencryptionbegin.a(privatekey); + this.g = LoginListener.EnumProtocolState.AUTHENTICATING; + this.networkManager.a(this.loginKey); + // Paper start - Cache authenticator threads + authenticatorPool.execute(new Runnable() { + public void run() { + GameProfile gameprofile = LoginListener.this.i; + + try { + String s = (new BigInteger(MinecraftEncryption.a("", LoginListener.this.server.E().getPublic(), LoginListener.this.loginKey))).toString(16); + + LoginListener.this.i = LoginListener.this.server.ap().hasJoinedServer(new GameProfile((UUID) null, gameprofile.getName()), s, this.a()); + if (LoginListener.this.i != null) { + // CraftBukkit start - fire PlayerPreLoginEvent + if (!networkManager.isConnected()) { + return; + } + + new LoginHandler().fireEvents(); + } else if (LoginListener.this.server.H()) { + LoginListener.c.warn("Failed to verify username but will let them in anyway!"); + LoginListener.this.i = LoginListener.this.a(gameprofile); + LoginListener.this.g = LoginListener.EnumProtocolState.READY_TO_ACCEPT; + } else { + LoginListener.this.disconnect(new ChatMessage("multiplayer.disconnect.unverified_username", new Object[0])); + LoginListener.c.error("Username '{}' tried to join with an invalid session", gameprofile.getName()); + } + } catch (AuthenticationUnavailableException authenticationunavailableexception) { + if (LoginListener.this.server.H()) { + LoginListener.c.warn("Authentication servers are down but will let them in anyway!"); + LoginListener.this.i = LoginListener.this.a(gameprofile); + LoginListener.this.g = LoginListener.EnumProtocolState.READY_TO_ACCEPT; + } else { + // Paper start + if (com.destroystokyo.paper.PaperConfig.authenticationServersDownKickMessage != null) { + LoginListener.this.disconnect(new ChatComponentText(com.destroystokyo.paper.PaperConfig.authenticationServersDownKickMessage)); + } else // Paper end + LoginListener.this.disconnect(new ChatMessage("multiplayer.disconnect.authservers_down", new Object[0])); + LoginListener.c.error("Couldn't verify username because servers are unavailable"); + } + // CraftBukkit start - catch all exceptions + } catch (Exception exception) { + disconnect("Failed to verify username!"); + server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + gameprofile.getName(), exception); + // CraftBukkit end + } + + } + + @Nullable + private InetAddress a() { + SocketAddress socketaddress = LoginListener.this.networkManager.getSocketAddress(); + + return LoginListener.this.server.S() && socketaddress instanceof InetSocketAddress ? ((InetSocketAddress) socketaddress).getAddress() : null; + } + }); + // Paper end + } + } + + // Spigot start + public class LoginHandler { + + public void fireEvents() throws Exception { + // Paper start - Velocity support + if (LoginListener.this.velocityLoginMessageId == -1 && com.destroystokyo.paper.PaperConfig.velocitySupport) { + disconnect("This server requires you to connect with Velocity."); + return; + } + // Paper end + String playerName = i.getName(); + java.net.InetAddress address = ((java.net.InetSocketAddress) networkManager.getSocketAddress()).getAddress(); + java.util.UUID uniqueId = i.getId(); + final org.bukkit.craftbukkit.CraftServer server = LoginListener.this.server.server; + + // Paper start + PlayerProfile profile = CraftPlayerProfile.asBukkitMirror(getGameProfile()); + AsyncPlayerPreLoginEvent asyncEvent = new AsyncPlayerPreLoginEvent(playerName, address, uniqueId, profile); + server.getPluginManager().callEvent(asyncEvent); + profile = asyncEvent.getPlayerProfile(); + profile.complete(true); + setGameProfile(CraftPlayerProfile.asAuthlib(profile)); + playerName = i.getName(); + uniqueId = i.getId(); + // Paper end + + if (PlayerPreLoginEvent.getHandlerList().getRegisteredListeners().length != 0) { + final PlayerPreLoginEvent event = new PlayerPreLoginEvent(playerName, address, uniqueId); + if (asyncEvent.getResult() != PlayerPreLoginEvent.Result.ALLOWED) { + event.disallow(asyncEvent.getResult(), asyncEvent.getKickMessage()); + } + Waitable waitable = new Waitable() { + @Override + protected PlayerPreLoginEvent.Result evaluate() { + server.getPluginManager().callEvent(event); + return event.getResult(); + }}; + + LoginListener.this.server.processQueue.add(waitable); + if (waitable.get() != PlayerPreLoginEvent.Result.ALLOWED) { + disconnect(event.getKickMessage()); + return; + } + } else { + if (asyncEvent.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) { + disconnect(asyncEvent.getKickMessage()); + return; + } + } + // CraftBukkit end + LoginListener.c.info("UUID of player {} is {}", LoginListener.this.i.getName(), LoginListener.this.i.getId()); + LoginListener.this.g = LoginListener.EnumProtocolState.READY_TO_ACCEPT; + } + } + // Spigot end + + public void a(PacketLoginInCustomPayload packetloginincustompayload) { + // Paper start - Velocity support + if (com.destroystokyo.paper.PaperConfig.velocitySupport && packetloginincustompayload.getId() == this.velocityLoginMessageId) { + PacketDataSerializer buf = packetloginincustompayload.getBuf(); + if (buf == null) { + this.disconnect("This server requires you to connect with Velocity."); + return; + } + + if (!com.destroystokyo.paper.proxy.VelocityProxy.checkIntegrity(buf)) { + this.disconnect("Unable to verify player details"); + return; + } + + this.networkManager.setSpoofedRemoteAddress(new java.net.InetSocketAddress(com.destroystokyo.paper.proxy.VelocityProxy.readAddress(buf), ((java.net.InetSocketAddress) this.networkManager.getSocketAddress()).getPort())); + + this.setGameProfile(com.destroystokyo.paper.proxy.VelocityProxy.createProfile(buf)); + + // Proceed with login + authenticatorPool.execute(() -> { + try { + new LoginHandler().fireEvents(); + } catch (Exception ex) { + disconnect("Failed to verify username!"); + server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + i.getName(), ex); + } + }); + return; + } + // Paper end + this.disconnect(new ChatMessage("multiplayer.disconnect.unexpected_query_response", new Object[0])); + } + + protected GameProfile a(GameProfile gameprofile) { + UUID uuid = EntityHuman.getOfflineUUID(gameprofile.getName()); + + return new GameProfile(uuid, gameprofile.getName()); + } + + static enum EnumProtocolState { + + HELLO, KEY, AUTHENTICATING, NEGOTIATING, READY_TO_ACCEPT, DELAY_ACCEPT, ACCEPTED; + + private EnumProtocolState() {} + } +} diff --git a/src/main/java/net/minecraft/server/LootEnchantFunction.java b/src/main/java/net/minecraft/server/LootEnchantFunction.java new file mode 100644 index 000000000000..23028f0e3f84 --- /dev/null +++ b/src/main/java/net/minecraft/server/LootEnchantFunction.java @@ -0,0 +1,65 @@ +package net.minecraft.server; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import java.util.Random; + +public class LootEnchantFunction extends LootItemFunction { + + private final LootValueBounds a; + private final int b; + + public LootEnchantFunction(LootItemCondition[] alootitemcondition, LootValueBounds lootvaluebounds, int i) { + super(alootitemcondition); + this.a = lootvaluebounds; + this.b = i; + } + + public ItemStack a(ItemStack itemstack, Random random, LootTableInfo loottableinfo) { + Entity entity = loottableinfo.c(); + + if (entity instanceof EntityLiving) { + int i = EnchantmentManager.g((EntityLiving) entity); + // CraftBukkit start - use lootingModifier if set by plugin + if (loottableinfo.lootingMod > org.bukkit.loot.LootContext.DEFAULT_LOOT_MODIFIER) { + i = loottableinfo.lootingMod; + } + // CraftBukkit end + + if (i <= 0) { // CraftBukkit - account for possible negative looting values from Bukkit + return itemstack; + } + + float f = (float) i * this.a.b(random); + + itemstack.add(Math.round(f)); + if (this.b != 0 && itemstack.getCount() > this.b) { + itemstack.setCount(this.b); + } + } + + return itemstack; + } + + public static class a extends LootItemFunction.a { + + protected a() { + super(new MinecraftKey("looting_enchant"), LootEnchantFunction.class); + } + + public void a(JsonObject jsonobject, LootEnchantFunction lootenchantfunction, JsonSerializationContext jsonserializationcontext) { + jsonobject.add("count", jsonserializationcontext.serialize(lootenchantfunction.a)); + if (lootenchantfunction.b > 0) { + jsonobject.add("limit", jsonserializationcontext.serialize(lootenchantfunction.b)); + } + + } + + public LootEnchantFunction b(JsonObject jsonobject, JsonDeserializationContext jsondeserializationcontext, LootItemCondition[] alootitemcondition) { + int i = ChatDeserializer.a(jsonobject, "limit", 0); + + return new LootEnchantFunction(alootitemcondition, (LootValueBounds) ChatDeserializer.a(jsonobject, "count", jsondeserializationcontext, LootValueBounds.class), i); + } + } +} diff --git a/src/main/java/net/minecraft/server/LootItemConditionRandomChanceWithLooting.java b/src/main/java/net/minecraft/server/LootItemConditionRandomChanceWithLooting.java new file mode 100644 index 000000000000..5e42d668be70 --- /dev/null +++ b/src/main/java/net/minecraft/server/LootItemConditionRandomChanceWithLooting.java @@ -0,0 +1,48 @@ +package net.minecraft.server; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import java.util.Random; + +public class LootItemConditionRandomChanceWithLooting implements LootItemCondition { + + private final float a; + private final float b; + + public LootItemConditionRandomChanceWithLooting(float f, float f1) { + this.a = f; + this.b = f1; + } + + public boolean a(Random random, LootTableInfo loottableinfo) { + int i = 0; + + if (loottableinfo.c() instanceof EntityLiving) { + i = EnchantmentManager.g((EntityLiving) loottableinfo.c()); + } + // CraftBukkit start - only use lootingModifier if set by Bukkit + if (loottableinfo.lootingMod > org.bukkit.loot.LootContext.DEFAULT_LOOT_MODIFIER) { + i = loottableinfo.lootingMod; + } + // CraftBukkit end + + return random.nextFloat() < this.a + (float) i * this.b; + } + + public static class a extends LootItemCondition.a { + + protected a() { + super(new MinecraftKey("random_chance_with_looting"), LootItemConditionRandomChanceWithLooting.class); + } + + public void a(JsonObject jsonobject, LootItemConditionRandomChanceWithLooting lootitemconditionrandomchancewithlooting, JsonSerializationContext jsonserializationcontext) { + jsonobject.addProperty("chance", lootitemconditionrandomchancewithlooting.a); + jsonobject.addProperty("looting_multiplier", lootitemconditionrandomchancewithlooting.b); + } + + public LootItemConditionRandomChanceWithLooting b(JsonObject jsonobject, JsonDeserializationContext jsondeserializationcontext) { + return new LootItemConditionRandomChanceWithLooting(ChatDeserializer.l(jsonobject, "chance"), ChatDeserializer.l(jsonobject, "looting_multiplier")); + } + } +} diff --git a/src/main/java/net/minecraft/server/LootSelectorEntry.java b/src/main/java/net/minecraft/server/LootSelectorEntry.java new file mode 100644 index 000000000000..80f9f9a25209 --- /dev/null +++ b/src/main/java/net/minecraft/server/LootSelectorEntry.java @@ -0,0 +1,114 @@ +package net.minecraft.server; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.JsonSyntaxException; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.Random; + +public abstract class LootSelectorEntry { + + protected final int c; public int getWeight() { return c; } // Paper - OBFHELPER + protected final int d; public int getQuality() { return d; } // Paper - OBFHELPER + protected final LootItemCondition[] e; + + protected LootSelectorEntry(int i, int j, LootItemCondition[] alootitemcondition) { + this.c = i; + this.d = j; + this.e = alootitemcondition; + } + + public int a(float f) { + // Paper start - Offer an alternative loot formula to refactor how luck bonus applies + // SEE: https://luckformula.emc.gs for details and data + if (lastLuck != null && lastLuck == f) { + return lastWeight; + } + // This is vanilla + float qualityModifer = (float) this.getQuality() * f; + double baseWeight = (this.getWeight() + qualityModifer); + if (com.destroystokyo.paper.PaperConfig.useAlternativeLuckFormula) { + // Random boost to avoid losing precision in the final int cast on return + final int weightBoost = 100; + baseWeight *= weightBoost; + // If we have vanilla 1, bump that down to 0 so nothing is is impacted + // vanilla 3 = 300, 200 basis = impact 2% + // =($B2*(($B2-100)/100/100)) + double impacted = baseWeight * ((baseWeight - weightBoost) / weightBoost / 100); + // =($B$7/100) + float luckModifier = Math.min(100, f * 10) / 100; + // =B2 - (C2 *($B$7/100)) + baseWeight = Math.ceil(baseWeight - (impacted * luckModifier)); + } + lastLuck = f; + lastWeight = (int) Math.max(0, Math.floor(baseWeight)); + return lastWeight; + } + private Float lastLuck = null; + private int lastWeight = 0; + // Paper end + + public abstract void a(Collection collection, Random random, LootTableInfo loottableinfo); + + protected abstract void a(JsonObject jsonobject, JsonSerializationContext jsonserializationcontext); + + public static class a implements JsonDeserializer, JsonSerializer { + + public a() {} + + public LootSelectorEntry deserialize(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws JsonParseException { + JsonObject jsonobject = ChatDeserializer.m(jsonelement, "loot item"); + String s = ChatDeserializer.h(jsonobject, "type"); + int i = ChatDeserializer.a(jsonobject, "weight", 1); + int j = ChatDeserializer.a(jsonobject, "quality", 0); + LootItemCondition[] alootitemcondition; + + if (jsonobject.has("conditions")) { + alootitemcondition = (LootItemCondition[]) ChatDeserializer.a(jsonobject, "conditions", jsondeserializationcontext, LootItemCondition[].class); + } else { + alootitemcondition = new LootItemCondition[0]; + } + + if ("item".equals(s)) { + return LootItem.a(jsonobject, jsondeserializationcontext, i, j, alootitemcondition); + } else if ("loot_table".equals(s)) { + return LootSelectorLootTable.a(jsonobject, jsondeserializationcontext, i, j, alootitemcondition); + } else if ("empty".equals(s)) { + return LootSelectorEmpty.a(jsonobject, jsondeserializationcontext, i, j, alootitemcondition); + } else { + throw new JsonSyntaxException("Unknown loot entry type '" + s + "'"); + } + } + + public JsonElement serialize(LootSelectorEntry lootselectorentry, Type type, JsonSerializationContext jsonserializationcontext) { + JsonObject jsonobject = new JsonObject(); + + jsonobject.addProperty("weight", lootselectorentry.c); + jsonobject.addProperty("quality", lootselectorentry.d); + if (lootselectorentry.e.length > 0) { + jsonobject.add("conditions", jsonserializationcontext.serialize(lootselectorentry.e)); + } + + if (lootselectorentry instanceof LootItem) { + jsonobject.addProperty("type", "item"); + } else if (lootselectorentry instanceof LootSelectorLootTable) { + jsonobject.addProperty("type", "loot_table"); + } else { + if (!(lootselectorentry instanceof LootSelectorEmpty)) { + throw new IllegalArgumentException("Don't know how to serialize " + lootselectorentry); + } + + jsonobject.addProperty("type", "empty"); + } + + lootselectorentry.a(jsonobject, jsonserializationcontext); + return jsonobject; + } + } +} diff --git a/src/main/java/net/minecraft/server/LootTableInfo.java b/src/main/java/net/minecraft/server/LootTableInfo.java new file mode 100644 index 000000000000..5230a25407b0 --- /dev/null +++ b/src/main/java/net/minecraft/server/LootTableInfo.java @@ -0,0 +1,182 @@ +package net.minecraft.server; + +import com.google.common.collect.Sets; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.util.Set; +import javax.annotation.Nullable; + +public class LootTableInfo { + + private final float a; + public final int lootingMod; // CraftBukkit - add field + private final WorldServer b; + private final LootTableRegistry c; + @Nullable + private final Entity d; + @Nullable + private final EntityHuman e; + @Nullable + private final DamageSource f; + @Nullable + private final BlockPosition g; + private final Set h = Sets.newLinkedHashSet(); + + // CraftBukkit - add looting modifier to constructor + public LootTableInfo(float f, WorldServer worldserver, LootTableRegistry loottableregistry, @Nullable Entity entity, @Nullable EntityHuman entityhuman, @Nullable DamageSource damagesource, @Nullable BlockPosition blockposition, int lootingModifier) { + this.a = f; + this.b = worldserver; + this.c = loottableregistry; + this.d = entity; + this.e = entityhuman; + this.f = damagesource; + this.g = blockposition; + this.lootingMod = lootingModifier; // CraftBukkit + } + + @Nullable + public Entity a() { + return this.d; + } + + @Nullable + public Entity b() { + return this.e; + } + + @Nullable + public Entity c() { + return this.f == null ? null : this.f.getEntity(); + } + + @Nullable + public BlockPosition e() { + return this.g; + } + + public boolean a(LootTable loottable) { + return this.h.add(loottable); + } + + public void b(LootTable loottable) { + this.h.remove(loottable); + } + + public LootTableRegistry f() { + return this.c; + } + + public float g() { + return this.a; + } + + public WorldServer h() { + return this.b; + } + + @Nullable + public Entity a(LootTableInfo.EntityTarget loottableinfo_entitytarget) { + switch (loottableinfo_entitytarget) { + case THIS: + return this.a(); + case KILLER: + return this.c(); + case KILLER_PLAYER: + return this.b(); + default: + return null; + } + } + + public static enum EntityTarget { + + THIS("this"), KILLER("killer"), KILLER_PLAYER("killer_player"); + + private final String d; + + private EntityTarget(String s) { + this.d = s; + } + + public static LootTableInfo.EntityTarget a(String s) { + LootTableInfo.EntityTarget[] aloottableinfo_entitytarget = values(); + int i = aloottableinfo_entitytarget.length; + + for (int j = 0; j < i; ++j) { + LootTableInfo.EntityTarget loottableinfo_entitytarget = aloottableinfo_entitytarget[j]; + + if (loottableinfo_entitytarget.d.equals(s)) { + return loottableinfo_entitytarget; + } + } + + throw new IllegalArgumentException("Invalid entity target " + s); + } + + public static class a extends TypeAdapter { + + public a() {} + + public void write(JsonWriter jsonwriter, LootTableInfo.EntityTarget loottableinfo_entitytarget) throws IOException { + jsonwriter.value(loottableinfo_entitytarget.d); + } + + public LootTableInfo.EntityTarget read(JsonReader jsonreader) throws IOException { + return LootTableInfo.EntityTarget.a(jsonreader.nextString()); + } + } + } + + public static class Builder { + + private final WorldServer a; + private float b; + private int lootingMod = org.bukkit.loot.LootContext.DEFAULT_LOOT_MODIFIER; // CraftBukkit + private Entity c; + private EntityHuman d; + private DamageSource e; + private BlockPosition f; + + public Builder(WorldServer worldserver) { + this.a = worldserver; + } + + public LootTableInfo.Builder luck(float f) { + this.b = f; + return this; + } + + public LootTableInfo.Builder entity(Entity entity) { + this.c = entity; + return this; + } + + public LootTableInfo.Builder killer(EntityHuman entityhuman) { + this.d = entityhuman; + return this; + } + + public LootTableInfo.Builder damageSource(DamageSource damagesource) { + this.e = damagesource; + return this; + } + + public LootTableInfo.Builder position(BlockPosition blockposition) { + this.f = blockposition; + return this; + } + + // CraftBukkit start - add looting modifier + public LootTableInfo.Builder lootingModifier(int modifier) { + this.lootingMod = modifier; + return this; + } + // CraftBukkit end + + public LootTableInfo build() { + return new LootTableInfo(this.b, this.a, this.a.getMinecraftServer().getLootTableRegistry(), this.c, this.d, this.e, this.f, this.lootingMod); // CraftBukkit add looting modifier + } + } +} diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java new file mode 100644 index 000000000000..f70f5899fa4a --- /dev/null +++ b/src/main/java/net/minecraft/server/MCUtil.java @@ -0,0 +1,357 @@ +package net.minecraft.server; + +import com.destroystokyo.paper.block.TargetBlockInfo; +import com.destroystokyo.paper.profile.CraftPlayerProfile; +import com.destroystokyo.paper.profile.PlayerProfile; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.mojang.authlib.GameProfile; +import org.apache.commons.lang.exception.ExceptionUtils; +import org.bukkit.Location; +import org.bukkit.block.BlockFace; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.util.Waitable; +import org.spigotmc.AsyncCatcher; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Queue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Supplier; + +public final class MCUtil { + private static final Executor asyncExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("Paper Async Task Handler Thread - %1$d").build()); + + private MCUtil() {} + + /** + * Quickly generate a stack trace for current location + * + * @return Stacktrace + */ + public static String stack() { + return ExceptionUtils.getFullStackTrace(new Throwable()); + } + + /** + * Quickly generate a stack trace for current location with message + * + * @param str + * @return Stacktrace + */ + public static String stack(String str) { + return ExceptionUtils.getFullStackTrace(new Throwable(str)); + } + + public static boolean isMainThread() { + return MinecraftServer.getServer().isMainThread(); + } + + private static class DelayedRunnable implements Runnable { + + private final int ticks; + private final Runnable run; + + private DelayedRunnable(int ticks, Runnable run) { + this.ticks = ticks; + this.run = run; + } + + @Override + public void run() { + if (ticks <= 0) { + run.run(); + } else { + scheduleTask(ticks-1, run); + } + } + } + + public static void scheduleTask(int ticks, Runnable runnable) { + // We use post to main instead of process queue as we don't want to process these mid tick if + // Someone uses processQueueWhileWaiting + MinecraftServer.getServer().postToMainThread(new DelayedRunnable(ticks, runnable)); + } + + public static void processQueue() { + Runnable runnable; + Queue processQueue = getProcessQueue(); + while ((runnable = processQueue.poll()) != null) { + try { + runnable.run(); + } catch (Exception e) { + MinecraftServer.LOGGER.error("Error executing task", e); + } + } + } + public static T processQueueWhileWaiting(CompletableFuture future) { + try { + if (isMainThread()) { + while (!future.isDone()) { + try { + return future.get(1, TimeUnit.MILLISECONDS); + } catch (TimeoutException ignored) { + processQueue(); + } + } + } + return future.get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static void ensureMain(Runnable run) { + ensureMain(null, run); + } + /** + * Ensures the target code is running on the main thread + * @param reason + * @param run + * @return + */ + public static void ensureMain(String reason, Runnable run) { + if (AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread) { + if (reason != null) { + new IllegalStateException("Asynchronous " + reason + "!").printStackTrace(); + } + getProcessQueue().add(run); + return; + } + run.run(); + } + + private static Queue getProcessQueue() { + return MinecraftServer.getServer().processQueue; + } + + public static T ensureMain(Supplier run) { + return ensureMain(null, run); + } + /** + * Ensures the target code is running on the main thread + * @param reason + * @param run + * @param + * @return + */ + public static T ensureMain(String reason, Supplier run) { + if (AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread) { + if (reason != null) { + new IllegalStateException("Asynchronous " + reason + "! Blocking thread until it returns ").printStackTrace(); + } + Waitable wait = new Waitable() { + @Override + protected T evaluate() { + return run.get(); + } + }; + getProcessQueue().add(wait); + try { + return wait.get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + return null; + } + return run.get(); + } + + public static PlayerProfile toBukkit(GameProfile profile) { + return CraftPlayerProfile.asBukkitMirror(profile); + } + + /** + * Calculates distance between 2 entities + * @param e1 + * @param e2 + * @return + */ + public static double distance(Entity e1, Entity e2) { + return Math.sqrt(distanceSq(e1, e2)); + } + + + /** + * Calculates distance between 2 block positions + * @param e1 + * @param e2 + * @return + */ + public static double distance(BlockPosition e1, BlockPosition e2) { + return Math.sqrt(distanceSq(e1, e2)); + } + + /** + * Gets the distance between 2 positions + * @param x1 + * @param y1 + * @param z1 + * @param x2 + * @param y2 + * @param z2 + * @return + */ + public static double distance(double x1, double y1, double z1, double x2, double y2, double z2) { + return Math.sqrt(distanceSq(x1, y1, z1, x2, y2, z2)); + } + + /** + * Get's the distance squared between 2 entities + * @param e1 + * @param e2 + * @return + */ + public static double distanceSq(Entity e1, Entity e2) { + return distanceSq(e1.locX,e1.locY,e1.locZ, e2.locX,e2.locY,e2.locZ); + } + + /** + * Gets the distance sqaured between 2 block positions + * @param pos1 + * @param pos2 + * @return + */ + public static double distanceSq(BlockPosition pos1, BlockPosition pos2) { + return distanceSq(pos1.getX(), pos1.getY(), pos1.getZ(), pos2.getX(), pos2.getY(), pos2.getZ()); + } + + /** + * Gets the distance squared between 2 positions + * @param x1 + * @param y1 + * @param z1 + * @param x2 + * @param y2 + * @param z2 + * @return + */ + public static double distanceSq(double x1, double y1, double z1, double x2, double y2, double z2) { + return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2); + } + + /** + * Converts a NMS World/BlockPosition to Bukkit Location + * @param world + * @param x + * @param y + * @param z + * @return + */ + public static Location toLocation(World world, double x, double y, double z) { + return new Location(world.getWorld(), x, y, z); + } + + /** + * Converts a NMS World/BlockPosition to Bukkit Location + * @param world + * @param pos + * @return + */ + public static Location toLocation(World world, BlockPosition pos) { + return new Location(world.getWorld(), pos.getX(), pos.getY(), pos.getZ()); + } + + /** + * Converts an NMS entity's current location to a Bukkit Location + * @param entity + * @return + */ + public static Location toLocation(Entity entity) { + return new Location(entity.getWorld().getWorld(), entity.locX, entity.locY, entity.locZ); + } + + public static org.bukkit.block.Block toBukkitBlock(World world, BlockPosition pos) { + return world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()); + } + + public static BlockPosition toBlockPosition(Location loc) { + return new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); + } + + public static boolean isEdgeOfChunk(BlockPosition pos) { + final int modX = pos.getX() & 15; + final int modZ = pos.getZ() & 15; + return (modX == 0 || modX == 15 || modZ == 0 || modZ == 15); + } + + /** + * Posts a task to be executed asynchronously + * @param run + */ + public static void scheduleAsyncTask(Runnable run) { + asyncExecutor.execute(run); + } + + @Nullable + public static TileEntityHopper getHopper(World world, BlockPosition pos) { + Chunk chunk = world.getChunkIfLoaded(pos.getX() >> 4, pos.getZ() >> 4); + if (chunk != null && chunk.getBlockData(pos.getX(), pos.getY(), pos.getZ()).getBlock() == Blocks.HOPPER) { + TileEntity tileEntity = chunk.getTileEntityImmediately(pos); + if (tileEntity instanceof TileEntityHopper) { + return (TileEntityHopper) tileEntity; + } + } + return null; + } + + @Nonnull + public static World getNMSWorld(@Nonnull org.bukkit.World world) { + return ((CraftWorld) world).getHandle(); + } + + public static World getNMSWorld(@Nonnull org.bukkit.entity.Entity entity) { + return getNMSWorld(entity.getWorld()); + } + + public static FluidCollisionOption getNMSFluidCollisionOption(TargetBlockInfo.FluidMode fluidMode) { + if (fluidMode == TargetBlockInfo.FluidMode.NEVER) { + return FluidCollisionOption.NEVER; + } + if (fluidMode == TargetBlockInfo.FluidMode.SOURCE_ONLY) { + return FluidCollisionOption.SOURCE_ONLY; + } + if (fluidMode == TargetBlockInfo.FluidMode.ALWAYS) { + return FluidCollisionOption.ALWAYS; + } + return null; + } + + public static BlockFace toBukkitBlockFace(EnumDirection enumDirection) { + switch (enumDirection) { + case DOWN: + return BlockFace.DOWN; + case UP: + return BlockFace.UP; + case NORTH: + return BlockFace.NORTH; + case SOUTH: + return BlockFace.SOUTH; + case WEST: + return BlockFace.WEST; + case EAST: + return BlockFace.EAST; + default: + return null; + } + } + + @Nullable + public static IChatBaseComponent getBaseComponentFromNbt(String key, NBTTagCompound compound) { + if (!compound.hasKey(key)) { + return null; + } + String string = compound.getString(key); + try { + return IChatBaseComponent.ChatSerializer.jsonToComponent(string); + } catch (com.google.gson.JsonParseException e) { + org.bukkit.Bukkit.getLogger().warning("Unable to parse " + key + " from " + compound +": " + e.getMessage()); + } + + return null; + } +} diff --git a/src/main/java/net/minecraft/server/MathHelper.java b/src/main/java/net/minecraft/server/MathHelper.java new file mode 100644 index 000000000000..67bb289545bd --- /dev/null +++ b/src/main/java/net/minecraft/server/MathHelper.java @@ -0,0 +1,353 @@ +package net.minecraft.server; + +import java.util.Random; +import java.util.UUID; +import java.util.function.IntPredicate; + +public class MathHelper { + + public static final float a = c(2.0F); + private static final float[] b = (float[]) SystemUtils.a((new float[65536]), (afloat) -> { // Paper - Decompile fix + for (int i = 0; i < afloat.length; ++i) { + afloat[i] = (float) Math.sin((double) i * 3.141592653589793D * 2.0D / 65536.0D); + } + + }); + private static final Random c = new Random(); + private static final int[] d = new int[] { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9}; + private static final double e = Double.longBitsToDouble(4805340802404319232L); + private static final double[] f = new double[257]; + private static final double[] g = new double[257]; + + public static float sin(float f) { + return MathHelper.b[(int) (f * 10430.378F) & '\uffff']; + } + + public static float cos(float f) { + return MathHelper.b[(int) (f * 10430.378F + 16384.0F) & '\uffff']; + } + + public static float c(float f) { + return (float) Math.sqrt((double) f); + } + + public static float sqrt(double d0) { + return (float) Math.sqrt(d0); + } + + public static int d(float f) { + int i = (int) f; + + return f < (float) i ? i - 1 : i; + } + + public static int floor(double d0) { + int i = (int) d0; + + return d0 < (double) i ? i - 1 : i; + } + + public static long d(double d0) { + long i = (long) d0; + + return d0 < (double) i ? i - 1L : i; + } + + public static float e(float f) { + return f >= 0.0F ? f : -f; + } + + public static int a(int i) { + return i >= 0 ? i : -i; + } + + public static int f(float f) { + int i = (int) f; + + return f > (float) i ? i + 1 : i; + } + + public static int f(double d0) { + int i = (int) d0; + + return d0 > (double) i ? i + 1 : i; + } + + public static int clamp(int i, int j, int k) { + return i < j ? j : (i > k ? k : i); + } + + public static float a(float f, float f1, float f2) { + return f < f1 ? f1 : (f > f2 ? f2 : f); + } + + public static double clamp(double d0, double d1, double d2) { return a(d0, d1, d2); } // Paper - OBFHELPER + public static double a(double d0, double d1, double d2) { + return d0 < d1 ? d1 : (d0 > d2 ? d2 : d0); + } + + public static double b(double d0, double d1, double d2) { + return d2 < 0.0D ? d0 : (d2 > 1.0D ? d1 : d0 + (d1 - d0) * d2); + } + + public static double a(double d0, double d1) { + if (d0 < 0.0D) { + d0 = -d0; + } + + if (d1 < 0.0D) { + d1 = -d1; + } + + return d0 > d1 ? d0 : d1; + } + + public static int a(int i, int j) { + return Math.floorDiv(i, j); + } + + public static int nextInt(Random random, int i, int j) { + return i >= j ? i : random.nextInt(j - i + 1) + i; + } + + public static float a(Random random, float f, float f1) { + return f >= f1 ? f : random.nextFloat() * (f1 - f) + f; + } + + public static double a(Random random, double d0, double d1) { + return d0 >= d1 ? d0 : random.nextDouble() * (d1 - d0) + d0; + } + + public static double a(long[] along) { + long i = 0L; + long[] along1 = along; + int j = along.length; + + for (int k = 0; k < j; ++k) { + long l = along1[k]; + + i += l; + } + + return (double) i / (double) along.length; + } + + public static int b(int i, int j) { + return Math.floorMod(i, j); + } + + public static float normalizeYaw(float fx) { return g(fx); } // Paper - OBFHELPER + public static float g(float f) { + f %= 360.0F; + if (f >= 180.0F) { + f -= 360.0F; + } + + if (f < -180.0F) { + f += 360.0F; + } + + return f; + } + + public static double g(double d0) { + d0 %= 360.0D; + if (d0 >= 180.0D) { + d0 -= 360.0D; + } + + if (d0 < -180.0D) { + d0 += 360.0D; + } + + return d0; + } + + public static float c(float f, float f1) { + float f2 = g(f - f1); + + return f2 < 180.0F ? f2 : f2 - 360.0F; + } + + public static float d(float f, float f1) { + float f2 = g(f - f1); + + return f2 < 180.0F ? e(f2) : e(f2 - 360.0F); + } + + public static float b(float f, float f1, float f2) { + f2 = e(f2); + return f < f1 ? a(f + f2, f, f1) : a(f - f2, f1, f); + } + + public static float c(float f, float f1, float f2) { + float f3 = c(f1, f); + + return b(f, f + f3, f2); + } + + public static int c(int i) { + int j = i - 1; + + j |= j >> 1; + j |= j >> 2; + j |= j >> 4; + j |= j >> 8; + j |= j >> 16; + return j + 1; + } + + private static boolean g(int i) { + return i != 0 && (i & i - 1) == 0; + } + + public static int d(int i) { + i = g(i) ? i : c(i); + return MathHelper.d[(int) ((long) i * 125613361L >> 27) & 31]; + } + + public static int e(int i) { + return d(i) - (g(i) ? 0 : 1); + } + + public static int c(int i, int j) { + if (j == 0) { + return 0; + } else if (i == 0) { + return j; + } else { + if (i < 0) { + j *= -1; + } + + int k = i % j; + + return k == 0 ? i : i + j - k; + } + } + + public static long c(int i, int j, int k) { + long l = (long) (i * 3129871) ^ (long) k * 116129781L ^ (long) j; + + l = l * l * 42317861L + l * 11L; + return l >> 16; + } + + public static UUID a(Random random) { + long i = random.nextLong() & -61441L | 16384L; + long j = random.nextLong() & 4611686018427387903L | Long.MIN_VALUE; + + return new UUID(i, j); + } + + public static UUID a() { + return a(MathHelper.c); + } + + public static double c(double d0, double d1, double d2) { + return (d0 - d1) / (d2 - d1); + } + + public static double c(double d0, double d1) { + double d2 = d1 * d1 + d0 * d0; + + if (Double.isNaN(d2)) { + return Double.NaN; + } else { + boolean flag = d0 < 0.0D; + + if (flag) { + d0 = -d0; + } + + boolean flag1 = d1 < 0.0D; + + if (flag1) { + d1 = -d1; + } + + boolean flag2 = d0 > d1; + double d3; + + if (flag2) { + d3 = d1; + d1 = d0; + d0 = d3; + } + + d3 = i(d2); + d1 *= d3; + d0 *= d3; + double d4 = MathHelper.e + d0; + int i = (int) Double.doubleToRawLongBits(d4); + double d5 = MathHelper.f[i]; + double d6 = MathHelper.g[i]; + double d7 = d4 - MathHelper.e; + double d8 = d0 * d6 - d1 * d7; + double d9 = (6.0D + d8 * d8) * d8 * 0.16666666666666666D; + double d10 = d5 + d9; + + if (flag2) { + d10 = 1.5707963267948966D - d10; + } + + if (flag1) { + d10 = 3.141592653589793D - d10; + } + + if (flag) { + d10 = -d10; + } + + return d10; + } + } + + public static double i(double d0) { + double d1 = 0.5D * d0; + long i = Double.doubleToRawLongBits(d0); + + i = 6910469410427058090L - (i >> 1); + d0 = Double.longBitsToDouble(i); + d0 *= 1.5D - d1 * d0 * d0; + return d0; + } + + public static int f(int i) { + i ^= i >>> 16; + i *= -2048144789; + i ^= i >>> 13; + i *= -1028477387; + i ^= i >>> 16; + return i; + } + + public static int a(int i, int j, IntPredicate intpredicate) { + int k = j - i; + + while (k > 0) { + int l = k / 2; + int i1 = i + l; + + if (intpredicate.test(i1)) { + k = l; + } else { + i = i1 + 1; + k -= l + 1; + } + } + + return i; + } + + static { + for (int i = 0; i < 257; ++i) { + double d0 = (double) i / 256.0D; + double d1 = Math.asin(d0); + + MathHelper.g[i] = Math.cos(d1); + MathHelper.f[i] = d1; + } + + } +} diff --git a/src/main/java/net/minecraft/server/MerchantRecipe.java b/src/main/java/net/minecraft/server/MerchantRecipe.java new file mode 100644 index 000000000000..21666d2d5dea --- /dev/null +++ b/src/main/java/net/minecraft/server/MerchantRecipe.java @@ -0,0 +1,140 @@ +package net.minecraft.server; + +import org.bukkit.craftbukkit.inventory.CraftMerchantRecipe; // CraftBukkit + +public class MerchantRecipe { + + public ItemStack buyingItem1; + public ItemStack buyingItem2; + public ItemStack sellingItem; + public int uses; + public int maxUses; + public boolean rewardExp; + // CraftBukkit start + private CraftMerchantRecipe bukkitHandle; + + public CraftMerchantRecipe asBukkit() { + return (bukkitHandle == null) ? bukkitHandle = new CraftMerchantRecipe(this) : bukkitHandle; + } + + public MerchantRecipe(ItemStack itemstack, ItemStack itemstack1, ItemStack itemstack2, int i, int j, CraftMerchantRecipe bukkit) { + this(itemstack, itemstack1, itemstack2, i, j); + this.bukkitHandle = bukkit; + } + // CraftBukkit end + + public MerchantRecipe(NBTTagCompound nbttagcompound) { + this.buyingItem1 = ItemStack.a; + this.buyingItem2 = ItemStack.a; + this.sellingItem = ItemStack.a; + this.a(nbttagcompound); + } + + public MerchantRecipe(ItemStack itemstack, ItemStack itemstack1, ItemStack itemstack2) { + this(itemstack, itemstack1, itemstack2, 0, 7); + } + + public MerchantRecipe(ItemStack itemstack, ItemStack itemstack1, ItemStack itemstack2, int i, int j) { + this.buyingItem1 = ItemStack.a; + this.buyingItem2 = ItemStack.a; + this.sellingItem = ItemStack.a; + this.buyingItem1 = itemstack; + this.buyingItem2 = itemstack1; + this.sellingItem = itemstack2; + this.uses = i; + this.maxUses = j; + this.rewardExp = true; + } + + public MerchantRecipe(ItemStack itemstack, ItemStack itemstack1) { + this(itemstack, ItemStack.a, itemstack1); + } + + public MerchantRecipe(ItemStack itemstack, Item item) { + this(itemstack, new ItemStack(item)); + } + + public ItemStack getBuyItem1() { + return this.buyingItem1; + } + + public ItemStack getBuyItem2() { + return this.buyingItem2; + } + + public boolean hasSecondItem() { + return !this.buyingItem2.isEmpty(); + } + + public ItemStack getBuyItem3() { + return this.sellingItem; + } + + public int e() { + return this.uses; + } + + public int f() { + return this.maxUses; + } + + public void increaseUses() { + ++this.uses; + } + + public void a(int i) { + this.maxUses += i; + } + + public boolean h() { + return this.uses >= this.maxUses; + } + + public boolean j() { + return this.rewardExp; + } + + public void a(NBTTagCompound nbttagcompound) { + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("buy"); + + this.buyingItem1 = ItemStack.a(nbttagcompound1); + NBTTagCompound nbttagcompound2 = nbttagcompound.getCompound("sell"); + + this.sellingItem = ItemStack.a(nbttagcompound2); + if (nbttagcompound.hasKeyOfType("buyB", 10)) { + this.buyingItem2 = ItemStack.a(nbttagcompound.getCompound("buyB")); + } + + if (nbttagcompound.hasKeyOfType("uses", 99)) { + this.uses = nbttagcompound.getInt("uses"); + } + + if (nbttagcompound.hasKeyOfType("maxUses", 99)) { + this.maxUses = nbttagcompound.getInt("maxUses"); + } else { + this.maxUses = 7; + } + + if (nbttagcompound.hasKeyOfType("rewardExp", 1)) { + this.rewardExp = nbttagcompound.getBoolean("rewardExp"); + } else { + this.rewardExp = true; + } + + } + + public NBTTagCompound k() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.set("buy", this.buyingItem1.save(new NBTTagCompound())); + nbttagcompound.set("sell", this.sellingItem.save(new NBTTagCompound())); + if (!this.buyingItem2.isEmpty()) { + nbttagcompound.set("buyB", this.buyingItem2.save(new NBTTagCompound())); + } + + nbttagcompound.setInt("uses", this.uses); + nbttagcompound.setInt("maxUses", this.maxUses); + nbttagcompound.setBoolean("rewardExp", this.rewardExp); + return nbttagcompound; + } +} diff --git a/src/main/java/net/minecraft/server/MethodProfiler.java b/src/main/java/net/minecraft/server/MethodProfiler.java new file mode 100644 index 000000000000..ac3a51372d9c --- /dev/null +++ b/src/main/java/net/minecraft/server/MethodProfiler.java @@ -0,0 +1,188 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class MethodProfiler { + + public static final boolean ENABLED = Boolean.getBoolean("enableDebugMethodProfiler"); // CraftBukkit - disable unless specified in JVM arguments + private static final Logger a = LogManager.getLogger(); + private final List b = Lists.newArrayList(); + private final List c = Lists.newArrayList(); + private boolean d; + private String e = ""; + private final Map f = Maps.newHashMap(); + private long g; + private int h; + + public MethodProfiler() {} + + public boolean a() { + return this.d; + } + + public void b() { + this.d = false; + } + + public long c() { + return this.g; + } + + public int d() { + return this.h; + } + + public void a(int i) { + if (!ENABLED) return; // CraftBukkit + if (!this.d) { + this.d = true; + this.f.clear(); + this.e = ""; + this.b.clear(); + this.h = i; + this.g = SystemUtils.getMonotonicNanos(); + } + } + + public void enter(String s) { + if (!ENABLED) return; // CraftBukkit + if (this.d) { + if (!this.e.isEmpty()) { + this.e = this.e + "."; + } + + this.e = this.e + s; + this.b.add(this.e); + this.c.add(SystemUtils.getMonotonicNanos()); + } + } + + public void a(Supplier supplier) { + if (!ENABLED) return; // CraftBukkit + if (this.d) { + this.enter((String) supplier.get()); + } + } + + public void exit() { + if (!ENABLED) return; // CraftBukkit + if (this.d && !this.c.isEmpty()) { + long i = SystemUtils.getMonotonicNanos(); + long j = (Long) this.c.remove(this.c.size() - 1); + + this.b.remove(this.b.size() - 1); + long k = i - j; + + if (this.f.containsKey(this.e)) { + this.f.put(this.e, (Long) this.f.get(this.e) + k); + } else { + this.f.put(this.e, k); + } + + if (k > 100000000L) { + MethodProfiler.a.warn("Something's taking too long! '{}' took aprox {} ms", this.e, (double) k / 1000000.0D); + } + + this.e = this.b.isEmpty() ? "" : (String) this.b.get(this.b.size() - 1); + } + } + + public List b(String s) { + if (!ENABLED) return Collections.emptyList(); // CraftBukkit + long i = this.f.containsKey("root") ? (Long) this.f.get("root") : 0L; + long j = this.f.containsKey(s) ? (Long) this.f.get(s) : -1L; + List list = Lists.newArrayList(); + + if (!s.isEmpty()) { + s = s + "."; + } + + long k = 0L; + Iterator iterator = this.f.keySet().iterator(); + + while (iterator.hasNext()) { + String s1 = (String) iterator.next(); + + if (s1.length() > s.length() && s1.startsWith(s) && s1.indexOf(".", s.length() + 1) < 0) { + k += (Long) this.f.get(s1); + } + } + + float f = (float) k; + + if (k < j) { + k = j; + } + + if (i < k) { + i = k; + } + + Iterator iterator1 = this.f.keySet().iterator(); + + String s2; + + while (iterator1.hasNext()) { + s2 = (String) iterator1.next(); + if (s2.length() > s.length() && s2.startsWith(s) && s2.indexOf(".", s.length() + 1) < 0) { + long l = (Long) this.f.get(s2); + double d0 = (double) l * 100.0D / (double) k; + double d1 = (double) l * 100.0D / (double) i; + String s3 = s2.substring(s.length()); + + list.add(new MethodProfiler.ProfilerInfo(s3, d0, d1)); + } + } + + iterator1 = this.f.keySet().iterator(); + + while (iterator1.hasNext()) { + s2 = (String) iterator1.next(); + this.f.put(s2, (Long) this.f.get(s2) * 999L / 1000L); + } + + if ((float) k > f) { + list.add(new MethodProfiler.ProfilerInfo("unspecified", (double) ((float) k - f) * 100.0D / (double) k, (double) ((float) k - f) * 100.0D / (double) i)); + } + + Collections.sort(list); + list.add(0, new MethodProfiler.ProfilerInfo(s, 100.0D, (double) k * 100.0D / (double) i)); + return list; + } + + public void exitEnter(String s) { + if (!ENABLED) return; // CraftBukkit + this.exit(); + this.enter(s); + } + + public String f() { + if (!ENABLED) return "[DISABLED]"; // CraftBukkit + return this.b.isEmpty() ? "[UNKNOWN]" : (String) this.b.get(this.b.size() - 1); + } + + public static final class ProfilerInfo implements Comparable { + + public double a; + public double b; + public String c; + + public ProfilerInfo(String s, double d0, double d1) { + this.c = s; + this.a = d0; + this.b = d1; + } + + public int compareTo(MethodProfiler.ProfilerInfo methodprofiler_profilerinfo) { + return methodprofiler_profilerinfo.a < this.a ? -1 : (methodprofiler_profilerinfo.a > this.a ? 1 : methodprofiler_profilerinfo.c.compareTo(this.c)); + } + } +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java new file mode 100644 index 000000000000..ce39ea09e831 --- /dev/null +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -0,0 +1,1871 @@ +package net.minecraft.server; + +import co.aikar.timings.Timings; +import com.destroystokyo.paper.event.server.PaperServerListPingEvent; +import com.google.common.base.Stopwatch; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Queues; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListenableFutureTask; +import com.google.gson.JsonElement; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.GameProfileRepository; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; +import com.mojang.datafixers.DataFixer; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufOutputStream; +import io.netty.buffer.Unpooled; +import it.unimi.dsi.fastutil.longs.LongIterator; +import java.awt.GraphicsEnvironment; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.Proxy; +import java.net.URLEncoder; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.security.KeyPair; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.BooleanSupplier; +import javax.annotation.Nullable; +import javax.imageio.ImageIO; +import org.apache.commons.lang3.Validate; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +// CraftBukkit start +import joptsimple.OptionSet; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.Main; +import org.bukkit.event.server.ServerLoadEvent; +// CraftBukkit end +import org.spigotmc.SlackActivityAccountant; // Spigot +import co.aikar.timings.MinecraftTimings; // Paper + +public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStatistics, ICommandListener, Runnable { + + private static MinecraftServer SERVER; // Paper + public static final Logger LOGGER = LogManager.getLogger(); + public static final File a = new File("usercache.json"); + public Convertable convertable; + private final MojangStatisticsGenerator snooper = new MojangStatisticsGenerator("server", this, SystemUtils.getMonotonicMillis()); + public File universe; + private final List k = Lists.newArrayList(); + public final MethodProfiler methodProfiler = new MethodProfiler(); + private ServerConnection serverConnection; // Spigot + private final ServerPing m = new ServerPing(); + private final Random n = new Random(); + public final DataFixer dataConverterManager; + private String serverIp; + private int q = -1; + public final Map worldServer = new com.destroystokyo.paper.PaperWorldMap(); // Paper; + private PlayerList playerList; + private boolean isRunning = true; + private boolean isRestarting = false; // Paper - flag to signify we're attempting to restart + private boolean isStopped; + private int ticks; + protected final Proxy c; + private IChatBaseComponent w; + private int x; + private boolean onlineMode; + private boolean z; + private boolean spawnAnimals; + private boolean spawnNPCs; + private boolean pvpMode; + private boolean allowFlight; + private String motd; + private int F; + private int G; + public final long[] d = new long[100]; + protected final Map e = Maps.newIdentityHashMap(); + private KeyPair H; + private String I; + private String J; + private boolean demoMode; + private boolean M; + private String N = ""; + private String O = ""; + private boolean P; + private long lastOverloadTime; + private IChatBaseComponent R; + private boolean S; + private boolean T; + private final YggdrasilAuthenticationService U; + private final MinecraftSessionService V; + private final GameProfileRepository W; + private final UserCache X; + private long Y; + protected final Queue> f = Queues.newConcurrentLinkedQueue(); + private Thread serverThread; + private long nextTick = SystemUtils.getMonotonicMillis(); + private final IReloadableResourceManager ac; + private final ResourcePackRepository resourcePackRepository; + private ResourcePackSourceFolder resourcePackFolder; + public CommandDispatcher commandDispatcher; + private final CraftingManager ag; + private final TagRegistry ah; + private final ScoreboardServer ai; + private final BossBattleCustomData aj; + private final LootTableRegistry ak; + private final AdvancementDataWorld al; + private final CustomFunctionData am; + private boolean an; + private boolean forceUpgrade; + private float ap; + + // CraftBukkit start + public org.bukkit.craftbukkit.CraftServer server; + public OptionSet options; + public org.bukkit.command.ConsoleCommandSender console; + public org.bukkit.command.RemoteConsoleCommandSender remoteConsole; + //public ConsoleReader reader; // Paper + public static int currentTick = 0; // Paper - Further improve tick loop + public boolean serverAutoSave = false; // Paper + public final Thread primaryThread; + public java.util.Queue processQueue = new java.util.concurrent.ConcurrentLinkedQueue(); + public int autosavePeriod; + public File bukkitDataPackFolder; + public CommandDispatcher vanillaCommandDispatcher; + public List expiringMaps = java.util.Collections.synchronizedList(new java.util.ArrayList<>()); // Paper + // CraftBukkit end + // Spigot start + public static final int TPS = 20; + public static final int TICK_TIME = 1000000000 / TPS; + private static final int SAMPLE_INTERVAL = 20; // Paper + public final double[] recentTps = new double[ 3 ]; + public final SlackActivityAccountant slackActivityAccountant = new SlackActivityAccountant(); + // Spigot end + + public MinecraftServer(OptionSet options, Proxy proxy, DataFixer datafixer, CommandDispatcher commanddispatcher, YggdrasilAuthenticationService yggdrasilauthenticationservice, MinecraftSessionService minecraftsessionservice, GameProfileRepository gameprofilerepository, UserCache usercache) { + SERVER = this; // Paper - better singleton + this.commandDispatcher = commanddispatcher; // CraftBukkit + this.ac = new ResourceManager(EnumResourcePackType.SERVER_DATA); + this.resourcePackRepository = new ResourcePackRepository<>(ResourcePackLoader::new); + this.ag = new CraftingManager(); + this.ah = new TagRegistry(); + this.ai = new ScoreboardServer(this); + this.aj = new BossBattleCustomData(this); + this.ak = new LootTableRegistry(); + this.al = new AdvancementDataWorld(); + this.am = new CustomFunctionData(this); + this.c = proxy; + this.commandDispatcher = this.vanillaCommandDispatcher = commanddispatcher; // CraftBukkit + this.U = yggdrasilauthenticationservice; + this.V = minecraftsessionservice; + this.W = gameprofilerepository; + this.X = usercache; + // this.universe = file; // CraftBukkit + // this.serverConnection = new ServerConnection(this); // CraftBukkit // Spigot + // this.convertable = file == null ? null : new WorldLoaderServer(file.toPath(), file.toPath().resolve("../backups"), datafixer); // CraftBukkit - moved to DedicatedServer.init + this.dataConverterManager = datafixer; + this.ac.a((IResourcePackListener) this.ah); + this.ac.a((IResourcePackListener) this.ag); + this.ac.a((IResourcePackListener) this.ak); + this.ac.a((IResourcePackListener) this.am); + this.ac.a((IResourcePackListener) this.al); + // CraftBukkit start + this.options = options; + // Paper start - Handled by TerminalConsoleAppender + // Try to see if we're actually running in a terminal, disable jline if not + /* + if (System.console() == null && System.getProperty("jline.terminal") == null) { + System.setProperty("jline.terminal", "jline.UnsupportedTerminal"); + Main.useJline = false; + } + + try { + reader = new ConsoleReader(System.in, System.out); + reader.setExpandEvents(false); // Avoid parsing exceptions for uncommonly used event designators + } catch (Throwable e) { + try { + // Try again with jline disabled for Windows users without C++ 2008 Redistributable + System.setProperty("jline.terminal", "jline.UnsupportedTerminal"); + System.setProperty("user.language", "en"); + Main.useJline = false; + reader = new ConsoleReader(System.in, System.out); + reader.setExpandEvents(false); + } catch (IOException ex) { + LOGGER.warn((String) null, ex); + } + } + */ + // Paper end + Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this)); + + this.serverThread = primaryThread = new Thread(this, "Server thread"); // Moved from main + } + + public abstract PropertyManager getPropertyManager(); + // CraftBukkit end + + public abstract boolean init() throws IOException; + + public void convertWorld(String s) { + if (this.getConvertable().isConvertable(s)) { + MinecraftServer.LOGGER.info("Converting map!"); + this.b((IChatBaseComponent) (new ChatMessage("menu.convertingLevel", new Object[0]))); + this.getConvertable().convert(s, new IProgressUpdate() { + private long b = SystemUtils.getMonotonicMillis(); + + public void a(IChatBaseComponent ichatbasecomponent) {} + + public void a(int i) { + if (SystemUtils.getMonotonicMillis() - this.b >= 1000L) { + this.b = SystemUtils.getMonotonicMillis(); + MinecraftServer.LOGGER.info("Converting... {}%", i); + } + + } + + public void c(IChatBaseComponent ichatbasecomponent) {} + }); + } + + if (this.forceUpgrade) { + MinecraftServer.LOGGER.info("Forcing world upgrade! {}", s); // CraftBukkit + WorldData worlddata = this.getConvertable().c(s); // CraftBukkit + + if (worlddata != null) { + WorldUpgrader worldupgrader = new WorldUpgrader(s, this.getConvertable(), worlddata); // CraftBukkit + IChatBaseComponent ichatbasecomponent = null; + + while (!worldupgrader.b()) { + IChatBaseComponent ichatbasecomponent1 = worldupgrader.g(); + + if (ichatbasecomponent != ichatbasecomponent1) { + ichatbasecomponent = ichatbasecomponent1; + MinecraftServer.LOGGER.info(worldupgrader.g().getString()); + } + + int i = worldupgrader.d(); + + if (i > 0) { + int j = worldupgrader.e() + worldupgrader.f(); + + MinecraftServer.LOGGER.info("{}% completed ({} / {} chunks)...", MathHelper.d((float) j / (float) i * 100.0F), j, i); + } + + if (this.isStopped()) { + worldupgrader.a(); + } else { + try { + Thread.sleep(1000L); + } catch (InterruptedException interruptedexception) { + ; + } + } + } + } + } + + } + + protected synchronized void b(IChatBaseComponent ichatbasecomponent) { + this.R = ichatbasecomponent; + } + + public void a(String s, String s1, long i, WorldType worldtype, JsonElement jsonelement) { + // this.convertWorld(s); // CraftBukkit - moved down + this.b((IChatBaseComponent) (new ChatMessage("menu.loadingLevel", new Object[0]))); + /* CraftBukkit start - Remove ticktime arrays and worldsettings + IDataManager idatamanager = this.getConvertable().a(s, this); + + this.a(this.getWorld(), idatamanager); + WorldData worlddata = idatamanager.getWorldData(); + WorldSettings worldsettings; + + if (worlddata == null) { + if (this.L()) { + worldsettings = DemoWorldServer.a; + } else { + worldsettings = new WorldSettings(i, this.getGamemode(), this.getGenerateStructures(), this.isHardcore(), worldtype); + worldsettings.setGeneratorSettings(jsonelement); + if (this.M) { + worldsettings.a(); + } + } + + worlddata = new WorldData(worldsettings, s1); + } else { + worlddata.a(s1); + worldsettings = new WorldSettings(worlddata); + } + + this.a(idatamanager.getDirectory(), worlddata); + */ + int worldCount = 3; + + for (int j = 0; j < worldCount; ++j) { + WorldServer world; + WorldData worlddata; + byte dimension = 0; + + if (j == 1) { + if (getAllowNether()) { + dimension = -1; + } else { + continue; + } + } + + if (j == 2) { + if (server.getAllowEnd()) { + dimension = 1; + } else { + continue; + } + } + + String worldType = org.bukkit.World.Environment.getEnvironment(dimension).toString().toLowerCase(); + String name = (dimension == 0) ? s : s + "_" + worldType; + this.convertWorld(name); // Run conversion now + + org.bukkit.generator.ChunkGenerator gen = this.server.getGenerator(name); + WorldSettings worldsettings = new WorldSettings(com.destroystokyo.paper.PaperConfig.seedOverride.getOrDefault(name, i), this.getGamemode(), this.getGenerateStructures(), this.isHardcore(), worldtype); // Paper + worldsettings.setGeneratorSettings(jsonelement); + + if (j == 0) { + IDataManager idatamanager = new ServerNBTManager(server.getWorldContainer(), s1, this, this.dataConverterManager); + worlddata = idatamanager.getWorldData(); + if (worlddata == null) { + worlddata = new WorldData(worldsettings, s1); + } + worlddata.checkName(s1); // CraftBukkit - Migration did not rewrite the level.dat; This forces 1.8 to take the last loaded world as respawn (in this case the end) + this.a(idatamanager.getDirectory(), worlddata); + PersistentCollection persistentcollection = new PersistentCollection(idatamanager); + + if (this.L()) { + world = (WorldServer) (new DemoWorldServer(this, idatamanager, persistentcollection, worlddata, DimensionManager.OVERWORLD, this.methodProfiler)).i_(); + } else { + world = (WorldServer) (new WorldServer(this, idatamanager, persistentcollection, worlddata, DimensionManager.OVERWORLD, this.methodProfiler, org.bukkit.World.Environment.getEnvironment(dimension), gen)).i_(); + } + + world.a(worldsettings); + this.server.scoreboardManager = new org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager(this, world.getScoreboard()); + } else { + String dim = "DIM" + dimension; + + File newWorld = new File(new File(name), dim); + File oldWorld = new File(new File(s), dim); + File oldLevelDat = new File(new File(s), "level.dat"); // The data folders exist on first run as they are created in the PersistentCollection constructor above, but the level.dat won't + + if (!newWorld.isDirectory() && oldWorld.isDirectory() && oldLevelDat.isFile()) { + MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder required ----"); + MinecraftServer.LOGGER.info("Unfortunately due to the way that Minecraft implemented multiworld support in 1.6, Bukkit requires that you move your " + worldType + " folder to a new location in order to operate correctly."); + MinecraftServer.LOGGER.info("We will move this folder for you, but it will mean that you need to move it back should you wish to stop using Bukkit in the future."); + MinecraftServer.LOGGER.info("Attempting to move " + oldWorld + " to " + newWorld + "..."); + + if (newWorld.exists()) { + MinecraftServer.LOGGER.warn("A file or folder already exists at " + newWorld + "!"); + MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder failed ----"); + } else if (newWorld.getParentFile().mkdirs()) { + if (oldWorld.renameTo(newWorld)) { + MinecraftServer.LOGGER.info("Success! To restore " + worldType + " in the future, simply move " + newWorld + " to " + oldWorld); + // Migrate world data too. + try { + com.google.common.io.Files.copy(oldLevelDat, new File(new File(name), "level.dat")); + org.apache.commons.io.FileUtils.copyDirectory(new File(new File(s), "data"), new File(new File(name), "data")); + } catch (IOException exception) { + MinecraftServer.LOGGER.warn("Unable to migrate world data."); + } + MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder complete ----"); + } else { + MinecraftServer.LOGGER.warn("Could not move folder " + oldWorld + " to " + newWorld + "!"); + MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder failed ----"); + } + } else { + MinecraftServer.LOGGER.warn("Could not create path for " + newWorld + "!"); + MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder failed ----"); + } + } + + IDataManager idatamanager = new ServerNBTManager(server.getWorldContainer(), name, this, this.dataConverterManager); + // world =, b0 to dimension, s1 to name, added Environment and gen + worlddata = idatamanager.getWorldData(); + if (worlddata == null) { + worlddata = new WorldData(worldsettings, name); + } + worlddata.checkName(name); // CraftBukkit - Migration did not rewrite the level.dat; This forces 1.8 to take the last loaded world as respawn (in this case the end) + world = (WorldServer) new SecondaryWorldServer(this, idatamanager, DimensionManager.a(dimension), this.getWorldServer(DimensionManager.OVERWORLD), this.methodProfiler, worlddata, org.bukkit.World.Environment.getEnvironment(dimension), gen).i_(); + } + + this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldInitEvent(world.getWorld())); + + world.addIWorldAccess(new WorldManager(this, world)); + if (!this.H()) { + world.getWorldData().setGameType(this.getGamemode()); + } + + this.worldServer.put(world.dimension, world); + this.getPlayerList().setPlayerFileData(world); + + if (worlddata.P() != null) { + this.getBossBattleCustomData().a(worlddata.P()); + } + } + this.a(this.getDifficulty()); + this.a(this.getWorldServer(DimensionManager.OVERWORLD).worldMaps); + // CraftBukkit end + + // Paper start - Handle collideRule team for player collision toggle + final Scoreboard scoreboard = this.getScoreboard(); + final java.util.Collection toRemove = scoreboard.getTeams().stream().filter(team -> team.getName().startsWith("collideRule_")).map(ScoreboardTeam::getName).collect(java.util.stream.Collectors.toList()); + for (String teamName : toRemove) { + scoreboard.removeTeam(scoreboard.getTeam(teamName)); // Clean up after ourselves + } + + if (!com.destroystokyo.paper.PaperConfig.enablePlayerCollisions) { + this.getPlayerList().collideRuleTeamName = org.apache.commons.lang3.StringUtils.left("collideRule_" + java.util.concurrent.ThreadLocalRandom.current().nextInt(), 16); + ScoreboardTeam collideTeam = scoreboard.createTeam(this.getPlayerList().collideRuleTeamName); + collideTeam.setCanSeeFriendlyInvisibles(false); // Because we want to mimic them not being on a team at all + } + // Paper end + } + + protected void a(File file, WorldData worlddata) { + this.resourcePackRepository.a((ResourcePackSource) (new ResourcePackSourceVanilla())); + this.resourcePackFolder = new ResourcePackSourceFolder(new File(file, "datapacks")); + // CraftBukkit start + bukkitDataPackFolder = new File(new File(file, "datapacks"), "bukkit"); + if (!bukkitDataPackFolder.exists()) { + bukkitDataPackFolder.mkdirs(); + } + File mcMeta = new File(bukkitDataPackFolder, "pack.mcmeta"); + if (!mcMeta.exists()) { + try { + com.google.common.io.Files.write("{\n" + + " \"pack\": {\n" + + " \"description\": \"Data pack for resources provided by Bukkit plugins\",\n" + + " \"pack_format\": 1\n" + + " }\n" + + "}", mcMeta, com.google.common.base.Charsets.UTF_8); + } catch (IOException ex) { + throw new RuntimeException("Could not initialize Bukkit datapack", ex); + } + } + // CraftBukkit end + this.resourcePackRepository.a((ResourcePackSource) this.resourcePackFolder); + this.resourcePackRepository.a(); + List list = Lists.newArrayList(); + Iterator iterator = worlddata.O().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + ResourcePackLoader resourcepackloader = this.resourcePackRepository.a(s); + + if (resourcepackloader != null) { + list.add(resourcepackloader); + } else { + MinecraftServer.LOGGER.warn("Missing data pack {}", s); + } + } + + this.resourcePackRepository.a((Collection) list); + this.a(worlddata); + } + + protected void a(PersistentCollection persistentcollection) { + boolean flag = true; + boolean flag1 = true; + boolean flag2 = true; + boolean flag3 = true; + boolean flag4 = true; + + this.b((IChatBaseComponent) (new ChatMessage("menu.generatingTerrain", new Object[0]))); + + // CraftBukkit start - fire WorldLoadEvent and handle whether or not to keep the spawn in memory + Stopwatch stopwatch = Stopwatch.createStarted(); + boolean waitForChunks = Boolean.getBoolean("paper.waitforchunks"); // Paper + for (WorldServer worldserver : this.getWorlds()) { + MinecraftServer.LOGGER.info("Preparing start region for level " + worldserver.dimension + " (Seed: " + worldserver.getSeed() + ")"); + if (!worldserver.getWorld().getKeepSpawnInMemory()) { + continue; + } + + BlockPosition blockposition = worldserver.getSpawn(); + List list = worldserver.getChunkProvider().getSpiralOutChunks(blockposition, worldserver.paperConfig.keepLoadedRange >> 4); // Paper + Set set = Sets.newConcurrentHashSet(); + + // Paper - remove arraylist creation, call spiral above + if (this.isRunning()) { // Paper + int expected = list.size(); // Paper + + CompletableFuture completablefuture = worldserver.getChunkProvider().loadAllChunks(list, (chunk) -> { // Paper + set.add(chunk.getPos()); + if (waitForChunks && (set.size() == expected || (set.size() < expected && set.size() % (set.size() / 10) == 0))) { + this.a(new ChatMessage("menu.preparingSpawn", new Object[0]), set.size() * 100 / expected); // Paper + } + }); + + while (waitForChunks && !completablefuture.isDone() && isRunning()) { // Paper + try { + PaperAsyncChunkProvider.processMainThreadQueue(this); // Paper + completablefuture.get(50L, TimeUnit.MILLISECONDS); // Paper + } catch (InterruptedException interruptedexception) { + throw new RuntimeException(interruptedexception); + } catch (ExecutionException executionexception) { + if (executionexception.getCause() instanceof RuntimeException) { + throw (RuntimeException) executionexception.getCause(); + } + + throw new RuntimeException(executionexception.getCause()); + } catch (TimeoutException timeoutexception) { + //this.a(new ChatMessage("menu.preparingSpawn", new Object[0]), set.size() * 100 / expected); // Paper + } + } + + if (waitForChunks) this.a(new ChatMessage("menu.preparingSpawn", new Object[0]), set.size() * 100 / expected); // Paper + } + } + + for (WorldServer world : com.google.common.collect.Lists.newArrayList(this.getWorlds())) { // Paper - avoid como if 1 world triggers another world + this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldLoadEvent(world.getWorld())); + } + // CraftBukkit end + MinecraftServer.LOGGER.info("Time elapsed: {} ms", stopwatch.elapsed(TimeUnit.MILLISECONDS)); + Iterator iterator = DimensionManager.b().iterator(); + + while (iterator.hasNext()) { + DimensionManager dimensionmanager = (DimensionManager) iterator.next(); + ForcedChunk forcedchunk = (ForcedChunk) persistentcollection.get(dimensionmanager, ForcedChunk::new, "chunks"); + + if (forcedchunk != null) { + WorldServer worldserver1 = this.getWorldServer(dimensionmanager); + LongIterator longiterator = forcedchunk.a().iterator(); + + while (longiterator.hasNext()) { + this.a(new ChatMessage("menu.loadingForcedChunks", new Object[] { dimensionmanager}), forcedchunk.a().size() * 100 / 625); + long k = longiterator.nextLong(); + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(k); + + worldserver1.getChunkProvider().getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z, true, true); + } + } + } + + this.l(); + } + + protected void a(String s, IDataManager idatamanager) { + File file = new File(idatamanager.getDirectory(), "resources.zip"); + + if (file.isFile()) { + try { + this.setResourcePack("level://" + URLEncoder.encode(s, StandardCharsets.UTF_8.toString()) + "/" + "resources.zip", ""); + } catch (UnsupportedEncodingException unsupportedencodingexception) { + MinecraftServer.LOGGER.warn("Something went wrong url encoding {}", s); + } + } + + } + + public abstract boolean getGenerateStructures(); + + public abstract EnumGamemode getGamemode(); + + public abstract EnumDifficulty getDifficulty(); + + public abstract boolean isHardcore(); + + public abstract int j(); + + public abstract boolean k(); + + protected void a(IChatBaseComponent ichatbasecomponent, int i) { + this.w = ichatbasecomponent; + this.x = i; + MinecraftServer.LOGGER.info("{}: {}%", ichatbasecomponent.getString(), i); + } + + protected void l() { + this.w = null; + this.x = 0; + // CraftBukkit Start + this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD); + this.server.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.STARTUP)); + // CraftBukkit end + } + + protected void saveChunks(boolean flag) { + Iterator iterator = this.getWorlds().iterator(); + + while (iterator.hasNext()) { + WorldServer worldserver = (WorldServer) iterator.next(); + + if (worldserver != null) { + if (!flag) { + MinecraftServer.LOGGER.info("Saving chunks for level '{}'/{}", worldserver.getWorldData().getName(), DimensionManager.a(worldserver.worldProvider.getDimensionManager())); + } + + try { + worldserver.save(true, (IProgressUpdate) null); + } catch (ExceptionWorldConflict exceptionworldconflict) { + MinecraftServer.LOGGER.warn(exceptionworldconflict.getMessage()); + } + } + } + + } + + // CraftBukkit start + private boolean hasStopped = false; + private final Object stopLock = new Object(); + // CraftBukkit end + + public void stop() throws ExceptionWorldConflict { // CraftBukkit - added throws + // CraftBukkit start - prevent double stopping on multiple threads + synchronized(stopLock) { + if (hasStopped) return; + hasStopped = true; + } + PaperAsyncChunkProvider.stop(this); // Paper + // CraftBukkit end + MinecraftServer.LOGGER.info("Stopping server"); + MinecraftTimings.stopServer(); // Paper + // CraftBukkit start + if (this.server != null) { + this.server.disablePlugins(); + } + // CraftBukkit end + if (this.getServerConnection() != null) { + this.getServerConnection().b(); + } + + if (this.playerList != null) { + MinecraftServer.LOGGER.info("Saving players"); + this.playerList.savePlayers(); + this.playerList.u(isRestarting); // Paper + try { Thread.sleep(100); } catch (InterruptedException ex) {} // CraftBukkit - SPIGOT-625 - give server at least a chance to send packets + } + + MinecraftServer.LOGGER.info("Saving worlds"); + Iterator iterator = this.getWorlds().iterator(); + + WorldServer worldserver; + + while (iterator.hasNext()) { + worldserver = (WorldServer) iterator.next(); + if (worldserver != null) { + worldserver.savingDisabled = false; + } + } + + this.saveChunks(false); + iterator = this.getWorlds().iterator(); + + while (iterator.hasNext()) { + worldserver = (WorldServer) iterator.next(); + if (worldserver != null) { + worldserver.close(); + } + } + + if (this.snooper.d()) { + this.snooper.e(); + } + + // Spigot start + if (org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) { + LOGGER.info("Saving usercache.json"); + this.getUserCache().c(false); // Paper + } + // Spigot end + } + + public String getServerIp() { + return this.serverIp; + } + + public void b(String s) { + this.serverIp = s; + } + + public boolean isRunning() { + return this.isRunning; + } + + // Paper start - allow passing of the intent to restart + public void safeShutdown() { + safeShutdown(false); + } + + public void safeShutdown(boolean isRestarting) { + this.isRunning = false; + this.isRestarting = isRestarting; + } + // Paper end + + private boolean canSleepForTick() { + return System.nanoTime() - lastTick + catchupTime < TICK_TIME; // Paper - improved "are we lagging" check to match our own + } + + // Spigot Start + private static double calcTps(double avg, double exp, double tps) + { + return ( avg * exp ) + ( tps * ( 1 - exp ) ); + } + + // Paper start - Further improve server tick loop + private static final long SEC_IN_NANO = 1000000000; + private static final long MAX_CATCHUP_BUFFER = TICK_TIME * TPS * 60L; + private long lastTick = 0; + private long catchupTime = 0; + public final RollingAverage tps1 = new RollingAverage(60); + public final RollingAverage tps5 = new RollingAverage(60 * 5); + public final RollingAverage tps15 = new RollingAverage(60 * 15); + + public static class RollingAverage { + private final int size; + private long time; + private java.math.BigDecimal total; + private int index = 0; + private final java.math.BigDecimal[] samples; + private final long[] times; + + RollingAverage(int size) { + this.size = size; + this.time = size * SEC_IN_NANO; + this.total = dec(TPS).multiply(dec(SEC_IN_NANO)).multiply(dec(size)); + this.samples = new java.math.BigDecimal[size]; + this.times = new long[size]; + for (int i = 0; i < size; i++) { + this.samples[i] = dec(TPS); + this.times[i] = SEC_IN_NANO; + } + } + + private static java.math.BigDecimal dec(long t) { + return new java.math.BigDecimal(t); + } + public void add(java.math.BigDecimal x, long t) { + time -= times[index]; + total = total.subtract(samples[index].multiply(dec(times[index]))); + samples[index] = x; + times[index] = t; + time += t; + total = total.add(x.multiply(dec(t))); + if (++index == size) { + index = 0; + } + } + + public double getAverage() { + return total.divide(dec(time), 30, java.math.RoundingMode.HALF_UP).doubleValue(); + } + } + private static final java.math.BigDecimal TPS_BASE = new java.math.BigDecimal(1E9).multiply(new java.math.BigDecimal(SAMPLE_INTERVAL)); + // Paper End + // Spigot End + + public void run() { + try { + if (this.init()) { + this.nextTick = SystemUtils.getMonotonicMillis(); + this.m.setMOTD(new ChatComponentText(this.motd)); + this.m.setServerInfo(new ServerPing.ServerData("1.13.2", 404)); + this.a(this.m); + + // Spigot start + org.spigotmc.WatchdogThread.hasStarted = true; // Paper + Arrays.fill( recentTps, 20 ); + long start = System.nanoTime(), curTime, wait, tickSection = start; // Paper - Further improve server tick loop + lastTick = start - TICK_TIME; // Paper + while (this.isRunning) { + curTime = System.nanoTime(); + // Paper start - Further improve server tick loop + wait = TICK_TIME - (curTime - lastTick); + if (wait > 0) { + if (catchupTime < 2E6) { + wait += Math.abs(catchupTime); + } else if (wait < catchupTime) { + catchupTime -= wait; + wait = 0; + } else { + wait -= catchupTime; + catchupTime = 0; + } + } + if (wait > 0) { + Thread.sleep(wait / 1000000); + curTime = System.nanoTime(); + wait = TICK_TIME - (curTime - lastTick); + } + + catchupTime = Math.min(MAX_CATCHUP_BUFFER, catchupTime - wait); + if ( ++MinecraftServer.currentTick % SAMPLE_INTERVAL == 0 ) + { + final long diff = curTime - tickSection; + java.math.BigDecimal currentTps = TPS_BASE.divide(new java.math.BigDecimal(diff), 30, java.math.RoundingMode.HALF_UP); + tps1.add(currentTps, diff); + tps5.add(currentTps, diff); + tps15.add(currentTps, diff); + // Backwards compat with bad plugins + recentTps[0] = tps1.getAverage(); + recentTps[1] = tps5.getAverage(); + recentTps[2] = tps15.getAverage(); + // Paper end + tickSection = curTime; + } + lastTick = curTime; + + //MinecraftServer.currentTick = (int) (System.currentTimeMillis() / 50); // CraftBukkit // Paper - don't overwrite current tick time + this.a(this::canSleepForTick); + this.nextTick += 50L; + // Spigot end + this.P = true; + } + } else { + this.a((CrashReport) null); + } + } catch (Throwable throwable) { + MinecraftServer.LOGGER.error("Encountered an unexpected exception", throwable); + // Spigot Start + if ( throwable.getCause() != null ) + { + MinecraftServer.LOGGER.error( "\tCause of unexpected exception was", throwable.getCause() ); + } + // Spigot End + CrashReport crashreport; + + if (throwable instanceof ReportedException) { + crashreport = this.b(((ReportedException) throwable).a()); + } else { + crashreport = this.b(new CrashReport("Exception in server tick loop", throwable)); + } + + File file = new File(new File(this.s(), "crash-reports"), "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-server.txt"); + + if (crashreport.a(file)) { + MinecraftServer.LOGGER.error("This crash report has been saved to: {}", file.getAbsolutePath()); + } else { + MinecraftServer.LOGGER.error("We were unable to save this crash report to disk."); + } + + this.a(crashreport); + } finally { + try { + org.spigotmc.WatchdogThread.doStop(); + this.isStopped = true; + this.stop(); + } catch (Throwable throwable1) { + MinecraftServer.LOGGER.error("Exception stopping the server", throwable1); + } finally { + // CraftBukkit start - Restore terminal to original settings + try { + net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender + } catch (Exception ignored) { + } + // CraftBukkit end + this.t(); + } + + } + + } + + public void a(ServerPing serverping) { + File file = this.c("server-icon.png"); + + if (!file.exists()) { + file = this.getConvertable().b(this.getWorld(), "icon.png"); + } + + if (file.isFile()) { + ByteBuf bytebuf = Unpooled.buffer(); + + try { + BufferedImage bufferedimage = ImageIO.read(file); + + Validate.validState(bufferedimage.getWidth() == 64, "Must be 64 pixels wide", new Object[0]); + Validate.validState(bufferedimage.getHeight() == 64, "Must be 64 pixels high", new Object[0]); + ImageIO.write(bufferedimage, "PNG", new ByteBufOutputStream(bytebuf)); + ByteBuffer bytebuffer = Base64.getEncoder().encode(bytebuf.nioBuffer()); + + serverping.setFavicon("data:image/png;base64," + StandardCharsets.UTF_8.decode(bytebuffer)); + } catch (Exception exception) { + MinecraftServer.LOGGER.error("Couldn't load server icon", exception); + } finally { + bytebuf.release(); + } + } + + } + + public File s() { + return new File("."); + } + + protected void a(CrashReport crashreport) {} + + public void t() {} + + protected void a(BooleanSupplier booleansupplier) { + co.aikar.timings.TimingsManager.FULL_SERVER_TICK.startTiming(); // Paper + this.slackActivityAccountant.tickStarted(); // Spigot + long i = SystemUtils.getMonotonicNanos(); long startTime = i; // Paper + + ++this.ticks; + if (this.S) { + this.S = false; + this.methodProfiler.a(this.ticks); + } + + this.methodProfiler.enter("root"); + this.b(booleansupplier); + if (i - this.Y >= 5000000000L) { + this.Y = i; + this.m.setPlayerSample(new ServerPing.ServerPingPlayerSample(this.getMaxPlayers(), this.getPlayerCount())); + GameProfile[] agameprofile = new GameProfile[Math.min(this.getPlayerCount(), org.spigotmc.SpigotConfig.playerSample)]; // Paper + int j = MathHelper.nextInt(this.n, 0, this.getPlayerCount() - agameprofile.length); + + for (int k = 0; k < agameprofile.length; ++k) { + agameprofile[k] = ((EntityPlayer) this.playerList.v().get(j + k)).getProfile(); + } + + Collections.shuffle(Arrays.asList(agameprofile)); + this.m.b().a(agameprofile); + } + + this.methodProfiler.enter("save"); + + serverAutoSave = (autosavePeriod > 0 && this.ticks % autosavePeriod == 0); // Paper + int playerSaveInterval = com.destroystokyo.paper.PaperConfig.playerAutoSaveRate; + if (playerSaveInterval < 0) { + playerSaveInterval = autosavePeriod; + } + if (playerSaveInterval > 0) { // CraftBukkit // Paper + this.playerList.savePlayers(playerSaveInterval); + // Spigot Start + } // Paper - Incremental Auto Saving + + // We replace this with saving each individual world as this.saveChunks(...) is broken, + // and causes the main thread to sleep for random amounts of time depending on chunk activity + // Also pass flag to only save modified chunks + server.playerCommandState = true; + for (World world : getWorlds()) { + if (world.paperConfig.autoSavePeriod > 0) world.getWorld().save(false); // Paper - Incremental / Configurable Auto Saving + } + server.playerCommandState = false; + // this.saveChunks(true); + // Spigot End + this.methodProfiler.exit(); + //} // Paper - Incremental Auto Saving + + this.methodProfiler.enter("snooper"); + if (getSnooperEnabled() && !this.snooper.d() && this.ticks > 100) { // Spigot + this.snooper.a(); + } + + if (getSnooperEnabled() && this.ticks % 6000 == 0) { // Spigot + this.snooper.b(); + } + + this.methodProfiler.exit(); + this.methodProfiler.enter("tallying"); + long l = this.d[this.ticks % 100] = SystemUtils.getMonotonicNanos() - i; + + this.ap = this.ap * 0.8F + (float) l / 1000000.0F * 0.19999999F; + this.methodProfiler.exit(); + this.methodProfiler.exit(); + org.spigotmc.WatchdogThread.tick(); // Spigot + PaperLightingQueue.processQueue(startTime); // Paper + expiringMaps.removeIf(ExpiringMap::clean); // Paper + this.slackActivityAccountant.tickEnded(l); // Spigot + co.aikar.timings.TimingsManager.FULL_SERVER_TICK.stopTiming(); // Paper + } + + public void b(BooleanSupplier booleansupplier) { + MinecraftTimings.bukkitSchedulerTimer.startTiming(); // Paper + this.server.getScheduler().mainThreadHeartbeat(this.ticks); // CraftBukkit + MinecraftTimings.bukkitSchedulerTimer.stopTiming(); // Paper + MinecraftTimings.minecraftSchedulerTimer.startTiming(); // Paper + this.methodProfiler.enter("jobs"); + + FutureTask futuretask; + + while ((futuretask = (FutureTask) this.f.poll()) != null) { + SystemUtils.a(futuretask, MinecraftServer.LOGGER); + } + PaperAsyncChunkProvider.processMainThreadQueue(this); // Paper + MinecraftTimings.minecraftSchedulerTimer.stopTiming(); // Paper + + this.methodProfiler.exitEnter("commandFunctions"); + MinecraftTimings.commandFunctionsTimer.startTiming(); // Spigot + this.getFunctionData().tick(); + MinecraftTimings.commandFunctionsTimer.stopTiming(); // Spigot + this.methodProfiler.exitEnter("levels"); + + // CraftBukkit start + // Run tasks that are waiting on processing + MinecraftTimings.processQueueTimer.startTiming(); // Spigot + while (!processQueue.isEmpty()) { + processQueue.remove().run(); + } + MinecraftTimings.processQueueTimer.stopTiming(); // Spigot + + MinecraftTimings.chunkIOTickTimer.startTiming(); // Spigot + org.bukkit.craftbukkit.chunkio.ChunkIOExecutor.tick(); + MinecraftTimings.chunkIOTickTimer.stopTiming(); // Spigot + + MinecraftTimings.timeUpdateTimer.startTiming(); // Spigot + // Send time updates to everyone, it will get the right time from the world the player is in. + // Paper start - optimize time updates + for (final WorldServer world : this.getWorlds()) { + final boolean doDaylight = world.getGameRules().getBoolean("doDaylightCycle"); + final long dayTime = world.getDayTime(); + long worldTime = world.getTime(); + final PacketPlayOutUpdateTime worldPacket = new PacketPlayOutUpdateTime(worldTime, dayTime, doDaylight); + for (EntityHuman entityhuman : world.players) { + if (!(entityhuman instanceof EntityPlayer) || (ticks + entityhuman.getId()) % 20 != 0) { + continue; + } + EntityPlayer entityplayer = (EntityPlayer) entityhuman; + long playerTime = entityplayer.getPlayerTime(); + PacketPlayOutUpdateTime packet = (playerTime == dayTime) ? worldPacket : + new PacketPlayOutUpdateTime(worldTime, playerTime, doDaylight); + entityplayer.playerConnection.sendPacket(packet); // Add support for per player time + } + } + // Paper end + MinecraftTimings.timeUpdateTimer.stopTiming(); // Spigot + + // WorldServer worldserver; // CraftBukkit - dropped down + long i; + + // CraftBukkit - dropTickTime + for (Iterator iterator = this.getWorlds().iterator(); iterator.hasNext();) { + WorldServer worldserver = (WorldServer) iterator.next(); + PaperAsyncChunkProvider.processMainThreadQueue(worldserver); // Paper + worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper + TileEntityHopper.skipHopperEvents = worldserver.paperConfig.disableHopperMoveEvents || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper + i = SystemUtils.getMonotonicNanos(); + if (true || worldserver.worldProvider.getDimensionManager() == DimensionManager.OVERWORLD || this.getAllowNether()) { // CraftBukkit + this.methodProfiler.a(() -> { + return "dim-" + worldserver.worldProvider.getDimensionManager().getDimensionID(); + }); + /* Drop global time updates + if (this.ticks % 20 == 0) { + this.methodProfiler.enter("timeSync"); + this.playerList.a((Packet) (new PacketPlayOutUpdateTime(worldserver.getTime(), worldserver.getDayTime(), worldserver.getGameRules().getBoolean("doDaylightCycle"))), worldserver.worldProvider.getDimensionManager()); + this.methodProfiler.exit(); + } + // CraftBukkit end */ + + this.methodProfiler.enter("tick"); + + CrashReport crashreport; + + try { + worldserver.timings.doTick.startTiming(); // Spigot + worldserver.doTick(booleansupplier); + worldserver.timings.doTick.stopTiming(); // Spigot + } catch (Throwable throwable) { + // Spigot Start + try { + crashreport = CrashReport.a(throwable, "Exception ticking world"); + } catch (Throwable t){ + throw new RuntimeException("Error generating crash report", t); + } + // Spigot End + worldserver.a(crashreport); + throw new ReportedException(crashreport); + } + + try { + worldserver.timings.tickEntities.startTiming(); // Spigot + worldserver.tickEntities(); + worldserver.timings.tickEntities.stopTiming(); // Spigot + } catch (Throwable throwable1) { + // Spigot Start + try { + crashreport = CrashReport.a(throwable1, "Exception ticking world entities"); + } catch (Throwable t){ + throw new RuntimeException("Error generating crash report", t); + } + // Spigot End + worldserver.a(crashreport); + throw new ReportedException(crashreport); + } + + this.methodProfiler.exit(); + this.methodProfiler.enter("tracker"); + worldserver.getTracker().updatePlayers(); + this.methodProfiler.exit(); + this.methodProfiler.exit(); + worldserver.explosionDensityCache.clear(); // Paper - Optimize explosions + } + } + + this.methodProfiler.exitEnter("connection"); + MinecraftTimings.connectionTimer.startTiming(); // Spigot + this.getServerConnection().c(); + MinecraftTimings.connectionTimer.stopTiming(); // Spigot + this.methodProfiler.exitEnter("players"); + MinecraftTimings.playerListTimer.startTiming(); // Spigot + this.playerList.tick(); + MinecraftTimings.playerListTimer.stopTiming(); // Spigot + this.methodProfiler.exitEnter("tickables"); + + MinecraftTimings.tickablesTimer.startTiming(); // Spigot + for (int j = 0; j < this.k.size(); ++j) { + ((ITickable) this.k.get(j)).tick(); + } + MinecraftTimings.tickablesTimer.stopTiming(); // Spigot + + this.methodProfiler.exit(); + } + + public boolean getAllowNether() { + return true; + } + + public void a(ITickable itickable) { + this.k.add(itickable); + } + + public static void main(final OptionSet options) { // CraftBukkit - replaces main(String[] astring) + DispenserRegistry.c(); + + try { + /* CraftBukkit start - Replace everything + boolean flag = true; + String s = null; + String s1 = "."; + String s2 = null; + boolean flag1 = false; + boolean flag2 = false; + boolean flag3 = false; + int i = -1; + + for (int j = 0; j < astring.length; ++j) { + String s3 = astring[j]; + String s4 = j == astring.length - 1 ? null : astring[j + 1]; + boolean flag4 = false; + + if (!"nogui".equals(s3) && !"--nogui".equals(s3)) { + if ("--port".equals(s3) && s4 != null) { + flag4 = true; + + try { + i = Integer.parseInt(s4); + } catch (NumberFormatException numberformatexception) { + ; + } + } else if ("--singleplayer".equals(s3) && s4 != null) { + flag4 = true; + s = s4; + } else if ("--universe".equals(s3) && s4 != null) { + flag4 = true; + s1 = s4; + } else if ("--world".equals(s3) && s4 != null) { + flag4 = true; + s2 = s4; + } else if ("--demo".equals(s3)) { + flag1 = true; + } else if ("--bonusChest".equals(s3)) { + flag2 = true; + } else if ("--forceUpgrade".equals(s3)) { + flag3 = true; + } + } else { + flag = false; + } + + if (flag4) { + ++j; + } + } + */ // CraftBukkit end + + String s1 = "."; // PAIL? + YggdrasilAuthenticationService yggdrasilauthenticationservice = new com.destroystokyo.paper.profile.PaperAuthenticationService(Proxy.NO_PROXY, UUID.randomUUID().toString()); // Paper + MinecraftSessionService minecraftsessionservice = yggdrasilauthenticationservice.createMinecraftSessionService(); + GameProfileRepository gameprofilerepository = yggdrasilauthenticationservice.createProfileRepository(); + UserCache usercache = new UserCache(gameprofilerepository, new File(s1, MinecraftServer.a.getName())); + final DedicatedServer dedicatedserver = new DedicatedServer(options, DataConverterRegistry.a(), yggdrasilauthenticationservice, minecraftsessionservice, gameprofilerepository, usercache); + + /* CraftBukkit start + if (s != null) { + dedicatedserver.h(s); + } + + if (s2 != null) { + dedicatedserver.setWorld(s2); + } + + if (i >= 0) { + dedicatedserver.setPort(i); + } + + if (flag1) { + dedicatedserver.c(true); + } + + if (flag2) { + dedicatedserver.d(true); + } + + if (flag && !GraphicsEnvironment.isHeadless()) { + dedicatedserver.aW(); + } + + if (flag3) { + dedicatedserver.setForceUpgrade(true); + } + + dedicatedserver.v(); + Thread thread = new Thread("Server Shutdown Thread") { + public void run() { + dedicatedserver.stop(); + } + }; + + thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(MinecraftServer.LOGGER)); + Runtime.getRuntime().addShutdownHook(thread); + */ + + if (options.has("port")) { + int port = (Integer) options.valueOf("port"); + if (port > 0) { + dedicatedserver.setPort(port); + } + } + + if (options.has("universe")) { + dedicatedserver.universe = (File) options.valueOf("universe"); + } + + if (options.has("world")) { + dedicatedserver.setWorld((String) options.valueOf("world")); + } + + if (options.has("forceUpgrade")) { + dedicatedserver.setForceUpgrade(true); + } + + dedicatedserver.primaryThread.start(); + // CraftBukkit end + } catch (Exception exception) { + MinecraftServer.LOGGER.fatal("Failed to start the minecraft server", exception); + } + + } + + protected void setForceUpgrade(boolean flag) { + this.forceUpgrade = flag; + } + + public void v() { + /* CraftBukkit start - prevent abuse + this.serverThread = new Thread(this, "Server thread"); + this.serverThread.setUncaughtExceptionHandler((thread, throwable) -> { + MinecraftServer.LOGGER.error(throwable); + }); + this.serverThread.start(); + // CraftBukkit end */ + } + + public File c(String s) { + return new File(this.s(), s); + } + + public void info(String s) { + MinecraftServer.LOGGER.info(s); + } + + public void warning(String s) { + MinecraftServer.LOGGER.warn(s); + } + + public WorldServer getWorldServer(DimensionManager dimensionmanager) { + return (WorldServer) this.worldServer.get(dimensionmanager); + } + + public Iterable getWorlds() { + return this.worldServer.values(); + } + + public String getVersion() { + return "1.13.2"; + } + + public int getPlayerCount() { + return this.playerList.getPlayerCount(); + } + + public int getMaxPlayers() { + return this.playerList.getMaxPlayers(); + } + + public String[] getPlayers() { + return this.playerList.f(); + } + + public boolean isDebugging() { + return this.getPropertyManager().getBoolean("debug", false); // CraftBukkit - don't hardcode + } + + public void f(String s) { + MinecraftServer.LOGGER.error(s); + } + + public void g(String s) { + if (this.isDebugging()) { + MinecraftServer.LOGGER.info(s); + } + + } + + public String getServerModName() { + return "Paper"; //Paper - Paper > // Spigot - Spigot > // CraftBukkit - cb > vanilla! + } + + public CrashReport b(CrashReport crashreport) { + crashreport.g().a("Profiler Position", () -> { + return this.methodProfiler.a() ? this.methodProfiler.f() : "N/A (disabled)"; + }); + if (this.playerList != null) { + crashreport.g().a("Player Count", () -> { + return this.playerList.getPlayerCount() + " / " + this.playerList.getMaxPlayers() + "; " + this.playerList.v(); + }); + } + + crashreport.g().a("Data Packs", () -> { + StringBuilder stringbuilder = new StringBuilder(); + Iterator iterator = this.resourcePackRepository.d().iterator(); + + while (iterator.hasNext()) { + ResourcePackLoader resourcepackloader = (ResourcePackLoader) iterator.next(); + + if (stringbuilder.length() > 0) { + stringbuilder.append(", "); + } + + stringbuilder.append(resourcepackloader.e()); + if (!resourcepackloader.c().a()) { + stringbuilder.append(" (incompatible)"); + } + } + + return stringbuilder.toString(); + }); + return crashreport; + } + + public boolean D() { + return true; // CraftBukkit + } + + public void sendMessage(IChatBaseComponent ichatbasecomponent) { + MinecraftServer.LOGGER.info(org.bukkit.craftbukkit.util.CraftChatMessage.fromComponent(ichatbasecomponent, net.minecraft.server.EnumChatFormat.WHITE));// Paper - Log message with colors + } + + public KeyPair E() { + return this.H; + } + + public int getPort() { + return this.q; + } + + public void setPort(int i) { + this.q = i; + } + + public String G() { + return this.I; + } + + public void h(String s) { + this.I = s; + } + + public boolean H() { + return this.I != null; + } + + public String getWorld() { + return this.J; + } + + public void setWorld(String s) { + this.J = s; + } + + public void a(KeyPair keypair) { + this.H = keypair; + } + + public void a(EnumDifficulty enumdifficulty) { + Iterator iterator = this.getWorlds().iterator(); + + while (iterator.hasNext()) { + WorldServer worldserver = (WorldServer) iterator.next(); + + if (worldserver.getWorldData().isHardcore()) { + worldserver.getWorldData().setDifficulty(EnumDifficulty.HARD); + worldserver.setSpawnFlags(true, true); + } else if (this.H()) { + worldserver.getWorldData().setDifficulty(enumdifficulty); + worldserver.setSpawnFlags(worldserver.getDifficulty() != EnumDifficulty.PEACEFUL, true); + } else { + worldserver.getWorldData().setDifficulty(enumdifficulty); + worldserver.setSpawnFlags(this.getSpawnMonsters(), this.spawnAnimals); + } + } + + } + + public boolean getSpawnMonsters() { + return true; + } + + public boolean L() { + return this.demoMode; + } + + public void c(boolean flag) { + this.demoMode = flag; + } + + public void d(boolean flag) { + this.M = flag; + } + + public Convertable getConvertable() { + return this.convertable; + } + + public String getResourcePack() { + return this.N; + } + + public String getResourcePackHash() { + return this.O; + } + + public void setResourcePack(String s, String s1) { + this.N = s; + this.O = s1; + } + + public void a(MojangStatisticsGenerator mojangstatisticsgenerator) { + mojangstatisticsgenerator.a("whitelist_enabled", false); + mojangstatisticsgenerator.a("whitelist_count", 0); + if (this.playerList != null) { + mojangstatisticsgenerator.a("players_current", this.getPlayerCount()); + mojangstatisticsgenerator.a("players_max", this.getMaxPlayers()); + mojangstatisticsgenerator.a("players_seen", this.playerList.getSeenPlayers().length); + } + + mojangstatisticsgenerator.a("uses_auth", this.onlineMode); + mojangstatisticsgenerator.a("gui_state", this.ag() ? "enabled" : "disabled"); + mojangstatisticsgenerator.a("run_time", (SystemUtils.getMonotonicMillis() - mojangstatisticsgenerator.g()) / 60L * 1000L); + mojangstatisticsgenerator.a("avg_tick_ms", (int) (MathHelper.a(this.d) * 1.0E-6D)); + int i = 0; + Iterator iterator = this.getWorlds().iterator(); + + while (iterator.hasNext()) { + WorldServer worldserver = (WorldServer) iterator.next(); + + if (worldserver != null) { + WorldData worlddata = worldserver.getWorldData(); + + mojangstatisticsgenerator.a("world[" + i + "][dimension]", worldserver.worldProvider.getDimensionManager()); + mojangstatisticsgenerator.a("world[" + i + "][mode]", worlddata.getGameType()); + mojangstatisticsgenerator.a("world[" + i + "][difficulty]", worldserver.getDifficulty()); + mojangstatisticsgenerator.a("world[" + i + "][hardcore]", worlddata.isHardcore()); + mojangstatisticsgenerator.a("world[" + i + "][generator_name]", worlddata.getType().name()); + mojangstatisticsgenerator.a("world[" + i + "][generator_version]", worlddata.getType().getVersion()); + mojangstatisticsgenerator.a("world[" + i + "][height]", this.F); + mojangstatisticsgenerator.a("world[" + i + "][chunks_loaded]", worldserver.getChunkProvider().g()); + ++i; + } + } + + mojangstatisticsgenerator.a("worlds", i); + } + + public boolean getSnooperEnabled() { + return true; + } + + public abstract boolean Q(); + + public boolean getOnlineMode() { + return server.getOnlineMode(); // CraftBukkit + } + + public void setOnlineMode(boolean flag) { + this.onlineMode = flag; + } + + public boolean S() { + return this.z; + } + + public void f(boolean flag) { + this.z = flag; + } + + public boolean getSpawnAnimals() { + return this.spawnAnimals; + } + + public void setSpawnAnimals(boolean flag) { + this.spawnAnimals = flag; + } + + public boolean getSpawnNPCs() { + return this.spawnNPCs; + } + + public abstract boolean V(); + + public void setSpawnNPCs(boolean flag) { + this.spawnNPCs = flag; + } + + public boolean getPVP() { + return this.pvpMode; + } + + public void setPVP(boolean flag) { + this.pvpMode = flag; + } + + public boolean getAllowFlight() { + return this.allowFlight; + } + + public void setAllowFlight(boolean flag) { + this.allowFlight = flag; + } + + public abstract boolean getEnableCommandBlock(); + + public String getMotd() { + return this.motd; + } + + public void setMotd(String s) { + this.motd = s; + } + + public int getMaxBuildHeight() { + return this.F; + } + + public void b(int i) { + this.F = i; + } + + public boolean isStopped() { + return this.isStopped; + } + + public PlayerList getPlayerList() { + return this.playerList; + } + + public void a(PlayerList playerlist) { + this.playerList = playerlist; + } + + public abstract boolean ad(); + + public void setGamemode(EnumGamemode enumgamemode) { + Iterator iterator = this.getWorlds().iterator(); + + while (iterator.hasNext()) { + WorldServer worldserver = (WorldServer) iterator.next(); + + worldserver.getWorldData().setGameType(enumgamemode); + } + + } + + public ServerConnection getServerConnection() { + return this.serverConnection == null ? this.serverConnection = new ServerConnection(this) : this.serverConnection; // Spigot + } + + public boolean ag() { + return false; + } + + public abstract boolean a(EnumGamemode enumgamemode, boolean flag, int i); + + public int ah() { + return this.ticks; + } + + public void ai() { + this.S = true; + } + + public int getSpawnProtection() { + return 16; + } + + public boolean a(World world, BlockPosition blockposition, EntityHuman entityhuman) { + return false; + } + + public void setForceGamemode(boolean flag) { + this.T = flag; + } + + public boolean getForceGamemode() { + return this.T; + } + + public int getIdleTimeout() { + return this.G; + } + + public void setIdleTimeout(int i) { + this.G = i; + } + + public MinecraftSessionService getSessionService() { return ap(); } // Paper - OBFHELPER + public MinecraftSessionService ap() { + return this.V; + } + + public GameProfileRepository getGameProfileRepository() { + return this.W; + } + + public UserCache getUserCache() { + return this.X; + } + + public ServerPing getServerPing() { + return this.m; + } + + public void at() { + this.Y = 0L; + } + + public int au() { + return 29999984; + } + + public ListenableFuture a(Callable callable) { + Validate.notNull(callable); + if (!this.isMainThread()) { // CraftBukkit && !this.isStopped()) { + ListenableFutureTask listenablefuturetask = ListenableFutureTask.create(callable); + + this.f.add(listenablefuturetask); + return listenablefuturetask; + } else { + try { + return Futures.immediateFuture(callable.call()); + } catch (Exception exception) { + return Futures.immediateFailedCheckedFuture(exception); + } + } + } + + public ListenableFuture postToMainThread(Runnable runnable) { + Validate.notNull(runnable); + return this.a(Executors.callable(runnable)); + } + + public boolean isMainThread() { + return Thread.currentThread() == this.serverThread; + } + + public int aw() { + return 256; + } + + public long ax() { + return this.nextTick; + } + + public Thread ay() { + return this.serverThread; + } + + public DataFixer az() { + return this.dataConverterManager; + } + + public int a(@Nullable WorldServer worldserver) { + return worldserver != null ? worldserver.getGameRules().c("spawnRadius") : 10; + } + + public AdvancementDataWorld getAdvancementData() { + return this.al; + } + + public CustomFunctionData getFunctionData() { + return this.am; + } + + public void reload() { + if (!this.isMainThread()) { + this.postToMainThread(this::reload); + } else { + this.getPlayerList().savePlayers(); + this.resourcePackRepository.a(); + this.a(this.getWorldServer(DimensionManager.OVERWORLD).getWorldData()); + this.getPlayerList().reload(); + } + } + + private void a(WorldData worlddata) { + List list = Lists.newArrayList(this.resourcePackRepository.d()); + Iterator iterator = this.resourcePackRepository.b().iterator(); + + while (iterator.hasNext()) { + ResourcePackLoader resourcepackloader = (ResourcePackLoader) iterator.next(); + + if (!worlddata.N().contains(resourcepackloader.e()) && !list.contains(resourcepackloader)) { + MinecraftServer.LOGGER.info("Found new data pack {}, loading it automatically", resourcepackloader.e()); + resourcepackloader.h().a(list, resourcepackloader, (resourcepackloader1) -> { + return resourcepackloader1; + }, false); + } + } + + this.resourcePackRepository.a((Collection) list); + List list1 = Lists.newArrayList(); + + this.resourcePackRepository.d().forEach((resourcepackloader1) -> { + list1.add(resourcepackloader1.d()); + }); + this.ac.a((List) list1); + worlddata.O().clear(); + worlddata.N().clear(); + this.resourcePackRepository.d().forEach((resourcepackloader1) -> { + worlddata.O().add(resourcepackloader1.e()); + }); + this.resourcePackRepository.b().forEach((resourcepackloader1) -> { + if (!this.resourcePackRepository.d().contains(resourcepackloader1)) { + worlddata.N().add(resourcepackloader1.e()); + } + + }); + } + + public void a(CommandListenerWrapper commandlistenerwrapper) { + if (this.aQ()) { + PlayerList playerlist = commandlistenerwrapper.getServer().getPlayerList(); + WhiteList whitelist = playerlist.getWhitelist(); + + if (whitelist.isEnabled()) { + List list = Lists.newArrayList(playerlist.v()); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + if (!whitelist.isWhitelisted(entityplayer.getProfile())) { + entityplayer.playerConnection.disconnect(new ChatMessage("multiplayer.disconnect.not_whitelisted", new Object[0])); + } + } + + } + } + } + + public IReloadableResourceManager getResourceManager() { + return this.ac; + } + + public ResourcePackRepository getResourcePackRepository() { + return this.resourcePackRepository; + } + + public CommandDispatcher getCommandDispatcher() { + return this.commandDispatcher; + } + + public CommandListenerWrapper getServerCommandListener() { + return new CommandListenerWrapper(this, this.getWorldServer(DimensionManager.OVERWORLD) == null ? Vec3D.a : new Vec3D(this.getWorldServer(DimensionManager.OVERWORLD).getSpawn()), Vec2F.a, this.getWorldServer(DimensionManager.OVERWORLD), 4, "Server", new ChatComponentText("Server"), this, (Entity) null); + } + + public boolean a() { + return true; + } + + public boolean b() { + return true; + } + + public CraftingManager getCraftingManager() { + return this.ag; + } + + public TagRegistry getTagRegistry() { + return this.ah; + } + + public ScoreboardServer getScoreboard() { + return this.ai; + } + + public LootTableRegistry getLootTableRegistry() { + return this.ak; + } + + public GameRules getGameRules() { + return this.getWorldServer(DimensionManager.OVERWORLD).getGameRules(); + } + + public BossBattleCustomData getBossBattleCustomData() { + return this.aj; + } + + public boolean aQ() { + return this.an; + } + + public void l(boolean flag) { + this.an = flag; + } + + public int a(GameProfile gameprofile) { + if (this.getPlayerList().isOp(gameprofile)) { + OpListEntry oplistentry = (OpListEntry) this.getPlayerList().getOPs().get(gameprofile); + + return oplistentry != null ? oplistentry.a() : (this.H() ? (this.G().equals(gameprofile.getName()) ? 4 : (this.getPlayerList().x() ? 4 : 0)) : this.j()); + } else { + return 0; + } + } + + // CraftBukkit start + @Deprecated + public static MinecraftServer getServer() { + return SERVER; + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/MobEffectList.java b/src/main/java/net/minecraft/server/MobEffectList.java new file mode 100644 index 000000000000..a9a6e5c2fbbe --- /dev/null +++ b/src/main/java/net/minecraft/server/MobEffectList.java @@ -0,0 +1,244 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; +// CraftBukkit end + +public class MobEffectList { + + private final Map a = Maps.newHashMap(); + private final boolean b; + private final int c; + @Nullable + private String d; + private int e = -1; + public double durationModifier; + private boolean g; + + @Nullable + public static MobEffectList fromId(int i) { + return (MobEffectList) IRegistry.MOB_EFFECT.fromId(i); + } + + public static int getId(MobEffectList mobeffectlist) { + return IRegistry.MOB_EFFECT.a(mobeffectlist); // CraftBukkit - decompile error + } + + protected MobEffectList(boolean flag, int i) { + this.b = flag; + if (flag) { + this.durationModifier = 0.5D; + } else { + this.durationModifier = 1.0D; + } + + this.c = i; + } + + protected MobEffectList b(int i, int j) { + this.e = i + j * 12; + return this; + } + + public void tick(EntityLiving entityliving, int i) { + if (this == MobEffects.REGENERATION) { + if (entityliving.getHealth() < entityliving.getMaxHealth()) { + entityliving.heal(1.0F, RegainReason.MAGIC_REGEN); // CraftBukkit + } + } else if (this == MobEffects.POISON) { + if (entityliving.getHealth() > 1.0F) { + entityliving.damageEntity(CraftEventFactory.POISON, 1.0F); // CraftBukkit - DamageSource.MAGIC -> CraftEventFactory.POISON + } + } else if (this == MobEffects.WITHER) { + entityliving.damageEntity(DamageSource.WITHER, 1.0F); + } else if (this == MobEffects.HUNGER && entityliving instanceof EntityHuman) { + ((EntityHuman) entityliving).applyExhaustion(0.005F * (float) (i + 1)); + } else if (this == MobEffects.SATURATION && entityliving instanceof EntityHuman) { + if (!entityliving.world.isClientSide) { + // CraftBukkit start + EntityHuman entityhuman = (EntityHuman) entityliving; + int oldFoodLevel = entityhuman.getFoodData().foodLevel; + + org.bukkit.event.entity.FoodLevelChangeEvent event = CraftEventFactory.callFoodLevelChangeEvent(entityhuman, i + 1 + oldFoodLevel); + + if (!event.isCancelled()) { + entityhuman.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 1.0F); + } + + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutUpdateHealth(((EntityPlayer) entityhuman).getBukkitEntity().getScaledHealth(), entityhuman.getFoodData().foodLevel, entityhuman.getFoodData().saturationLevel)); + // CraftBukkit end + } + } else if ((this != MobEffects.HEAL || entityliving.cp()) && (this != MobEffects.HARM || !entityliving.cp())) { + if (this == MobEffects.HARM && !entityliving.cp() || this == MobEffects.HEAL && entityliving.cp()) { + entityliving.damageEntity(DamageSource.MAGIC, (float) (6 << i)); + } + } else { + entityliving.heal((float) Math.max(4 << i, 0), RegainReason.MAGIC); // CraftBukkit + } + + } + + public void applyInstantEffect(@Nullable Entity entity, @Nullable Entity entity1, EntityLiving entityliving, int i, double d0) { + int j; + + if ((this != MobEffects.HEAL || entityliving.cp()) && (this != MobEffects.HARM || !entityliving.cp())) { + if ((this != MobEffects.HARM || entityliving.cp()) && (this != MobEffects.HEAL || !entityliving.cp())) { + this.tick(entityliving, i); + } else { + j = (int) (d0 * (double) (6 << i) + 0.5D); + if (entity == null) { + entityliving.damageEntity(DamageSource.MAGIC, (float) j); + } else { + entityliving.damageEntity(DamageSource.c(entity, entity1), (float) j); + } + } + } else { + j = (int) (d0 * (double) (4 << i) + 0.5D); + entityliving.heal((float) j, RegainReason.MAGIC); // CraftBukkit + } + + } + + public boolean a(int i, int j) { + int k; + + if (this == MobEffects.REGENERATION) { + k = 50 >> j; + return k > 0 ? i % k == 0 : true; + } else if (this == MobEffects.POISON) { + k = 25 >> j; + return k > 0 ? i % k == 0 : true; + } else if (this == MobEffects.WITHER) { + k = 40 >> j; + return k > 0 ? i % k == 0 : true; + } else { + return this == MobEffects.HUNGER; + } + } + + public boolean isInstant() { + return false; + } + + protected String b() { + if (this.d == null) { + this.d = SystemUtils.a("effect", IRegistry.MOB_EFFECT.getKey(this)); + } + + return this.d; + } + + public String c() { + return this.b(); + } + + public IChatBaseComponent d() { + return new ChatMessage(this.c(), new Object[0]); + } + + protected MobEffectList a(double d0) { + this.durationModifier = d0; + return this; + } + + public int getColor() { + return this.c; + } + + public MobEffectList a(IAttribute iattribute, String s, double d0, int i) { + AttributeModifier attributemodifier = new AttributeModifier(UUID.fromString(s), this::c, d0, i); + + this.a.put(iattribute, attributemodifier); + return this; + } + + public void a(EntityLiving entityliving, AttributeMapBase attributemapbase, int i) { + Iterator iterator = this.a.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + AttributeInstance attributeinstance = attributemapbase.a((IAttribute) entry.getKey()); + + if (attributeinstance != null) { + attributeinstance.c((AttributeModifier) entry.getValue()); + } + } + + } + + public void b(EntityLiving entityliving, AttributeMapBase attributemapbase, int i) { + Iterator iterator = this.a.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + AttributeInstance attributeinstance = attributemapbase.a((IAttribute) entry.getKey()); + + if (attributeinstance != null) { + AttributeModifier attributemodifier = (AttributeModifier) entry.getValue(); + + attributeinstance.c(attributemodifier); + attributeinstance.b(new AttributeModifier(attributemodifier.a(), this.c() + " " + i, this.a(i, attributemodifier), attributemodifier.c())); + } + } + + } + + public double a(int i, AttributeModifier attributemodifier) { + return attributemodifier.d() * (double) (i + 1); + } + + public MobEffectList l() { + this.g = true; + return this; + } + + public static void m() { + a(1, "speed", (new MobEffectList(false, 8171462)).b(0, 0).a(GenericAttributes.MOVEMENT_SPEED, "91AEAA56-376B-4498-935B-2F7F68070635", 0.20000000298023224D, 2).l()); + a(2, "slowness", (new MobEffectList(true, 5926017)).b(1, 0).a(GenericAttributes.MOVEMENT_SPEED, "7107DE5E-7CE8-4030-940E-514C1F160890", -0.15000000596046448D, 2)); + a(3, "haste", (new MobEffectList(false, 14270531)).b(2, 0).a(1.5D).l().a(GenericAttributes.g, "AF8B6E3F-3328-4C0A-AA36-5BA2BB9DBEF3", 0.10000000149011612D, 2)); + a(4, "mining_fatigue", (new MobEffectList(true, 4866583)).b(3, 0).a(GenericAttributes.g, "55FCED67-E92A-486E-9800-B47F202C4386", -0.10000000149011612D, 2)); + a(5, "strength", (new MobEffectAttackDamage(false, 9643043, 3.0D)).b(4, 0).a(GenericAttributes.ATTACK_DAMAGE, "648D7064-6A60-4F59-8ABE-C2C23A6DD7A9", 0.0D, 0).l()); + a(6, "instant_health", (new InstantMobEffect(false, 16262179)).l()); + a(7, "instant_damage", (new InstantMobEffect(true, 4393481)).l()); + a(8, "jump_boost", (new MobEffectList(false, 2293580)).b(2, 1).l()); + a(9, "nausea", (new MobEffectList(true, 5578058)).b(3, 1).a(0.25D)); + a(10, "regeneration", (new MobEffectList(false, 13458603)).b(7, 0).a(0.25D).l()); + a(11, "resistance", (new MobEffectList(false, 10044730)).b(6, 1).l()); + a(12, "fire_resistance", (new MobEffectList(false, 14981690)).b(7, 1).l()); + a(13, "water_breathing", (new MobEffectList(false, 3035801)).b(0, 2).l()); + a(14, "invisibility", (new MobEffectList(false, 8356754)).b(0, 1).l()); + a(15, "blindness", (new MobEffectList(true, 2039587)).b(5, 1).a(0.25D)); + a(16, "night_vision", (new MobEffectList(false, 2039713)).b(4, 1).l()); + a(17, "hunger", (new MobEffectList(true, 5797459)).b(1, 1)); + a(18, "weakness", (new MobEffectAttackDamage(true, 4738376, -4.0D)).b(5, 0).a(GenericAttributes.ATTACK_DAMAGE, "22653B89-116E-49DC-9B6B-9971489B5BE5", 0.0D, 0)); + a(19, "poison", (new MobEffectList(true, 5149489)).b(6, 0).a(0.25D)); + a(20, "wither", (new MobEffectList(true, 3484199)).b(1, 2).a(0.25D)); + a(21, "health_boost", (new MobEffectHealthBoost(false, 16284963)).b(7, 2).a(GenericAttributes.maxHealth, "5D6F0BA2-1186-46AC-B896-C61C5CEE99CC", 4.0D, 0).l()); + a(22, "absorption", (new MobEffectAbsorption(false, 2445989)).b(2, 2).l()); + a(23, "saturation", (new InstantMobEffect(false, 16262179)).l()); + a(24, "glowing", (new MobEffectList(false, 9740385)).b(4, 2)); + a(25, "levitation", (new MobEffectList(true, 13565951)).b(3, 2)); + a(26, "luck", (new MobEffectList(false, 3381504)).b(5, 2).l().a(GenericAttributes.j, "03C3C89D-7037-4B42-869F-B146BCB64D2E", 1.0D, 0)); + a(27, "unluck", (new MobEffectList(true, 12624973)).b(6, 2).a(GenericAttributes.j, "CC5AF142-2BD2-4215-B636-2605AED11727", -1.0D, 0)); + a(28, "slow_falling", (new MobEffectList(false, 16773073)).b(8, 0).l()); + a(29, "conduit_power", (new MobEffectList(false, 1950417)).b(9, 0).l()); + a(30, "dolphins_grace", (new MobEffectList(false, 8954814)).b(10, 0).l()); + // CraftBukkit start + for (Object effect : IRegistry.MOB_EFFECT) { + org.bukkit.potion.PotionEffectType.registerPotionEffectType(new org.bukkit.craftbukkit.potion.CraftPotionEffectType((MobEffectList) effect)); + } + // CraftBukkit end + } + + private static void a(int i, String s, MobEffectList mobeffectlist) { + IRegistry.MOB_EFFECT.a(i, new MinecraftKey(s), mobeffectlist); + } +} diff --git a/src/main/java/net/minecraft/server/MobSpawnerAbstract.java b/src/main/java/net/minecraft/server/MobSpawnerAbstract.java new file mode 100644 index 000000000000..b2d2de7f8132 --- /dev/null +++ b/src/main/java/net/minecraft/server/MobSpawnerAbstract.java @@ -0,0 +1,284 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; + +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public abstract class MobSpawnerAbstract { + + private static final Logger a = LogManager.getLogger(); + public int spawnDelay = 20; + private final List mobs = Lists.newArrayList(); + private MobSpawnerData spawnData = new MobSpawnerData(); + private double e; + private double f; + public int minSpawnDelay = 200; + public int maxSpawnDelay = 800; + public int spawnCount = 4; + private Entity j; + public int maxNearbyEntities = 6; + public int requiredPlayerRange = 16; + public int spawnRange = 4; + private int tickDelay = 0; // Paper + + public MobSpawnerAbstract() {} + + @Nullable + public MinecraftKey getMobName() { + String s = this.spawnData.b().getString("id"); + + try { + return UtilColor.b(s) ? null : new MinecraftKey(s); + } catch (ResourceKeyInvalidException resourcekeyinvalidexception) { + BlockPosition blockposition = this.b(); + + MobSpawnerAbstract.a.warn("Invalid entity id '{}' at spawner {}:[{},{},{}]", s, this.a().worldProvider.getDimensionManager(), blockposition.getX(), blockposition.getY(), blockposition.getZ()); + return null; + } + } + + public void setMobName(EntityTypes entitytypes) { + this.spawnData.b().setString("id", IRegistry.ENTITY_TYPE.getKey(entitytypes).toString()); + this.mobs.clear(); // CraftBukkit - SPIGOT-3496, MC-92282 + } + + private boolean h() { + BlockPosition blockposition = this.b(); + + return this.a().b((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, (double) this.requiredPlayerRange); + } + + public void c() { + // Paper start - Configurable mob spawner tick rate + if (spawnDelay > 0 && --tickDelay > 0) return; + tickDelay = this.a().paperConfig.mobSpawnerTickRate; + // Paper end + if (!this.h()) { + this.f = this.e; + } else { + BlockPosition blockposition = this.b(); + + if (this.a().isClientSide) { + double d0 = (double) ((float) blockposition.getX() + this.a().random.nextFloat()); + double d1 = (double) ((float) blockposition.getY() + this.a().random.nextFloat()); + double d2 = (double) ((float) blockposition.getZ() + this.a().random.nextFloat()); + + this.a().addParticle(Particles.M, d0, d1, d2, 0.0D, 0.0D, 0.0D); + this.a().addParticle(Particles.y, d0, d1, d2, 0.0D, 0.0D, 0.0D); + if (this.spawnDelay > 0) { + this.spawnDelay -= tickDelay; // Paper + } + + this.f = this.e; + this.e = (this.e + (double) (1000.0F / ((float) this.spawnDelay + 200.0F))) % 360.0D; + } else { + if (this.spawnDelay < -tickDelay) { // Paper + this.i(); + } + + if (this.spawnDelay > 0) { + this.spawnDelay -= tickDelay; // Paper + return; + } + + boolean flag = false; + + for (int i = 0; i < this.spawnCount; ++i) { + NBTTagCompound nbttagcompound = this.spawnData.b(); + NBTTagList nbttaglist = nbttagcompound.getList("Pos", 6); + World world = this.a(); + int j = nbttaglist.size(); + double d3 = j >= 1 ? nbttaglist.k(0) : (double) blockposition.getX() + (world.random.nextDouble() - world.random.nextDouble()) * (double) this.spawnRange + 0.5D; + double d4 = j >= 2 ? nbttaglist.k(1) : (double) (blockposition.getY() + world.random.nextInt(3) - 1); + double d5 = j >= 3 ? nbttaglist.k(2) : (double) blockposition.getZ() + (world.random.nextDouble() - world.random.nextDouble()) * (double) this.spawnRange + 0.5D; + // Paper start + if (this.getMobName() == null) { + return; + } + String key = this.getMobName().getKey(); + org.bukkit.entity.EntityType type = org.bukkit.entity.EntityType.fromName(key); + if (type != null) { + com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent event; + event = new com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent( + MCUtil.toLocation(world, d3, d4, d5), + type, + MCUtil.toLocation(world, blockposition) + ); + if (!event.callEvent()) { + flag = true; + if (event.shouldAbortSpawn()) { + break; + } + continue; + } + } + // Paper end + Entity entity = ChunkRegionLoader.a(nbttagcompound, world, d3, d4, d5, false); + + if (entity == null) { + this.i(); + return; + } + + int k = world.a(entity.getClass(), (new AxisAlignedBB((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), (double) (blockposition.getX() + 1), (double) (blockposition.getY() + 1), (double) (blockposition.getZ() + 1))).g((double) this.spawnRange)).size(); + + if (k >= this.maxNearbyEntities) { + this.i(); + return; + } + + EntityInsentient entityinsentient = entity instanceof EntityInsentient ? (EntityInsentient) entity : null; + + entity.setPositionRotation(entity.locX, entity.locY, entity.locZ, world.random.nextFloat() * 360.0F, 0.0F); + if (entityinsentient == null || entityinsentient.a((GeneratorAccess) world, true) && entityinsentient.canSpawn()) { + if (this.spawnData.b().d() == 1 && this.spawnData.b().hasKeyOfType("id", 8) && entity instanceof EntityInsentient) { + ((EntityInsentient) entity).prepare(world.getDamageScaler(new BlockPosition(entity)), (GroupDataEntity) null, (NBTTagCompound) null); + } + entity.spawnedViaMobSpawner = true; // Paper + // Spigot Start + if ( entity.world.spigotConfig.nerfSpawnerMobs ) + { + entity.fromMobSpawner = true; + } + + flag = true; // Paper + + if (org.bukkit.craftbukkit.event.CraftEventFactory.callSpawnerSpawnEvent(entity, blockposition).isCancelled()) { + Entity vehicle = entity.getVehicle(); + if (vehicle != null) { + vehicle.dead = true; + } + for (Entity passenger : entity.getAllPassengers()) { + passenger.dead = true; + } + continue; + } + // Spigot End + ChunkRegionLoader.a(entity, (GeneratorAccess) world, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER); // CraftBukkit + world.triggerEffect(2004, blockposition, 0); + if (entityinsentient != null) { + entityinsentient.doSpawnEffect(); + } + + /*flag = true;*/ // Paper - moved up above cancellable event + } + } + + if (flag) { + this.i(); + } + } + + } + } + + private void i() { + if (this.maxSpawnDelay <= this.minSpawnDelay) { + this.spawnDelay = this.minSpawnDelay; + } else { + int i = this.maxSpawnDelay - this.minSpawnDelay; + + this.spawnDelay = this.minSpawnDelay + this.a().random.nextInt(i); + } + + if (!this.mobs.isEmpty()) { + this.a((MobSpawnerData) WeightedRandom.a(this.a().random, this.mobs)); + } + + this.a(1); + } + + public void a(NBTTagCompound nbttagcompound) { + this.spawnDelay = nbttagcompound.getShort("Delay"); + this.mobs.clear(); + if (nbttagcompound.hasKeyOfType("SpawnPotentials", 9)) { + NBTTagList nbttaglist = nbttagcompound.getList("SpawnPotentials", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + this.mobs.add(new MobSpawnerData(nbttaglist.getCompound(i))); + } + } + + if (nbttagcompound.hasKeyOfType("SpawnData", 10)) { + this.a(new MobSpawnerData(1, nbttagcompound.getCompound("SpawnData"))); + } else if (!this.mobs.isEmpty()) { + this.a((MobSpawnerData) WeightedRandom.a(this.a().random, this.mobs)); + } + + if (nbttagcompound.hasKeyOfType("MinSpawnDelay", 99)) { + this.minSpawnDelay = nbttagcompound.getShort("MinSpawnDelay"); + this.maxSpawnDelay = nbttagcompound.getShort("MaxSpawnDelay"); + this.spawnCount = nbttagcompound.getShort("SpawnCount"); + } + + if (nbttagcompound.hasKeyOfType("MaxNearbyEntities", 99)) { + this.maxNearbyEntities = nbttagcompound.getShort("MaxNearbyEntities"); + this.requiredPlayerRange = nbttagcompound.getShort("RequiredPlayerRange"); + } + + if (nbttagcompound.hasKeyOfType("SpawnRange", 99)) { + this.spawnRange = nbttagcompound.getShort("SpawnRange"); + } + + if (this.a() != null) { + this.j = null; + } + + } + + public NBTTagCompound b(NBTTagCompound nbttagcompound) { + MinecraftKey minecraftkey = this.getMobName(); + + if (minecraftkey == null) { + return nbttagcompound; + } else { + nbttagcompound.setShort("Delay", (short) this.spawnDelay); + nbttagcompound.setShort("MinSpawnDelay", (short) this.minSpawnDelay); + nbttagcompound.setShort("MaxSpawnDelay", (short) this.maxSpawnDelay); + nbttagcompound.setShort("SpawnCount", (short) this.spawnCount); + nbttagcompound.setShort("MaxNearbyEntities", (short) this.maxNearbyEntities); + nbttagcompound.setShort("RequiredPlayerRange", (short) this.requiredPlayerRange); + nbttagcompound.setShort("SpawnRange", (short) this.spawnRange); + nbttagcompound.set("SpawnData", this.spawnData.b().clone()); + NBTTagList nbttaglist = new NBTTagList(); + + if (this.mobs.isEmpty()) { + nbttaglist.add((NBTBase) this.spawnData.a()); + } else { + Iterator iterator = this.mobs.iterator(); + + while (iterator.hasNext()) { + MobSpawnerData mobspawnerdata = (MobSpawnerData) iterator.next(); + + nbttaglist.add((NBTBase) mobspawnerdata.a()); + } + } + + nbttagcompound.set("SpawnPotentials", nbttaglist); + return nbttagcompound; + } + } + + public boolean b(int i) { + if (i == 1 && this.a().isClientSide) { + this.spawnDelay = this.minSpawnDelay; + return true; + } else { + return false; + } + } + + public void a(MobSpawnerData mobspawnerdata) { + this.spawnData = mobspawnerdata; + } + + public abstract void a(int i); + + public abstract World a(); + + public abstract BlockPosition b(); +} diff --git a/src/main/java/net/minecraft/server/MobSpawnerPhantom.java b/src/main/java/net/minecraft/server/MobSpawnerPhantom.java new file mode 100644 index 000000000000..bb7e072ee18c --- /dev/null +++ b/src/main/java/net/minecraft/server/MobSpawnerPhantom.java @@ -0,0 +1,82 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.Random; + +public class MobSpawnerPhantom { + + private int a; + + public MobSpawnerPhantom() {} + + public int a(World world, boolean flag, boolean flag1) { + if (!flag) { + return 0; + } else { + Random random = world.random; + + --this.a; + if (this.a > 0) { + return 0; + } else { + this.a += (60 + random.nextInt(60)) * 20; + if (world.c() < 5 && world.worldProvider.g()) { + return 0; + } else { + int i = 0; + Iterator iterator = world.players.iterator(); + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + if (!entityhuman.isSpectator()) { + BlockPosition blockposition = new BlockPosition(entityhuman); + + if (!world.worldProvider.g() || blockposition.getY() >= world.getSeaLevel() && world.e(blockposition)) { + DifficultyDamageScaler difficultydamagescaler = world.getDamageScaler(blockposition); + + if (difficultydamagescaler.a(random.nextFloat() * 3.0F)) { + ServerStatisticManager serverstatisticmanager = ((EntityPlayer) entityhuman).getStatisticManager(); + int j = MathHelper.clamp(serverstatisticmanager.getStatisticValue(StatisticList.CUSTOM.b(StatisticList.TIME_SINCE_REST)), 1, Integer.MAX_VALUE); + boolean flag2 = true; + + if (random.nextInt(j) >= 72000) { + BlockPosition blockposition1 = blockposition.up(20 + random.nextInt(15)).east(-10 + random.nextInt(21)).south(-10 + random.nextInt(21)); + IBlockData iblockdata = world.getType(blockposition1); + Fluid fluid = world.getFluid(blockposition1); + + if (SpawnerCreature.a(iblockdata, fluid)) { + GroupDataEntity groupdataentity = null; + int k = 1 + random.nextInt(difficultydamagescaler.a().a() + 1); + + for (int l = 0; l < k; ++l) { + // Paper start + com.destroystokyo.paper.event.entity.PhantomPreSpawnEvent event = new com.destroystokyo.paper.event.entity.PhantomPreSpawnEvent(MCUtil.toLocation(world, blockposition1), ((EntityPlayer) entityhuman).getBukkitEntity(), org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); + if (!event.callEvent()) { + if (event.shouldAbortSpawn()) { + break; + } + continue; + } + // Paper end + EntityPhantom entityphantom = EntityTypes.PHANTOM.create(world); // Paper + entityphantom.spawningEntity = entityhuman.uniqueID; // Paper + entityphantom.setPositionRotation(blockposition1, 0.0F, 0.0F); + groupdataentity = entityphantom.prepare(difficultydamagescaler, groupdataentity, (NBTTagCompound) null); + world.addEntity(entityphantom, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); // CraftBukkit + } + + i += k; + } + } + } + } + } + } + + return i; + } + } + } + } +} diff --git a/src/main/java/net/minecraft/server/NBTBase.java b/src/main/java/net/minecraft/server/NBTBase.java new file mode 100644 index 000000000000..b2757aad80d8 --- /dev/null +++ b/src/main/java/net/minecraft/server/NBTBase.java @@ -0,0 +1,102 @@ +package net.minecraft.server; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +public interface NBTBase { + + String[] a = new String[] { "END", "BYTE", "SHORT", "INT", "LONG", "FLOAT", "DOUBLE", "BYTE[]", "STRING", "LIST", "COMPOUND", "INT[]", "LONG[]"}; + EnumChatFormat b = EnumChatFormat.AQUA; + EnumChatFormat c = EnumChatFormat.GREEN; + EnumChatFormat d = EnumChatFormat.GOLD; + EnumChatFormat e = EnumChatFormat.RED; + + void write(DataOutput dataoutput) throws IOException; + + void load(DataInput datainput, int i, NBTReadLimiter nbtreadlimiter) throws IOException; + + String toString(); + + byte getTypeId(); + + static NBTBase createTag(byte b0) { + switch (b0) { + case 0: + return new NBTTagEnd(); + case 1: + return new NBTTagByte(); + case 2: + return new NBTTagShort(); + case 3: + return new NBTTagInt(); + case 4: + return new NBTTagLong(); + case 5: + return new NBTTagFloat(); + case 6: + return new NBTTagDouble(); + case 7: + return new NBTTagByteArray(); + case 8: + return new NBTTagString(); + case 9: + return new NBTTagList(); + case 10: + return new NBTTagCompound(); + case 11: + return new NBTTagIntArray(); + case 12: + return new NBTTagLongArray(); + default: + return null; + } + } + + static String n(int i) { + switch (i) { + case 0: + return "TAG_End"; + case 1: + return "TAG_Byte"; + case 2: + return "TAG_Short"; + case 3: + return "TAG_Int"; + case 4: + return "TAG_Long"; + case 5: + return "TAG_Float"; + case 6: + return "TAG_Double"; + case 7: + return "TAG_Byte_Array"; + case 8: + return "TAG_String"; + case 9: + return "TAG_List"; + case 10: + return "TAG_Compound"; + case 11: + return "TAG_Int_Array"; + case 12: + return "TAG_Long_Array"; + case 99: + return "Any Numeric Tag"; + default: + return "UNKNOWN"; + } + } + + public NBTBase clone(); // Paper - decompile fix + + default String asString() { + return this.toString(); + } + + default IChatBaseComponent k() { + return this.a("", 0); + } + + IChatBaseComponent a(String s, int i); +} diff --git a/src/main/java/net/minecraft/server/NBTCompressedStreamTools.java b/src/main/java/net/minecraft/server/NBTCompressedStreamTools.java new file mode 100644 index 000000000000..e1f7e06ab21b --- /dev/null +++ b/src/main/java/net/minecraft/server/NBTCompressedStreamTools.java @@ -0,0 +1,97 @@ +package net.minecraft.server; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +public class NBTCompressedStreamTools { + + public static NBTTagCompound a(InputStream inputstream) throws IOException { + DataInputStream datainputstream = new DataInputStream(new BufferedInputStream(new GZIPInputStream(inputstream))); + + NBTTagCompound nbttagcompound; + + try { + nbttagcompound = a((DataInput) datainputstream, NBTReadLimiter.a); + } finally { + datainputstream.close(); + } + + return nbttagcompound; + } + + public static void a(NBTTagCompound nbttagcompound, OutputStream outputstream) throws IOException { + DataOutputStream dataoutputstream = new DataOutputStream(new BufferedOutputStream(new GZIPOutputStream(outputstream))); + + try { + a(nbttagcompound, (DataOutput) dataoutputstream); + } finally { + dataoutputstream.close(); + } + + } + + public static NBTTagCompound readNBT(DataInputStream datainputstream) throws IOException { return a(datainputstream); } // Paper - OBFHELPER + public static NBTTagCompound a(DataInputStream datainputstream) throws IOException { + return a((DataInput) datainputstream, NBTReadLimiter.a); + } + + public static NBTTagCompound a(DataInput datainput, NBTReadLimiter nbtreadlimiter) throws IOException { + // Spigot start + if ( datainput instanceof io.netty.buffer.ByteBufInputStream ) + { + datainput = new DataInputStream(new org.spigotmc.LimitStream((InputStream) datainput, nbtreadlimiter)); + } + // Spigot end + NBTBase nbtbase = a(datainput, 0, nbtreadlimiter); + + if (nbtbase instanceof NBTTagCompound) { + return (NBTTagCompound) nbtbase; + } else { + throw new IOException("Root tag must be a named compound tag"); + } + } + + public static void writeNBT(NBTTagCompound nbttagcompound, DataOutput dataoutput) throws IOException { a(nbttagcompound, dataoutput); } // Paper - OBFHELPER + public static void a(NBTTagCompound nbttagcompound, DataOutput dataoutput) throws IOException { + a((NBTBase) nbttagcompound, dataoutput); + } + + private static void a(NBTBase nbtbase, DataOutput dataoutput) throws IOException { + dataoutput.writeByte(nbtbase.getTypeId()); + if (nbtbase.getTypeId() != 0) { + dataoutput.writeUTF(""); + nbtbase.write(dataoutput); + } + } + + private static NBTBase a(DataInput datainput, int i, NBTReadLimiter nbtreadlimiter) throws IOException { + byte b0 = datainput.readByte(); + + if (b0 == 0) { + return new NBTTagEnd(); + } else { + datainput.readUTF(); + NBTBase nbtbase = NBTBase.createTag(b0); + + try { + nbtbase.load(datainput, i, nbtreadlimiter); + return nbtbase; + } catch (IOException ioexception) { + CrashReport crashreport = CrashReport.a(ioexception, "Loading NBT data"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("NBT Tag"); + + crashreportsystemdetails.a("Tag type", (Object) b0); + throw new ReportedException(crashreport); + } + } + } +} diff --git a/src/main/java/net/minecraft/server/NBTTagByteArray.java b/src/main/java/net/minecraft/server/NBTTagByteArray.java new file mode 100644 index 000000000000..e0fb6fb495a1 --- /dev/null +++ b/src/main/java/net/minecraft/server/NBTTagByteArray.java @@ -0,0 +1,121 @@ +package net.minecraft.server; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import org.apache.commons.lang3.ArrayUtils; + +public class NBTTagByteArray extends NBTList { + + private byte[] data; + + NBTTagByteArray() {} + + public NBTTagByteArray(byte[] abyte) { + this.data = abyte; + } + + public NBTTagByteArray(List list) { + this(a(list)); + } + + private static byte[] a(List list) { + byte[] abyte = new byte[list.size()]; + + for (int i = 0; i < list.size(); ++i) { + Byte obyte = (Byte) list.get(i); + + abyte[i] = obyte == null ? 0 : obyte; + } + + return abyte; + } + + public void write(DataOutput dataoutput) throws IOException { + dataoutput.writeInt(this.data.length); + dataoutput.write(this.data); + } + + public void load(DataInput datainput, int i, NBTReadLimiter nbtreadlimiter) throws IOException { + nbtreadlimiter.a(192L); + int j = datainput.readInt(); + com.google.common.base.Preconditions.checkArgument( j < 1 << 24); + + nbtreadlimiter.a((long) (8 * j)); + this.data = new byte[j]; + datainput.readFully(this.data); + } + + public byte getTypeId() { + return 7; + } + + public String toString() { + StringBuilder stringbuilder = new StringBuilder("[B;"); + + for (int i = 0; i < this.data.length; ++i) { + if (i != 0) { + stringbuilder.append(','); + } + + stringbuilder.append(this.data[i]).append('B'); + } + + return stringbuilder.append(']').toString(); + } + + @Override + public NBTTagByteArray clone() { // Paper - decompile fix + byte[] abyte = new byte[this.data.length]; + + System.arraycopy(this.data, 0, abyte, 0, this.data.length); + return new NBTTagByteArray(abyte); + } + + public boolean equals(Object object) { + return this == object ? true : object instanceof NBTTagByteArray && Arrays.equals(this.data, ((NBTTagByteArray) object).data); + } + + public int hashCode() { + return Arrays.hashCode(this.data); + } + + public IChatBaseComponent a(String s, int i) { + IChatBaseComponent ichatbasecomponent = (new ChatComponentText("B")).a(NBTTagByteArray.e); + IChatBaseComponent ichatbasecomponent1 = (new ChatComponentText("[")).addSibling(ichatbasecomponent).a(";"); + + for (int j = 0; j < this.data.length; ++j) { + IChatBaseComponent ichatbasecomponent2 = (new ChatComponentText(String.valueOf(this.data[j]))).a(NBTTagByteArray.d); + + ichatbasecomponent1.a(" ").addSibling(ichatbasecomponent2).addSibling(ichatbasecomponent); + if (j != this.data.length - 1) { + ichatbasecomponent1.a(","); + } + } + + ichatbasecomponent1.a("]"); + return ichatbasecomponent1; + } + + public byte[] c() { + return this.data; + } + + public int size() { + return this.data.length; + } + + public NBTTagByte c(int i) { + return new NBTTagByte(this.data[i]); + } + + public void a(int i, NBTBase nbtbase) { + this.data[i] = ((NBTNumber) nbtbase).asByte(); + } + + public void b(int i) { + this.data = ArrayUtils.remove(this.data, i); + } +} diff --git a/src/main/java/net/minecraft/server/NBTTagCompound.java b/src/main/java/net/minecraft/server/NBTTagCompound.java new file mode 100644 index 000000000000..d4165f7e440f --- /dev/null +++ b/src/main/java/net/minecraft/server/NBTTagCompound.java @@ -0,0 +1,496 @@ +package net.minecraft.server; + +import com.google.common.base.Strings; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +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.Set; +import java.util.UUID; +import java.util.regex.Pattern; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class NBTTagCompound implements NBTBase { + + private static final Logger f = LogManager.getLogger(); + private static final Pattern g = Pattern.compile("[A-Za-z0-9._+-]+"); + public final Map map = Maps.newHashMap(); // Paper + + public NBTTagCompound() {} + + public void write(DataOutput dataoutput) throws IOException { + Iterator iterator = this.map.keySet().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + NBTBase nbtbase = (NBTBase) this.map.get(s); + + a(s, nbtbase, dataoutput); + } + + dataoutput.writeByte(0); + } + + public void load(DataInput datainput, int i, NBTReadLimiter nbtreadlimiter) throws IOException { + nbtreadlimiter.a(384L); + if (i > 512) { + throw new RuntimeException("Tried to read NBT tag with too high complexity, depth > 512"); + } else { + this.map.clear(); + + byte b0; + + while ((b0 = a(datainput, nbtreadlimiter)) != 0) { + String s = b(datainput, nbtreadlimiter); + + nbtreadlimiter.a((long) (224 + 16 * s.length())); + NBTBase nbtbase = a(b0, s, datainput, i + 1, nbtreadlimiter); + + if (this.map.put(s, nbtbase) != null) { + nbtreadlimiter.a(288L); + } + } + + } + } + + public Set getKeys() { + return this.map.keySet(); + } + + public byte getTypeId() { + return 10; + } + + public int d() { + return this.map.size(); + } + + public void set(String s, NBTBase nbtbase) { + this.map.put(s, nbtbase); + } + + public void setByte(String s, byte b0) { + this.map.put(s, new NBTTagByte(b0)); + } + + public void setShort(String s, short short0) { + this.map.put(s, new NBTTagShort(short0)); + } + + public void setInt(String s, int i) { + this.map.put(s, new NBTTagInt(i)); + } + + public void setLong(String s, long i) { + this.map.put(s, new NBTTagLong(i)); + } + + public void setUUID(String prefix, UUID uuid) { a(prefix, uuid); } // Paper - OBFHELPER + public void a(String s, UUID uuid) { + this.setLong(s + "Most", uuid.getMostSignificantBits()); + this.setLong(s + "Least", uuid.getLeastSignificantBits()); + } + + public UUID getUUID(String prefix) { return a(prefix); } // Paper - OBFHELPER + @Nullable + public UUID a(String s) { + return new UUID(this.getLong(s + "Most"), this.getLong(s + "Least")); + } + + public boolean hasUUID(String s) { return b(s); } public boolean b(String s) { // Paper - OBFHELPER + return this.hasKeyOfType(s + "Most", 99) && this.hasKeyOfType(s + "Least", 99); + } + + public void setFloat(String s, float f) { + this.map.put(s, new NBTTagFloat(f)); + } + + public void setDouble(String s, double d0) { + this.map.put(s, new NBTTagDouble(d0)); + } + + public void setString(String s, String s1) { + this.map.put(s, new NBTTagString(s1)); + } + + public void setByteArray(String s, byte[] abyte) { + this.map.put(s, new NBTTagByteArray(abyte)); + } + + public void setIntArray(String s, int[] aint) { + this.map.put(s, new NBTTagIntArray(aint)); + } + + public void b(String s, List list) { + this.map.put(s, new NBTTagIntArray(list)); + } + + public void a(String s, long[] along) { + this.map.put(s, new NBTTagLongArray(along)); + } + + public void c(String s, List list) { + this.map.put(s, new NBTTagLongArray(list)); + } + + public void setBoolean(String s, boolean flag) { + this.setByte(s, (byte) (flag ? 1 : 0)); + } + + public NBTBase get(String s) { + return (NBTBase) this.map.get(s); + } + + public byte d(String s) { + NBTBase nbtbase = (NBTBase) this.map.get(s); + + return nbtbase == null ? 0 : nbtbase.getTypeId(); + } + + public boolean hasKey(String s) { + return this.map.containsKey(s); + } + + public boolean hasKeyOfType(String s, int i) { + byte b0 = this.d(s); + + return b0 == i ? true : (i != 99 ? false : b0 == 1 || b0 == 2 || b0 == 3 || b0 == 4 || b0 == 5 || b0 == 6); + } + + public byte getByte(String s) { + try { + if (this.hasKeyOfType(s, 99)) { + return ((NBTNumber) this.map.get(s)).asByte(); + } + } catch (ClassCastException classcastexception) { + ; + } + + return 0; + } + + public short getShort(String s) { + try { + if (this.hasKeyOfType(s, 99)) { + return ((NBTNumber) this.map.get(s)).asShort(); + } + } catch (ClassCastException classcastexception) { + ; + } + + return 0; + } + + public int getInt(String s) { + try { + if (this.hasKeyOfType(s, 99)) { + return ((NBTNumber) this.map.get(s)).asInt(); + } + } catch (ClassCastException classcastexception) { + ; + } + + return 0; + } + + public long getLong(String s) { + try { + if (this.hasKeyOfType(s, 99)) { + return ((NBTNumber) this.map.get(s)).asLong(); + } + } catch (ClassCastException classcastexception) { + ; + } + + return 0L; + } + + public float getFloat(String s) { + try { + if (this.hasKeyOfType(s, 99)) { + return ((NBTNumber) this.map.get(s)).asFloat(); + } + } catch (ClassCastException classcastexception) { + ; + } + + return 0.0F; + } + + public double getDouble(String s) { + try { + if (this.hasKeyOfType(s, 99)) { + return ((NBTNumber) this.map.get(s)).asDouble(); + } + } catch (ClassCastException classcastexception) { + ; + } + + return 0.0D; + } + + public String getString(String s) { + try { + if (this.hasKeyOfType(s, 8)) { + return ((NBTBase) this.map.get(s)).asString(); + } + } catch (ClassCastException classcastexception) { + ; + } + + return ""; + } + + public byte[] getByteArray(String s) { + try { + if (this.hasKeyOfType(s, 7)) { + return ((NBTTagByteArray) this.map.get(s)).c(); + } + } catch (ClassCastException classcastexception) { + throw new ReportedException(this.a(s, 7, classcastexception)); + } + + return new byte[0]; + } + + public int[] getIntArray(String s) { + try { + if (this.hasKeyOfType(s, 11)) { + return ((NBTTagIntArray) this.map.get(s)).d(); + } + } catch (ClassCastException classcastexception) { + throw new ReportedException(this.a(s, 11, classcastexception)); + } + + return new int[0]; + } + + public long[] o(String s) { + try { + if (this.hasKeyOfType(s, 12)) { + return ((NBTTagLongArray) this.map.get(s)).d(); + } + } catch (ClassCastException classcastexception) { + throw new ReportedException(this.a(s, 12, classcastexception)); + } + + return new long[0]; + } + + public NBTTagCompound getCompound(String s) { + try { + if (this.hasKeyOfType(s, 10)) { + return (NBTTagCompound) this.map.get(s); + } + } catch (ClassCastException classcastexception) { + throw new ReportedException(this.a(s, 10, classcastexception)); + } + + return new NBTTagCompound(); + } + + public NBTTagList getList(String s, int i) { + try { + if (this.d(s) == 9) { + NBTTagList nbttaglist = (NBTTagList) this.map.get(s); + + if (!nbttaglist.isEmpty() && nbttaglist.d() != i) { + return new NBTTagList(); + } + + return nbttaglist; + } + } catch (ClassCastException classcastexception) { + throw new ReportedException(this.a(s, 9, classcastexception)); + } + + return new NBTTagList(); + } + + public boolean getBoolean(String s) { + return this.getByte(s) != 0; + } + + public void remove(String s) { + this.map.remove(s); + } + + public String toString() { + StringBuilder stringbuilder = new StringBuilder("{"); + Collection collection = this.map.keySet(); + + if (NBTTagCompound.f.isDebugEnabled()) { + List list = Lists.newArrayList(this.map.keySet()); + + Collections.sort(list); + collection = list; + } + + String s; + + for (Iterator iterator = ((Collection) collection).iterator(); iterator.hasNext(); stringbuilder.append(s(s)).append(':').append(this.map.get(s))) { + s = (String) iterator.next(); + if (stringbuilder.length() != 1) { + stringbuilder.append(','); + } + } + + return stringbuilder.append('}').toString(); + } + + public boolean isEmpty() { + return this.map.isEmpty(); + } + + private CrashReport a(String s, int i, ClassCastException classcastexception) { + CrashReport crashreport = CrashReport.a(classcastexception, "Reading NBT data"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Corrupt NBT tag", 1); + + crashreportsystemdetails.a("Tag type found", () -> { + return NBTTagCompound.a[((NBTBase) this.map.get(s)).getTypeId()]; + }); + crashreportsystemdetails.a("Tag type expected", () -> { + return NBTTagCompound.a[i]; + }); + crashreportsystemdetails.a("Tag name", (Object) s); + return crashreport; + } + + public NBTTagCompound clone() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + Iterator iterator = this.map.keySet().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + nbttagcompound.set(s, ((NBTBase) this.map.get(s)).clone()); + } + + return nbttagcompound; + } + + public boolean equals(Object object) { + return this == object ? true : object instanceof NBTTagCompound && Objects.equals(this.map, ((NBTTagCompound) object).map); + } + + public int hashCode() { + return this.map.hashCode(); + } + + private static void a(String s, NBTBase nbtbase, DataOutput dataoutput) throws IOException { + dataoutput.writeByte(nbtbase.getTypeId()); + if (nbtbase.getTypeId() != 0) { + dataoutput.writeUTF(s); + nbtbase.write(dataoutput); + } + } + + private static byte a(DataInput datainput, NBTReadLimiter nbtreadlimiter) throws IOException { + return datainput.readByte(); + } + + private static String b(DataInput datainput, NBTReadLimiter nbtreadlimiter) throws IOException { + return datainput.readUTF(); + } + + static NBTBase a(byte b0, String s, DataInput datainput, int i, NBTReadLimiter nbtreadlimiter) throws IOException { + NBTBase nbtbase = NBTBase.createTag(b0); + + try { + nbtbase.load(datainput, i, nbtreadlimiter); + return nbtbase; + } catch (IOException ioexception) { + CrashReport crashreport = CrashReport.a(ioexception, "Loading NBT data"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("NBT Tag"); + + crashreportsystemdetails.a("Tag name", (Object) s); + crashreportsystemdetails.a("Tag type", (Object) b0); + throw new ReportedException(crashreport); + } + } + + public NBTTagCompound a(NBTTagCompound nbttagcompound) { + Iterator iterator = nbttagcompound.map.keySet().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + NBTBase nbtbase = (NBTBase) nbttagcompound.map.get(s); + + if (nbtbase.getTypeId() == 10) { + if (this.hasKeyOfType(s, 10)) { + NBTTagCompound nbttagcompound1 = this.getCompound(s); + + nbttagcompound1.a((NBTTagCompound) nbtbase); + } else { + this.set(s, nbtbase.clone()); + } + } else { + this.set(s, nbtbase.clone()); + } + } + + return this; + } + + protected static String s(String s) { + return NBTTagCompound.g.matcher(s).matches() ? s : NBTTagString.a(s, true); + } + + protected static IChatBaseComponent t(String s) { + if (NBTTagCompound.g.matcher(s).matches()) { + return (new ChatComponentText(s)).a(NBTTagCompound.b); + } else { + IChatBaseComponent ichatbasecomponent = (new ChatComponentText(NBTTagString.a(s, false))).a(NBTTagCompound.b); + + return (new ChatComponentText("\"")).addSibling(ichatbasecomponent).a("\""); + } + } + + public IChatBaseComponent a(String s, int i) { + if (this.map.isEmpty()) { + return new ChatComponentText("{}"); + } else { + ChatComponentText chatcomponenttext = new ChatComponentText("{"); + Collection collection = this.map.keySet(); + + if (NBTTagCompound.f.isDebugEnabled()) { + List list = Lists.newArrayList(this.map.keySet()); + + Collections.sort(list); + collection = list; + } + + if (!s.isEmpty()) { + chatcomponenttext.a("\n"); + } + + IChatBaseComponent ichatbasecomponent; + + for (Iterator iterator = ((Collection) collection).iterator(); iterator.hasNext(); chatcomponenttext.addSibling(ichatbasecomponent)) { + String s1 = (String) iterator.next(); + + ichatbasecomponent = (new ChatComponentText(Strings.repeat(s, i + 1))).addSibling(t(s1)).a(String.valueOf(':')).a(" ").addSibling(((NBTBase) this.map.get(s1)).a(s, i + 1)); + if (iterator.hasNext()) { + ichatbasecomponent.a(String.valueOf(',')).a(s.isEmpty() ? " " : "\n"); + } + } + + if (!s.isEmpty()) { + chatcomponenttext.a("\n").a(Strings.repeat(s, i)); + } + + chatcomponenttext.a("}"); + return chatcomponenttext; + } + } +} diff --git a/src/main/java/net/minecraft/server/NBTTagIntArray.java b/src/main/java/net/minecraft/server/NBTTagIntArray.java new file mode 100644 index 000000000000..2652a28e17b0 --- /dev/null +++ b/src/main/java/net/minecraft/server/NBTTagIntArray.java @@ -0,0 +1,130 @@ +package net.minecraft.server; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import org.apache.commons.lang3.ArrayUtils; + +public class NBTTagIntArray extends NBTList { + + private int[] data; + + NBTTagIntArray() {} + + public NBTTagIntArray(int[] aint) { + this.data = aint; + } + + public NBTTagIntArray(List list) { + this(a(list)); + } + + private static int[] a(List list) { + int[] aint = new int[list.size()]; + + for (int i = 0; i < list.size(); ++i) { + Integer integer = (Integer) list.get(i); + + aint[i] = integer == null ? 0 : integer; + } + + return aint; + } + + public void write(DataOutput dataoutput) throws IOException { + dataoutput.writeInt(this.data.length); + int[] aint = this.data; + int i = aint.length; + + for (int j = 0; j < i; ++j) { + int k = aint[j]; + + dataoutput.writeInt(k); + } + + } + + public void load(DataInput datainput, int i, NBTReadLimiter nbtreadlimiter) throws IOException { + nbtreadlimiter.a(192L); + int j = datainput.readInt(); + com.google.common.base.Preconditions.checkArgument( j < 1 << 24); + + nbtreadlimiter.a((long) (32 * j)); + this.data = new int[j]; + + for (int k = 0; k < j; ++k) { + this.data[k] = datainput.readInt(); + } + + } + + public byte getTypeId() { + return 11; + } + + public String toString() { + StringBuilder stringbuilder = new StringBuilder("[I;"); + + for (int i = 0; i < this.data.length; ++i) { + if (i != 0) { + stringbuilder.append(','); + } + + stringbuilder.append(this.data[i]); + } + + return stringbuilder.append(']').toString(); + } + + public NBTTagIntArray clone() { + int[] aint = new int[this.data.length]; + + System.arraycopy(this.data, 0, aint, 0, this.data.length); + return new NBTTagIntArray(aint); + } + + public boolean equals(Object object) { + return this == object ? true : object instanceof NBTTagIntArray && Arrays.equals(this.data, ((NBTTagIntArray) object).data); + } + + public int hashCode() { + return Arrays.hashCode(this.data); + } + + public int[] d() { + return this.data; + } + + public IChatBaseComponent a(String s, int i) { + IChatBaseComponent ichatbasecomponent = (new ChatComponentText("I")).a(NBTTagIntArray.e); + IChatBaseComponent ichatbasecomponent1 = (new ChatComponentText("[")).addSibling(ichatbasecomponent).a(";"); + + for (int j = 0; j < this.data.length; ++j) { + ichatbasecomponent1.a(" ").addSibling((new ChatComponentText(String.valueOf(this.data[j]))).a(NBTTagIntArray.d)); + if (j != this.data.length - 1) { + ichatbasecomponent1.a(","); + } + } + + ichatbasecomponent1.a("]"); + return ichatbasecomponent1; + } + + public int size() { + return this.data.length; + } + + public NBTTagInt c(int i) { + return new NBTTagInt(this.data[i]); + } + + public void a(int i, NBTBase nbtbase) { + this.data[i] = ((NBTNumber) nbtbase).asInt(); + } + + public void b(int i) { + this.data = ArrayUtils.remove(this.data, i); + } +} diff --git a/src/main/java/net/minecraft/server/NBTTagList.java b/src/main/java/net/minecraft/server/NBTTagList.java new file mode 100644 index 000000000000..22027321bdd0 --- /dev/null +++ b/src/main/java/net/minecraft/server/NBTTagList.java @@ -0,0 +1,297 @@ +package net.minecraft.server; + +import com.google.common.base.Strings; +import com.google.common.collect.Lists; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class NBTTagList extends NBTList { + + private static final Logger f = LogManager.getLogger(); + public List list = Lists.newArrayList(); // Paper + private byte type = 0; + + public NBTTagList() {} + + public void write(DataOutput dataoutput) throws IOException { + if (this.list.isEmpty()) { + this.type = 0; + } else { + this.type = ((NBTBase) this.list.get(0)).getTypeId(); + } + + dataoutput.writeByte(this.type); + dataoutput.writeInt(this.list.size()); + + for (int i = 0; i < this.list.size(); ++i) { + ((NBTBase) this.list.get(i)).write(dataoutput); + } + + } + + public void load(DataInput datainput, int i, NBTReadLimiter nbtreadlimiter) throws IOException { + nbtreadlimiter.a(296L); + if (i > 512) { + throw new RuntimeException("Tried to read NBT tag with too high complexity, depth > 512"); + } else { + this.type = datainput.readByte(); + int j = datainput.readInt(); + + if (this.type == 0 && j > 0) { + throw new RuntimeException("Missing type on ListTag"); + } else { + nbtreadlimiter.a(32L * (long) j); + this.list = Lists.newArrayListWithCapacity(j); + + for (int k = 0; k < j; ++k) { + NBTBase nbtbase = NBTBase.createTag(this.type); + + nbtbase.load(datainput, i + 1, nbtreadlimiter); + this.list.add(nbtbase); + } + + } + } + } + + public byte getTypeId() { + return 9; + } + + public String toString() { + StringBuilder stringbuilder = new StringBuilder("["); + + for (int i = 0; i < this.list.size(); ++i) { + if (i != 0) { + stringbuilder.append(','); + } + + stringbuilder.append(this.list.get(i)); + } + + return stringbuilder.append(']').toString(); + } + + public boolean add(NBTBase nbtbase) { + if (nbtbase.getTypeId() == 0) { + NBTTagList.f.warn("Invalid TagEnd added to ListTag"); + return false; + } else { + if (this.type == 0) { + this.type = nbtbase.getTypeId(); + } else if (this.type != nbtbase.getTypeId()) { + NBTTagList.f.warn("Adding mismatching tag types to tag list"); + return false; + } + + this.list.add(nbtbase); + return true; + } + } + + public NBTBase set(int i, NBTBase nbtbase) { + if (nbtbase.getTypeId() == 0) { + NBTTagList.f.warn("Invalid TagEnd added to ListTag"); + return (NBTBase) this.list.get(i); + } else if (i >= 0 && i < this.list.size()) { + if (this.type == 0) { + this.type = nbtbase.getTypeId(); + } else if (this.type != nbtbase.getTypeId()) { + NBTTagList.f.warn("Adding mismatching tag types to tag list"); + return (NBTBase) this.list.get(i); + } + + return (NBTBase) this.list.set(i, nbtbase); + } else { + NBTTagList.f.warn("index out of bounds to set tag in tag list"); + return null; + } + } + + public NBTBase remove(int i) { + return (NBTBase) this.list.remove(i); + } + + public boolean isEmpty() { + return this.list.isEmpty(); + } + + public NBTTagCompound getCompound(int i) { + if (i >= 0 && i < this.list.size()) { + NBTBase nbtbase = (NBTBase) this.list.get(i); + + if (nbtbase.getTypeId() == 10) { + return (NBTTagCompound) nbtbase; + } + } + + return new NBTTagCompound(); + } + + public NBTTagList f(int i) { + if (i >= 0 && i < this.list.size()) { + NBTBase nbtbase = (NBTBase) this.list.get(i); + + if (nbtbase.getTypeId() == 9) { + return (NBTTagList) nbtbase; + } + } + + return new NBTTagList(); + } + + public short g(int i) { + if (i >= 0 && i < this.list.size()) { + NBTBase nbtbase = (NBTBase) this.list.get(i); + + if (nbtbase.getTypeId() == 2) { + return ((NBTTagShort) nbtbase).asShort(); + } + } + + return 0; + } + + public int h(int i) { + if (i >= 0 && i < this.list.size()) { + NBTBase nbtbase = (NBTBase) this.list.get(i); + + if (nbtbase.getTypeId() == 3) { + return ((NBTTagInt) nbtbase).asInt(); + } + } + + return 0; + } + + public int[] i(int i) { + if (i >= 0 && i < this.list.size()) { + NBTBase nbtbase = (NBTBase) this.list.get(i); + + if (nbtbase.getTypeId() == 11) { + return ((NBTTagIntArray) nbtbase).d(); + } + } + + return new int[0]; + } + + public final double getDoubleAt(int i) { return this.k(i); } // Paper - OBFHELPER + public double k(int i) { + if (i >= 0 && i < this.list.size()) { + NBTBase nbtbase = (NBTBase) this.list.get(i); + + if (nbtbase.getTypeId() == 6) { + return ((NBTTagDouble) nbtbase).asDouble(); + } + } + + return 0.0D; + } + + public float l(int i) { + if (i >= 0 && i < this.list.size()) { + NBTBase nbtbase = (NBTBase) this.list.get(i); + + if (nbtbase.getTypeId() == 5) { + return ((NBTTagFloat) nbtbase).asFloat(); + } + } + + return 0.0F; + } + + public String getString(int i) { + if (i >= 0 && i < this.list.size()) { + NBTBase nbtbase = (NBTBase) this.list.get(i); + + return nbtbase.getTypeId() == 8 ? nbtbase.asString() : nbtbase.toString(); + } else { + return ""; + } + } + + public NBTBase get(int i) { + return (NBTBase) (i >= 0 && i < this.list.size() ? (NBTBase) this.list.get(i) : new NBTTagEnd()); + } + + public int size() { + return this.list.size(); + } + + public NBTBase c(int i) { + return (NBTBase) this.list.get(i); + } + + public void a(int i, NBTBase nbtbase) { + this.list.set(i, nbtbase); + } + + public void b(int i) { + this.list.remove(i); + } + + public NBTTagList clone() { + NBTTagList nbttaglist = new NBTTagList(); + + nbttaglist.type = this.type; + Iterator iterator = this.list.iterator(); + + while (iterator.hasNext()) { + NBTBase nbtbase = (NBTBase) iterator.next(); + NBTBase nbtbase1 = nbtbase.clone(); + + nbttaglist.list.add(nbtbase1); + } + + return nbttaglist; + } + + public boolean equals(Object object) { + return this == object ? true : object instanceof NBTTagList && Objects.equals(this.list, ((NBTTagList) object).list); + } + + public int hashCode() { + return this.list.hashCode(); + } + + public IChatBaseComponent a(String s, int i) { + if (this.isEmpty()) { + return new ChatComponentText("[]"); + } else { + ChatComponentText chatcomponenttext = new ChatComponentText("["); + + if (!s.isEmpty()) { + chatcomponenttext.a("\n"); + } + + for (int j = 0; j < this.list.size(); ++j) { + ChatComponentText chatcomponenttext1 = new ChatComponentText(Strings.repeat(s, i + 1)); + + chatcomponenttext1.addSibling(((NBTBase) this.list.get(j)).a(s, i + 1)); + if (j != this.list.size() - 1) { + chatcomponenttext1.a(String.valueOf(',')).a(s.isEmpty() ? " " : "\n"); + } + + chatcomponenttext.addSibling(chatcomponenttext1); + } + + if (!s.isEmpty()) { + chatcomponenttext.a("\n").a(Strings.repeat(s, i)); + } + + chatcomponenttext.a("]"); + return chatcomponenttext; + } + } + + public int d() { + return this.type; + } +} diff --git a/src/main/java/net/minecraft/server/NameReferencingFileConverter.java b/src/main/java/net/minecraft/server/NameReferencingFileConverter.java new file mode 100644 index 000000000000..dcaba3c401e9 --- /dev/null +++ b/src/main/java/net/minecraft/server/NameReferencingFileConverter.java @@ -0,0 +1,532 @@ +package net.minecraft.server; + +import com.destroystokyo.paper.exception.ServerInternalException; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.io.Files; +import com.mojang.authlib.Agent; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.ProfileLookupCallback; +import com.mojang.authlib.yggdrasil.ProfileNotFoundException; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class NameReferencingFileConverter { + + private static final Logger e = LogManager.getLogger(); + public static final File a = new File("banned-ips.txt"); + public static final File b = new File("banned-players.txt"); + public static final File c = new File("ops.txt"); + public static final File d = new File("white-list.txt"); + + static List a(File file, Map map) throws IOException { + List list = Files.readLines(file, StandardCharsets.UTF_8); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + s = s.trim(); + if (!s.startsWith("#") && s.length() >= 1) { + String[] astring = s.split("\\|"); + + map.put(astring[0].toLowerCase(Locale.ROOT), astring); + } + } + + return list; + } + + private static void a(MinecraftServer minecraftserver, Collection collection, ProfileLookupCallback profilelookupcallback) { + String[] astring = (String[]) collection.stream().filter((s) -> { + return !UtilColor.b(s); + }).toArray((i) -> { + return new String[i]; + }); + + if (minecraftserver.getOnlineMode() + || (com.destroystokyo.paper.PaperConfig.isProxyOnlineMode())) { // Spigot: bungee = online mode, for now. // Paper - Handle via setting + minecraftserver.getGameProfileRepository().findProfilesByNames(astring, Agent.MINECRAFT, profilelookupcallback); + } else { + String[] astring1 = astring; + int i = astring.length; + + for (int j = 0; j < i; ++j) { + String s = astring1[j]; + UUID uuid = EntityHuman.a(new GameProfile((UUID) null, s)); + GameProfile gameprofile = new GameProfile(uuid, s); + + profilelookupcallback.onProfileLookupSucceeded(gameprofile); + } + } + + } + + public static boolean a(final MinecraftServer minecraftserver) { + final GameProfileBanList gameprofilebanlist = new GameProfileBanList(PlayerList.a); + + if (NameReferencingFileConverter.b.exists() && NameReferencingFileConverter.b.isFile()) { + if (gameprofilebanlist.c().exists()) { + try { + gameprofilebanlist.load(); + // CraftBukkit start - FileNotFoundException -> IOException, don't print stacktrace + } catch (IOException filenotfoundexception) { + NameReferencingFileConverter.e.warn("Could not load existing file {}", gameprofilebanlist.c().getName()); + } + } + + try { + final Map map = Maps.newHashMap(); + + a(NameReferencingFileConverter.b, (Map) map); + ProfileLookupCallback profilelookupcallback = new ProfileLookupCallback() { + public void onProfileLookupSucceeded(GameProfile gameprofile) { + minecraftserver.getUserCache().a(gameprofile); + String[] astring = (String[]) map.get(gameprofile.getName().toLowerCase(Locale.ROOT)); + + if (astring == null) { + NameReferencingFileConverter.e.warn("Could not convert user banlist entry for {}", gameprofile.getName()); + throw new NameReferencingFileConverter.FileConversionException("Profile not in the conversionlist"); + } else { + Date date = astring.length > 1 ? NameReferencingFileConverter.b(astring[1], (Date) null) : null; + String s = astring.length > 2 ? astring[2] : null; + Date date1 = astring.length > 3 ? NameReferencingFileConverter.b(astring[3], (Date) null) : null; + String s1 = astring.length > 4 ? astring[4] : null; + + gameprofilebanlist.add(new GameProfileBanEntry(gameprofile, date, s, date1, s1)); + } + } + + public void onProfileLookupFailed(GameProfile gameprofile, Exception exception) { + NameReferencingFileConverter.e.warn("Could not lookup user banlist entry for {}", gameprofile.getName(), exception); + if (!(exception instanceof ProfileNotFoundException)) { + throw new NameReferencingFileConverter.FileConversionException("Could not request user " + gameprofile.getName() + " from backend systems", exception); + } + } + }; + + a(minecraftserver, map.keySet(), profilelookupcallback); + gameprofilebanlist.save(); + c(NameReferencingFileConverter.b); + return true; + } catch (IOException ioexception) { + NameReferencingFileConverter.e.warn("Could not read old user banlist to convert it!", ioexception); + return false; + } catch (NameReferencingFileConverter.FileConversionException namereferencingfileconverter_fileconversionexception) { + NameReferencingFileConverter.e.error("Conversion failed, please try again later", namereferencingfileconverter_fileconversionexception); + return false; + } + } else { + return true; + } + } + + public static boolean b(MinecraftServer minecraftserver) { + IpBanList ipbanlist = new IpBanList(PlayerList.b); + + if (NameReferencingFileConverter.a.exists() && NameReferencingFileConverter.a.isFile()) { + if (ipbanlist.c().exists()) { + try { + ipbanlist.load(); + // CraftBukkit start - FileNotFoundException -> IOException, don't print stacktrace + } catch (IOException filenotfoundexception) { + NameReferencingFileConverter.e.warn("Could not load existing file {}", ipbanlist.c().getName()); + } + } + + try { + Map map = Maps.newHashMap(); + + a(NameReferencingFileConverter.a, (Map) map); + Iterator iterator = map.keySet().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + String[] astring = (String[]) map.get(s); + Date date = astring.length > 1 ? b(astring[1], (Date) null) : null; + String s1 = astring.length > 2 ? astring[2] : null; + Date date1 = astring.length > 3 ? b(astring[3], (Date) null) : null; + String s2 = astring.length > 4 ? astring[4] : null; + + ipbanlist.add(new IpBanEntry(s, date, s1, date1, s2)); + } + + ipbanlist.save(); + c(NameReferencingFileConverter.a); + return true; + } catch (IOException ioexception) { + NameReferencingFileConverter.e.warn("Could not parse old ip banlist to convert it!", ioexception); + return false; + } + } else { + return true; + } + } + + public static boolean c(final MinecraftServer minecraftserver) { + final OpList oplist = new OpList(PlayerList.c); + + if (NameReferencingFileConverter.c.exists() && NameReferencingFileConverter.c.isFile()) { + if (oplist.c().exists()) { + try { + oplist.load(); + // CraftBukkit start - FileNotFoundException -> IOException, don't print stacktrace + } catch (IOException filenotfoundexception) { + NameReferencingFileConverter.e.warn("Could not load existing file {}", oplist.c().getName()); + } + } + + try { + List list = Files.readLines(NameReferencingFileConverter.c, StandardCharsets.UTF_8); + ProfileLookupCallback profilelookupcallback = new ProfileLookupCallback() { + public void onProfileLookupSucceeded(GameProfile gameprofile) { + minecraftserver.getUserCache().a(gameprofile); + oplist.add(new OpListEntry(gameprofile, minecraftserver.j(), false)); + } + + public void onProfileLookupFailed(GameProfile gameprofile, Exception exception) { + NameReferencingFileConverter.e.warn("Could not lookup oplist entry for {}", gameprofile.getName(), exception); + if (!(exception instanceof ProfileNotFoundException)) { + throw new NameReferencingFileConverter.FileConversionException("Could not request user " + gameprofile.getName() + " from backend systems", exception); + } + } + }; + + a(minecraftserver, list, profilelookupcallback); + oplist.save(); + c(NameReferencingFileConverter.c); + return true; + } catch (IOException ioexception) { + NameReferencingFileConverter.e.warn("Could not read old oplist to convert it!", ioexception); + return false; + } catch (NameReferencingFileConverter.FileConversionException namereferencingfileconverter_fileconversionexception) { + NameReferencingFileConverter.e.error("Conversion failed, please try again later", namereferencingfileconverter_fileconversionexception); + return false; + } + } else { + return true; + } + } + + public static boolean d(final MinecraftServer minecraftserver) { + final WhiteList whitelist = new WhiteList(PlayerList.d); + + if (NameReferencingFileConverter.d.exists() && NameReferencingFileConverter.d.isFile()) { + if (whitelist.c().exists()) { + try { + whitelist.load(); + // CraftBukkit start - FileNotFoundException -> IOException, don't print stacktrace + } catch (IOException filenotfoundexception) { + NameReferencingFileConverter.e.warn("Could not load existing file {}", whitelist.c().getName()); + } + } + + try { + List list = Files.readLines(NameReferencingFileConverter.d, StandardCharsets.UTF_8); + ProfileLookupCallback profilelookupcallback = new ProfileLookupCallback() { + public void onProfileLookupSucceeded(GameProfile gameprofile) { + minecraftserver.getUserCache().a(gameprofile); + whitelist.add(new WhiteListEntry(gameprofile)); + } + + public void onProfileLookupFailed(GameProfile gameprofile, Exception exception) { + NameReferencingFileConverter.e.warn("Could not lookup user whitelist entry for {}", gameprofile.getName(), exception); + if (!(exception instanceof ProfileNotFoundException)) { + throw new NameReferencingFileConverter.FileConversionException("Could not request user " + gameprofile.getName() + " from backend systems", exception); + } + } + }; + + a(minecraftserver, list, profilelookupcallback); + whitelist.save(); + c(NameReferencingFileConverter.d); + return true; + } catch (IOException ioexception) { + NameReferencingFileConverter.e.warn("Could not read old whitelist to convert it!", ioexception); + return false; + } catch (NameReferencingFileConverter.FileConversionException namereferencingfileconverter_fileconversionexception) { + NameReferencingFileConverter.e.error("Conversion failed, please try again later", namereferencingfileconverter_fileconversionexception); + return false; + } + } else { + return true; + } + } + + public static String a(final MinecraftServer minecraftserver, String s) { + if (!UtilColor.b(s) && s.length() <= 16) { + GameProfile gameprofile = minecraftserver.getUserCache().getProfile(s); + + if (gameprofile != null && gameprofile.getId() != null) { + return gameprofile.getId().toString(); + } else if (!minecraftserver.H() && minecraftserver.getOnlineMode()) { + final List list = Lists.newArrayList(); + ProfileLookupCallback profilelookupcallback = new ProfileLookupCallback() { + public void onProfileLookupSucceeded(GameProfile gameprofile1) { + minecraftserver.getUserCache().a(gameprofile1); + list.add(gameprofile1); + } + + public void onProfileLookupFailed(GameProfile gameprofile1, Exception exception) { + NameReferencingFileConverter.e.warn("Could not lookup user whitelist entry for {}", gameprofile1.getName(), exception); + } + }; + + a(minecraftserver, Lists.newArrayList(new String[] { s}), profilelookupcallback); + return !list.isEmpty() && ((GameProfile) list.get(0)).getId() != null ? ((GameProfile) list.get(0)).getId().toString() : ""; + } else { + return EntityHuman.a(new GameProfile((UUID) null, s)).toString(); + } + } else { + return s; + } + } + + public static boolean a(final DedicatedServer dedicatedserver, PropertyManager propertymanager) { + final File file = d(propertymanager); + final File file1 = new File(file.getParentFile(), "playerdata"); + final File file2 = new File(file.getParentFile(), "unknownplayers"); + + if (file.exists() && file.isDirectory()) { + File[] afile = file.listFiles(); + List list = Lists.newArrayList(); + File[] afile1 = afile; + int i = afile.length; + + for (int j = 0; j < i; ++j) { + File file3 = afile1[j]; + String s = file3.getName(); + + if (s.toLowerCase(Locale.ROOT).endsWith(".dat")) { + String s1 = s.substring(0, s.length() - ".dat".length()); + + if (!s1.isEmpty()) { + list.add(s1); + } + } + } + + try { + final String[] astring = (String[]) list.toArray(new String[list.size()]); + ProfileLookupCallback profilelookupcallback = new ProfileLookupCallback() { + public void onProfileLookupSucceeded(GameProfile gameprofile) { + dedicatedserver.getUserCache().a(gameprofile); + UUID uuid = gameprofile.getId(); + + if (uuid == null) { + throw new NameReferencingFileConverter.FileConversionException("Missing UUID for user profile " + gameprofile.getName()); + } else { + this.a(file1, this.a(gameprofile), uuid.toString()); + } + } + + public void onProfileLookupFailed(GameProfile gameprofile, Exception exception) { + NameReferencingFileConverter.e.warn("Could not lookup user uuid for {}", gameprofile.getName(), exception); + if (exception instanceof ProfileNotFoundException) { + String s2 = this.a(gameprofile); + + this.a(file2, s2, s2); + } else { + throw new NameReferencingFileConverter.FileConversionException("Could not request user " + gameprofile.getName() + " from backend systems", exception); + } + } + + private void a(File file4, String s2, String s3) { + File file5 = new File(file, s2 + ".dat"); + File file6 = new File(file4, s3 + ".dat"); + + // CraftBukkit start - Use old file name to seed lastKnownName + NBTTagCompound root = null; + + try { + root = NBTCompressedStreamTools.a(new java.io.FileInputStream(file5)); + } catch (Exception exception) { + exception.printStackTrace(); + ServerInternalException.reportInternalException(exception); // Paper + } + + if (root != null) { + if (!root.hasKey("bukkit")) { + root.set("bukkit", new NBTTagCompound()); + } + NBTTagCompound data = root.getCompound("bukkit"); + data.setString("lastKnownName", s2); + + try { + NBTCompressedStreamTools.a(root, new java.io.FileOutputStream(file2)); + } catch (Exception exception) { + exception.printStackTrace(); + ServerInternalException.reportInternalException(exception); // Paper + } + } + // CraftBukkit end + + NameReferencingFileConverter.b(file4); + if (!file5.renameTo(file6)) { + throw new NameReferencingFileConverter.FileConversionException("Could not convert file for " + s2); + } + } + + private String a(GameProfile gameprofile) { + String s2 = null; + String[] astring1 = astring; + int k = astring1.length; + + for (int l = 0; l < k; ++l) { + String s3 = astring1[l]; + + if (s3 != null && s3.equalsIgnoreCase(gameprofile.getName())) { + s2 = s3; + break; + } + } + + if (s2 == null) { + throw new NameReferencingFileConverter.FileConversionException("Could not find the filename for " + gameprofile.getName() + " anymore"); + } else { + return s2; + } + } + }; + + a(dedicatedserver, Lists.newArrayList(astring), profilelookupcallback); + return true; + } catch (NameReferencingFileConverter.FileConversionException namereferencingfileconverter_fileconversionexception) { + NameReferencingFileConverter.e.error("Conversion failed, please try again later", namereferencingfileconverter_fileconversionexception); + return false; + } + } else { + return true; + } + } + + private static void b(File file) { + if (file.exists()) { + if (!file.isDirectory()) { + throw new NameReferencingFileConverter.FileConversionException("Can't create directory " + file.getName() + " in world save directory."); + } + } else if (!file.mkdirs()) { + throw new NameReferencingFileConverter.FileConversionException("Can't create directory " + file.getName() + " in world save directory."); + } + } + + public static boolean a(PropertyManager propertymanager) { + boolean flag = b(propertymanager); + + flag = flag && c(propertymanager); + return flag; + } + + private static boolean b(PropertyManager propertymanager) { + boolean flag = false; + + if (NameReferencingFileConverter.b.exists() && NameReferencingFileConverter.b.isFile()) { + flag = true; + } + + boolean flag1 = false; + + if (NameReferencingFileConverter.a.exists() && NameReferencingFileConverter.a.isFile()) { + flag1 = true; + } + + boolean flag2 = false; + + if (NameReferencingFileConverter.c.exists() && NameReferencingFileConverter.c.isFile()) { + flag2 = true; + } + + boolean flag3 = false; + + if (NameReferencingFileConverter.d.exists() && NameReferencingFileConverter.d.isFile()) { + flag3 = true; + } + + if (!flag && !flag1 && !flag2 && !flag3) { + return true; + } else { + NameReferencingFileConverter.e.warn("**** FAILED TO START THE SERVER AFTER ACCOUNT CONVERSION!"); + NameReferencingFileConverter.e.warn("** please remove the following files and restart the server:"); + if (flag) { + NameReferencingFileConverter.e.warn("* {}", NameReferencingFileConverter.b.getName()); + } + + if (flag1) { + NameReferencingFileConverter.e.warn("* {}", NameReferencingFileConverter.a.getName()); + } + + if (flag2) { + NameReferencingFileConverter.e.warn("* {}", NameReferencingFileConverter.c.getName()); + } + + if (flag3) { + NameReferencingFileConverter.e.warn("* {}", NameReferencingFileConverter.d.getName()); + } + + return false; + } + } + + private static boolean c(PropertyManager propertymanager) { + File file = d(propertymanager); + + if (file.exists() && file.isDirectory() && (file.list().length > 0 || !file.delete())) { + NameReferencingFileConverter.e.warn("**** DETECTED OLD PLAYER DIRECTORY IN THE WORLD SAVE"); + NameReferencingFileConverter.e.warn("**** THIS USUALLY HAPPENS WHEN THE AUTOMATIC CONVERSION FAILED IN SOME WAY"); + NameReferencingFileConverter.e.warn("** please restart the server and if the problem persists, remove the directory '{}'", file.getPath()); + return false; + } else { + return true; + } + } + + private static File d(PropertyManager propertymanager) { + String s = propertymanager.getString("level-name", "world"); + File file = new File(MinecraftServer.getServer().server.getWorldContainer(), s); // CraftBukkit - Respect container setting + + return new File(file, "players"); + } + + private static void c(File file) { + File file1 = new File(file.getName() + ".converted"); + + file.renameTo(file1); + } + + private static Date b(String s, Date date) { + Date date1; + + try { + date1 = ExpirableListEntry.a.parse(s); + } catch (ParseException parseexception) { + date1 = date; + } + + return date1; + } + + static class FileConversionException extends RuntimeException { + + private FileConversionException(String s, Throwable throwable) { + super(s, throwable); + } + + private FileConversionException(String s) { + super(s); + } + } +} diff --git a/src/main/java/net/minecraft/server/NavigationAbstract.java b/src/main/java/net/minecraft/server/NavigationAbstract.java new file mode 100644 index 000000000000..cac0ce2a3a49 --- /dev/null +++ b/src/main/java/net/minecraft/server/NavigationAbstract.java @@ -0,0 +1,334 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public abstract class NavigationAbstract { + + protected EntityInsentient a; public Entity getEntity() { return a; } // Paper - OBFHELPER + protected World b; + @Nullable + protected PathEntity c; + protected double d; + private final AttributeInstance p; + protected int e; + protected int f; + protected Vec3D g; + protected Vec3D h; + protected long i; + protected long j; + protected double k; + protected float l; + protected boolean m; + protected long n; + protected PathfinderAbstract o; + private BlockPosition q; + private Pathfinder r; public Pathfinder getPathfinder() { return r; } // Paper - OBFHELPER + + private void setWorld() { if (getPathfinder() != null && getPathfinder().getPathfinder() != null) getPathfinder().getPathfinder().world = getEntity().world; } // Paper + public NavigationAbstract(EntityInsentient entityinsentient, World world) { + this.g = Vec3D.a; + this.h = Vec3D.a; + this.l = 0.5F; + this.a = entityinsentient; + this.b = world; + this.p = entityinsentient.getAttributeInstance(GenericAttributes.FOLLOW_RANGE); + this.r = this.a(); + setWorld(); // Paper + } + + public BlockPosition i() { + return this.q; + } + + protected abstract Pathfinder a(); + + public void a(double d0) { + this.d = d0; + } + + public float j() { + return (float) this.p.getValue(); + } + + public boolean k() { + return this.m; + } + + public void l() { + if (this.b.getTime() - this.n > 20L) { + if (this.q != null) { + this.c = null; + this.c = this.b(this.q); + this.n = this.b.getTime(); + this.m = false; + } + } else { + this.m = true; + } + + } + + @Nullable + public final PathEntity calculateDestination(double d0, double d1, double d2) { return a(d0, d1, d2); } @Nullable public final PathEntity a(double d0, double d1, double d2) { // Paper - OBFHELPER + return this.b(new BlockPosition(d0, d1, d2)); + } + + @Nullable + public PathEntity b(BlockPosition blockposition) { + if (!getEntity().getWorld().getWorldBorder().isInBounds(blockposition)) return null; // Paper - don't path out of world border + if (!this.b()) { + return null; + } else if (this.c != null && !this.c.b() && blockposition.equals(this.q)) { + return this.c; + } else { + if (!new com.destroystokyo.paper.event.entity.EntityPathfindEvent(getEntity().getBukkitEntity(), MCUtil.toLocation(getEntity().world, blockposition), null).callEvent()) { return null; } // Paper + this.q = blockposition; + float f = this.j(); + + this.b.methodProfiler.enter("pathfind"); + BlockPosition blockposition1 = new BlockPosition(this.a); + int i = (int) (f + 8.0F); + ChunkCache chunkcache = new ChunkCache(this.b, blockposition1.a(-i, -i, -i), blockposition1.a(i, i, i), 0); + PathEntity pathentity = this.r.a(chunkcache, this.a, this.q, f); + + this.b.methodProfiler.exit(); + return pathentity; + } + } + + @Nullable + public PathEntity calculateDestination(Entity entity) { return a(entity); } @Nullable public PathEntity a(Entity entity) { // Paper - OBFHELPER + if (!this.b()) { + return null; + } else { + BlockPosition blockposition = new BlockPosition(entity); + if (!getEntity().getWorld().getWorldBorder().isInBounds(blockposition)) return null; // Paper - don't path out of world border + if (this.c != null && !this.c.b() && blockposition.equals(this.q)) { + return this.c; + } else { + if (!new com.destroystokyo.paper.event.entity.EntityPathfindEvent(getEntity().getBukkitEntity(), MCUtil.toLocation(entity.world, blockposition), entity.getBukkitEntity()).callEvent()) { return null; } // Paper + this.q = blockposition; + float f = this.j(); + + this.b.methodProfiler.enter("pathfind"); + BlockPosition blockposition1 = (new BlockPosition(this.a)).up(); + int i = (int) (f + 16.0F); + ChunkCache chunkcache = new ChunkCache(this.b, blockposition1.a(-i, -i, -i), blockposition1.a(i, i, i), 0); + PathEntity pathentity = this.r.a(chunkcache, this.a, entity, f); + + this.b.methodProfiler.exit(); + return pathentity; + } + } + } + + public boolean a(double d0, double d1, double d2, double d3) { + return this.a(this.a(d0, d1, d2), d3); + } + + public boolean a(Entity entity, double d0) { + // Paper start - Pathfinding optimizations + if (this.pathfindFailures > 10 && this.c == null && MinecraftServer.currentTick < this.lastFailure + 40) { + return false; + } + + PathEntity pathentity = this.a(entity); + + if (pathentity != null && this.a(pathentity, d0)) { + this.lastFailure = 0; + this.pathfindFailures = 0; + return true; + } else { + this.pathfindFailures++; + this.lastFailure = MinecraftServer.currentTick; + return false; + } + } + private int lastFailure = 0; + private int pathfindFailures = 0; + // Paper end + + public boolean setDestination(@Nullable PathEntity pathentity, double speed) { return a(pathentity, speed); } // Paper - OBFHELPER + public boolean a(@Nullable PathEntity pathentity, double d0) { + if (pathentity == null) { + this.c = null; + return false; + } else { + if (!pathentity.a(this.c)) { + this.c = pathentity; + } + + this.E_(); + if (this.c.d() <= 0) { + return false; + } else { + this.d = d0; + Vec3D vec3d = this.c(); + + this.f = this.e; + this.g = vec3d; + return true; + } + } + } + @Nullable public PathEntity getPathEntity() { return m(); } @Nullable // Paper - OBFHELPER + public PathEntity m() { + return this.c; + } + + public void d() { + setWorld(); // Paper + ++this.e; + if (this.m) { + this.l(); + } + + if (!this.p()) { + Vec3D vec3d; + + if (this.b()) { + this.o(); + } else if (this.c != null && this.c.e() < this.c.d()) { + vec3d = this.c(); + Vec3D vec3d1 = this.c.a(this.a, this.c.e()); + + if (vec3d.y > vec3d1.y && !this.a.onGround && MathHelper.floor(vec3d.x) == MathHelper.floor(vec3d1.x) && MathHelper.floor(vec3d.z) == MathHelper.floor(vec3d1.z)) { + this.c.c(this.c.e() + 1); + } + } + + this.n(); + if (!this.p()) { + vec3d = this.c.a((Entity) this.a); + BlockPosition blockposition = new BlockPosition(vec3d); + + this.a.getControllerMove().a(vec3d.x, this.b.getType(blockposition.down()).isAir() ? vec3d.y : PathfinderNormal.a((IBlockAccess) this.b, blockposition), vec3d.z, this.d); + } + } + } + + protected void n() {} + + protected void o() { + Vec3D vec3d = this.c(); + int i = this.c.d(); + + for (int j = this.c.e(); j < this.c.d(); ++j) { + if ((double) this.c.a(j).b != Math.floor(vec3d.y)) { + i = j; + break; + } + } + + this.l = this.a.width > 0.75F ? this.a.width / 2.0F : 0.75F - this.a.width / 2.0F; + Vec3D vec3d1 = this.c.f(); + + if (MathHelper.e((float) (this.a.locX - (vec3d1.x + 0.5D))) < this.l && MathHelper.e((float) (this.a.locZ - (vec3d1.z + 0.5D))) < this.l && Math.abs(this.a.locY - vec3d1.y) < 1.0D) { + this.c.c(this.c.e() + 1); + } + + int k = MathHelper.f(this.a.width); + int l = MathHelper.f(this.a.length); + int i1 = k; + + for (int j1 = i - 1; j1 >= this.c.e(); --j1) { + if (this.a(vec3d, this.c.a(this.a, j1), k, l, i1)) { + this.c.c(j1); + break; + } + } + + this.a(vec3d); + } + + protected void a(Vec3D vec3d) { + if (this.e - this.f > 100) { + if (vec3d.distanceSquared(this.g) < 2.25D) { + this.q(); + } + + this.f = this.e; + this.g = vec3d; + } + + if (this.c != null && !this.c.b()) { + Vec3D vec3d1 = this.c.f(); + + if (vec3d1.equals(this.h)) { + this.i += SystemUtils.getMonotonicMillis() - this.j; + } else { + this.h = vec3d1; + double d0 = vec3d.f(this.h); + + this.k = this.a.cK() > 0.0F ? d0 / (double) this.a.cK() * 1000.0D : 0.0D; + } + + if (this.k > 0.0D && (double) this.i > this.k * 3.0D) { + this.h = Vec3D.a; + this.i = 0L; + this.k = 0.0D; + this.q(); + } + + this.j = SystemUtils.getMonotonicMillis(); + } + + } + + public boolean p() { + return this.c == null || this.c.b(); + } + + public void stopPathfinding() { q(); } // Paper - OBFHELPER + public void q() { + this.pathfindFailures = 0; this.lastFailure = 0; // Paper - Pathfinding optimizations + this.c = null; + } + + protected abstract Vec3D c(); + + protected abstract boolean b(); + + protected boolean r() { + return this.a.aq() || this.a.ax(); + } + + protected void E_() { + if (this.c != null) { + for (int i = 0; i < this.c.d(); ++i) { + PathPoint pathpoint = this.c.a(i); + PathPoint pathpoint1 = i + 1 < this.c.d() ? this.c.a(i + 1) : null; + IBlockData iblockdata = this.b.getType(new BlockPosition(pathpoint.a, pathpoint.b, pathpoint.c)); + Block block = iblockdata.getBlock(); + + if (block == Blocks.CAULDRON) { + this.c.a(i, pathpoint.a(pathpoint.a, pathpoint.b + 1, pathpoint.c)); + if (pathpoint1 != null && pathpoint.b >= pathpoint1.b) { + this.c.a(i + 1, pathpoint1.a(pathpoint1.a, pathpoint.b + 1, pathpoint1.c)); + } + } + } + + } + } + + protected abstract boolean a(Vec3D vec3d, Vec3D vec3d1, int i, int j, int k); + + public boolean a(BlockPosition blockposition) { + BlockPosition blockposition1 = blockposition.down(); + + return this.b.getType(blockposition1).f(this.b, blockposition1); + } + + public PathfinderAbstract s() { + return this.o; + } + + public void d(boolean flag) { + this.o.c(flag); + } + + public boolean t() { + return this.o.e(); + } +} diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java new file mode 100644 index 000000000000..30f646e421f3 --- /dev/null +++ b/src/main/java/net/minecraft/server/NetworkManager.java @@ -0,0 +1,413 @@ +package net.minecraft.server; + +import com.google.common.collect.Queues; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.DefaultEventLoopGroup; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.local.LocalChannel; +import io.netty.channel.local.LocalServerChannel; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.handler.timeout.TimeoutException; +import io.netty.util.AttributeKey; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; + +import java.net.SocketAddress; +import java.util.Queue; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import javax.annotation.Nullable; +import javax.crypto.SecretKey; +import org.apache.commons.lang3.Validate; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.MarkerManager; + +public class NetworkManager extends SimpleChannelInboundHandler> { + + private static final Logger g = LogManager.getLogger(); + public static final Marker a = MarkerManager.getMarker("NETWORK"); + public static final Marker b = MarkerManager.getMarker("NETWORK_PACKETS", NetworkManager.a); + public static final AttributeKey c = AttributeKey.valueOf("protocol"); + public static final LazyInitVar d = new LazyInitVar<>(() -> { + return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build()); + }); + public static final LazyInitVar e = new LazyInitVar<>(() -> { + return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).build()); + }); + public static final LazyInitVar f = new LazyInitVar<>(() -> { + return new DefaultEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).build()); + }); + private final EnumProtocolDirection h; + private final Queue packetQueue = Queues.newConcurrentLinkedQueue(); private final Queue getPacketQueue() { return this.packetQueue; } // Paper - OBFHELPER + private final ReentrantReadWriteLock j = new ReentrantReadWriteLock(); + public Channel channel; + public SocketAddress socketAddress; public void setSpoofedRemoteAddress(SocketAddress address) { this.socketAddress = address; } // Paper - OBFHELPER + // Spigot Start + public java.util.UUID spoofedUUID; + public com.mojang.authlib.properties.Property[] spoofedProfile; + public boolean preparing = true; + // Spigot End + private PacketListener packetListener; + private IChatBaseComponent n; + private boolean o; + private boolean p; + private int q; + private int r; + private float s; + private float t; + private int u; + private boolean v; + // Paper start - NetworkClient implementation + public int protocolVersion; + public java.net.InetSocketAddress virtualHost; + private static boolean enableExplicitFlush = Boolean.getBoolean("paper.explicit-flush"); + // Paper end + + public NetworkManager(EnumProtocolDirection enumprotocoldirection) { + this.h = enumprotocoldirection; + } + + public void channelActive(ChannelHandlerContext channelhandlercontext) throws Exception { + super.channelActive(channelhandlercontext); + this.channel = channelhandlercontext.channel(); + this.socketAddress = this.channel.remoteAddress(); + // Spigot Start + this.preparing = false; + // Spigot End + + try { + this.setProtocol(EnumProtocol.HANDSHAKING); + } catch (Throwable throwable) { + NetworkManager.g.fatal(throwable); + } + + } + + public void setProtocol(EnumProtocol enumprotocol) { + this.channel.attr(NetworkManager.c).set(enumprotocol); + this.channel.config().setAutoRead(true); + NetworkManager.g.debug("Enabled auto read"); + } + + public void channelInactive(ChannelHandlerContext channelhandlercontext) throws Exception { + this.close(new ChatMessage("disconnect.endOfStream", new Object[0])); + } + + public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) { + // Paper start + if (throwable instanceof io.netty.handler.codec.EncoderException && throwable.getCause() instanceof PacketEncoder.PacketTooLargeException) { + if (((PacketEncoder.PacketTooLargeException) throwable.getCause()).getPacket().packetTooLarge(this)) { + return; + } else { + throwable = throwable.getCause(); + } + } + // Paper end + if (throwable instanceof SkipEncodeException) { + NetworkManager.g.debug("Skipping packet due to errors", throwable.getCause()); + } else { + boolean flag = !this.v; + + this.v = true; + if (this.channel.isOpen()) { + if (throwable instanceof TimeoutException) { + NetworkManager.g.debug("Timeout", throwable); + this.close(new ChatMessage("disconnect.timeout", new Object[0])); + } else { + ChatMessage chatmessage = new ChatMessage("disconnect.genericReason", new Object[] { "Internal Exception: " + throwable}); + + if (flag) { + NetworkManager.g.debug("Failed to sent packet", throwable); + this.sendPacket(new PacketPlayOutKickDisconnect(chatmessage), (future) -> { + this.close(chatmessage); + }); + this.stopReading(); + } else { + NetworkManager.g.debug("Double fault", throwable); + this.close(chatmessage); + } + } + + } + } + if (MinecraftServer.getServer().isDebugging()) throwable.printStackTrace(); // Spigot + } + + protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet packet) throws Exception { + if (this.channel.isOpen()) { + try { + a(packet, this.packetListener); + } catch (CancelledPacketHandleException cancelledpackethandleexception) { + ; + } + + ++this.q; + } + + } + + private static void a(Packet packet, PacketListener packetlistener) { + packet.a((T) packetlistener); // CraftBukkit - decompile error + } + + public void setPacketListener(PacketListener packetlistener) { + Validate.notNull(packetlistener, "packetListener", new Object[0]); + NetworkManager.g.debug("Set listener of {} to {}", this, packetlistener); + this.packetListener = packetlistener; + } + + public void sendPacket(Packet packet) { + this.sendPacket(packet, (GenericFutureListener) null); + } + + public void sendPacket(Packet packet, @Nullable GenericFutureListener> genericfuturelistener) { + if (this.isConnected() && this.sendPacketQueue() && !(packet instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) packet).isReady())) { // Paper - Async-Anti-Xray - Add chunk packets which are not ready or all packets if the packet queue contains chunk packets which are not ready to the packet queue and send the packets later in the right order + //this.o(); // Paper - Async-Anti-Xray - Move to if statement (this.sendPacketQueue()) + this.b(packet, genericfuturelistener); + } else { + this.j.writeLock().lock(); + + try { + this.packetQueue.add(new NetworkManager.QueuedPacket(packet, genericfuturelistener)); + } finally { + this.j.writeLock().unlock(); + } + } + + } + + private void dispatchPacket(Packet packet, @Nullable GenericFutureListener> genericFutureListener) { this.b(packet, genericFutureListener); } // Paper - OBFHELPER + private void b(Packet packet, @Nullable GenericFutureListener> genericfuturelistener) { + EnumProtocol enumprotocol = EnumProtocol.a(packet); + EnumProtocol enumprotocol1 = (EnumProtocol) this.channel.attr(NetworkManager.c).get(); + + ++this.r; + if (enumprotocol1 != enumprotocol) { + NetworkManager.g.debug("Disabled auto read"); + this.channel.config().setAutoRead(false); + } + + if (this.channel.eventLoop().inEventLoop()) { + if (enumprotocol != enumprotocol1) { + this.setProtocol(enumprotocol); + } + + ChannelFuture channelfuture = this.channel.writeAndFlush(packet); + + if (genericfuturelistener != null) { + channelfuture.addListener(genericfuturelistener); + } + + channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); + } else { + this.channel.eventLoop().execute(() -> { + if (enumprotocol != enumprotocol1) { + this.setProtocol(enumprotocol); + } + + ChannelFuture channelfuture1 = this.channel.writeAndFlush(packet); + + if (genericfuturelistener != null) { + channelfuture1.addListener(genericfuturelistener); + } + + channelfuture1.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); + }); + } + + // Paper start + java.util.List extraPackets = packet.getExtraPackets(); + if (extraPackets != null && !extraPackets.isEmpty()) { + for (Packet extraPacket : extraPackets) { + this.dispatchPacket(extraPacket, genericfuturelistener); + } + } + // Paper end + + } + + // Paper start - Async-Anti-Xray - Stop dispatching further packets and return false if the peeked packet is a chunk packet which is not ready + private boolean sendPacketQueue() { return this.o(); } // OBFHELPER // void -> boolean + private boolean o() { // void -> boolean + if (this.channel != null && this.channel.isOpen()) { + if (this.packetQueue.isEmpty()) { // return if the packet queue is empty so that the write lock by Anti-Xray doesn't affect the vanilla performance at all + return true; + } + + this.j.writeLock().lock(); // readLock -> writeLock (because of race condition between peek and poll) + + try { + while (!this.packetQueue.isEmpty()) { + NetworkManager.QueuedPacket networkmanager_queuedpacket = (NetworkManager.QueuedPacket) this.getPacketQueue().peek(); // poll -> peek + + if (networkmanager_queuedpacket != null) { // Fix NPE (Spigot bug caused by handleDisconnection()) + if (networkmanager_queuedpacket.getPacket() instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) networkmanager_queuedpacket.getPacket()).isReady()) { // Check if the peeked packet is a chunk packet which is not ready + return false; // Return false if the peeked packet is a chunk packet which is not ready + } else { + this.getPacketQueue().poll(); // poll here + this.dispatchPacket(networkmanager_queuedpacket.getPacket(), networkmanager_queuedpacket.getGenericFutureListener()); // dispatch the packet + } + } + } + } finally { + this.j.writeLock().unlock(); // readLock -> writeLock (because of race condition between peek and poll) + } + + } + + return true; // Return true if all packets were dispatched + } + // Paper end + + public void a() { + this.o(); + if (this.packetListener instanceof ITickable) { + ((ITickable) this.packetListener).tick(); + } + + if (this.channel != null) { + if (enableExplicitFlush) this.channel.eventLoop().execute(() -> this.channel.flush()); // Paper - we don't need to explicit flush here, but allow opt in incase issues are found to a better version + } + + if (this.u++ % 20 == 0) { + this.t = this.t * 0.75F + (float) this.r * 0.25F; + this.s = this.s * 0.75F + (float) this.q * 0.25F; + this.r = 0; + this.q = 0; + } + + } + + public SocketAddress getSocketAddress() { + return this.socketAddress; + } + + public void close(IChatBaseComponent ichatbasecomponent) { + // Spigot Start + this.preparing = false; + // Spigot End + if (this.channel.isOpen()) { + this.channel.close(); // We can't wait as this may be called from an event loop. + this.n = ichatbasecomponent; + } + + } + + public boolean isLocal() { + return this.channel instanceof LocalChannel || this.channel instanceof LocalServerChannel; + } + + public void a(SecretKey secretkey) { + this.o = true; + this.channel.pipeline().addBefore("splitter", "decrypt", new PacketDecrypter(MinecraftEncryption.a(2, secretkey))); + this.channel.pipeline().addBefore("prepender", "encrypt", new PacketEncrypter(MinecraftEncryption.a(1, secretkey))); + } + + public boolean isConnected() { + return this.channel != null && this.channel.isOpen(); + } + + public boolean h() { + return this.channel == null; + } + + public PacketListener i() { + return this.packetListener; + } + + @Nullable + public IChatBaseComponent j() { + return this.n; + } + + public void stopReading() { + this.channel.config().setAutoRead(false); + } + + public void setCompressionLevel(int i) { + if (i >= 0) { + if (this.channel.pipeline().get("decompress") instanceof PacketDecompressor) { + ((PacketDecompressor) this.channel.pipeline().get("decompress")).a(i); + } else { + this.channel.pipeline().addBefore("decoder", "decompress", new PacketDecompressor(i)); + } + + if (this.channel.pipeline().get("compress") instanceof PacketCompressor) { + ((PacketCompressor) this.channel.pipeline().get("compress")).a(i); + } else { + this.channel.pipeline().addBefore("encoder", "compress", new PacketCompressor(i)); + } + } else { + if (this.channel.pipeline().get("decompress") instanceof PacketDecompressor) { + this.channel.pipeline().remove("decompress"); + } + + if (this.channel.pipeline().get("compress") instanceof PacketCompressor) { + this.channel.pipeline().remove("compress"); + } + } + + } + + public void handleDisconnection() { + if (this.channel != null && !this.channel.isOpen()) { + if (this.p) { + NetworkManager.g.warn("handleDisconnection() called twice"); + } else { + this.p = true; + if (this.j() != null) { + this.i().a(this.j()); + } else if (this.i() != null) { + this.i().a(new ChatMessage("multiplayer.disconnect.generic", new Object[0])); + } + this.packetQueue.clear(); // Free up packet queue. + // Paper start - Add PlayerConnectionCloseEvent + final PacketListener packetListener = this.i(); + if (packetListener instanceof PlayerConnection) { + /* Player was logged in */ + final PlayerConnection playerConnection = (PlayerConnection) packetListener; + new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(playerConnection.player.uniqueID, + playerConnection.player.getName(), ((java.net.InetSocketAddress)socketAddress).getAddress(), false).callEvent(); + } else if (packetListener instanceof LoginListener) { + /* Player is login stage */ + final LoginListener loginListener = (LoginListener) packetListener; + switch (loginListener.getLoginState()) { + case READY_TO_ACCEPT: + case DELAY_ACCEPT: + case ACCEPTED: + final com.mojang.authlib.GameProfile profile = loginListener.getGameProfile(); /* Should be non-null at this stage */ + new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(profile.getId(), profile.getName(), + ((java.net.InetSocketAddress)socketAddress).getAddress(), false).callEvent(); + } + } + // Paper end + } + + } + } + + static class QueuedPacket { + + private final Packet a; private final Packet getPacket() { return this.a; } // Paper - OBFHELPER + @Nullable + private final GenericFutureListener> b; private final GenericFutureListener> getGenericFutureListener() { return this.b; } // Paper - OBFHELPER + + public QueuedPacket(Packet packet, @Nullable GenericFutureListener> genericfuturelistener) { + this.a = packet; + this.b = genericfuturelistener; + } + } + + // Spigot Start + public SocketAddress getRawAddress() + { + return this.channel.remoteAddress(); + } + // Spigot End +} diff --git a/src/main/java/net/minecraft/server/NibbleArray.java b/src/main/java/net/minecraft/server/NibbleArray.java new file mode 100644 index 000000000000..79c5bc4292e6 --- /dev/null +++ b/src/main/java/net/minecraft/server/NibbleArray.java @@ -0,0 +1,56 @@ +package net.minecraft.server; + +public class NibbleArray { + + private final byte[] a; + + public NibbleArray() { + this.a = new byte[2048]; + } + + public NibbleArray(byte[] abyte) { + this.a = abyte; + if (abyte.length != 2048) { + throw new IllegalArgumentException("ChunkNibbleArrays should be 2048 bytes not: " + abyte.length); + } + } + + public int a(int i, int j, int k) { + return this.a(this.b(i, j, k)); + } + + public void a(int i, int j, int k, int l) { + this.a(this.b(i, j, k), l); + } + + private int b(int i, int j, int k) { + return j << 8 | k << 4 | i; + } + + public int a(int i) { + int j = this.c(i); + + return this.a[j] >> ((i & 1) << 2) & 15; // Spigot + } + + public void a(int i, int j) { + int k = this.c(i); + + // Spigot start + int shift = (i & 1) << 2; + this.a[k] = (byte) (this.a[k] & ~(15 << shift) | (j & 15) << shift); + // Spigot end + } + + private boolean b(int i) { + return (i & 1) == 0; + } + + private int c(int i) { + return i >> 1; + } + + public byte[] asBytes() { + return this.a; + } +} diff --git a/src/main/java/net/minecraft/server/Packet.java b/src/main/java/net/minecraft/server/Packet.java new file mode 100644 index 000000000000..8d0965a0535b --- /dev/null +++ b/src/main/java/net/minecraft/server/Packet.java @@ -0,0 +1,23 @@ +package net.minecraft.server; + +import java.io.IOException; + +public interface Packet { + + void a(PacketDataSerializer packetdataserializer) throws IOException; + + void b(PacketDataSerializer packetdataserializer) throws IOException; + + void a(T t0); + + // Paper start + default java.util.List getExtraPackets() { return null; } + default boolean packetTooLarge(NetworkManager manager) { + return false; + } + // Paper end + + default boolean a() { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/PacketDataSerializer.java b/src/main/java/net/minecraft/server/PacketDataSerializer.java new file mode 100644 index 000000000000..621aad150330 --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketDataSerializer.java @@ -0,0 +1,1088 @@ +package net.minecraft.server; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.ByteBufInputStream; +import io.netty.buffer.ByteBufOutputStream; +import io.netty.handler.codec.DecoderException; +import io.netty.handler.codec.EncoderException; +import io.netty.util.ByteProcessor; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.UUID; +import javax.annotation.Nullable; + +import org.bukkit.craftbukkit.inventory.CraftItemStack; // CraftBukkit + +public class PacketDataSerializer extends ByteBuf { + + private final ByteBuf a; + + public PacketDataSerializer(ByteBuf bytebuf) { + this.a = bytebuf; + } + + public static int countBytes(int i) { return PacketDataSerializer.a(i); } // Paper - OBFHELPER + public static int a(int i) { + for (int j = 1; j < 5; ++j) { + if ((i & -1 << j * 7) == 0) { + return j; + } + } + + return 5; + } + + public PacketDataSerializer a(byte[] abyte) { + this.d(abyte.length); + this.writeBytes(abyte); + return this; + } + + public byte[] a() { + return this.b(this.readableBytes()); + } + + public byte[] b(int i) { + int j = this.g(); + + if (j > i) { + throw new DecoderException("ByteArray with size " + j + " is bigger than allowed " + i); + } else { + byte[] abyte = new byte[j]; + + this.readBytes(abyte); + return abyte; + } + } + + public PacketDataSerializer a(int[] aint) { + this.d(aint.length); + int[] aint1 = aint; + int i = aint.length; + + for (int j = 0; j < i; ++j) { + int k = aint1[j]; + + this.d(k); + } + + return this; + } + + public int[] b() { + return this.c(this.readableBytes()); + } + + public int[] c(int i) { + int j = this.g(); + + if (j > i) { + throw new DecoderException("VarIntArray with size " + j + " is bigger than allowed " + i); + } else { + int[] aint = new int[j]; + + for (int k = 0; k < aint.length; ++k) { + aint[k] = this.g(); + } + + return aint; + } + } + + public PacketDataSerializer a(long[] along) { + this.d(along.length); + long[] along1 = along; + int i = along.length; + + for (int j = 0; j < i; ++j) { + long k = along1[j]; + + this.writeLong(k); + } + + return this; + } + + public BlockPosition e() { + return BlockPosition.fromLong(this.readLong()); + } + + public PacketDataSerializer a(BlockPosition blockposition) { + this.writeLong(blockposition.asLong()); + return this; + } + + public IChatBaseComponent f() { + return IChatBaseComponent.ChatSerializer.a(this.e(262144)); + } + + public PacketDataSerializer a(IChatBaseComponent ichatbasecomponent) { + return this.a(IChatBaseComponent.ChatSerializer.a(ichatbasecomponent), 262144); + } + + public > T a(Class oclass) { + return ((T[]) oclass.getEnumConstants())[this.g()]; // CraftBukkit - fix decompile error + } + + public PacketDataSerializer a(Enum oenum) { + return this.d(oenum.ordinal()); + } + + public int readVarInt() { return this.g(); } // Paper - OBFHELPER + public int g() { + int i = 0; + int j = 0; + + byte b0; + + do { + b0 = this.readByte(); + i |= (b0 & 127) << j++ * 7; + if (j > 5) { + throw new RuntimeException("VarInt too big"); + } + } while ((b0 & 128) == 128); + + return i; + } + + public long h() { + long i = 0L; + int j = 0; + + byte b0; + + do { + b0 = this.readByte(); + i |= (long) (b0 & 127) << j++ * 7; + if (j > 10) { + throw new RuntimeException("VarLong too big"); + } + } while ((b0 & 128) == 128); + + return i; + } + + public PacketDataSerializer a(UUID uuid) { + this.writeLong(uuid.getMostSignificantBits()); + this.writeLong(uuid.getLeastSignificantBits()); + return this; + } + + public UUID readUUID() { return this.i(); } // Paper - OBFHELPER + public UUID i() { + return new UUID(this.readLong(), this.readLong()); + } + + public PacketDataSerializer d(int i) { + while ((i & -128) != 0) { + this.writeByte(i & 127 | 128); + i >>>= 7; + } + + this.writeByte(i); + return this; + } + + public PacketDataSerializer b(long i) { + while ((i & -128L) != 0L) { + this.writeByte((int) (i & 127L) | 128); + i >>>= 7; + } + + this.writeByte((int) i); + return this; + } + + public PacketDataSerializer a(@Nullable NBTTagCompound nbttagcompound) { + if (nbttagcompound == null) { + this.writeByte(0); + } else { + try { + NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) (new ByteBufOutputStream(this))); + } catch (Exception ioexception) { // CraftBukkit - IOException -> Exception + throw new EncoderException(ioexception); + } + } + + return this; + } + + @Nullable + public NBTTagCompound j() { + int i = this.readerIndex(); + byte b0 = this.readByte(); + + if (b0 == 0) { + return null; + } else { + this.readerIndex(i); + + try { + return NBTCompressedStreamTools.a((DataInput) (new ByteBufInputStream(this)), new NBTReadLimiter(2097152L)); + } catch (IOException ioexception) { + throw new EncoderException(ioexception); + } + } + } + + public PacketDataSerializer a(ItemStack itemstack) { + if (itemstack.isEmpty() || itemstack.getItem() == null) { // CraftBukkit - NPE fix itemstack.getItem() + this.writeBoolean(false); + } else { + this.writeBoolean(true); + Item item = itemstack.getItem(); + + this.d(Item.getId(item)); + this.writeByte(itemstack.getCount()); + NBTTagCompound nbttagcompound = null; + + if (item.usesDurability() || item.n()) { + // Spigot start - filter + itemstack = itemstack.cloneItemStack(); + CraftItemStack.setItemMeta(itemstack, CraftItemStack.getItemMeta(itemstack)); + // Spigot end + nbttagcompound = itemstack.getTag(); + // Paper start + if (nbttagcompound != null && nbttagcompound.hasKeyOfType("SkullOwner", 10)) { + NBTTagCompound owner = nbttagcompound.getCompound("SkullOwner"); + if (owner.hasKey("Id")) { + nbttagcompound.setString("SkullOwnerOrig", owner.getString("Id")); + TileEntitySkull.sanitizeUUID(owner); + } + } + // Paper end + } + + this.a(nbttagcompound); + } + + return this; + } + + public ItemStack k() { + if (!this.readBoolean()) { + return ItemStack.a; + } else { + int i = this.g(); + byte b0 = this.readByte(); + ItemStack itemstack = new ItemStack(Item.getById(i), b0); + + itemstack.setTag(this.j()); + // CraftBukkit start + if (itemstack.getTag() != null) { + // Paper start - Fix skulls of same owner - restore orig ID since we changed it on send to client + if (itemstack.tag.hasKey("SkullOwnerOrig")) { + NBTTagCompound owner = itemstack.tag.getCompound("SkullOwner"); + String ownerOrig = itemstack.tag.getString("SkullOwnerOrig"); + if (!owner.isEmpty() && !ownerOrig.isEmpty()) { + owner.setString("Id", ownerOrig); + } + itemstack.tag.remove("SkullOwnerOrig"); + } + // Paper end + CraftItemStack.setItemMeta(itemstack, CraftItemStack.getItemMeta(itemstack)); + } + // CraftBukkit end + return itemstack; + } + } + + public String readUTF(int maxLength) { return this.e(maxLength); } // Paper - OBFHELPER + public String e(int i) { + int j = this.g(); + + if (j > i * 4) { + throw new DecoderException("The received encoded string buffer length is longer than maximum allowed (" + j + " > " + i * 4 + ")"); + } else if (j < 0) { + throw new DecoderException("The received encoded string buffer length is less than zero! Weird string!"); + } else { + String s = this.toString(this.readerIndex(), j, StandardCharsets.UTF_8); + + this.readerIndex(this.readerIndex() + j); + if (s.length() > i) { + throw new DecoderException("The received string length is longer than maximum allowed (" + j + " > " + i + ")"); + } else { + return s; + } + } + } + + public PacketDataSerializer a(String s) { + return this.a(s, 32767); + } + + public PacketDataSerializer a(String s, int i) { + byte[] abyte = s.getBytes(StandardCharsets.UTF_8); + + if (abyte.length > i) { + throw new EncoderException("String too big (was " + abyte.length + " bytes encoded, max " + i + ")"); + } else { + this.d(abyte.length); + this.writeBytes(abyte); + return this; + } + } + + public MinecraftKey l() { + return new MinecraftKey(this.e(32767)); + } + + public PacketDataSerializer a(MinecraftKey minecraftkey) { + this.a(minecraftkey.toString()); + return this; + } + + public Date m() { + return new Date(this.readLong()); + } + + public PacketDataSerializer a(Date date) { + this.writeLong(date.getTime()); + return this; + } + + public int capacity() { + return this.a.capacity(); + } + + public ByteBuf capacity(int i) { + return this.a.capacity(i); + } + + public int maxCapacity() { + return this.a.maxCapacity(); + } + + public ByteBufAllocator alloc() { + return this.a.alloc(); + } + + public ByteOrder order() { + return this.a.order(); + } + + public ByteBuf order(ByteOrder byteorder) { + return this.a.order(byteorder); + } + + public ByteBuf unwrap() { + return this.a.unwrap(); + } + + public boolean isDirect() { + return this.a.isDirect(); + } + + public boolean isReadOnly() { + return this.a.isReadOnly(); + } + + public ByteBuf asReadOnly() { + return this.a.asReadOnly(); + } + + public int readerIndex() { + return this.a.readerIndex(); + } + + public ByteBuf readerIndex(int i) { + return this.a.readerIndex(i); + } + + public int writerIndex() { + return this.a.writerIndex(); + } + + public ByteBuf writerIndex(int i) { + return this.a.writerIndex(i); + } + + public ByteBuf setIndex(int i, int j) { + return this.a.setIndex(i, j); + } + + public int readableBytes() { + return this.a.readableBytes(); + } + + public int writableBytes() { + return this.a.writableBytes(); + } + + public int maxWritableBytes() { + return this.a.maxWritableBytes(); + } + + public boolean isReadable() { + return this.a.isReadable(); + } + + public boolean isReadable(int i) { + return this.a.isReadable(i); + } + + public boolean isWritable() { + return this.a.isWritable(); + } + + public boolean isWritable(int i) { + return this.a.isWritable(i); + } + + public ByteBuf clear() { + return this.a.clear(); + } + + public ByteBuf markReaderIndex() { + return this.a.markReaderIndex(); + } + + public ByteBuf resetReaderIndex() { + return this.a.resetReaderIndex(); + } + + public ByteBuf markWriterIndex() { + return this.a.markWriterIndex(); + } + + public ByteBuf resetWriterIndex() { + return this.a.resetWriterIndex(); + } + + public ByteBuf discardReadBytes() { + return this.a.discardReadBytes(); + } + + public ByteBuf discardSomeReadBytes() { + return this.a.discardSomeReadBytes(); + } + + public ByteBuf ensureWritable(int i) { + return this.a.ensureWritable(i); + } + + public int ensureWritable(int i, boolean flag) { + return this.a.ensureWritable(i, flag); + } + + public boolean getBoolean(int i) { + return this.a.getBoolean(i); + } + + public byte getByte(int i) { + return this.a.getByte(i); + } + + public short getUnsignedByte(int i) { + return this.a.getUnsignedByte(i); + } + + public short getShort(int i) { + return this.a.getShort(i); + } + + public short getShortLE(int i) { + return this.a.getShortLE(i); + } + + public int getUnsignedShort(int i) { + return this.a.getUnsignedShort(i); + } + + public int getUnsignedShortLE(int i) { + return this.a.getUnsignedShortLE(i); + } + + public int getMedium(int i) { + return this.a.getMedium(i); + } + + public int getMediumLE(int i) { + return this.a.getMediumLE(i); + } + + public int getUnsignedMedium(int i) { + return this.a.getUnsignedMedium(i); + } + + public int getUnsignedMediumLE(int i) { + return this.a.getUnsignedMediumLE(i); + } + + public int getInt(int i) { + return this.a.getInt(i); + } + + public int getIntLE(int i) { + return this.a.getIntLE(i); + } + + public long getUnsignedInt(int i) { + return this.a.getUnsignedInt(i); + } + + public long getUnsignedIntLE(int i) { + return this.a.getUnsignedIntLE(i); + } + + public long getLong(int i) { + return this.a.getLong(i); + } + + public long getLongLE(int i) { + return this.a.getLongLE(i); + } + + public char getChar(int i) { + return this.a.getChar(i); + } + + public float getFloat(int i) { + return this.a.getFloat(i); + } + + public double getDouble(int i) { + return this.a.getDouble(i); + } + + public ByteBuf getBytes(int i, ByteBuf bytebuf) { + return this.a.getBytes(i, bytebuf); + } + + public ByteBuf getBytes(int i, ByteBuf bytebuf, int j) { + return this.a.getBytes(i, bytebuf, j); + } + + public ByteBuf getBytes(int i, ByteBuf bytebuf, int j, int k) { + return this.a.getBytes(i, bytebuf, j, k); + } + + public ByteBuf getBytes(int i, byte[] abyte) { + return this.a.getBytes(i, abyte); + } + + public ByteBuf getBytes(int i, byte[] abyte, int j, int k) { + return this.a.getBytes(i, abyte, j, k); + } + + public ByteBuf getBytes(int i, ByteBuffer bytebuffer) { + return this.a.getBytes(i, bytebuffer); + } + + public ByteBuf getBytes(int i, OutputStream outputstream, int j) throws IOException { + return this.a.getBytes(i, outputstream, j); + } + + public int getBytes(int i, GatheringByteChannel gatheringbytechannel, int j) throws IOException { + return this.a.getBytes(i, gatheringbytechannel, j); + } + + public int getBytes(int i, FileChannel filechannel, long j, int k) throws IOException { + return this.a.getBytes(i, filechannel, j, k); + } + + public CharSequence getCharSequence(int i, int j, Charset charset) { + return this.a.getCharSequence(i, j, charset); + } + + public ByteBuf setBoolean(int i, boolean flag) { + return this.a.setBoolean(i, flag); + } + + public ByteBuf setByte(int i, int j) { + return this.a.setByte(i, j); + } + + public ByteBuf setShort(int i, int j) { + return this.a.setShort(i, j); + } + + public ByteBuf setShortLE(int i, int j) { + return this.a.setShortLE(i, j); + } + + public ByteBuf setMedium(int i, int j) { + return this.a.setMedium(i, j); + } + + public ByteBuf setMediumLE(int i, int j) { + return this.a.setMediumLE(i, j); + } + + public ByteBuf setInt(int i, int j) { + return this.a.setInt(i, j); + } + + public ByteBuf setIntLE(int i, int j) { + return this.a.setIntLE(i, j); + } + + public ByteBuf setLong(int i, long j) { + return this.a.setLong(i, j); + } + + public ByteBuf setLongLE(int i, long j) { + return this.a.setLongLE(i, j); + } + + public ByteBuf setChar(int i, int j) { + return this.a.setChar(i, j); + } + + public ByteBuf setFloat(int i, float f) { + return this.a.setFloat(i, f); + } + + public ByteBuf setDouble(int i, double d0) { + return this.a.setDouble(i, d0); + } + + public ByteBuf setBytes(int i, ByteBuf bytebuf) { + return this.a.setBytes(i, bytebuf); + } + + public ByteBuf setBytes(int i, ByteBuf bytebuf, int j) { + return this.a.setBytes(i, bytebuf, j); + } + + public ByteBuf setBytes(int i, ByteBuf bytebuf, int j, int k) { + return this.a.setBytes(i, bytebuf, j, k); + } + + public ByteBuf setBytes(int i, byte[] abyte) { + return this.a.setBytes(i, abyte); + } + + public ByteBuf setBytes(int i, byte[] abyte, int j, int k) { + return this.a.setBytes(i, abyte, j, k); + } + + public ByteBuf setBytes(int i, ByteBuffer bytebuffer) { + return this.a.setBytes(i, bytebuffer); + } + + public int setBytes(int i, InputStream inputstream, int j) throws IOException { + return this.a.setBytes(i, inputstream, j); + } + + public int setBytes(int i, ScatteringByteChannel scatteringbytechannel, int j) throws IOException { + return this.a.setBytes(i, scatteringbytechannel, j); + } + + public int setBytes(int i, FileChannel filechannel, long j, int k) throws IOException { + return this.a.setBytes(i, filechannel, j, k); + } + + public ByteBuf setZero(int i, int j) { + return this.a.setZero(i, j); + } + + public int setCharSequence(int i, CharSequence charsequence, Charset charset) { + return this.a.setCharSequence(i, charsequence, charset); + } + + public boolean readBoolean() { + return this.a.readBoolean(); + } + + public byte readByte() { + return this.a.readByte(); + } + + public short readUnsignedByte() { + return this.a.readUnsignedByte(); + } + + public short readShort() { + return this.a.readShort(); + } + + public short readShortLE() { + return this.a.readShortLE(); + } + + public int readUnsignedShort() { + return this.a.readUnsignedShort(); + } + + public int readUnsignedShortLE() { + return this.a.readUnsignedShortLE(); + } + + public int readMedium() { + return this.a.readMedium(); + } + + public int readMediumLE() { + return this.a.readMediumLE(); + } + + public int readUnsignedMedium() { + return this.a.readUnsignedMedium(); + } + + public int readUnsignedMediumLE() { + return this.a.readUnsignedMediumLE(); + } + + public int readInt() { + return this.a.readInt(); + } + + public int readIntLE() { + return this.a.readIntLE(); + } + + public long readUnsignedInt() { + return this.a.readUnsignedInt(); + } + + public long readUnsignedIntLE() { + return this.a.readUnsignedIntLE(); + } + + public long readLong() { + return this.a.readLong(); + } + + public long readLongLE() { + return this.a.readLongLE(); + } + + public char readChar() { + return this.a.readChar(); + } + + public float readFloat() { + return this.a.readFloat(); + } + + public double readDouble() { + return this.a.readDouble(); + } + + public ByteBuf readBytes(int i) { + return this.a.readBytes(i); + } + + public ByteBuf readSlice(int i) { + return this.a.readSlice(i); + } + + public ByteBuf readRetainedSlice(int i) { + return this.a.readRetainedSlice(i); + } + + public ByteBuf readBytes(ByteBuf bytebuf) { + return this.a.readBytes(bytebuf); + } + + public ByteBuf readBytes(ByteBuf bytebuf, int i) { + return this.a.readBytes(bytebuf, i); + } + + public ByteBuf readBytes(ByteBuf bytebuf, int i, int j) { + return this.a.readBytes(bytebuf, i, j); + } + + public ByteBuf readBytes(byte[] abyte) { + return this.a.readBytes(abyte); + } + + public ByteBuf readBytes(byte[] abyte, int i, int j) { + return this.a.readBytes(abyte, i, j); + } + + public ByteBuf readBytes(ByteBuffer bytebuffer) { + return this.a.readBytes(bytebuffer); + } + + public ByteBuf readBytes(OutputStream outputstream, int i) throws IOException { + return this.a.readBytes(outputstream, i); + } + + public int readBytes(GatheringByteChannel gatheringbytechannel, int i) throws IOException { + return this.a.readBytes(gatheringbytechannel, i); + } + + public CharSequence readCharSequence(int i, Charset charset) { + return this.a.readCharSequence(i, charset); + } + + public int readBytes(FileChannel filechannel, long i, int j) throws IOException { + return this.a.readBytes(filechannel, i, j); + } + + public ByteBuf skipBytes(int i) { + return this.a.skipBytes(i); + } + + public ByteBuf writeBoolean(boolean flag) { + return this.a.writeBoolean(flag); + } + + public ByteBuf writeByte(int i) { + return this.a.writeByte(i); + } + + public ByteBuf writeShort(int i) { + return this.a.writeShort(i); + } + + public ByteBuf writeShortLE(int i) { + return this.a.writeShortLE(i); + } + + public ByteBuf writeMedium(int i) { + return this.a.writeMedium(i); + } + + public ByteBuf writeMediumLE(int i) { + return this.a.writeMediumLE(i); + } + + public ByteBuf writeInt(int i) { + return this.a.writeInt(i); + } + + public ByteBuf writeIntLE(int i) { + return this.a.writeIntLE(i); + } + + public ByteBuf writeLong(long i) { + return this.a.writeLong(i); + } + + public ByteBuf writeLongLE(long i) { + return this.a.writeLongLE(i); + } + + public ByteBuf writeChar(int i) { + return this.a.writeChar(i); + } + + public ByteBuf writeFloat(float f) { + return this.a.writeFloat(f); + } + + public ByteBuf writeDouble(double d0) { + return this.a.writeDouble(d0); + } + + public ByteBuf writeBytes(ByteBuf bytebuf) { + return this.a.writeBytes(bytebuf); + } + + public ByteBuf writeBytes(ByteBuf bytebuf, int i) { + return this.a.writeBytes(bytebuf, i); + } + + public ByteBuf writeBytes(ByteBuf bytebuf, int i, int j) { + return this.a.writeBytes(bytebuf, i, j); + } + + public ByteBuf writeBytes(byte[] abyte) { + return this.a.writeBytes(abyte); + } + + public ByteBuf writeBytes(byte[] abyte, int i, int j) { + return this.a.writeBytes(abyte, i, j); + } + + public ByteBuf writeBytes(ByteBuffer bytebuffer) { + return this.a.writeBytes(bytebuffer); + } + + public int writeBytes(InputStream inputstream, int i) throws IOException { + return this.a.writeBytes(inputstream, i); + } + + public int writeBytes(ScatteringByteChannel scatteringbytechannel, int i) throws IOException { + return this.a.writeBytes(scatteringbytechannel, i); + } + + public int writeBytes(FileChannel filechannel, long i, int j) throws IOException { + return this.a.writeBytes(filechannel, i, j); + } + + public ByteBuf writeZero(int i) { + return this.a.writeZero(i); + } + + public int writeCharSequence(CharSequence charsequence, Charset charset) { + return this.a.writeCharSequence(charsequence, charset); + } + + public int indexOf(int i, int j, byte b0) { + return this.a.indexOf(i, j, b0); + } + + public int bytesBefore(byte b0) { + return this.a.bytesBefore(b0); + } + + public int bytesBefore(int i, byte b0) { + return this.a.bytesBefore(i, b0); + } + + public int bytesBefore(int i, int j, byte b0) { + return this.a.bytesBefore(i, j, b0); + } + + public int forEachByte(ByteProcessor byteprocessor) { + return this.a.forEachByte(byteprocessor); + } + + public int forEachByte(int i, int j, ByteProcessor byteprocessor) { + return this.a.forEachByte(i, j, byteprocessor); + } + + public int forEachByteDesc(ByteProcessor byteprocessor) { + return this.a.forEachByteDesc(byteprocessor); + } + + public int forEachByteDesc(int i, int j, ByteProcessor byteprocessor) { + return this.a.forEachByteDesc(i, j, byteprocessor); + } + + public ByteBuf copy() { + return this.a.copy(); + } + + public ByteBuf copy(int i, int j) { + return this.a.copy(i, j); + } + + public ByteBuf slice() { + return this.a.slice(); + } + + public ByteBuf retainedSlice() { + return this.a.retainedSlice(); + } + + public ByteBuf slice(int i, int j) { + return this.a.slice(i, j); + } + + public ByteBuf retainedSlice(int i, int j) { + return this.a.retainedSlice(i, j); + } + + public ByteBuf duplicate() { + return this.a.duplicate(); + } + + public ByteBuf retainedDuplicate() { + return this.a.retainedDuplicate(); + } + + public int nioBufferCount() { + return this.a.nioBufferCount(); + } + + public ByteBuffer nioBuffer() { + return this.a.nioBuffer(); + } + + public ByteBuffer nioBuffer(int i, int j) { + return this.a.nioBuffer(i, j); + } + + public ByteBuffer internalNioBuffer(int i, int j) { + return this.a.internalNioBuffer(i, j); + } + + public ByteBuffer[] nioBuffers() { + return this.a.nioBuffers(); + } + + public ByteBuffer[] nioBuffers(int i, int j) { + return this.a.nioBuffers(i, j); + } + + public boolean hasArray() { + return this.a.hasArray(); + } + + public byte[] array() { + return this.a.array(); + } + + public int arrayOffset() { + return this.a.arrayOffset(); + } + + public boolean hasMemoryAddress() { + return this.a.hasMemoryAddress(); + } + + public long memoryAddress() { + return this.a.memoryAddress(); + } + + public String toString(Charset charset) { + return this.a.toString(charset); + } + + public String toString(int i, int j, Charset charset) { + return this.a.toString(i, j, charset); + } + + public int hashCode() { + return this.a.hashCode(); + } + + public boolean equals(Object object) { + return this.a.equals(object); + } + + public int compareTo(ByteBuf bytebuf) { + return this.a.compareTo(bytebuf); + } + + public String toString() { + return this.a.toString(); + } + + public ByteBuf retain(int i) { + return this.a.retain(i); + } + + public ByteBuf retain() { + return this.a.retain(); + } + + public ByteBuf touch() { + return this.a.touch(); + } + + public ByteBuf touch(Object object) { + return this.a.touch(object); + } + + public int refCnt() { + return this.a.refCnt(); + } + + public boolean release() { + return this.a.release(); + } + + public boolean release(int i) { + return this.a.release(i); + } +} diff --git a/src/main/java/net/minecraft/server/PacketEncoder.java b/src/main/java/net/minecraft/server/PacketEncoder.java new file mode 100644 index 000000000000..4f7bc186aa0c --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketEncoder.java @@ -0,0 +1,79 @@ +package net.minecraft.server; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import java.io.IOException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.MarkerManager; + +public class PacketEncoder extends MessageToByteEncoder> { + + private static final Logger a = LogManager.getLogger(); + private static final Marker b = MarkerManager.getMarker("PACKET_SENT", NetworkManager.b); + private final EnumProtocolDirection c; + + public PacketEncoder(EnumProtocolDirection enumprotocoldirection) { + this.c = enumprotocoldirection; + } + + protected void encode(ChannelHandlerContext channelhandlercontext, Packet packet, ByteBuf bytebuf) throws Exception { + EnumProtocol enumprotocol = (EnumProtocol) channelhandlercontext.channel().attr(NetworkManager.c).get(); + + if (enumprotocol == null) { + throw new RuntimeException("ConnectionProtocol unknown: " + packet); + } else { + Integer integer = enumprotocol.a(this.c, packet); + + if (PacketEncoder.a.isDebugEnabled()) { + PacketEncoder.a.debug(PacketEncoder.b, "OUT: [{}:{}] {}", channelhandlercontext.channel().attr(NetworkManager.c).get(), integer, packet.getClass().getName()); + } + + if (integer == null) { + throw new IOException("Can't serialize unregistered packet"); + } else { + PacketDataSerializer packetdataserializer = new PacketDataSerializer(bytebuf); + + packetdataserializer.d(integer); + + try { + packet.b(packetdataserializer); + } catch (Throwable throwable) { + PacketEncoder.a.error(throwable); + throwable.printStackTrace(); // Paper - WHAT WAS IT? WHO DID THIS TO YOU? WHAT DID YOU SEE? + if (packet.a()) { + throw new SkipEncodeException(throwable); + } else { + throw throwable; + } + } + + // Paper start + int packetLength = bytebuf.readableBytes(); + if (packetLength > MAX_PACKET_SIZE) { + throw new PacketTooLargeException(packet, packetLength); + } + // Paper end + } + } + } + + // Paper start + private static int MAX_PACKET_SIZE = 2097152; + + public static class PacketTooLargeException extends RuntimeException { + private final Packet packet; + + PacketTooLargeException(Packet packet, int packetLength) { + super("PacketTooLarge - " + packet.getClass().getSimpleName() + " is " + packetLength + ". Max is " + MAX_PACKET_SIZE); + this.packet = packet; + } + + public Packet getPacket() { + return packet; + } + } + // Paper end +} diff --git a/src/main/java/net/minecraft/server/PacketHandshakingInSetProtocol.java b/src/main/java/net/minecraft/server/PacketHandshakingInSetProtocol.java new file mode 100644 index 000000000000..f1a3be69d0f1 --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketHandshakingInSetProtocol.java @@ -0,0 +1,40 @@ +package net.minecraft.server; + +import java.io.IOException; + +public class PacketHandshakingInSetProtocol implements Packet { + + private int a; + public String hostname; + public int port; + private EnumProtocol d; + + public PacketHandshakingInSetProtocol() {} + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.g(); + this.hostname = packetdataserializer.e(Short.MAX_VALUE); // Spigot + this.port = packetdataserializer.readUnsignedShort(); + this.d = EnumProtocol.a(packetdataserializer.g()); + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.d(this.a); + packetdataserializer.a(this.hostname); + packetdataserializer.writeShort(this.port); + packetdataserializer.d(this.d.a()); + } + + public void a(PacketHandshakingInListener packethandshakinginlistener) { + packethandshakinginlistener.a(this); + } + + public EnumProtocol b() { + return this.d; + } + + public int getProtocolVersion() { return c(); } // Paper - OBFHELPER + public int c() { + return this.a; + } +} diff --git a/src/main/java/net/minecraft/server/PacketLoginInCustomPayload.java b/src/main/java/net/minecraft/server/PacketLoginInCustomPayload.java new file mode 100644 index 000000000000..430445cc6dae --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketLoginInCustomPayload.java @@ -0,0 +1,42 @@ +package net.minecraft.server; + +import java.io.IOException; + +public class PacketLoginInCustomPayload implements Packet { + + private int a; public int getId() { return a; } // Paper - OBFHELPER + private PacketDataSerializer b; public PacketDataSerializer getBuf() { return b; } // Paper - OBFHELPER + + public PacketLoginInCustomPayload() {} + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.g(); + if (packetdataserializer.readBoolean()) { + int i = packetdataserializer.readableBytes(); + + if (i < 0 || i > 1048576) { + throw new IOException("Payload may not be larger than 1048576 bytes"); + } + + this.b = new PacketDataSerializer(packetdataserializer.readBytes(i)); + } else { + this.b = null; + } + + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.d(this.a); + if (this.b != null) { + packetdataserializer.writeBoolean(true); + packetdataserializer.writeBytes(this.b.copy()); + } else { + packetdataserializer.writeBoolean(false); + } + + } + + public void a(PacketLoginInListener packetlogininlistener) { + packetlogininlistener.a(this); + } +} diff --git a/src/main/java/net/minecraft/server/PacketLoginOutCustomPayload.java b/src/main/java/net/minecraft/server/PacketLoginOutCustomPayload.java new file mode 100644 index 000000000000..23c96f44b37a --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketLoginOutCustomPayload.java @@ -0,0 +1,42 @@ +package net.minecraft.server; + +import java.io.IOException; + +public class PacketLoginOutCustomPayload implements Packet { + + private int a; + private MinecraftKey b; + private PacketDataSerializer c; + + public PacketLoginOutCustomPayload() {} + + // Paper start + public PacketLoginOutCustomPayload(int id, MinecraftKey channel, PacketDataSerializer buf) { + this.a = id; + this.b = channel; + this.c = buf; + } + // Paper end + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.g(); + this.b = packetdataserializer.l(); + int i = packetdataserializer.readableBytes(); + + if (i >= 0 && i <= 1048576) { + this.c = new PacketDataSerializer(packetdataserializer.readBytes(i)); + } else { + throw new IOException("Payload may not be larger than 1048576 bytes"); + } + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.d(this.a); + packetdataserializer.a(this.b); + packetdataserializer.writeBytes(this.c.copy()); + } + + public void a(PacketLoginOutListener packetloginoutlistener) { + packetloginoutlistener.a(this); + } +} diff --git a/src/main/java/net/minecraft/server/PacketPlayInBlockPlace.java b/src/main/java/net/minecraft/server/PacketPlayInBlockPlace.java new file mode 100644 index 000000000000..167da27b2a1e --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketPlayInBlockPlace.java @@ -0,0 +1,32 @@ +package net.minecraft.server; + +import java.io.IOException; + +public class PacketPlayInBlockPlace implements Packet { + + private EnumHand a; + public long timestamp; // Spigot + + public PacketPlayInBlockPlace() {} + + public PacketPlayInBlockPlace(EnumHand enumhand) { + this.a = enumhand; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.timestamp = System.currentTimeMillis(); // Spigot + this.a = (EnumHand) packetdataserializer.a(EnumHand.class); + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.a((Enum) this.a); + } + + public void a(PacketListenerPlayIn packetlistenerplayin) { + packetlistenerplayin.a(this); + } + + public EnumHand b() { + return this.a; + } +} diff --git a/src/main/java/net/minecraft/server/PacketPlayInChat.java b/src/main/java/net/minecraft/server/PacketPlayInChat.java new file mode 100644 index 000000000000..c6dbfe302018 --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketPlayInChat.java @@ -0,0 +1,51 @@ +package net.minecraft.server; + +import java.io.IOException; + +public class PacketPlayInChat implements Packet { + + private String a; + + public PacketPlayInChat() {} + + public PacketPlayInChat(String s) { + if (s.length() > 256) { + s = s.substring(0, 256); + } + + this.a = s; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.e(256); + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.a(this.a); + } + + // Spigot Start + private static final java.util.concurrent.ExecutorService executors = java.util.concurrent.Executors.newCachedThreadPool( + new com.google.common.util.concurrent.ThreadFactoryBuilder().setDaemon( true ).setNameFormat( "Async Chat Thread - #%d" ).build() ); + public void a(final PacketListenerPlayIn packetlistenerplayin) { + if ( !a.startsWith("/") ) + { + executors.submit( new Runnable() + { + + @Override + public void run() + { + packetlistenerplayin.a( PacketPlayInChat.this ); + } + } ); + return; + } + // Spigot End + packetlistenerplayin.a(this); + } + + public String b() { + return this.a; + } +} diff --git a/src/main/java/net/minecraft/server/PacketPlayInCloseWindow.java b/src/main/java/net/minecraft/server/PacketPlayInCloseWindow.java new file mode 100644 index 000000000000..4dfb6c021582 --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketPlayInCloseWindow.java @@ -0,0 +1,28 @@ +package net.minecraft.server; + +import java.io.IOException; + +public class PacketPlayInCloseWindow implements Packet { + + private int id; + + public PacketPlayInCloseWindow() {} + + // CraftBukkit start + public PacketPlayInCloseWindow(int id) { + this.id = id; + } + // CraftBukkit end + + public void a(PacketListenerPlayIn packetlistenerplayin) { + packetlistenerplayin.a(this); + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.id = packetdataserializer.readByte(); + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.writeByte(this.id); + } +} diff --git a/src/main/java/net/minecraft/server/PacketPlayInUseEntity.java b/src/main/java/net/minecraft/server/PacketPlayInUseEntity.java new file mode 100644 index 000000000000..8711462e1681 --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketPlayInUseEntity.java @@ -0,0 +1,75 @@ +package net.minecraft.server; + +import java.io.IOException; +import javax.annotation.Nullable; + +public class PacketPlayInUseEntity implements Packet { + + private int a; public int getEntityId() { return this.a; } // Paper - add accessor + private PacketPlayInUseEntity.EnumEntityUseAction action; + private Vec3D c; + private EnumHand d; + + public PacketPlayInUseEntity() {} + + public PacketPlayInUseEntity(Entity entity) { + this.a = entity.getId(); + this.action = PacketPlayInUseEntity.EnumEntityUseAction.ATTACK; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.g(); + this.action = (PacketPlayInUseEntity.EnumEntityUseAction) packetdataserializer.a(PacketPlayInUseEntity.EnumEntityUseAction.class); + if (this.action == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT_AT) { + this.c = new Vec3D((double) packetdataserializer.readFloat(), (double) packetdataserializer.readFloat(), (double) packetdataserializer.readFloat()); + } + + if (this.action == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT || this.action == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT_AT) { + this.d = (EnumHand) packetdataserializer.a(EnumHand.class); + } + + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.d(this.a); + packetdataserializer.a((Enum) this.action); + if (this.action == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT_AT) { + packetdataserializer.writeFloat((float) this.c.x); + packetdataserializer.writeFloat((float) this.c.y); + packetdataserializer.writeFloat((float) this.c.z); + } + + if (this.action == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT || this.action == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT_AT) { + packetdataserializer.a((Enum) this.d); + } + + } + + public void a(PacketListenerPlayIn packetlistenerplayin) { + packetlistenerplayin.a(this); + } + + @Nullable + public Entity a(World world) { + return world.getEntity(this.a); + } + + public PacketPlayInUseEntity.EnumEntityUseAction b() { + return this.action; + } + + public EnumHand c() { + return this.d; + } + + public Vec3D d() { + return this.c; + } + + public static enum EnumEntityUseAction { + + INTERACT, ATTACK, INTERACT_AT; + + private EnumEntityUseAction() {} + } +} diff --git a/src/main/java/net/minecraft/server/PacketPlayInUseItem.java b/src/main/java/net/minecraft/server/PacketPlayInUseItem.java new file mode 100644 index 000000000000..ced2336fe815 --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketPlayInUseItem.java @@ -0,0 +1,63 @@ +package net.minecraft.server; + +import java.io.IOException; + +public class PacketPlayInUseItem implements Packet { + + private BlockPosition a; + private EnumDirection b; + private EnumHand c; + private float d; + private float e; + private float f; + public long timestamp; + + public PacketPlayInUseItem() {} + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.timestamp = System.currentTimeMillis(); // Spigot + this.a = packetdataserializer.e(); + this.b = (EnumDirection) packetdataserializer.a(EnumDirection.class); + this.c = (EnumHand) packetdataserializer.a(EnumHand.class); + this.d = packetdataserializer.readFloat(); + this.e = packetdataserializer.readFloat(); + this.f = packetdataserializer.readFloat(); + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.a(this.a); + packetdataserializer.a((Enum) this.b); + packetdataserializer.a((Enum) this.c); + packetdataserializer.writeFloat(this.d); + packetdataserializer.writeFloat(this.e); + packetdataserializer.writeFloat(this.f); + } + + public void a(PacketListenerPlayIn packetlistenerplayin) { + packetlistenerplayin.a(this); + } + + public BlockPosition b() { + return this.a; + } + + public EnumDirection c() { + return this.b; + } + + public EnumHand d() { + return this.c; + } + + public float e() { + return this.d; + } + + public float f() { + return this.e; + } + + public float g() { + return this.f; + } +} diff --git a/src/main/java/net/minecraft/server/PacketPlayOutChat.java b/src/main/java/net/minecraft/server/PacketPlayOutChat.java new file mode 100644 index 000000000000..eba6aadad762 --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketPlayOutChat.java @@ -0,0 +1,60 @@ +package net.minecraft.server; + +import java.io.IOException; + +public class PacketPlayOutChat implements Packet { + + private IChatBaseComponent a; + public net.md_5.bungee.api.chat.BaseComponent[] components; // Spigot + private ChatMessageType b; + + public PacketPlayOutChat() {} + + public PacketPlayOutChat(IChatBaseComponent ichatbasecomponent) { + this(ichatbasecomponent, ChatMessageType.SYSTEM); + } + + public PacketPlayOutChat(IChatBaseComponent ichatbasecomponent, ChatMessageType chatmessagetype) { + this.a = ichatbasecomponent; + this.b = chatmessagetype; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.f(); + this.b = ChatMessageType.a(packetdataserializer.readByte()); + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + // Spigot start + if (components != null) { + //packetdataserializer.a(net.md_5.bungee.chat.ComponentSerializer.toString(components)); // Paper - comment, replaced with below + // Paper start - don't nest if we don't need to so that we can preserve formatting + if (this.components.length == 1) { + packetdataserializer.a(net.md_5.bungee.chat.ComponentSerializer.toString(this.components[0])); + } else { + packetdataserializer.a(net.md_5.bungee.chat.ComponentSerializer.toString(this.components)); + } + // Paper end + } else { + packetdataserializer.a(this.a); + } + // Spigot end + packetdataserializer.writeByte(this.b.a()); + } + + public void a(PacketListenerPlayOut packetlistenerplayout) { + packetlistenerplayout.a(this); + } + + public boolean c() { + return this.b == ChatMessageType.SYSTEM || this.b == ChatMessageType.GAME_INFO; + } + + public ChatMessageType d() { + return this.b; + } + + public boolean a() { + return true; + } +} diff --git a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java new file mode 100644 index 000000000000..eb54bdb64243 --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java @@ -0,0 +1,207 @@ +package net.minecraft.server; + +import com.destroystokyo.paper.antixray.ChunkPacketInfo; // Paper - Anti-Xray +import com.google.common.collect.Lists; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; + +public class PacketPlayOutMapChunk implements Packet { + + private int a; + private int b; + private int c; + private byte[] d; private byte[] getData() { return this.d; } // Paper - OBFHELPER + private List e; + private boolean f; + private volatile boolean ready = false; // Paper - Async-Anti-Xray - Ready flag for the network manager + + public PacketPlayOutMapChunk() { + this.ready = true; // Paper - Async-Anti-Xray - Set the ready flag to true + } + + // Paper start + private final java.util.List extraPackets = new java.util.ArrayList<>(); + private static final int SKIP_EXCESSIVE_SIGNS_LIMIT = Integer.getInteger("Paper.excessiveSignsLimit", 500); + public java.util.List getExtraPackets() { + return extraPackets; + } + // Paper end + public PacketPlayOutMapChunk(Chunk chunk, int i) { + ChunkPacketInfo chunkPacketInfo = chunk.world.chunkPacketBlockController.getChunkPacketInfo(this, chunk, i); // Paper - Anti-Xray - Add chunk packet info + this.a = chunk.locX; + this.b = chunk.locZ; + this.f = i == 65535; + boolean flag = chunk.getWorld().worldProvider.g(); + + this.d = new byte[this.a(chunk, flag, i)]; + + // Paper start - Anti-Xray - Add chunk packet info + if (chunkPacketInfo != null) { + chunkPacketInfo.setData(this.getData()); + } + // Paper end + + this.c = this.writeChunk(new PacketDataSerializer(this.h()), chunk, flag, i, chunkPacketInfo); // Paper - Anti-Xray - Add chunk packet info + this.e = Lists.newArrayList(); + Iterator iterator = chunk.getTileEntities().entrySet().iterator(); + int totalSigns = 0; // Paper + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + BlockPosition blockposition = (BlockPosition) entry.getKey(); + TileEntity tileentity = (TileEntity) entry.getValue(); + int j = blockposition.getY() >> 4; + + if (this.f() || (i & 1 << j) != 0) { + // Paper start - send signs separately + if (tileentity instanceof TileEntitySign) { + if (SKIP_EXCESSIVE_SIGNS_LIMIT < 0 || ++totalSigns < SKIP_EXCESSIVE_SIGNS_LIMIT) { + extraPackets.add(tileentity.getUpdatePacket()); + } + continue; + } + // Paper end + + NBTTagCompound nbttagcompound = tileentity.aa_(); + if (tileentity instanceof TileEntitySkull) { TileEntitySkull.sanitizeTileEntityUUID(nbttagcompound); } // Paper + + this.e.add(nbttagcompound); + } + } + + chunk.world.chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks + } + + // Paper start - Async-Anti-Xray - Getter and Setter for the ready flag + public boolean isReady() { + return this.ready; + } + + public void setReady(boolean ready) { + this.ready = ready; + } + // Paper end + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.readInt(); + this.b = packetdataserializer.readInt(); + this.f = packetdataserializer.readBoolean(); + this.c = packetdataserializer.g(); + int i = packetdataserializer.g(); + + if (i > 2097152) { // Paper - if this changes, update PacketEncoder + throw new RuntimeException("Chunk Packet trying to allocate too much memory on read."); + } else { + this.d = new byte[i]; + packetdataserializer.readBytes(this.d); + int j = packetdataserializer.g(); + + this.e = Lists.newArrayList(); + + for (int k = 0; k < j; ++k) { + this.e.add(packetdataserializer.j()); + } + + } + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.writeInt(this.a); + packetdataserializer.writeInt(this.b); + packetdataserializer.writeBoolean(this.f); + packetdataserializer.d(this.c); + packetdataserializer.d(this.d.length); + packetdataserializer.writeBytes(this.d); + packetdataserializer.d(this.e.size()); + Iterator iterator = this.e.iterator(); + + while (iterator.hasNext()) { + NBTTagCompound nbttagcompound = (NBTTagCompound) iterator.next(); + + packetdataserializer.a(nbttagcompound); + } + + } + + public void a(PacketListenerPlayOut packetlistenerplayout) { + packetlistenerplayout.a(this); + } + + private ByteBuf h() { + ByteBuf bytebuf = Unpooled.wrappedBuffer(this.d); + + bytebuf.writerIndex(0); + return bytebuf; + } + + // Paper start - Anti-Xray - Support default methods + public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector); } + public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i) { + return this.a(packetdataserializer, chunk, flag, i, null); + } + // Paper end + + public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector, ChunkPacketInfo chunkPacketInfo) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector, chunkPacketInfo); } // Paper - OBFHELPER // Paper - Anti-Xray - Add chunk packet info + public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i, ChunkPacketInfo chunkPacketInfo) { // Paper - Anti-Xray - Add chunk packet info + int j = 0; + ChunkSection[] achunksection = chunk.getSections(); + int k = 0; + + int l; + + for (l = achunksection.length; k < l; ++k) { + ChunkSection chunksection = achunksection[k]; + + if (chunksection != Chunk.a && (!this.f() || !chunksection.a()) && (i & 1 << k) != 0) { + j |= 1 << k; + chunksection.getBlocks().writeDataPaletteBlock(packetdataserializer, chunkPacketInfo, k); // Paper - Anti-Xray - Add chunk packet info + packetdataserializer.writeBytes(chunksection.getEmittedLightArray().asBytes()); + if (flag) { + packetdataserializer.writeBytes(chunksection.getSkyLightArray().asBytes()); + } + } + } + + if (this.f()) { + BiomeBase[] abiomebase = chunk.getBiomeIndex(); + + for (l = 0; l < abiomebase.length; ++l) { + packetdataserializer.writeInt(IRegistry.BIOME.a(abiomebase[l])); // Paper - Decompile fix + } + } + + return j; + } + + protected int a(Chunk chunk, boolean flag, int i) { + int j = 0; + ChunkSection[] achunksection = chunk.getSections(); + int k = 0; + + for (int l = achunksection.length; k < l; ++k) { + ChunkSection chunksection = achunksection[k]; + + if (chunksection != Chunk.a && (!this.f() || !chunksection.a()) && (i & 1 << k) != 0) { + j += chunksection.getBlocks().a(); + j += chunksection.getEmittedLightArray().asBytes().length; + if (flag) { + j += chunksection.getSkyLightArray().asBytes().length; + } + } + } + + if (this.f()) { + j += chunk.getBiomeIndex().length * 4; + } + + return j; + } + + public boolean f() { + return this.f; + } +} diff --git a/src/main/java/net/minecraft/server/PacketPlayOutScoreboardTeam.java b/src/main/java/net/minecraft/server/PacketPlayOutScoreboardTeam.java new file mode 100644 index 000000000000..575e3762b2b8 --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketPlayOutScoreboardTeam.java @@ -0,0 +1,119 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; + +public class PacketPlayOutScoreboardTeam implements Packet { + + private String a = ""; + private IChatBaseComponent b = new ChatComponentText(""); + private IChatBaseComponent c = new ChatComponentText(""); + private IChatBaseComponent d = new ChatComponentText(""); + private String e; + private String f; + private EnumChatFormat g; + private final Collection h; + private int i; + private int j; + + public PacketPlayOutScoreboardTeam() { + this.e = ScoreboardTeamBase.EnumNameTagVisibility.ALWAYS.e; + this.f = ScoreboardTeamBase.EnumTeamPush.ALWAYS.e; + this.g = EnumChatFormat.RESET; + this.h = Lists.newArrayList(); + } + + public PacketPlayOutScoreboardTeam(ScoreboardTeam scoreboardteam, int i) { + this.e = ScoreboardTeamBase.EnumNameTagVisibility.ALWAYS.e; + this.f = ScoreboardTeamBase.EnumTeamPush.ALWAYS.e; + this.g = EnumChatFormat.RESET; + this.h = Lists.newArrayList(); + this.a = scoreboardteam.getName(); + this.i = i; + if (i == 0 || i == 2) { + this.b = scoreboardteam.getDisplayName(); + this.j = scoreboardteam.packOptionData(); + this.e = scoreboardteam.getNameTagVisibility().e; + this.f = scoreboardteam.getCollisionRule().e; + this.g = scoreboardteam.getColor(); + this.c = scoreboardteam.getPrefix(); + this.d = scoreboardteam.getSuffix(); + } + + if (i == 0) { + this.h.addAll(scoreboardteam.getPlayerNameSet()); + } + + } + + public PacketPlayOutScoreboardTeam(ScoreboardTeam scoreboardteam, Collection collection, int i) { + this.e = ScoreboardTeamBase.EnumNameTagVisibility.ALWAYS.e; + this.f = ScoreboardTeamBase.EnumTeamPush.ALWAYS.e; + this.g = EnumChatFormat.RESET; + this.h = Lists.newArrayList(); + if (i != 3 && i != 4) { + throw new IllegalArgumentException("Method must be join or leave for player constructor"); + } else if (collection != null && !collection.isEmpty()) { + this.i = i; + this.a = scoreboardteam.getName(); + this.h.addAll(collection); + } else { + throw new IllegalArgumentException("Players cannot be null/empty"); + } + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.e(16); + this.i = packetdataserializer.readByte(); + if (this.i == 0 || this.i == 2) { + this.b = packetdataserializer.f(); + this.j = packetdataserializer.readByte(); + this.e = packetdataserializer.e(40); + this.f = packetdataserializer.e(40); + this.g = (EnumChatFormat) packetdataserializer.a(EnumChatFormat.class); + this.c = packetdataserializer.f(); + this.d = packetdataserializer.f(); + } + + if (this.i == 0 || this.i == 3 || this.i == 4) { + int i = packetdataserializer.g(); + + for (int j = 0; j < i; ++j) { + this.h.add(packetdataserializer.e(40)); + } + } + + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.a(this.a); + packetdataserializer.writeByte(this.i); + if (this.i == 0 || this.i == 2) { + packetdataserializer.a(this.b); + packetdataserializer.writeByte(this.j); + packetdataserializer.a(this.e); + packetdataserializer.a(!com.destroystokyo.paper.PaperConfig.enablePlayerCollisions ? "never" : this.f); // Paper + packetdataserializer.a((Enum) this.g); + packetdataserializer.a(this.c); + packetdataserializer.a(this.d); + } + + if (this.i == 0 || this.i == 3 || this.i == 4) { + packetdataserializer.d(this.h.size()); + Iterator iterator = this.h.iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + packetdataserializer.a(s); + } + } + + } + + public void a(PacketListenerPlayOut packetlistenerplayout) { + packetlistenerplayout.a(this); + } +} diff --git a/src/main/java/net/minecraft/server/PacketPlayOutTitle.java b/src/main/java/net/minecraft/server/PacketPlayOutTitle.java new file mode 100644 index 000000000000..15d62cf06655 --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketPlayOutTitle.java @@ -0,0 +1,87 @@ +package net.minecraft.server; + +import java.io.IOException; +import javax.annotation.Nullable; + +public class PacketPlayOutTitle implements Packet { + + private PacketPlayOutTitle.EnumTitleAction a; + private IChatBaseComponent b; + private int c; + private int d; + private int e; + + public PacketPlayOutTitle() {} + + public PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction packetplayouttitle_enumtitleaction, IChatBaseComponent ichatbasecomponent) { + this(packetplayouttitle_enumtitleaction, ichatbasecomponent, -1, -1, -1); + } + + public PacketPlayOutTitle(int i, int j, int k) { + this(PacketPlayOutTitle.EnumTitleAction.TIMES, (IChatBaseComponent) null, i, j, k); + } + + public PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction packetplayouttitle_enumtitleaction, @Nullable IChatBaseComponent ichatbasecomponent, int i, int j, int k) { + this.a = packetplayouttitle_enumtitleaction; + this.b = ichatbasecomponent; + this.c = i; + this.d = j; + this.e = k; + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = (PacketPlayOutTitle.EnumTitleAction) packetdataserializer.a(PacketPlayOutTitle.EnumTitleAction.class); + if (this.a == PacketPlayOutTitle.EnumTitleAction.TITLE || this.a == PacketPlayOutTitle.EnumTitleAction.SUBTITLE || this.a == PacketPlayOutTitle.EnumTitleAction.ACTIONBAR) { + this.b = packetdataserializer.f(); + } + + if (this.a == PacketPlayOutTitle.EnumTitleAction.TIMES) { + this.c = packetdataserializer.readInt(); + this.d = packetdataserializer.readInt(); + this.e = packetdataserializer.readInt(); + } + + } + // Paper start + public net.md_5.bungee.api.chat.BaseComponent[] components; + + public PacketPlayOutTitle(EnumTitleAction action, net.md_5.bungee.api.chat.BaseComponent[] components, int fadeIn, int stay, int fadeOut) { + this.a = action; + this.components = components; + this.c = fadeIn; + this.d = stay; + this.e = fadeOut; + } + // Paper end + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.a((Enum) this.a); + if (this.a == PacketPlayOutTitle.EnumTitleAction.TITLE || this.a == PacketPlayOutTitle.EnumTitleAction.SUBTITLE || this.a == PacketPlayOutTitle.EnumTitleAction.ACTIONBAR) { + // Paper start + if (this.components != null) { + packetdataserializer.a(net.md_5.bungee.chat.ComponentSerializer.toString(components)); + } else { + packetdataserializer.a(this.b); + } + // Paper end + } + + if (this.a == PacketPlayOutTitle.EnumTitleAction.TIMES) { + packetdataserializer.writeInt(this.c); + packetdataserializer.writeInt(this.d); + packetdataserializer.writeInt(this.e); + } + + } + + public void a(PacketListenerPlayOut packetlistenerplayout) { + packetlistenerplayout.a(this); + } + + public static enum EnumTitleAction { + + TITLE, SUBTITLE, ACTIONBAR, TIMES, CLEAR, RESET; + + private EnumTitleAction() {} + } +} diff --git a/src/main/java/net/minecraft/server/PacketPlayOutUpdateTime.java b/src/main/java/net/minecraft/server/PacketPlayOutUpdateTime.java new file mode 100644 index 000000000000..6413f76e781d --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketPlayOutUpdateTime.java @@ -0,0 +1,46 @@ +package net.minecraft.server; + +import java.io.IOException; + +public class PacketPlayOutUpdateTime implements Packet { + + // World Age in ticks + // Not changed by server commands + // World Age must not be negative + private long a; + // Time of Day in ticks + // If negative the sun will stop moving at the Math.abs of the time + // Displayed in the debug screen (F3) + private long b; + + public PacketPlayOutUpdateTime() {} + + public PacketPlayOutUpdateTime(long i, long j, boolean flag) { + this.a = i; + this.b = j; + if (!flag) { + this.b = -this.b; + if (this.b == 0L) { + this.b = -1L; + } + } + + // Paper start + this.a = this.a % 192000; + // Paper end + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.readLong(); + this.b = packetdataserializer.readLong(); + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.writeLong(this.a); + packetdataserializer.writeLong(this.b); + } + + public void a(PacketListenerPlayOut packetlistenerplayout) { + packetlistenerplayout.a(this); + } +} diff --git a/src/main/java/net/minecraft/server/PacketPlayOutWindowItems.java b/src/main/java/net/minecraft/server/PacketPlayOutWindowItems.java new file mode 100644 index 000000000000..c0d8f8b4286a --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketPlayOutWindowItems.java @@ -0,0 +1,61 @@ +package net.minecraft.server; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; + +public class PacketPlayOutWindowItems implements Packet { + + private int a; + private List b; + + //Paper start + @Override + public boolean packetTooLarge(NetworkManager manager) { + for (int i = 0 ; i < this.b.size() ; i++) { + manager.sendPacket(new PacketPlayOutSetSlot(this.a, i, this.b.get(i))); + } + return true; + } + // Paper end + public PacketPlayOutWindowItems() {} + + public PacketPlayOutWindowItems(int i, NonNullList nonnulllist) { + this.a = i; + this.b = NonNullList.a(nonnulllist.size(), ItemStack.a); + + for (int j = 0; j < this.b.size(); ++j) { + this.b.set(j, ((ItemStack) nonnulllist.get(j)).cloneItemStack()); + } + + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = packetdataserializer.readUnsignedByte(); + short short0 = packetdataserializer.readShort(); + + this.b = NonNullList.a(short0, ItemStack.a); + + for (int i = 0; i < short0; ++i) { + this.b.set(i, packetdataserializer.k()); + } + + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.writeByte(this.a); + packetdataserializer.writeShort(this.b.size()); + Iterator iterator = this.b.iterator(); + + while (iterator.hasNext()) { + ItemStack itemstack = (ItemStack) iterator.next(); + + packetdataserializer.a(itemstack); + } + + } + + public void a(PacketListenerPlayOut packetlistenerplayout) { + packetlistenerplayout.a(this); + } +} diff --git a/src/main/java/net/minecraft/server/PacketPlayOutWorldBorder.java b/src/main/java/net/minecraft/server/PacketPlayOutWorldBorder.java new file mode 100644 index 000000000000..c15263fea367 --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketPlayOutWorldBorder.java @@ -0,0 +1,111 @@ +package net.minecraft.server; + +import java.io.IOException; + +public class PacketPlayOutWorldBorder implements Packet { + + private PacketPlayOutWorldBorder.EnumWorldBorderAction a; + private int b; + private double c; + private double d; + private double e; + private double f; + private long g; + private int h; + private int i; + + public PacketPlayOutWorldBorder() {} + + public PacketPlayOutWorldBorder(WorldBorder worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction packetplayoutworldborder_enumworldborderaction) { + this.a = packetplayoutworldborder_enumworldborderaction; + // CraftBukkit start - multiply out nether border + this.c = worldborder.getCenterX() * (worldborder.world.worldProvider instanceof WorldProviderHell ? 8 : 1); + this.d = worldborder.getCenterZ() * (worldborder.world.worldProvider instanceof WorldProviderHell ? 8 : 1); + // CraftBukkit end + this.f = worldborder.getSize(); + this.e = worldborder.j(); + this.g = worldborder.i(); + this.b = worldborder.l(); + this.i = worldborder.getWarningDistance(); + this.h = worldborder.getWarningTime(); + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { + this.a = (PacketPlayOutWorldBorder.EnumWorldBorderAction) packetdataserializer.a(PacketPlayOutWorldBorder.EnumWorldBorderAction.class); + switch (this.a) { + case SET_SIZE: + this.e = packetdataserializer.readDouble(); + break; + case LERP_SIZE: + this.f = packetdataserializer.readDouble(); + this.e = packetdataserializer.readDouble(); + this.g = packetdataserializer.h(); + break; + case SET_CENTER: + this.c = packetdataserializer.readDouble(); + this.d = packetdataserializer.readDouble(); + break; + case SET_WARNING_BLOCKS: + this.i = packetdataserializer.g(); + break; + case SET_WARNING_TIME: + this.h = packetdataserializer.g(); + break; + case INITIALIZE: + this.c = packetdataserializer.readDouble(); + this.d = packetdataserializer.readDouble(); + this.f = packetdataserializer.readDouble(); + this.e = packetdataserializer.readDouble(); + this.g = packetdataserializer.h(); + this.b = packetdataserializer.g(); + this.i = packetdataserializer.g(); + this.h = packetdataserializer.g(); + } + + } + + public void b(PacketDataSerializer packetdataserializer) throws IOException { + packetdataserializer.a((Enum) this.a); + switch (this.a) { + case SET_SIZE: + packetdataserializer.writeDouble(this.e); + break; + case LERP_SIZE: + packetdataserializer.writeDouble(this.f); + packetdataserializer.writeDouble(this.e); + packetdataserializer.b(this.g); + break; + case SET_CENTER: + packetdataserializer.writeDouble(this.c); + packetdataserializer.writeDouble(this.d); + break; + case SET_WARNING_BLOCKS: + packetdataserializer.d(this.i); + break; + case SET_WARNING_TIME: + packetdataserializer.d(this.h); + break; + case INITIALIZE: + packetdataserializer.writeDouble(this.c); + packetdataserializer.writeDouble(this.d); + packetdataserializer.writeDouble(this.f); + packetdataserializer.writeDouble(this.e); + packetdataserializer.b(this.g); + packetdataserializer.d(this.b); + packetdataserializer.d(this.i); + packetdataserializer.d(this.h); + } + + } + + public void a(PacketListenerPlayOut packetlistenerplayout) { + packetlistenerplayout.a(this); + } + + public static enum EnumWorldBorderAction { + + SET_SIZE, LERP_SIZE, SET_CENTER, INITIALIZE, SET_WARNING_TIME, SET_WARNING_BLOCKS; + + private EnumWorldBorderAction() {} + } +} diff --git a/src/main/java/net/minecraft/server/PacketStatusListener.java b/src/main/java/net/minecraft/server/PacketStatusListener.java new file mode 100644 index 000000000000..8aa121e2f760 --- /dev/null +++ b/src/main/java/net/minecraft/server/PacketStatusListener.java @@ -0,0 +1,141 @@ +package net.minecraft.server; + +// CraftBukkit start +import com.mojang.authlib.GameProfile; +import io.netty.channel.ChannelFutureListener; +import java.net.InetSocketAddress; +import java.util.Iterator; + +import org.bukkit.craftbukkit.util.CraftIconCache; +import org.bukkit.entity.Player; + +// CraftBukkit end + +public class PacketStatusListener implements PacketStatusInListener { + + private static final IChatBaseComponent a = new ChatMessage("multiplayer.status.request_handled", new Object[0]); + private final MinecraftServer minecraftServer; + private final NetworkManager networkManager; + private boolean d; + + public PacketStatusListener(MinecraftServer minecraftserver, NetworkManager networkmanager) { + this.minecraftServer = minecraftserver; + this.networkManager = networkmanager; + } + + public void a(IChatBaseComponent ichatbasecomponent) {} + + public void a(PacketStatusInStart packetstatusinstart) { + if (this.d) { + this.networkManager.close(PacketStatusListener.a); + } else { + this.d = true; + // Paper start - Replace everything + /* + // CraftBukkit start + // this.networkManager.sendPacket(new PacketStatusOutServerInfo(this.minecraftServer.getServerPing())); + final Object[] players = minecraftServer.getPlayerList().players.toArray(); + class ServerListPingEvent extends org.bukkit.event.server.ServerListPingEvent { + + CraftIconCache icon = minecraftServer.server.getServerIcon(); + + ServerListPingEvent() { + super(((InetSocketAddress) networkManager.getSocketAddress()).getAddress(), minecraftServer.getMotd(), minecraftServer.getPlayerList().getMaxPlayers()); + } + + @Override + public void setServerIcon(org.bukkit.util.CachedServerIcon icon) { + if (!(icon instanceof CraftIconCache)) { + throw new IllegalArgumentException(icon + " was not created by " + org.bukkit.craftbukkit.CraftServer.class); + } + this.icon = (CraftIconCache) icon; + } + + @Override + public Iterator iterator() throws UnsupportedOperationException { + return new Iterator() { + int i; + int ret = Integer.MIN_VALUE; + EntityPlayer player; + + @Override + public boolean hasNext() { + if (player != null) { + return true; + } + final Object[] currentPlayers = players; + for (int length = currentPlayers.length, i = this.i; i < length; i++) { + final EntityPlayer player = (EntityPlayer) currentPlayers[i]; + if (player != null) { + this.i = i + 1; + this.player = player; + return true; + } + } + return false; + } + + @Override + public Player next() { + if (!hasNext()) { + throw new java.util.NoSuchElementException(); + } + final EntityPlayer player = this.player; + this.player = null; + this.ret = this.i - 1; + return player.getBukkitEntity(); + } + + @Override + public void remove() { + final Object[] currentPlayers = players; + final int i = this.ret; + if (i < 0 || currentPlayers[i] == null) { + throw new IllegalStateException(); + } + currentPlayers[i] = null; + } + }; + } + } + + ServerListPingEvent event = new ServerListPingEvent(); + this.minecraftServer.server.getPluginManager().callEvent(event); + + java.util.List profiles = new java.util.ArrayList(players.length); + for (Object player : players) { + if (player != null) { + profiles.add(((EntityPlayer) player).getProfile()); + } + } + + ServerPing.ServerPingPlayerSample playerSample = new ServerPing.ServerPingPlayerSample(event.getMaxPlayers(), profiles.size()); + // Spigot Start + if ( !profiles.isEmpty() ) + { + java.util.Collections.shuffle( profiles ); // This sucks, its inefficient but we have no simple way of doing it differently + profiles = profiles.subList( 0, Math.min( profiles.size(), org.spigotmc.SpigotConfig.playerSample ) ); // Cap the sample to n (or less) displayed players, ie: Vanilla behaviour + } + // Spigot End + playerSample.a(profiles.toArray(new GameProfile[profiles.size()])); + + ServerPing ping = new ServerPing(); + ping.setFavicon(event.icon.value); + ping.setMOTD(new ChatComponentText(event.getMotd())); + ping.setPlayerSample(playerSample); + int version = minecraftServer.getServerPing().getServerData().getProtocolVersion(); + ping.setServerInfo(new ServerPing.ServerData(minecraftServer.getServerModName() + " " + minecraftServer.getVersion(), version)); + + this.networkManager.sendPacket(new PacketStatusOutServerInfo(ping)); + */ + com.destroystokyo.paper.network.StandardPaperServerListPingEventImpl.processRequest(this.minecraftServer, this.networkManager); + // Paper end + } + // CraftBukkit end + } + + public void a(PacketStatusInPing packetstatusinping) { + this.networkManager.sendPacket(new PacketStatusOutPong(packetstatusinping.b())); + this.networkManager.close(PacketStatusListener.a); + } +} diff --git a/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java b/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java new file mode 100644 index 000000000000..851756d65ec5 --- /dev/null +++ b/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java @@ -0,0 +1,658 @@ +/* + * This file is licensed under the MIT License (MIT). + * + * Copyright (c) 2018 Daniel Ennis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package net.minecraft.server; + +import com.destroystokyo.paper.PaperConfig; +import com.destroystokyo.paper.util.PriorityQueuedExecutor; +import com.destroystokyo.paper.util.PriorityQueuedExecutor.Priority; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.generator.CustomChunkGenerator; +import org.bukkit.craftbukkit.generator.InternalChunkGenerator; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +@SuppressWarnings("unused") +public class PaperAsyncChunkProvider extends ChunkProviderServer { + + private static final int GEN_THREAD_PRIORITY = Integer.getInteger("paper.genThreadPriority", 3); + private static final int LOAD_THREAD_PRIORITY = Integer.getInteger("paper.loadThreadPriority", 4); + private static final PriorityQueuedExecutor EXECUTOR = new PriorityQueuedExecutor("PaperChunkLoader", PaperConfig.asyncChunks ? PaperConfig.asyncChunkLoadThreads : 0, LOAD_THREAD_PRIORITY); + private static final PriorityQueuedExecutor SINGLE_GEN_EXECUTOR = new PriorityQueuedExecutor("PaperChunkGenerator", PaperConfig.asyncChunks && PaperConfig.asyncChunkGeneration && !PaperConfig.asyncChunkGenThreadPerWorld ? 1 : 0, GEN_THREAD_PRIORITY); + private static final ConcurrentLinkedDeque MAIN_THREAD_QUEUE = new ConcurrentLinkedDeque<>(); + + private final PriorityQueuedExecutor generationExecutor; + //private static final PriorityQueuedExecutor generationExecutor = new PriorityQueuedExecutor("PaperChunkGen", 1); + private final Long2ObjectMap pendingChunks = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); + private final IAsyncTaskHandler asyncHandler; + + private final WorldServer world; + private final IChunkLoader chunkLoader; + private final MinecraftServer server; + private final boolean shouldGenSync; + + public PaperAsyncChunkProvider(WorldServer world, IChunkLoader chunkLoader, InternalChunkGenerator generator, MinecraftServer server) { + super(world, chunkLoader, generator, server); + + this.server = world.getMinecraftServer(); + this.world = world; + this.asyncHandler = server; + this.chunkLoader = chunkLoader; + String worldName = this.world.getWorld().getName(); + this.shouldGenSync = generator instanceof CustomChunkGenerator && !(((CustomChunkGenerator) generator).asyncSupported) || !PaperConfig.asyncChunkGeneration; + this.generationExecutor = PaperConfig.asyncChunkGenThreadPerWorld ? new PriorityQueuedExecutor("PaperChunkGen-" + worldName, shouldGenSync ? 0 : 1, GEN_THREAD_PRIORITY) : SINGLE_GEN_EXECUTOR; + } + + private static Priority calculatePriority(boolean isBlockingMain, boolean priority) { + if (isBlockingMain) { + return Priority.URGENT; + } + + if (priority) { + return Priority.HIGH; + } + + return Priority.NORMAL; + } + + static void stop(MinecraftServer server) { + for (WorldServer world : server.getWorlds()) { + world.getPlayerChunkMap().shutdown(); + } + } + + static void processMainThreadQueue(MinecraftServer server) { + for (WorldServer world : server.getWorlds()) { + processMainThreadQueue(world); + } + } + + static void processMainThreadQueue(World world) { + IChunkProvider chunkProvider = world.getChunkProvider(); + if (chunkProvider instanceof PaperAsyncChunkProvider) { + ((PaperAsyncChunkProvider) chunkProvider).processMainThreadQueue(); + } + } + + private void processMainThreadQueue() { + processMainThreadQueue((PendingChunk) null); + } + private boolean processMainThreadQueue(PendingChunk pending) { + Runnable run; + boolean hadLoad = false; + while ((run = MAIN_THREAD_QUEUE.poll()) != null) { + run.run(); + hadLoad = true; + if (pending != null && pending.hasPosted) { + break; + } + } + return hadLoad; + } + + @Override + public void bumpPriority(ChunkCoordIntPair coords) { + final PendingChunk pending = pendingChunks.get(coords.asLong()); + if (pending != null) { + pending.bumpPriority(Priority.HIGH); + } + } + + @Nullable + @Override + public Chunk getChunkAt(int x, int z, boolean load, boolean gen) { + return getChunkAt(x, z, load, gen, null); + } + + @Nullable + @Override + public Chunk getChunkAt(int x, int z, boolean load, boolean gen, boolean priority, Consumer consumer) { + final long key = ChunkCoordIntPair.asLong(x, z); + final Chunk chunk = this.chunks.get(key); + if (chunk != null || !load) { // return null if we aren't loading + if (consumer != null) { + consumer.accept(chunk); + } + return chunk; + } + return loadOrGenerateChunk(x, z, gen, priority, consumer); // Async overrides this method + } + + private Chunk loadOrGenerateChunk(int x, int z, boolean gen, boolean priority, Consumer consumer) { + return requestChunk(x, z, gen, priority, consumer).getChunk(); + } + + final PendingChunkRequest requestChunk(int x, int z, boolean gen, boolean priority, Consumer consumer) { + try (co.aikar.timings.Timing timing = world.timings.syncChunkLoadTimer.startTiming()) { + final long key = ChunkCoordIntPair.asLong(x, z); + final boolean isChunkThread = isChunkThread(); + final boolean isBlockingMain = consumer == null && server.isMainThread(); + final boolean loadOnThisThread = isChunkThread || isBlockingMain; + final Priority taskPriority = calculatePriority(isBlockingMain, priority); + + // Obtain a PendingChunk + final PendingChunk pending; + synchronized (pendingChunks) { + PendingChunk pendingChunk = pendingChunks.get(key); + if (pendingChunk == null) { + pending = new PendingChunk(x, z, key, gen, taskPriority); + pendingChunks.put(key, pending); + } else if (pendingChunk.hasFinished && gen && !pendingChunk.canGenerate && pendingChunk.chunk == null) { + // need to overwrite the old + pending = new PendingChunk(x, z, key, true, taskPriority); + pendingChunks.put(key, pending); + } else { + pending = pendingChunk; + if (pending.taskPriority != taskPriority) { + pending.bumpPriority(taskPriority); + } + } + } + + // Listen for when result is ready + final CompletableFuture future = new CompletableFuture<>(); + final PendingChunkRequest request = pending.addListener(future, gen, !loadOnThisThread); + + // Chunk Generation can trigger Chunk Loading, those loads may need to convert, and could be slow + // Give an opportunity for urgent tasks to jump in at these times + if (isChunkThread) { + processUrgentTasks(); + } + + if (loadOnThisThread) { + // do loads on main if blocking, or on current if we are a load/gen thread + // gen threads do trigger chunk loads + pending.loadTask.run(); + } + + if (isBlockingMain) { + while (!future.isDone()) { + // We aren't done, obtain lock on queue + synchronized (MAIN_THREAD_QUEUE) { + // We may of received our request now, check it + if (processMainThreadQueue(pending)) { + // If we processed SOMETHING, don't wait + continue; + } + try { + // We got nothing from the queue, wait until something has been added + MAIN_THREAD_QUEUE.wait(1); + } catch (InterruptedException ignored) { + } + } + // Queue has been notified or timed out, process it + processMainThreadQueue(pending); + } + // We should be done AND posted into chunk map now, return it + request.initialReturnChunk = pending.postChunk(); + } else if (consumer == null) { + // This is on another thread + request.initialReturnChunk = future.join(); + } else { + future.thenAccept((c) -> this.asyncHandler.postToMainThread(() -> consumer.accept(c))); + } + + return request; + } + } + + private void processUrgentTasks() { + final PriorityQueuedExecutor executor = PriorityQueuedExecutor.getExecutor(); + if (executor != null) { + executor.processUrgentTasks(); + } + } + + @Override + public CompletableFuture loadAllChunks(Iterable iterable, Consumer consumer) { + final Iterator iterator = iterable.iterator(); + + final List> all = new ArrayList<>(); + while (iterator.hasNext()) { + final ChunkCoordIntPair chunkcoordintpair = iterator.next(); + final CompletableFuture future = new CompletableFuture<>(); + all.add(future); + this.getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z, true, true, chunk -> { + future.complete(chunk); + if (consumer != null) { + consumer.accept(chunk); + } + }); + } + return CompletableFuture.allOf(all.toArray(new CompletableFuture[0])); + } + + boolean chunkGoingToExists(int x, int z) { + synchronized (pendingChunks) { + PendingChunk pendingChunk = pendingChunks.get(ChunkCoordIntPair.asLong(x, z)); + return pendingChunk != null && pendingChunk.canGenerate; + } + } + + private enum PendingStatus { + /** + * Request has just started + */ + STARTED, + /** + * Chunk is attempting to be loaded from disk + */ + LOADING, + /** + * Chunk must generate on main and is pending main + */ + GENERATION_PENDING, + /** + * Chunk is generating + */ + GENERATING, + /** + * Chunk is ready and is pending post to main + */ + PENDING_MAIN, + /** + * Could not load chunk, and did not need to generat + */ + FAIL, + /** + * Fully done with this request (may or may not of loaded) + */ + DONE, + /** + * Chunk load was cancelled (no longer needed) + */ + CANCELLED + } + + public interface CancellableChunkRequest { + void cancel(); + Chunk getChunk(); + } + + public static class PendingChunkRequest implements CancellableChunkRequest { + private final PendingChunk pending; + private final AtomicBoolean cancelled = new AtomicBoolean(false); + private volatile boolean generating; + private volatile Chunk initialReturnChunk; + + private PendingChunkRequest(PendingChunk pending) { + this.pending = pending; + this.cancelled.set(true); + } + + private PendingChunkRequest(PendingChunk pending, boolean gen) { + this.pending = pending; + this.generating = gen; + } + + public void cancel() { + this.pending.cancel(this); + } + + /** + * Will be null on asynchronous loads + */ + @Override @Nullable + public Chunk getChunk() { + return initialReturnChunk; + } + } + + private boolean isLoadThread() { + return EXECUTOR.isCurrentThread(); + } + + private boolean isGenThread() { + return generationExecutor.isCurrentThread(); + } + private boolean isChunkThread() { + return isLoadThread() || isGenThread(); + } + + private class PendingChunk implements Runnable { + private final int x; + private final int z; + private final long key; + private final long started = System.currentTimeMillis(); + private final CompletableFuture loadOnly = new CompletableFuture<>(); + private final CompletableFuture generate = new CompletableFuture<>(); + private final AtomicInteger requests = new AtomicInteger(0); + + private volatile PendingStatus status = PendingStatus.STARTED; + private volatile PriorityQueuedExecutor.PendingTask loadTask; + private volatile PriorityQueuedExecutor.PendingTask genTask; + private volatile Priority taskPriority; + private volatile boolean generating; + private volatile boolean canGenerate; + private volatile boolean isHighPriority; + private volatile boolean hasPosted; + private volatile boolean hasFinished; + private volatile Chunk chunk; + private volatile NBTTagCompound pendingLevel; + + PendingChunk(int x, int z, long key, boolean canGenerate, boolean priority) { + this.x = x; + this.z = z; + this.key = key; + this.canGenerate = canGenerate; + taskPriority = priority ? Priority.HIGH : Priority.NORMAL; + } + + PendingChunk(int x, int z, long key, boolean canGenerate, Priority taskPriority) { + this.x = x; + this.z = z; + this.key = key; + this.canGenerate = canGenerate; + this.taskPriority = taskPriority; + } + + private synchronized void setStatus(PendingStatus status) { + this.status = status; + } + + private Chunk loadChunk(int x, int z) throws IOException { + setStatus(PendingStatus.LOADING); + Object[] data = chunkLoader.loadChunk(world, x, z, null); + if (data != null) { + // Level must be loaded on main + this.pendingLevel = ((NBTTagCompound) data[1]).getCompound("Level"); + return (Chunk) data[0]; + } else { + return null; + } + } + + private Chunk generateChunk() { + synchronized (this) { + if (requests.get() <= 0) { + return null; + } + } + + try { + CompletableFuture pending = new CompletableFuture<>(); + batchScheduler.startBatch(); + batchScheduler.add(new ChunkCoordIntPair(x, z)); + + ProtoChunk protoChunk = batchScheduler.executeBatch().join(); + boolean saved = false; + if (!Bukkit.isPrimaryThread()) { + // If we are async, dispatch later + try { + chunkLoader.saveChunk(world, protoChunk, true); + saved = true; + } catch (IOException | ExceptionWorldConflict e) { + e.printStackTrace(); + } + } + Chunk chunk = new Chunk(world, protoChunk, x, z); + if (saved) { + chunk.setLastSaved(world.getTime()); + } + generateFinished(chunk); + + return chunk; + } catch (Throwable e) { + MinecraftServer.LOGGER.error("Couldn't generate chunk (" +world.getWorld().getName() + ":" + x + "," + z + ")", e); + generateFinished(null); + return null; + } + } + + boolean loadFinished(Chunk chunk) { + if (chunk != null) { + postChunkToMain(chunk); + return false; + } + loadOnly.complete(null); + + synchronized (this) { + boolean cancelled = requests.get() <= 0; + if (!canGenerate || cancelled) { + if (!cancelled) { + setStatus(PendingStatus.FAIL); + } + this.chunk = null; + this.hasFinished = true; + pendingChunks.remove(key); + return false; + } else { + setStatus(PendingStatus.GENERATING); + generating = true; + return true; + } + } + } + + void generateFinished(Chunk chunk) { + synchronized (this) { + this.chunk = chunk; + this.hasFinished = true; + } + if (chunk != null) { + postChunkToMain(chunk); + } else { + synchronized (this) { + pendingChunks.remove(key); + completeFutures(null); + } + } + } + + synchronized private void completeFutures(Chunk chunk) { + loadOnly.complete(chunk); + generate.complete(chunk); + } + + private void postChunkToMain(Chunk chunk) { + synchronized (this) { + setStatus(PendingStatus.PENDING_MAIN); + this.chunk = chunk; + this.hasFinished = true; + } + + if (server.isMainThread()) { + postChunk(); + return; + } + + // Don't post here, even if on main, it must enter the queue so we can exit any open batch + // schedulers, as post stage may trigger a new generation and cause errors + synchronized (MAIN_THREAD_QUEUE) { + if (this.taskPriority == Priority.URGENT) { + MAIN_THREAD_QUEUE.addFirst(this::postChunk); + } else { + MAIN_THREAD_QUEUE.addLast(this::postChunk); + } + MAIN_THREAD_QUEUE.notify(); + } + } + + Chunk postChunk() { + if (!server.isMainThread()) { + throw new IllegalStateException("Must post from main"); + } + synchronized (this) { + if (hasPosted || requests.get() <= 0) { // if pending is 0, all were cancelled + return chunk; + } + hasPosted = true; + } + try { + if (chunk == null) { + chunk = chunks.get(key); + completeFutures(chunk); + return chunk; + } + if (pendingLevel != null) { + chunkLoader.loadEntities(pendingLevel, chunk); + pendingLevel = null; + } + synchronized (chunks) { + final Chunk other = chunks.get(key); + if (other != null) { + this.chunk = other; + completeFutures(other); + return other; + } + if (chunk != null) { + chunks.put(key, chunk); + } + } + + chunk.addEntities(); + + completeFutures(chunk); + return chunk; + } finally { + pendingChunks.remove(key); + setStatus(PendingStatus.DONE); + } + } + + synchronized PendingChunkRequest addListener(CompletableFuture future, boolean gen, boolean autoSubmit) { + requests.incrementAndGet(); + if (loadTask == null) { + // Take care of a race condition in that a request could be cancelled after the synchronize + // on pendingChunks, but before a listener is added, which would erase these pending tasks. + genTask = generationExecutor.createPendingTask(this::generateChunk, taskPriority); + loadTask = EXECUTOR.createPendingTask(this, taskPriority); + if (autoSubmit) { + // We will execute it outside of the synchronized context immediately after + loadTask.submit(); + } + } + + if (hasFinished) { + future.complete(chunk); + return new PendingChunkRequest(this); + } else if (gen) { + canGenerate = true; + generate.thenAccept(future::complete); + } else { + if (generating) { + future.complete(null); + return new PendingChunkRequest(this); + } else { + loadOnly.thenAccept(future::complete); + } + } + + return new PendingChunkRequest(this, gen); + } + + @Override + public void run() { + try { + if (!loadFinished(loadChunk(x, z))) { + return; + } + } catch (Throwable ex) { + MinecraftServer.LOGGER.error("Couldn't load chunk (" +world.getWorld().getName() + ":" + x + "," + z + ")", ex); + if (ex instanceof IOException) { + generateFinished(null); + return; + } + } + + if (shouldGenSync) { + synchronized (this) { + setStatus(PendingStatus.GENERATION_PENDING); + if (this.taskPriority == Priority.URGENT) { + MAIN_THREAD_QUEUE.addFirst(() -> generateFinished(this.generateChunk())); + } else { + MAIN_THREAD_QUEUE.addLast(() -> generateFinished(this.generateChunk())); + } + + } + synchronized (MAIN_THREAD_QUEUE) { + MAIN_THREAD_QUEUE.notify(); + } + } else { + if (isGenThread()) { + // ideally we should never run into 1 chunk generating another chunk... + // but if we do, let's apply same solution + genTask.run(); + } else { + genTask.submit(); + } + } + } + + void bumpPriority() { + bumpPriority(Priority.HIGH); + } + + void bumpPriority(Priority newPriority) { + if (taskPriority.ordinal() >= newPriority.ordinal()) { + return; + } + + this.taskPriority = newPriority; + PriorityQueuedExecutor.PendingTask loadTask = this.loadTask; + PriorityQueuedExecutor.PendingTask genTask = this.genTask; + if (loadTask != null) { + loadTask.bumpPriority(newPriority); + } + if (genTask != null) { + genTask.bumpPriority(newPriority); + } + } + + public synchronized boolean isCancelled() { + return requests.get() <= 0; + } + + public synchronized void cancel(PendingChunkRequest request) { + synchronized (pendingChunks) { + if (!request.cancelled.compareAndSet(false, true)) { + return; + } + + if (requests.decrementAndGet() > 0) { + return; + } + + boolean c1 = genTask.cancel(); + boolean c2 = loadTask.cancel(); + loadTask = null; + genTask = null; + pendingChunks.remove(key); + setStatus(PendingStatus.CANCELLED); + } + } + } + +} diff --git a/src/main/java/net/minecraft/server/PaperLightingQueue.java b/src/main/java/net/minecraft/server/PaperLightingQueue.java new file mode 100644 index 000000000000..9783f3a0d9cb --- /dev/null +++ b/src/main/java/net/minecraft/server/PaperLightingQueue.java @@ -0,0 +1,98 @@ +package net.minecraft.server; + +import co.aikar.timings.Timing; +import com.destroystokyo.paper.PaperConfig; +import it.unimi.dsi.fastutil.objects.ObjectCollection; + +import java.util.ArrayDeque; + +class PaperLightingQueue { + private static final long MAX_TIME = (long) (1000000 * (50 + PaperConfig.maxTickMsLostLightQueue)); + + static void processQueue(long curTime) { + final long startTime = System.nanoTime(); + final long maxTickTime = MAX_TIME - (startTime - curTime); + + if (maxTickTime <= 0) { + return; + } + + START: + for (World world : MinecraftServer.getServer().getWorlds()) { + if (!world.paperConfig.queueLightUpdates) { + continue; + } + + ObjectCollection loadedChunks = ((WorldServer) world).getChunkProvider().chunks.values(); + for (Chunk chunk : loadedChunks.toArray(new Chunk[0])) { + if (chunk.lightingQueue.processQueue(startTime, maxTickTime)) { + break START; + } + } + } + } + + static class LightingQueue extends ArrayDeque { + final private Chunk chunk; + + LightingQueue(Chunk chunk) { + super(); + this.chunk = chunk; + } + + /** + * Processes the lighting queue for this chunk + * + * @param startTime If start Time is 0, we will not limit execution time + * @param maxTickTime Maximum time to spend processing lighting updates + * @return true to abort processing furthur lighting updates + */ + private boolean processQueue(long startTime, long maxTickTime) { + if (this.isEmpty()) { + return false; + } + if (isOutOfTime(maxTickTime, startTime)) { + return true; + } + try (Timing ignored = chunk.world.timings.lightingQueueTimer.startTiming()) { + Runnable lightUpdate; + while ((lightUpdate = this.poll()) != null) { + lightUpdate.run(); + if (isOutOfTime(maxTickTime, startTime)) { + return true; + } + } + } + + return false; + } + + /** + * Flushes lighting updates to unload the chunk + */ + void processUnload() { + if (!chunk.world.paperConfig.queueLightUpdates) { + return; + } + processQueue(0, 0); // No timeout + + final int radius = 1; + for (int x = chunk.locX - radius; x <= chunk.locX + radius; ++x) { + for (int z = chunk.locZ - radius; z <= chunk.locZ + radius; ++z) { + if (x == chunk.locX && z == chunk.locZ) { + continue; + } + + Chunk neighbor = chunk.world.getChunkIfLoaded(x, z); + if (neighbor != null) { + neighbor.lightingQueue.processQueue(0, 0); // No timeout + } + } + } + } + } + + private static boolean isOutOfTime(long maxTickTime, long startTime) { + return startTime > 0 && System.nanoTime() - startTime > maxTickTime; + } +} diff --git a/src/main/java/net/minecraft/server/PathEntity.java b/src/main/java/net/minecraft/server/PathEntity.java new file mode 100644 index 000000000000..5cf7f3bf1bde --- /dev/null +++ b/src/main/java/net/minecraft/server/PathEntity.java @@ -0,0 +1,95 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class PathEntity { + + private final PathPoint[] a; public PathPoint[] getPoints() { return a; } // Paper - OBFHELPER + private PathPoint[] b = new PathPoint[0]; + private PathPoint[] c = new PathPoint[0]; + private PathPoint d; + private int e; public int getNextIndex() { return e; } // Paper - OBFHELPER + private int f; public int getPathCount() { return f; } // Paper - OBFHELPER + public boolean hasNext() { return getNextIndex() < getPathCount(); } // Paper + + public PathEntity(PathPoint[] apathpoint) { + this.a = apathpoint; + this.f = apathpoint.length; + } + + public void a() { + ++this.e; + } + + public boolean b() { + return this.e >= this.f; + } + + @Nullable + public PathPoint getFinalPoint() { return c(); } @Nullable public PathPoint c() { // Paper - OBFHELPER + return this.f > 0 ? this.a[this.f - 1] : null; + } + + public PathPoint a(int i) { + return this.a[i]; + } + + public void a(int i, PathPoint pathpoint) { + this.a[i] = pathpoint; + } + + public int d() { + return this.f; + } + + public void b(int i) { + this.f = i; + } + + public int e() { + return this.e; + } + + public void c(int i) { + this.e = i; + } + + public Vec3D a(Entity entity, int i) { + double d0 = (double) this.a[i].a + (double) ((int) (entity.width + 1.0F)) * 0.5D; + double d1 = (double) this.a[i].b; + double d2 = (double) this.a[i].c + (double) ((int) (entity.width + 1.0F)) * 0.5D; + + return new Vec3D(d0, d1, d2); + } + + public Vec3D a(Entity entity) { + return this.a(entity, this.e); + } + + public Vec3D getNext() { return f(); } public Vec3D f() { // Paper - OBFHELPER + PathPoint pathpoint = this.a[this.e]; + + return new Vec3D((double) pathpoint.a, (double) pathpoint.b, (double) pathpoint.c); + } + + public boolean a(PathEntity pathentity) { + if (pathentity == null) { + return false; + } else if (pathentity.a.length != this.a.length) { + return false; + } else { + for (int i = 0; i < this.a.length; ++i) { + if (this.a[i].a != pathentity.a[i].a || this.a[i].b != pathentity.a[i].b || this.a[i].c != pathentity.a[i].c) { + return false; + } + } + + return true; + } + } + + @Nullable + public PathPoint i() { + return this.d; + } +} diff --git a/src/main/java/net/minecraft/server/PathPoint.java b/src/main/java/net/minecraft/server/PathPoint.java new file mode 100644 index 000000000000..0dd6e46d12e4 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathPoint.java @@ -0,0 +1,93 @@ +package net.minecraft.server; + +public class PathPoint { + + public final int a; public final int getX() { return a; } // Paper - OBFHELPER + public final int b; public final int getY() { return b; } // Paper - OBFHELPER + public final int c; public final int getZ() { return c; } // Paper - OBFHELPER + private final int n; + public int d = -1; + public float e; + public float f; + public float g; + public PathPoint h; + public boolean i; + public float j; + public float k; + public float l; + public PathType m; + + public PathPoint(int i, int j, int k) { + this.m = PathType.BLOCKED; + this.a = i; + this.b = j; + this.c = k; + this.n = b(i, j, k); + } + + public PathPoint a(int i, int j, int k) { + PathPoint pathpoint = new PathPoint(i, j, k); + + pathpoint.d = this.d; + pathpoint.e = this.e; + pathpoint.f = this.f; + pathpoint.g = this.g; + pathpoint.h = this.h; + pathpoint.i = this.i; + pathpoint.j = this.j; + pathpoint.k = this.k; + pathpoint.l = this.l; + pathpoint.m = this.m; + return pathpoint; + } + + public static int b(int i, int j, int k) { + return j & 255 | (i & 32767) << 8 | (k & 32767) << 24 | (i < 0 ? Integer.MIN_VALUE : 0) | (k < 0 ? '\u8000' : 0); + } + + public float a(PathPoint pathpoint) { + float f = (float) (pathpoint.a - this.a); + float f1 = (float) (pathpoint.b - this.b); + float f2 = (float) (pathpoint.c - this.c); + + return MathHelper.c(f * f + f1 * f1 + f2 * f2); + } + + public float b(PathPoint pathpoint) { + float f = (float) (pathpoint.a - this.a); + float f1 = (float) (pathpoint.b - this.b); + float f2 = (float) (pathpoint.c - this.c); + + return f * f + f1 * f1 + f2 * f2; + } + + public float c(PathPoint pathpoint) { + float f = (float) Math.abs(pathpoint.a - this.a); + float f1 = (float) Math.abs(pathpoint.b - this.b); + float f2 = (float) Math.abs(pathpoint.c - this.c); + + return f + f1 + f2; + } + + public boolean equals(Object object) { + if (!(object instanceof PathPoint)) { + return false; + } else { + PathPoint pathpoint = (PathPoint) object; + + return this.n == pathpoint.n && this.a == pathpoint.a && this.b == pathpoint.b && this.c == pathpoint.c; + } + } + + public int hashCode() { + return this.n; + } + + public boolean a() { + return this.d >= 0; + } + + public String toString() { + return this.a + ", " + this.b + ", " + this.c; + } +} diff --git a/src/main/java/net/minecraft/server/Pathfinder.java b/src/main/java/net/minecraft/server/Pathfinder.java new file mode 100644 index 000000000000..89d51d471aaf --- /dev/null +++ b/src/main/java/net/minecraft/server/Pathfinder.java @@ -0,0 +1,123 @@ +package net.minecraft.server; + +import com.google.common.collect.Sets; +import java.util.Set; +import javax.annotation.Nullable; + +public class Pathfinder { + + private final Path a = new Path(); + private final Set b = Sets.newHashSet(); + private final PathPoint[] c = new PathPoint[32]; + private PathfinderAbstract d; public PathfinderAbstract getPathfinder() { return d; } // Paper - OBFHELPER + + public Pathfinder(PathfinderAbstract pathfinderabstract) { + this.d = pathfinderabstract; + } + + @Nullable + public PathEntity a(IBlockAccess iblockaccess, EntityInsentient entityinsentient, Entity entity, float f) { + return this.a(iblockaccess, entityinsentient, entity.locX, entity.getBoundingBox().minY, entity.locZ, f); + } + + @Nullable + public PathEntity a(IBlockAccess iblockaccess, EntityInsentient entityinsentient, BlockPosition blockposition, float f) { + return this.a(iblockaccess, entityinsentient, (double) ((float) blockposition.getX() + 0.5F), (double) ((float) blockposition.getY() + 0.5F), (double) ((float) blockposition.getZ() + 0.5F), f); + } + + @Nullable + private PathEntity a(IBlockAccess iblockaccess, EntityInsentient entityinsentient, double d0, double d1, double d2, float f) { + this.a.a(); + this.d.a(iblockaccess, entityinsentient); + PathPoint pathpoint = this.d.b(); + PathPoint pathpoint1 = this.d.a(d0, d1, d2); + PathEntity pathentity = this.a(pathpoint, pathpoint1, f); + + this.d.a(); + return pathentity; + } + + @Nullable + private PathEntity a(PathPoint pathpoint, PathPoint pathpoint1, float f) { + pathpoint.e = 0.0F; + pathpoint.f = pathpoint.c(pathpoint1); + pathpoint.g = pathpoint.f; + this.a.a(); + this.b.clear(); + this.a.a(pathpoint); + PathPoint pathpoint2 = pathpoint; + int i = 0; + + while (!this.a.e()) { + ++i; + if (i >= 200) { + break; + } + + PathPoint pathpoint3 = this.a.c(); + + if (pathpoint3.equals(pathpoint1)) { + pathpoint2 = pathpoint1; + break; + } + + if (pathpoint3.c(pathpoint1) < pathpoint2.c(pathpoint1)) { + pathpoint2 = pathpoint3; + } + + pathpoint3.i = true; + int j = this.d.a(this.c, pathpoint3, pathpoint1, f); + + for (int k = 0; k < j; ++k) { + PathPoint pathpoint4 = this.c[k]; + float f1 = pathpoint3.c(pathpoint4); + + pathpoint4.j = pathpoint3.j + f1; + pathpoint4.k = f1 + pathpoint4.l; + float f2 = pathpoint3.e + pathpoint4.k; + + if (pathpoint4.j < f && (!pathpoint4.a() || f2 < pathpoint4.e)) { + pathpoint4.h = pathpoint3; + pathpoint4.e = f2; + pathpoint4.f = pathpoint4.c(pathpoint1) + pathpoint4.l; + if (pathpoint4.a()) { + this.a.a(pathpoint4, pathpoint4.e + pathpoint4.f); + } else { + pathpoint4.g = pathpoint4.e + pathpoint4.f; + this.a.a(pathpoint4); + } + } + } + } + + if (pathpoint2 == pathpoint) { + return null; + } else { + PathEntity pathentity = this.a(pathpoint, pathpoint2); + + return pathentity; + } + } + + private PathEntity a(PathPoint pathpoint, PathPoint pathpoint1) { + int i = 1; + + PathPoint pathpoint2; + + for (pathpoint2 = pathpoint1; pathpoint2.h != null; pathpoint2 = pathpoint2.h) { + ++i; + } + + PathPoint[] apathpoint = new PathPoint[i]; + + pathpoint2 = pathpoint1; + --i; + + for (apathpoint[i] = pathpoint1; pathpoint2.h != null; apathpoint[i] = pathpoint2) { + pathpoint2 = pathpoint2.h; + --i; + } + + return new PathEntity(apathpoint); + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderAbstract.java b/src/main/java/net/minecraft/server/PathfinderAbstract.java new file mode 100644 index 000000000000..d722c851395e --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderAbstract.java @@ -0,0 +1,78 @@ +package net.minecraft.server; + +public abstract class PathfinderAbstract { + + protected IBlockAccess a; + protected EntityInsentient b; + public World world; // Paper + protected final IntHashMap c = new IntHashMap<>(); + protected int d; + protected int e; + protected int f; + protected boolean g; + protected boolean h; + protected boolean i; + + public PathfinderAbstract() {} + + public void a(IBlockAccess iblockaccess, EntityInsentient entityinsentient) { + this.a = iblockaccess; + if (iblockaccess instanceof World) world = (World) iblockaccess; // Paper + this.b = entityinsentient; + this.c.c(); + this.d = MathHelper.d(entityinsentient.width + 1.0F); + this.e = MathHelper.d(entityinsentient.length + 1.0F); + this.f = MathHelper.d(entityinsentient.width + 1.0F); + } + + public void a() { + this.a = null; + this.b = null; + } + + protected PathPoint a(int i, int j, int k) { + int l = PathPoint.b(i, j, k); + PathPoint pathpoint = (PathPoint) this.c.get(l); + + if (pathpoint == null) { + pathpoint = new PathPoint(i, j, k); + this.c.a(l, pathpoint); + } + + return pathpoint; + } + + public abstract PathPoint b(); + + public abstract PathPoint a(double d0, double d1, double d2); + + public abstract int a(PathPoint[] apathpoint, PathPoint pathpoint, PathPoint pathpoint1, float f); + + public abstract PathType a(IBlockAccess iblockaccess, int i, int j, int k, EntityInsentient entityinsentient, int l, int i1, int j1, boolean flag, boolean flag1); + + public abstract PathType a(IBlockAccess iblockaccess, int i, int j, int k); + + public void a(boolean flag) { + this.g = flag; + } + + public void b(boolean flag) { + this.h = flag; + } + + public void c(boolean flag) { + this.i = flag; + } + + public boolean c() { + return this.g; + } + + public boolean d() { + return this.h; + } + + public boolean e() { + return this.i; + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoal.java b/src/main/java/net/minecraft/server/PathfinderGoal.java new file mode 100644 index 000000000000..a19853463c2e --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoal.java @@ -0,0 +1,35 @@ +package net.minecraft.server; + +public abstract class PathfinderGoal { + + private int a; + + public PathfinderGoal() {} + + public abstract boolean a(); + + public boolean b() { + return this.a(); + } + + public boolean f() { + return true; + } + + public void c() {} + + public void d() { + onTaskReset(); // Paper + } + public void onTaskReset() {} // Paper + + public void e() {} + + public void a(int i) { + this.a = i; + } + + public int h() { + return this.a; + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalBreakDoor.java b/src/main/java/net/minecraft/server/PathfinderGoalBreakDoor.java new file mode 100644 index 000000000000..74da8a93517b --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalBreakDoor.java @@ -0,0 +1,59 @@ +package net.minecraft.server; + +public class PathfinderGoalBreakDoor extends PathfinderGoalDoorInteract { + + private int d; + private int e = -1; + + public PathfinderGoalBreakDoor(EntityInsentient entityinsentient) { + super(entityinsentient); + } + + public boolean a() { + return !super.a() ? false : (!this.a.world.getGameRules().getBoolean("mobGriefing") ? false : !this.g()); + } + + public void c() { + super.c(); + this.d = 0; + } + + public boolean b() { + double d0 = this.a.c(this.b); + + return this.d <= 240 && !this.g() && d0 < 4.0D; + } + + public void d() { + super.d(); + this.a.world.c(this.a.getId(), this.b, -1); + } + + public void e() { + super.e(); + if (this.a.getRandom().nextInt(20) == 0) { + this.a.world.triggerEffect(1019, this.b, 0); + } + + ++this.d; + int i = (int) ((float) this.d / 240.0F * 10.0F); + + if (i != this.e) { + this.a.world.c(this.a.getId(), this.b, i); + this.e = i; + } + + if (this.d == 240 && this.a.world.getDifficulty() == EnumDifficulty.HARD) { + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreakDoorEvent(this.a, this.b.getX(), this.b.getY(), this.b.getZ()).isCancelled()) { + this.c(); + return; + } + // CraftBukkit end + this.a.world.setAir(this.b); + this.a.world.triggerEffect(1021, this.b, 0); + this.a.world.triggerEffect(2001, this.b, Block.getCombinedId(this.a.world.getType(this.b))); + } + + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalBreed.java b/src/main/java/net/minecraft/server/PathfinderGoalBreed.java new file mode 100644 index 000000000000..55f978768087 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalBreed.java @@ -0,0 +1,132 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.Random; + +public class PathfinderGoalBreed extends PathfinderGoal { + + protected final EntityAnimal animal; + private final Class d; + protected World b; + protected EntityAnimal partner; + private int e; + private final double f; + + public PathfinderGoalBreed(EntityAnimal entityanimal, double d0) { + this(entityanimal, d0, entityanimal.getClass()); + } + + public PathfinderGoalBreed(EntityAnimal entityanimal, double d0, Class oclass) { + this.animal = entityanimal; + this.b = entityanimal.world; + this.d = oclass; + this.f = d0; + this.a(3); + } + + public boolean a() { + if (!this.animal.isInLove()) { + return false; + } else { + this.partner = this.i(); + return this.partner != null; + } + } + + public boolean b() { + return this.partner.isAlive() && this.partner.isInLove() && this.e < 60; + } + + public void d() { + this.partner = null; + this.e = 0; + } + + public void e() { + this.animal.getControllerLook().a(this.partner, 10.0F, (float) this.animal.K()); + this.animal.getNavigation().a((Entity) this.partner, this.f); + ++this.e; + if (this.e >= 60 && this.animal.h(this.partner) < 9.0D) { + this.g(); + } + + } + + private EntityAnimal i() { + List list = this.b.a(this.d, this.animal.getBoundingBox().g(8.0D)); + double d0 = Double.MAX_VALUE; + EntityAnimal entityanimal = null; + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityAnimal entityanimal1 = (EntityAnimal) iterator.next(); + + if (this.animal.mate(entityanimal1) && this.animal.h(entityanimal1) < d0) { + entityanimal = entityanimal1; + d0 = this.animal.h(entityanimal1); + } + } + + return entityanimal; + } + + protected void g() { + EntityAgeable entityageable = this.animal.createChild(this.partner); + + if (entityageable != null) { + // CraftBukkit start - set persistence for tame animals + if (entityageable instanceof EntityTameableAnimal && ((EntityTameableAnimal) entityageable).isTamed()) { + entityageable.persistent = true; + } + // CraftBukkit end + EntityPlayer entityplayer = this.animal.getBreedCause(); + + if (entityplayer == null && this.partner.getBreedCause() != null) { + entityplayer = this.partner.getBreedCause(); + } + // CraftBukkit start - call EntityBreedEvent + int experience = this.animal.getRandom().nextInt(7) + 1; + org.bukkit.event.entity.EntityBreedEvent entityBreedEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreedEvent(entityageable, animal, partner, entityplayer, this.animal.breedItem, experience); + if (entityBreedEvent.isCancelled()) { + return; + } + experience = entityBreedEvent.getExperience(); + // CraftBukkit end + + if (entityplayer != null) { + entityplayer.a(StatisticList.ANIMALS_BRED); + CriterionTriggers.o.a(entityplayer, this.animal, this.partner, entityageable); + } + + this.animal.setAgeRaw(6000); + this.partner.setAgeRaw(6000); + this.animal.resetLove(); + this.partner.resetLove(); + entityageable.setAgeRaw(-24000); + entityageable.setPositionRotation(this.animal.locX, this.animal.locY, this.animal.locZ, 0.0F, 0.0F); + this.b.addEntity(entityageable, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason + Random random = this.animal.getRandom(); + + for (int i = 0; i < 7; ++i) { + double d0 = random.nextGaussian() * 0.02D; + double d1 = random.nextGaussian() * 0.02D; + double d2 = random.nextGaussian() * 0.02D; + double d3 = random.nextDouble() * (double) this.animal.width * 2.0D - (double) this.animal.width; + double d4 = 0.5D + random.nextDouble() * (double) this.animal.length; + double d5 = random.nextDouble() * (double) this.animal.width * 2.0D - (double) this.animal.width; + + this.b.addParticle(Particles.A, this.animal.locX + d3, this.animal.locY + d4, this.animal.locZ + d5, d0, d1, d2); + } + + if (this.b.getGameRules().getBoolean("doMobLoot")) { + // CraftBukkit start - use event experience + if (experience > 0) { + this.b.addEntity(new EntityExperienceOrb(this.b, this.animal.locX, this.animal.locY, this.animal.locZ, experience, org.bukkit.entity.ExperienceOrb.SpawnReason.BREED, entityplayer, entityageable)); // Paper + } + // CraftBukkit end + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalDefendVillage.java b/src/main/java/net/minecraft/server/PathfinderGoalDefendVillage.java new file mode 100644 index 000000000000..d6969293a3cc --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalDefendVillage.java @@ -0,0 +1,38 @@ +package net.minecraft.server; + +public class PathfinderGoalDefendVillage extends PathfinderGoalTarget { + + private final EntityIronGolem a; + private EntityLiving b; + + public PathfinderGoalDefendVillage(EntityIronGolem entityirongolem) { + super(entityirongolem, false, true); + this.a = entityirongolem; + this.a(1); + } + + public boolean a() { + Village village = this.a.l(); + + if (village == null) { + return false; + } else { + this.b = village.b((EntityLiving) this.a); + if (this.b instanceof EntityCreeper) { + return false; + } else if (this.a(this.b, false)) { + return true; + } else if (this.e.getRandom().nextInt(20) == 0) { + this.b = village.c((EntityLiving) this.a); + return this.a(this.b, false); + } else { + return false; + } + } + } + + public void c() { + this.a.setGoalTarget(this.b, org.bukkit.event.entity.EntityTargetEvent.TargetReason.DEFEND_VILLAGE, true); // CraftBukkit - reason + super.c(); + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalDoorInteract.java b/src/main/java/net/minecraft/server/PathfinderGoalDoorInteract.java new file mode 100644 index 000000000000..92eb290523c3 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalDoorInteract.java @@ -0,0 +1,109 @@ +package net.minecraft.server; + +public abstract class PathfinderGoalDoorInteract extends PathfinderGoal { + + protected EntityInsentient a; + protected BlockPosition b; + protected boolean c; + private boolean d; + private float e; + private float f; + + public PathfinderGoalDoorInteract(EntityInsentient entityinsentient) { + this.b = BlockPosition.ZERO; + this.a = entityinsentient; + if (!(entityinsentient.getNavigation() instanceof Navigation)) { + throw new IllegalArgumentException("Unsupported mob type for DoorInteractGoal"); + } + } + + protected boolean g() { + if (!this.c) { + return false; + } else { + IBlockData iblockdata = this.a.world.getType(this.b); + + if (!(iblockdata.getBlock() instanceof BlockDoor)) { + this.c = false; + return false; + } else { + return (Boolean) iblockdata.get(BlockDoor.OPEN); + } + } + } + + protected void a(boolean flag) { + if (this.c) { + IBlockData iblockdata = this.a.world.getType(this.b); + + if (iblockdata.getBlock() instanceof BlockDoor) { + // CraftBukkit start - entities opening doors + org.bukkit.event.entity.EntityInteractEvent event = new org.bukkit.event.entity.EntityInteractEvent(this.a.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(this.a.world, this.b)); + this.a.world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + // CaftBukkit end + + ((BlockDoor) iblockdata.getBlock()).setDoor(this.a.world, this.b, flag); + } + } + + } + + public boolean a() { + if (!this.a.positionChanged) { + return false; + } else { + Navigation navigation = (Navigation) this.a.getNavigation(); + PathEntity pathentity = navigation.m(); + + if (pathentity != null && !pathentity.b() && navigation.g()) { + for (int i = 0; i < Math.min(pathentity.e() + 2, pathentity.d()); ++i) { + PathPoint pathpoint = pathentity.a(i); + + this.b = new BlockPosition(pathpoint.a, pathpoint.b + 1, pathpoint.c); + if (this.a.d((double) this.b.getX(), this.a.locY, (double) this.b.getZ()) <= 2.25D) { + this.c = this.a(this.b); + if (this.c) { + return true; + } + } + } + + this.b = (new BlockPosition(this.a)).up(); + this.c = this.a(this.b); + return this.c; + } else { + return false; + } + } + } + + public boolean b() { + return !this.d; + } + + public void c() { + this.d = false; + this.e = (float) ((double) ((float) this.b.getX() + 0.5F) - this.a.locX); + this.f = (float) ((double) ((float) this.b.getZ() + 0.5F) - this.a.locZ); + } + + public void e() { + float f = (float) ((double) ((float) this.b.getX() + 0.5F) - this.a.locX); + float f1 = (float) ((double) ((float) this.b.getZ() + 0.5F) - this.a.locZ); + float f2 = this.e * f + this.f * f1; + + if (f2 < 0.0F) { + this.d = true; + } + + } + + private boolean a(BlockPosition blockposition) { + IBlockData iblockdata = this.a.world.getType(blockposition); + + return iblockdata.getBlock() instanceof BlockDoor && iblockdata.getMaterial() == Material.WOOD; + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalEatTile.java b/src/main/java/net/minecraft/server/PathfinderGoalEatTile.java new file mode 100644 index 000000000000..e887da2f6736 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalEatTile.java @@ -0,0 +1,78 @@ +package net.minecraft.server; + +import java.util.function.Predicate; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +// CraftBukkit end + +public class PathfinderGoalEatTile extends PathfinderGoal { + + private static final Predicate a = BlockStatePredicate.a(Blocks.GRASS); + private final EntityInsentient b; + private final World c; + private int d; + + public PathfinderGoalEatTile(EntityInsentient entityinsentient) { + this.b = entityinsentient; + this.c = entityinsentient.world; + this.a(7); + } + + public boolean a() { + if (this.b.getRandom().nextInt(this.b.isBaby() ? 50 : 1000) != 0) { + return false; + } else { + BlockPosition blockposition = new BlockPosition(this.b.locX, this.b.locY, this.b.locZ); + + return PathfinderGoalEatTile.a.test(this.c.getType(blockposition)) ? true : this.c.getType(blockposition.down()).getBlock() == Blocks.GRASS_BLOCK; + } + } + + public void c() { + this.d = 40; + this.c.broadcastEntityEffect(this.b, (byte) 10); + this.b.getNavigation().q(); + } + + public void d() { + this.d = 0; + } + + public boolean b() { + return this.d > 0; + } + + public int g() { + return this.d; + } + + public void e() { + this.d = Math.max(0, this.d - 1); + if (this.d == 4) { + BlockPosition blockposition = new BlockPosition(this.b.locX, this.b.locY, this.b.locZ); + + if (PathfinderGoalEatTile.a.test(this.c.getType(blockposition))) { + // CraftBukkit + if (!CraftEventFactory.callEntityChangeBlockEvent(this.b, blockposition, Blocks.AIR.getBlockData(), !this.c.getGameRules().getBoolean("mobGriefing")).isCancelled()) { + this.c.setAir(blockposition, false); + } + + this.b.x(); + } else { + BlockPosition blockposition1 = blockposition.down(); + + if (this.c.getType(blockposition1).getBlock() == Blocks.GRASS_BLOCK) { + // CraftBukkit + if (!CraftEventFactory.callEntityChangeBlockEvent(this.b, blockposition, Blocks.AIR.getBlockData(), !this.c.getGameRules().getBoolean("mobGriefing")).isCancelled()) { + this.c.triggerEffect(2001, blockposition1, Block.getCombinedId(Blocks.GRASS_BLOCK.getBlockData())); + this.c.setTypeAndData(blockposition1, Blocks.DIRT.getBlockData(), 2); + } + + this.b.x(); + } + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalFloat.java b/src/main/java/net/minecraft/server/PathfinderGoalFloat.java new file mode 100644 index 000000000000..38a0b2db1eed --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalFloat.java @@ -0,0 +1,26 @@ +package net.minecraft.server; + +public class PathfinderGoalFloat extends PathfinderGoal { + + private final EntityInsentient a; + + public PathfinderGoalFloat(EntityInsentient entityinsentient) { + this.a = entityinsentient; + if (entityinsentient.getWorld().paperConfig.nerfedMobsShouldJump) entityinsentient.goalFloat = this; // Paper + this.a(4); + entityinsentient.getNavigation().d(true); + } + + public boolean validConditions() { return this.a(); } // Paper - OBFHELPER + public boolean a() { + return this.a.isInWater() && this.a.bY() > 0.4D || this.a.ax(); + } + + public void update() { this.e(); } // Paper - OBFHELPER + public void e() { + if (this.a.getRandom().nextFloat() < 0.8F) { + this.a.getControllerJump().a(); + } + + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalFollowOwner.java b/src/main/java/net/minecraft/server/PathfinderGoalFollowOwner.java new file mode 100644 index 000000000000..d7f219454191 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalFollowOwner.java @@ -0,0 +1,113 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.Location; +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.event.entity.EntityTeleportEvent; +// CraftBukkit end + +public class PathfinderGoalFollowOwner extends PathfinderGoal { + + private final EntityTameableAnimal b; + private EntityLiving c; + protected final IWorldReader a; + private final double d; + private final NavigationAbstract e; + private int f; + private final float g; + private final float h; + private float i; + + public PathfinderGoalFollowOwner(EntityTameableAnimal entitytameableanimal, double d0, float f, float f1) { + this.b = entitytameableanimal; + this.a = entitytameableanimal.world; + this.d = d0; + this.e = entitytameableanimal.getNavigation(); + this.h = f; + this.g = f1; + this.a(3); + if (!(entitytameableanimal.getNavigation() instanceof Navigation) && !(entitytameableanimal.getNavigation() instanceof NavigationFlying)) { + throw new IllegalArgumentException("Unsupported mob type for FollowOwnerGoal"); + } + } + + public boolean a() { + EntityLiving entityliving = this.b.getOwner(); + + if (entityliving == null) { + return false; + } else if (entityliving instanceof EntityHuman && ((EntityHuman) entityliving).isSpectator()) { + return false; + } else if (this.b.isSitting()) { + return false; + } else if (this.b.h(entityliving) < (double) (this.h * this.h)) { + return false; + } else { + this.c = entityliving; + return true; + } + } + + public boolean b() { + return !this.e.p() && this.b.h(this.c) > (double) (this.g * this.g) && !this.b.isSitting(); + } + + public void c() { + this.f = 0; + this.i = this.b.a(PathType.WATER); + this.b.a(PathType.WATER, 0.0F); + } + + public void d() { + this.c = null; + this.e.q(); + this.b.a(PathType.WATER, this.i); + } + + public void e() { + this.b.getControllerLook().a(this.c, 10.0F, (float) this.b.K()); + if (!this.b.isSitting()) { + if (--this.f <= 0) { + this.f = 10; + if (!this.e.a((Entity) this.c, this.d)) { + if (!this.b.isLeashed() && !this.b.isPassenger()) { + if (this.b.h(this.c) >= 144.0D) { + int i = MathHelper.floor(this.c.locX) - 2; + int j = MathHelper.floor(this.c.locZ) - 2; + int k = MathHelper.floor(this.c.getBoundingBox().minY); + + for (int l = 0; l <= 4; ++l) { + for (int i1 = 0; i1 <= 4; ++i1) { + if ((l < 1 || i1 < 1 || l > 3 || i1 > 3) && this.a(i, j, k, l, i1)) { + // CraftBukkit start + CraftEntity entity = this.b.getBukkitEntity(); + Location to = new Location(entity.getWorld(), (double) ((float) (i + l) + 0.5F), (double) k, (double) ((float) (j + i1) + 0.5F), this.b.yaw, this.b.pitch); + EntityTeleportEvent event = new EntityTeleportEvent(entity, entity.getLocation(), to); + this.b.world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + to = event.getTo(); + + this.b.setPositionRotation(to.getX(), to.getY(), to.getZ(), to.getYaw(), to.getPitch()); + // CraftBukkit end + this.e.q(); + return; + } + } + } + + } + } + } + } + } + } + + protected boolean a(int i, int j, int k, int l, int i1) { + BlockPosition blockposition = new BlockPosition(i + l, k - 1, j + i1); + IBlockData iblockdata = this.a.getType(blockposition); + + return iblockdata.c(this.a, blockposition, EnumDirection.DOWN) == EnumBlockFaceShape.SOLID && iblockdata.a((Entity) this.b) && this.a.isEmpty(blockposition.up()) && this.a.isEmpty(blockposition.up(2)); + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalGotoTarget.java b/src/main/java/net/minecraft/server/PathfinderGoalGotoTarget.java new file mode 100644 index 000000000000..756d63239cc6 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalGotoTarget.java @@ -0,0 +1,116 @@ +package net.minecraft.server; + +public abstract class PathfinderGoalGotoTarget extends PathfinderGoal { + + private final EntityCreature f;public EntityCreature getEntity() { return f; } // Paper - OBFHELPER + public double a; + protected int b; + protected int c; + private int g; + protected BlockPosition d; public BlockPosition getTarget() { return d; } public void setTarget(BlockPosition pos) { this.d = pos; getEntity().movingTarget = pos != BlockPosition.ZERO ? pos : null; } // Paper - OBFHELPER + private boolean h; + private final int i; + private final int j; + public int e; + + public PathfinderGoalGotoTarget(EntityCreature entitycreature, double d0, int i) { + this(entitycreature, d0, i, 1); + } + // Paper start - activation range improvements + @Override + public void onTaskReset() { + super.onTaskReset(); + setTarget(BlockPosition.ZERO); + } + // Paper end + + public PathfinderGoalGotoTarget(EntityCreature entitycreature, double d0, int i, int j) { + this.d = BlockPosition.ZERO; + this.f = entitycreature; + this.a = d0; + this.i = i; + this.e = 0; + this.j = j; + this.a(5); + } + + public boolean a() { + if (this.b > 0) { + --this.b; + return false; + } else { + this.b = this.a(this.f); + return this.l(); + } + } + + protected int a(EntityCreature entitycreature) { + return 200 + entitycreature.getRandom().nextInt(200); + } + + public boolean b() { + return this.c >= -this.g && this.c <= 1200 && this.a(this.f.world, this.d); + } + + public void c() { + this.f.getNavigation().a((double) ((float) this.d.getX()) + 0.5D, (double) (this.d.getY() + 1), (double) ((float) this.d.getZ()) + 0.5D, this.a); + this.c = 0; + this.g = this.f.getRandom().nextInt(this.f.getRandom().nextInt(1200) + 1200) + 1200; + } + + public double g() { + return 1.0D; + } + + public void e() { + if (this.f.d(this.d.up()) > this.g()) { + this.h = false; + ++this.c; + if (this.i()) { + this.f.getNavigation().a((double) ((float) this.d.getX()) + 0.5D, (double) (this.d.getY() + this.j()), (double) ((float) this.d.getZ()) + 0.5D, this.a); + } + } else { + this.h = true; + --this.c; + } + + } + + public boolean i() { + return this.c % 40 == 0; + } + + public int j() { + return 1; + } + + protected boolean k() { + return this.h; + } + + private boolean l() { + int i = this.i; + int j = this.j; + BlockPosition blockposition = new BlockPosition(this.f); + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + for (int k = this.e; k <= j; k = k > 0 ? -k : 1 - k) { + for (int l = 0; l < i; ++l) { + for (int i1 = 0; i1 <= l; i1 = i1 > 0 ? -i1 : 1 - i1) { + for (int j1 = i1 < l && i1 > -l ? l : 0; j1 <= l; j1 = j1 > 0 ? -j1 : 1 - j1) { + blockposition_mutableblockposition.g(blockposition).d(i1, k - 1, j1); + if (this.f.f((BlockPosition) blockposition_mutableblockposition) && this.a(this.f.world, blockposition_mutableblockposition)) { + this.d = blockposition_mutableblockposition; + setTarget(blockposition_mutableblockposition.toBlockPosition()); // Paper + return true; + } + } + } + } + } + + return false; + } + + protected abstract boolean a(IWorldReader iworldreader, BlockPosition blockposition); +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalHorseTrap.java b/src/main/java/net/minecraft/server/PathfinderGoalHorseTrap.java new file mode 100644 index 000000000000..887e4461f380 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalHorseTrap.java @@ -0,0 +1,67 @@ +package net.minecraft.server; + +public class PathfinderGoalHorseTrap extends PathfinderGoal { + + private final EntityHorseSkeleton a; + + public PathfinderGoalHorseTrap(EntityHorseSkeleton entityhorseskeleton) { + this.a = entityhorseskeleton; + } + + public boolean a() { + return this.a.world.isPlayerNearby(this.a.locX, this.a.locY, this.a.locZ, 10.0D); + } + + public void e() { + if (!new com.destroystokyo.paper.event.entity.SkeletonHorseTrapEvent((org.bukkit.entity.SkeletonHorse) this.a.getBukkitEntity()).callEvent()) return; // Paper + DifficultyDamageScaler difficultydamagescaler = this.a.world.getDamageScaler(new BlockPosition(this.a)); + + this.a.s(false); + this.a.setTamed(true); + this.a.setAgeRaw(0); + ((WorldServer) this.a.world).strikeLightning(new EntityLightning(this.a.world, this.a.locX, this.a.locY, this.a.locZ, true), org.bukkit.event.weather.LightningStrikeEvent.Cause.TRAP); // CraftBukkit + EntitySkeleton entityskeleton = this.a(difficultydamagescaler, this.a); + + if (entityskeleton != null) entityskeleton.startRiding(this.a); // CraftBukkit + + for (int i = 0; i < 3; ++i) { + EntityHorseAbstract entityhorseabstract = this.a(difficultydamagescaler); + if (entityhorseabstract == null) continue; // CraftBukkit + EntitySkeleton entityskeleton1 = this.a(difficultydamagescaler, entityhorseabstract); + + if (entityskeleton1 != null) entityskeleton1.startRiding(entityhorseabstract); // CraftBukkit + entityhorseabstract.f(this.a.getRandom().nextGaussian() * 0.5D, 0.0D, this.a.getRandom().nextGaussian() * 0.5D); + } + + } + + private EntityHorseAbstract a(DifficultyDamageScaler difficultydamagescaler) { + EntityHorseSkeleton entityhorseskeleton = EntityTypes.SKELETON_HORSE.create(a.world); // Paper + + entityhorseskeleton.prepare(difficultydamagescaler, (GroupDataEntity) null, (NBTTagCompound) null); + entityhorseskeleton.setPosition(this.a.locX, this.a.locY, this.a.locZ); + entityhorseskeleton.noDamageTicks = 60; + entityhorseskeleton.di(); + entityhorseskeleton.setTamed(true); + entityhorseskeleton.setAgeRaw(0); + if (!entityhorseskeleton.world.addEntity(entityhorseskeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.TRAP)) return null; // CraftBukkit + return entityhorseskeleton; + } + + private EntitySkeleton a(DifficultyDamageScaler difficultydamagescaler, EntityHorseAbstract entityhorseabstract) { + EntitySkeleton entityskeleton = EntityTypes.SKELETON.create(entityhorseabstract.world); // Paper + + entityskeleton.prepare(difficultydamagescaler, (GroupDataEntity) null, (NBTTagCompound) null); + entityskeleton.setPosition(entityhorseabstract.locX, entityhorseabstract.locY, entityhorseabstract.locZ); + entityskeleton.noDamageTicks = 60; + entityskeleton.di(); + if (entityskeleton.getEquipment(EnumItemSlot.HEAD).isEmpty()) { + entityskeleton.setSlot(EnumItemSlot.HEAD, new ItemStack(Items.IRON_HELMET)); + } + + entityskeleton.setSlot(EnumItemSlot.MAINHAND, EnchantmentManager.a(entityskeleton.getRandom(), entityskeleton.getItemInMainHand(), (int) (5.0F + difficultydamagescaler.d() * (float) entityskeleton.getRandom().nextInt(18)), false)); + entityskeleton.setSlot(EnumItemSlot.HEAD, EnchantmentManager.a(entityskeleton.getRandom(), entityskeleton.getEquipment(EnumItemSlot.HEAD), (int) (5.0F + difficultydamagescaler.d() * (float) entityskeleton.getRandom().nextInt(18)), false)); + if (!entityskeleton.world.addEntity(entityskeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.JOCKEY)) return null; // CraftBukkit + return entityskeleton; + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalHurtByTarget.java b/src/main/java/net/minecraft/server/PathfinderGoalHurtByTarget.java new file mode 100644 index 000000000000..0b1ed139fa56 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalHurtByTarget.java @@ -0,0 +1,71 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; + +public class PathfinderGoalHurtByTarget extends PathfinderGoalTarget { + + private final boolean a; + private int b; + private final Class[] c; + + public PathfinderGoalHurtByTarget(EntityCreature entitycreature, boolean flag, Class... aclass) { + super(entitycreature, true); + this.a = flag; + this.c = aclass; + this.a(1); + } + + public boolean a() { + int i = this.e.cg(); + EntityLiving entityliving = this.e.getLastDamager(); + + return i != this.b && entityliving != null && this.a(entityliving, false); + } + + public void c() { + this.e.setGoalTarget(this.e.getLastDamager(), org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_ENTITY, true); // CraftBukkit - reason + this.g = this.e.getGoalTarget(); + this.b = this.e.cg(); + this.h = 300; + if (this.a) { + this.g(); + } + + super.c(); + } + + protected void g() { + double d0 = this.i(); + List list = this.e.world.a(this.e.getClass(), (new AxisAlignedBB(this.e.locX, this.e.locY, this.e.locZ, this.e.locX + 1.0D, this.e.locY + 1.0D, this.e.locZ + 1.0D)).grow(d0, 10.0D, d0)); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityCreature entitycreature = (EntityCreature) iterator.next(); + + if (this.e != entitycreature && entitycreature.getGoalTarget() == null && (!(this.e instanceof EntityTameableAnimal) || ((EntityTameableAnimal) this.e).getOwner() == ((EntityTameableAnimal) entitycreature).getOwner()) && !entitycreature.r(this.e.getLastDamager())) { + boolean flag = false; + Class[] aclass = this.c; + int i = aclass.length; + + for (int j = 0; j < i; ++j) { + Class oclass = aclass[j]; + + if (entitycreature.getClass() == oclass) { + flag = true; + break; + } + } + + if (!flag) { + this.a(entitycreature, this.e.getLastDamager()); + } + } + } + + } + + protected void a(EntityCreature entitycreature, EntityLiving entityliving) { + entitycreature.setGoalTarget(entityliving, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_NEARBY_ENTITY, true); // CraftBukkit - reason + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalMakeLove.java b/src/main/java/net/minecraft/server/PathfinderGoalMakeLove.java new file mode 100644 index 000000000000..d0ff2a871e51 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalMakeLove.java @@ -0,0 +1,98 @@ +package net.minecraft.server; + +public class PathfinderGoalMakeLove extends PathfinderGoal { + + private final EntityVillager a; + private EntityVillager b; + private final World c; + private int d; + private Village e; + + public PathfinderGoalMakeLove(EntityVillager entityvillager) { + this.a = entityvillager; + this.c = entityvillager.world; + this.a(3); + } + + public boolean a() { + if (this.a.getAge() != 0) { + return false; + } else if (this.a.getRandom().nextInt(500) != 0) { + return false; + } else { + this.e = this.c.af().getClosestVillage(new BlockPosition(this.a), 0); + if (this.e == null) { + return false; + } else if (this.g() && this.a.u(true)) { + Entity entity = this.c.a(EntityVillager.class, this.a.getBoundingBox().grow(8.0D, 3.0D, 8.0D), (Entity) this.a); + + if (entity == null) { + return false; + } else { + this.b = (EntityVillager) entity; + return this.b.getAge() == 0 && this.b.u(true); + } + } else { + return false; + } + } + } + + public void c() { + this.d = 300; + this.a.s(true); + } + + public void d() { + this.e = null; + this.b = null; + this.a.s(false); + } + + public boolean b() { + return this.d >= 0 && this.g() && this.a.getAge() == 0 && this.a.u(false); + } + + public void e() { + --this.d; + this.a.getControllerLook().a(this.b, 10.0F, 30.0F); + if (this.a.h(this.b) > 2.25D) { + this.a.getNavigation().a((Entity) this.b, 0.25D); + } else if (this.d == 0 && this.b.isInLove()) { + this.i(); + } + + if (this.a.getRandom().nextInt(35) == 0) { + this.c.broadcastEntityEffect(this.a, (byte) 12); + } + + } + + private boolean g() { + if (!this.e.i()) { + return false; + } else { + int i = (int) ((double) ((float) this.e.c()) * 0.35D); + + return this.e.e() < i; + } + } + + private void i() { + EntityVillager entityvillager = this.a.createChild(this.b); + // CraftBukkit start - call EntityBreedEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreedEvent(entityvillager, this.a, this.b, null, null, 0).isCancelled()) { + return; + } + // CraftBukkit end + + this.b.setAgeRaw(6000); + this.a.setAgeRaw(6000); + this.b.v(false); + this.a.v(false); + entityvillager.setAgeRaw(-24000); + entityvillager.setPositionRotation(this.a.locX, this.a.locY, this.a.locZ, 0.0F, 0.0F); + this.c.addEntity(entityvillager, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason + this.c.broadcastEntityEffect(entityvillager, (byte) 12); + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalNearestAttackableTarget.java b/src/main/java/net/minecraft/server/PathfinderGoalNearestAttackableTarget.java new file mode 100644 index 000000000000..f64532f8bfc2 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalNearestAttackableTarget.java @@ -0,0 +1,87 @@ +package net.minecraft.server; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public class PathfinderGoalNearestAttackableTarget extends PathfinderGoalTarget { + + protected final Class a; + private final int i; + protected final PathfinderGoalNearestAttackableTarget.DistanceComparator b; + protected final Predicate c; + protected T d; + + public PathfinderGoalNearestAttackableTarget(EntityCreature entitycreature, Class oclass, boolean flag) { + this(entitycreature, oclass, flag, false); + } + + public PathfinderGoalNearestAttackableTarget(EntityCreature entitycreature, Class oclass, boolean flag, boolean flag1) { + this(entitycreature, oclass, 10, flag, flag1, (Predicate) null); + } + + public PathfinderGoalNearestAttackableTarget(EntityCreature entitycreature, Class oclass, int i, boolean flag, boolean flag1, @Nullable Predicate predicate) { + super(entitycreature, flag, flag1); + this.a = oclass; + this.i = i; + this.b = new PathfinderGoalNearestAttackableTarget.DistanceComparator(entitycreature); + this.a(1); + this.c = (entityliving) -> { + return entityliving == null ? false : (predicate != null && !predicate.test(entityliving) ? false : (!IEntitySelector.f.test(entityliving) ? false : this.a(entityliving, false))); + }; + } + + public boolean a() { + if (this.i > 0 && this.e.getRandom().nextInt(this.i) != 0) { + return false; + } else if (this.a != EntityHuman.class && this.a != EntityPlayer.class) { + List list = this.e.world.a(this.a, this.a(this.i()), this.c); + + if (list.isEmpty()) { + return false; + } else { + Collections.sort(list, this.b); + this.d = (T) list.get(0); // CraftBukkit - fix decompile error + return true; + } + } else { + this.d = (T) this.e.world.a(this.e.locX, this.e.locY + (double) this.e.getHeadHeight(), this.e.locZ, this.i(), this.i(), new Function() { // CraftBukkit - fix decompile error + @Nullable + public Double apply(@Nullable EntityHuman entityhuman) { + ItemStack itemstack = entityhuman.getEquipment(EnumItemSlot.HEAD); + + return (!(PathfinderGoalNearestAttackableTarget.this.e instanceof EntitySkeleton) || itemstack.getItem() != Items.SKELETON_SKULL) && (!(PathfinderGoalNearestAttackableTarget.this.e instanceof EntityZombie) || itemstack.getItem() != Items.ZOMBIE_HEAD) && (!(PathfinderGoalNearestAttackableTarget.this.e instanceof EntityCreeper) || itemstack.getItem() != Items.CREEPER_HEAD) ? 1.0D : 0.5D; + } + }, (Predicate) this.c); // CraftBukkit - fix decompile error + return this.d != null; + } + } + + protected AxisAlignedBB a(double d0) { + return this.e.getBoundingBox().grow(d0, 4.0D, d0); + } + + public void c() { + this.e.setGoalTarget(this.d, d instanceof EntityPlayer ? org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER : org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY, true); // Craftbukkit - reason + super.c(); + } + + public static class DistanceComparator implements Comparator { + + private final Entity a; + + public DistanceComparator(Entity entity) { + this.a = entity; + } + + public int compare(Entity entity, Entity entity1) { + double d0 = this.a.h(entity); + double d1 = this.a.h(entity1); + + return d0 < d1 ? -1 : (d0 > d1 ? 1 : 0); + } + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalNearestAttackableTargetInsentient.java b/src/main/java/net/minecraft/server/PathfinderGoalNearestAttackableTargetInsentient.java new file mode 100644 index 000000000000..8eaa641b814f --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalNearestAttackableTargetInsentient.java @@ -0,0 +1,79 @@ +package net.minecraft.server; + +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class PathfinderGoalNearestAttackableTargetInsentient extends PathfinderGoal { + + private static final Logger a = LogManager.getLogger(); + private final EntityInsentient b; + private final Predicate c; + private final PathfinderGoalNearestAttackableTarget.DistanceComparator d; + private EntityLiving e; + private final Class f; + + public PathfinderGoalNearestAttackableTargetInsentient(EntityInsentient entityinsentient, Class oclass) { + this.b = entityinsentient; + this.f = oclass; + if (entityinsentient instanceof EntityCreature) { + PathfinderGoalNearestAttackableTargetInsentient.a.warn("Use NearestAttackableTargetGoal.class for PathfinerMob mobs!"); + } + + this.c = (entityliving) -> { + double d0 = this.g(); + + if (entityliving.isSneaking()) { + d0 *= 0.800000011920929D; + } + + return entityliving.isInvisible() ? false : ((double) entityliving.g(this.b) > d0 ? false : PathfinderGoalTarget.a(this.b, entityliving, false, true)); + }; + this.d = new PathfinderGoalNearestAttackableTarget.DistanceComparator(entityinsentient); + } + + public boolean a() { + double d0 = this.g(); + List list = this.b.world.a(this.f, this.b.getBoundingBox().grow(d0, 4.0D, d0), this.c); + + Collections.sort(list, this.d); + if (list.isEmpty()) { + return false; + } else { + this.e = (EntityLiving) list.get(0); + return true; + } + } + + public boolean b() { + EntityLiving entityliving = this.b.getGoalTarget(); + + if (entityliving == null) { + return false; + } else if (!entityliving.isAlive()) { + return false; + } else { + double d0 = this.g(); + + return this.b.h(entityliving) > d0 * d0 ? false : !(entityliving instanceof EntityPlayer) || !((EntityPlayer) entityliving).playerInteractManager.isCreative(); + } + } + + public void c() { + this.b.setGoalTarget(this.e, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY, true); // CraftBukkit - reason + super.c(); + } + + public void d() { + this.b.setGoalTarget((EntityLiving) null); + super.c(); + } + + protected double g() { + AttributeInstance attributeinstance = this.b.getAttributeInstance(GenericAttributes.FOLLOW_RANGE); + + return attributeinstance == null ? 16.0D : attributeinstance.getValue(); + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalOwnerHurtByTarget.java b/src/main/java/net/minecraft/server/PathfinderGoalOwnerHurtByTarget.java new file mode 100644 index 000000000000..8344860244ea --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalOwnerHurtByTarget.java @@ -0,0 +1,42 @@ +package net.minecraft.server; + +public class PathfinderGoalOwnerHurtByTarget extends PathfinderGoalTarget { + + private final EntityTameableAnimal a; + private EntityLiving b; + private int c; + + public PathfinderGoalOwnerHurtByTarget(EntityTameableAnimal entitytameableanimal) { + super(entitytameableanimal, false); + this.a = entitytameableanimal; + this.a(1); + } + + public boolean a() { + if (!this.a.isTamed()) { + return false; + } else { + EntityLiving entityliving = this.a.getOwner(); + + if (entityliving == null) { + return false; + } else { + this.b = entityliving.getLastDamager(); + int i = entityliving.cg(); + + return i != this.c && this.a(this.b, false) && this.a.a(this.b, entityliving); + } + } + } + + public void c() { + this.e.setGoalTarget(this.b, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_OWNER, true); // CraftBukkit - reason + EntityLiving entityliving = this.a.getOwner(); + + if (entityliving != null) { + this.c = entityliving.cg(); + } + + super.c(); + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalOwnerHurtTarget.java b/src/main/java/net/minecraft/server/PathfinderGoalOwnerHurtTarget.java new file mode 100644 index 000000000000..7ea80b8907d7 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalOwnerHurtTarget.java @@ -0,0 +1,42 @@ +package net.minecraft.server; + +public class PathfinderGoalOwnerHurtTarget extends PathfinderGoalTarget { + + private final EntityTameableAnimal a; + private EntityLiving b; + private int c; + + public PathfinderGoalOwnerHurtTarget(EntityTameableAnimal entitytameableanimal) { + super(entitytameableanimal, false); + this.a = entitytameableanimal; + this.a(1); + } + + public boolean a() { + if (!this.a.isTamed()) { + return false; + } else { + EntityLiving entityliving = this.a.getOwner(); + + if (entityliving == null) { + return false; + } else { + this.b = entityliving.ch(); + int i = entityliving.ci(); + + return i != this.c && this.a(this.b, false) && this.a.a(this.b, entityliving); + } + } + } + + public void c() { + this.e.setGoalTarget(this.b, org.bukkit.event.entity.EntityTargetEvent.TargetReason.OWNER_ATTACKED_TARGET, true); // CraftBukkit - reason + EntityLiving entityliving = this.a.getOwner(); + + if (entityliving != null) { + this.c = entityliving.ci(); + } + + super.c(); + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalPanic.java b/src/main/java/net/minecraft/server/PathfinderGoalPanic.java new file mode 100644 index 000000000000..9377c1fba372 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalPanic.java @@ -0,0 +1,93 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class PathfinderGoalPanic extends PathfinderGoal { + + protected final EntityCreature a; + protected double b; + protected double c; + protected double d; + protected double e; + + public PathfinderGoalPanic(EntityCreature entitycreature, double d0) { + this.a = entitycreature; + this.b = d0; + this.a(1); + } + + public boolean a() { + if (this.a.getLastDamager() == null && !this.a.isBurning()) { + return false; + } else { + if (this.a.isBurning()) { + BlockPosition blockposition = this.a(this.a.world, this.a, 5, 4); + + if (blockposition != null) { + this.c = (double) blockposition.getX(); + this.d = (double) blockposition.getY(); + this.e = (double) blockposition.getZ(); + return true; + } + } + + return this.g(); + } + } + + protected boolean g() { + Vec3D vec3d = RandomPositionGenerator.a(this.a, 5, 4); + + if (vec3d == null) { + return false; + } else { + this.c = vec3d.x; + this.d = vec3d.y; + this.e = vec3d.z; + return true; + } + } + + public void c() { + this.a.getNavigation().a(this.c, this.d, this.e, this.b); + } + + public boolean b() { + // CraftBukkit start - introduce a temporary timeout hack until this is fixed properly + if ((this.a.ticksLived - this.a.hurtTimestamp) > 100) { + this.a.setLastDamager((EntityLiving) null); + return false; + } + // CraftBukkit end + return !this.a.getNavigation().p(); + } + + @Nullable + protected BlockPosition a(IBlockAccess iblockaccess, Entity entity, int i, int j) { + BlockPosition blockposition = new BlockPosition(entity); + int k = blockposition.getX(); + int l = blockposition.getY(); + int i1 = blockposition.getZ(); + float f = (float) (i * i * j * 2); + BlockPosition blockposition1 = null; + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + for (int j1 = k - i; j1 <= k + i; ++j1) { + for (int k1 = l - j; k1 <= l + j; ++k1) { + for (int l1 = i1 - i; l1 <= i1 + i; ++l1) { + blockposition_mutableblockposition.c(j1, k1, l1); + if (iblockaccess.getFluid(blockposition_mutableblockposition).a(TagsFluid.WATER)) { + float f1 = (float) ((j1 - k) * (j1 - k) + (k1 - l) * (k1 - l) + (l1 - i1) * (l1 - i1)); + + if (f1 < f) { + f = f1; + blockposition1 = new BlockPosition(blockposition_mutableblockposition); + } + } + } + } + } + + return blockposition1; + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalRemoveBlock.java b/src/main/java/net/minecraft/server/PathfinderGoalRemoveBlock.java new file mode 100644 index 000000000000..3ca32123bbe6 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalRemoveBlock.java @@ -0,0 +1,131 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; +// CraftBukkit start +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.event.entity.EntityInteractEvent; +// CraftBukkit end + +public class PathfinderGoalRemoveBlock extends PathfinderGoalGotoTarget { + + private final Block f; + private final EntityInsentient entity; + private int h; + private World world; // Paper + + public PathfinderGoalRemoveBlock(Block block, EntityCreature entitycreature, double d0, int i) { + super(entitycreature, d0, 24, i); + this.f = block; + this.entity = entitycreature; + this.world = entitycreature.world; // Paper + } + + public boolean a() { + return !this.entity.world.getGameRules().getBoolean("mobGriefing") ? false : (this.entity.getRandom().nextInt(20) != 0 ? false : super.a()); + } + + protected int a(EntityCreature entitycreature) { + return 0; + } + + public boolean b() { + return super.b(); + } + + public void d() { + super.d(); + this.entity.fallDistance = 1.0F; + } + + public void c() { + super.c(); + this.h = 0; + } + + public void a(GeneratorAccess generatoraccess, BlockPosition blockposition) {} + + public void a(World world, BlockPosition blockposition) {} + + public void e() { + super.e(); + World world = this.entity.world; + BlockPosition blockposition = new BlockPosition(this.entity); + BlockPosition blockposition1 = this.a(blockposition, (IBlockAccess) world); + Random random = this.entity.getRandom(); + + if (this.k() && blockposition1 != null) { + if (this.h > 0) { + this.entity.motY = 0.3D; + if (!world.isClientSide) { + double d0 = 0.08D; + + ((WorldServer) world).a(new ParticleParamItem(Particles.C, new ItemStack(Items.EGG)), (double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.7D, (double) blockposition1.getZ() + 0.5D, 3, ((double) random.nextFloat() - 0.5D) * 0.08D, ((double) random.nextFloat() - 0.5D) * 0.08D, ((double) random.nextFloat() - 0.5D) * 0.08D, 0.15000000596046448D); + } + } + + if (this.h % 2 == 0) { + this.entity.motY = -0.3D; + if (this.h % 6 == 0) { + this.a((GeneratorAccess) world, this.d); + } + } + + if (this.h > 60) { + // CraftBukkit start - Step on eggs + EntityInteractEvent event = new EntityInteractEvent(this.entity.getBukkitEntity(), CraftBlock.at(world, blockposition1)); + world.getServer().getPluginManager().callEvent((EntityInteractEvent) event); + + if (event.isCancelled()) { + return; + } + // CraftBukkit end + world.setAir(blockposition1); + if (!world.isClientSide) { + for (int i = 0; i < 20; ++i) { + double d1 = random.nextGaussian() * 0.02D; + double d2 = random.nextGaussian() * 0.02D; + double d3 = random.nextGaussian() * 0.02D; + + ((WorldServer) world).a(Particles.J, (double) blockposition1.getX() + 0.5D, (double) blockposition1.getY(), (double) blockposition1.getZ() + 0.5D, 1, d1, d2, d3, 0.15000000596046448D); + } + + this.a(world, this.d); + } + } + + ++this.h; + } + + } + + @Nullable + private BlockPosition a(BlockPosition blockposition, IBlockAccess iblockaccess) { + Block block = world.getBlockIfLoaded(blockposition); // Paper + if (block == null) return null; // Paper + if (block == this.f) { // Paper + return blockposition; + } else { + BlockPosition[] ablockposition = new BlockPosition[] { blockposition.down(), blockposition.west(), blockposition.east(), blockposition.north(), blockposition.south(), blockposition.down().down()}; + BlockPosition[] ablockposition1 = ablockposition; + int i = ablockposition.length; + + for (int j = 0; j < i; ++j) { + BlockPosition blockposition1 = ablockposition1[j]; + + if (world.getBlockIfLoaded(blockposition1) == this.f) { // Paper + return blockposition1; + } + } + + return null; + } + } + + protected boolean a(IWorldReader iworldreader, BlockPosition blockposition) { + Block block = world.getBlockIfLoaded(blockposition); // Paper + if (block == null) return false; // Paper + + return block == this.f && iworldreader.getType(blockposition.up()).isAir() && iworldreader.getType(blockposition.up(2)).isAir(); + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalSelector.java b/src/main/java/net/minecraft/server/PathfinderGoalSelector.java new file mode 100644 index 000000000000..3e2f9c749bfd --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalSelector.java @@ -0,0 +1,190 @@ +package net.minecraft.server; + +import com.google.common.collect.Sets; +import java.util.Iterator; +import java.util.Set; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class PathfinderGoalSelector { + + private static final Logger a = LogManager.getLogger(); + private final Set b = Sets.newLinkedHashSet(); + private final Set c = Sets.newLinkedHashSet();private Set getExecutingTasks() { return c; }// Paper - OBFHELPER + private final MethodProfiler d; + private int e;private int getCurRate() { return e; } private void incRate() { this.e++; }// Paper - OBFHELPER + private int f = 3;private int getTickRate() { return f; } // Paper - OBFHELPER + private int g; + + public PathfinderGoalSelector(MethodProfiler methodprofiler) { + this.d = methodprofiler; + } + + public void a(int i, PathfinderGoal pathfindergoal) { + this.b.add(new PathfinderGoalSelector.PathfinderGoalSelectorItem(i, pathfindergoal)); + } + + // Paper start + public boolean inactiveTick() { + if (getCurRate() % getTickRate() != 0) { + incRate(); + return false; + } else { + return true; + } + } + public boolean hasTasks() { + return !getExecutingTasks().isEmpty(); + } + // Paper end + + public void a(PathfinderGoal pathfindergoal) { + Iterator iterator = this.b.iterator(); + + PathfinderGoalSelector.PathfinderGoalSelectorItem pathfindergoalselector_pathfindergoalselectoritem; + PathfinderGoal pathfindergoal1; + + do { + if (!iterator.hasNext()) { + return; + } + + pathfindergoalselector_pathfindergoalselectoritem = (PathfinderGoalSelector.PathfinderGoalSelectorItem) iterator.next(); + pathfindergoal1 = pathfindergoalselector_pathfindergoalselectoritem.a; + } while (pathfindergoal1 != pathfindergoal); + + if (pathfindergoalselector_pathfindergoalselectoritem.c) { + pathfindergoalselector_pathfindergoalselectoritem.c = false; + pathfindergoalselector_pathfindergoalselectoritem.a.d(); + this.c.remove(pathfindergoalselector_pathfindergoalselectoritem); + } + + iterator.remove(); + } + + public void doTick() { + this.d.enter("goalSetup"); + Iterator iterator; + PathfinderGoalSelector.PathfinderGoalSelectorItem pathfindergoalselector_pathfindergoalselectoritem; + + if (this.e++ % this.f == 0) { + iterator = this.b.iterator(); + + while (iterator.hasNext()) { + pathfindergoalselector_pathfindergoalselectoritem = (PathfinderGoalSelector.PathfinderGoalSelectorItem) iterator.next(); + if (pathfindergoalselector_pathfindergoalselectoritem.c) { + if (!this.b(pathfindergoalselector_pathfindergoalselectoritem) || !this.a(pathfindergoalselector_pathfindergoalselectoritem)) { + pathfindergoalselector_pathfindergoalselectoritem.c = false; + pathfindergoalselector_pathfindergoalselectoritem.a.d(); + this.c.remove(pathfindergoalselector_pathfindergoalselectoritem); + } + } else if (this.b(pathfindergoalselector_pathfindergoalselectoritem) && pathfindergoalselector_pathfindergoalselectoritem.a.a()) { + pathfindergoalselector_pathfindergoalselectoritem.c = true; + pathfindergoalselector_pathfindergoalselectoritem.a.c(); + this.c.add(pathfindergoalselector_pathfindergoalselectoritem); + } + } + } else { + iterator = this.c.iterator(); + + while (iterator.hasNext()) { + pathfindergoalselector_pathfindergoalselectoritem = (PathfinderGoalSelector.PathfinderGoalSelectorItem) iterator.next(); + if (!this.a(pathfindergoalselector_pathfindergoalselectoritem)) { + pathfindergoalselector_pathfindergoalselectoritem.c = false; + pathfindergoalselector_pathfindergoalselectoritem.a.d(); + iterator.remove(); + } + } + } + + this.d.exit(); + if (!this.c.isEmpty()) { + this.d.enter("goalTick"); + iterator = this.c.iterator(); + + while (iterator.hasNext()) { + pathfindergoalselector_pathfindergoalselectoritem = (PathfinderGoalSelector.PathfinderGoalSelectorItem) iterator.next(); + pathfindergoalselector_pathfindergoalselectoritem.a.e(); + } + + this.d.exit(); + } + + } + + private boolean a(PathfinderGoalSelector.PathfinderGoalSelectorItem pathfindergoalselector_pathfindergoalselectoritem) { + return pathfindergoalselector_pathfindergoalselectoritem.a.b(); + } + + private boolean b(PathfinderGoalSelector.PathfinderGoalSelectorItem pathfindergoalselector_pathfindergoalselectoritem) { + if (this.c.isEmpty()) { + return true; + } else if (this.b(pathfindergoalselector_pathfindergoalselectoritem.a.h())) { + return false; + } else { + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + PathfinderGoalSelector.PathfinderGoalSelectorItem pathfindergoalselector_pathfindergoalselectoritem1 = (PathfinderGoalSelector.PathfinderGoalSelectorItem) iterator.next(); + + if (pathfindergoalselector_pathfindergoalselectoritem1 != pathfindergoalselector_pathfindergoalselectoritem) { + if (pathfindergoalselector_pathfindergoalselectoritem.b >= pathfindergoalselector_pathfindergoalselectoritem1.b) { + if (!this.a(pathfindergoalselector_pathfindergoalselectoritem, pathfindergoalselector_pathfindergoalselectoritem1)) { + return false; + } + } else if (!pathfindergoalselector_pathfindergoalselectoritem1.a.f()) { + return false; + } + } + } + + return true; + } + } + + private boolean a(PathfinderGoalSelector.PathfinderGoalSelectorItem pathfindergoalselector_pathfindergoalselectoritem, PathfinderGoalSelector.PathfinderGoalSelectorItem pathfindergoalselector_pathfindergoalselectoritem1) { + return (pathfindergoalselector_pathfindergoalselectoritem.a.h() & pathfindergoalselector_pathfindergoalselectoritem1.a.h()) == 0; + } + + public boolean b(int i) { + return (this.g & i) > 0; + } + + public void c(int i) { + this.g |= i; + } + + public void d(int i) { + this.g &= ~i; + } + + public void a(int i, boolean flag) { + if (flag) { + this.d(i); + } else { + this.c(i); + } + + } + + class PathfinderGoalSelectorItem { + + public final PathfinderGoal a; + public final int b; + public boolean c; + + public PathfinderGoalSelectorItem(int i, PathfinderGoal pathfindergoal) { + this.b = i; + this.a = pathfindergoal; + } + + public boolean equals(@Nullable Object object) { + return this == object ? true : (object != null && this.getClass() == object.getClass() ? this.a.equals(((PathfinderGoalSelector.PathfinderGoalSelectorItem) object).a) : false); + } + + public int hashCode() { + return this.a.hashCode(); + } + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalSit.java b/src/main/java/net/minecraft/server/PathfinderGoalSit.java new file mode 100644 index 000000000000..c5df8681c282 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalSit.java @@ -0,0 +1,39 @@ +package net.minecraft.server; + +public class PathfinderGoalSit extends PathfinderGoal { + + private final EntityTameableAnimal entity; + private boolean willSit; + + public PathfinderGoalSit(EntityTameableAnimal entitytameableanimal) { + this.entity = entitytameableanimal; + this.a(5); + } + + public boolean a() { + if (!this.entity.isTamed()) { + return this.willSit && this.entity.getGoalTarget() == null; // CraftBukkit - Allow sitting for wild animals + } else if (this.entity.aq()) { + return false; + } else if (!this.entity.onGround) { + return false; + } else { + EntityLiving entityliving = this.entity.getOwner(); + + return entityliving == null ? true : (this.entity.h(entityliving) < 144.0D && entityliving.getLastDamager() != null ? false : this.willSit); + } + } + + public void c() { + this.entity.getNavigation().q(); + this.entity.setSitting(true); + } + + public void d() { + this.entity.setSitting(false); + } + + public void setSitting(boolean flag) { + this.willSit = flag; + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalTame.java b/src/main/java/net/minecraft/server/PathfinderGoalTame.java new file mode 100644 index 000000000000..9d185e73eae5 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalTame.java @@ -0,0 +1,69 @@ +package net.minecraft.server; + +public class PathfinderGoalTame extends PathfinderGoal { + + private final EntityHorseAbstract entity; + private final double b; + private double c; + private double d; + private double e; + + public PathfinderGoalTame(EntityHorseAbstract entityhorseabstract, double d0) { + this.entity = entityhorseabstract; + this.b = d0; + this.a(1); + } + + public boolean a() { + if (!this.entity.isTamed() && this.entity.isVehicle()) { + Vec3D vec3d = RandomPositionGenerator.a(this.entity, 5, 4); + + if (vec3d == null) { + return false; + } else { + this.c = vec3d.x; + this.d = vec3d.y; + this.e = vec3d.z; + return true; + } + } else { + return false; + } + } + + public void c() { + this.entity.getNavigation().a(this.c, this.d, this.e, this.b); + } + + public boolean b() { + return !this.entity.isTamed() && !this.entity.getNavigation().p() && this.entity.isVehicle(); + } + + public void e() { + if (!this.entity.isTamed() && this.entity.getRandom().nextInt(50) == 0) { + Entity entity = (Entity) this.entity.bP().get(0); + + if (entity == null) { + return; + } + + if (entity instanceof EntityHuman) { + int i = this.entity.getTemper(); + int j = this.entity.getMaxDomestication(); + + // CraftBukkit - fire EntityTameEvent + if (j > 0 && this.entity.getRandom().nextInt(j) < i && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this.entity, ((org.bukkit.craftbukkit.entity.CraftHumanEntity) this.entity.getBukkitEntity().getPassenger()).getHandle()).isCancelled()) { + this.entity.h((EntityHuman) entity); + return; + } + + this.entity.r(5); + } + + this.entity.ejectPassengers(); + this.entity.dZ(); + this.entity.world.broadcastEntityEffect(this.entity, (byte) 6); + } + + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalTarget.java b/src/main/java/net/minecraft/server/PathfinderGoalTarget.java new file mode 100644 index 000000000000..f986c8fc8585 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalTarget.java @@ -0,0 +1,163 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; +import org.bukkit.event.entity.EntityTargetEvent; // CraftBukkit + +public abstract class PathfinderGoalTarget extends PathfinderGoal { + + protected final EntityCreature e; + protected boolean f; + private final boolean a; + private int b; + private int c; + private int d; + protected EntityLiving g; + protected int h; + + public PathfinderGoalTarget(EntityCreature entitycreature, boolean flag) { + this(entitycreature, flag, false); + } + + public PathfinderGoalTarget(EntityCreature entitycreature, boolean flag, boolean flag1) { + this.h = 60; + this.e = entitycreature; + this.f = flag; + this.a = flag1; + } + + public boolean b() { + EntityLiving entityliving = this.e.getGoalTarget(); + + if (entityliving == null) { + entityliving = this.g; + } + + if (entityliving == null) { + return false; + } else if (!entityliving.isAlive()) { + return false; + } else { + ScoreboardTeamBase scoreboardteambase = this.e.getScoreboardTeam(); + ScoreboardTeamBase scoreboardteambase1 = entityliving.getScoreboardTeam(); + + if (scoreboardteambase != null && scoreboardteambase1 == scoreboardteambase) { + return false; + } else { + double d0 = this.i(); + + if (this.e.h(entityliving) > d0 * d0) { + return false; + } else { + if (this.f) { + if (this.e.getEntitySenses().a(entityliving)) { + this.d = 0; + } else if (++this.d > this.h) { + return false; + } + } + + if (entityliving instanceof EntityHuman && ((EntityHuman) entityliving).abilities.isInvulnerable) { + return false; + } else { + this.e.setGoalTarget(entityliving, EntityTargetEvent.TargetReason.CLOSEST_ENTITY, true); // CraftBukkit + return true; + } + } + } + } + } + + protected double i() { + AttributeInstance attributeinstance = this.e.getAttributeInstance(GenericAttributes.FOLLOW_RANGE); + + return attributeinstance == null ? 16.0D : attributeinstance.getValue(); + } + + public void c() { + this.b = 0; + this.c = 0; + this.d = 0; + } + + public void d() { + this.e.setGoalTarget((EntityLiving) null, EntityTargetEvent.TargetReason.FORGOT_TARGET, true); // CraftBukkit + this.g = null; + } + + public static boolean a(EntityInsentient entityinsentient, @Nullable EntityLiving entityliving, boolean flag, boolean flag1) { + if (entityliving == null) { + return false; + } else if (entityliving == entityinsentient) { + return false; + } else if (!entityliving.isAlive()) { + return false; + } else if (!entityinsentient.b(entityliving.getClass())) { + return false; + } else if (entityinsentient.r(entityliving)) { + return false; + } else { + if (entityinsentient instanceof EntityOwnable && ((EntityOwnable) entityinsentient).getOwnerUUID() != null) { + if (entityliving instanceof EntityOwnable && ((EntityOwnable) entityinsentient).getOwnerUUID().equals(((EntityOwnable) entityliving).getOwnerUUID())) { + return false; + } + + if (entityliving == ((EntityOwnable) entityinsentient).getOwner()) { + return false; + } + } else if (entityliving instanceof EntityHuman && !flag && ((EntityHuman) entityliving).abilities.isInvulnerable) { + return false; + } + + return !flag1 || entityinsentient.getEntitySenses().a(entityliving); + } + } + + protected boolean a(@Nullable EntityLiving entityliving, boolean flag) { + if (!a(this.e, entityliving, flag, this.f)) { + return false; + } else if (!this.e.f(new BlockPosition(entityliving))) { + return false; + } else { + if (this.a) { + if (--this.c <= 0) { + this.b = 0; + } + + if (this.b == 0) { + this.b = this.a(entityliving) ? 1 : 2; + } + + if (this.b == 2) { + return false; + } + } + + return true; + } + } + + private boolean a(EntityLiving entityliving) { + this.c = 10 + this.e.getRandom().nextInt(5); + PathEntity pathentity = this.e.getNavigation().a((Entity) entityliving); + + if (pathentity == null) { + return false; + } else { + PathPoint pathpoint = pathentity.c(); + + if (pathpoint == null) { + return false; + } else { + int i = pathpoint.a - MathHelper.floor(entityliving.locX); + int j = pathpoint.c - MathHelper.floor(entityliving.locZ); + + return (double) (i * i + j * j) <= 2.25D; + } + } + } + + public PathfinderGoalTarget b(int i) { + this.h = i; + return this; + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalTargetNearestPlayer.java b/src/main/java/net/minecraft/server/PathfinderGoalTargetNearestPlayer.java new file mode 100644 index 000000000000..df676f2d68a5 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalTargetNearestPlayer.java @@ -0,0 +1,102 @@ +package net.minecraft.server; + +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class PathfinderGoalTargetNearestPlayer extends PathfinderGoal { + + private static final Logger a = LogManager.getLogger(); + private final EntityInsentient b; + private final Predicate c; + private final PathfinderGoalNearestAttackableTarget.DistanceComparator d; + private EntityLiving e; + + public PathfinderGoalTargetNearestPlayer(EntityInsentient entityinsentient) { + this.b = entityinsentient; + if (entityinsentient instanceof EntityCreature) { + PathfinderGoalTargetNearestPlayer.a.warn("Use NearestAttackableTargetGoal.class for PathfinerMob mobs!"); + } + + this.c = (entity) -> { + if (!(entity instanceof EntityHuman)) { + return false; + } else if (((EntityHuman) entity).abilities.isInvulnerable) { + return false; + } else { + double d0 = this.g(); + + if (entity.isSneaking()) { + d0 *= 0.800000011920929D; + } + + if (entity.isInvisible()) { + float f = ((EntityHuman) entity).dk(); + + if (f < 0.1F) { + f = 0.1F; + } + + d0 *= (double) (0.7F * f); + } + + return (double) entity.g(this.b) > d0 ? false : PathfinderGoalTarget.a(this.b, (EntityLiving) entity, false, true); + } + }; + this.d = new PathfinderGoalNearestAttackableTarget.DistanceComparator(entityinsentient); + } + + public boolean a() { + double d0 = this.g(); + List list = this.b.world.a(EntityHuman.class, this.b.getBoundingBox().grow(d0, 4.0D, d0), this.c); + + Collections.sort(list, this.d); + if (list.isEmpty()) { + return false; + } else { + this.e = (EntityLiving) list.get(0); + return true; + } + } + + public boolean b() { + EntityLiving entityliving = this.b.getGoalTarget(); + + if (entityliving == null) { + return false; + } else if (!entityliving.isAlive()) { + return false; + } else if (entityliving instanceof EntityHuman && ((EntityHuman) entityliving).abilities.isInvulnerable) { + return false; + } else { + ScoreboardTeamBase scoreboardteambase = this.b.getScoreboardTeam(); + ScoreboardTeamBase scoreboardteambase1 = entityliving.getScoreboardTeam(); + + if (scoreboardteambase != null && scoreboardteambase1 == scoreboardteambase) { + return false; + } else { + double d0 = this.g(); + + return this.b.h(entityliving) > d0 * d0 ? false : !(entityliving instanceof EntityPlayer) || !((EntityPlayer) entityliving).playerInteractManager.isCreative(); + } + } + } + + public void c() { + this.b.setGoalTarget(this.e, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER, true); // CraftBukkit - added reason + super.c(); + } + + public void d() { + this.b.setGoalTarget((EntityLiving) null); + super.c(); + } + + protected double g() { + AttributeInstance attributeinstance = this.b.getAttributeInstance(GenericAttributes.FOLLOW_RANGE); + + return attributeinstance == null ? 16.0D : attributeinstance.getValue(); + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalTempt.java b/src/main/java/net/minecraft/server/PathfinderGoalTempt.java new file mode 100644 index 000000000000..1f3ae55a004c --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalTempt.java @@ -0,0 +1,114 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityTargetEvent; +import org.bukkit.event.entity.EntityTargetLivingEntityEvent; +// CraftBukkit end + +public class PathfinderGoalTempt extends PathfinderGoal { + + private final EntityCreature a; + private final double b; + private double c; + private double d; + private double e; + private double f; + private double g; + private EntityLiving target; // CraftBukkit + private int i; + private boolean j; + private final RecipeItemStack k; + private final boolean l; + + public PathfinderGoalTempt(EntityCreature entitycreature, double d0, RecipeItemStack recipeitemstack, boolean flag) { + this(entitycreature, d0, flag, recipeitemstack); + } + + public PathfinderGoalTempt(EntityCreature entitycreature, double d0, boolean flag, RecipeItemStack recipeitemstack) { + this.a = entitycreature; + this.b = d0; + this.k = recipeitemstack; + this.l = flag; + this.a(3); + if (!(entitycreature.getNavigation() instanceof Navigation)) { + throw new IllegalArgumentException("Unsupported mob type for TemptGoal"); + } + } + + public boolean a() { + if (this.i > 0) { + --this.i; + return false; + } else { + this.target = this.a.world.findNearbyPlayer(this.a, 10.0D); + // CraftBukkit start + boolean tempt = this.target == null ? false : this.a(this.target.getItemInMainHand()) || this.a(this.target.getItemInOffHand()); + if (tempt) { + EntityTargetLivingEntityEvent event = CraftEventFactory.callEntityTargetLivingEvent(this.a, this.target, EntityTargetEvent.TargetReason.TEMPT); + if (event.isCancelled()) { + return false; + } + this.target = (event.getTarget() == null) ? null : ((CraftLivingEntity) event.getTarget()).getHandle(); + } + return tempt && this.target != null; // Paper - must have target - plugin might of cancelled + // CraftBukkit end + } + } + + protected boolean a(ItemStack itemstack) { + return this.k.test(itemstack); + } + + public boolean b() { + if (this.l) { + if (this.a.h(this.target) < 36.0D) { + if (this.target.d(this.c, this.d, this.e) > 0.010000000000000002D) { + return false; + } + + if (Math.abs((double) this.target.pitch - this.f) > 5.0D || Math.abs((double) this.target.yaw - this.g) > 5.0D) { + return false; + } + } else { + this.c = this.target.locX; + this.d = this.target.locY; + this.e = this.target.locZ; + } + + this.f = (double) this.target.pitch; + this.g = (double) this.target.yaw; + } + + return this.a(); + } + + public void c() { + this.c = this.target.locX; + this.d = this.target.locY; + this.e = this.target.locZ; + this.j = true; + } + + public void d() { + this.target = null; + this.a.getNavigation().q(); + this.i = 100; + this.j = false; + } + + public void e() { + this.a.getControllerLook().a(this.target, (float) (this.a.L() + 20), (float) this.a.K()); + if (this.a.h(this.target) < 6.25D) { + this.a.getNavigation().q(); + } else { + this.a.getNavigation().a((Entity) this.target, this.b); + } + + } + + public boolean g() { + return this.j; + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoalVillagerFarm.java b/src/main/java/net/minecraft/server/PathfinderGoalVillagerFarm.java new file mode 100644 index 000000000000..be418203ac66 --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderGoalVillagerFarm.java @@ -0,0 +1,117 @@ +package net.minecraft.server; + +public class PathfinderGoalVillagerFarm extends PathfinderGoalGotoTarget { + + private final EntityVillager f; + private boolean g; + private boolean h; + private int i; + + public PathfinderGoalVillagerFarm(EntityVillager entityvillager, double d0) { + super(entityvillager, d0, 16); + this.f = entityvillager; + } + + public boolean a() { + if (this.b <= 0) { + if (!this.f.world.getGameRules().getBoolean("mobGriefing")) { + return false; + } + + this.i = -1; + this.g = this.f.dH(); + this.h = this.f.dG(); + } + + return super.a(); + } + + public boolean b() { + return this.i >= 0 && super.b(); + } + + public void e() { + super.e(); + this.f.getControllerLook().a((double) this.d.getX() + 0.5D, (double) (this.d.getY() + 1), (double) this.d.getZ() + 0.5D, 10.0F, (float) this.f.K()); + if (this.k()) { + World world = this.f.world; + BlockPosition blockposition = this.d.up(); + IBlockData iblockdata = world.getType(blockposition); + Block block = iblockdata.getBlock(); + + if (this.i == 0 && block instanceof BlockCrops && ((BlockCrops) block).w(iblockdata)) { + // CraftBukkit start + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.f, blockposition, Blocks.AIR.getBlockData()).isCancelled()) { + world.setAir(blockposition, true); + } + // CraftBukkit end + } else if (this.i == 1 && iblockdata.isAir()) { + InventorySubcontainer inventorysubcontainer = this.f.dD(); + + for (int i = 0; i < inventorysubcontainer.getSize(); ++i) { + ItemStack itemstack = inventorysubcontainer.getItem(i); + boolean flag = false; + + if (!itemstack.isEmpty()) { + // CraftBukkit start + Block planted = null; + if (itemstack.getItem() == Items.WHEAT_SEEDS) { + planted = Blocks.WHEAT; + flag = true; + } else if (itemstack.getItem() == Items.POTATO) { + planted = Blocks.POTATOES; + flag = true; + } else if (itemstack.getItem() == Items.CARROT) { + planted = Blocks.CARROTS; + flag = true; + } else if (itemstack.getItem() == Items.BEETROOT_SEEDS) { + planted = Blocks.BEETROOTS; + flag = true; + } + + if (planted != null && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.f, blockposition, planted.getBlockData()).isCancelled()) { + world.setTypeAndData(blockposition, planted.getBlockData(), 3); + } else { + flag = false; + } + // CraftBukkit end + } + + if (flag) { + itemstack.subtract(1); + if (itemstack.isEmpty()) { + inventorysubcontainer.setItem(i, ItemStack.a); + } + break; + } + } + } + + this.i = -1; + this.b = 10; + } + + } + + protected boolean a(IWorldReader iworldreader, BlockPosition blockposition) { + Block block = iworldreader.getType(blockposition).getBlock(); + + if (block == Blocks.FARMLAND) { + blockposition = blockposition.up(); + IBlockData iblockdata = iworldreader.getType(blockposition); + + block = iblockdata.getBlock(); + if (block instanceof BlockCrops && ((BlockCrops) block).w(iblockdata) && this.h && (this.i == 0 || this.i < 0)) { + this.i = 0; + return true; + } + + if (iblockdata.isAir() && this.g && (this.i == 1 || this.i < 0)) { + this.i = 1; + return true; + } + } + + return false; + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderNormal.java b/src/main/java/net/minecraft/server/PathfinderNormal.java new file mode 100644 index 000000000000..3b5ace88deea --- /dev/null +++ b/src/main/java/net/minecraft/server/PathfinderNormal.java @@ -0,0 +1,423 @@ +package net.minecraft.server; + +import com.google.common.collect.Sets; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.Set; +import javax.annotation.Nullable; + +public class PathfinderNormal extends PathfinderAbstract { + + protected float j; + + public PathfinderNormal() {} + + public void a(IBlockAccess iblockaccess, EntityInsentient entityinsentient) { + super.a(iblockaccess, entityinsentient); + this.j = entityinsentient.a(PathType.WATER); + } + + public void a() { + this.b.a(PathType.WATER, this.j); + super.a(); + } + + public PathPoint b() { + int i; + BlockPosition blockposition; + + if (this.e() && this.b.isInWater()) { + i = (int) this.b.getBoundingBox().minY; + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(MathHelper.floor(this.b.locX), i, MathHelper.floor(this.b.locZ)); + + for (Block block = this.a.getType(blockposition_mutableblockposition).getBlock(); block == Blocks.WATER; block = this.a.getType(blockposition_mutableblockposition).getBlock()) { + ++i; + blockposition_mutableblockposition.c(MathHelper.floor(this.b.locX), i, MathHelper.floor(this.b.locZ)); + } + + --i; + } else if (this.b.onGround) { + i = MathHelper.floor(this.b.getBoundingBox().minY + 0.5D); + } else { + for (blockposition = new BlockPosition(this.b); (this.a.getType(blockposition).isAir() || this.a.getType(blockposition).a(this.a, blockposition, PathMode.LAND)) && blockposition.getY() > 0; blockposition = blockposition.down()) { + ; + } + + i = blockposition.up().getY(); + } + + blockposition = new BlockPosition(this.b); + PathType pathtype = this.a(this.b, blockposition.getX(), i, blockposition.getZ()); + + if (this.b.a(pathtype) < 0.0F) { + Set set = Sets.newHashSet(); + + set.add(new BlockPosition(this.b.getBoundingBox().minX, (double) i, this.b.getBoundingBox().minZ)); + set.add(new BlockPosition(this.b.getBoundingBox().minX, (double) i, this.b.getBoundingBox().maxZ)); + set.add(new BlockPosition(this.b.getBoundingBox().maxX, (double) i, this.b.getBoundingBox().minZ)); + set.add(new BlockPosition(this.b.getBoundingBox().maxX, (double) i, this.b.getBoundingBox().maxZ)); + Iterator iterator = set.iterator(); + + while (iterator.hasNext()) { + BlockPosition blockposition1 = (BlockPosition) iterator.next(); + PathType pathtype1 = this.a(this.b, blockposition1); + + if (this.b.a(pathtype1) >= 0.0F) { + return this.a(blockposition1.getX(), blockposition1.getY(), blockposition1.getZ()); + } + } + } + + return this.a(blockposition.getX(), i, blockposition.getZ()); + } + + public PathPoint a(double d0, double d1, double d2) { + return this.a(MathHelper.floor(d0), MathHelper.floor(d1), MathHelper.floor(d2)); + } + + public int a(PathPoint[] apathpoint, PathPoint pathpoint, PathPoint pathpoint1, float f) { + int i = 0; + int j = 0; + PathType pathtype = this.a(this.b, pathpoint.a, pathpoint.b + 1, pathpoint.c); + + if (this.b.a(pathtype) >= 0.0F) { + j = MathHelper.d(Math.max(1.0F, this.b.Q)); + } + + double d0 = a(this.a, new BlockPosition(pathpoint.a, pathpoint.b, pathpoint.c)); + PathPoint pathpoint2 = this.a(pathpoint.a, pathpoint.b, pathpoint.c + 1, j, d0, EnumDirection.SOUTH); + PathPoint pathpoint3 = this.a(pathpoint.a - 1, pathpoint.b, pathpoint.c, j, d0, EnumDirection.WEST); + PathPoint pathpoint4 = this.a(pathpoint.a + 1, pathpoint.b, pathpoint.c, j, d0, EnumDirection.EAST); + PathPoint pathpoint5 = this.a(pathpoint.a, pathpoint.b, pathpoint.c - 1, j, d0, EnumDirection.NORTH); + + if (pathpoint2 != null && !pathpoint2.i && pathpoint2.a(pathpoint1) < f) { + apathpoint[i++] = pathpoint2; + } + + if (pathpoint3 != null && !pathpoint3.i && pathpoint3.a(pathpoint1) < f) { + apathpoint[i++] = pathpoint3; + } + + if (pathpoint4 != null && !pathpoint4.i && pathpoint4.a(pathpoint1) < f) { + apathpoint[i++] = pathpoint4; + } + + if (pathpoint5 != null && !pathpoint5.i && pathpoint5.a(pathpoint1) < f) { + apathpoint[i++] = pathpoint5; + } + + boolean flag = pathpoint5 == null || pathpoint5.m == PathType.OPEN || pathpoint5.l != 0.0F; + boolean flag1 = pathpoint2 == null || pathpoint2.m == PathType.OPEN || pathpoint2.l != 0.0F; + boolean flag2 = pathpoint4 == null || pathpoint4.m == PathType.OPEN || pathpoint4.l != 0.0F; + boolean flag3 = pathpoint3 == null || pathpoint3.m == PathType.OPEN || pathpoint3.l != 0.0F; + PathPoint pathpoint6; + + if (flag && flag3) { + pathpoint6 = this.a(pathpoint.a - 1, pathpoint.b, pathpoint.c - 1, j, d0, EnumDirection.NORTH); + if (pathpoint6 != null && !pathpoint6.i && pathpoint6.a(pathpoint1) < f) { + apathpoint[i++] = pathpoint6; + } + } + + if (flag && flag2) { + pathpoint6 = this.a(pathpoint.a + 1, pathpoint.b, pathpoint.c - 1, j, d0, EnumDirection.NORTH); + if (pathpoint6 != null && !pathpoint6.i && pathpoint6.a(pathpoint1) < f) { + apathpoint[i++] = pathpoint6; + } + } + + if (flag1 && flag3) { + pathpoint6 = this.a(pathpoint.a - 1, pathpoint.b, pathpoint.c + 1, j, d0, EnumDirection.SOUTH); + if (pathpoint6 != null && !pathpoint6.i && pathpoint6.a(pathpoint1) < f) { + apathpoint[i++] = pathpoint6; + } + } + + if (flag1 && flag2) { + pathpoint6 = this.a(pathpoint.a + 1, pathpoint.b, pathpoint.c + 1, j, d0, EnumDirection.SOUTH); + if (pathpoint6 != null && !pathpoint6.i && pathpoint6.a(pathpoint1) < f) { + apathpoint[i++] = pathpoint6; + } + } + + return i; + } + + @Nullable + private PathPoint a(int i, int j, int k, int l, double d0, EnumDirection enumdirection) { + PathPoint pathpoint = null; + BlockPosition blockposition = new BlockPosition(i, j, k); + double d1 = a(this.a, blockposition); + + if (d1 - d0 > 1.125D) { + return null; + } else { + PathType pathtype = this.a(this.b, i, j, k); + float f = this.b.a(pathtype); + double d2 = (double) this.b.width / 2.0D; + + if (f >= 0.0F) { + pathpoint = this.a(i, j, k); + pathpoint.m = pathtype; + pathpoint.l = Math.max(pathpoint.l, f); + } + + if (pathtype == PathType.WALKABLE) { + return pathpoint; + } else { + if (pathpoint == null && l > 0 && pathtype != PathType.FENCE && pathtype != PathType.TRAPDOOR) { + pathpoint = this.a(i, j + 1, k, l - 1, d0, enumdirection); + if (pathpoint != null && (pathpoint.m == PathType.OPEN || pathpoint.m == PathType.WALKABLE) && this.b.width < 1.0F) { + double d3 = (double) (i - enumdirection.getAdjacentX()) + 0.5D; + double d4 = (double) (k - enumdirection.getAdjacentZ()) + 0.5D; + AxisAlignedBB axisalignedbb = new AxisAlignedBB(d3 - d2, (double) j + 0.001D, d4 - d2, d3 + d2, (double) this.b.length + a(this.a, blockposition.up()) - 0.002D, d4 + d2); + + if (!this.b.world.getCubes((Entity) null, axisalignedbb)) { + pathpoint = null; + } + } + } + + if (pathtype == PathType.WATER && !this.e()) { + if (this.a(this.b, i, j - 1, k) != PathType.WATER) { + return pathpoint; + } + + while (j > 0) { + --j; + pathtype = this.a(this.b, i, j, k); + if (pathtype != PathType.WATER) { + return pathpoint; + } + + pathpoint = this.a(i, j, k); + pathpoint.m = pathtype; + pathpoint.l = Math.max(pathpoint.l, this.b.a(pathtype)); + } + } + + if (pathtype == PathType.OPEN) { + AxisAlignedBB axisalignedbb1 = new AxisAlignedBB((double) i - d2 + 0.5D, (double) j + 0.001D, (double) k - d2 + 0.5D, (double) i + d2 + 0.5D, (double) ((float) j + this.b.length), (double) k + d2 + 0.5D); + + if (!this.b.world.getCubes((Entity) null, axisalignedbb1)) { + return null; + } + + if (this.b.width >= 1.0F) { + PathType pathtype1 = this.a(this.b, i, j - 1, k); + + if (pathtype1 == PathType.BLOCKED) { + pathpoint = this.a(i, j, k); + pathpoint.m = PathType.WALKABLE; + pathpoint.l = Math.max(pathpoint.l, f); + return pathpoint; + } + } + + int i1 = 0; + + while (j > 0 && pathtype == PathType.OPEN) { + --j; + if (i1++ >= this.b.bn()) { + return null; + } + + pathtype = this.a(this.b, i, j, k); + f = this.b.a(pathtype); + if (pathtype != PathType.OPEN && f >= 0.0F) { + pathpoint = this.a(i, j, k); + pathpoint.m = pathtype; + pathpoint.l = Math.max(pathpoint.l, f); + break; + } + + if (f < 0.0F) { + return null; + } + } + } + + return pathpoint; + } + } + } + + public static double a(IBlockAccess iblockaccess, BlockPosition blockposition) { + BlockPosition blockposition1 = blockposition.down(); + VoxelShape voxelshape = iblockaccess.getType(blockposition1).getCollisionShape(iblockaccess, blockposition1); + + return (double) blockposition1.getY() + (voxelshape.isEmpty() ? 0.0D : voxelshape.c(EnumDirection.EnumAxis.Y)); + } + + public PathType a(IBlockAccess iblockaccess, int i, int j, int k, EntityInsentient entityinsentient, int l, int i1, int j1, boolean flag, boolean flag1) { + EnumSet enumset = EnumSet.noneOf(PathType.class); + PathType pathtype = PathType.BLOCKED; + double d0 = (double) entityinsentient.width / 2.0D; + BlockPosition blockposition = new BlockPosition(entityinsentient); + + pathtype = this.a(iblockaccess, i, j, k, l, i1, j1, flag, flag1, enumset, pathtype, blockposition); + if (enumset.contains(PathType.FENCE)) { + return PathType.FENCE; + } else { + PathType pathtype1 = PathType.BLOCKED; + Iterator iterator = enumset.iterator(); + + while (iterator.hasNext()) { + PathType pathtype2 = (PathType) iterator.next(); + + if (entityinsentient.a(pathtype2) < 0.0F) { + return pathtype2; + } + + if (entityinsentient.a(pathtype2) >= entityinsentient.a(pathtype1)) { + pathtype1 = pathtype2; + } + } + + if (pathtype == PathType.OPEN && entityinsentient.a(pathtype1) == 0.0F) { + return PathType.OPEN; + } else { + return pathtype1; + } + } + } + + public PathType a(IBlockAccess iblockaccess, int i, int j, int k, int l, int i1, int j1, boolean flag, boolean flag1, EnumSet enumset, PathType pathtype, BlockPosition blockposition) { + for (int k1 = 0; k1 < l; ++k1) { + for (int l1 = 0; l1 < i1; ++l1) { + for (int i2 = 0; i2 < j1; ++i2) { + int j2 = k1 + i; + int k2 = l1 + j; + int l2 = i2 + k; + PathType pathtype1 = this.a(iblockaccess, j2, k2, l2); + + if (pathtype1 == PathType.DOOR_WOOD_CLOSED && flag && flag1) { + pathtype1 = PathType.WALKABLE; + } + + if (pathtype1 == PathType.DOOR_OPEN && !flag1) { + pathtype1 = PathType.BLOCKED; + } + + if (pathtype1 == PathType.RAIL && !(iblockaccess.getType(blockposition).getBlock() instanceof BlockMinecartTrackAbstract) && !(iblockaccess.getType(blockposition.down()).getBlock() instanceof BlockMinecartTrackAbstract)) { + pathtype1 = PathType.FENCE; + } + + if (k1 == 0 && l1 == 0 && i2 == 0) { + pathtype = pathtype1; + } + + enumset.add(pathtype1); + } + } + } + + return pathtype; + } + + private PathType a(EntityInsentient entityinsentient, BlockPosition blockposition) { + return this.a(entityinsentient, blockposition.getX(), blockposition.getY(), blockposition.getZ()); + } + + private PathType a(EntityInsentient entityinsentient, int i, int j, int k) { + return this.a(this.a, i, j, k, entityinsentient, this.d, this.e, this.f, this.d(), this.c()); + } + + public PathType a(IBlockAccess iblockaccess, int i, int j, int k) { + PathType pathtype = this.b(iblockaccess, i, j, k); + + if (pathtype == PathType.OPEN && j >= 1) { + Block block = world.getBlockIfLoaded(new BlockPosition(i, j - 1, k)); // Paper + if (block == null) return PathType.BLOCKED; // Paper + PathType pathtype1 = this.b(iblockaccess, i, j - 1, k); + + pathtype = pathtype1 != PathType.WALKABLE && pathtype1 != PathType.OPEN && pathtype1 != PathType.WATER && pathtype1 != PathType.LAVA ? PathType.WALKABLE : PathType.OPEN; + if (pathtype1 == PathType.DAMAGE_FIRE || block == Blocks.MAGMA_BLOCK) { + pathtype = PathType.DAMAGE_FIRE; + } + + if (pathtype1 == PathType.DAMAGE_CACTUS) { + pathtype = PathType.DAMAGE_CACTUS; + } + } + + pathtype = this.a(iblockaccess, i, j, k, pathtype); + return pathtype; + } + + public PathType a(IBlockAccess iblockaccess, int i, int j, int k, PathType pathtype) { + if (pathtype == PathType.WALKABLE) { + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + for (int l = -1; l <= 1; ++l) { + for (int i1 = -1; i1 <= 1; ++i1) { + if (l != 0 || i1 != 0) { + Block block = world.getBlockIfLoaded(blockposition_b.c(l + i, j, i1 + k)); // Paper + + if (block == null) pathtype = PathType.BLOCKED; // Paper + else if (block == Blocks.CACTUS) { // Paper + pathtype = PathType.DANGER_CACTUS; + } else if (block == Blocks.FIRE) { + pathtype = PathType.DANGER_FIRE; + } + } + } + } + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + } + + return pathtype; + } + + protected PathType b(IBlockAccess iblockaccess, int i, int j, int k) { + BlockPosition blockposition = new BlockPosition(i, j, k); + IBlockData iblockdata = world.getTypeIfLoaded(blockposition); // Paper + if (iblockdata == null) return PathType.BLOCKED; // Paper + Block block = iblockdata.getBlock(); + Material material = iblockdata.getMaterial(); + + if (iblockdata.isAir()) { + return PathType.OPEN; + } else if (!block.a(TagsBlock.TRAPDOORS) && block != Blocks.LILY_PAD) { + if (block == Blocks.FIRE) { + return PathType.DAMAGE_FIRE; + } else if (block == Blocks.CACTUS) { + return PathType.DAMAGE_CACTUS; + } else if (block instanceof BlockDoor && material == Material.WOOD && !(Boolean) iblockdata.get(BlockDoor.OPEN)) { + return PathType.DOOR_WOOD_CLOSED; + } else if (block instanceof BlockDoor && material == Material.ORE && !(Boolean) iblockdata.get(BlockDoor.OPEN)) { + return PathType.DOOR_IRON_CLOSED; + } else if (block instanceof BlockDoor && (Boolean) iblockdata.get(BlockDoor.OPEN)) { + return PathType.DOOR_OPEN; + } else if (block instanceof BlockMinecartTrackAbstract) { + return PathType.RAIL; + } else if (!(block instanceof BlockFence) && !(block instanceof BlockCobbleWall) && (!(block instanceof BlockFenceGate) || (Boolean) iblockdata.get(BlockFenceGate.OPEN))) { + Fluid fluid = iblockaccess.getFluid(blockposition); + + return fluid.a(TagsFluid.WATER) ? PathType.WATER : (fluid.a(TagsFluid.LAVA) ? PathType.LAVA : (iblockdata.a(iblockaccess, blockposition, PathMode.LAND) ? PathType.OPEN : PathType.BLOCKED)); + } else { + return PathType.FENCE; + } + } else { + return PathType.TRAPDOOR; + } + } +} diff --git a/src/main/java/net/minecraft/server/PersistentScoreboard.java b/src/main/java/net/minecraft/server/PersistentScoreboard.java new file mode 100644 index 000000000000..3a9dfb979855 --- /dev/null +++ b/src/main/java/net/minecraft/server/PersistentScoreboard.java @@ -0,0 +1,252 @@ +package net.minecraft.server; + +import java.util.Collection; +import java.util.Iterator; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class PersistentScoreboard extends PersistentBase { + + private static final Logger a = LogManager.getLogger(); + private Scoreboard b; + private NBTTagCompound c; + + public PersistentScoreboard() { + this("scoreboard"); + } + + public PersistentScoreboard(String s) { + super(s); + } + + public void a(Scoreboard scoreboard) { + this.b = scoreboard; + if (this.c != null) { + this.a(this.c); + } + + } + + public void a(NBTTagCompound nbttagcompound) { + if (this.b == null) { + this.c = nbttagcompound; + } else { + this.b(nbttagcompound.getList("Objectives", 10)); + this.b.a(nbttagcompound.getList("PlayerScores", 10)); + if (nbttagcompound.hasKeyOfType("DisplaySlots", 10)) { + this.c(nbttagcompound.getCompound("DisplaySlots")); + } + + if (nbttagcompound.hasKeyOfType("Teams", 9)) { + this.a(nbttagcompound.getList("Teams", 10)); + } + + } + } + + protected void a(NBTTagList nbttaglist) { + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist.getCompound(i); + String s = nbttagcompound.getString("Name"); + + if (s.length() > 16) { + s = s.substring(0, 16); + } + + ScoreboardTeam scoreboardteam = this.b.createTeam(s); + IChatBaseComponent ichatbasecomponent = IChatBaseComponent.ChatSerializer.a(nbttagcompound.getString("DisplayName")); + + if (ichatbasecomponent != null) { + scoreboardteam.setDisplayName(ichatbasecomponent); + } + + if (nbttagcompound.hasKeyOfType("TeamColor", 8)) { + scoreboardteam.setColor(EnumChatFormat.c(nbttagcompound.getString("TeamColor"))); + } + + if (nbttagcompound.hasKeyOfType("AllowFriendlyFire", 99)) { + scoreboardteam.setAllowFriendlyFire(nbttagcompound.getBoolean("AllowFriendlyFire")); + } + + if (nbttagcompound.hasKeyOfType("SeeFriendlyInvisibles", 99)) { + scoreboardteam.setCanSeeFriendlyInvisibles(nbttagcompound.getBoolean("SeeFriendlyInvisibles")); + } + + IChatBaseComponent ichatbasecomponent1; + + if (nbttagcompound.hasKeyOfType("MemberNamePrefix", 8)) { + ichatbasecomponent1 = IChatBaseComponent.ChatSerializer.a(nbttagcompound.getString("MemberNamePrefix")); + if (ichatbasecomponent1 != null) { + scoreboardteam.setPrefix(ichatbasecomponent1); + } + } + + if (nbttagcompound.hasKeyOfType("MemberNameSuffix", 8)) { + ichatbasecomponent1 = IChatBaseComponent.ChatSerializer.a(nbttagcompound.getString("MemberNameSuffix")); + if (ichatbasecomponent1 != null) { + scoreboardteam.setSuffix(ichatbasecomponent1); + } + } + + ScoreboardTeamBase.EnumNameTagVisibility scoreboardteambase_enumnametagvisibility; + + if (nbttagcompound.hasKeyOfType("NameTagVisibility", 8)) { + scoreboardteambase_enumnametagvisibility = ScoreboardTeamBase.EnumNameTagVisibility.a(nbttagcompound.getString("NameTagVisibility")); + if (scoreboardteambase_enumnametagvisibility != null) { + scoreboardteam.setNameTagVisibility(scoreboardteambase_enumnametagvisibility); + } + } + + if (nbttagcompound.hasKeyOfType("DeathMessageVisibility", 8)) { + scoreboardteambase_enumnametagvisibility = ScoreboardTeamBase.EnumNameTagVisibility.a(nbttagcompound.getString("DeathMessageVisibility")); + if (scoreboardteambase_enumnametagvisibility != null) { + scoreboardteam.setDeathMessageVisibility(scoreboardteambase_enumnametagvisibility); + } + } + + if (nbttagcompound.hasKeyOfType("CollisionRule", 8)) { + ScoreboardTeamBase.EnumTeamPush scoreboardteambase_enumteampush = ScoreboardTeamBase.EnumTeamPush.a(nbttagcompound.getString("CollisionRule")); + + if (scoreboardteambase_enumteampush != null) { + scoreboardteam.setCollisionRule(scoreboardteambase_enumteampush); + } + } + + this.a(scoreboardteam, nbttagcompound.getList("Players", 8)); + } + + } + + protected void a(ScoreboardTeam scoreboardteam, NBTTagList nbttaglist) { + for (int i = 0; i < nbttaglist.size(); ++i) { + this.b.addPlayerToTeam(nbttaglist.getString(i), scoreboardteam); + } + + } + + protected void c(NBTTagCompound nbttagcompound) { + for (int i = 0; i < 19; ++i) { + if (nbttagcompound.hasKeyOfType("slot_" + i, 8)) { + String s = nbttagcompound.getString("slot_" + i); + ScoreboardObjective scoreboardobjective = this.b.getObjective(s); + + this.b.setDisplaySlot(i, scoreboardobjective); + } + } + + } + + protected void b(NBTTagList nbttaglist) { + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist.getCompound(i); + IScoreboardCriteria iscoreboardcriteria = IScoreboardCriteria.a(nbttagcompound.getString("CriteriaName")); + + if (iscoreboardcriteria != null) { + String s = nbttagcompound.getString("Name"); + + if (s.length() > 16) { + s = s.substring(0, 16); + } + + IChatBaseComponent ichatbasecomponent = IChatBaseComponent.ChatSerializer.a(nbttagcompound.getString("DisplayName")); + IScoreboardCriteria.EnumScoreboardHealthDisplay iscoreboardcriteria_enumscoreboardhealthdisplay = IScoreboardCriteria.EnumScoreboardHealthDisplay.a(nbttagcompound.getString("RenderType")); + + this.b.registerObjective(s, iscoreboardcriteria, ichatbasecomponent, iscoreboardcriteria_enumscoreboardhealthdisplay); + } + } + + } + + public NBTTagCompound b(NBTTagCompound nbttagcompound) { + if (this.b == null) { + PersistentScoreboard.a.warn("Tried to save scoreboard without having a scoreboard..."); + return nbttagcompound; + } else { + nbttagcompound.set("Objectives", this.b()); + nbttagcompound.set("PlayerScores", this.b.i()); + nbttagcompound.set("Teams", this.a()); + this.d(nbttagcompound); + return nbttagcompound; + } + } + + protected NBTTagList a() { + NBTTagList nbttaglist = new NBTTagList(); + Collection collection = this.b.getTeams(); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + ScoreboardTeam scoreboardteam = (ScoreboardTeam) iterator.next(); + if (!com.destroystokyo.paper.PaperConfig.saveEmptyScoreboardTeams && scoreboardteam.getPlayerNameSet().isEmpty()) continue; // Paper + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setString("Name", scoreboardteam.getName()); + nbttagcompound.setString("DisplayName", IChatBaseComponent.ChatSerializer.a(scoreboardteam.getDisplayName())); + if (scoreboardteam.getColor().b() >= 0) { + nbttagcompound.setString("TeamColor", scoreboardteam.getColor().g()); + } + + nbttagcompound.setBoolean("AllowFriendlyFire", scoreboardteam.allowFriendlyFire()); + nbttagcompound.setBoolean("SeeFriendlyInvisibles", scoreboardteam.canSeeFriendlyInvisibles()); + nbttagcompound.setString("MemberNamePrefix", IChatBaseComponent.ChatSerializer.a(scoreboardteam.getPrefix())); + nbttagcompound.setString("MemberNameSuffix", IChatBaseComponent.ChatSerializer.a(scoreboardteam.getSuffix())); + nbttagcompound.setString("NameTagVisibility", scoreboardteam.getNameTagVisibility().e); + nbttagcompound.setString("DeathMessageVisibility", scoreboardteam.getDeathMessageVisibility().e); + nbttagcompound.setString("CollisionRule", scoreboardteam.getCollisionRule().e); + NBTTagList nbttaglist1 = new NBTTagList(); + Iterator iterator1 = scoreboardteam.getPlayerNameSet().iterator(); + + while (iterator1.hasNext()) { + String s = (String) iterator1.next(); + + nbttaglist1.add((NBTBase) (new NBTTagString(s))); + } + + nbttagcompound.set("Players", nbttaglist1); + nbttaglist.add((NBTBase) nbttagcompound); + } + + return nbttaglist; + } + + protected void d(NBTTagCompound nbttagcompound) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + boolean flag = false; + + for (int i = 0; i < 19; ++i) { + ScoreboardObjective scoreboardobjective = this.b.getObjectiveForSlot(i); + + if (scoreboardobjective != null) { + nbttagcompound1.setString("slot_" + i, scoreboardobjective.getName()); + flag = true; + } + } + + if (flag) { + nbttagcompound.set("DisplaySlots", nbttagcompound1); + } + + } + + protected NBTTagList b() { + NBTTagList nbttaglist = new NBTTagList(); + Collection collection = this.b.getObjectives(); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + ScoreboardObjective scoreboardobjective = (ScoreboardObjective) iterator.next(); + + if (scoreboardobjective.getCriteria() != null) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setString("Name", scoreboardobjective.getName()); + nbttagcompound.setString("CriteriaName", scoreboardobjective.getCriteria().getName()); + nbttagcompound.setString("DisplayName", IChatBaseComponent.ChatSerializer.a(scoreboardobjective.getDisplayName())); + nbttagcompound.setString("RenderType", scoreboardobjective.getRenderType().a()); + nbttaglist.add((NBTBase) nbttagcompound); + } + } + + return nbttaglist; + } +} diff --git a/src/main/java/net/minecraft/server/PersistentVillage.java b/src/main/java/net/minecraft/server/PersistentVillage.java new file mode 100644 index 000000000000..e40cd418694c --- /dev/null +++ b/src/main/java/net/minecraft/server/PersistentVillage.java @@ -0,0 +1,268 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; + +public class PersistentVillage extends PersistentBase { + + private World world; + private final List b = Lists.newArrayList(); + private final List c = Lists.newArrayList(); + private final List villages = Lists.newArrayList(); + private int time; + + public PersistentVillage(String s) { + super(s); + } + + public PersistentVillage(World world) { + super(a(world.worldProvider)); + this.world = world; + this.c(); + } + + public void a(World world) { + this.world = world; + Iterator iterator = this.villages.iterator(); + + while (iterator.hasNext()) { + Village village = (Village) iterator.next(); + + village.a(world); + } + + } + + public void a(BlockPosition blockposition) { + if (this.b.size() <= 64) { + if (!this.d(blockposition)) { + this.b.add(blockposition); + } + + } + } + + public void tick() { + ++this.time; + Iterator iterator = this.villages.iterator(); + + while (iterator.hasNext()) { + Village village = (Village) iterator.next(); + + village.a(this.time); + } + + this.f(); + this.g(); + this.h(); + if (this.time % 400 == 0) { + this.c(); + } + + } + + private void f() { + Iterator iterator = this.villages.iterator(); + + while (iterator.hasNext()) { + Village village = (Village) iterator.next(); + + if (village.g()) { + iterator.remove(); + this.c(); + } + } + + } + + public List getVillages() { + return this.villages; + } + + public Village getClosestVillage(BlockPosition blockposition, int i) { + Village village = null; + double d0 = 3.4028234663852886E38D; + Iterator iterator = this.villages.iterator(); + + while (iterator.hasNext()) { + Village village1 = (Village) iterator.next(); + double d1 = village1.a().n(blockposition); + + if (d1 < d0) { + float f = (float) (i + village1.b()); + + if (d1 <= (double) (f * f)) { + village = village1; + d0 = d1; + } + } + } + + return village; + } + + private void g() { + if (!this.b.isEmpty()) { + this.b((BlockPosition) this.b.remove(0)); + } + } + + private void h() { + for (int i = 0; i < this.c.size(); ++i) { + VillageDoor villagedoor = (VillageDoor) this.c.get(i); + Village village = this.getClosestVillage(villagedoor.d(), 32); + + if (village == null) { + village = new Village(this.world); + this.villages.add(village); + this.c(); + } + + village.a(villagedoor); + } + + this.c.clear(); + } + + private void b(BlockPosition blockposition) { + boolean flag = true; + boolean flag1 = true; + boolean flag2 = true; + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + for (int i = -16; i < 16; ++i) { + for (int j = -4; j < 4; ++j) { + for (int k = -16; k < 16; ++k) { + blockposition_mutableblockposition.g(blockposition).d(i, j, k); + IBlockData iblockdata = this.world.paperConfig.villagesLoadChunks ? this.world.getType(blockposition_mutableblockposition) : this.world.getTypeIfLoaded(blockposition_mutableblockposition); // Paper + + if (this.a(iblockdata)) { + VillageDoor villagedoor = this.c(blockposition_mutableblockposition); + + if (villagedoor == null) { + this.a(iblockdata, blockposition_mutableblockposition); + } else { + villagedoor.a(this.time); + } + } + } + } + } + + } + + @Nullable + private VillageDoor c(BlockPosition blockposition) { + Iterator iterator = this.c.iterator(); + + VillageDoor villagedoor; + + do { + if (!iterator.hasNext()) { + iterator = this.villages.iterator(); + + VillageDoor villagedoor1; + + do { + if (!iterator.hasNext()) { + return null; + } + + Village village = (Village) iterator.next(); + + villagedoor1 = village.e(blockposition); + } while (villagedoor1 == null); + + return villagedoor1; + } + + villagedoor = (VillageDoor) iterator.next(); + } while (villagedoor.d().getX() != blockposition.getX() || villagedoor.d().getZ() != blockposition.getZ() || Math.abs(villagedoor.d().getY() - blockposition.getY()) > 1); + + return villagedoor; + } + + private void a(IBlockData iblockdata, BlockPosition blockposition) { + EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockDoor.FACING); + EnumDirection enumdirection1 = enumdirection.opposite(); + int i = this.a(blockposition, enumdirection, 5); + int j = this.a(blockposition, enumdirection1, i + 1); + + if (i != j) { + this.c.add(new VillageDoor(blockposition, i < j ? enumdirection : enumdirection1, this.time)); + } + + } + + private int a(BlockPosition blockposition, EnumDirection enumdirection, int i) { + int j = 0; + + for (int k = 1; k <= 5; ++k) { + if (this.world.e(blockposition.shift(enumdirection, k))) { + ++j; + if (j >= i) { + return j; + } + } + } + + return j; + } + + private boolean d(BlockPosition blockposition) { + Iterator iterator = this.b.iterator(); + + BlockPosition blockposition1; + + do { + if (!iterator.hasNext()) { + return false; + } + + blockposition1 = (BlockPosition) iterator.next(); + } while (!blockposition1.equals(blockposition)); + + return true; + } + + private boolean a(IBlockData iblockdata) { + return iblockdata != null && iblockdata.getBlock() instanceof BlockDoor && iblockdata.getMaterial() == Material.WOOD; // Paper + } + + public void a(NBTTagCompound nbttagcompound) { + this.time = nbttagcompound.getInt("Tick"); + NBTTagList nbttaglist = nbttagcompound.getList("Villages", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.getCompound(i); + Village village = new Village(world); // Paper + + village.a(nbttagcompound1); + this.villages.add(village); + } + + } + + public NBTTagCompound b(NBTTagCompound nbttagcompound) { + nbttagcompound.setInt("Tick", this.time); + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = this.villages.iterator(); + + while (iterator.hasNext()) { + Village village = (Village) iterator.next(); + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + village.b(nbttagcompound1); + nbttaglist.add((NBTBase) nbttagcompound1); + } + + nbttagcompound.set("Villages", nbttaglist); + return nbttagcompound; + } + + public static String a(WorldProvider worldprovider) { + return "villages" + worldprovider.getDimensionManager().d(); + } +} diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java new file mode 100644 index 000000000000..ac5d1580933b --- /dev/null +++ b/src/main/java/net/minecraft/server/PlayerChunk.java @@ -0,0 +1,329 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class PlayerChunk { + + private static final Logger a = LogManager.getLogger(); + private final PlayerChunkMap playerChunkMap; + public final List players = Lists.newArrayList(); + private final ChunkCoordIntPair location; + private final short[] dirtyBlocks = new short[64]; + @Nullable + public Chunk chunk; + private int dirtyCount; + private int h; + private long i; + boolean done; // Paper - package-private + boolean chunkExists; // Paper + // Paper start + PaperAsyncChunkProvider.CancellableChunkRequest chunkRequest; + private java.util.function.Consumer chunkLoadedConsumer = chunk -> { + chunkRequest = null; + PlayerChunk pChunk = PlayerChunk.this; + pChunk.chunk = chunk; + markChunkUsed(); // Paper - delay chunk unloads + }; + private boolean markedHigh = false; + void checkHighPriority(EntityPlayer player) { + if (done || markedHigh || chunk != null) { + return; + } + final double dist = getDistance(player.locX, player.locZ); + if (dist > 8) { + return; + } + if (dist >= 3 && getDistance(player, 5) > 3.5) { + return; + } + + markedHigh = true; + playerChunkMap.getWorld().getChunkProvider().bumpPriority(location); + if (chunkRequest == null) { + requestChunkIfNeeded(PlayerChunkMap.CAN_GEN_CHUNKS.test(player)); + } + } + private void requestChunkIfNeeded(boolean flag) { + if (chunkRequest == null) { + chunkRequest = this.playerChunkMap.getWorld().getChunkProvider().requestChunk(this.location.x, this.location.z, flag, markedHigh, chunkLoadedConsumer); + this.chunk = chunkRequest.getChunk(); // Paper) + markChunkUsed(); // Paper - delay chunk unloads + } + } + private double getDistance(EntityPlayer player, int inFront) { + final float yaw = MathHelper.normalizeYaw(player.yaw); + final double x = player.locX + (inFront * Math.cos(Math.toRadians(yaw))); + final double z = player.locZ + (inFront * Math.sin(Math.toRadians(yaw))); + return getDistance(x, z); + } + + private double getDistance(double blockX, double blockZ) { + final double x = location.x - ((int)Math.floor(blockX) >> 4); + final double z = location.z - ((int)Math.floor(blockZ) >> 4); + return Math.sqrt((x * x) + (z * z)); + } + + public PlayerChunk(PlayerChunkMap playerchunkmap, int i, int j) { + this.playerChunkMap = playerchunkmap; + this.location = new ChunkCoordIntPair(i, j); + ChunkProviderServer chunkproviderserver = playerchunkmap.getWorld().getChunkProvider(); + + chunkproviderserver.a(i, j); + this.chunk = chunkproviderserver.getChunkAt(i, j, false, false); // Paper + this.chunkExists = this.chunk != null || chunkproviderserver.chunkGoingToExists(i, j); // Paper + markChunkUsed(); // Paper - delay chunk unloads + } + + // Paper start + private void markChunkUsed() { + if (!chunkHasPlayers && chunkRequest != null) { + chunkRequest.cancel(); + chunkRequest = null; + } + if (chunk == null) { + return; + } + if (chunkHasPlayers) { + chunk.scheduledForUnload = null; + } else if (chunk.scheduledForUnload == null) { + chunk.scheduledForUnload = System.currentTimeMillis(); + } + } + private boolean chunkHasPlayers = false; + // Paper end + + public ChunkCoordIntPair a() { + return this.location; + } + + public void a(EntityPlayer entityplayer) { + if (this.players.contains(entityplayer)) { + PlayerChunk.a.debug("Failed to add player. {} already is in chunk {}, {}", entityplayer, this.location.x, this.location.z); + } else { + if (this.players.isEmpty()) { + this.i = this.playerChunkMap.getWorld().getTime(); + chunkHasPlayers = true; // Paper - delay chunk unloads + markChunkUsed(); // Paper - delay chunk unloads + } + + this.players.add(entityplayer); + if (this.done) { + this.sendChunk(entityplayer); + } else checkHighPriority(entityplayer); // Paper + + } + } + + public void b(EntityPlayer entityplayer) { + if (this.players.contains(entityplayer)) { + if (this.done) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutUnloadChunk(this.location.x, this.location.z)); + } + + this.players.remove(entityplayer); + if (this.players.isEmpty()) { + chunkHasPlayers = false; // Paper - delay chunk unloads + markChunkUsed(); // Paper - delay chunk unloads + this.playerChunkMap.b(this); + } + + } + } + + public boolean a(boolean flag) { + if (this.chunk != null) { + return true; + } else { + // Paper start - async chunks + requestChunkIfNeeded(flag); + // Paper end + return this.chunk != null; + } + } + + public boolean sendAll() { return b(); } // Paper - OBFHELPER + public boolean b() { + if (this.done) { + return true; + } else if (this.chunk == null) { + return false; + } else if (!this.chunk.isReady()) { + return false; + } else if (!this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, '\uffff', false)) { // Paper - Anti-Xray - Load nearby chunks if necessary + return false; // Paper - Anti-Xray - Wait and try again later + } else { + this.dirtyCount = 0; + this.h = 0; + this.done = true; + if (!this.players.isEmpty()) { + Packet packet = new PacketPlayOutMapChunk(this.chunk, 65535); + Iterator iterator = this.players.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + entityplayer.playerConnection.sendPacket(packet); + this.playerChunkMap.getWorld().getTracker().a(entityplayer, this.chunk); + } + } + + return true; + } + } + + public void sendChunk(EntityPlayer entityplayer) { + if (this.done) { + this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, '\uffff', true); // Paper - Anti-Xray - Load nearby chunks if necessary + entityplayer.playerConnection.sendPacket(new PacketPlayOutMapChunk(this.chunk, 65535)); + this.playerChunkMap.getWorld().getTracker().a(entityplayer, this.chunk); + } + } + + public void c() { + long i = this.playerChunkMap.getWorld().getTime(); + + if (this.chunk != null) { + this.chunk.b(this.chunk.m() + i - this.i); + } + + this.i = i; + } + + public void a(int i, int j, int k) { + if (this.done) { + if (this.dirtyCount == 0) { + this.playerChunkMap.a(this); + } + + this.h |= 1 << (j >> 4); + if (this.dirtyCount < 64) { + short short0 = (short) (i << 12 | k << 8 | j); + + for (int l = 0; l < this.dirtyCount; ++l) { + if (this.dirtyBlocks[l] == short0) { + return; + } + } + + this.dirtyBlocks[this.dirtyCount++] = short0; + } + + } + } + + public void a(Packet packet) { + if (this.done) { + for (int i = 0; i < this.players.size(); ++i) { + ((EntityPlayer) this.players.get(i)).playerConnection.sendPacket(packet); + } + + } + } + + public void d() { + if (this.done && this.chunk != null) { + if (this.dirtyCount != 0) { + int i; + int j; + int k; + + if (this.dirtyCount == 1) { + i = (this.dirtyBlocks[0] >> 12 & 15) + this.location.x * 16; + j = this.dirtyBlocks[0] & 255; + k = (this.dirtyBlocks[0] >> 8 & 15) + this.location.z * 16; + BlockPosition blockposition = new BlockPosition(i, j, k); + + this.a((Packet) (new PacketPlayOutBlockChange(this.playerChunkMap.getWorld(), blockposition))); + if (this.playerChunkMap.getWorld().getType(blockposition).getBlock().isTileEntity()) { + this.a(this.playerChunkMap.getWorld().getTileEntity(blockposition)); + } + } else if (this.dirtyCount == 64) { + // Paper - Anti-Xray - Loading chunks here could cause a ConcurrentModificationException #1104 + // Paper - Anti-Xray - TODO: Check if this is still the case for 1.13 + //this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, this.h, true); // Paper - Anti-Xray - Load nearby chunks if necessary + this.a((Packet) (new PacketPlayOutMapChunk(this.chunk, this.h))); + } else { + this.a((Packet) (new PacketPlayOutMultiBlockChange(this.dirtyCount, this.dirtyBlocks, this.chunk))); + + for (i = 0; i < this.dirtyCount; ++i) { + j = (this.dirtyBlocks[i] >> 12 & 15) + this.location.x * 16; + k = this.dirtyBlocks[i] & 255; + int l = (this.dirtyBlocks[i] >> 8 & 15) + this.location.z * 16; + BlockPosition blockposition1 = new BlockPosition(j, k, l); + + if (this.playerChunkMap.getWorld().getType(blockposition1).getBlock().isTileEntity()) { + this.a(this.playerChunkMap.getWorld().getTileEntity(blockposition1)); + } + } + } + + this.dirtyCount = 0; + this.h = 0; + } + } + } + + private void a(@Nullable TileEntity tileentity) { + if (tileentity != null) { + PacketPlayOutTileEntityData packetplayouttileentitydata = tileentity.getUpdatePacket(); + + if (packetplayouttileentitydata != null) { + this.a((Packet) packetplayouttileentitydata); + } + } + + } + + public boolean d(EntityPlayer entityplayer) { + return this.players.contains(entityplayer); + } + + public boolean a(Predicate predicate) { + return this.players.stream().anyMatch(predicate); + } + + public boolean a(double d0, Predicate predicate) { + int i = 0; + + for (int j = this.players.size(); i < j; ++i) { + EntityPlayer entityplayer = (EntityPlayer) this.players.get(i); + + if (predicate.test(entityplayer) && this.location.a(entityplayer) < d0 * d0) { + return true; + } + } + + return false; + } + + public boolean e() { + return this.done; + } + + @Nullable + public Chunk f() { + return this.chunk; + } + + public double g() { + double d0 = Double.MAX_VALUE; + Iterator iterator = this.players.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + double d1 = this.location.a(entityplayer); + + if (d1 < d0) { + d0 = d1; + } + } + + return d0; + } +} diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java new file mode 100644 index 000000000000..b7dda8e2821d --- /dev/null +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -0,0 +1,561 @@ +package net.minecraft.server; + +import co.aikar.timings.Timing; +import com.google.common.collect.AbstractIterator; +import com.google.common.collect.ComparisonChain; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +// CraftBukkit start +import java.util.LinkedList; +// CraftBukkit end + +public class PlayerChunkMap { + + private static final Predicate a = (entityplayer) -> { + return entityplayer != null && !entityplayer.isSpectator(); + }; + private static final Predicate b = (entityplayer) -> { + return entityplayer != null && (!entityplayer.isSpectator() || entityplayer.getWorldServer().getGameRules().getBoolean("spectatorsGenerateChunks")); + }; static final Predicate CAN_GEN_CHUNKS = b; // Paper - OBFHELPER + private final WorldServer world; + private final List managedPlayers = Lists.newArrayList(); + private final Long2ObjectMap e = new Long2ObjectOpenHashMap(4096); Long2ObjectMap getChunks() { return e; } // Paper - OBFHELPER + private final Set f = Sets.newHashSet(); + private final List g = Lists.newLinkedList(); + private final List h = Lists.newLinkedList(); + private final List i = Lists.newArrayList(); + private int j;public int getViewDistance() { return j; } // Paper OBFHELPER + private long k; + private boolean l = true; + private boolean m = true; + private boolean wasNotEmpty; // CraftBukkit - add field + + public PlayerChunkMap(WorldServer worldserver) { + this.world = worldserver; + this.a(worldserver.spigotConfig.viewDistance); // Spigot + } + + public WorldServer getWorld() { + return this.world; + } + + public Iterator b() { + final Iterator iterator = this.i.iterator(); + + return new AbstractIterator() { + protected Chunk computeNext() { + while (true) { + if (iterator.hasNext()) { + PlayerChunk playerchunk = (PlayerChunk) iterator.next(); + Chunk chunk = playerchunk.f(); + + if (chunk == null) { + continue; + } + + if (!chunk.v()) { + return chunk; + } + + if (!playerchunk.a(128.0D, PlayerChunkMap.a)) { + continue; + } + + return chunk; + } + + return (Chunk) this.endOfData(); + } + } + }; + } + + public void flush() { + long i = this.world.getTime(); + PlayerChunk playerchunk; + int j; + + if (i - this.k > 8000L) { + try (Timing ignored = world.timings.doChunkMapUpdate.startTiming()) { // Paper + this.k = i; + + for (j = 0; j < this.i.size(); ++j) { + playerchunk = (PlayerChunk) this.i.get(j); + playerchunk.d(); + playerchunk.c(); + } + } // Paper timing + } + + if (!this.f.isEmpty()) { + try (Timing ignored = world.timings.doChunkMapToUpdate.startTiming()) { // Paper + Iterator iterator = this.f.iterator(); + + while (iterator.hasNext()) { + playerchunk = (PlayerChunk) iterator.next(); + playerchunk.d(); + } + + this.f.clear(); + } // Paper timing + } + + if (this.l && i % 4L == 0L) { + this.l = false; + try (Timing ignored = world.timings.doChunkMapSortMissing.startTiming()) { // Paper + Collections.sort(this.h, (playerchunk1, playerchunk2) -> { + return ComparisonChain.start().compare(playerchunk1.g(), playerchunk2.g()).result(); + }); + } // Paper timing + } + + if (this.m && i % 4L == 2L) { + this.m = false; + try (Timing ignored = world.timings.doChunkMapSortSendToPlayers.startTiming()) { // Paper + Collections.sort(this.g, (playerchunk1, playerchunk2) -> { + return ComparisonChain.start().compare(playerchunk1.g(), playerchunk2.g()).result(); + }); + } // Paper timing + } + + if (!this.h.isEmpty()) { + try (Timing ignored = world.timings.doChunkMapPlayersNeedingChunks.startTiming()) { // Paper + // Spigot start + org.spigotmc.SlackActivityAccountant activityAccountant = this.world.getMinecraftServer().slackActivityAccountant; + activityAccountant.startActivity(0.5); + int chunkGensAllowed = world.paperConfig.maxChunkGensPerTick; // Paper + // Spigot end + + Iterator iterator1 = this.h.iterator(); + + while (iterator1.hasNext()) { + PlayerChunk playerchunk1 = (PlayerChunk) iterator1.next(); + + if (playerchunk1.f() == null) { + boolean flag = playerchunk1.a(PlayerChunkMap.b); + // Paper start + if (flag && !playerchunk1.chunkExists && chunkGensAllowed-- <= 0) { + continue; + } + // Paper end + + if (playerchunk1.a(flag)) { + iterator1.remove(); + if (playerchunk1.b()) { + this.g.remove(playerchunk1); + } + + if (activityAccountant.activityTimeIsExhausted()) { // Spigot + break; + } + } + // CraftBukkit start - SPIGOT-2891: remove once chunk has been provided + } else { + iterator1.remove(); + } + // CraftBukkit end + } + + activityAccountant.endActivity(); // Spigot + } // Paper timing + } + + if (!this.g.isEmpty()) { + j = world.paperConfig.maxChunkSendsPerTick; // Paper + try (Timing ignored = world.timings.doChunkMapPendingSendToPlayers.startTiming()) { // Paper + Iterator iterator2 = this.g.iterator(); + + while (iterator2.hasNext()) { + PlayerChunk playerchunk2 = (PlayerChunk) iterator2.next(); + + if (playerchunk2.b()) { + iterator2.remove(); + --j; + if (j < 0) { + break; + } + } + } + } // Paper timing + } + + if (this.managedPlayers.isEmpty()) { + try (Timing ignored = world.timings.doChunkMapUnloadChunks.startTiming()) { // Paper + WorldProvider worldprovider = this.world.worldProvider; + + if (!worldprovider.canRespawn() && !this.world.savingDisabled) { // Paper - respect saving disabled setting + this.world.getChunkProvider().b(); + } + } // Paper timing + } + + } + + public boolean a(int i, int j) { + long k = d(i, j); + + return this.e.get(k) != null; + } + + @Nullable + public PlayerChunk getChunk(int i, int j) { + return (PlayerChunk) this.e.get(d(i, j)); + } + + private PlayerChunk c(int i, int j) { + long k = d(i, j); + PlayerChunk playerchunk = (PlayerChunk) this.e.get(k); + + if (playerchunk == null) { + playerchunk = new PlayerChunk(this, i, j); + this.e.put(k, playerchunk); + this.i.add(playerchunk); + if (playerchunk.f() == null) { + this.h.add(playerchunk); + } + + if (!playerchunk.b()) { + this.g.add(playerchunk); + } + } + + return playerchunk; + } + + // CraftBukkit start - add method + public final boolean isChunkInUse(int x, int z) { + PlayerChunk pi = getChunk(x, z); + if (pi != null) { + return (pi.players.size() > 0); + } + return false; + } + // CraftBukkit end + + public void flagDirty(BlockPosition blockposition) { + int i = blockposition.getX() >> 4; + int j = blockposition.getZ() >> 4; + PlayerChunk playerchunk = this.getChunk(i, j); + + if (playerchunk != null) { + playerchunk.a(blockposition.getX() & 15, blockposition.getY(), blockposition.getZ() & 15); + } + + } + + public void addPlayer(EntityPlayer entityplayer) { + int i = (int) entityplayer.locX >> 4; + int j = (int) entityplayer.locZ >> 4; + + entityplayer.d = entityplayer.locX; + entityplayer.e = entityplayer.locZ; + + + // CraftBukkit start - Load nearby chunks first + List chunkList = new LinkedList(); + + // Paper start - Player view distance API + int viewDistance = entityplayer.getViewDistance(); + for (int k = i - viewDistance; k <= i + viewDistance; ++k) { + for (int l = j - viewDistance; l <= j + viewDistance; ++l) { + // Paper end + chunkList.add(new ChunkCoordIntPair(k, l)); + } + } + + Collections.sort(chunkList, new ChunkCoordComparator(entityplayer)); + for (ChunkCoordIntPair pair : chunkList) { + this.c(pair.x, pair.z).a(entityplayer); + } + // CraftBukkit end + + this.managedPlayers.add(entityplayer); + this.e(); + } + + public void removePlayer(EntityPlayer entityplayer) { + int i = (int) entityplayer.d >> 4; + int j = (int) entityplayer.e >> 4; + + // Paper start - Player view distance API + int viewDistance = entityplayer.getViewDistance(); + for (int k = i - viewDistance; k <= i + viewDistance; ++k) { + for (int l = j - viewDistance; l <= j + viewDistance; ++l) { + // Paper end + PlayerChunk playerchunk = this.getChunk(k, l); + + if (playerchunk != null) { + playerchunk.b(entityplayer); + } + } + } + + this.managedPlayers.remove(entityplayer); + this.e(); + } + + private boolean a(int i, int j, int k, int l, int i1) { + int j1 = i - k; + int k1 = j - l; + + return j1 >= -i1 && j1 <= i1 ? k1 >= -i1 && k1 <= i1 : false; + } + + public void movePlayer(EntityPlayer entityplayer) { + int i = (int) entityplayer.locX >> 4; + int j = (int) entityplayer.locZ >> 4; + double d0 = entityplayer.d - entityplayer.locX; + double d1 = entityplayer.e - entityplayer.locZ; + double d2 = d0 * d0 + d1 * d1; + + if (d2 >= 64.0D) { + int k = (int) entityplayer.d >> 4; + int l = (int) entityplayer.e >> 4; + int i1 = entityplayer.getViewDistance(); // Paper - Player view distance API + + int j1 = i - k; + int k1 = j - l; + + List chunksToLoad = new LinkedList(); // CraftBukkit + + if (j1 != 0 || k1 != 0) { + for (int l1 = i - i1; l1 <= i + i1; ++l1) { + for (int i2 = j - i1; i2 <= j + i1; ++i2) { + if (!this.a(l1, i2, k, l, i1)) { + // this.c(l1, i2).a(entityplayer); + chunksToLoad.add(new ChunkCoordIntPair(l1, i2)); // CraftBukkit + } + + if (!this.a(l1 - j1, i2 - k1, i, j, i1)) { + PlayerChunk playerchunk = this.getChunk(l1 - j1, i2 - k1); + + if (playerchunk != null) { + playerchunk.b(entityplayer); + } + } else { // Paper start + PlayerChunk playerchunk = this.getChunk(l1 - j1, i2 - k1); + if (playerchunk != null) { + playerchunk.checkHighPriority(entityplayer); // Paper + } + } + // Paper end + } + } + + entityplayer.d = entityplayer.locX; + entityplayer.e = entityplayer.locZ; + this.e(); + + // CraftBukkit start - send nearest chunks first + Collections.sort(chunksToLoad, new ChunkCoordComparator(entityplayer)); + for (ChunkCoordIntPair pair : chunksToLoad) { + // Paper start + PlayerChunk c = this.c(pair.x, pair.z); + c.checkHighPriority(entityplayer); + c.a(entityplayer); + // Paper end + } + // CraftBukkit end + } + } + } + + public boolean a(EntityPlayer entityplayer, int i, int j) { + PlayerChunk playerchunk = this.getChunk(i, j); + + return playerchunk != null && playerchunk.d(entityplayer) && playerchunk.e(); + } + + public final void setViewDistanceForAll(int viewDistance) { this.a(viewDistance); } // Paper - OBFHELPER + // Paper start - Separate into two methods + public void a(int i) { + i = MathHelper.clamp(i, 3, 32); + if (i != this.j) { + int j = i - this.j; + List list = Lists.newArrayList(this.managedPlayers); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + this.setViewDistance(entityplayer, i, false); // Paper - Split, don't mark sort pending, we'll handle it after + } + + this.j = i; + this.e(); + } + } + + public void setViewDistance(EntityPlayer entityplayer, int i) { + this.setViewDistance(entityplayer, i, true); // Mark sort pending by default so we don't have to remember to do so all the time + } + + // Copied from above with minor changes + public void setViewDistance(EntityPlayer entityplayer, int i, boolean markSort) { + i = MathHelper.clamp(i, 3, 32); + int oldViewDistance = entityplayer.getViewDistance(); + if (i != oldViewDistance) { + int j = i - oldViewDistance; + + int k = (int) entityplayer.locX >> 4; + int l = (int) entityplayer.locZ >> 4; + int i1; + int j1; + + if (j > 0) { + for (i1 = k - i; i1 <= k + i; ++i1) { + for (j1 = l - i; j1 <= l + i; ++j1) { + PlayerChunk playerchunk = this.c(i1, j1); + + if (!playerchunk.d(entityplayer)) { + playerchunk.a(entityplayer); + } + } + } + } else { + for (i1 = k - oldViewDistance; i1 <= k + oldViewDistance; ++i1) { + for (j1 = l - oldViewDistance; j1 <= l + oldViewDistance; ++j1) { + if (!this.a(i1, j1, k, l, i)) { + this.c(i1, j1).b(entityplayer); + } + } + } + if (markSort) { + this.e(); + } + } + } + } + + void shutdown() { + getChunks().values().forEach(pchunk -> { + PaperAsyncChunkProvider.CancellableChunkRequest chunkRequest = pchunk.chunkRequest; + if (chunkRequest != null) { + chunkRequest.cancel(); + } + }); + } + // Paper end + + private void e() { + this.l = true; + this.m = true; + } + + public static int getFurthestViewableBlock(int i) { + return i * 16 - 16; + } + + private static long d(int i, int j) { + return (long) i + 2147483647L | (long) j + 2147483647L << 32; + } + + public void a(PlayerChunk playerchunk) { + org.spigotmc.AsyncCatcher.catchOp("Async Player Chunk Add"); // Paper + this.f.add(playerchunk); + } + + public void b(PlayerChunk playerchunk) { + org.spigotmc.AsyncCatcher.catchOp("Async Player Chunk Remove"); // Paper + ChunkCoordIntPair chunkcoordintpair = playerchunk.a(); + long i = d(chunkcoordintpair.x, chunkcoordintpair.z); + + playerchunk.c(); + this.e.remove(i); + this.i.remove(playerchunk); + this.f.remove(playerchunk); + this.g.remove(playerchunk); + this.h.remove(playerchunk); + Chunk chunk = playerchunk.f(); + + if (chunk != null) { + // Paper start - delay chunk unloads + if (world.paperConfig.delayChunkUnloadsBy <= 0) { + this.getWorld().getChunkProvider().unload(chunk); + } else { + chunk.scheduledForUnload = System.currentTimeMillis(); + } + // Paper end + } + + } + + // CraftBukkit start - Sorter to load nearby chunks first + private static class ChunkCoordComparator implements java.util.Comparator { + private int x; + private int z; + + public ChunkCoordComparator (EntityPlayer entityplayer) { + x = (int) entityplayer.locX >> 4; + z = (int) entityplayer.locZ >> 4; + } + + public int compare(ChunkCoordIntPair a, ChunkCoordIntPair b) { + if (a.equals(b)) { + return 0; + } + + // Subtract current position to set center point + int ax = a.x - this.x; + int az = a.z - this.z; + int bx = b.x - this.x; + int bz = b.z - this.z; + + int result = ((ax - bx) * (ax + bx)) + ((az - bz) * (az + bz)); + if (result != 0) { + return result; + } + + if (ax < 0) { + if (bx < 0) { + return bz - az; + } else { + return -1; + } + } else { + if (bx < 0) { + return 1; + } else { + return az - bz; + } + } + } + } + // CraftBukkit end + + // Paper start - Player view distance API + public void updateViewDistance(EntityPlayer player, int distanceIn) { + final int oldViewDistance = player.getViewDistance(); + + // This represents the view distance that we will set on the player + // It can exist as a negative value + int playerViewDistance = MathHelper.clamp(distanceIn, 3, 32); + + // This value is the one we actually use to update the chunk map + // We don't ever want this to be a negative + int toSet = playerViewDistance; + + if (distanceIn < 0) { + playerViewDistance = -1; + toSet = world.getPlayerChunkMap().getViewDistance(); + } + + if (toSet != oldViewDistance) { + // Order matters + this.setViewDistance(player, toSet); + player.setViewDistance(playerViewDistance); + + //Force update entity trackers + this.getWorld().getTracker().updatePlayer(player); + } + } + // Paper end +} diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java new file mode 100644 index 000000000000..dc8c20efb4d7 --- /dev/null +++ b/src/main/java/net/minecraft/server/PlayerConnection.java @@ -0,0 +1,2653 @@ +package net.minecraft.server; + +import com.google.common.primitives.Doubles; +import com.google.common.primitives.Floats; +import com.google.common.util.concurrent.Futures; +import com.mojang.brigadier.ParseResults; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.suggestion.Suggestions; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; +import javax.annotation.Nullable; + +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import java.util.HashSet; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import org.bukkit.Location; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.craftbukkit.util.LazyPlayerSet; +import org.bukkit.craftbukkit.util.Waitable; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.CraftItemEvent; +import org.bukkit.event.inventory.InventoryAction; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCreativeEvent; +import org.bukkit.event.inventory.InventoryType.SlotType; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerAnimationEvent; +import org.bukkit.event.player.PlayerChatEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerItemHeldEvent; +import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerResourcePackStatusEvent; +import org.bukkit.event.player.PlayerSwapHandItemsEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.event.player.PlayerToggleFlightEvent; +import org.bukkit.event.player.PlayerToggleSneakEvent; +import org.bukkit.event.player.PlayerToggleSprintEvent; +import org.bukkit.inventory.CraftingInventory; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.InventoryView; +import org.bukkit.util.NumberConversions; +import com.destroystokyo.paper.event.player.IllegalPacketEvent; // Paper +import com.destroystokyo.paper.event.player.PlayerJumpEvent; // Paper +import co.aikar.timings.MinecraftTimings; // Paper +// CraftBukkit end + +public class PlayerConnection implements PacketListenerPlayIn, ITickable { + + private static final Logger LOGGER = LogManager.getLogger(); + public final NetworkManager networkManager; + private final MinecraftServer minecraftServer; + public EntityPlayer player; + private int e; + private long lastKeepAlive = SystemUtils.getMonotonicMillis(); private void setLastPing(long lastPing) { this.lastKeepAlive = lastPing;}; private long getLastPing() { return this.lastKeepAlive;}; // Paper - OBFHELPER + private boolean awaitingKeepAlive; private void setPendingPing(boolean isPending) { this.awaitingKeepAlive = isPending;}; private boolean isPendingPing() { return this.awaitingKeepAlive;}; // Paper - OBFHELPER + private long h; private void setKeepAliveID(long keepAliveID) { this.h = keepAliveID;}; private long getKeepAliveID() {return this.h; }; // Paper - OBFHELPER + // CraftBukkit start - multithreaded fields + private volatile int chatThrottle; + private static final AtomicIntegerFieldUpdater chatSpamField = AtomicIntegerFieldUpdater.newUpdater(PlayerConnection.class, "chatThrottle"); + private final java.util.concurrent.atomic.AtomicInteger tabSpamLimiter = new java.util.concurrent.atomic.AtomicInteger(); // Paper - configurable tab spam limits + // CraftBukkit end + private int j; + private final IntHashMap k = new IntHashMap<>(); + private double l; + private double m; + private double n; + private double o; + private double p; + private double q; + private Entity r; + private double s; + private double t; + private double u; + private double v; + private double w; + private double x; + private Vec3D teleportPos; + private int teleportAwait; + private int A; + private boolean B; + private int C; + private boolean D; + private int E; + private int receivedMovePackets; + private int processedMovePackets; + private static final int MAX_SIGN_LINE_LENGTH = Integer.getInteger("Paper.maxSignLength", 80); + private static final long KEEPALIVE_LIMIT = Long.getLong("paper.playerconnection.keepalive", 30) * 1000; // Paper - provide property to set keepalive limit + + public PlayerConnection(MinecraftServer minecraftserver, NetworkManager networkmanager, EntityPlayer entityplayer) { + this.minecraftServer = minecraftserver; + this.networkManager = networkmanager; + networkmanager.setPacketListener(this); + this.player = entityplayer; + entityplayer.playerConnection = this; + + // CraftBukkit start - add fields and methods + this.server = minecraftserver.server; + } + + private final org.bukkit.craftbukkit.CraftServer server; + public boolean processedDisconnect; + private int lastTick = MinecraftServer.currentTick; + private int allowedPlayerTicks = 1; + private int lastDropTick = MinecraftServer.currentTick; + private int lastBookTick = MinecraftServer.currentTick; + private int dropCount = 0; + private static final int SURVIVAL_PLACE_DISTANCE_SQUARED = 6 * 6; + private static final int CREATIVE_PLACE_DISTANCE_SQUARED = 7 * 7; + + // Get position of last block hit for BlockDamageLevel.STOPPED + private double lastPosX = Double.MAX_VALUE; + private double lastPosY = Double.MAX_VALUE; + private double lastPosZ = Double.MAX_VALUE; + private float lastPitch = Float.MAX_VALUE; + private float lastYaw = Float.MAX_VALUE; + private boolean justTeleported = false; + private boolean hasMoved; // Spigot + + public CraftPlayer getPlayer() { + return (this.player == null) ? null : (CraftPlayer) this.player.getBukkitEntity(); + } + // CraftBukkit end + + public void tick() { + this.syncPosition(); + this.player.playerTick(); + this.player.setLocation(this.l, this.m, this.n, this.player.yaw, this.player.pitch); + ++this.e; + this.processedMovePackets = this.receivedMovePackets; + if (this.B) { + if (++this.C > 80) { + PlayerConnection.LOGGER.warn("{} was kicked for floating too long!", this.player.getDisplayName().getString()); + this.disconnect(com.destroystokyo.paper.PaperConfig.flyingKickPlayerMessage); // Paper - use configurable kick message + return; + } + } else { + this.B = false; + this.C = 0; + } + + this.r = this.player.getRootVehicle(); + if (this.r != this.player && this.r.bO() == this.player) { + this.s = this.r.locX; + this.t = this.r.locY; + this.u = this.r.locZ; + this.v = this.r.locX; + this.w = this.r.locY; + this.x = this.r.locZ; + if (this.D && this.player.getRootVehicle().bO() == this.player) { + if (++this.E > 80) { + PlayerConnection.LOGGER.warn("{} was kicked for floating a vehicle too long!", this.player.getDisplayName().getString()); + this.disconnect(com.destroystokyo.paper.PaperConfig.flyingKickVehicleMessage); // Paper - use configurable kick message + return; + } + } else { + this.D = false; + this.E = 0; + } + } else { + this.r = null; + this.D = false; + this.E = 0; + } + + this.minecraftServer.methodProfiler.enter("keepAlive"); + // Paper Start - give clients a longer time to respond to pings as per pre 1.12.2 timings + // This should effectively place the keepalive handling back to "as it was" before 1.12.2 + long currentTime = SystemUtils.getMonotonicMillis(); + long elapsedTime = currentTime - this.getLastPing(); + + if (this.isPendingPing()) { + if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected + PlayerConnection.LOGGER.warn("{} was kicked due to keepalive timeout!", this.player.getName()); // more info + this.disconnect(new ChatMessage("disconnect.timeout", new Object[0])); + } + } else { + if (elapsedTime >= 15000L) { // 15 seconds + this.setPendingPing(true); + this.setLastPing(currentTime); + this.setKeepAliveID(currentTime); + this.sendPacket(new PacketPlayOutKeepAlive(this.getKeepAliveID())); + + } + } + // Paper end + + this.minecraftServer.methodProfiler.exit(); + // CraftBukkit start + for (int spam; (spam = this.chatThrottle) > 0 && !chatSpamField.compareAndSet(this, spam, spam - 1); ) ; + if (tabSpamLimiter.get() > 0) tabSpamLimiter.getAndDecrement(); // Paper - split to seperate variable + /* Use thread-safe field access instead + if (this.chatThrottle > 0) { + --this.chatThrottle; + } + */ + // CraftBukkit end + + if (this.j > 0) { + --this.j; + } + + if (this.player.F() > 0L && this.minecraftServer.getIdleTimeout() > 0 && SystemUtils.getMonotonicMillis() - this.player.F() > (long) (this.minecraftServer.getIdleTimeout() * 1000 * 60)) { + this.player.resetIdleTimer(); // CraftBukkit - SPIGOT-854 + this.disconnect(new ChatMessage("multiplayer.disconnect.idling", new Object[0])); + } + + } + + public void syncPosition() { + this.l = this.player.locX; + this.m = this.player.locY; + this.n = this.player.locZ; + this.o = this.player.locX; + this.p = this.player.locY; + this.q = this.player.locZ; + } + + public NetworkManager a() { + return this.networkManager; + } + + // CraftBukkit start + @Deprecated + public void disconnect(IChatBaseComponent ichatbasecomponent) { + disconnect(CraftChatMessage.fromComponent(ichatbasecomponent, EnumChatFormat.WHITE)); + } + // CraftBukkit end + + public void disconnect(String s) { + // CraftBukkit start - fire PlayerKickEvent + if (this.processedDisconnect) { + return; + } + String leaveMessage = EnumChatFormat.YELLOW + this.player.getName() + " left the game."; + + PlayerKickEvent event = new PlayerKickEvent(this.server.getPlayer(this.player), s, leaveMessage); + + if (this.server.getServer().isRunning()) { + this.server.getPluginManager().callEvent(event); + } + + if (event.isCancelled()) { + // Do not kick the player + return; + } + // Send the possibly modified leave message + s = event.getReason(); + // CraftBukkit end + final ChatComponentText ichatbasecomponent = new ChatComponentText(s); + + this.networkManager.sendPacket(new PacketPlayOutKickDisconnect(ichatbasecomponent), (future) -> { + this.networkManager.close(ichatbasecomponent); + }); + this.a(ichatbasecomponent); // CraftBukkit - fire quit instantly + this.networkManager.stopReading(); + MinecraftServer minecraftserver = this.minecraftServer; + NetworkManager networkmanager = this.networkManager; + + this.networkManager.getClass(); + // CraftBukkit - Don't wait + minecraftserver.postToMainThread(networkmanager::handleDisconnection); + } + + public void a(PacketPlayInSteerVehicle packetplayinsteervehicle) { + PlayerConnectionUtils.ensureMainThread(packetplayinsteervehicle, this, this.player.getWorldServer()); + this.player.a(packetplayinsteervehicle.b(), packetplayinsteervehicle.c(), packetplayinsteervehicle.d(), packetplayinsteervehicle.e()); + } + + private static boolean b(PacketPlayInFlying packetplayinflying) { + return Doubles.isFinite(packetplayinflying.a(0.0D)) && Doubles.isFinite(packetplayinflying.b(0.0D)) && Doubles.isFinite(packetplayinflying.c(0.0D)) && Floats.isFinite(packetplayinflying.b(0.0F)) && Floats.isFinite(packetplayinflying.a(0.0F)) ? Math.abs(packetplayinflying.a(0.0D)) > 3.0E7D || Math.abs(packetplayinflying.b(0.0D)) > 3.0E7D || Math.abs(packetplayinflying.c(0.0D)) > 3.0E7D : true; + } + + private static boolean b(PacketPlayInVehicleMove packetplayinvehiclemove) { + return !Doubles.isFinite(packetplayinvehiclemove.getX()) || !Doubles.isFinite(packetplayinvehiclemove.getY()) || !Doubles.isFinite(packetplayinvehiclemove.getZ()) || !Floats.isFinite(packetplayinvehiclemove.getPitch()) || !Floats.isFinite(packetplayinvehiclemove.getYaw()); + } + + public void a(PacketPlayInVehicleMove packetplayinvehiclemove) { + PlayerConnectionUtils.ensureMainThread(packetplayinvehiclemove, this, this.player.getWorldServer()); + if (b(packetplayinvehiclemove)) { + this.disconnect(new ChatMessage("multiplayer.disconnect.invalid_vehicle_movement", new Object[0])); + } else { + Entity entity = this.player.getRootVehicle(); + + if (entity != this.player && entity.bO() == this.player && entity == this.r) { + WorldServer worldserver = this.player.getWorldServer(); + double d0 = entity.locX; + double d1 = entity.locY; + double d2 = entity.locZ; + double d3 = packetplayinvehiclemove.getX(); + double d4 = packetplayinvehiclemove.getY(); + double d5 = packetplayinvehiclemove.getZ(); + float f = packetplayinvehiclemove.getYaw(); + float f1 = packetplayinvehiclemove.getPitch(); + double d6 = d3 - this.s; + double d7 = d4 - this.t; + double d8 = d5 - this.u; + double d9 = entity.motX * entity.motX + entity.motY * entity.motY + entity.motZ * entity.motZ; + double d10 = d6 * d6 + d7 * d7 + d8 * d8; + + + // CraftBukkit start - handle custom speeds and skipped ticks + this.allowedPlayerTicks += (System.currentTimeMillis() / 50) - this.lastTick; + this.allowedPlayerTicks = Math.max(this.allowedPlayerTicks, 1); + this.lastTick = (int) (System.currentTimeMillis() / 50); + + ++this.receivedMovePackets; + int i = this.receivedMovePackets - this.processedMovePackets; + if (i > Math.max(this.allowedPlayerTicks, 5)) { + PlayerConnection.LOGGER.debug(this.player.getName() + " is sending move packets too frequently (" + i + " packets since last tick)"); + i = 1; + } + + if (d10 > 0) { + allowedPlayerTicks -= 1; + } else { + allowedPlayerTicks = 20; + } + double speed; + if (player.abilities.isFlying) { + speed = player.abilities.flySpeed * 20f; + } else { + speed = player.abilities.walkSpeed * 10f; + } + speed *= 2f; // TODO: Get the speed of the vehicle instead of the player + + // Paper start - Prevent moving into unloaded chunks + if (player.world.paperConfig.preventMovingIntoUnloadedChunks && !worldserver.isChunkLoaded((int) Math.floor(packetplayinvehiclemove.getX()) >> 4, (int) Math.floor(packetplayinvehiclemove.getZ()) >> 4, false)) { + this.networkManager.sendPacket(new PacketPlayOutVehicleMove(entity)); + return; + } + // Paper end + + if (d10 - d9 > Math.max(100.0D, Math.pow((double) (org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed), 2)) && (!this.minecraftServer.H() || !this.minecraftServer.G().equals(entity.getDisplayName().getString()))) { + // CraftBukkit end + PlayerConnection.LOGGER.warn("{} (vehicle of {}) moved too quickly! {},{},{}", entity.getDisplayName().getString(), this.player.getDisplayName().getString(), d6, d7, d8); + this.networkManager.sendPacket(new PacketPlayOutVehicleMove(entity)); + return; + } + + boolean flag = worldserver.getCubes(entity, entity.getBoundingBox().shrink(0.0625D)); + + d6 = d3 - this.v; + d7 = d4 - this.w - 1.0E-6D; + d8 = d5 - this.x; + entity.move(EnumMoveType.PLAYER, d6, d7, d8); + double d11 = d7; + + d6 = d3 - entity.locX; + d7 = d4 - entity.locY; + if (d7 > -0.5D || d7 < 0.5D) { + d7 = 0.0D; + } + + d8 = d5 - entity.locZ; + d10 = d6 * d6 + d7 * d7 + d8 * d8; + boolean flag1 = false; + + if (d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot + flag1 = true; + PlayerConnection.LOGGER.warn(entity.getName() + " (vehicle of " + this.player.getName() + ") moved wrongly!"); // Paper - More informative + } + Location curPos = this.getPlayer().getLocation(); // Spigot + + entity.setLocation(d3, d4, d5, f, f1); + player.setLocation(d3, d4, d5, this.player.yaw, this.player.pitch); // CraftBukkit + boolean flag2 = worldserver.getCubes(entity, entity.getBoundingBox().shrink(0.0625D)); + + if (flag && (flag1 || !flag2)) { + entity.setLocation(d0, d1, d2, f, f1); + player.setLocation(d0, d1, d2, this.player.yaw, this.player.pitch); // CraftBukkit + this.networkManager.sendPacket(new PacketPlayOutVehicleMove(entity)); + return; + } + + // CraftBukkit start - fire PlayerMoveEvent + Player player = this.getPlayer(); + // Spigot Start + if ( !hasMoved ) + { + lastPosX = curPos.getX(); + lastPosY = curPos.getY(); + lastPosZ = curPos.getZ(); + lastYaw = curPos.getYaw(); + lastPitch = curPos.getPitch(); + hasMoved = true; + } + // Spigot End + Location from = new Location(player.getWorld(), lastPosX, lastPosY, lastPosZ, lastYaw, lastPitch); // Get the Players previous Event location. + Location to = player.getLocation().clone(); // Start off the To location as the Players current location. + + // If the packet contains movement information then we update the To location with the correct XYZ. + to.setX(packetplayinvehiclemove.getX()); + to.setY(packetplayinvehiclemove.getY()); + to.setZ(packetplayinvehiclemove.getZ()); + + + // If the packet contains look information then we update the To location with the correct Yaw & Pitch. + to.setYaw(packetplayinvehiclemove.getYaw()); + to.setPitch(packetplayinvehiclemove.getPitch()); + + // Prevent 40 event-calls for less than a single pixel of movement >.> + double delta = Math.pow(this.lastPosX - to.getX(), 2) + Math.pow(this.lastPosY - to.getY(), 2) + Math.pow(this.lastPosZ - to.getZ(), 2); + float deltaAngle = Math.abs(this.lastYaw - to.getYaw()) + Math.abs(this.lastPitch - to.getPitch()); + + if ((delta > 1f / 256 || deltaAngle > 10f) && !this.player.isFrozen()) { + this.lastPosX = to.getX(); + this.lastPosY = to.getY(); + this.lastPosZ = to.getZ(); + this.lastYaw = to.getYaw(); + this.lastPitch = to.getPitch(); + + // Skip the first time we do this + if (true) { // Spigot - don't skip any move events + Location oldTo = to.clone(); + PlayerMoveEvent event = new PlayerMoveEvent(player, from, to); + this.server.getPluginManager().callEvent(event); + + // If the event is cancelled we move the player back to their old location. + if (event.isCancelled()) { + teleport(from); + return; + } + + // If a Plugin has changed the To destination then we teleport the Player + // there to avoid any 'Moved wrongly' or 'Moved too quickly' errors. + // We only do this if the Event was not cancelled. + if (!oldTo.equals(event.getTo()) && !event.isCancelled()) { + this.player.getBukkitEntity().teleport(event.getTo(), PlayerTeleportEvent.TeleportCause.PLUGIN); + return; + } + + // Check to see if the Players Location has some how changed during the call of the event. + // This can happen due to a plugin teleporting the player instead of using .setTo() + if (!from.equals(this.getPlayer().getLocation()) && this.justTeleported) { + this.justTeleported = false; + return; + } + } + } + // CraftBukkit end + + this.minecraftServer.getPlayerList().updateChunks(this.player); + this.player.checkMovement(this.player.locX - d0, this.player.locY - d1, this.player.locZ - d2); + this.D = d11 >= -0.03125D && !this.minecraftServer.getAllowFlight() && !worldserver.a(entity.getBoundingBox().g(0.0625D).b(0.0D, -0.55D, 0.0D)); + this.v = entity.locX; + this.w = entity.locY; + this.x = entity.locZ; + } + + } + } + + public void a(PacketPlayInTeleportAccept packetplayinteleportaccept) { + PlayerConnectionUtils.ensureMainThread(packetplayinteleportaccept, this, this.player.getWorldServer()); + if (packetplayinteleportaccept.b() == this.teleportAwait && this.teleportPos != null) { // CraftBukkit + this.player.setLocation(this.teleportPos.x, this.teleportPos.y, this.teleportPos.z, this.player.yaw, this.player.pitch); + this.o = this.teleportPos.x; + this.p = this.teleportPos.y; + this.q = this.teleportPos.z; + if (this.player.H()) { + this.player.I(); + } + + this.teleportPos = null; + this.minecraftServer.getPlayerList().updateChunks(this.player); // CraftBukkit + } + + } + + public void a(PacketPlayInRecipeDisplayed packetplayinrecipedisplayed) { + PlayerConnectionUtils.ensureMainThread(packetplayinrecipedisplayed, this, this.player.getWorldServer()); + if (packetplayinrecipedisplayed.b() == PacketPlayInRecipeDisplayed.Status.SHOWN) { + IRecipe irecipe = this.minecraftServer.getCraftingManager().a(packetplayinrecipedisplayed.c()); + + if (irecipe != null) { + this.player.B().e(irecipe); + } + } else if (packetplayinrecipedisplayed.b() == PacketPlayInRecipeDisplayed.Status.SETTINGS) { + this.player.B().a(packetplayinrecipedisplayed.d()); + this.player.B().b(packetplayinrecipedisplayed.e()); + this.player.B().c(packetplayinrecipedisplayed.f()); + this.player.B().d(packetplayinrecipedisplayed.g()); + } + + } + + public void a(PacketPlayInAdvancements packetplayinadvancements) { + PlayerConnectionUtils.ensureMainThread(packetplayinadvancements, this, this.player.getWorldServer()); + if (packetplayinadvancements.c() == PacketPlayInAdvancements.Status.OPENED_TAB) { + MinecraftKey minecraftkey = packetplayinadvancements.d(); + Advancement advancement = this.minecraftServer.getAdvancementData().a(minecraftkey); + + if (advancement != null) { + this.player.getAdvancementData().a(advancement); + } + } + + } + + public void a(PacketPlayInTabComplete packetplayintabcomplete) { + // PlayerConnectionUtils.ensureMainThread(packetplayintabcomplete, this, this.player.getWorldServer()); // Paper - run this async + // CraftBukkit start + if (tabSpamLimiter.addAndGet(com.destroystokyo.paper.PaperConfig.tabSpamIncrement) > com.destroystokyo.paper.PaperConfig.tabSpamLimit && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) { // Paper start - split and make configurable + minecraftServer.postToMainThread(() -> this.disconnect(new ChatMessage("disconnect.spam", new Object[0]))); // Paper + return; + } + // CraftBukkit end + StringReader stringreader = new StringReader(packetplayintabcomplete.c()); + + if (stringreader.canRead() && stringreader.peek() == '/') { + stringreader.skip(); + } + + // Paper start - async tab completion + com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event; + java.util.List completions = new java.util.ArrayList<>(); + String buffer = packetplayintabcomplete.c(); + event = new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(this.getPlayer(), completions, + buffer, true, null); + event.callEvent(); + completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.getCompletions(); + // If the event isn't handled, we can assume that we have no completions, and so we'll ask the server + if (!event.isHandled()) { + if (!event.isCancelled()) { + // Paper end - async tab completion + ParseResults parseresults = this.minecraftServer.getCommandDispatcher().a().parse(stringreader, this.player.getCommandListener()); + + this.minecraftServer.getCommandDispatcher().a().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> { + if (((Suggestions) suggestions).isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [] from showing for plugins with nothing more to offer + this.networkManager.sendPacket(new PacketPlayOutTabComplete(packetplayintabcomplete.b(), (Suggestions) suggestions)); // CraftBukkit - decompile error + }); + } + // Paper start - async tab completion + } else if (!completions.isEmpty()) { + com.mojang.brigadier.suggestion.SuggestionsBuilder builder = new com.mojang.brigadier.suggestion.SuggestionsBuilder(packetplayintabcomplete.c(), stringreader.getTotalLength()); + + builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + 1); + completions.forEach(builder::suggest); + player.playerConnection.sendPacket(new PacketPlayOutTabComplete(packetplayintabcomplete.b(), builder.buildFuture().join())); + } + // Paper end - async tab completion + + } + + public void a(PacketPlayInSetCommandBlock packetplayinsetcommandblock) { + PlayerConnectionUtils.ensureMainThread(packetplayinsetcommandblock, this, this.player.getWorldServer()); + if (!this.minecraftServer.getEnableCommandBlock()) { + this.player.sendMessage(new ChatMessage("advMode.notEnabled", new Object[0])); + } else if (!this.player.isCreativeAndOp()) { + this.player.sendMessage(new ChatMessage("advMode.notAllowed", new Object[0])); + } else { + CommandBlockListenerAbstract commandblocklistenerabstract = null; + TileEntityCommand tileentitycommand = null; + BlockPosition blockposition = packetplayinsetcommandblock.b(); + TileEntity tileentity = this.player.world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityCommand) { + tileentitycommand = (TileEntityCommand) tileentity; + commandblocklistenerabstract = tileentitycommand.getCommandBlock(); + } + + String s = packetplayinsetcommandblock.c(); + boolean flag = packetplayinsetcommandblock.d(); + + if (commandblocklistenerabstract != null) { + EnumDirection enumdirection = (EnumDirection) this.player.world.getType(blockposition).get(BlockCommand.a); + IBlockData iblockdata; + + switch (packetplayinsetcommandblock.g()) { + case SEQUENCE: + iblockdata = Blocks.CHAIN_COMMAND_BLOCK.getBlockData(); + this.player.world.setTypeAndData(blockposition, (IBlockData) ((IBlockData) iblockdata.set(BlockCommand.a, enumdirection)).set(BlockCommand.b, packetplayinsetcommandblock.e()), 2); + break; + case AUTO: + iblockdata = Blocks.REPEATING_COMMAND_BLOCK.getBlockData(); + this.player.world.setTypeAndData(blockposition, (IBlockData) ((IBlockData) iblockdata.set(BlockCommand.a, enumdirection)).set(BlockCommand.b, packetplayinsetcommandblock.e()), 2); + break; + case REDSTONE: + default: + iblockdata = Blocks.COMMAND_BLOCK.getBlockData(); + this.player.world.setTypeAndData(blockposition, (IBlockData) ((IBlockData) iblockdata.set(BlockCommand.a, enumdirection)).set(BlockCommand.b, packetplayinsetcommandblock.e()), 2); + } + + tileentity.z(); + this.player.world.setTileEntity(blockposition, tileentity); + commandblocklistenerabstract.setCommand(s); + commandblocklistenerabstract.a(flag); + if (!flag) { + commandblocklistenerabstract.c((IChatBaseComponent) null); + } + + tileentitycommand.b(packetplayinsetcommandblock.f()); + commandblocklistenerabstract.e(); + if (!UtilColor.b(s)) { + this.player.sendMessage(new ChatMessage("advMode.setCommand.success", new Object[] { s})); + } + } + + } + } + + public void a(PacketPlayInSetCommandMinecart packetplayinsetcommandminecart) { + PlayerConnectionUtils.ensureMainThread(packetplayinsetcommandminecart, this, this.player.getWorldServer()); + if (!this.minecraftServer.getEnableCommandBlock()) { + this.player.sendMessage(new ChatMessage("advMode.notEnabled", new Object[0])); + } else if (!this.player.isCreativeAndOp()) { + this.player.sendMessage(new ChatMessage("advMode.notAllowed", new Object[0])); + } else { + CommandBlockListenerAbstract commandblocklistenerabstract = packetplayinsetcommandminecart.a(this.player.world); + + if (commandblocklistenerabstract != null) { + commandblocklistenerabstract.setCommand(packetplayinsetcommandminecart.b()); + commandblocklistenerabstract.a(packetplayinsetcommandminecart.c()); + if (!packetplayinsetcommandminecart.c()) { + commandblocklistenerabstract.c((IChatBaseComponent) null); + } + + commandblocklistenerabstract.e(); + this.player.sendMessage(new ChatMessage("advMode.setCommand.success", new Object[] { packetplayinsetcommandminecart.b()})); + } + + } + } + + public void a(PacketPlayInPickItem packetplayinpickitem) { + PlayerConnectionUtils.ensureMainThread(packetplayinpickitem, this, this.player.getWorldServer()); + this.player.inventory.d(packetplayinpickitem.b()); + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-2, this.player.inventory.itemInHandIndex, this.player.inventory.getItem(this.player.inventory.itemInHandIndex))); + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-2, packetplayinpickitem.b(), this.player.inventory.getItem(packetplayinpickitem.b()))); + this.player.playerConnection.sendPacket(new PacketPlayOutHeldItemSlot(this.player.inventory.itemInHandIndex)); + } + + public void a(PacketPlayInItemName packetplayinitemname) { + PlayerConnectionUtils.ensureMainThread(packetplayinitemname, this, this.player.getWorldServer()); + if (this.player.activeContainer instanceof ContainerAnvil) { + ContainerAnvil containeranvil = (ContainerAnvil) this.player.activeContainer; + String s = SharedConstants.a(packetplayinitemname.b()); + + if (s.length() <= 35) { + containeranvil.a(s); + } + } + + } + + public void a(PacketPlayInBeacon packetplayinbeacon) { + PlayerConnectionUtils.ensureMainThread(packetplayinbeacon, this, this.player.getWorldServer()); + if (this.player.activeContainer instanceof ContainerBeacon) { + ContainerBeacon containerbeacon = (ContainerBeacon) this.player.activeContainer; + Slot slot = containerbeacon.getSlot(0); + + if (slot.hasItem()) { + slot.a(1); + IInventory iinventory = containerbeacon.d(); + + iinventory.setProperty(1, packetplayinbeacon.b()); + iinventory.setProperty(2, packetplayinbeacon.c()); + iinventory.update(); + } + } + + } + + public void a(PacketPlayInStruct packetplayinstruct) { + PlayerConnectionUtils.ensureMainThread(packetplayinstruct, this, this.player.getWorldServer()); + if (this.player.isCreativeAndOp()) { + BlockPosition blockposition = packetplayinstruct.b(); + IBlockData iblockdata = this.player.world.getType(blockposition); + TileEntity tileentity = this.player.world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityStructure) { + TileEntityStructure tileentitystructure = (TileEntityStructure) tileentity; + + tileentitystructure.setUsageMode(packetplayinstruct.d()); + tileentitystructure.setStructureName(packetplayinstruct.e()); + tileentitystructure.b(packetplayinstruct.f()); + tileentitystructure.c(packetplayinstruct.g()); + tileentitystructure.b(packetplayinstruct.h()); + tileentitystructure.b(packetplayinstruct.i()); + tileentitystructure.b(packetplayinstruct.j()); + tileentitystructure.a(packetplayinstruct.k()); + tileentitystructure.e(packetplayinstruct.l()); + tileentitystructure.f(packetplayinstruct.m()); + tileentitystructure.a(packetplayinstruct.n()); + tileentitystructure.a(packetplayinstruct.o()); + if (tileentitystructure.d()) { + String s = tileentitystructure.getStructureName(); + + if (packetplayinstruct.c() == TileEntityStructure.UpdateType.SAVE_AREA) { + if (tileentitystructure.q()) { + this.player.a((IChatBaseComponent) (new ChatMessage("structure_block.save_success", new Object[] { s})), false); + } else { + this.player.a((IChatBaseComponent) (new ChatMessage("structure_block.save_failure", new Object[] { s})), false); + } + } else if (packetplayinstruct.c() == TileEntityStructure.UpdateType.LOAD_AREA) { + if (!tileentitystructure.D()) { + this.player.a((IChatBaseComponent) (new ChatMessage("structure_block.load_not_found", new Object[] { s})), false); + } else if (tileentitystructure.r()) { + this.player.a((IChatBaseComponent) (new ChatMessage("structure_block.load_success", new Object[] { s})), false); + } else { + this.player.a((IChatBaseComponent) (new ChatMessage("structure_block.load_prepare", new Object[] { s})), false); + } + } else if (packetplayinstruct.c() == TileEntityStructure.UpdateType.SCAN_AREA) { + if (tileentitystructure.p()) { + this.player.a((IChatBaseComponent) (new ChatMessage("structure_block.size_success", new Object[] { s})), false); + } else { + this.player.a((IChatBaseComponent) (new ChatMessage("structure_block.size_failure", new Object[0])), false); + } + } + } else { + this.player.a((IChatBaseComponent) (new ChatMessage("structure_block.invalid_structure_name", new Object[] { packetplayinstruct.e()})), false); + } + + tileentitystructure.update(); + this.player.world.notify(blockposition, iblockdata, iblockdata, 3); + } + + } + } + + public void a(PacketPlayInTrSel packetplayintrsel) { + PlayerConnectionUtils.ensureMainThread(packetplayintrsel, this, this.player.getWorldServer()); + int i = packetplayintrsel.b(); + Container container = this.player.activeContainer; + + if (container instanceof ContainerMerchant) { + ((ContainerMerchant) container).d(i); + } + + } + + public void a(PacketPlayInBEdit packetplayinbedit) { + // Paper start + ItemStack testStack = packetplayinbedit.b(); + if (!server.isPrimaryThread() && !testStack.isEmpty() && testStack.getTag() != null) { + NBTTagList pageList = testStack.getTag().getList("pages", 8); + long byteTotal = 0; + int maxBookPageSize = com.destroystokyo.paper.PaperConfig.maxBookPageSize; + double multiplier = Math.max(0.3D, Math.min(1D, com.destroystokyo.paper.PaperConfig.maxBookTotalSizeMultiplier)); + long byteAllowed = maxBookPageSize; + for (int i = 0; i < pageList.size(); ++i) { + String testString = pageList.getString(i); + int byteLength = testString.getBytes(java.nio.charset.StandardCharsets.UTF_8).length; + byteTotal += byteLength; + int length = testString.length(); + int multibytes = 0; + if (byteLength != length) { + for (char c : testString.toCharArray()) { + if (c > 127) { + multibytes++; + } + } + } + byteAllowed += (maxBookPageSize * Math.min(1, Math.max(0.1D, (double) length / 255D))) * multiplier; + + if (multibytes > 1) { + // penalize MB + byteAllowed -= multibytes; + } + } + + if (byteTotal > byteAllowed) { + PlayerConnection.LOGGER.warn(this.player.getName() + " tried to send too large of a book. Book Size: " + byteTotal + " - Allowed: "+ byteAllowed + " - Pages: " + pageList.size()); + minecraftServer.postToMainThread(() -> this.disconnect("Book too large!")); + return; + } + } + // Paper end + // CraftBukkit start + PlayerConnectionUtils.ensureMainThread(packetplayinbedit, this, this.player.getWorldServer()); + if (this.lastBookTick + 20 > MinecraftServer.currentTick) { + this.disconnect("Book edited too quickly!"); + return; + } + this.lastBookTick = MinecraftServer.currentTick; + EnumItemSlot enumitemslot = packetplayinbedit.d() == EnumHand.MAIN_HAND ? EnumItemSlot.MAINHAND : EnumItemSlot.OFFHAND; + // CraftBukkit end + ItemStack itemstack = packetplayinbedit.b(); + + if (!itemstack.isEmpty()) { + if (ItemBookAndQuill.b(itemstack.getTag())) { + ItemStack itemstack1 = this.player.b(packetplayinbedit.d()); + + if (!itemstack1.isEmpty()) { + if (itemstack.getItem() == Items.WRITABLE_BOOK && itemstack1.getItem() == Items.WRITABLE_BOOK) { + if (packetplayinbedit.c()) { + ItemStack itemstack2 = new ItemStack(Items.WRITTEN_BOOK); + + itemstack2.a("author", (NBTBase) (new NBTTagString(this.player.getDisplayName().getString()))); + itemstack2.a("title", (NBTBase) (new NBTTagString(itemstack.getTag().getString("title")))); + NBTTagList nbttaglist = itemstack.getTag().getList("pages", 8); + + for (int i = 0; i < nbttaglist.size(); ++i) { + String s = nbttaglist.getString(i); + ChatComponentText chatcomponenttext = new ChatComponentText(s); + + s = IChatBaseComponent.ChatSerializer.a((IChatBaseComponent) chatcomponenttext); + nbttaglist.set(i, (NBTBase) (new NBTTagString(s))); + } + + itemstack2.a("pages", (NBTBase) nbttaglist); + // EnumItemSlot enumitemslot = packetplayinbedit.d() == EnumHand.MAIN_HAND ? EnumItemSlot.MAINHAND : EnumItemSlot.OFFHAND; // CraftBukkit - Moved up + + this.player.setSlot(enumitemslot, CraftEventFactory.handleEditBookEvent(player, enumitemslot, itemstack1, itemstack2)); // CraftBukkit + } else { + // Paper start - dont mutate players current item, set it from the event + ItemStack newBook = itemstack1.cloneItemStack(); + newBook.getOrCreateTagAndSet("pages", (NBTBase) itemstack.getTag().getList("pages", 8)); + this.player.setSlot(enumitemslot, CraftEventFactory.handleEditBookEvent(player, enumitemslot, itemstack1, newBook)); + // Paper end + } + player.getBukkitEntity().updateInventory(); // Paper - fix client desync when event is cancelled + } + + } + } + } + } + + public void a(PacketPlayInEntityNBTQuery packetplayinentitynbtquery) { + PlayerConnectionUtils.ensureMainThread(packetplayinentitynbtquery, this, this.player.getWorldServer()); + if (this.player.j(2)) { + Entity entity = this.player.getWorldServer().getEntity(packetplayinentitynbtquery.c()); + + if (entity != null) { + NBTTagCompound nbttagcompound = entity.save(new NBTTagCompound()); + + this.player.playerConnection.sendPacket(new PacketPlayOutNBTQuery(packetplayinentitynbtquery.b(), nbttagcompound)); + } + + } + } + + public void a(PacketPlayInTileNBTQuery packetplayintilenbtquery) { + PlayerConnectionUtils.ensureMainThread(packetplayintilenbtquery, this, this.player.getWorldServer()); + if (this.player.j(2)) { + TileEntity tileentity = this.player.getWorldServer().getTileEntity(packetplayintilenbtquery.c()); + NBTTagCompound nbttagcompound = tileentity != null ? tileentity.save(new NBTTagCompound()) : null; + + this.player.playerConnection.sendPacket(new PacketPlayOutNBTQuery(packetplayintilenbtquery.b(), nbttagcompound)); + } + } + + public void a(PacketPlayInFlying packetplayinflying) { + PlayerConnectionUtils.ensureMainThread(packetplayinflying, this, this.player.getWorldServer()); + if (b(packetplayinflying)) { + this.disconnect(new ChatMessage("multiplayer.disconnect.invalid_player_movement", new Object[0])); + } else { + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + + if (!this.player.viewingCredits && !this.player.isFrozen()) { // CraftBukkit + if (this.e == 0) { + this.syncPosition(); + } + + if (this.teleportPos != null) { + if (this.e - this.A > 20) { + this.A = this.e; + this.a(this.teleportPos.x, this.teleportPos.y, this.teleportPos.z, this.player.yaw, this.player.pitch); + } + this.allowedPlayerTicks = 20; // CraftBukkit + } else { + this.A = this.e; + if (this.player.isPassenger()) { + this.player.setLocation(this.player.locX, this.player.locY, this.player.locZ, packetplayinflying.a(this.player.yaw), packetplayinflying.b(this.player.pitch)); + this.minecraftServer.getPlayerList().updateChunks(this.player); + this.allowedPlayerTicks = 20; // CraftBukkit + } else { + // CraftBukkit - Make sure the move is valid but then reset it for plugins to modify + double prevX = player.locX; + double prevY = player.locY; + double prevZ = player.locZ; + float prevYaw = player.yaw; + float prevPitch = player.pitch; + // CraftBukkit end + double d0 = this.player.locX; + double d1 = this.player.locY; + double d2 = this.player.locZ; + double d3 = this.player.locY; + double d4 = packetplayinflying.a(this.player.locX);double toX = d4; // Paper - OBFHELPER + double d5 = packetplayinflying.b(this.player.locY); + double d6 = packetplayinflying.c(this.player.locZ);double toZ = d6; // Paper - OBFHELPER + float f = packetplayinflying.a(this.player.yaw); + float f1 = packetplayinflying.b(this.player.pitch); + double d7 = d4 - this.l; + double d8 = d5 - this.m; + double d9 = d6 - this.n; + double d10 = this.player.motX * this.player.motX + this.player.motY * this.player.motY + this.player.motZ * this.player.motZ; + double d11 = d7 * d7 + d8 * d8 + d9 * d9; + + if (this.player.isSleeping()) { + if (d11 > 1.0D) { + this.a(this.player.locX, this.player.locY, this.player.locZ, packetplayinflying.a(this.player.yaw), packetplayinflying.b(this.player.pitch)); + } + + } else { + ++this.receivedMovePackets; + int i = this.receivedMovePackets - this.processedMovePackets; + + // CraftBukkit start - handle custom speeds and skipped ticks + this.allowedPlayerTicks += (System.currentTimeMillis() / 50) - this.lastTick; + this.allowedPlayerTicks = Math.max(this.allowedPlayerTicks, 1); + this.lastTick = (int) (System.currentTimeMillis() / 50); + + if (i > Math.max(this.allowedPlayerTicks, 5)) { + PlayerConnection.LOGGER.debug("{} is sending move packets too frequently ({} packets since last tick)", this.player.getDisplayName().getString(), i); + i = 1; + } + + if (packetplayinflying.hasLook || d11 > 0) { + allowedPlayerTicks -= 1; + } else { + allowedPlayerTicks = 20; + } + double speed; + if (player.abilities.isFlying) { + speed = player.abilities.flySpeed * 20f; + } else { + speed = player.abilities.walkSpeed * 10f; + } + + // Paper start - Prevent moving into unloaded chunks + if (player.world.paperConfig.preventMovingIntoUnloadedChunks && (this.player.locX != toX || this.player.locZ != toZ) && !worldserver.isChunkLoaded((int) Math.floor(toX) >> 4, (int) Math.floor(toZ) >> 4, false)) { + this.internalTeleport(this.player.locX, this.player.locY, this.player.locZ, this.player.yaw, this.player.pitch, Collections.emptySet()); + return; + } + // Paper end + + if (!this.player.H() && (!this.player.getWorldServer().getGameRules().getBoolean("disableElytraMovementCheck") || !this.player.dc())) { + float f2 = this.player.dc() ? 300.0F : 100.0F; + + if (d11 - d10 > Math.max(f2, Math.pow((double) (org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed), 2)) && (!this.minecraftServer.H() || !this.minecraftServer.G().equals(this.player.getProfile().getName()))) { + // CraftBukkit end + PlayerConnection.LOGGER.warn("{} moved too quickly! {},{},{}", this.player.getDisplayName().getString(), d7, d8, d9); + this.a(this.player.locX, this.player.locY, this.player.locZ, this.player.yaw, this.player.pitch); + return; + } + } + + boolean flag = worldserver.getCubes(this.player, this.player.getBoundingBox().shrink(0.0625D)); + + d7 = d4 - this.o; + d8 = d5 - this.p; + d9 = d6 - this.q; + if (this.player.onGround && !packetplayinflying.b() && d8 > 0.0D) { + // Paper start - Add player jump event + Player player = this.getPlayer(); + Location from = new Location(player.getWorld(), lastPosX, lastPosY, lastPosZ, lastYaw, lastPitch); // Get the Players previous Event location. + Location to = player.getLocation().clone(); // Start off the To location as the Players current location. + + // If the packet contains movement information then we update the To location with the correct XYZ. + if (packetplayinflying.hasPos) { + to.setX(packetplayinflying.x); + to.setY(packetplayinflying.y); + to.setZ(packetplayinflying.z); + } + + // If the packet contains look information then we update the To location with the correct Yaw & Pitch. + if (packetplayinflying.hasLook) { + to.setYaw(packetplayinflying.yaw); + to.setPitch(packetplayinflying.pitch); + } + + PlayerJumpEvent event = new PlayerJumpEvent(player, from, to); + + if (event.callEvent()) { + this.player.jump(); + } else { + from = event.getFrom(); + this.internalTeleport(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch(), Collections.emptySet()); + return; + } + // Paper end + } + + this.player.move(EnumMoveType.PLAYER, d7, d8, d9); + this.player.onGround = packetplayinflying.b(); + double d12 = d8; + + d7 = d4 - this.player.locX; + d8 = d5 - this.player.locY; + if (d8 > -0.5D || d8 < 0.5D) { + d8 = 0.0D; + } + + d9 = d6 - this.player.locZ; + d11 = d7 * d7 + d8 * d8 + d9 * d9; + boolean flag1 = false; + + if (!this.player.H() && d11 > org.spigotmc.SpigotConfig.movedWronglyThreshold && !this.player.isSleeping() && !this.player.playerInteractManager.isCreative() && this.player.playerInteractManager.getGameMode() != EnumGamemode.SPECTATOR) { // Spigot + flag1 = true; + PlayerConnection.LOGGER.warn("{} moved wrongly!", this.player.getDisplayName().getString()); + } + + this.player.setLocation(d4, d5, d6, f, f1); + this.player.checkMovement(this.player.locX - d0, this.player.locY - d1, this.player.locZ - d2); + if (!this.player.noclip && !this.player.isSleeping()) { + boolean flag2 = worldserver.getCubes(this.player, this.player.getBoundingBox().shrink(0.0625D)); + + if (flag && (flag1 || !flag2)) { + this.a(d0, d1, d2, f, f1); + return; + } + } + + // CraftBukkit start - fire PlayerMoveEvent + // Rest to old location first + this.player.setLocation(prevX, prevY, prevZ, prevYaw, prevPitch); + + Player player = this.getPlayer(); + Location from = new Location(player.getWorld(), lastPosX, lastPosY, lastPosZ, lastYaw, lastPitch); // Get the Players previous Event location. + Location to = player.getLocation().clone(); // Start off the To location as the Players current location. + + // If the packet contains movement information then we update the To location with the correct XYZ. + if (packetplayinflying.hasPos) { + to.setX(packetplayinflying.x); + to.setY(packetplayinflying.y); + to.setZ(packetplayinflying.z); + } + + // If the packet contains look information then we update the To location with the correct Yaw & Pitch. + if (packetplayinflying.hasLook) { + to.setYaw(packetplayinflying.yaw); + to.setPitch(packetplayinflying.pitch); + } + + // Prevent 40 event-calls for less than a single pixel of movement >.> + double delta = Math.pow(this.lastPosX - to.getX(), 2) + Math.pow(this.lastPosY - to.getY(), 2) + Math.pow(this.lastPosZ - to.getZ(), 2); + float deltaAngle = Math.abs(this.lastYaw - to.getYaw()) + Math.abs(this.lastPitch - to.getPitch()); + + if ((delta > 1f / 256 || deltaAngle > 10f) && !this.player.isFrozen()) { + this.lastPosX = to.getX(); + this.lastPosY = to.getY(); + this.lastPosZ = to.getZ(); + this.lastYaw = to.getYaw(); + this.lastPitch = to.getPitch(); + + // Skip the first time we do this + if (from.getX() != Double.MAX_VALUE) { + Location oldTo = to.clone(); + PlayerMoveEvent event = new PlayerMoveEvent(player, from, to); + this.server.getPluginManager().callEvent(event); + + // If the event is cancelled we move the player back to their old location. + if (event.isCancelled()) { + teleport(from); + return; + } + + // If a Plugin has changed the To destination then we teleport the Player + // there to avoid any 'Moved wrongly' or 'Moved too quickly' errors. + // We only do this if the Event was not cancelled. + if (!oldTo.equals(event.getTo()) && !event.isCancelled()) { + this.player.getBukkitEntity().teleport(event.getTo(), PlayerTeleportEvent.TeleportCause.PLUGIN); + return; + } + + // Check to see if the Players Location has some how changed during the call of the event. + // This can happen due to a plugin teleporting the player instead of using .setTo() + if (!from.equals(this.getPlayer().getLocation()) && this.justTeleported) { + this.justTeleported = false; + return; + } + } + } + this.player.setLocation(d4, d5, d6, f, f1); // Copied from above + // CraftBukkit end + + this.B = d12 >= -0.03125D; + this.B &= !this.minecraftServer.getAllowFlight() && !this.player.abilities.canFly; + this.B &= !this.player.hasEffect(MobEffects.LEVITATION) && !this.player.dc() && !worldserver.a(this.player.getBoundingBox().g(0.0625D).b(0.0D, -0.55D, 0.0D)); + this.player.onGround = packetplayinflying.b(); + this.minecraftServer.getPlayerList().updateChunks(this.player); + this.player.a(this.player.locY - d3, packetplayinflying.b()); + this.o = this.player.locX; + this.p = this.player.locY; + this.q = this.player.locZ; + } + } + } + } + } + } + + public void a(double d0, double d1, double d2, float f, float f1) { + this.a(d0, d1, d2, f, f1, Collections.emptySet()); + } + + // CraftBukkit start - Delegate to teleport(Location) + public void a(double d0, double d1, double d2, float f, float f1, PlayerTeleportEvent.TeleportCause cause) { + this.a(d0, d1, d2, f, f1, Collections.emptySet(), cause); + } + + public void a(double d0, double d1, double d2, float f, float f1, Set set) { + this.a(d0, d1, d2, f, f1, set, PlayerTeleportEvent.TeleportCause.UNKNOWN); + } + + public void a(double d0, double d1, double d2, float f, float f1, Set set, PlayerTeleportEvent.TeleportCause cause) { + Player player = this.getPlayer(); + Location from = player.getLocation(); + + double x = d0; + double y = d1; + double z = d2; + float yaw = f; + float pitch = f1; + if (set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.X)) { + x += from.getX(); + } + if (set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.Y)) { + y += from.getY(); + } + if (set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.Z)) { + z += from.getZ(); + } + if (set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.Y_ROT)) { + yaw += from.getYaw(); + } + if (set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.X_ROT)) { + pitch += from.getPitch(); + } + + + Location to = new Location(this.getPlayer().getWorld(), x, y, z, yaw, pitch); + PlayerTeleportEvent event = new PlayerTeleportEvent(player, from.clone(), to.clone(), cause); + this.server.getPluginManager().callEvent(event); + + if (event.isCancelled() || !to.equals(event.getTo())) { + set.clear(); // Can't relative teleport + to = event.isCancelled() ? event.getFrom() : event.getTo(); + d0 = to.getX(); + d1 = to.getY(); + d2 = to.getZ(); + f = to.getYaw(); + f1 = to.getPitch(); + } + + this.internalTeleport(d0, d1, d2, f, f1, set); + } + + public void teleport(Location dest) { + internalTeleport(dest.getX(), dest.getY(), dest.getZ(), dest.getYaw(), dest.getPitch(), Collections.emptySet()); + } + + private void internalTeleport(double d0, double d1, double d2, float f, float f1, Set set) { + // CraftBukkit start + if (Float.isNaN(f)) { + f = 0; + } + if (Float.isNaN(f1)) { + f1 = 0; + } + + this.justTeleported = true; + // CraftBukkit end + double d3 = set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.X) ? this.player.locX : 0.0D; + double d4 = set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.Y) ? this.player.locY : 0.0D; + double d5 = set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.Z) ? this.player.locZ : 0.0D; + float f2 = set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.Y_ROT) ? this.player.yaw : 0.0F; + float f3 = set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.X_ROT) ? this.player.pitch : 0.0F; + + this.teleportPos = new Vec3D(d0, d1, d2); + if (++this.teleportAwait == Integer.MAX_VALUE) { + this.teleportAwait = 0; + } + + // CraftBukkit start - update last location + this.lastPosX = this.teleportPos.x; + this.lastPosY = this.teleportPos.y; + this.lastPosZ = this.teleportPos.z; + this.lastYaw = f; + this.lastPitch = f1; + // CraftBukkit end + + this.A = this.e; + this.player.setLocation(d0, d1, d2, f, f1); + this.player.playerConnection.sendPacket(new PacketPlayOutPosition(d0 - d3, d1 - d4, d2 - d5, f - f2, f1 - f3, set, this.teleportAwait)); + } + + public void a(PacketPlayInBlockDig packetplayinblockdig) { + PlayerConnectionUtils.ensureMainThread(packetplayinblockdig, this, this.player.getWorldServer()); + if (this.player.isFrozen()) return; // CraftBukkit + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + BlockPosition blockposition = packetplayinblockdig.b(); + + this.player.resetIdleTimer(); + switch (packetplayinblockdig.d()) { + case SWAP_HELD_ITEMS: + if (!this.player.isSpectator()) { + ItemStack itemstack = this.player.b(EnumHand.OFF_HAND); + + // CraftBukkit start - inspiration taken from DispenserRegistry (See SpigotCraft#394) + CraftItemStack mainHand = CraftItemStack.asCraftMirror(itemstack); + CraftItemStack offHand = CraftItemStack.asCraftMirror(this.player.b(EnumHand.MAIN_HAND)); + PlayerSwapHandItemsEvent swapItemsEvent = new PlayerSwapHandItemsEvent(getPlayer(), mainHand.clone(), offHand.clone()); + this.server.getPluginManager().callEvent(swapItemsEvent); + if (swapItemsEvent.isCancelled()) { + return; + } + if (swapItemsEvent.getOffHandItem().equals(offHand)) { + this.player.a(EnumHand.OFF_HAND, this.player.b(EnumHand.MAIN_HAND)); + } else { + this.player.a(EnumHand.OFF_HAND, CraftItemStack.asNMSCopy(swapItemsEvent.getOffHandItem())); + } + if (swapItemsEvent.getMainHandItem().equals(mainHand)) { + this.player.a(EnumHand.MAIN_HAND, itemstack); + } else { + this.player.a(EnumHand.MAIN_HAND, CraftItemStack.asNMSCopy(swapItemsEvent.getMainHandItem())); + } + // CraftBukkit end + } + + return; + case DROP_ITEM: + if (!this.player.isSpectator()) { + // limit how quickly items can be dropped + // If the ticks aren't the same then the count starts from 0 and we update the lastDropTick. + if (this.lastDropTick != MinecraftServer.currentTick) { + this.dropCount = 0; + this.lastDropTick = MinecraftServer.currentTick; + } else { + // Else we increment the drop count and check the amount. + this.dropCount++; + if (this.dropCount >= 20) { + LOGGER.warn(this.player.getName() + " dropped their items too quickly!"); + this.disconnect("You dropped your items too quickly (Hacking?)"); + return; + } + } + // CraftBukkit end + this.player.a(false); + } + + return; + case DROP_ALL_ITEMS: + if (!this.player.isSpectator()) { + this.player.a(true); + } + + return; + case RELEASE_USE_ITEM: + this.player.clearActiveItem(); + return; + case START_DESTROY_BLOCK: + case ABORT_DESTROY_BLOCK: + case STOP_DESTROY_BLOCK: + // Paper start - Don't allow digging in unloaded chunks + if (!worldserver.isChunkLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4, true)) { + return; + } + // Paper end - Don't allow digging in unloaded chunks + double d0 = this.player.locX - ((double) blockposition.getX() + 0.5D); + double d1 = this.player.locY - ((double) blockposition.getY() + 0.5D) + 1.5D; + double d2 = this.player.locZ - ((double) blockposition.getZ() + 0.5D); + double d3 = d0 * d0 + d1 * d1 + d2 * d2; + + if (d3 > 36.0D) { + if (worldserver.isChunkLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4, true)) // Paper - Fix block break desync - Don't send for unloaded chunks + this.sendPacket(new PacketPlayOutBlockChange(worldserver, blockposition)); // Paper - Fix block break desync + return; + } else if (blockposition.getY() >= this.minecraftServer.getMaxBuildHeight()) { + return; + } else { + if (packetplayinblockdig.d() == PacketPlayInBlockDig.EnumPlayerDigType.START_DESTROY_BLOCK) { + if (!this.minecraftServer.a(worldserver, blockposition, this.player) && worldserver.getWorldBorder().a(blockposition)) { + this.player.playerInteractManager.a(blockposition, packetplayinblockdig.c()); + } else { + // CraftBukkit start - fire PlayerInteractEvent + CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, blockposition, packetplayinblockdig.c(), this.player.inventory.getItemInHand(), EnumHand.MAIN_HAND); + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(worldserver, blockposition)); + // Update any tile entity data for this block + TileEntity tileentity = worldserver.getTileEntity(blockposition); + if (tileentity != null) { + this.player.playerConnection.sendPacket(tileentity.getUpdatePacket()); + } + // CraftBukkit end + } + } else { + if (packetplayinblockdig.d() == PacketPlayInBlockDig.EnumPlayerDigType.STOP_DESTROY_BLOCK) { + this.player.playerInteractManager.a(blockposition); + } else if (packetplayinblockdig.d() == PacketPlayInBlockDig.EnumPlayerDigType.ABORT_DESTROY_BLOCK) { + this.player.playerInteractManager.e(); + } + + if (!worldserver.getType(blockposition).isAir()) { + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(worldserver, blockposition)); + } + } + + return; + } + default: + throw new IllegalArgumentException("Invalid player action"); + } + // CraftBukkit end + } + + // Spigot start - limit place/interactions + private int limitedPackets; + private long lastLimitedPacket = -1; + private static final int THRESHOLD = com.destroystokyo.paper.PaperConfig.packetInSpamThreshold; // Paper - Configurable threshold + + private boolean checkLimit(long timestamp) { + if (lastLimitedPacket != -1 && timestamp - lastLimitedPacket < THRESHOLD && limitedPackets++ >= 8) { // Paper - Use threshold, raise packet limit to 8 + return false; + } + + if (lastLimitedPacket == -1 || timestamp - lastLimitedPacket >= THRESHOLD) { // Paper + lastLimitedPacket = timestamp; + limitedPackets = 0; + return true; + } + + return true; + } + // Spigot end + + public void a(PacketPlayInUseItem packetplayinuseitem) { + PlayerConnectionUtils.ensureMainThread(packetplayinuseitem, this, this.player.getWorldServer()); + if (this.player.isFrozen()) return; // CraftBukkit + if (!checkLimit(packetplayinuseitem.timestamp)) return; // Spigot - check limit + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + EnumHand enumhand = packetplayinuseitem.d(); + ItemStack itemstack = this.player.b(enumhand); + BlockPosition blockposition = packetplayinuseitem.b(); + EnumDirection enumdirection = packetplayinuseitem.c(); + IBlockData clickedBlock = worldserver.getType(blockposition); // Spigot + + this.player.resetIdleTimer(); + if (blockposition.getY() >= this.minecraftServer.getMaxBuildHeight() - 1 && ((enumdirection == EnumDirection.UP && !(clickedBlock.getBlock() instanceof BlockStepAbstract && clickedBlock.get(BlockStepAbstract.a) == BlockPropertySlabType.BOTTOM)) || blockposition.getY() >= this.minecraftServer.getMaxBuildHeight())) { // Spigot + IChatBaseComponent ichatbasecomponent = (new ChatMessage("build.tooHigh", new Object[] { this.minecraftServer.getMaxBuildHeight()})).a(EnumChatFormat.RED); + + this.player.playerConnection.sendPacket(new PacketPlayOutChat(ichatbasecomponent, ChatMessageType.GAME_INFO)); + } else if (this.teleportPos == null && this.player.d((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D) < 64.0D && !this.minecraftServer.a(worldserver, blockposition, this.player) && worldserver.getWorldBorder().a(blockposition)) { + // CraftBukkit start - Check if we can actually do something over this large a distance + Location eyeLoc = this.getPlayer().getEyeLocation(); + double reachDistance = NumberConversions.square(eyeLoc.getX() - blockposition.getX()) + NumberConversions.square(eyeLoc.getY() - blockposition.getY()) + NumberConversions.square(eyeLoc.getZ() - blockposition.getZ()); + if (reachDistance > (this.getPlayer().getGameMode() == org.bukkit.GameMode.CREATIVE ? CREATIVE_PLACE_DISTANCE_SQUARED : SURVIVAL_PLACE_DISTANCE_SQUARED)) { + return; + } + // CraftBukkit end + this.player.playerInteractManager.a(this.player, worldserver, itemstack, enumhand, blockposition, enumdirection, packetplayinuseitem.e(), packetplayinuseitem.f(), packetplayinuseitem.g()); + } + + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(worldserver, blockposition)); + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(worldserver, blockposition.shift(enumdirection))); + } + + public void a(PacketPlayInBlockPlace packetplayinblockplace) { + PlayerConnectionUtils.ensureMainThread(packetplayinblockplace, this, this.player.getWorldServer()); + if (this.player.isFrozen()) return; // CraftBukkit + if (!checkLimit(packetplayinblockplace.timestamp)) return; // Spigot - check limit + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + EnumHand enumhand = packetplayinblockplace.b(); + ItemStack itemstack = this.player.b(enumhand); + + this.player.resetIdleTimer(); + if (!itemstack.isEmpty()) { + // CraftBukkit start + // Raytrace to look for 'rogue armswings' + float f1 = this.player.pitch; + float f2 = this.player.yaw; + double d0 = this.player.locX; + double d1 = this.player.locY + (double) this.player.getHeadHeight(); + double d2 = this.player.locZ; + Vec3D vec3d = new Vec3D(d0, d1, d2); + + float f3 = MathHelper.cos(-f2 * 0.017453292F - 3.1415927F); + float f4 = MathHelper.sin(-f2 * 0.017453292F - 3.1415927F); + float f5 = -MathHelper.cos(-f1 * 0.017453292F); + float f6 = MathHelper.sin(-f1 * 0.017453292F); + float f7 = f4 * f5; + float f8 = f3 * f5; + double d3 = player.playerInteractManager.getGameMode()== EnumGamemode.CREATIVE ? 5.0D : 4.5D; + Vec3D vec3d1 = vec3d.add((double) f7 * d3, (double) f6 * d3, (double) f8 * d3); + MovingObjectPosition movingobjectposition = this.player.world.rayTrace(vec3d, vec3d1); + + boolean cancelled; + if (movingobjectposition == null || movingobjectposition.type != MovingObjectPosition.EnumMovingObjectType.BLOCK) { + org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.RIGHT_CLICK_AIR, itemstack, enumhand); + cancelled = event.useItemInHand() == Event.Result.DENY; + } else { + if (player.playerInteractManager.firedInteract) { + player.playerInteractManager.firedInteract = false; + cancelled = player.playerInteractManager.interactResult; + } else { + org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(player, Action.RIGHT_CLICK_BLOCK, movingobjectposition.getBlockPosition(), movingobjectposition.direction, itemstack, true, enumhand); + cancelled = event.useItemInHand() == Event.Result.DENY; + } + } + + if (cancelled) { + this.player.getBukkitEntity().updateInventory(); // SPIGOT-2524 + } else { + this.player.playerInteractManager.a(this.player, worldserver, itemstack, enumhand); + } + // CraftBukkit end + } + } + + public void a(PacketPlayInSpectate packetplayinspectate) { + PlayerConnectionUtils.ensureMainThread(packetplayinspectate, this, this.player.getWorldServer()); + if (this.player.isSpectator()) { + Entity entity = null; + Iterator iterator = this.minecraftServer.getWorlds().iterator(); + + while (iterator.hasNext()) { + WorldServer worldserver = (WorldServer) iterator.next(); + + entity = packetplayinspectate.a(worldserver); + if (entity != null) { + break; + } + } + + if (entity != null) { + this.player.a((WorldServer) entity.world, entity.locX, entity.locY, entity.locZ, entity.yaw, entity.pitch, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.SPECTATE); // CraftBukkit + } + } + + } + + // CraftBukkit start + public void a(PacketPlayInResourcePackStatus packetplayinresourcepackstatus) { + PlayerConnectionUtils.ensureMainThread(packetplayinresourcepackstatus, this, this.player.getWorldServer()); + // Paper start + PlayerResourcePackStatusEvent.Status packStatus = PlayerResourcePackStatusEvent.Status.values()[packetplayinresourcepackstatus.status.ordinal()]; + player.getBukkitEntity().setResourcePackStatus(packStatus); + this.server.getPluginManager().callEvent(new PlayerResourcePackStatusEvent(getPlayer(), packStatus)); + // Paper end + } + // CraftBukkit end + + public void a(PacketPlayInBoatMove packetplayinboatmove) { + PlayerConnectionUtils.ensureMainThread(packetplayinboatmove, this, this.player.getWorldServer()); + Entity entity = this.player.getVehicle(); + + if (entity instanceof EntityBoat) { + ((EntityBoat) entity).a(packetplayinboatmove.b(), packetplayinboatmove.c()); + } + + } + + public void a(IChatBaseComponent ichatbasecomponent) { + // CraftBukkit start - Rarely it would send a disconnect line twice + if (this.processedDisconnect) { + return; + } else { + this.processedDisconnect = true; + } + // CraftBukkit end + PlayerConnection.LOGGER.info("{} lost connection: {}", this.player.getDisplayName().getString(), ichatbasecomponent.getString()); + // CraftBukkit start - Replace vanilla quit message handling with our own. + /* + this.minecraftServer.at(); + this.minecraftServer.getPlayerList().sendMessage((new ChatMessage("multiplayer.player.left", new Object[] { this.player.getScoreboardDisplayName()})).a(EnumChatFormat.YELLOW)); + */ + + this.player.n(); + String quitMessage = this.minecraftServer.getPlayerList().disconnect(this.player); + if ((quitMessage != null) && (quitMessage.length() > 0)) { + this.minecraftServer.getPlayerList().sendMessage(CraftChatMessage.fromString(quitMessage)); + } + // CraftBukkit end + if (this.minecraftServer.H() && this.player.getDisplayName().getString().equals(this.minecraftServer.G())) { + PlayerConnection.LOGGER.info("Stopping singleplayer server as player logged out"); + this.minecraftServer.safeShutdown(); + } + + } + + public void sendPacket(Packet packet) { + this.a(packet, (GenericFutureListener) null); + } + + public void a(Packet packet, @Nullable GenericFutureListener> genericfuturelistener) { + if (packet instanceof PacketPlayOutChat) { + PacketPlayOutChat packetplayoutchat = (PacketPlayOutChat) packet; + EntityHuman.EnumChatVisibility entityhuman_enumchatvisibility = this.player.getChatFlags(); + + if (entityhuman_enumchatvisibility == EntityHuman.EnumChatVisibility.HIDDEN && packetplayoutchat.d() != ChatMessageType.GAME_INFO) { + return; + } + + if (entityhuman_enumchatvisibility == EntityHuman.EnumChatVisibility.SYSTEM && !packetplayoutchat.c()) { + return; + } + } + + // CraftBukkit start + if (packet == null || this.processedDisconnect) { // Spigot + return; + } else if (packet instanceof PacketPlayOutSpawnPosition) { + PacketPlayOutSpawnPosition packet6 = (PacketPlayOutSpawnPosition) packet; + this.player.compassTarget = new Location(this.getPlayer().getWorld(), packet6.position.getX(), packet6.position.getY(), packet6.position.getZ()); + } + // CraftBukkit end + + try { + this.networkManager.sendPacket(packet, genericfuturelistener); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Sending packet"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Packet being sent"); + + crashreportsystemdetails.a("Packet class", () -> { + return packet.getClass().getCanonicalName(); + }); + throw new ReportedException(crashreport); + } + } + + public void a(PacketPlayInHeldItemSlot packetplayinhelditemslot) { + PlayerConnectionUtils.ensureMainThread(packetplayinhelditemslot, this, this.player.getWorldServer()); + if (this.player.isFrozen()) return; // CraftBukkit + if (packetplayinhelditemslot.b() >= 0 && packetplayinhelditemslot.b() < PlayerInventory.getHotbarSize()) { + PlayerItemHeldEvent event = new PlayerItemHeldEvent(this.getPlayer(), this.player.inventory.itemInHandIndex, packetplayinhelditemslot.b()); + this.server.getPluginManager().callEvent(event); + if (event.isCancelled()) { + this.sendPacket(new PacketPlayOutHeldItemSlot(this.player.inventory.itemInHandIndex)); + this.player.resetIdleTimer(); + return; + } + // CraftBukkit end + this.player.inventory.itemInHandIndex = packetplayinhelditemslot.b(); + this.player.resetIdleTimer(); + } else { + PlayerConnection.LOGGER.warn("{} tried to set an invalid carried item", this.player.getDisplayName().getString()); + this.disconnect("Invalid hotbar selection (Hacking?)"); // CraftBukkit + } + } + + public void a(PacketPlayInChat packetplayinchat) { + // CraftBukkit start - async chat + // SPIGOT-3638 + if (this.minecraftServer.isStopped()) { + return; + } + + boolean isSync = packetplayinchat.b().startsWith("/"); + if (packetplayinchat.b().startsWith("/")) { + PlayerConnectionUtils.ensureMainThread(packetplayinchat, this, this.player.getWorldServer()); + } + // CraftBukkit end + if (this.player.dead || this.player.getChatFlags() == EntityHuman.EnumChatVisibility.HIDDEN) { // CraftBukkit - dead men tell no tales + this.sendPacket(new PacketPlayOutChat((new ChatMessage("chat.cannotSend", new Object[0])).a(EnumChatFormat.RED))); + } else { + this.player.resetIdleTimer(); + String s = packetplayinchat.b(); + + s = StringUtils.normalizeSpace(s); + + for (int i = 0; i < s.length(); ++i) { + if (!SharedConstants.isAllowedChatCharacter(s.charAt(i))) { + // CraftBukkit start - threadsafety + if (!isSync) { + Waitable waitable = new Waitable() { + @Override + protected Object evaluate() { + PlayerConnection.this.disconnect(new ChatMessage("multiplayer.disconnect.illegal_characters", new Object[0])); + return null; + } + }; + + this.minecraftServer.processQueue.add(waitable); + + try { + waitable.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } else { + this.disconnect(new ChatMessage("multiplayer.disconnect.illegal_characters", new Object[0])); + } + // CraftBukkit end + return; + } + } + + // CraftBukkit start + if (isSync) { + try { + this.minecraftServer.server.playerCommandState = true; + this.handleCommand(s); + } finally { + this.minecraftServer.server.playerCommandState = false; + } + } else if (s.isEmpty()) { + LOGGER.warn(this.player.getName() + " tried to send an empty message"); + } else if (getPlayer().isConversing()) { + final String conversationInput = s; + this.minecraftServer.processQueue.add(new Runnable() { + @Override + public void run() { + getPlayer().acceptConversationInput(conversationInput); + } + }); + } else if (this.player.getChatFlags() == EntityHuman.EnumChatVisibility.SYSTEM) { // Re-add "Command Only" flag check + ChatMessage chatmessage = new ChatMessage("chat.cannotSend", new Object[0]); + + chatmessage.getChatModifier().setColor(EnumChatFormat.RED); + this.sendPacket(new PacketPlayOutChat(chatmessage)); + } else if (true) { + this.chat(s, true); + // CraftBukkit end - the below is for reference. :) + } else { + ChatMessage chatmessage = new ChatMessage("chat.type.text", new Object[] { this.player.getScoreboardDisplayName(), s}); + + this.minecraftServer.getPlayerList().sendMessage(chatmessage, false); + } + + // Spigot start - spam exclusions + boolean counted = true; + for ( String exclude : org.spigotmc.SpigotConfig.spamExclusions ) + { + if ( exclude != null && s.startsWith( exclude ) ) + { + counted = false; + break; + } + } + // Spigot end + // CraftBukkit start - replaced with thread safe throttle + // this.chatThrottle += 20; + if (counted && chatSpamField.addAndGet(this, 20) > 200 && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) { // Spigot + if (!isSync) { + Waitable waitable = new Waitable() { + @Override + protected Object evaluate() { + PlayerConnection.this.disconnect(new ChatMessage("disconnect.spam", new Object[0])); + return null; + } + }; + + this.minecraftServer.processQueue.add(waitable); + + try { + waitable.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } else { + this.disconnect(new ChatMessage("disconnect.spam", new Object[0])); + } + // CraftBukkit end + } + + } + } + + // CraftBukkit start - add method + public void chat(String s, boolean async) { + if (s.isEmpty() || this.player.getChatFlags() == EntityHuman.EnumChatVisibility.HIDDEN) { + return; + } + + if (!async && s.startsWith("/")) { + // Paper Start + if (!org.spigotmc.AsyncCatcher.shuttingDown && !org.bukkit.Bukkit.isPrimaryThread()) { + final String fCommandLine = s; + MinecraftServer.LOGGER.log(org.apache.logging.log4j.Level.ERROR, "Command Dispatched Async: " + fCommandLine); + MinecraftServer.LOGGER.log(org.apache.logging.log4j.Level.ERROR, "Please notify author of plugin causing this execution to fix this bug! see: http://bit.ly/1oSiM6C", new Throwable()); + Waitable wait = new Waitable() { + @Override + protected Object evaluate() { + chat(fCommandLine, false); + return null; + } + }; + minecraftServer.processQueue.add(wait); + try { + wait.get(); + return; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // This is proper habit for java. If we aren't handling it, pass it on! + } catch (Exception e) { + throw new RuntimeException("Exception processing chat command", e.getCause()); + } + } + // Paper End + this.handleCommand(s); + } else if (this.player.getChatFlags() == EntityHuman.EnumChatVisibility.SYSTEM) { + // Do nothing, this is coming from a plugin + } else { + Player player = this.getPlayer(); + AsyncPlayerChatEvent event = new AsyncPlayerChatEvent(async, player, s, new LazyPlayerSet(minecraftServer)); + this.server.getPluginManager().callEvent(event); + + if (PlayerChatEvent.getHandlerList().getRegisteredListeners().length != 0) { + // Evil plugins still listening to deprecated event + final PlayerChatEvent queueEvent = new PlayerChatEvent(player, event.getMessage(), event.getFormat(), event.getRecipients()); + queueEvent.setCancelled(event.isCancelled()); + Waitable waitable = new Waitable() { + @Override + protected Object evaluate() { + org.bukkit.Bukkit.getPluginManager().callEvent(queueEvent); + + if (queueEvent.isCancelled()) { + return null; + } + + String message = String.format(queueEvent.getFormat(), queueEvent.getPlayer().getDisplayName(), queueEvent.getMessage()); + PlayerConnection.this.minecraftServer.console.sendMessage(message); + if (((LazyPlayerSet) queueEvent.getRecipients()).isLazy()) { + for (Object player : PlayerConnection.this.minecraftServer.getPlayerList().players) { + ((EntityPlayer) player).sendMessage(CraftChatMessage.fromString(message)); + } + } else { + for (Player player : queueEvent.getRecipients()) { + player.sendMessage(message); + } + } + return null; + }}; + if (async) { + minecraftServer.processQueue.add(waitable); + } else { + waitable.run(); + } + try { + waitable.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // This is proper habit for java. If we aren't handling it, pass it on! + } catch (ExecutionException e) { + throw new RuntimeException("Exception processing chat event", e.getCause()); + } + } else { + if (event.isCancelled()) { + return; + } + + // Paper Start - (Meh) Support for vanilla world scoreboard name coloring + String displayName = event.getPlayer().getDisplayName(); + if (this.player.getWorld().paperConfig.useVanillaScoreboardColoring) { + IChatBaseComponent nameFromTeam = ScoreboardTeam.a(this.player.getScoreboardTeam(),((CraftPlayer) player).getHandle().getDisplayName()); + // Explicitly add a RESET here, vanilla uses components for this now... + displayName = CraftChatMessage.fromComponent(nameFromTeam, EnumChatFormat.WHITE) + org.bukkit.ChatColor.RESET; + } + + s = String.format(event.getFormat(), displayName, event.getMessage()); + // Paper end + minecraftServer.console.sendMessage(s); + if (((LazyPlayerSet) event.getRecipients()).isLazy()) { + for (Object recipient : minecraftServer.getPlayerList().players) { + ((EntityPlayer) recipient).sendMessage(CraftChatMessage.fromString(s)); + } + } else { + for (Player recipient : event.getRecipients()) { + recipient.sendMessage(s); + } + } + } + } + } + // CraftBukkit end + + private void handleCommand(String s) { + MinecraftTimings.playerCommandTimer.startTiming(); // Paper + // CraftBukkit start - whole method + if ( org.spigotmc.SpigotConfig.logCommands ) // Spigot + this.LOGGER.info(this.player.getName() + " issued server command: " + s); + + CraftPlayer player = this.getPlayer(); + + PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(player, s, new LazyPlayerSet(minecraftServer)); + this.server.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + MinecraftTimings.playerCommandTimer.stopTiming(); // Paper + return; + } + + try { + if (this.server.dispatchCommand(event.getPlayer(), event.getMessage().substring(1))) { + return; + } + } catch (org.bukkit.command.CommandException ex) { + player.sendMessage(org.bukkit.ChatColor.RED + "An internal error occurred while attempting to perform this command"); + java.util.logging.Logger.getLogger(PlayerConnection.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + return; + } finally { + MinecraftTimings.playerCommandTimer.stopTiming(); // Paper + } + // this.minecraftServer.getCommandDispatcher().a(this.player.getCommandListener(), s); + // CraftBukkit end + } + + public void a(PacketPlayInArmAnimation packetplayinarmanimation) { + PlayerConnectionUtils.ensureMainThread(packetplayinarmanimation, this, this.player.getWorldServer()); + if (this.player.isFrozen()) return; // CraftBukkit + this.player.resetIdleTimer(); + // CraftBukkit start - Raytrace to look for 'rogue armswings' + float f1 = this.player.pitch; + float f2 = this.player.yaw; + double d0 = this.player.locX; + double d1 = this.player.locY + (double) this.player.getHeadHeight(); + double d2 = this.player.locZ; + Vec3D vec3d = new Vec3D(d0, d1, d2); + + float f3 = MathHelper.cos(-f2 * 0.017453292F - 3.1415927F); + float f4 = MathHelper.sin(-f2 * 0.017453292F - 3.1415927F); + float f5 = -MathHelper.cos(-f1 * 0.017453292F); + float f6 = MathHelper.sin(-f1 * 0.017453292F); + float f7 = f4 * f5; + float f8 = f3 * f5; + double d3 = player.playerInteractManager.getGameMode()== EnumGamemode.CREATIVE ? 5.0D : 4.5D; + Vec3D vec3d1 = vec3d.add((double) f7 * d3, (double) f6 * d3, (double) f8 * d3); + MovingObjectPosition movingobjectposition = this.player.world.rayTrace(vec3d, vec3d1); + + if (movingobjectposition == null || movingobjectposition.type != MovingObjectPosition.EnumMovingObjectType.BLOCK) { + CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_AIR, this.player.inventory.getItemInHand(), EnumHand.MAIN_HAND); + } + + // Arm swing animation + PlayerAnimationEvent event = new PlayerAnimationEvent(this.getPlayer()); + this.server.getPluginManager().callEvent(event); + + if (event.isCancelled()) return; + // CraftBukkit end + this.player.a(packetplayinarmanimation.b()); + } + + public void a(PacketPlayInEntityAction packetplayinentityaction) { + PlayerConnectionUtils.ensureMainThread(packetplayinentityaction, this, this.player.getWorldServer()); + // CraftBukkit start + if (this.player.dead) return; + switch (packetplayinentityaction.c()) { + case START_SNEAKING: + case STOP_SNEAKING: + PlayerToggleSneakEvent event = new PlayerToggleSneakEvent(this.getPlayer(), packetplayinentityaction.c() == PacketPlayInEntityAction.EnumPlayerAction.START_SNEAKING); + this.server.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + break; + case START_SPRINTING: + case STOP_SPRINTING: + PlayerToggleSprintEvent e2 = new PlayerToggleSprintEvent(this.getPlayer(), packetplayinentityaction.c() == PacketPlayInEntityAction.EnumPlayerAction.START_SPRINTING); + this.server.getPluginManager().callEvent(e2); + + if (e2.isCancelled()) { + return; + } + break; + } + // CraftBukkit end + this.player.resetIdleTimer(); + IJumpable ijumpable; + + switch (packetplayinentityaction.c()) { + case START_SNEAKING: + this.player.setSneaking(true); + + // Paper start - Hang on! + if (this.player.world.paperConfig.parrotsHangOnBetter) { + this.player.releaseShoulderEntities(); + } + // Paper end + + break; + case STOP_SNEAKING: + this.player.setSneaking(false); + break; + case START_SPRINTING: + this.player.setSprinting(true); + break; + case STOP_SPRINTING: + this.player.setSprinting(false); + break; + case STOP_SLEEPING: + if (this.player.isSleeping()) { + this.player.a(false, true, true); + this.teleportPos = new Vec3D(this.player.locX, this.player.locY, this.player.locZ); + } + break; + case START_RIDING_JUMP: + if (this.player.getVehicle() instanceof IJumpable) { + ijumpable = (IJumpable) this.player.getVehicle(); + int i = packetplayinentityaction.d(); + + if (ijumpable.G_() && i > 0) { + ijumpable.b(i); + } + } + break; + case STOP_RIDING_JUMP: + if (this.player.getVehicle() instanceof IJumpable) { + ijumpable = (IJumpable) this.player.getVehicle(); + ijumpable.I_(); + } + break; + case OPEN_INVENTORY: + if (this.player.getVehicle() instanceof EntityHorseAbstract) { + ((EntityHorseAbstract) this.player.getVehicle()).c((EntityHuman) this.player); + } + break; + case START_FALL_FLYING: + if (!this.player.onGround && this.player.motY < 0.0D && !this.player.dc() && !this.player.isInWater()) { + ItemStack itemstack = this.player.getEquipment(EnumItemSlot.CHEST); + + if (itemstack.getItem() == Items.ELYTRA && ItemElytra.e(itemstack)) { + this.player.J(); + } + } else { + this.player.K(); + } + break; + default: + throw new IllegalArgumentException("Invalid client command!"); + } + + } + + public void a(PacketPlayInUseEntity packetplayinuseentity) { + PlayerConnectionUtils.ensureMainThread(packetplayinuseentity, this, this.player.getWorldServer()); + if (this.player.isFrozen()) return; // CraftBukkit + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + Entity entity = packetplayinuseentity.a((World) worldserver); + // Spigot Start + if ( entity == player && !player.isSpectator() ) + { + disconnect( "Cannot interact with self!" ); + return; + } + // Spigot End + + this.player.resetIdleTimer(); + if (entity != null) { + boolean flag = this.player.hasLineOfSight(entity); + double d0 = 36.0D; + + if (!flag) { + d0 = 9.0D; + } + + if (this.player.h(entity) < d0) { + EnumHand enumhand; + + ItemStack itemInHand = this.player.b(packetplayinuseentity.c() == null ? EnumHand.MAIN_HAND : packetplayinuseentity.c()); // CraftBukkit + + if (packetplayinuseentity.b() == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT + || packetplayinuseentity.b() == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT_AT) { + // CraftBukkit start + boolean triggerLeashUpdate = itemInHand != null && itemInHand.getItem() == Items.LEAD && entity instanceof EntityInsentient; + Item origItem = this.player.inventory.getItemInHand() == null ? null : this.player.inventory.getItemInHand().getItem(); + PlayerInteractEntityEvent event; + if (packetplayinuseentity.b() == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT) { + event = new PlayerInteractEntityEvent((Player) this.getPlayer(), entity.getBukkitEntity(), (packetplayinuseentity.c() == EnumHand.OFF_HAND) ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND); + } else { + Vec3D target = packetplayinuseentity.d(); + event = new PlayerInteractAtEntityEvent((Player) this.getPlayer(), entity.getBukkitEntity(), new org.bukkit.util.Vector(target.x, target.y, target.z), (packetplayinuseentity.c() == EnumHand.OFF_HAND) ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND); + } + this.server.getPluginManager().callEvent(event); + + // Fish bucket - SPIGOT-4048 + if ((entity instanceof EntityFish && origItem != null && origItem.getItem() == Items.WATER_BUCKET) && (event.isCancelled() || this.player.inventory.getItemInHand() == null || this.player.inventory.getItemInHand().getItem() != origItem)) { + this.sendPacket(new PacketPlayOutSpawnEntityLiving((EntityFish) entity)); + this.player.updateInventory(this.player.activeContainer); + } + + if (triggerLeashUpdate && (event.isCancelled() || this.player.inventory.getItemInHand() == null || this.player.inventory.getItemInHand().getItem() != origItem)) { + // Refresh the current leash state + this.sendPacket(new PacketPlayOutAttachEntity(entity, ((EntityInsentient) entity).getLeashHolder())); + } + + if (event.isCancelled() || this.player.inventory.getItemInHand() == null || this.player.inventory.getItemInHand().getItem() != origItem) { + // Refresh the current entity metadata + this.sendPacket(new PacketPlayOutEntityMetadata(entity.getId(), entity.datawatcher, true)); + } + + if (event.isCancelled()) { + this.player.updateInventory(this.player.activeContainer); // Paper - Refresh player inventory + return; + } + // CraftBukkit end + } + + if (packetplayinuseentity.b() == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT) { + enumhand = packetplayinuseentity.c(); + this.player.a(entity, enumhand); + // CraftBukkit start + if (!itemInHand.isEmpty() && itemInHand.getCount() <= -1) { + this.player.updateInventory(this.player.activeContainer); + } + // CraftBukkit end + } else if (packetplayinuseentity.b() == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT_AT) { + enumhand = packetplayinuseentity.c(); + entity.a(this.player, packetplayinuseentity.d(), enumhand); + // CraftBukkit start + if (!itemInHand.isEmpty() && itemInHand.getCount() <= -1) { + this.player.updateInventory(this.player.activeContainer); + } + // CraftBukkit end + } else if (packetplayinuseentity.b() == PacketPlayInUseEntity.EnumEntityUseAction.ATTACK) { + if (entity instanceof EntityItem || entity instanceof EntityExperienceOrb || entity instanceof EntityArrow || (entity == this.player && !player.isSpectator())) { // CraftBukkit + this.disconnect(new ChatMessage("multiplayer.disconnect.invalid_entity_attacked", new Object[0])); + this.minecraftServer.warning("Player " + this.player.getDisplayName().getString() + " tried to attack an invalid entity"); + return; + } + + this.player.attack(entity); + + // CraftBukkit start + if (!itemInHand.isEmpty() && itemInHand.getCount() <= -1) { + this.player.updateInventory(this.player.activeContainer); + } + // CraftBukkit end + } + } + } + // Paper start - fire event + else { + this.server.getPluginManager().callEvent(new com.destroystokyo.paper.event.player.PlayerUseUnknownEntityEvent( + this.getPlayer(), + packetplayinuseentity.getEntityId(), + packetplayinuseentity.b() == PacketPlayInUseEntity.EnumEntityUseAction.ATTACK, + packetplayinuseentity.c() == EnumHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND + )); + } + // Paper end + + } + + public void a(PacketPlayInClientCommand packetplayinclientcommand) { + PlayerConnectionUtils.ensureMainThread(packetplayinclientcommand, this, this.player.getWorldServer()); + this.player.resetIdleTimer(); + PacketPlayInClientCommand.EnumClientCommand packetplayinclientcommand_enumclientcommand = packetplayinclientcommand.b(); + + switch (packetplayinclientcommand_enumclientcommand) { + case PERFORM_RESPAWN: + if (this.player.viewingCredits) { + this.player.viewingCredits = false; + // this.player = this.minecraftServer.getPlayerList().moveToWorld(this.player, DimensionManager.OVERWORLD, true); + this.minecraftServer.getPlayerList().changeDimension(this.player, DimensionManager.OVERWORLD, PlayerTeleportEvent.TeleportCause.END_PORTAL); // CraftBukkit - reroute logic through custom portal management + CriterionTriggers.v.a(this.player, DimensionManager.THE_END, DimensionManager.OVERWORLD); + } else { + if (this.player.getHealth() > 0.0F) { + return; + } + + this.player = this.minecraftServer.getPlayerList().moveToWorld(this.player, DimensionManager.OVERWORLD, false); + if (this.minecraftServer.isHardcore()) { + this.player.a(EnumGamemode.SPECTATOR); + this.player.getWorldServer().getGameRules().set("spectatorsGenerateChunks", "false", this.minecraftServer); + } + } + break; + case REQUEST_STATS: + this.player.getStatisticManager().a(this.player); + } + + } + + public void a(PacketPlayInCloseWindow packetplayinclosewindow) { + PlayerConnectionUtils.ensureMainThread(packetplayinclosewindow, this, this.player.getWorldServer()); + + if (this.player.isFrozen()) return; // CraftBukkit + CraftEventFactory.handleInventoryCloseEvent(this.player, org.bukkit.event.inventory.InventoryCloseEvent.Reason.PLAYER); // CraftBukkit // Paper + + this.player.m(); + } + + public void a(PacketPlayInWindowClick packetplayinwindowclick) { + PlayerConnectionUtils.ensureMainThread(packetplayinwindowclick, this, this.player.getWorldServer()); + if (this.player.isFrozen()) return; // CraftBukkit + this.player.resetIdleTimer(); + if (this.player.activeContainer.windowId == packetplayinwindowclick.b() && this.player.activeContainer.c(this.player) && this.player.activeContainer.canUse(this.player)) { // CraftBukkit + boolean cancelled = this.player.isSpectator(); // CraftBukkit - see below if + if (false/*this.player.isSpectator()*/) { // CraftBukkit + NonNullList nonnulllist = NonNullList.a(); + + for (int i = 0; i < this.player.activeContainer.slots.size(); ++i) { + nonnulllist.add(((Slot) this.player.activeContainer.slots.get(i)).getItem()); + } + + this.player.a(this.player.activeContainer, nonnulllist); + } else { + // CraftBukkit start - Call InventoryClickEvent + if (packetplayinwindowclick.c() < -1 && packetplayinwindowclick.c() != -999) { + return; + } + + InventoryView inventory = this.player.activeContainer.getBukkitView(); + SlotType type = inventory.getSlotType(packetplayinwindowclick.c()); + + InventoryClickEvent event; + ClickType click = ClickType.UNKNOWN; + InventoryAction action = InventoryAction.UNKNOWN; + + ItemStack itemstack = ItemStack.a; + + switch (packetplayinwindowclick.g()) { + case PICKUP: + if (packetplayinwindowclick.d() == 0) { + click = ClickType.LEFT; + } else if (packetplayinwindowclick.d() == 1) { + click = ClickType.RIGHT; + } + if (packetplayinwindowclick.d() == 0 || packetplayinwindowclick.d() == 1) { + action = InventoryAction.NOTHING; // Don't want to repeat ourselves + if (packetplayinwindowclick.c() == -999) { + if (!player.inventory.getCarried().isEmpty()) { + action = packetplayinwindowclick.d() == 0 ? InventoryAction.DROP_ALL_CURSOR : InventoryAction.DROP_ONE_CURSOR; + } + } else if (packetplayinwindowclick.c() < 0) { + action = InventoryAction.NOTHING; + } else { + Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.c()); + if (slot != null) { + ItemStack clickedItem = slot.getItem(); + ItemStack cursor = player.inventory.getCarried(); + if (clickedItem.isEmpty()) { + if (!cursor.isEmpty()) { + action = packetplayinwindowclick.d() == 0 ? InventoryAction.PLACE_ALL : InventoryAction.PLACE_ONE; + } + } else if (slot.isAllowed(player)) { + if (cursor.isEmpty()) { + action = packetplayinwindowclick.d() == 0 ? InventoryAction.PICKUP_ALL : InventoryAction.PICKUP_HALF; + } else if (slot.isAllowed(cursor)) { + if (clickedItem.doMaterialsMatch(cursor) && ItemStack.equals(clickedItem, cursor)) { + int toPlace = packetplayinwindowclick.d() == 0 ? cursor.getCount() : 1; + toPlace = Math.min(toPlace, clickedItem.getMaxStackSize() - clickedItem.getCount()); + toPlace = Math.min(toPlace, slot.inventory.getMaxStackSize() - clickedItem.getCount()); + if (toPlace == 1) { + action = InventoryAction.PLACE_ONE; + } else if (toPlace == cursor.getCount()) { + action = InventoryAction.PLACE_ALL; + } else if (toPlace < 0) { + action = toPlace != -1 ? InventoryAction.PICKUP_SOME : InventoryAction.PICKUP_ONE; // this happens with oversized stacks + } else if (toPlace != 0) { + action = InventoryAction.PLACE_SOME; + } + } else if (cursor.getCount() <= slot.getMaxStackSize()) { + action = InventoryAction.SWAP_WITH_CURSOR; + } + } else if (cursor.getItem() == clickedItem.getItem() && ItemStack.equals(cursor, clickedItem)) { + if (clickedItem.getCount() >= 0) { + if (clickedItem.getCount() + cursor.getCount() <= cursor.getMaxStackSize()) { + // As of 1.5, this is result slots only + action = InventoryAction.PICKUP_ALL; + } + } + } + } + } + } + } + break; + // TODO check on updates + case QUICK_MOVE: + if (packetplayinwindowclick.d() == 0) { + click = ClickType.SHIFT_LEFT; + } else if (packetplayinwindowclick.d() == 1) { + click = ClickType.SHIFT_RIGHT; + } + if (packetplayinwindowclick.d() == 0 || packetplayinwindowclick.d() == 1) { + if (packetplayinwindowclick.c() < 0) { + action = InventoryAction.NOTHING; + } else { + Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.c()); + if (slot != null && slot.isAllowed(this.player) && slot.hasItem()) { + action = InventoryAction.MOVE_TO_OTHER_INVENTORY; + } else { + action = InventoryAction.NOTHING; + } + } + } + break; + case SWAP: + if (packetplayinwindowclick.d() >= 0 && packetplayinwindowclick.d() < 9) { + click = ClickType.NUMBER_KEY; + Slot clickedSlot = this.player.activeContainer.getSlot(packetplayinwindowclick.c()); + if (clickedSlot.isAllowed(player)) { + ItemStack hotbar = this.player.inventory.getItem(packetplayinwindowclick.d()); + boolean canCleanSwap = hotbar.isEmpty() || (clickedSlot.inventory == player.inventory && clickedSlot.isAllowed(hotbar)); // the slot will accept the hotbar item + if (clickedSlot.hasItem()) { + if (canCleanSwap) { + action = InventoryAction.HOTBAR_SWAP; + } else { + action = InventoryAction.HOTBAR_MOVE_AND_READD; + } + } else if (!clickedSlot.hasItem() && !hotbar.isEmpty() && clickedSlot.isAllowed(hotbar)) { + action = InventoryAction.HOTBAR_SWAP; + } else { + action = InventoryAction.NOTHING; + } + } else { + action = InventoryAction.NOTHING; + } + } + break; + case CLONE: + if (packetplayinwindowclick.d() == 2) { + click = ClickType.MIDDLE; + if (packetplayinwindowclick.c() < 0) { // Paper - GH-404 + action = InventoryAction.NOTHING; + } else { + Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.c()); + if (slot != null && slot.hasItem() && player.abilities.canInstantlyBuild && player.inventory.getCarried().isEmpty()) { + action = InventoryAction.CLONE_STACK; + } else { + action = InventoryAction.NOTHING; + } + } + } else { + click = ClickType.UNKNOWN; + action = InventoryAction.UNKNOWN; + } + break; + case THROW: + if (packetplayinwindowclick.c() >= 0) { + if (packetplayinwindowclick.d() == 0) { + click = ClickType.DROP; + Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.c()); + if (slot != null && slot.hasItem() && slot.isAllowed(player) && !slot.getItem().isEmpty() && slot.getItem().getItem() != Item.getItemOf(Blocks.AIR)) { + action = InventoryAction.DROP_ONE_SLOT; + } else { + action = InventoryAction.NOTHING; + } + } else if (packetplayinwindowclick.d() == 1) { + click = ClickType.CONTROL_DROP; + Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.c()); + if (slot != null && slot.hasItem() && slot.isAllowed(player) && !slot.getItem().isEmpty() && slot.getItem().getItem() != Item.getItemOf(Blocks.AIR)) { + action = InventoryAction.DROP_ALL_SLOT; + } else { + action = InventoryAction.NOTHING; + } + } + } else { + // Sane default (because this happens when they are holding nothing. Don't ask why.) + click = ClickType.LEFT; + if (packetplayinwindowclick.d() == 1) { + click = ClickType.RIGHT; + } + action = InventoryAction.NOTHING; + } + break; + case QUICK_CRAFT: + itemstack = this.player.activeContainer.a(packetplayinwindowclick.c(), packetplayinwindowclick.d(), packetplayinwindowclick.g(), this.player); + break; + case PICKUP_ALL: + click = ClickType.DOUBLE_CLICK; + action = InventoryAction.NOTHING; + if (packetplayinwindowclick.c() >= 0 && !this.player.inventory.getCarried().isEmpty()) { + ItemStack cursor = this.player.inventory.getCarried(); + action = InventoryAction.NOTHING; + // Quick check for if we have any of the item + if (inventory.getTopInventory().contains(CraftMagicNumbers.getMaterial(cursor.getItem())) || inventory.getBottomInventory().contains(CraftMagicNumbers.getMaterial(cursor.getItem()))) { + action = InventoryAction.COLLECT_TO_CURSOR; + } + } + break; + default: + break; + } + + if (packetplayinwindowclick.g() != InventoryClickType.QUICK_CRAFT) { + if (click == ClickType.NUMBER_KEY) { + event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.c(), click, action, packetplayinwindowclick.d()); + } else { + event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.c(), click, action); + } + + org.bukkit.inventory.Inventory top = inventory.getTopInventory(); + if (packetplayinwindowclick.c() == 0 && top instanceof CraftingInventory) { + org.bukkit.inventory.Recipe recipe = ((CraftingInventory) top).getRecipe(); + if (recipe != null) { + if (click == ClickType.NUMBER_KEY) { + event = new CraftItemEvent(recipe, inventory, type, packetplayinwindowclick.c(), click, action, packetplayinwindowclick.d()); + } else { + event = new CraftItemEvent(recipe, inventory, type, packetplayinwindowclick.c(), click, action); + } + } + } + + event.setCancelled(cancelled); + Container oldContainer = this.player.activeContainer; // SPIGOT-1224 + server.getPluginManager().callEvent(event); + if (this.player.activeContainer != oldContainer) { + return; + } + + switch (event.getResult()) { + case ALLOW: + case DEFAULT: + itemstack = this.player.activeContainer.a(packetplayinwindowclick.c(), packetplayinwindowclick.d(), packetplayinwindowclick.g(), this.player); + break; + case DENY: + /* Needs enum constructor in InventoryAction + if (action.modifiesOtherSlots()) { + + } else { + if (action.modifiesCursor()) { + this.player.playerConnection.sendPacket(new Packet103SetSlot(-1, -1, this.player.inventory.getCarried())); + } + if (action.modifiesClicked()) { + this.player.playerConnection.sendPacket(new Packet103SetSlot(this.player.activeContainer.windowId, packet102windowclick.slot, this.player.activeContainer.getSlot(packet102windowclick.slot).getItem())); + } + }*/ + switch (action) { + // Modified other slots + case PICKUP_ALL: + case MOVE_TO_OTHER_INVENTORY: + case HOTBAR_MOVE_AND_READD: + case HOTBAR_SWAP: + case COLLECT_TO_CURSOR: + case UNKNOWN: + this.player.updateInventory(this.player.activeContainer); + break; + // Modified cursor and clicked + case PICKUP_SOME: + case PICKUP_HALF: + case PICKUP_ONE: + case PLACE_ALL: + case PLACE_SOME: + case PLACE_ONE: + case SWAP_WITH_CURSOR: + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.player.inventory.getCarried())); + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.player.activeContainer.windowId, packetplayinwindowclick.c(), this.player.activeContainer.getSlot(packetplayinwindowclick.c()).getItem())); + break; + // Modified clicked only + case DROP_ALL_SLOT: + case DROP_ONE_SLOT: + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.player.activeContainer.windowId, packetplayinwindowclick.c(), this.player.activeContainer.getSlot(packetplayinwindowclick.c()).getItem())); + break; + // Modified cursor only + case DROP_ALL_CURSOR: + case DROP_ONE_CURSOR: + case CLONE_STACK: + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.player.inventory.getCarried())); + break; + // Nothing + case NOTHING: + break; + } + return; + } + + if (event instanceof CraftItemEvent) { + // Need to update the inventory on crafting to + // correctly support custom recipes + player.updateInventory(player.activeContainer); + } + } + // CraftBukkit end + if (ItemStack.matches(packetplayinwindowclick.f(), itemstack)) { + this.player.playerConnection.sendPacket(new PacketPlayOutTransaction(packetplayinwindowclick.b(), packetplayinwindowclick.e(), true)); + this.player.f = true; + this.player.activeContainer.b(); + this.player.broadcastCarriedItem(); + this.player.f = false; + } else { + this.k.a(this.player.activeContainer.windowId, packetplayinwindowclick.e()); + this.player.playerConnection.sendPacket(new PacketPlayOutTransaction(packetplayinwindowclick.b(), packetplayinwindowclick.e(), false)); + this.player.activeContainer.a(this.player, false); + NonNullList nonnulllist1 = NonNullList.a(); + + for (int j = 0; j < this.player.activeContainer.slots.size(); ++j) { + ItemStack itemstack1 = ((Slot) this.player.activeContainer.slots.get(j)).getItem(); + + nonnulllist1.add(itemstack1.isEmpty() ? ItemStack.a : itemstack1); + } + + this.player.a(this.player.activeContainer, nonnulllist1); + } + } + } + + } + + public void a(PacketPlayInAutoRecipe packetplayinautorecipe) { + PlayerConnectionUtils.ensureMainThread(packetplayinautorecipe, this, this.player.getWorldServer()); + this.player.resetIdleTimer(); + if (!this.player.isSpectator() && this.player.activeContainer.windowId == packetplayinautorecipe.b() && this.player.activeContainer.c(this.player)) { + IRecipe irecipe = this.minecraftServer.getCraftingManager().a(packetplayinautorecipe.c()); + + if (this.player.activeContainer instanceof ContainerFurnace) { + (new AutoRecipeFurnace()).a(this.player, irecipe, packetplayinautorecipe.d()); + } else { + (new AutoRecipe()).a(this.player, irecipe, packetplayinautorecipe.d()); + } + + } + } + + public void a(PacketPlayInEnchantItem packetplayinenchantitem) { + PlayerConnectionUtils.ensureMainThread(packetplayinenchantitem, this, this.player.getWorldServer()); + if (this.player.isFrozen()) return; // CraftBukkit + this.player.resetIdleTimer(); + if (this.player.activeContainer.windowId == packetplayinenchantitem.b() && this.player.activeContainer.c(this.player) && !this.player.isSpectator()) { + this.player.activeContainer.a(this.player, packetplayinenchantitem.c()); + this.player.activeContainer.b(); + } + + } + + public void a(PacketPlayInSetCreativeSlot packetplayinsetcreativeslot) { + PlayerConnectionUtils.ensureMainThread(packetplayinsetcreativeslot, this, this.player.getWorldServer()); + if (this.player.playerInteractManager.isCreative()) { + boolean flag = packetplayinsetcreativeslot.b() < 0; + ItemStack itemstack = packetplayinsetcreativeslot.getItemStack(); + NBTTagCompound nbttagcompound = itemstack.b("BlockEntityTag"); + + if (!itemstack.isEmpty() && nbttagcompound != null && nbttagcompound.hasKey("x") && nbttagcompound.hasKey("y") && nbttagcompound.hasKey("z") && this.player.getBukkitEntity().hasPermission("minecraft.nbt.copy")) { // Spigot + BlockPosition blockposition = new BlockPosition(nbttagcompound.getInt("x"), nbttagcompound.getInt("y"), nbttagcompound.getInt("z")); + TileEntity tileentity = this.player.world.getTileEntity(blockposition); + + if (tileentity != null) { + NBTTagCompound nbttagcompound1 = tileentity.save(new NBTTagCompound()); + + nbttagcompound1.remove("x"); + nbttagcompound1.remove("y"); + nbttagcompound1.remove("z"); + itemstack.a("BlockEntityTag", (NBTBase) nbttagcompound1); + } + } + + boolean flag1 = packetplayinsetcreativeslot.b() >= 1 && packetplayinsetcreativeslot.b() <= 45; + boolean flag2 = itemstack.isEmpty() || itemstack.getDamage() >= 0 && itemstack.getCount() <= 64 && !itemstack.isEmpty(); + if (flag || (flag1 && !ItemStack.matches(this.player.defaultContainer.getSlot(packetplayinsetcreativeslot.b()).getItem(), packetplayinsetcreativeslot.getItemStack()))) { // Insist on valid slot + // CraftBukkit start - Call click event + InventoryView inventory = this.player.defaultContainer.getBukkitView(); + org.bukkit.inventory.ItemStack item = CraftItemStack.asBukkitCopy(packetplayinsetcreativeslot.getItemStack()); + + SlotType type = SlotType.QUICKBAR; + if (flag) { + type = SlotType.OUTSIDE; + } else if (packetplayinsetcreativeslot.b() < 36) { + if (packetplayinsetcreativeslot.b() >= 5 && packetplayinsetcreativeslot.b() < 9) { + type = SlotType.ARMOR; + } else { + type = SlotType.CONTAINER; + } + } + InventoryCreativeEvent event = new InventoryCreativeEvent(inventory, type, flag ? -999 : packetplayinsetcreativeslot.b(), item); + server.getPluginManager().callEvent(event); + + itemstack = CraftItemStack.asNMSCopy(event.getCursor()); + + switch (event.getResult()) { + case ALLOW: + // Plugin cleared the id / stacksize checks + flag2 = true; + break; + case DEFAULT: + break; + case DENY: + // Reset the slot + if (packetplayinsetcreativeslot.b() >= 0) { + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.player.defaultContainer.windowId, packetplayinsetcreativeslot.b(), this.player.defaultContainer.getSlot(packetplayinsetcreativeslot.b()).getItem())); + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, ItemStack.a)); + } + return; + } + } + // CraftBukkit end + + if (flag1 && flag2) { + if (itemstack.isEmpty()) { + this.player.defaultContainer.setItem(packetplayinsetcreativeslot.b(), ItemStack.a); + } else { + this.player.defaultContainer.setItem(packetplayinsetcreativeslot.b(), itemstack); + } + + this.player.defaultContainer.a(this.player, true); + } else if (flag && flag2 && this.j < 200) { + this.j += 20; + EntityItem entityitem = this.player.drop(itemstack, true); + + if (entityitem != null) { + entityitem.f(); + } + } + } + + } + + public void a(PacketPlayInTransaction packetplayintransaction) { + PlayerConnectionUtils.ensureMainThread(packetplayintransaction, this, this.player.getWorldServer()); + if (this.player.isFrozen()) return; // CraftBukkit + Short oshort = (Short) this.k.get(this.player.activeContainer.windowId); + + if (oshort != null && packetplayintransaction.c() == oshort && this.player.activeContainer.windowId == packetplayintransaction.b() && !this.player.activeContainer.c(this.player) && !this.player.isSpectator()) { + this.player.activeContainer.a(this.player, true); + } + + } + + public void a(PacketPlayInUpdateSign packetplayinupdatesign) { + PlayerConnectionUtils.ensureMainThread(packetplayinupdatesign, this, this.player.getWorldServer()); + if (this.player.isFrozen()) return; // CraftBukkit + this.player.resetIdleTimer(); + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + BlockPosition blockposition = packetplayinupdatesign.b(); + + if (worldserver.isLoaded(blockposition)) { + IBlockData iblockdata = worldserver.getType(blockposition); + TileEntity tileentity = worldserver.getTileEntity(blockposition); + + if (!(tileentity instanceof TileEntitySign)) { + return; + } + + TileEntitySign tileentitysign = (TileEntitySign) tileentity; + + if (!tileentitysign.d() || tileentitysign.signEditor == null || !tileentitysign.signEditor.equals(this.player.getUniqueID())) { // Paper + this.minecraftServer.warning("Player " + this.player.getDisplayName().getString() + " just tried to change non-editable sign"); + this.sendPacket(tileentity.getUpdatePacket()); // CraftBukkit + return; + } + + String[] astring = packetplayinupdatesign.c(); + + // CraftBukkit start + Player player = this.server.getPlayer(this.player); + int x = packetplayinupdatesign.b().getX(); + int y = packetplayinupdatesign.b().getY(); + int z = packetplayinupdatesign.b().getZ(); + String[] lines = new String[4]; + + for (int i = 0; i < astring.length; ++i) { + // Paper start - cap line length - modified clients can send longer data than normal + if (MAX_SIGN_LINE_LENGTH > 0 && astring[i].length() > MAX_SIGN_LINE_LENGTH) { + // This handles multibyte characters as 1 + int offset = astring[i].codePoints().limit(MAX_SIGN_LINE_LENGTH).map(Character::charCount).sum(); + if (offset > astring.length) { + astring[i] = astring[i].substring(0, offset); + } + } + // Paper end + lines[i] = SharedConstants.a(astring[i]); //Paper - Replaced with anvil color stripping method to stop exploits that allow colored signs to be created. + } + SignChangeEvent event = new SignChangeEvent((org.bukkit.craftbukkit.block.CraftBlock) player.getWorld().getBlockAt(x, y, z), this.server.getPlayer(this.player), lines); + this.server.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + System.arraycopy(org.bukkit.craftbukkit.block.CraftSign.sanitizeLines(event.getLines()), 0, tileentitysign.lines, 0, 4); + tileentitysign.isEditable = false; + } + // CraftBukkit end + + tileentitysign.update(); + worldserver.notify(blockposition, iblockdata, iblockdata, 3); + } + + } + + public void a(PacketPlayInKeepAlive packetplayinkeepalive) { + //PlayerConnectionUtils.ensureMainThread(packetplayinkeepalive, this, this.player.getWorldServer()); // CraftBukkit // Paper - This shouldn't be on the main thread + if (this.awaitingKeepAlive && packetplayinkeepalive.b() == this.h) { + int i = (int) (SystemUtils.getMonotonicMillis() - this.lastKeepAlive); + + this.player.ping = (this.player.ping * 3 + i) / 4; + this.awaitingKeepAlive = false; + } else if (!this.player.getDisplayName().getString().equals(this.minecraftServer.G())) { + // Paper start - This needs to be handled on the main thread for plugins + minecraftServer.postToMainThread(() -> { + this.disconnect(new ChatMessage("disconnect.timeout", new Object[0])); + }); + // Paper end + } + + } + + public void a(PacketPlayInAbilities packetplayinabilities) { + PlayerConnectionUtils.ensureMainThread(packetplayinabilities, this, this.player.getWorldServer()); + // CraftBukkit start + if (this.player.abilities.canFly && this.player.abilities.isFlying != packetplayinabilities.isFlying()) { + PlayerToggleFlightEvent event = new PlayerToggleFlightEvent(this.server.getPlayer(this.player), packetplayinabilities.isFlying()); + this.server.getPluginManager().callEvent(event); + if (!event.isCancelled()) { + this.player.abilities.isFlying = packetplayinabilities.isFlying(); // Actually set the player's flying status + } else { + this.player.updateAbilities(); // Tell the player their ability was reverted + } + } + // CraftBukkit end + } + + public void a(PacketPlayInSettings packetplayinsettings) { + PlayerConnectionUtils.ensureMainThread(packetplayinsettings, this, this.player.getWorldServer()); + this.player.a(packetplayinsettings); + } + + // CraftBukkit start + private static final MinecraftKey CUSTOM_REGISTER = new MinecraftKey("register"); + private static final MinecraftKey CUSTOM_UNREGISTER = new MinecraftKey("unregister"); + + public void a(PacketPlayInCustomPayload packetplayincustompayload) { + PlayerConnectionUtils.ensureMainThread(packetplayincustompayload, this, this.player.getWorldServer()); + if (packetplayincustompayload.tag.equals(CUSTOM_REGISTER)) { + try { + String channels = packetplayincustompayload.data.toString(com.google.common.base.Charsets.UTF_8); + for (String channel : channels.split("\0")) { + getPlayer().addChannel(channel); + } + } catch (Exception ex) { + PlayerConnection.LOGGER.error("Couldn\'t register custom payload", ex); + this.disconnect("Invalid payload REGISTER!"); + } + } else if (packetplayincustompayload.tag.equals(CUSTOM_UNREGISTER)) { + try { + String channels = packetplayincustompayload.data.toString(com.google.common.base.Charsets.UTF_8); + for (String channel : channels.split("\0")) { + getPlayer().removeChannel(channel); + } + } catch (Exception ex) { + PlayerConnection.LOGGER.error("Couldn\'t unregister custom payload", ex); + this.disconnect("Invalid payload UNREGISTER!"); + } + } else { + try { + byte[] data = new byte[packetplayincustompayload.data.readableBytes()]; + packetplayincustompayload.data.readBytes(data); + server.getMessenger().dispatchIncomingMessage(player.getBukkitEntity(), packetplayincustompayload.tag.toString(), data); + } catch (Exception ex) { + PlayerConnection.LOGGER.error("Couldn\'t dispatch custom payload", ex); + this.disconnect("Invalid custom payload!"); + } + } + + } + + public final boolean isDisconnected() { + return (!this.player.joining && !this.networkManager.isConnected()) || this.processedDisconnect; // Paper + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/PlayerConnectionUtils.java b/src/main/java/net/minecraft/server/PlayerConnectionUtils.java new file mode 100644 index 000000000000..596aa27febab --- /dev/null +++ b/src/main/java/net/minecraft/server/PlayerConnectionUtils.java @@ -0,0 +1,21 @@ +package net.minecraft.server; + +import co.aikar.timings.MinecraftTimings; // Paper +import co.aikar.timings.Timing; // Paper + +public class PlayerConnectionUtils { + + public static void ensureMainThread(Packet packet, T t0, IAsyncTaskHandler iasynctaskhandler) throws CancelledPacketHandleException { + if (!iasynctaskhandler.isMainThread()) { + Timing timing = MinecraftTimings.getPacketTiming(packet); // Paper + + iasynctaskhandler.postToMainThread(() -> { + if (t0 instanceof PlayerConnection && ((PlayerConnection) t0).processedDisconnect) return; // CraftBukkit + try (Timing ignored = timing.startTiming()) { // Paper + packet.a(t0); + } // Paper - timings + }); + throw CancelledPacketHandleException.INSTANCE; + } + } +} diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java new file mode 100644 index 000000000000..5e595b62e529 --- /dev/null +++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java @@ -0,0 +1,548 @@ +package net.minecraft.server; + +// CraftBukkit start +import java.util.ArrayList; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.Event; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +// CraftBukkit end + +public class PlayerInteractManager { + + public World world; + public EntityPlayer player; + private EnumGamemode gamemode; + private boolean d; + private int lastDigTick; + private BlockPosition f; + private int currentTick; + private boolean h; + private BlockPosition i; + private int j; + private int k; + + public PlayerInteractManager(World world) { + this.gamemode = EnumGamemode.NOT_SET; + this.f = BlockPosition.ZERO; + this.i = BlockPosition.ZERO; + this.k = -1; + this.world = world; + } + + public void setGameMode(EnumGamemode enumgamemode) { + this.gamemode = enumgamemode; + enumgamemode.a(this.player.abilities); + this.player.updateAbilities(); + this.player.server.getPlayerList().sendAll(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_GAME_MODE, new EntityPlayer[] { this.player}), this.player); // CraftBukkit + this.world.everyoneSleeping(); + } + + public EnumGamemode getGameMode() { + return this.gamemode; + } + + public boolean c() { + return this.gamemode.f(); + } + + public boolean isCreative() { + return this.gamemode.isCreative(); + } + + public void b(EnumGamemode enumgamemode) { + if (this.gamemode == EnumGamemode.NOT_SET) { + this.gamemode = enumgamemode; + } + + this.setGameMode(this.gamemode); + } + + public void a() { + this.currentTick = MinecraftServer.currentTick; // CraftBukkit; + float f; + int i; + + if (this.h) { + int j = this.currentTick - this.j; + IBlockData iblockdata = this.world.getType(this.i); + + if (iblockdata.isAir()) { + this.h = false; + } else { + f = iblockdata.getDamage(this.player, this.player.world, this.i) * (float) (j + 1); + i = (int) (f * 10.0F); + if (i != this.k) { + this.world.c(this.player.getId(), this.i, i); + this.k = i; + } + + if (f >= 1.0F) { + this.h = false; + this.breakBlock(this.i); + } + } + } else if (this.d) { + IBlockData iblockdata1 = this.world.getType(this.f); + + if (iblockdata1.isAir()) { + this.world.c(this.player.getId(), this.f, -1); + this.k = -1; + this.d = false; + } else { + int k = this.currentTick - this.lastDigTick; + + f = iblockdata1.getDamage(this.player, this.player.world, this.i) * (float) (k + 1); + i = (int) (f * 10.0F); + if (i != this.k) { + this.world.c(this.player.getId(), this.f, i); + this.k = i; + } + } + } + + } + + public void a(BlockPosition blockposition, EnumDirection enumdirection) { + // CraftBukkit start + PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, blockposition, enumdirection, this.player.inventory.getItemInHand(), EnumHand.MAIN_HAND); + if (event.isCancelled()) { + // Let the client know the block still exists + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + // Paper start - brute force neighbor blocks for any attached blocks + for (EnumDirection dir : EnumDirection.values()) { + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(world, blockposition.shift(dir))); + } + // Paper end + // Update any tile entity data for this block + TileEntity tileentity = this.world.getTileEntity(blockposition); + if (tileentity != null) { + this.player.playerConnection.sendPacket(tileentity.getUpdatePacket()); + } + return; + } + // CraftBukkit end + if (this.isCreative()) { + if (!this.world.douseFire((EntityHuman) null, blockposition, enumdirection)) { + this.breakBlock(blockposition); + } + + } else { + if (this.gamemode.d()) { + if (this.gamemode == EnumGamemode.SPECTATOR) { + return; + } + + if (!this.player.dy()) { + ItemStack itemstack = this.player.getItemInMainHand(); + + if (itemstack.isEmpty()) { + return; + } + + ShapeDetectorBlock shapedetectorblock = new ShapeDetectorBlock(this.world, blockposition, false); + + if (!itemstack.a(this.world.F(), shapedetectorblock)) { + return; + } + } + } + + // this.world.douseFire((EntityHuman) null, blockposition, enumdirection); // CraftBukkit - Moved down + this.lastDigTick = this.currentTick; + float f = 1.0F; + IBlockData iblockdata = this.world.getType(blockposition); + + // CraftBukkit start - Swings at air do *NOT* exist. + if (event.useInteractedBlock() == Event.Result.DENY) { + // If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door. + IBlockData data = this.world.getType(blockposition); + if (data.getBlock() instanceof BlockDoor) { + // For some reason *BOTH* the bottom/top part have to be marked updated. + boolean bottom = data.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.LOWER; + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, bottom ? blockposition.up() : blockposition.down())); + } else if (data.getBlock() instanceof BlockTrapdoor) { + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + } + } else if (!iblockdata.isAir()) { + iblockdata.attack(this.world, blockposition, this.player); + f = iblockdata.getDamage(this.player, this.player.world, blockposition); + // Allow fire punching to be blocked + this.world.douseFire((EntityHuman) null, blockposition, enumdirection); + } + + if (event.useItemInHand() == Event.Result.DENY) { + // If we 'insta destroyed' then the client needs to be informed. + if (f > 1.0f) { + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + } + return; + } + org.bukkit.event.block.BlockDamageEvent blockEvent = CraftEventFactory.callBlockDamageEvent(this.player, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this.player.inventory.getItemInHand(), f >= 1.0f); + + if (blockEvent.isCancelled()) { + // Let the client know the block still exists + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + return; + } + + if (blockEvent.getInstaBreak()) { + f = 2.0f; + } + // CraftBukkit end + + if (!iblockdata.isAir() && f >= 1.0F) { + this.breakBlock(blockposition); + } else { + this.d = true; + this.f = blockposition; + int i = (int) (f * 10.0F); + + this.world.c(this.player.getId(), blockposition, i); + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + this.k = i; + } + + } + + this.world.chunkPacketBlockController.onPlayerLeftClickBlock(this, blockposition, enumdirection); // Paper - Anti-Xray + } + + public void a(BlockPosition blockposition) { + if (blockposition.equals(this.f)) { + this.currentTick = MinecraftServer.currentTick; // CraftBukkit + int i = this.currentTick - this.lastDigTick; + IBlockData iblockdata = this.world.getType(blockposition); + + if (!iblockdata.isAir()) { + float f = iblockdata.getDamage(this.player, this.player.world, blockposition) * (float) (i + 1); + + if (f >= 0.7F) { + this.d = false; + this.world.c(this.player.getId(), blockposition, -1); + this.breakBlock(blockposition); + } else if (!this.h) { + this.d = false; + this.h = true; + this.i = blockposition; + this.j = this.lastDigTick; + } + } + // CraftBukkit start - Force block reset to client + } else { + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + // CraftBukkit end + } + + } + + public void e() { + this.d = false; + this.world.c(this.player.getId(), this.f, -1); + } + + private boolean c(BlockPosition blockposition) { + IBlockData iblockdata = this.world.getType(blockposition); + + iblockdata.getBlock().a(this.world, blockposition, iblockdata, (EntityHuman) this.player); + boolean flag = this.world.setAir(blockposition); + + if (flag) { + iblockdata.getBlock().postBreak(this.world, blockposition, iblockdata); + } + + return flag; + } + + public boolean breakBlock(BlockPosition blockposition) { + IBlockData iblockdata = this.world.getType(blockposition); + // CraftBukkit start - fire BlockBreakEvent + org.bukkit.block.Block bblock = CraftBlock.at(world, blockposition); + BlockBreakEvent event = null; + + if (this.player instanceof EntityPlayer) { + // Sword + Creative mode pre-cancel + boolean isSwordNoBreak = !this.player.getItemInMainHand().getItem().a(iblockdata, this.world, blockposition, (EntityHuman) this.player); + + // Tell client the block is gone immediately then process events + // Don't tell the client if its a creative sword break because its not broken! + if (world.getTileEntity(blockposition) == null && !isSwordNoBreak) { + PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(this.world, blockposition); + packet.block = Blocks.AIR.getBlockData(); + ((EntityPlayer) this.player).playerConnection.sendPacket(packet); + } + + event = new BlockBreakEvent(bblock, this.player.getBukkitEntity()); + + // Sword + Creative mode pre-cancel + event.setCancelled(isSwordNoBreak); + + // Calculate default block experience + IBlockData nmsData = this.world.getType(blockposition); + Block nmsBlock = nmsData.getBlock(); + + ItemStack itemstack = this.player.getEquipment(EnumItemSlot.MAINHAND); + + if (nmsBlock != null && !event.isCancelled() && !this.isCreative() && this.player.hasBlock(nmsBlock.getBlockData())) { + // Copied from block.a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) + // PAIL: checkme each update + if (!(nmsBlock.X_() && EnchantmentManager.getEnchantmentLevel(Enchantments.SILK_TOUCH, itemstack) > 0)) { + int bonusLevel = EnchantmentManager.getEnchantmentLevel(Enchantments.LOOT_BONUS_BLOCKS, itemstack); + + event.setExpToDrop(nmsBlock.getExpDrop(nmsData, this.world, blockposition, bonusLevel)); + } + } + + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + if (isSwordNoBreak) { + return false; + } + // Let the client know the block still exists + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + + // Brute force all possible updates + for (EnumDirection dir : EnumDirection.values()) { + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(world, blockposition.shift(dir))); + } + + // Update any tile entity data for this block + TileEntity tileentity = this.world.getTileEntity(blockposition); + if (tileentity != null) { + this.player.playerConnection.sendPacket(tileentity.getUpdatePacket()); + } + return false; + } + } + // CraftBukkit end + + if (false && !this.player.getItemInMainHand().getItem().a(iblockdata, this.world, blockposition, (EntityHuman) this.player)) { // CraftBukkit - false + return false; + } else { + iblockdata = this.world.getType(blockposition); // CraftBukkit - update state from plugins + if (iblockdata.isAir()) return false; // CraftBukkit - A plugin set block to air without cancelling + TileEntity tileentity = this.world.getTileEntity(blockposition); + Block block = iblockdata.getBlock(); + + // CraftBukkit start - Special case skulls, their item data comes from a tile entity (Also check if block should drop items) + // And shulker boxes too for duplication on BlockPlaceEvent cancel reasons (Also check if block should drop items) + if (((iblockdata.getBlock() instanceof BlockSkullAbstract && !this.isCreative()) || iblockdata.getBlock() instanceof BlockShulkerBox) && event.isDropItems()) { + org.bukkit.block.BlockState state = bblock.getState(); + world.captureDrops = new ArrayList<>(); + + iblockdata.getBlock().dropNaturally(iblockdata, world, blockposition, 1.0F, 0); + boolean flag = this.c(blockposition); + + if (event.isDropItems()) { + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDropItemEvent(bblock, state, this.player, world.captureDrops); + } + + world.captureDrops = null; + return flag; + } + // CraftBukkit end + + if ((block instanceof BlockCommand || block instanceof BlockStructure) && !this.player.isCreativeAndOp()) { + this.world.notify(blockposition, iblockdata, iblockdata, 3); + return false; + } else { + if (this.gamemode.d()) { + if (this.gamemode == EnumGamemode.SPECTATOR) { + return false; + } + + if (!this.player.dy()) { + ItemStack itemstack = this.player.getItemInMainHand(); + + if (itemstack.isEmpty()) { + return false; + } + + ShapeDetectorBlock shapedetectorblock = new ShapeDetectorBlock(this.world, blockposition, false); + + if (!itemstack.a(this.world.F(), shapedetectorblock)) { + return false; + } + } + } + + // CraftBukkit start + org.bukkit.block.BlockState state = bblock.getState(); + world.captureDrops = new ArrayList<>(); + // CraftBukkit end + boolean flag = this.c(blockposition); + + if (!this.isCreative()) { + ItemStack itemstack1 = this.player.getItemInMainHand(); + boolean flag1 = this.player.hasBlock(iblockdata); + + ItemStack itemstack2 = flag && flag1 && event.isDropItems() && !itemstack1.isEmpty() ? itemstack1.cloneItemStack() : ItemStack.a; // Paper - MC-136865 - clone before use + itemstack1.a(this.world, iblockdata, blockposition, this.player); + // CraftBukkit start - Check if block should drop items + if (flag && flag1 && event.isDropItems()) { + //ItemStack itemstack2 = itemstack1.isEmpty() ? ItemStack.a : itemstack1.cloneItemStack(); // Paper - MC-136865 - move up + + iblockdata.getBlock().a(this.world, this.player, blockposition, iblockdata, tileentity, itemstack2); + } + // CraftBukkit end + } + + if (event.isDropItems()) { + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDropItemEvent(bblock, state, this.player, world.captureDrops); + } + world.captureDrops = null; + // CraftBukkit end + + // CraftBukkit start - Drop event experience + if (flag && event != null) { + iblockdata.getBlock().dropExperience(this.world, blockposition, event.getExpToDrop(), this.player); // Paper + } + // CraftBukkit end + + return flag; + } + } + } + + public EnumInteractionResult a(EntityHuman entityhuman, World world, ItemStack itemstack, EnumHand enumhand) { + if (this.gamemode == EnumGamemode.SPECTATOR) { + return EnumInteractionResult.PASS; + } else if (entityhuman.getCooldownTracker().a(itemstack.getItem())) { + return EnumInteractionResult.PASS; + } else { + int i = itemstack.getCount(); + int j = itemstack.getDamage(); + InteractionResultWrapper interactionresultwrapper = itemstack.a(world, entityhuman, enumhand); + ItemStack itemstack1 = (ItemStack) interactionresultwrapper.b(); + + if (itemstack1 == itemstack && itemstack1.getCount() == i && itemstack1.k() <= 0 && itemstack1.getDamage() == j) { + return interactionresultwrapper.a(); + } else if (interactionresultwrapper.a() == EnumInteractionResult.FAIL && itemstack1.k() > 0 && !entityhuman.isHandRaised()) { + return interactionresultwrapper.a(); + } else { + entityhuman.a(enumhand, itemstack1); + if (this.isCreative()) { + itemstack1.setCount(i); + if (itemstack1.e()) { + itemstack1.setDamage(j); + } + } + + if (itemstack1.isEmpty()) { + entityhuman.a(enumhand, ItemStack.a); + } + + if (!entityhuman.isHandRaised()) { + ((EntityPlayer) entityhuman).updateInventory(entityhuman.defaultContainer); + } + + return interactionresultwrapper.a(); + } + } + } + + // CraftBukkit start - whole method + public boolean interactResult = false; + public boolean firedInteract = false; + public EnumInteractionResult a(EntityHuman entityhuman, World world, ItemStack itemstack, EnumHand enumhand, BlockPosition blockposition, EnumDirection enumdirection, float f, float f1, float f2) { + IBlockData iblockdata = world.getType(blockposition); + EnumInteractionResult enuminteractionresult = EnumInteractionResult.PASS; + if (iblockdata.isAir()) return enuminteractionresult; + boolean cancelledBlock = false; + + if (this.gamemode == EnumGamemode.SPECTATOR) { + TileEntity tileentity = world.getTileEntity(blockposition); + cancelledBlock = !(tileentity instanceof ITileInventory || tileentity instanceof IInventory); + } + + if (entityhuman.getCooldownTracker().a(itemstack.getItem())) { + cancelledBlock = true; + } + + PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(entityhuman, Action.RIGHT_CLICK_BLOCK, blockposition, enumdirection, itemstack, cancelledBlock, enumhand); + firedInteract = true; + interactResult = event.useItemInHand() == Event.Result.DENY; + + if (event.useInteractedBlock() == Event.Result.DENY) { + // If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door. + if (iblockdata.getBlock() instanceof BlockDoor) { + boolean bottom = iblockdata.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.LOWER; + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutBlockChange(world, bottom ? blockposition.up() : blockposition.down())); + } else if (iblockdata.getBlock() instanceof BlockCake) { + ((EntityPlayer) entityhuman).getBukkitEntity().sendHealthUpdate(); // SPIGOT-1341 - reset health for cake + // Paper start - extend Player Interact cancellation // TODO: consider merging this into the extracted method + } else if (iblockdata.getBlock() instanceof BlockStructure) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutCloseWindow()); + } else if (iblockdata.getBlock() instanceof BlockCommand) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutCloseWindow()); + } else if (iblockdata.getBlock() instanceof BlockFlowerPot) { + // Send a block change to air and then send back the correct block, just to make the client happy + PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(this.world, blockposition); + packet.block = Blocks.AIR.getBlockData(); + this.player.playerConnection.sendPacket(packet); + + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + + TileEntity tileentity = this.world.getTileEntity(blockposition); + if (tileentity != null) { + player.playerConnection.sendPacket(tileentity.getUpdatePacket()); + } + } + // Paper end - extend Player Interact cancellation + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); // SPIGOT-2867 + enuminteractionresult = (event.useItemInHand() != Event.Result.ALLOW) ? EnumInteractionResult.SUCCESS : EnumInteractionResult.PASS; + } else if (this.gamemode == EnumGamemode.SPECTATOR) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof ITileInventory) { + Block block = iblockdata.getBlock(); + ITileInventory itileinventory = (ITileInventory) tileentity; + + if (itileinventory instanceof TileEntityChest && block instanceof BlockChest) { + itileinventory = ((BlockChest) block).getInventory(iblockdata, world, blockposition, false); + } + + if (itileinventory != null) { + entityhuman.openContainer(itileinventory); + return EnumInteractionResult.SUCCESS; + } + } else if (tileentity instanceof IInventory) { + entityhuman.openContainer((IInventory) tileentity); + return EnumInteractionResult.SUCCESS; + } + + return EnumInteractionResult.PASS; + } else { + boolean flag = !entityhuman.getItemInMainHand().isEmpty() || !entityhuman.getItemInOffHand().isEmpty(); + boolean flag1 = entityhuman.isSneaking() && flag; + + if (!flag1) { + enuminteractionresult = iblockdata.interact(world, blockposition, entityhuman, enumhand, enumdirection, f, f1, f2) ? EnumInteractionResult.SUCCESS : EnumInteractionResult.FAIL; + } + + if (!itemstack.isEmpty() && enuminteractionresult != EnumInteractionResult.SUCCESS && !interactResult) { // add !interactResult SPIGOT-764 + ItemActionContext itemactioncontext = new ItemActionContext(entityhuman, entityhuman.b(enumhand), blockposition, enumdirection, f, f1, f2); + + if (this.isCreative()) { + int i = itemstack.getCount(); + enuminteractionresult = itemstack.placeItem(itemactioncontext, enumhand); + + itemstack.setCount(i); + return enuminteractionresult; + } else { + return itemstack.placeItem(itemactioncontext, enumhand); + } + } + } + return enuminteractionresult; + // CraftBukkit end + } + + public void a(WorldServer worldserver) { + this.world = worldserver; + } +} diff --git a/src/main/java/net/minecraft/server/PlayerInventory.java b/src/main/java/net/minecraft/server/PlayerInventory.java new file mode 100644 index 000000000000..997fdc499787 --- /dev/null +++ b/src/main/java/net/minecraft/server/PlayerInventory.java @@ -0,0 +1,703 @@ +package net.minecraft.server; + +import com.google.common.collect.ImmutableList; +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +// CraftBukkit start +import java.util.ArrayList; +import java.util.List; +import org.bukkit.Location; + +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class PlayerInventory implements IInventory { + + public final NonNullList items; + public final NonNullList armor; + public final NonNullList extraSlots; + private final List> f; + public int itemInHandIndex; + public EntityHuman player; + private ItemStack carried; + private int h; + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public List getContents() { + List combined = new ArrayList(items.size() + armor.size() + extraSlots.size()); + for (List sub : this.f) { + combined.addAll(sub); + } + + return combined; + } + + public List getArmorContents() { + return this.armor; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public org.bukkit.inventory.InventoryHolder getOwner() { + return this.player.getBukkitEntity(); + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + + @Override + public Location getLocation() { + return player.getBukkitEntity().getLocation(); + } + // CraftBukkit end + + public PlayerInventory(EntityHuman entityhuman) { + this.items = NonNullList.a(36, ItemStack.a); + this.armor = NonNullList.a(4, ItemStack.a); + this.extraSlots = NonNullList.a(1, ItemStack.a); + this.f = ImmutableList.of(this.items, this.armor, this.extraSlots); + this.carried = ItemStack.a; + this.player = entityhuman; + } + + public ItemStack getItemInHand() { + return e(this.itemInHandIndex) ? (ItemStack) this.items.get(this.itemInHandIndex) : ItemStack.a; + } + + public static int getHotbarSize() { + return 9; + } + + private boolean a(ItemStack itemstack, ItemStack itemstack1) { + return !itemstack.isEmpty() && this.b(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize(); + } + + private boolean b(ItemStack itemstack, ItemStack itemstack1) { + return itemstack.getItem() == itemstack1.getItem() && ItemStack.equals(itemstack, itemstack1); + } + + // CraftBukkit start - Watch method above! :D + public int canHold(ItemStack itemstack) { + int remains = itemstack.getCount(); + for (int i = 0; i < this.items.size(); ++i) { + ItemStack itemstack1 = this.getItem(i); + if (itemstack1.isEmpty()) return itemstack.getCount(); + + if (this.a(itemstack1, itemstack)) { // PAIL rename isSimilarAndNotFull + remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount(); + } + if (remains <= 0) return itemstack.getCount(); + } + return itemstack.getCount() - remains; + } + // CraftBukkit end + + public int getFirstEmptySlotIndex() { + for (int i = 0; i < this.items.size(); ++i) { + if (((ItemStack) this.items.get(i)).isEmpty()) { + return i; + } + } + + return -1; + } + + public void d(int i) { + this.itemInHandIndex = this.l(); + ItemStack itemstack = (ItemStack) this.items.get(this.itemInHandIndex); + + this.items.set(this.itemInHandIndex, this.items.get(i)); + this.items.set(i, itemstack); + } + + public static boolean e(int i) { + return i >= 0 && i < 9; + } + + public int c(ItemStack itemstack) { + for (int i = 0; i < this.items.size(); ++i) { + ItemStack itemstack1 = (ItemStack) this.items.get(i); + + if (!((ItemStack) this.items.get(i)).isEmpty() && this.b(itemstack, (ItemStack) this.items.get(i)) && !((ItemStack) this.items.get(i)).f() && !itemstack1.hasEnchantments() && !itemstack1.hasName()) { + return i; + } + } + + return -1; + } + + public int l() { + int i; + int j; + + for (j = 0; j < 9; ++j) { + i = (this.itemInHandIndex + j) % 9; + if (((ItemStack) this.items.get(i)).isEmpty()) { + return i; + } + } + + for (j = 0; j < 9; ++j) { + i = (this.itemInHandIndex + j) % 9; + if (!((ItemStack) this.items.get(i)).hasEnchantments()) { + return i; + } + } + + return this.itemInHandIndex; + } + + public int a(Predicate predicate, int i) { + int j = 0; + + int k; + + for (k = 0; k < this.getSize(); ++k) { + ItemStack itemstack = this.getItem(k); + + if (!itemstack.isEmpty() && predicate.test(itemstack)) { + int l = i <= 0 ? itemstack.getCount() : Math.min(i - j, itemstack.getCount()); + + j += l; + if (i != 0) { + itemstack.subtract(l); + if (itemstack.isEmpty()) { + this.setItem(k, ItemStack.a); + } + + if (i > 0 && j >= i) { + return j; + } + } + } + } + + if (!this.carried.isEmpty() && predicate.test(this.carried)) { + k = i <= 0 ? this.carried.getCount() : Math.min(i - j, this.carried.getCount()); + j += k; + if (i != 0) { + this.carried.subtract(k); + if (this.carried.isEmpty()) { + this.carried = ItemStack.a; + } + + if (i > 0 && j >= i) { + return j; + } + } + } + + return j; + } + + private int i(ItemStack itemstack) { + int i = this.firstPartial(itemstack); + + if (i == -1) { + i = this.getFirstEmptySlotIndex(); + } + + return i == -1 ? itemstack.getCount() : this.d(i, itemstack); + } + + private int d(int i, ItemStack itemstack) { + Item item = itemstack.getItem(); + int j = itemstack.getCount(); + ItemStack itemstack1 = this.getItem(i); + + if (itemstack1.isEmpty()) { + itemstack1 = new ItemStack(item, 0); + if (itemstack.hasTag()) { + itemstack1.setTag(itemstack.getTag().clone()); + } + + this.setItem(i, itemstack1); + } + + int k = j; + + if (j > itemstack1.getMaxStackSize() - itemstack1.getCount()) { + k = itemstack1.getMaxStackSize() - itemstack1.getCount(); + } + + if (k > this.getMaxStackSize() - itemstack1.getCount()) { + k = this.getMaxStackSize() - itemstack1.getCount(); + } + + if (k == 0) { + return j; + } else { + j -= k; + itemstack1.add(k); + itemstack1.d(5); + return j; + } + } + + public int firstPartial(ItemStack itemstack) { + if (this.a(this.getItem(this.itemInHandIndex), itemstack)) { + return this.itemInHandIndex; + } else if (this.a(this.getItem(40), itemstack)) { + return 40; + } else { + for (int i = 0; i < this.items.size(); ++i) { + if (this.a((ItemStack) this.items.get(i), itemstack)) { + return i; + } + } + + return -1; + } + } + + public void p() { + Iterator iterator = this.f.iterator(); + + while (iterator.hasNext()) { + NonNullList nonnulllist = (NonNullList) iterator.next(); + + for (int i = 0; i < nonnulllist.size(); ++i) { + if (!((ItemStack) nonnulllist.get(i)).isEmpty()) { + ((ItemStack) nonnulllist.get(i)).a(this.player.world, this.player, i, this.itemInHandIndex == i); + } + } + } + + } + + public boolean pickup(ItemStack itemstack) { + return this.c(-1, itemstack); + } + + public boolean c(int i, ItemStack itemstack) { + if (itemstack.isEmpty()) { + return false; + } else { + try { + if (itemstack.f()) { + if (i == -1) { + i = this.getFirstEmptySlotIndex(); + } + + if (i >= 0) { + this.items.set(i, itemstack.cloneItemStack()); + ((ItemStack) this.items.get(i)).d(5); + itemstack.setCount(0); + return true; + } else if (this.player.abilities.canInstantlyBuild) { + itemstack.setCount(0); + return true; + } else { + return false; + } + } else { + int j; + + do { + j = itemstack.getCount(); + if (i == -1) { + itemstack.setCount(this.i(itemstack)); + } else { + itemstack.setCount(this.d(i, itemstack)); + } + } while (!itemstack.isEmpty() && itemstack.getCount() < j); + + if (itemstack.getCount() == j && this.player.abilities.canInstantlyBuild) { + itemstack.setCount(0); + return true; + } else { + return itemstack.getCount() < j; + } + } + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Adding item to inventory"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Item being added"); + + crashreportsystemdetails.a("Item ID", (Object) Item.getId(itemstack.getItem())); + crashreportsystemdetails.a("Item data", (Object) itemstack.getDamage()); + crashreportsystemdetails.a("Item name", () -> { + return itemstack.getName().getString(); + }); + throw new ReportedException(crashreport); + } + } + } + + public void a(World world, ItemStack itemstack) { + if (!world.isClientSide) { + while (!itemstack.isEmpty()) { + int i = this.firstPartial(itemstack); + + if (i == -1) { + i = this.getFirstEmptySlotIndex(); + } + + if (i == -1) { + this.player.drop(itemstack, false); + break; + } + + int j = itemstack.getMaxStackSize() - this.getItem(i).getCount(); + + if (this.c(i, itemstack.cloneAndSubtract(j))) { + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutSetSlot(-2, i, this.getItem(i))); + } + } + + } + } + + public ItemStack splitStack(int i, int j) { + List list = null; + + NonNullList nonnulllist; + + for (Iterator iterator = this.f.iterator(); iterator.hasNext(); i -= nonnulllist.size()) { + nonnulllist = (NonNullList) iterator.next(); + if (i < nonnulllist.size()) { + list = nonnulllist; + break; + } + } + + return list != null && !((ItemStack) list.get(i)).isEmpty() ? ContainerUtil.a(list, i, j) : ItemStack.a; + } + + public void f(ItemStack itemstack) { + Iterator iterator = this.f.iterator(); + + while (iterator.hasNext()) { + NonNullList nonnulllist = (NonNullList) iterator.next(); + + for (int i = 0; i < nonnulllist.size(); ++i) { + if (nonnulllist.get(i) == itemstack) { + nonnulllist.set(i, ItemStack.a); + break; + } + } + } + + } + + public ItemStack splitWithoutUpdate(int i) { + NonNullList nonnulllist = null; + + NonNullList nonnulllist1; + + for (Iterator iterator = this.f.iterator(); iterator.hasNext(); i -= nonnulllist1.size()) { + nonnulllist1 = (NonNullList) iterator.next(); + if (i < nonnulllist1.size()) { + nonnulllist = nonnulllist1; + break; + } + } + + if (nonnulllist != null && !((ItemStack) nonnulllist.get(i)).isEmpty()) { + ItemStack itemstack = (ItemStack) nonnulllist.get(i); + + nonnulllist.set(i, ItemStack.a); + return itemstack; + } else { + return ItemStack.a; + } + } + + public void setItem(int i, ItemStack itemstack) { + NonNullList nonnulllist = null; + + NonNullList nonnulllist1; + + for (Iterator iterator = this.f.iterator(); iterator.hasNext(); i -= nonnulllist1.size()) { + nonnulllist1 = (NonNullList) iterator.next(); + if (i < nonnulllist1.size()) { + nonnulllist = nonnulllist1; + break; + } + } + + if (nonnulllist != null) { + nonnulllist.set(i, itemstack); + } + + } + + public float a(IBlockData iblockdata) { + return ((ItemStack) this.items.get(this.itemInHandIndex)).a(iblockdata); + } + + public NBTTagList a(NBTTagList nbttaglist) { + NBTTagCompound nbttagcompound; + int i; + + for (i = 0; i < this.items.size(); ++i) { + if (!((ItemStack) this.items.get(i)).isEmpty()) { + nbttagcompound = new NBTTagCompound(); + nbttagcompound.setByte("Slot", (byte) i); + ((ItemStack) this.items.get(i)).save(nbttagcompound); + nbttaglist.add((NBTBase) nbttagcompound); + } + } + + for (i = 0; i < this.armor.size(); ++i) { + if (!((ItemStack) this.armor.get(i)).isEmpty()) { + nbttagcompound = new NBTTagCompound(); + nbttagcompound.setByte("Slot", (byte) (i + 100)); + ((ItemStack) this.armor.get(i)).save(nbttagcompound); + nbttaglist.add((NBTBase) nbttagcompound); + } + } + + for (i = 0; i < this.extraSlots.size(); ++i) { + if (!((ItemStack) this.extraSlots.get(i)).isEmpty()) { + nbttagcompound = new NBTTagCompound(); + nbttagcompound.setByte("Slot", (byte) (i + 150)); + ((ItemStack) this.extraSlots.get(i)).save(nbttagcompound); + nbttaglist.add((NBTBase) nbttagcompound); + } + } + + return nbttaglist; + } + + public void b(NBTTagList nbttaglist) { + this.items.clear(); + this.armor.clear(); + this.extraSlots.clear(); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist.getCompound(i); + int j = nbttagcompound.getByte("Slot") & 255; + ItemStack itemstack = ItemStack.a(nbttagcompound); + + if (!itemstack.isEmpty()) { + if (j >= 0 && j < this.items.size()) { + this.items.set(j, itemstack); + } else if (j >= 100 && j < this.armor.size() + 100) { + this.armor.set(j - 100, itemstack); + } else if (j >= 150 && j < this.extraSlots.size() + 150) { + this.extraSlots.set(j - 150, itemstack); + } + } + } + + } + + public int getSize() { + return this.items.size() + this.armor.size() + this.extraSlots.size(); + } + + public boolean P_() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + iterator = this.armor.iterator(); + + do { + if (!iterator.hasNext()) { + iterator = this.extraSlots.iterator(); + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + public ItemStack getItem(int i) { + List list = null; + + NonNullList nonnulllist; + + for (Iterator iterator = this.f.iterator(); iterator.hasNext(); i -= nonnulllist.size()) { + nonnulllist = (NonNullList) iterator.next(); + if (i < nonnulllist.size()) { + list = nonnulllist; + break; + } + } + + return list == null ? ItemStack.a : (ItemStack) list.get(i); + } + + public IChatBaseComponent getDisplayName() { + return new ChatMessage("container.inventory", new Object[0]); + } + + @Nullable + public IChatBaseComponent getCustomName() { + return null; + } + + public boolean hasCustomName() { + return false; + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public boolean b(IBlockData iblockdata) { + return this.getItem(this.itemInHandIndex).b(iblockdata); + } + + public void a(float f) { + if (f > 0.0F) { + f /= 4.0F; + if (f < 1.0F) { + f = 1.0F; + } + + for (int i = 0; i < this.armor.size(); ++i) { + ItemStack itemstack = (ItemStack) this.armor.get(i); + + if (itemstack.getItem() instanceof ItemArmor) { + itemstack.damage((int) f, this.player); + } + } + + } + } + + public void dropContents() { + Iterator iterator = this.f.iterator(); + + while (iterator.hasNext()) { + List list = (List) iterator.next(); + + for (int i = 0; i < list.size(); ++i) { + ItemStack itemstack = (ItemStack) list.get(i); + + if (!itemstack.isEmpty()) { + this.player.a(itemstack, true, false); + list.set(i, ItemStack.a); + } + } + } + + } + + public void update() { + ++this.h; + } + + public void setCarried(ItemStack itemstack) { + this.carried = itemstack; + } + + public ItemStack getCarried() { + // CraftBukkit start + if (this.carried.isEmpty()) { + this.setCarried(ItemStack.a); + } + // CraftBukkit end + return this.carried; + } + + public boolean a(EntityHuman entityhuman) { + return this.player.dead ? false : entityhuman.h(this.player) <= 64.0D; + } + + public boolean h(ItemStack itemstack) { + Iterator iterator = this.f.iterator(); + + while (iterator.hasNext()) { + List list = (List) iterator.next(); + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) { + ItemStack itemstack1 = (ItemStack) iterator1.next(); + + if (!itemstack1.isEmpty() && itemstack1.doMaterialsMatch(itemstack)) { + return true; + } + } + } + + return false; + } + + public void startOpen(EntityHuman entityhuman) {} + + public void closeContainer(EntityHuman entityhuman) {} + + public boolean b(int i, ItemStack itemstack) { + return true; + } + + public void a(PlayerInventory playerinventory) { + for (int i = 0; i < this.getSize(); ++i) { + this.setItem(i, playerinventory.getItem(i)); + } + + this.itemInHandIndex = playerinventory.itemInHandIndex; + } + + public int getProperty(int i) { + return 0; + } + + public void setProperty(int i, int j) {} + + public int h() { + return 0; + } + + public void clear() { + Iterator iterator = this.f.iterator(); + + while (iterator.hasNext()) { + List list = (List) iterator.next(); + + list.clear(); + } + + } + + public void a(AutoRecipeStackManager autorecipestackmanager) { + Iterator iterator = this.items.iterator(); + + while (iterator.hasNext()) { + ItemStack itemstack = (ItemStack) iterator.next(); + + autorecipestackmanager.a(itemstack); + } + + } +} diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java new file mode 100644 index 000000000000..408c382d2a16 --- /dev/null +++ b/src/main/java/net/minecraft/server/PlayerList.java @@ -0,0 +1,1514 @@ +package net.minecraft.server; + +import co.aikar.timings.MinecraftTimings; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.mojang.authlib.GameProfile; +import io.netty.buffer.Unpooled; +import java.io.File; +import java.net.SocketAddress; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.TravelAgent; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerChangedWorldEvent; +import org.bukkit.event.player.PlayerPortalEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerRespawnEvent; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.util.Vector; +import org.spigotmc.event.player.PlayerSpawnLocationEvent; +// CraftBukkit end + +public abstract class PlayerList { + + public static final File a = new File("banned-players.json"); + public static final File b = new File("banned-ips.json"); + public static final File c = new File("ops.json"); + public static final File d = new File("whitelist.json"); + private static final Logger f = LogManager.getLogger(); + private static final SimpleDateFormat g = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z"); + private final MinecraftServer server; + public final List players = new java.util.concurrent.CopyOnWriteArrayList(); // CraftBukkit - ArrayList -> CopyOnWriteArrayList: Iterator safety + private final Map j = Maps.newHashMap(); + private final GameProfileBanList k; + private final IpBanList l; + private final OpList operators; + private final WhiteList whitelist; + // CraftBukkit start + // private final Map o; + // private final Map p; + // CraftBukkit end + public IPlayerFileData playerFileData; + //private boolean hasWhitelist; // Paper - moved to whitelist object so not duplicated + protected int maxPlayers; + private int s; + private EnumGamemode t; + private boolean u; + private int v; + + // CraftBukkit start + private CraftServer cserver; + private final Map playersByName = new java.util.HashMap<>(); + @Nullable String collideRuleTeamName; // Paper - Team name used for collideRule + + public PlayerList(MinecraftServer minecraftserver) { + this.cserver = minecraftserver.server = new CraftServer(minecraftserver, this); + minecraftserver.console = new com.destroystokyo.paper.console.TerminalConsoleCommandSender(); // Paper + // CraftBukkit end + + this.k = new GameProfileBanList(PlayerList.a); + this.l = new IpBanList(PlayerList.b); + this.operators = new OpList(PlayerList.c); + this.whitelist = new WhiteList(PlayerList.d); + // CraftBukkit start + // this.o = Maps.newHashMap(); + // this.p = Maps.newHashMap(); + // CraftBukkit end + this.server = minecraftserver; + this.getProfileBans().a(true); + this.getIPBans().a(true); + this.maxPlayers = 8; + } + + public void a(NetworkManager networkmanager, EntityPlayer entityplayer) { + entityplayer.loginTime = System.currentTimeMillis(); // Paper + GameProfile gameprofile = entityplayer.getProfile(); + UserCache usercache = this.server.getUserCache(); + GameProfile gameprofile1 = usercache.a(gameprofile.getId()); + String s = gameprofile1 == null ? gameprofile.getName() : gameprofile1.getName(); + + usercache.a(gameprofile); + NBTTagCompound nbttagcompound = this.a(entityplayer); + // CraftBukkit start - Better rename detection + if (nbttagcompound != null && nbttagcompound.hasKey("bukkit")) { + NBTTagCompound bukkit = nbttagcompound.getCompound("bukkit"); + s = bukkit.hasKeyOfType("lastKnownName", 8) ? bukkit.getString("lastKnownName") : s; + } + // CraftBukkit end + + // Paper start - support PlayerInitialSpawnEvent + Location originalLoc = new Location(entityplayer.world.getWorld(), entityplayer.locX, entityplayer.locY, entityplayer.locZ, entityplayer.yaw, entityplayer.pitch); + com.destroystokyo.paper.event.player.PlayerInitialSpawnEvent event = new com.destroystokyo.paper.event.player.PlayerInitialSpawnEvent(entityplayer.getBukkitEntity(), originalLoc); + this.server.server.getPluginManager().callEvent(event); + + Location newLoc = event.getSpawnLocation(); + entityplayer.world = ((CraftWorld) newLoc.getWorld()).getHandle(); + entityplayer.locX = newLoc.getX(); + entityplayer.locY = newLoc.getY(); + entityplayer.locZ = newLoc.getZ(); + entityplayer.yaw = newLoc.getYaw(); + entityplayer.pitch = newLoc.getPitch(); + entityplayer.dimension = ((CraftWorld) newLoc.getWorld()).getHandle().dimension; + // Paper end + + entityplayer.spawnIn(this.server.getWorldServer(entityplayer.dimension)); + entityplayer.playerInteractManager.a((WorldServer) entityplayer.world); + String s1 = "local"; + + if (networkmanager.getSocketAddress() != null) { + s1 = networkmanager.getSocketAddress().toString(); + } + + // Spigot start - spawn location event + Player bukkitPlayer = entityplayer.getBukkitEntity(); + PlayerSpawnLocationEvent ev = new PlayerSpawnLocationEvent(bukkitPlayer, bukkitPlayer.getLocation()); + Bukkit.getPluginManager().callEvent(ev); + + Location loc = ev.getSpawnLocation(); + WorldServer world = ((CraftWorld) loc.getWorld()).getHandle(); + + entityplayer.spawnIn(world); + entityplayer.setPosition(loc.getX(), loc.getY(), loc.getZ()); + entityplayer.setYawPitch(loc.getYaw(), loc.getPitch()); + // Spigot end + + // CraftBukkit - Moved message to after join + // PlayerList.f.info("{}[{}] logged in with entity id {} at ({}, {}, {})", entityplayer.getDisplayName().getString(), s1, entityplayer.getId(), entityplayer.locX, entityplayer.locY, entityplayer.locZ); + WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension); + WorldData worlddata = worldserver.getWorldData(); + + this.a(entityplayer, (EntityPlayer) null, worldserver); + PlayerConnection playerconnection = new PlayerConnection(this.server, networkmanager, entityplayer); + + playerconnection.sendPacket(new PacketPlayOutLogin(entityplayer.getId(), entityplayer.playerInteractManager.getGameMode(), worlddata.isHardcore(), worldserver.worldProvider.getDimensionManager(), worldserver.getDifficulty(), this.getMaxPlayers(), worlddata.getType(), worldserver.getGameRules().getBoolean("reducedDebugInfo"))); + entityplayer.getBukkitEntity().sendSupportedChannels(); // CraftBukkit + playerconnection.sendPacket(new PacketPlayOutCustomPayload(PacketPlayOutCustomPayload.b, (new PacketDataSerializer(Unpooled.buffer())).a(this.getServer().getServerModName()))); + playerconnection.sendPacket(new PacketPlayOutServerDifficulty(worlddata.getDifficulty(), worlddata.isDifficultyLocked())); + playerconnection.sendPacket(new PacketPlayOutAbilities(entityplayer.abilities)); + playerconnection.sendPacket(new PacketPlayOutHeldItemSlot(entityplayer.inventory.itemInHandIndex)); + playerconnection.sendPacket(new PacketPlayOutRecipeUpdate(this.server.getCraftingManager().b())); + playerconnection.sendPacket(new PacketPlayOutTags(this.server.getTagRegistry())); + playerconnection.sendPacket(new PacketPlayOutEntityStatus(entityplayer, (byte) (worldserver.getGameRules().getBoolean("reducedDebugInfo") ? 22 : 23))); // Paper - fix this rule not being initialized on the client + this.f(entityplayer); + entityplayer.getStatisticManager().c(); + entityplayer.B().a(entityplayer); + this.sendScoreboard(worldserver.getScoreboard(), entityplayer); + this.server.at(); + ChatMessage chatmessage; + + if (entityplayer.getProfile().getName().equalsIgnoreCase(s)) { + chatmessage = new ChatMessage("multiplayer.player.joined", new Object[] { entityplayer.getScoreboardDisplayName()}); + } else { + chatmessage = new ChatMessage("multiplayer.player.joined.renamed", new Object[] { entityplayer.getScoreboardDisplayName(), s}); + } + + chatmessage.a(EnumChatFormat.YELLOW); + this.onPlayerJoin(entityplayer, CraftChatMessage.fromComponent(chatmessage, EnumChatFormat.WHITE)); // Paper + // CraftBukkit end + worldserver = server.getWorldServer(entityplayer.dimension); // CraftBukkit - Update in case join event changed it + playerconnection.a(entityplayer.locX, entityplayer.locY, entityplayer.locZ, entityplayer.yaw, entityplayer.pitch); + this.b(entityplayer, worldserver); + if (!this.server.getResourcePack().isEmpty()) { + entityplayer.setResourcePack(this.server.getResourcePack(), this.server.getResourcePackHash()); + } + + Iterator iterator = entityplayer.getEffects().iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + playerconnection.sendPacket(new PacketPlayOutEntityEffect(entityplayer.getId(), mobeffect)); + } + + if (nbttagcompound != null && nbttagcompound.hasKeyOfType("RootVehicle", 10)) { + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("RootVehicle"); + Entity entity = ChunkRegionLoader.a(nbttagcompound1.getCompound("Entity"), worldserver, true); + + if (entity != null) { + UUID uuid = nbttagcompound1.a("Attach"); + Iterator iterator1; + Entity entity1; + + if (entity.getUniqueID().equals(uuid)) { + entityplayer.a(entity, true); + } else { + iterator1 = entity.getAllPassengers().iterator(); + + while (iterator1.hasNext()) { + entity1 = (Entity) iterator1.next(); + if (entity1.getUniqueID().equals(uuid)) { + entityplayer.a(entity1, true); + break; + } + } + } + + if (!entityplayer.isPassenger()) { + PlayerList.f.warn("Couldn't reattach entity to player"); + worldserver.removeEntity(entity); + iterator1 = entity.getAllPassengers().iterator(); + + while (iterator1.hasNext()) { + entity1 = (Entity) iterator1.next(); + worldserver.removeEntity(entity1); + } + } + } + } + + entityplayer.syncInventory(); + // Paper start - Add to collideRule team if needed + final Scoreboard scoreboard = this.getServer().getWorldServer(DimensionManager.OVERWORLD).getScoreboard(); + final ScoreboardTeam collideRuleTeam = scoreboard.getTeam(collideRuleTeamName); + if (this.collideRuleTeamName != null && collideRuleTeam != null && entityplayer.getScoreboardTeam() == null) { + scoreboard.addPlayerToTeam(entityplayer.getName(), collideRuleTeam); + } + // Paper end + // CraftBukkit - Moved from above, added world + PlayerList.f.info("{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", entityplayer.getDisplayName().getString(), s1, entityplayer.getId(), entityplayer.world.worldData.getName(), entityplayer.locX, entityplayer.locY, entityplayer.locZ); + } + + public void sendScoreboard(ScoreboardServer scoreboardserver, EntityPlayer entityplayer) { + Set set = Sets.newHashSet(); + Iterator iterator = scoreboardserver.getTeams().iterator(); + + while (iterator.hasNext()) { + ScoreboardTeam scoreboardteam = (ScoreboardTeam) iterator.next(); + + entityplayer.playerConnection.sendPacket(new PacketPlayOutScoreboardTeam(scoreboardteam, 0)); + } + + for (int i = 0; i < 19; ++i) { + ScoreboardObjective scoreboardobjective = scoreboardserver.getObjectiveForSlot(i); + + if (scoreboardobjective != null && !set.contains(scoreboardobjective)) { + List> list = scoreboardserver.getScoreboardScorePacketsForObjective(scoreboardobjective); + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) { + Packet packet = (Packet) iterator1.next(); + + entityplayer.playerConnection.sendPacket(packet); + } + + set.add(scoreboardobjective); + } + } + + } + + public void setPlayerFileData(WorldServer worldserver) { + if (playerFileData != null) return; // CraftBukkit + this.playerFileData = worldserver.getDataManager().getPlayerFileData(); + worldserver.getWorldBorder().a(new IWorldBorderListener() { + public void a(WorldBorder worldborder, double d0) { + PlayerList.this.sendAll(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_SIZE), worldborder.world); + } + + public void a(WorldBorder worldborder, double d0, double d1, long i) { + PlayerList.this.sendAll(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.LERP_SIZE), worldborder.world); + } + + public void a(WorldBorder worldborder, double d0, double d1) { + PlayerList.this.sendAll(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_CENTER), worldborder.world); + } + + public void a(WorldBorder worldborder, int i) { + PlayerList.this.sendAll(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_WARNING_TIME), worldborder.world); + } + + public void b(WorldBorder worldborder, int i) { + PlayerList.this.sendAll(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_WARNING_BLOCKS), worldborder.world); + } + + public void b(WorldBorder worldborder, double d0) {} + + public void c(WorldBorder worldborder, double d0) {} + }); + } + + public void a(EntityPlayer entityplayer, @Nullable WorldServer worldserver) { + WorldServer worldserver1 = entityplayer.getWorldServer(); + + if (worldserver != null) { + worldserver.getPlayerChunkMap().removePlayer(entityplayer); + } + + worldserver1.getPlayerChunkMap().addPlayer(entityplayer); + worldserver1.getChunkProvider().getChunkAt((int) entityplayer.locX >> 4, (int) entityplayer.locZ >> 4, true, true); + if (worldserver != null) { + CriterionTriggers.v.a(entityplayer, worldserver.worldProvider.getDimensionManager(), worldserver1.worldProvider.getDimensionManager()); + if (worldserver.worldProvider.getDimensionManager() == DimensionManager.NETHER && entityplayer.world.worldProvider.getDimensionManager() == DimensionManager.OVERWORLD && entityplayer.M() != null) { + CriterionTriggers.C.a(entityplayer, entityplayer.M()); + } + } + + } + + public int getFurthestViewableBlock() { + return PlayerChunkMap.getFurthestViewableBlock(this.getViewDistance()); + } + + @Nullable + public NBTTagCompound a(EntityPlayer entityplayer) { + NBTTagCompound nbttagcompound = this.server.getWorldServer(DimensionManager.OVERWORLD).getWorldData().h(); + NBTTagCompound nbttagcompound1; + + if (entityplayer.getDisplayName().getString().equals(this.server.G()) && nbttagcompound != null) { + nbttagcompound1 = nbttagcompound; + entityplayer.f(nbttagcompound); + PlayerList.f.debug("loading single player"); + } else { + nbttagcompound1 = this.playerFileData.load(entityplayer); + } + + return nbttagcompound1; + } + + protected void savePlayerFile(EntityPlayer entityplayer) { + if (!entityplayer.getBukkitEntity().isPersistent()) return; // CraftBukkit + entityplayer.lastSave = MinecraftServer.currentTick; // Paper + this.playerFileData.save(entityplayer); + ServerStatisticManager serverstatisticmanager = (ServerStatisticManager) entityplayer.getStatisticManager(); // CraftBukkit + + if (serverstatisticmanager != null) { + serverstatisticmanager.a(); + } + + AdvancementDataPlayer advancementdataplayer = (AdvancementDataPlayer) entityplayer.getAdvancementData(); // CraftBukkit + + if (advancementdataplayer != null) { + advancementdataplayer.c(); + } + + } + + public void onPlayerJoin(EntityPlayer entityplayer, String joinMessage) { // CraftBukkit added param + this.players.add(entityplayer); + this.playersByName.put(entityplayer.getName().toLowerCase(java.util.Locale.ROOT), entityplayer); // Spigot + this.j.put(entityplayer.getUniqueID(), entityplayer); + // this.sendAll(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, new EntityPlayer[] { entityplayer})); // CraftBukkit - replaced with loop below + WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension); + + // CraftBukkit start + PlayerJoinEvent playerJoinEvent = new PlayerJoinEvent(cserver.getPlayer(entityplayer), joinMessage); + cserver.getPluginManager().callEvent(playerJoinEvent); + + if (!entityplayer.playerConnection.networkManager.isConnected()) { + return; + } + + joinMessage = playerJoinEvent.getJoinMessage(); + + if (joinMessage != null && joinMessage.length() > 0) { + for (IChatBaseComponent line : org.bukkit.craftbukkit.util.CraftChatMessage.fromString(joinMessage)) { + server.getPlayerList().sendAll(new PacketPlayOutChat(line)); + } + } + + ChunkIOExecutor.adjustPoolSize(getPlayerCount()); + // CraftBukkit end + + // CraftBukkit start - sendAll above replaced with this loop + PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, entityplayer); + + for (int i = 0; i < this.players.size(); ++i) { + EntityPlayer entityplayer1 = (EntityPlayer) this.players.get(i); + + if (entityplayer1.getBukkitEntity().canSee(entityplayer.getBukkitEntity())) { + entityplayer1.playerConnection.sendPacket(packet); + } + + if (!entityplayer.getBukkitEntity().canSee(entityplayer1.getBukkitEntity())) { + continue; + } + + entityplayer.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, new EntityPlayer[] { entityplayer1})); + } + entityplayer.sentListPacket = true; + // CraftBukkit end + + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityMetadata(entityplayer.getId(), entityplayer.datawatcher, true)); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn + + // CraftBukkit start - Only add if the player wasn't moved in the event + if (entityplayer.world == worldserver && !worldserver.players.contains(entityplayer)) { + worldserver.addEntity(entityplayer); + this.a(entityplayer, (WorldServer) null); + this.server.getBossBattleCustomData().a(entityplayer); + } + // CraftBukkit end + } + + public void updateChunks(EntityPlayer entityplayer) { + entityplayer.getWorldServer().getPlayerChunkMap().movePlayer(entityplayer); + } + + public String disconnect(EntityPlayer entityplayer) { // CraftBukkit - return string + WorldServer worldserver = entityplayer.getWorldServer(); + + entityplayer.a(StatisticList.LEAVE_GAME); + + // CraftBukkit start - Quitting must be before we do final save of data, in case plugins need to modify it + org.bukkit.craftbukkit.event.CraftEventFactory.handleInventoryCloseEvent(entityplayer, org.bukkit.event.inventory.InventoryCloseEvent.Reason.DISCONNECT); // Paper + + PlayerQuitEvent playerQuitEvent = new PlayerQuitEvent(cserver.getPlayer(entityplayer), "\u00A7e" + entityplayer.getName() + " left the game"); + cserver.getPluginManager().callEvent(playerQuitEvent); + entityplayer.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage()); + + entityplayer.playerTick();// SPIGOT-924 + // CraftBukkit end + + // Paper start - Remove from collideRule team if needed + if (this.collideRuleTeamName != null) { + final Scoreboard scoreBoard = this.server.getWorldServer(DimensionManager.OVERWORLD).getScoreboard(); + final ScoreboardTeam team = scoreBoard.getTeam(this.collideRuleTeamName); + if (entityplayer.getScoreboardTeam() == team && team != null) { + scoreBoard.removePlayerFromTeam(entityplayer.getName(), team); + } + } + // Paper end + + this.savePlayerFile(entityplayer); + if (entityplayer.isPassenger()) { + Entity entity = entityplayer.getRootVehicle(); + + if (entity.bR()) { + PlayerList.f.debug("Removing player mount"); + entityplayer.stopRiding(); + worldserver.removeEntity(entity); + Iterator iterator = entity.getAllPassengers().iterator(); + + while (iterator.hasNext()) { + Entity entity1 = (Entity) iterator.next(); + + worldserver.removeEntity(entity1); + } + + worldserver.getChunkAt(entityplayer.chunkX, entityplayer.chunkZ).markDirty(); + } + } + + worldserver.kill(entityplayer); + worldserver.getPlayerChunkMap().removePlayer(entityplayer); + entityplayer.getAdvancementData().a(); + this.players.remove(entityplayer); + this.playersByName.remove(entityplayer.getName().toLowerCase(java.util.Locale.ROOT)); // Spigot + this.server.getBossBattleCustomData().b(entityplayer); + UUID uuid = entityplayer.getUniqueID(); + EntityPlayer entityplayer1 = (EntityPlayer) this.j.get(uuid); + + if (entityplayer1 == entityplayer) { + this.j.remove(uuid); + // CraftBukkit start + // this.o.remove(uuid); + // this.p.remove(uuid); + // CraftBukkit end + } + + // CraftBukkit start + // this.sendAll(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER, new EntityPlayer[] { entityplayer})); + PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, entityplayer); + for (int i = 0; i < players.size(); i++) { + EntityPlayer entityplayer2 = (EntityPlayer) this.players.get(i); + + if (entityplayer2.getBukkitEntity().canSee(entityplayer.getBukkitEntity())) { + entityplayer2.playerConnection.sendPacket(packet); + } else { + entityplayer2.getBukkitEntity().removeDisconnectingPlayer(entityplayer.getBukkitEntity()); + } + } + // This removes the scoreboard (and player reference) for the specific player in the manager + cserver.getScoreboardManager().removePlayer(entityplayer.getBukkitEntity()); + // CraftBukkit end + + ChunkIOExecutor.adjustPoolSize(this.getPlayerCount()); // CraftBukkit + + return playerQuitEvent.getQuitMessage(); // CraftBukkit + } + + // CraftBukkit start - Whole method, SocketAddress to LoginListener, added hostname to signature, return EntityPlayer + public EntityPlayer attemptLogin(LoginListener loginlistener, GameProfile gameprofile, String hostname) { + ChatMessage chatmessage; + + // Moved from processLogin + UUID uuid = EntityHuman.a(gameprofile); + List list = Lists.newArrayList(); + + EntityPlayer entityplayer; + + for (int i = 0; i < this.players.size(); ++i) { + entityplayer = (EntityPlayer) this.players.get(i); + if (entityplayer.getUniqueID().equals(uuid)) { + list.add(entityplayer); + } + } + + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + entityplayer = (EntityPlayer) iterator.next(); + savePlayerFile(entityplayer); // CraftBukkit - Force the player's inventory to be saved + entityplayer.playerConnection.disconnect(new ChatMessage("multiplayer.disconnect.duplicate_login", new Object[0])); + } + + // Instead of kicking then returning, we need to store the kick reason + // in the event, check with plugins to see if it's ok, and THEN kick + // depending on the outcome. + SocketAddress socketaddress = loginlistener.networkManager.getSocketAddress(); + + EntityPlayer entity = new EntityPlayer(this.server, this.server.getWorldServer(DimensionManager.OVERWORLD), gameprofile, new PlayerInteractManager(this.server.getWorldServer(DimensionManager.OVERWORLD))); + Player player = entity.getBukkitEntity(); + PlayerLoginEvent event = new PlayerLoginEvent(player, hostname, ((java.net.InetSocketAddress) socketaddress).getAddress(), ((java.net.InetSocketAddress) loginlistener.networkManager.getRawAddress()).getAddress()); + + if (getProfileBans().isBanned(gameprofile) && !getProfileBans().get(gameprofile).hasExpired()) { + GameProfileBanEntry gameprofilebanentry = (GameProfileBanEntry) this.k.get(gameprofile); + + chatmessage = new ChatMessage("multiplayer.disconnect.banned.reason", new Object[] { gameprofilebanentry.getReason()}); + if (gameprofilebanentry.getExpires() != null) { + chatmessage.addSibling(new ChatMessage("multiplayer.disconnect.banned.expiration", new Object[] { PlayerList.g.format(gameprofilebanentry.getExpires())})); + } + + // return chatmessage; + if (!gameprofilebanentry.hasExpired()) event.disallow(PlayerLoginEvent.Result.KICK_BANNED, CraftChatMessage.fromComponent(chatmessage)); // Spigot + } else if (!this.isWhitelisted(gameprofile, event)) { // Paper + chatmessage = new ChatMessage("multiplayer.disconnect.not_whitelisted", new Object[0]); + //event.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, org.spigotmc.SpigotConfig.whitelistMessage); // Spigot // Paper - moved to isWhitelisted + } else if (getIPBans().isBanned(socketaddress) && !getIPBans().get(socketaddress).hasExpired()) { + IpBanEntry ipbanentry = this.l.get(socketaddress); + + chatmessage = new ChatMessage("multiplayer.disconnect.banned_ip.reason", new Object[] { ipbanentry.getReason()}); + if (ipbanentry.getExpires() != null) { + chatmessage.addSibling(new ChatMessage("multiplayer.disconnect.banned_ip.expiration", new Object[] { PlayerList.g.format(ipbanentry.getExpires())})); + } + + // return chatmessage; + event.disallow(PlayerLoginEvent.Result.KICK_BANNED, CraftChatMessage.fromComponent(chatmessage)); + } else { + // return this.players.size() >= this.maxPlayers && !this.f(gameprofile) ? new ChatMessage("multiplayer.disconnect.server_full", new Object[0]) : null; + if (this.players.size() >= this.maxPlayers && !this.f(gameprofile)) { + event.disallow(PlayerLoginEvent.Result.KICK_FULL, org.spigotmc.SpigotConfig.serverFullMessage); // Spigot + } + } + + cserver.getPluginManager().callEvent(event); + if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) { + loginlistener.disconnect(event.getKickMessage()); + return null; + } + return entity; + } + + public EntityPlayer processLogin(GameProfile gameprofile, EntityPlayer player) { // CraftBukkit - added EntityPlayer + /* CraftBukkit startMoved up + UUID uuid = EntityHuman.a(gameprofile); + List list = Lists.newArrayList(); + + for (int i = 0; i < this.players.size(); ++i) { + EntityPlayer entityplayer = (EntityPlayer) this.players.get(i); + + if (entityplayer.getUniqueID().equals(uuid)) { + list.add(entityplayer); + } + } + + EntityPlayer entityplayer1 = (EntityPlayer) this.j.get(gameprofile.getId()); + + if (entityplayer1 != null && !list.contains(entityplayer1)) { + list.add(entityplayer1); + } + + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer2 = (EntityPlayer) iterator.next(); + + entityplayer2.playerConnection.disconnect(new ChatMessage("multiplayer.disconnect.duplicate_login", new Object[0])); + } + + Object object; + + if (this.server.L()) { + object = new DemoPlayerInteractManager(this.server.getWorldServer(DimensionManager.OVERWORLD)); + } else { + object = new PlayerInteractManager(this.server.getWorldServer(DimensionManager.OVERWORLD)); + } + + return new EntityPlayer(this.server, this.server.getWorldServer(DimensionManager.OVERWORLD), gameprofile, (PlayerInteractManager) object); + */ + return player; + // CraftBukkit end + } + + // CraftBukkit start + public EntityPlayer moveToWorld(EntityPlayer entityplayer, DimensionManager dimensionmanager, boolean flag) { + return this.moveToWorld(entityplayer, dimensionmanager, flag, null, true); + } + + public EntityPlayer moveToWorld(EntityPlayer entityplayer, DimensionManager dimensionmanager, boolean flag, Location location, boolean avoidSuffocation) { + entityplayer.stopRiding(); // CraftBukkit + entityplayer.getWorldServer().getTracker().untrackPlayer(entityplayer); + // entityplayer.getWorldServer().getTracker().untrackEntity(entityplayer); // CraftBukkit + entityplayer.getWorldServer().getPlayerChunkMap().removePlayer(entityplayer); + this.players.remove(entityplayer); + this.playersByName.remove(entityplayer.getName().toLowerCase(java.util.Locale.ROOT)); // Spigot + this.server.getWorldServer(entityplayer.dimension).removeEntity(entityplayer); + BlockPosition blockposition = entityplayer.getBed(); + boolean flag1 = entityplayer.isRespawnForced(); + + /* CraftBukkit start + entityplayer.dimension = dimensionmanager; + Object object; + + if (this.server.L()) { + object = new DemoPlayerInteractManager(this.server.getWorldServer(entityplayer.dimension)); + } else { + object = new PlayerInteractManager(this.server.getWorldServer(entityplayer.dimension)); + } + + EntityPlayer entityplayer1 = new EntityPlayer(this.server, this.server.getWorldServer(entityplayer.dimension), entityplayer.getProfile(), (PlayerInteractManager) object); + // */ + EntityPlayer entityplayer1 = entityplayer; + org.bukkit.World fromWorld = entityplayer.getBukkitEntity().getWorld(); + entityplayer.viewingCredits = false; + // CraftBukkit end + + entityplayer1.playerConnection = entityplayer.playerConnection; + entityplayer1.copyFrom(entityplayer, flag); + entityplayer1.e(entityplayer.getId()); + entityplayer1.a(entityplayer.getMainHand()); + Iterator iterator = entityplayer.getScoreboardTags().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + entityplayer1.addScoreboardTag(s); + } + + // WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension); // CraftBukkit - handled later + + // this.a(entityplayer1, entityplayer, worldserver); // CraftBukkit - removed + BlockPosition blockposition1; + + // CraftBukkit start - fire PlayerRespawnEvent + if (location == null) { + boolean isBedSpawn = false; + CraftWorld cworld = (CraftWorld) this.server.server.getWorld(entityplayer.spawnWorld); + if (cworld != null && blockposition != null) { + blockposition1 = EntityHuman.getBed(cworld.getHandle(), blockposition, flag1); + if (blockposition1 != null) { + isBedSpawn = true; + location = new Location(cworld, (double) ((float) blockposition1.getX() + 0.5F), (double) ((float) blockposition1.getY() + 0.1F), (double) ((float) blockposition1.getZ() + 0.5F)); + } else { + entityplayer1.setRespawnPosition(null, true); + entityplayer1.playerConnection.sendPacket(new PacketPlayOutGameStateChange(0, 0.0F)); + } + } + + if (location == null) { + cworld = (CraftWorld) this.server.server.getWorlds().get(0); + blockposition = entityplayer1.getSpawnPoint(cworld.getHandle()); + location = new Location(cworld, (double) ((float) blockposition.getX() + 0.5F), (double) ((float) blockposition.getY() + 0.1F), (double) ((float) blockposition.getZ() + 0.5F)); + } + + Player respawnPlayer = cserver.getPlayer(entityplayer1); + PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(respawnPlayer, location, isBedSpawn); + cserver.getPluginManager().callEvent(respawnEvent); + // Spigot Start + if (entityplayer.playerConnection.isDisconnected()) { + return entityplayer; + } + // Spigot End + + location = respawnEvent.getRespawnLocation(); + entityplayer.reset(); + } else { + location.setWorld(server.getWorldServer(dimensionmanager).getWorld()); + } + WorldServer worldserver = ((CraftWorld) location.getWorld()).getHandle(); + entityplayer1.forceSetPositionRotation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + // CraftBukkit end + + worldserver.getChunkProvider().getChunkAt((int) entityplayer1.locX >> 4, (int) entityplayer1.locZ >> 4, true, true); + + while (avoidSuffocation && !worldserver.getCubes(entityplayer1, entityplayer1.getBoundingBox()) && entityplayer1.locY < 256.0D) { + entityplayer1.setPosition(entityplayer1.locX, entityplayer1.locY + 1.0D, entityplayer1.locZ); + } + // CraftBukkit start + // Force the client to refresh their chunk cache + if (fromWorld.getEnvironment() == worldserver.getWorld().getEnvironment()) { + entityplayer1.playerConnection.sendPacket(new PacketPlayOutRespawn(worldserver.worldProvider.getDimensionManager().getDimensionID() >= 0 ? DimensionManager.NETHER : DimensionManager.OVERWORLD, worldserver.getDifficulty(), worldserver.getWorldData().getType(), entityplayer.playerInteractManager.getGameMode())); + } + + entityplayer1.playerConnection.sendPacket(new PacketPlayOutRespawn(worldserver.worldProvider.getDimensionManager(), worldserver.getDifficulty(), worldserver.getWorldData().getType(), entityplayer1.playerInteractManager.getGameMode())); + entityplayer1.spawnIn(worldserver); + entityplayer1.dead = false; + entityplayer1.playerConnection.teleport(new Location(worldserver.getWorld(), entityplayer1.locX, entityplayer1.locY, entityplayer1.locZ, entityplayer1.yaw, entityplayer1.pitch)); + entityplayer1.setSneaking(false); + blockposition1 = worldserver.getSpawn(); + // entityplayer1.playerConnection.a(entityplayer1.locX, entityplayer1.locY, entityplayer1.locZ, entityplayer1.yaw, entityplayer1.pitch); + entityplayer1.playerConnection.sendPacket(new PacketPlayOutSpawnPosition(blockposition1)); + entityplayer1.playerConnection.sendPacket(new PacketPlayOutExperience(entityplayer1.exp, entityplayer1.expTotal, entityplayer1.expLevel)); + this.b(entityplayer1, worldserver); + this.f(entityplayer1); + if (!entityplayer.playerConnection.isDisconnected()) { + worldserver.getPlayerChunkMap().addPlayer(entityplayer1); + worldserver.addEntity(entityplayer1); + this.players.add(entityplayer1); + this.playersByName.put(entityplayer1.getName().toLowerCase(java.util.Locale.ROOT), entityplayer1); // Spigot + this.j.put(entityplayer1.getUniqueID(), entityplayer1); + } + // entityplayer1.syncInventory(); + entityplayer1.setHealth(entityplayer1.getHealth()); + // Added from changeDimension + updateClient(entityplayer); // Update health, etc... + entityplayer.updateAbilities(); + for (Object o1 : entityplayer.getEffects()) { + MobEffect mobEffect = (MobEffect) o1; + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEffect(entityplayer.getId(), mobEffect)); + } + + // Fire advancement trigger + CriterionTriggers.v.a(entityplayer, ((CraftWorld) fromWorld).getHandle().worldProvider.getDimensionManager(), worldserver.worldProvider.getDimensionManager()); + if (((CraftWorld) fromWorld).getHandle().worldProvider.getDimensionManager() == DimensionManager.NETHER && worldserver.worldProvider.getDimensionManager() == DimensionManager.OVERWORLD && entityplayer.M() != null) { + CriterionTriggers.C.a(entityplayer, entityplayer.M()); + } + + // Don't fire on respawn + if (fromWorld != location.getWorld()) { + PlayerChangedWorldEvent event = new PlayerChangedWorldEvent(entityplayer.getBukkitEntity(), fromWorld); + server.server.getPluginManager().callEvent(event); + } + + // Save player file again if they were disconnected + if (entityplayer.playerConnection.isDisconnected()) { + this.savePlayerFile(entityplayer); + } + // CraftBukkit end + return entityplayer1; + } + + // CraftBukkit start - Replaced the standard handling of portals with a more customised method. + public void changeDimension(EntityPlayer entityplayer, DimensionManager dimensionManager, TeleportCause cause) { + WorldServer exitWorld = null; + if (entityplayer.dimension.getDimensionID() < CraftWorld.CUSTOM_DIMENSION_OFFSET) { // plugins must specify exit from custom Bukkit worlds + exitWorld = server.getWorldServer(dimensionManager); + } + + Location enter = entityplayer.getBukkitEntity().getLocation(); + Location exit = null; + boolean useTravelAgent = false; // don't use agent for custom worlds or return from THE_END + if (exitWorld != null) { + if ((cause == TeleportCause.END_PORTAL) && (dimensionManager == DimensionManager.OVERWORLD)) { + // THE_END -> NORMAL; use bed if available, otherwise default spawn + exit = ((org.bukkit.craftbukkit.entity.CraftPlayer) entityplayer.getBukkitEntity()).getBedSpawnLocation(); + if (exit == null || ((CraftWorld) exit.getWorld()).getHandle().dimension != DimensionManager.OVERWORLD) { + BlockPosition randomSpawn = entityplayer.getSpawnPoint(exitWorld); + exit = new Location(exitWorld.getWorld(), randomSpawn.getX(), randomSpawn.getY(), randomSpawn.getZ()); + } else { + exit = exit.add(0.5F, 0.1F, 0.5F); // SPIGOT-3879 + } + } else { + // NORMAL <-> NETHER or NORMAL -> THE_END + exit = this.calculateTarget(enter, exitWorld); + useTravelAgent = true; + } + } + + TravelAgent agent = exit != null ? (TravelAgent) ((CraftWorld) exit.getWorld()).getHandle().getTravelAgent() : org.bukkit.craftbukkit.CraftTravelAgent.DEFAULT; // return arbitrary TA to compensate for implementation dependent plugins + PlayerPortalEvent event = new PlayerPortalEvent(entityplayer.getBukkitEntity(), enter, exit, agent, cause); + event.useTravelAgent(useTravelAgent); + Bukkit.getServer().getPluginManager().callEvent(event); + if (event.isCancelled() || event.getTo() == null) { + return; + } + + exit = event.useTravelAgent() ? event.getPortalTravelAgent().findOrCreate(event.getTo()) : event.getTo(); + if (exit == null) { + return; + } + exitWorld = ((CraftWorld) exit.getWorld()).getHandle(); + + org.bukkit.event.player.PlayerTeleportEvent tpEvent = new org.bukkit.event.player.PlayerTeleportEvent(entityplayer.getBukkitEntity(), enter, exit, cause); + Bukkit.getServer().getPluginManager().callEvent(tpEvent); + if (tpEvent.isCancelled() || tpEvent.getTo() == null) { + return; + } + + Vector velocity = entityplayer.getBukkitEntity().getVelocity(); + exitWorld.getTravelAgent().adjustExit(entityplayer, exit, velocity); + + entityplayer.worldChangeInvuln = true; // CraftBukkit - Set teleport invulnerability only if player changing worlds + this.moveToWorld(entityplayer, exitWorld.dimension, true, exit, false); // Vanilla doesn't check for suffocation when handling portals, so neither should we + if (entityplayer.motX != velocity.getX() || entityplayer.motY != velocity.getY() || entityplayer.motZ != velocity.getZ()) { + entityplayer.getBukkitEntity().setVelocity(velocity); + } + } + + public void f(EntityPlayer entityplayer) { + GameProfile gameprofile = entityplayer.getProfile(); + int i = this.server.a(gameprofile); + + this.a(entityplayer, i); + } + + public void a(EntityPlayer entityplayer, DimensionManager dimensionmanager) { + DimensionManager dimensionmanager1 = entityplayer.dimension; + WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension); + + entityplayer.dimension = dimensionmanager; + WorldServer worldserver1 = this.server.getWorldServer(entityplayer.dimension); + + entityplayer.playerConnection.sendPacket(new PacketPlayOutRespawn(entityplayer.dimension, entityplayer.world.getDifficulty(), entityplayer.world.getWorldData().getType(), entityplayer.playerInteractManager.getGameMode())); + this.f(entityplayer); + worldserver.removeEntity(entityplayer); + entityplayer.dead = false; + this.changeWorld(entityplayer, dimensionmanager1, worldserver, worldserver1); + this.a(entityplayer, worldserver); + entityplayer.playerConnection.a(entityplayer.locX, entityplayer.locY, entityplayer.locZ, entityplayer.yaw, entityplayer.pitch); + entityplayer.playerInteractManager.a(worldserver1); + entityplayer.playerConnection.sendPacket(new PacketPlayOutAbilities(entityplayer.abilities)); + this.b(entityplayer, worldserver1); + this.updateClient(entityplayer); + Iterator iterator = entityplayer.getEffects().iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEffect(entityplayer.getId(), mobeffect)); + } + + } + + public void changeWorld(Entity entity, DimensionManager dimensionmanager, WorldServer worldserver, WorldServer worldserver1) { + // CraftBukkit start - Split into modular functions + Location exit = calculateTarget(entity.getBukkitEntity().getLocation(), worldserver1); + repositionEntity(entity, exit, true); + } + + // Copy of original changeWorld(Entity, int, WorldServer, WorldServer) method with only location calculation logic + public Location calculateTarget(Location enter, World target) { + WorldServer worldserver = ((CraftWorld) enter.getWorld()).getHandle(); + WorldServer worldserver1 = ((CraftWorld) target.getWorld()).getHandle(); + DimensionManager dimensionmanager = worldserver.dimension; + + double y = enter.getY(); + float yaw = enter.getYaw(); + float pitch = enter.getPitch(); + double d0 = enter.getX(); + double d1 = enter.getZ(); + double d2 = 8.0D; + /* + double d0 = entity.locX; + double d1 = entity.locZ; + double d2 = 8.0D; + float f = entity.yaw; + */ + + worldserver.methodProfiler.enter("moving"); + if (worldserver1.dimension == DimensionManager.NETHER) { + d0 = MathHelper.a(d0 / 8.0D, worldserver1.getWorldBorder().b() + 16.0D, worldserver1.getWorldBorder().d() - 16.0D); + d1 = MathHelper.a(d1 / 8.0D, worldserver1.getWorldBorder().c() + 16.0D, worldserver1.getWorldBorder().e() - 16.0D); + /* + entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); + if (entity.isAlive()) { + worldserver.entityJoinedWorld(entity, false); + } + */ + } else if (worldserver1.dimension == DimensionManager.OVERWORLD) { + d0 = MathHelper.a(d0 * 8.0D, worldserver1.getWorldBorder().b() + 16.0D, worldserver1.getWorldBorder().d() - 16.0D); + d1 = MathHelper.a(d1 * 8.0D, worldserver1.getWorldBorder().c() + 16.0D, worldserver1.getWorldBorder().e() - 16.0D); + /* + entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); + if (entity.isAlive()) { + worldserver.entityJoinedWorld(entity, false); + } + */ + } else { + BlockPosition blockposition; + + if (dimensionmanager == DimensionManager.THE_END) { + // use default NORMAL world spawn instead of target + worldserver1 = this.server.getWorldServer(DimensionManager.OVERWORLD); + blockposition = worldserver1.getSpawn(); + } else { + blockposition = worldserver1.getDimensionSpawn(); + } + + d0 = (double) blockposition.getX(); + y = (double) blockposition.getY(); + d1 = (double) blockposition.getZ(); + /* + entity.setPositionRotation(d0, entity.locY, d1, 90.0F, 0.0F); + if (entity.isAlive()) { + worldserver.entityJoinedWorld(entity, false); + } + */ + } + + worldserver.methodProfiler.exit(); + if (dimensionmanager != DimensionManager.THE_END) { + worldserver.methodProfiler.enter("placing"); + d0 = (double) MathHelper.clamp((int) d0, -29999872, 29999872); + d1 = (double) MathHelper.clamp((int) d1, -29999872, 29999872); + /* + if (entity.isAlive()) { + entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); + worldserver1.getTravelAgent().a(entity, f); + worldserver1.addEntity(entity); + worldserver1.entityJoinedWorld(entity, false); + } + */ + + worldserver.methodProfiler.exit(); + } + + // entity.spawnIn(worldserver1); + return new Location(worldserver1.getWorld(), d0, y, d1, yaw, pitch); + } + + // copy of original a(Entity, int, WorldServer, WorldServer) method with only entity repositioning logic + public void repositionEntity(Entity entity, Location exit, boolean portal) { + WorldServer worldserver = (WorldServer) entity.world; + WorldServer worldserver1 = ((CraftWorld) exit.getWorld()).getHandle(); + DimensionManager dimensionmanager = worldserver.dimension; + + /* + double d0 = entity.locX; + double d1 = entity.locZ; + double d2 = 8.0D; + float f = entity.yaw; + */ + + worldserver.methodProfiler.enter("moving"); + entity.setPositionRotation(exit.getX(), exit.getY(), exit.getZ(), exit.getYaw(), exit.getPitch()); + if (entity.isAlive()) { + worldserver.entityJoinedWorld(entity, false); + } + /* + if (entity.dimension == DimensionManager.NETHER) { + d0 = MathHelper.a(d0 / 8.0D, worldserver1.getWorldBorder().b() + 16.0D, worldserver1.getWorldBorder().d() - 16.0D); + d1 = MathHelper.a(d1 / 8.0D, worldserver1.getWorldBorder().c() + 16.0D, worldserver1.getWorldBorder().e() - 16.0D); + entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); + if (entity.isAlive()) { + worldserver.entityJoinedWorld(entity, false); + } + } else if (entity.dimension == DimensionManager.OVERWORLD) { + d0 = MathHelper.a(d0 * 8.0D, worldserver1.getWorldBorder().b() + 16.0D, worldserver1.getWorldBorder().d() - 16.0D); + d1 = MathHelper.a(d1 * 8.0D, worldserver1.getWorldBorder().c() + 16.0D, worldserver1.getWorldBorder().e() - 16.0D); + entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); + if (entity.isAlive()) { + worldserver.entityJoinedWorld(entity, false); + } + } else { + BlockPosition blockposition; + + if (dimensionmanager == DimensionManager.THE_END) { + // use default NORMAL world spawn instead of target + worldserver1 = this.server.worlds.get(0); + blockposition = worldserver1.getSpawn(); + } else { + blockposition = worldserver1.getDimensionSpawn(); + } + + d0 = (double) blockposition.getX(); + entity.locY = (double) blockposition.getY(); + d1 = (double) blockposition.getZ(); + entity.setPositionRotation(d0, entity.locY, d1, 90.0F, 0.0F); + if (entity.isAlive()) { + worldserver.entityJoinedWorld(entity, false); + } + } + */ + + worldserver.methodProfiler.exit(); + if (dimensionmanager != DimensionManager.THE_END) { + worldserver.methodProfiler.enter("placing"); + /* + d0 = (double) MathHelper.clamp((int) d0, -29999872, 29999872); + d1 = (double) MathHelper.clamp((int) d1, -29999872, 29999872); + */ + if (entity.isAlive()) { + // entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); + // worldserver1.getTravelAgent().a(entity, f); + if (portal) { + Vector velocity = entity.getBukkitEntity().getVelocity(); + worldserver1.getTravelAgent().adjustExit(entity, exit, velocity); + entity.setPositionRotation(exit.getX(), exit.getY(), exit.getZ(), exit.getYaw(), exit.getPitch()); + if (entity.motX != velocity.getX() || entity.motY != velocity.getY() || entity.motZ != velocity.getZ()) { + entity.getBukkitEntity().setVelocity(velocity); + } + } + // worldserver1.addEntity(entity); + worldserver1.entityJoinedWorld(entity, false); + } + + worldserver.methodProfiler.exit(); + } + + entity.spawnIn(worldserver1); + // CraftBukkit end + } + + public void tick() { + if (++this.v > 600) { + // CraftBukkit start + for (int i = 0; i < this.players.size(); ++i) { + final EntityPlayer target = (EntityPlayer) this.players.get(i); + + target.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_LATENCY, Iterables.filter(this.players, new Predicate() { + @Override + public boolean apply(EntityPlayer input) { + return target.getBukkitEntity().canSee(input.getBukkitEntity()); + } + }))); + } + // CraftBukkit end + this.v = 0; + } + + } + + public void sendAll(Packet packet) { + for (int i = 0; i < this.players.size(); ++i) { + ((EntityPlayer) this.players.get(i)).playerConnection.sendPacket(packet); + } + + } + + // CraftBukkit start - add a world/entity limited version + public void sendAll(Packet packet, EntityHuman entityhuman) { + for (int i = 0; i < this.players.size(); ++i) { + EntityPlayer entityplayer = this.players.get(i); + if (entityhuman != null && entityhuman instanceof EntityPlayer && !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) { + continue; + } + ((EntityPlayer) this.players.get(i)).playerConnection.sendPacket(packet); + } + } + + public void sendAll(Packet packet, World world) { + for (int i = 0; i < world.players.size(); ++i) { + ((EntityPlayer) world.players.get(i)).playerConnection.sendPacket(packet); + } + + } + // CraftBukkit end + + public void a(Packet packet, DimensionManager dimensionmanager) { + for (int i = 0; i < this.players.size(); ++i) { + EntityPlayer entityplayer = (EntityPlayer) this.players.get(i); + + if (entityplayer.dimension == dimensionmanager) { + entityplayer.playerConnection.sendPacket(packet); + } + } + + } + + public void a(EntityHuman entityhuman, IChatBaseComponent ichatbasecomponent) { + ScoreboardTeamBase scoreboardteambase = entityhuman.getScoreboardTeam(); + + if (scoreboardteambase != null) { + Collection collection = scoreboardteambase.getPlayerNameSet(); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + EntityPlayer entityplayer = this.getPlayer(s); + + if (entityplayer != null && entityplayer != entityhuman) { + entityplayer.sendMessage(ichatbasecomponent); + } + } + + } + } + + public void b(EntityHuman entityhuman, IChatBaseComponent ichatbasecomponent) { + ScoreboardTeamBase scoreboardteambase = entityhuman.getScoreboardTeam(); + + if (scoreboardteambase == null) { + this.sendMessage(ichatbasecomponent); + } else { + for (int i = 0; i < this.players.size(); ++i) { + EntityPlayer entityplayer = (EntityPlayer) this.players.get(i); + + if (entityplayer.getScoreboardTeam() != scoreboardteambase) { + entityplayer.sendMessage(ichatbasecomponent); + } + } + + } + } + + public String[] f() { + String[] astring = new String[this.players.size()]; + + for (int i = 0; i < this.players.size(); ++i) { + astring[i] = ((EntityPlayer) this.players.get(i)).getProfile().getName(); + } + + return astring; + } + + public GameProfileBanList getProfileBans() { + return this.k; + } + + public IpBanList getIPBans() { + return this.l; + } + + public void addOp(GameProfile gameprofile) { + this.operators.add(new OpListEntry(gameprofile, this.server.j(), this.operators.b(gameprofile))); + EntityPlayer entityplayer = this.a(gameprofile.getId()); + + if (entityplayer != null) { + this.f(entityplayer); + } + + } + + public void removeOp(GameProfile gameprofile) { + this.operators.remove(gameprofile); + EntityPlayer entityplayer = this.a(gameprofile.getId()); + + if (entityplayer != null) { + this.f(entityplayer); + } + + } + + private void a(EntityPlayer entityplayer, int i) { + if (entityplayer.playerConnection != null) { + byte b0; + + if (i <= 0) { + b0 = 24; + } else if (i >= 4) { + b0 = 28; + } else { + b0 = (byte) (24 + i); + } + + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityStatus(entityplayer, b0)); + } + + entityplayer.getBukkitEntity().recalculatePermissions(); // CraftBukkit + this.server.getCommandDispatcher().a(entityplayer); + } + + // Paper start + public boolean isWhitelisted(GameProfile gameprofile) { + return isWhitelisted(gameprofile, null); + } + public boolean isWhitelisted(GameProfile gameprofile, org.bukkit.event.player.PlayerLoginEvent loginEvent) { + boolean isOp = this.operators.d(gameprofile); + boolean isWhitelisted = !this.getHasWhitelist() || isOp || this.whitelist.d(gameprofile); + final com.destroystokyo.paper.event.profile.ProfileWhitelistVerifyEvent event; + event = new com.destroystokyo.paper.event.profile.ProfileWhitelistVerifyEvent(MCUtil.toBukkit(gameprofile), this.getHasWhitelist(), isWhitelisted, isOp, org.spigotmc.SpigotConfig.whitelistMessage); + event.callEvent(); + if (!event.isWhitelisted()) { + if (loginEvent != null) { + loginEvent.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, event.getKickMessage() == null ? org.spigotmc.SpigotConfig.whitelistMessage : event.getKickMessage()); + } + return false; + } + return true; + } + // Paper end + + public boolean isOp(GameProfile gameprofile) { + return this.operators.d(gameprofile) || this.server.H() && this.server.getWorldServer(DimensionManager.OVERWORLD).getWorldData().u() && this.server.G().equalsIgnoreCase(gameprofile.getName()) || this.u; + } + + @Nullable + public EntityPlayer getPlayer(String s) { + return this.playersByName.get(s.toLowerCase(java.util.Locale.ROOT)); // Spigot + } + + public void sendPacketNearby(@Nullable EntityHuman entityhuman, double d0, double d1, double d2, double d3, DimensionManager dimensionmanager, Packet packet) { + // Paper start - Use world list instead of server list where preferable + sendPacketNearby(entityhuman, d0, d1, d2, d3, dimensionmanager, null, packet); // Retained for compatibility + } + + public void sendPacketNearby(@Nullable EntityHuman entityhuman, double d0, double d1, double d2, double d3, WorldServer world, Packet packet) { + sendPacketNearby(entityhuman, d0, d1, d2, d3, world.dimension, world, packet); + } + + public void sendPacketNearby(@Nullable EntityHuman entityhuman, double d0, double d1, double d2, double d3, DimensionManager dimensionmanager, @Nullable WorldServer world, Packet packet) { + if (world == null && entityhuman != null && entityhuman.world instanceof WorldServer) { + world = (WorldServer) entityhuman.world; + } + + List players1 = world == null ? players : world.players; + for (int j = 0; j < players1.size(); ++j) { + EntityHuman entity = players1.get(j); + if (!(entity instanceof EntityPlayer)) continue; + EntityPlayer entityplayer = (EntityPlayer) entity; + // Paper end + + // CraftBukkit start - Test if player receiving packet can see the source of the packet + if (entityhuman != null && entityhuman instanceof EntityPlayer && !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) { + continue; + } + // CraftBukkit end + + if (entityplayer != entityhuman && (world != null || entityplayer.dimension == dimensionmanager)) { // Paper + double d4 = d0 - entityplayer.locX; + double d5 = d1 - entityplayer.locY; + double d6 = d2 - entityplayer.locZ; + + if (d4 * d4 + d5 * d5 + d6 * d6 < d3 * d3) { + entityplayer.playerConnection.sendPacket(packet); + } + } + } + + } + + // Paper start + public void savePlayers() { + savePlayers(null); + } + + public void savePlayers(Integer interval) { + MCUtil.ensureMain("Save Players", () -> { // Paper - ensure main + long now = MinecraftServer.currentTick; + MinecraftTimings.savePlayers.startTiming(); // Paper + int numSaved = 0; // Paper + for (int i = 0; i < this.players.size(); ++i) { + EntityPlayer entityplayer = this.players.get(i); + if (interval == null || now - entityplayer.lastSave >= interval) { + this.savePlayerFile(entityplayer); + if (interval != null && ++numSaved <= com.destroystokyo.paper.PaperConfig.maxPlayerAutoSavePerTick) { break; } // Paper + } + } + MinecraftTimings.savePlayers.stopTiming(); // Paper + return null; }); // Paper - ensure main + } + // Paper end + + public WhiteList getWhitelist() { + return this.whitelist; + } + + public String[] getWhitelisted() { + return this.whitelist.getEntries(); + } + + public OpList getOPs() { + return this.operators; + } + + public String[] n() { + return this.operators.getEntries(); + } + + public void reloadWhitelist() {} + + public void b(EntityPlayer entityplayer, WorldServer worldserver) { + WorldBorder worldborder = entityplayer.world.getWorldBorder(); // CraftBukkit + + entityplayer.playerConnection.sendPacket(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.INITIALIZE)); + entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateTime(worldserver.getTime(), worldserver.getDayTime(), worldserver.getGameRules().getBoolean("doDaylightCycle"))); + BlockPosition blockposition = worldserver.getSpawn(); + + entityplayer.playerConnection.sendPacket(new PacketPlayOutSpawnPosition(blockposition)); + if (worldserver.isRaining()) { + // CraftBukkit start - handle player weather + // entityplayer.playerConnection.sendPacket(new PacketPlayOutGameStateChange(1, 0.0F)); + // entityplayer.playerConnection.sendPacket(new PacketPlayOutGameStateChange(7, worldserver.i(1.0F))); + // entityplayer.playerConnection.sendPacket(new PacketPlayOutGameStateChange(8, worldserver.g(1.0F))); + entityplayer.setPlayerWeather(org.bukkit.WeatherType.DOWNFALL, false); + entityplayer.updateWeather(-worldserver.o, worldserver.o, -worldserver.q, worldserver.q); + // CraftBukkit end + } + + } + + public void updateClient(EntityPlayer entityplayer) { + entityplayer.updateInventory(entityplayer.defaultContainer); + // entityplayer.triggerHealthUpdate(); + entityplayer.getBukkitEntity().updateScaledHealth(); // CraftBukkit - Update scaled health on respawn and worldchange + entityplayer.playerConnection.sendPacket(new PacketPlayOutHeldItemSlot(entityplayer.inventory.itemInHandIndex)); + // CraftBukkit start - from GameRules + int i = entityplayer.world.getGameRules().get("reducedDebugInfo").b() ? 22 : 23; + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityStatus(entityplayer, (byte) i)); + // CraftBukkit end + } + + public int getPlayerCount() { + return this.players.size(); + } + + public int getMaxPlayers() { + return this.maxPlayers; + } + + public String[] getSeenPlayers() { + return this.server.getWorldServer(DimensionManager.OVERWORLD).getDataManager().getPlayerFileData().getSeenPlayers(); + } + + public boolean getHasWhitelist() { + return this.whitelist.isEnabled(); // Paper + } + + public void setHasWhitelist(boolean flag) { + this.whitelist.setEnabled(flag); // Paper + } + + public List b(String s) { + List list = Lists.newArrayList(); + Iterator iterator = this.players.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + if (entityplayer.v().equals(s)) { + list.add(entityplayer); + } + } + + return list; + } + + public int getViewDistance() { + return this.s; + } + + public MinecraftServer getServer() { + return this.server; + } + + public NBTTagCompound t() { + return null; + } + + private void a(EntityPlayer entityplayer, EntityPlayer entityplayer1, GeneratorAccess generatoraccess) { + if (entityplayer1 != null) { + entityplayer.playerInteractManager.setGameMode(entityplayer1.playerInteractManager.getGameMode()); + } else if (this.t != null) { + entityplayer.playerInteractManager.setGameMode(this.t); + } + + entityplayer.playerInteractManager.b(generatoraccess.getWorldData().getGameType()); + } + + // Paper start - Extract method to allow for restarting flag + public void u() { + u(false); + } + + public void u(boolean isRestarting) { + // CraftBukkit start - disconnect safely + for (EntityPlayer player : this.players) { + player.playerConnection.disconnect(!isRestarting ? this.server.server.getShutdownMessage() : org.spigotmc.SpigotConfig.restartMessage); // CraftBukkit - add custom shutdown message // Paper - add isRestarting flag + } + // CraftBukkit end + // Paper start - Remove collideRule team if it exists + if (this.collideRuleTeamName != null) { + final Scoreboard scoreboard = this.getServer().getWorldServer(DimensionManager.OVERWORLD).getScoreboard(); + final ScoreboardTeam team = scoreboard.getTeam(this.collideRuleTeamName); + if (team != null) scoreboard.removeTeam(team); + } + // Paper end + } + // Paper end + + // CraftBukkit start + public void sendMessage(IChatBaseComponent[] iChatBaseComponents) { + for (IChatBaseComponent component : iChatBaseComponents) { + sendMessage(component, true); + } + } + // CraftBukkit end + + public void sendMessage(IChatBaseComponent ichatbasecomponent, boolean flag) { + this.server.sendMessage(ichatbasecomponent); + ChatMessageType chatmessagetype = flag ? ChatMessageType.SYSTEM : ChatMessageType.CHAT; + + // CraftBukkit start - we run this through our processor first so we can get web links etc + this.sendAll(new PacketPlayOutChat(CraftChatMessage.fixComponent(ichatbasecomponent), chatmessagetype)); + // CraftBukkit end + } + + public void sendMessage(IChatBaseComponent ichatbasecomponent) { + this.sendMessage(ichatbasecomponent, true); + } + + public ServerStatisticManager getStatisticManager(EntityPlayer entityhuman) { + UUID uuid = entityhuman.getUniqueID(); + ServerStatisticManager serverstatisticmanager = uuid == null ? null : (ServerStatisticManager) entityhuman.getStatisticManager(); + // CraftBukkit end + + if (serverstatisticmanager == null) { + File file = new File(this.server.getWorldServer(DimensionManager.OVERWORLD).getDataManager().getDirectory(), "stats"); + File file1 = new File(file, uuid + ".json"); + + if (!file1.exists()) { + File file2 = new File(file, entityhuman.getDisplayName().getString() + ".json"); + + if (file2.exists() && file2.isFile()) { + file2.renameTo(file1); + } + } + + serverstatisticmanager = new ServerStatisticManager(this.server, file1); + // this.o.put(uuid, serverstatisticmanager); // CraftBukkit + } + + return serverstatisticmanager; + } + + public AdvancementDataPlayer h(EntityPlayer entityplayer) { + UUID uuid = entityplayer.getUniqueID(); + AdvancementDataPlayer advancementdataplayer = (AdvancementDataPlayer) entityplayer.getAdvancementData(); // CraftBukkit + + if (advancementdataplayer == null) { + File file = new File(this.server.getWorldServer(DimensionManager.OVERWORLD).getDataManager().getDirectory(), "advancements"); + File file1 = new File(file, uuid + ".json"); + + advancementdataplayer = new AdvancementDataPlayer(this.server, file1, entityplayer); + // this.p.put(uuid, advancementdataplayer); // CraftBukkit + } + + advancementdataplayer.a(entityplayer); + return advancementdataplayer; + } + + public void a(int i) { + this.s = i; + Iterator iterator = this.server.getWorlds().iterator(); + + while (iterator.hasNext()) { + WorldServer worldserver = (WorldServer) iterator.next(); + + if (worldserver != null) { + worldserver.getPlayerChunkMap().a(i); + worldserver.getTracker().a(i); + } + } + + } + + public List v() { + return this.players; + } + + @Nullable + public EntityPlayer a(UUID uuid) { + return (EntityPlayer) this.j.get(uuid); + } + + public boolean f(GameProfile gameprofile) { + return false; + } + + public void reload() { + // CraftBukkit start + /*Iterator iterator = this.p.values().iterator(); + + while (iterator.hasNext()) { + AdvancementDataPlayer advancementdataplayer = (AdvancementDataPlayer) iterator.next(); + + advancementdataplayer.b(); + }*/ + + for (EntityPlayer player : players) { + player.getAdvancementData().b(); + player.getAdvancementData().b(player); // CraftBukkit - trigger immediate flush of advancements + } + // CraftBukkit end + + this.sendAll(new PacketPlayOutTags(this.server.getTagRegistry())); + PacketPlayOutRecipeUpdate packetplayoutrecipeupdate = new PacketPlayOutRecipeUpdate(this.server.getCraftingManager().b()); + Iterator iterator1 = this.players.iterator(); + + while (iterator1.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator1.next(); + + entityplayer.playerConnection.sendPacket(packetplayoutrecipeupdate); + entityplayer.B().a(entityplayer); + } + + } + + public boolean x() { + return this.u; + } +} diff --git a/src/main/java/net/minecraft/server/PortalTravelAgent.java b/src/main/java/net/minecraft/server/PortalTravelAgent.java new file mode 100644 index 000000000000..d30a8a6bdda3 --- /dev/null +++ b/src/main/java/net/minecraft/server/PortalTravelAgent.java @@ -0,0 +1,524 @@ +package net.minecraft.server; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectIterator; +import java.util.Random; +// CraftBukkit start +import org.bukkit.Location; +import org.bukkit.event.entity.EntityPortalExitEvent; +import org.bukkit.util.Vector; +// CraftBukkit end + +public class PortalTravelAgent { + + private static final BlockPortal a = (BlockPortal) Blocks.NETHER_PORTAL; + public final WorldServer world; // Paper - private -> public + private final Random c; + private final Long2ObjectMap d = new Long2ObjectOpenHashMap(4096); + + public PortalTravelAgent(WorldServer worldserver) { + this.world = worldserver; + this.c = new Random(worldserver.getSeed()); + } + + public void a(Entity entity, float f) { + if (this.world.worldProvider.getDimensionManager() != DimensionManager.THE_END) { + if (!this.b(entity, f)) { + this.a(entity); + this.b(entity, f); + } + } else { + int i = MathHelper.floor(entity.locX); + int j = MathHelper.floor(entity.locY) - 1; + int k = MathHelper.floor(entity.locZ); + // CraftBukkit start - Modularize end portal creation + BlockPosition created = this.createEndPortal(entity.locX, entity.locY, entity.locZ); + entity.setPositionRotation((double) created.getX(), (double) created.getY(), (double) created.getZ(), entity.yaw, 0.0F); + entity.motX = entity.motY = entity.motZ = 0.0D; + } + } + + // Split out from original a(Entity, double, double, double, float) method in order to enable being called from createPortal + private BlockPosition createEndPortal(double x, double y, double z) { + int i = MathHelper.floor(x); + int j = MathHelper.floor(y) - 1; + int k = MathHelper.floor(z); + // CraftBukkit end + byte b0 = 1; + byte b1 = 0; + + java.util.Collection bukkitBlocks = new java.util.HashSet<>(); // Paper + java.util.Map nmsBlocks = new java.util.LinkedHashMap<>(); // Paper + + for (int l = -2; l <= 2; ++l) { + for (int i1 = -2; i1 <= 2; ++i1) { + for (int j1 = -1; j1 < 3; ++j1) { + int k1 = i + i1 * 1 + l * 0; + int l1 = j + j1; + int i2 = k + i1 * 0 - l * 1; + boolean flag2 = j1 < 0; + + // Paper start + BlockPosition pos = new BlockPosition(k1, l1, i2); + nmsBlocks.put(pos, flag2 ? Blocks.OBSIDIAN.getBlockData() : Blocks.AIR.getBlockData()); + bukkitBlocks.add(this.world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ())); + // Paper end + } + } + } + + // Paper start + org.bukkit.event.world.PortalCreateEvent event = new org.bukkit.event.world.PortalCreateEvent(bukkitBlocks, this.world.getWorld(), org.bukkit.event.world.PortalCreateEvent.CreateReason.OBC_DESTINATION); + if (event.callEvent()) { + nmsBlocks.forEach(this.world::setTypeUpdate); + } + // Paper end + + // CraftBukkit start + return new BlockPosition(i, k, k); + } + + // use logic based on creation to verify end portal + private BlockPosition findEndPortal(BlockPosition portal) { + int i = portal.getX(); + int j = portal.getY() - 1; + int k = portal.getZ(); + byte b0 = 1; + byte b1 = 0; + + for (int l = -2; l <= 2; ++l) { + for (int i1 = -2; i1 <= 2; ++i1) { + for (int j1 = -1; j1 < 3; ++j1) { + int k1 = i + i1 * b0 + l * b1; + int l1 = j + j1; + int i2 = k + i1 * b1 - l * b0; + boolean flag = j1 < 0; + + if (this.world.getType(new BlockPosition(k1, l1, i2)).getBlock() != (flag ? Blocks.OBSIDIAN : Blocks.AIR)) { + return null; + } + } + } + } + return new BlockPosition(i, j, k); + } + // CraftBukkit end + + public boolean b(Entity entity, float f) { + // CraftBukkit start - Modularize portal search process and entity teleportation + BlockPosition found = this.findPortal(entity.locX, entity.locY, entity.locZ, world.paperConfig.portalSearchRadius); // Paper - Configurable search radius + if (found == null) { + return false; + } + + Location exit = new Location(this.world.getWorld(), found.getX(), found.getY(), found.getZ(), f, entity.pitch); + Vector velocity = entity.getBukkitEntity().getVelocity(); + this.adjustExit(entity, exit, velocity); + entity.setPositionRotation(exit.getX(), exit.getY(), exit.getZ(), exit.getYaw(), exit.getPitch()); + if (entity.motX != velocity.getX() || entity.motY != velocity.getY() || entity.motZ != velocity.getZ()) { + entity.getBukkitEntity().setVelocity(velocity); + } + return true; + } + + public BlockPosition findPortal(double x, double y, double z, int radius) { + if (this.world.getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END) { + return this.findEndPortal(this.world.worldProvider.d()); + } + // CraftBukkit end + double d0 = -1.0D; + // CraftBukkit start + int i = MathHelper.floor(x); + int j = MathHelper.floor(z); + // CraftBukkit end + boolean flag1 = true; + Object object = BlockPosition.ZERO; + long k = ChunkCoordIntPair.a(i, j); + + if (this.d.containsKey(k)) { + PortalTravelAgent.ChunkCoordinatesPortal portaltravelagent_chunkcoordinatesportal = (PortalTravelAgent.ChunkCoordinatesPortal) this.d.get(k); + + d0 = 0.0D; + object = portaltravelagent_chunkcoordinatesportal; + portaltravelagent_chunkcoordinatesportal.b = this.world.getTime(); + flag1 = false; + } else { + BlockPosition blockposition = new BlockPosition(x, y, z); // CraftBukkit + + for (int l = -radius; l <= radius; ++l) { + BlockPosition blockposition1; + + for (int i1 = -radius; i1 <= radius; ++i1) { + for (BlockPosition blockposition2 = blockposition.a(l, this.world.ab() - 1 - blockposition.getY(), i1); blockposition2.getY() >= 0; blockposition2 = blockposition1) { + blockposition1 = blockposition2.down(); + if (this.world.getType(blockposition2).getBlock() == PortalTravelAgent.a) { + for (blockposition1 = blockposition2.down(); this.world.getType(blockposition1).getBlock() == PortalTravelAgent.a; blockposition1 = blockposition1.down()) { + blockposition2 = blockposition1; + } + + double d1 = blockposition2.n(blockposition); + + if (d0 < 0.0D || d1 < d0) { + d0 = d1; + object = blockposition2; + } + } + } + } + } + } + + if (d0 >= 0.0D) { + if (flag1) { + this.d.put(k, new PortalTravelAgent.ChunkCoordinatesPortal((BlockPosition) object, this.world.getTime())); + } + // CraftBukkit start - Move entity teleportation logic into exit + return (BlockPosition) object; + } else { + return null; + } + } + + // Entity repositioning logic split out from original b method and combined with repositioning logic for The End from original a method + public void adjustExit(Entity entity, Location position, Vector velocity) { + Location from = position.clone(); + Vector before = velocity.clone(); + BlockPosition object = new BlockPosition(position.getBlockX(), position.getBlockY(), position.getBlockZ()); + float f = position.getYaw(); + + if (this.world.getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END || entity.getBukkitEntity().getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END || entity.getPortalOffset() == null) { + // entity.setPositionRotation((double) i, (double) j, (double) k, entity.yaw, 0.0F); + // entity.motX = entity.motY = entity.motZ = 0.0D; + position.setPitch(0.0F); + velocity.setX(0); + velocity.setY(0); + velocity.setZ(0); + } else { + // CraftBukkit end + + double d2 = (double) ((BlockPosition) object).getX() + 0.5D; + double d3 = (double) ((BlockPosition) object).getZ() + 0.5D; + ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = PortalTravelAgent.a.c((GeneratorAccess) this.world, (BlockPosition) object); + boolean flag2 = shapedetector_shapedetectorcollection.getFacing().e().c() == EnumDirection.EnumAxisDirection.NEGATIVE; + double d4 = shapedetector_shapedetectorcollection.getFacing().k() == EnumDirection.EnumAxis.X ? (double) shapedetector_shapedetectorcollection.a().getZ() : (double) shapedetector_shapedetectorcollection.a().getX(); + double d5 = (double) (shapedetector_shapedetectorcollection.a().getY() + 1) - entity.getPortalOffset().y * (double) shapedetector_shapedetectorcollection.e(); + + if (flag2) { + ++d4; + } + + // Paper start - Prevent portal suffocation (and therefore getting teleported up in an attempt to avoid it) + // Based on work by CarpetMod - Licensed GPL-3.0 + double offset = (1.0D - entity.getPortalOffset().x) * (double) shapedetector_shapedetectorcollection.getWidth() * (double) shapedetector_shapedetectorcollection.getFacing().rotateY().getAxisDirection().getOffset(); + double adjustedRadius = 1.02 * entity.width / 2; + if (adjustedRadius >= shapedetector_shapedetectorcollection.getWidth() - adjustedRadius) { + // entity wider than portal, place it in the middle + adjustedRadius = (double) shapedetector_shapedetectorcollection.getWidth() / 2 - 0.001; + } + + if (offset >= 0) { + offset = MathHelper.clamp(offset, adjustedRadius, (double) shapedetector_shapedetectorcollection.getWidth() - adjustedRadius); + } else { + offset = MathHelper.clamp(offset, (double) -shapedetector_shapedetectorcollection.getWidth() + adjustedRadius, -adjustedRadius); + } + + if (shapedetector_shapedetectorcollection.getFacing().k() == EnumDirection.EnumAxis.X) { + d3 = d4 + offset; + } else { + d2 = d4 + offset; + } + // Paper end + + float f1 = 0.0F; + float f2 = 0.0F; + float f3 = 0.0F; + float f4 = 0.0F; + + if (shapedetector_shapedetectorcollection.getFacing().opposite() == entity.getPortalDirection()) { + f1 = 1.0F; + f2 = 1.0F; + } else if (shapedetector_shapedetectorcollection.getFacing().opposite() == entity.getPortalDirection().opposite()) { + f1 = -1.0F; + f2 = -1.0F; + } else if (shapedetector_shapedetectorcollection.getFacing().opposite() == entity.getPortalDirection().e()) { + f3 = 1.0F; + f4 = -1.0F; + } else { + f3 = -1.0F; + f4 = 1.0F; + } + + // CraftBukkit start + double d6 = velocity.getX(); + double d7 = velocity.getZ(); + // CraftBukkit end + + // CraftBukkit start - Adjust position and velocity instances instead of entity + velocity.setX(d6 * (double) f1 + d7 * (double) f4); + velocity.setZ(d6 * (double) f3 + d7 * (double) f2); + f = f - (float) (entity.getPortalDirection().opposite().get2DRotationValue() * 90) + (float) (shapedetector_shapedetectorcollection.getFacing().get2DRotationValue() * 90); + // entity.setPositionRotation(d2, d5, d3, entity.yaw, entity.pitch); + position.setX(d2); + position.setY(d5); + position.setZ(d3); + position.setYaw(f); + } + EntityPortalExitEvent event = new EntityPortalExitEvent(entity.getBukkitEntity(), from, position, before, velocity); + this.world.getServer().getPluginManager().callEvent(event); + Location to = event.getTo(); + if (event.isCancelled() || to == null || !entity.isAlive()) { + position.setX(from.getX()); + position.setY(from.getY()); + position.setZ(from.getZ()); + position.setYaw(from.getYaw()); + position.setPitch(from.getPitch()); + velocity.copy(before); + } else { + position.setX(to.getX()); + position.setY(to.getY()); + position.setZ(to.getZ()); + position.setYaw(to.getYaw()); + position.setPitch(to.getPitch()); + velocity.copy(event.getAfter()); // event.getAfter() will never be null, as setAfter() will cause an NPE if null is passed in + } + // CraftBukkit end + } + + public boolean a(Entity entity) { + // CraftBukkit start - Allow for portal creation to be based on coordinates instead of entity + return this.createPortal(entity.locX, entity.locY, entity.locZ, 16); + } + + public boolean createPortal(double x, double y, double z, int b0) { + if (this.world.getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END) { + createEndPortal(x, y, z); + return true; + } + // CraftBukkit end + double d0 = -1.0D; + // CraftBukkit start + int i = MathHelper.floor(x); + int j = MathHelper.floor(y); + int k = MathHelper.floor(z); + // CraftBukkit end + int l = i; + int i1 = j; + int j1 = k; + int k1 = 0; + int l1 = this.c.nextInt(4); + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + double d1; + int i2; + double d2; + int j2; + int k2; + int l2; + int i3; + int j3; + int k3; + int l3; + int i4; + int j4; + int k4; + double d3; + double d4; + + for (i2 = i - 16; i2 <= i + 16; ++i2) { + d1 = (double) i2 + 0.5D - x; // CraftBukkit + + for (j2 = k - 16; j2 <= k + 16; ++j2) { + d2 = (double) j2 + 0.5D - z; // CraftBukkit + + label257: + for (k2 = this.world.ab() - 1; k2 >= 0; --k2) { + if (this.world.isEmpty(blockposition_mutableblockposition.c(i2, k2, j2))) { + while (k2 > 0 && this.world.isEmpty(blockposition_mutableblockposition.c(i2, k2 - 1, j2))) { + --k2; + } + + for (i3 = l1; i3 < l1 + 4; ++i3) { + l2 = i3 % 2; + j3 = 1 - l2; + if (i3 % 4 >= 2) { + l2 = -l2; + j3 = -j3; + } + + for (l3 = 0; l3 < 3; ++l3) { + for (i4 = 0; i4 < 4; ++i4) { + for (k4 = -1; k4 < 4; ++k4) { + k3 = i2 + (i4 - 1) * l2 + l3 * j3; + j4 = k2 + k4; + int l4 = j2 + (i4 - 1) * j3 - l3 * l2; + + blockposition_mutableblockposition.c(k3, j4, l4); + if (k4 < 0 && !this.world.getType(blockposition_mutableblockposition).getMaterial().isBuildable() || k4 >= 0 && !this.world.isEmpty(blockposition_mutableblockposition)) { + continue label257; + } + } + } + } + + d3 = (double) k2 + 0.5D - y; // CraftBukkit + d4 = d1 * d1 + d3 * d3 + d2 * d2; + if (d0 < 0.0D || d4 < d0) { + d0 = d4; + l = i2; + i1 = k2; + j1 = j2; + k1 = i3 % 4; + } + } + } + } + } + } + + if (d0 < 0.0D) { + for (i2 = i - 16; i2 <= i + 16; ++i2) { + d1 = (double) i2 + 0.5D - x; // CraftBukkit + + for (j2 = k - 16; j2 <= k + 16; ++j2) { + d2 = (double) j2 + 0.5D - z; // CraftBukkit + + label205: + for (k2 = this.world.ab() - 1; k2 >= 0; --k2) { + if (this.world.isEmpty(blockposition_mutableblockposition.c(i2, k2, j2))) { + while (k2 > 0 && this.world.isEmpty(blockposition_mutableblockposition.c(i2, k2 - 1, j2))) { + --k2; + } + + for (i3 = l1; i3 < l1 + 2; ++i3) { + l2 = i3 % 2; + j3 = 1 - l2; + + for (l3 = 0; l3 < 4; ++l3) { + for (i4 = -1; i4 < 4; ++i4) { + k4 = i2 + (l3 - 1) * l2; + k3 = k2 + i4; + j4 = j2 + (l3 - 1) * j3; + blockposition_mutableblockposition.c(k4, k3, j4); + if (i4 < 0 && !this.world.getType(blockposition_mutableblockposition).getMaterial().isBuildable() || i4 >= 0 && !this.world.isEmpty(blockposition_mutableblockposition)) { + continue label205; + } + } + } + + d3 = (double) k2 + 0.5D - y; // CraftBukkit + d4 = d1 * d1 + d3 * d3 + d2 * d2; + if (d0 < 0.0D || d4 < d0) { + d0 = d4; + l = i2; + i1 = k2; + j1 = j2; + k1 = i3 % 2; + } + } + } + } + } + } + } + + int i5 = l; + int j5 = i1; + + j2 = j1; + int k5 = k1 % 2; + int l5 = 1 - k5; + + if (k1 % 4 >= 2) { + k5 = -k5; + l5 = -l5; + } + + java.util.Collection bukkitBlocks = new java.util.HashSet<>(); // Paper + java.util.Map nmsBlocks = new java.util.LinkedHashMap<>(); // Paper + + if (d0 < 0.0D) { + i1 = MathHelper.clamp(i1, 70, this.world.ab() - 10); + j5 = i1; + + for (k2 = -1; k2 <= 1; ++k2) { + for (i3 = 1; i3 < 3; ++i3) { + for (l2 = -1; l2 < 3; ++l2) { + j3 = i5 + (i3 - 1) * k5 + k2 * l5; + l3 = j5 + l2; + i4 = j2 + (i3 - 1) * l5 - k2 * k5; + boolean flag1 = l2 < 0; + + // Paper start + BlockPosition pos = new BlockPosition(j3, l3, i4); + nmsBlocks.put(pos, flag1 ? Blocks.OBSIDIAN.getBlockData() : Blocks.AIR.getBlockData()); + bukkitBlocks.add(this.world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ())); + // Paper end + } + } + } + } + + for (k2 = -1; k2 < 3; ++k2) { + for (i3 = -1; i3 < 4; ++i3) { + if (k2 == -1 || k2 == 2 || i3 == -1 || i3 == 3) { + blockposition_mutableblockposition.c(i5 + k2 * k5, j5 + i3, j2 + k2 * l5); + // Paper start + BlockPosition pos = new BlockPosition(blockposition_mutableblockposition.getX(), blockposition_mutableblockposition.getY(), blockposition_mutableblockposition.getZ()); + nmsBlocks.put(pos, Blocks.OBSIDIAN.getBlockData()); + bukkitBlocks.add(this.world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ())); + // Paper end + } + } + } + + IBlockData iblockdata = (IBlockData) PortalTravelAgent.a.getBlockData().set(BlockPortal.AXIS, k5 == 0 ? EnumDirection.EnumAxis.Z : EnumDirection.EnumAxis.X); + + for (i3 = 0; i3 < 2; ++i3) { + for (l2 = 0; l2 < 3; ++l2) { + blockposition_mutableblockposition.c(i5 + i3 * k5, j5 + l2, j2 + i3 * l5); + + // Paper start + BlockPosition pos = new BlockPosition(blockposition_mutableblockposition.getX(), blockposition_mutableblockposition.getY(), blockposition_mutableblockposition.getZ()); + nmsBlocks.put(pos, iblockdata); + bukkitBlocks.add(this.world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ())); + // Paper end + } + } + + // Paper start + org.bukkit.event.world.PortalCreateEvent event = new org.bukkit.event.world.PortalCreateEvent(bukkitBlocks, this.world.getWorld(), org.bukkit.event.world.PortalCreateEvent.CreateReason.OBC_DESTINATION); + if (event.callEvent()) { + nmsBlocks.forEach((pos, data) -> this.world.setTypeAndData(pos, data, 18)); // keep flag in sync with removed call above + } + // Paper end + + return true; + } + + public void a(long i) { + if (i % 100L == 0L) { + long j = i - 300L; + ObjectIterator objectiterator = this.d.values().iterator(); + + while (objectiterator.hasNext()) { + PortalTravelAgent.ChunkCoordinatesPortal portaltravelagent_chunkcoordinatesportal = (PortalTravelAgent.ChunkCoordinatesPortal) objectiterator.next(); + + if (portaltravelagent_chunkcoordinatesportal == null || portaltravelagent_chunkcoordinatesportal.b < j) { + objectiterator.remove(); + } + } + } + + } + + public class ChunkCoordinatesPortal extends BlockPosition { + + public long b; + + public ChunkCoordinatesPortal(BlockPosition blockposition, long i) { + super(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + this.b = i; + } + } +} diff --git a/src/main/java/net/minecraft/server/PotionUtil.java b/src/main/java/net/minecraft/server/PotionUtil.java new file mode 100644 index 000000000000..ea08c5a1c81b --- /dev/null +++ b/src/main/java/net/minecraft/server/PotionUtil.java @@ -0,0 +1,144 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; + +public class PotionUtil { + + public static List getEffects(ItemStack itemstack) { + return a(itemstack.getTag()); + } + + public static List a(PotionRegistry potionregistry, Collection collection) { + List list = Lists.newArrayList(); + + list.addAll(potionregistry.a()); + list.addAll(collection); + return list; + } + + public static List a(@Nullable NBTTagCompound nbttagcompound) { + List list = Lists.newArrayList(); + + list.addAll(c(nbttagcompound).a()); + a(nbttagcompound, (List) list); + return list; + } + + public static List b(ItemStack itemstack) { + return b(itemstack.getTag()); + } + + public static List b(@Nullable NBTTagCompound nbttagcompound) { + List list = Lists.newArrayList(); + + a(nbttagcompound, (List) list); + return list; + } + + public static void a(@Nullable NBTTagCompound nbttagcompound, List list) { + if (nbttagcompound != null && nbttagcompound.hasKeyOfType("CustomPotionEffects", 9)) { + NBTTagList nbttaglist = nbttagcompound.getList("CustomPotionEffects", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.getCompound(i); + MobEffect mobeffect = MobEffect.b(nbttagcompound1); + + if (mobeffect != null) { + list.add(mobeffect); + } + } + } + + } + + public static int c(ItemStack itemstack) { + NBTTagCompound nbttagcompound = itemstack.getTag(); + + return nbttagcompound != null && nbttagcompound.hasKeyOfType("CustomPotionColor", 99) ? nbttagcompound.getInt("CustomPotionColor") : (d(itemstack) == Potions.EMPTY ? 16253176 : a((Collection) getEffects(itemstack))); + } + + public static int a(PotionRegistry potionregistry) { + return potionregistry == Potions.EMPTY ? 16253176 : a((Collection) potionregistry.a()); + } + + public static int a(Collection collection) { + int i = 3694022; + + if (collection.isEmpty()) { + return 3694022; + } else { + float f = 0.0F; + float f1 = 0.0F; + float f2 = 0.0F; + int j = 0; + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + if (mobeffect.isShowParticles()) { + int k = mobeffect.getMobEffect().getColor(); + int l = mobeffect.getAmplifier() + 1; + + f += (float) (l * (k >> 16 & 255)) / 255.0F; + f1 += (float) (l * (k >> 8 & 255)) / 255.0F; + f2 += (float) (l * (k >> 0 & 255)) / 255.0F; + j += l; + } + } + + if (j == 0) { + return 0; + } else { + f = f / (float) j * 255.0F; + f1 = f1 / (float) j * 255.0F; + f2 = f2 / (float) j * 255.0F; + return (int) f << 16 | (int) f1 << 8 | (int) f2; + } + } + } + + public static PotionRegistry d(ItemStack itemstack) { + return c(itemstack.getTag()); + } + + public static PotionRegistry c(@Nullable NBTTagCompound nbttagcompound) { + return nbttagcompound == null ? Potions.EMPTY : PotionRegistry.a(nbttagcompound.getString("Potion")); + } + + public static ItemStack addPotionToItemStack(ItemStack itemstack, PotionRegistry potionregistry) { return a(itemstack, potionregistry); } // Paper - OBFHELPER + public static ItemStack a(ItemStack itemstack, PotionRegistry potionregistry) { + MinecraftKey minecraftkey = IRegistry.POTION.getKey(potionregistry); + + if (potionregistry == Potions.EMPTY) { + itemstack.c("Potion"); + } else { + itemstack.getOrCreateTag().setString("Potion", minecraftkey.toString()); + } + + return itemstack; + } + + public static ItemStack a(ItemStack itemstack, Collection collection) { + if (collection.isEmpty()) { + return itemstack; + } else { + NBTTagCompound nbttagcompound = itemstack.getOrCreateTag(); + NBTTagList nbttaglist = nbttagcompound.getList("CustomPotionEffects", 9); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + nbttaglist.add((NBTBase) mobeffect.a(new NBTTagCompound())); + } + + nbttagcompound.set("CustomPotionEffects", nbttaglist); + return itemstack; + } + } +} diff --git a/src/main/java/net/minecraft/server/PropertyManager.java b/src/main/java/net/minecraft/server/PropertyManager.java new file mode 100644 index 000000000000..5055a4fb8904 --- /dev/null +++ b/src/main/java/net/minecraft/server/PropertyManager.java @@ -0,0 +1,153 @@ +package net.minecraft.server; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import joptsimple.OptionSet; // CraftBukkit + +public class PropertyManager { + + private static final Logger a = LogManager.getLogger(); + public final Properties properties = new Properties(); + private final File file; + + public PropertyManager(File file) { + this.file = file; + if (file.exists()) { + FileInputStream fileinputstream = null; + + try { + fileinputstream = new FileInputStream(file); + this.properties.load(fileinputstream); + } catch (Exception exception) { + PropertyManager.a.warn("Failed to load {}", file, exception); + this.a(); + } finally { + if (fileinputstream != null) { + try { + fileinputstream.close(); + } catch (IOException ioexception) { + ; + } + } + + } + } else { + PropertyManager.a.warn("{} does not exist", file); + this.a(); + } + + } + + // CraftBukkit start + private OptionSet options = null; + + public PropertyManager(final OptionSet options) { + this((File) options.valueOf("config")); + + this.options = options; + } + + private T getOverride(String name, T value) { + if ((this.options != null) && (this.options.has(name)) && !name.equals( "online-mode")) { // Spigot + return (T) this.options.valueOf(name); + } + + return value; + } + // CraftBukkit end + + public void a() { + PropertyManager.a.info("Generating new properties file"); + this.savePropertiesFile(); + } + + public void savePropertiesFile() { + FileOutputStream fileoutputstream = null; + + try { + // CraftBukkit start - Don't attempt writing to file if it's read only + if (this.file.exists() && !this.file.canWrite()) { + return; + } + // CraftBukkit end + + fileoutputstream = new FileOutputStream(this.file); + this.properties.store(fileoutputstream, "Minecraft server properties"); + } catch (Exception exception) { + PropertyManager.a.warn("Failed to save {}", this.file, exception); + this.a(); + } finally { + if (fileoutputstream != null) { + try { + fileoutputstream.close(); + } catch (IOException ioexception) { + ; + } + } + + } + + } + + public File c() { + return this.file; + } + + public String getString(String s, String s1) { + if (!this.properties.containsKey(s)) { + this.properties.setProperty(s, s1); + this.savePropertiesFile(); + this.savePropertiesFile(); + } + + return getOverride(s, this.properties.getProperty(s, s1)); // CraftBukkit + } + + public int getInt(String s, int i) { + try { + return getOverride(s, Integer.parseInt(this.getString(s, "" + i))); // CraftBukkit + } catch (Exception exception) { + this.properties.setProperty(s, "" + i); + this.savePropertiesFile(); + return getOverride(s, i); // CraftBukkit + } + } + + public long getLong(String s, long i) { + try { + return getOverride(s, Long.parseLong(this.getString(s, "" + i))); // CraftBukkit + } catch (Exception exception) { + this.properties.setProperty(s, "" + i); + this.savePropertiesFile(); + return getOverride(s, i); // CraftBukkit + } + } + + public boolean getBoolean(String s, boolean flag) { + try { + return getOverride(s, Boolean.parseBoolean(this.getString(s, "" + flag))); //CraftBukkit + } catch (Exception exception) { + this.properties.setProperty(s, "" + flag); + this.savePropertiesFile(); + return getOverride(s, flag); // CraftBukkit + } + } + + public void setProperty(String s, Object object) { + this.properties.setProperty(s, "" + object); + } + + public boolean a(String s) { + return this.properties.containsKey(s); + } + + public void b(String s) { + this.properties.remove(s); + } +} diff --git a/src/main/java/net/minecraft/server/ProtoChunk.java b/src/main/java/net/minecraft/server/ProtoChunk.java new file mode 100644 index 000000000000..e4c0cc6a33e6 --- /dev/null +++ b/src/main/java/net/minecraft/server/ProtoChunk.java @@ -0,0 +1,514 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSet; +import it.unimi.dsi.fastutil.shorts.ShortArrayList; +import it.unimi.dsi.fastutil.shorts.ShortList; +import java.util.BitSet; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ProtoChunk implements IChunkAccess { + + private static final Logger a = LogManager.getLogger(); + private final ChunkCoordIntPair b; + private boolean c; + private final AtomicInteger d; + private BiomeBase[] e; + private final Map f; + private volatile ChunkStatus g; + private final Map h; + private final Map i; + private final ChunkSection[] j; + private final List k; + private final List l; + private final ShortList[] m; + private final Map n; + private final Map o; + private final ChunkConverter p; + private final ProtoChunkTickList q; + private final ProtoChunkTickList r; + private long s; + private final Map t; + private boolean u; + private GeneratorAccess world; // Paper - Anti-Xray + + // Paper start - Anti-Xray - Support default constructors + public ProtoChunk(int i, int j, ChunkConverter chunkconverter) { + this(new ChunkCoordIntPair(i, j), chunkconverter, null); + } + + public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter) { + this(chunkcoordintpair, chunkconverter, null); + } + + public ProtoChunk(int i, int j, ChunkConverter chunkconverter, GeneratorAccess world) { + this(new ChunkCoordIntPair(i, j), chunkconverter, world); + } + + public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter, GeneratorAccess world) { + this.world = world; + // Paper end + this.d = new AtomicInteger(); + this.f = Maps.newEnumMap(HeightMap.Type.class); + this.g = ChunkStatus.EMPTY; + this.h = Maps.newHashMap(); + this.i = Maps.newHashMap(); + this.j = new ChunkSection[16]; + this.k = Lists.newArrayList(); + this.l = Lists.newArrayList(); + this.m = new ShortList[16]; + this.n = Maps.newHashMap(); + this.o = Maps.newHashMap(); + this.t = Maps.newHashMap(); + this.b = chunkcoordintpair; + this.p = chunkconverter; + this.q = new ProtoChunkTickList<>((block) -> { + return block == null || block.getBlockData().isAir(); + }, IRegistry.BLOCK::getKey, IRegistry.BLOCK::getOrDefault, chunkcoordintpair); + this.r = new ProtoChunkTickList<>((fluidtype) -> { + return fluidtype == null || fluidtype == FluidTypes.EMPTY; + }, IRegistry.FLUID::getKey, IRegistry.FLUID::getOrDefault, chunkcoordintpair); + } + + public static ShortList a(ShortList[] ashortlist, int i) { + if (ashortlist[i] == null) { + ashortlist[i] = new ShortArrayList(); + } + + return ashortlist[i]; + } + + @Nullable + public IBlockData getType(BlockPosition blockposition) { + int i = blockposition.getX(); + int j = blockposition.getY(); + int k = blockposition.getZ(); + + return j >= 0 && j < 256 ? (this.j[j >> 4] == Chunk.a ? Blocks.AIR.getBlockData() : this.j[j >> 4].getType(i & 15, j & 15, k & 15)) : Blocks.VOID_AIR.getBlockData(); + } + + public Fluid getFluid(BlockPosition blockposition) { + int i = blockposition.getX(); + int j = blockposition.getY(); + int k = blockposition.getZ(); + + return j >= 0 && j < 256 && this.j[j >> 4] != Chunk.a ? this.j[j >> 4].b(i & 15, j & 15, k & 15) : FluidTypes.EMPTY.i(); + } + + public List j() { + return this.l; + } + + public ShortList[] p() { + ShortList[] ashortlist = new ShortList[16]; + Iterator iterator = this.l.iterator(); + + while (iterator.hasNext()) { + BlockPosition blockposition = (BlockPosition) iterator.next(); + + a(ashortlist, blockposition.getY() >> 4).add(i(blockposition)); + } + + return ashortlist; + } + + public void a(short short0, int i) { + this.h(a(short0, i, this.b)); + } + + public void h(BlockPosition blockposition) { + this.l.add(blockposition); + } + + @Nullable + public IBlockData setType(BlockPosition blockposition, IBlockData iblockdata, boolean flag) { + int i = blockposition.getX(); + int j = blockposition.getY(); + int k = blockposition.getZ(); + + if (j >= 0 && j < 256) { + if (iblockdata.e() > 0) { + this.l.add(new BlockPosition((i & 15) + this.getPos().d(), j, (k & 15) + this.getPos().e())); + } + + if (this.j[j >> 4] == Chunk.a) { + if (iblockdata.getBlock() == Blocks.AIR) { + return iblockdata; + } + + this.j[j >> 4] = new ChunkSection(j >> 4 << 4, this.x(), this, this.world, true); // Paper - Anti-Xray + } + + IBlockData iblockdata1 = this.j[j >> 4].getType(i & 15, j & 15, k & 15); + + this.j[j >> 4].setType(i & 15, j & 15, k & 15, iblockdata); + if (this.u) { + this.c(HeightMap.Type.MOTION_BLOCKING).a(i & 15, j, k & 15, iblockdata); + this.c(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES).a(i & 15, j, k & 15, iblockdata); + this.c(HeightMap.Type.OCEAN_FLOOR).a(i & 15, j, k & 15, iblockdata); + this.c(HeightMap.Type.WORLD_SURFACE).a(i & 15, j, k & 15, iblockdata); + } + + return iblockdata1; + } else { + return Blocks.VOID_AIR.getBlockData(); + } + } + + public void a(BlockPosition blockposition, TileEntity tileentity) { + tileentity.setPosition(blockposition); + this.h.put(blockposition, tileentity); + } + + public Set q() { + Set set = Sets.newHashSet(this.i.keySet()); + + set.addAll(this.h.keySet()); + return set; + } + + @Nullable + public TileEntity getTileEntity(BlockPosition blockposition) { + return (TileEntity) this.h.get(blockposition); + } + + public Map r() { + return this.h; + } + + public void b(NBTTagCompound nbttagcompound) { + this.k.add(nbttagcompound); + } + + public void a(Entity entity) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + entity.d(nbttagcompound); + this.b(nbttagcompound); + } + + public List s() { + return this.k; + } + + public void a(BiomeBase[] abiomebase) { + this.e = abiomebase; + } + + public BiomeBase[] getBiomeIndex() { + return this.e; + } + + public void a(boolean flag) { + this.c = flag; + } + + public boolean h() { + return this.c; + } + + public ChunkStatus i() { + return this.g; + } + + public void a(ChunkStatus chunkstatus) { + this.g = chunkstatus; + this.a(true); + } + + public void c(String s) { + this.a(ChunkStatus.a(s)); + } + + public ChunkSection[] getSections() { + return this.j; + } + + public int a(EnumSkyBlock enumskyblock, BlockPosition blockposition, boolean flag) { + int i = blockposition.getX() & 15; + int j = blockposition.getY(); + int k = blockposition.getZ() & 15; + int l = j >> 4; + + if (l >= 0 && l <= this.j.length - 1) { + ChunkSection chunksection = this.j[l]; + + return chunksection == Chunk.a ? (this.c(blockposition) ? enumskyblock.c : 0) : (enumskyblock == EnumSkyBlock.SKY ? (!flag ? 0 : chunksection.c(i, j & 15, k)) : (enumskyblock == EnumSkyBlock.BLOCK ? chunksection.d(i, j & 15, k) : enumskyblock.c)); + } else { + return 0; + } + } + + public int a(BlockPosition blockposition, int i, boolean flag) { + int j = blockposition.getX() & 15; + int k = blockposition.getY(); + int l = blockposition.getZ() & 15; + int i1 = k >> 4; + + if (i1 >= 0 && i1 <= this.j.length - 1) { + ChunkSection chunksection = this.j[i1]; + + if (chunksection == Chunk.a) { + return this.x() && i < EnumSkyBlock.SKY.c ? EnumSkyBlock.SKY.c - i : 0; + } else { + int j1 = flag ? chunksection.c(j, k & 15, l) : 0; + + j1 -= i; + int k1 = chunksection.d(j, k & 15, l); + + if (k1 > j1) { + j1 = k1; + } + + return j1; + } + } else { + return 0; + } + } + + public boolean c(BlockPosition blockposition) { + int i = blockposition.getX() & 15; + int j = blockposition.getY(); + int k = blockposition.getZ() & 15; + + return j >= this.a(HeightMap.Type.MOTION_BLOCKING, i, k); + } + + public void a(ChunkSection[] achunksection) { + if (this.j.length != achunksection.length) { + ProtoChunk.a.warn("Could not set level chunk sections, array length is {} instead of {}", achunksection.length, this.j.length); + } else { + System.arraycopy(achunksection, 0, this.j, 0, this.j.length); + } + } + + public Set t() { + return this.f.keySet(); + } + + @Nullable + public HeightMap b(HeightMap.Type heightmap_type) { + return (HeightMap) this.f.get(heightmap_type); + } + + public void a(HeightMap.Type heightmap_type, long[] along) { + this.c(heightmap_type).a(along); + } + + public void a(HeightMap.Type... aheightmap_type) { + HeightMap.Type[] aheightmap_type1 = aheightmap_type; + int i = aheightmap_type.length; + + for (int j = 0; j < i; ++j) { + HeightMap.Type heightmap_type = aheightmap_type1[j]; + + this.c(heightmap_type); + } + + } + + private HeightMap c(HeightMap.Type heightmap_type) { + return (HeightMap) this.f.computeIfAbsent(heightmap_type, (heightmap_type1) -> { + HeightMap heightmap = new HeightMap(this, heightmap_type1); + + heightmap.a(); + return heightmap; + }); + } + + public int a(HeightMap.Type heightmap_type, int i, int j) { + HeightMap heightmap = (HeightMap) this.f.get(heightmap_type); + + if (heightmap == null) { + this.a(heightmap_type); + heightmap = (HeightMap) this.f.get(heightmap_type); + } + + return heightmap.a(i & 15, j & 15) - 1; + } + + public ChunkCoordIntPair getPos() { + return this.b; + } + + public void setLastSaved(long i) {} + + @Nullable + public StructureStart a(String s) { + return (StructureStart) this.n.get(s); + } + + public void a(String s, StructureStart structurestart) { + this.n.put(s, structurestart); + this.c = true; + } + + public Map e() { + return Collections.unmodifiableMap(this.n); + } + + public void a(Map map) { + this.n.clear(); + this.n.putAll(map); + this.c = true; + } + + @Nullable + public LongSet b(String s) { + return (LongSet) this.o.computeIfAbsent(s, (s1) -> { + return new LongOpenHashSet(); + }); + } + + public void a(String s, long i) { + ((LongSet) this.o.computeIfAbsent(s, (s1) -> { + return new LongOpenHashSet(); + })).add(i); + this.c = true; + } + + public Map f() { + return Collections.unmodifiableMap(this.o); + } + + public void b(Map map) { + this.o.clear(); + this.o.putAll(map); + this.c = true; + } + + public void a(EnumSkyBlock enumskyblock, boolean flag, BlockPosition blockposition, int i) { + int j = blockposition.getX() & 15; + int k = blockposition.getY(); + int l = blockposition.getZ() & 15; + int i1 = k >> 4; + + if (i1 < 16 && i1 >= 0) { + if (this.j[i1] == Chunk.a) { + if (i == enumskyblock.c) { + return; + } + + this.j[i1] = new ChunkSection(i1 << 4, this.x(), this, this.world, true); // Paper - Anti-Xray + } + + if (enumskyblock == EnumSkyBlock.SKY) { + if (flag) { + this.j[i1].a(j, k & 15, l, i); + } + } else if (enumskyblock == EnumSkyBlock.BLOCK) { + this.j[i1].b(j, k & 15, l, i); + } + + } + } + + public static short i(BlockPosition blockposition) { + int i = blockposition.getX(); + int j = blockposition.getY(); + int k = blockposition.getZ(); + int l = i & 15; + int i1 = j & 15; + int j1 = k & 15; + + return (short) (l | i1 << 4 | j1 << 8); + } + + public static BlockPosition a(short short0, int i, ChunkCoordIntPair chunkcoordintpair) { + int j = (short0 & 15) + (chunkcoordintpair.x << 4); + int k = (short0 >>> 4 & 15) + (i << 4); + int l = (short0 >>> 8 & 15) + (chunkcoordintpair.z << 4); + + return new BlockPosition(j, k, l); + } + + public void e(BlockPosition blockposition) { + if (!World.k(blockposition)) { + a(this.m, blockposition.getY() >> 4).add(i(blockposition)); + } + + } + + public ShortList[] u() { + return this.m; + } + + public void b(short short0, int i) { + a(this.m, i).add(short0); + } + + public ProtoChunkTickList k() { + return this.q; + } + + public ProtoChunkTickList l() { + return this.r; + } + + private boolean x() { + return true; + } + + public ChunkConverter v() { + return this.p; + } + + public void b(long i) { + this.s = i; + } + + public long m() { + return this.s; + } + + public void a(NBTTagCompound nbttagcompound) { + this.i.put(new BlockPosition(nbttagcompound.getInt("x"), nbttagcompound.getInt("y"), nbttagcompound.getInt("z")), nbttagcompound); + } + + public Map w() { + return Collections.unmodifiableMap(this.i); + } + + public NBTTagCompound g(BlockPosition blockposition) { + return (NBTTagCompound) this.i.get(blockposition); + } + + public void d(BlockPosition blockposition) { + this.h.remove(blockposition); + this.i.remove(blockposition); + } + + public BitSet a(WorldGenStage.Features worldgenstage_features) { + return (BitSet) this.t.computeIfAbsent(worldgenstage_features, (worldgenstage_features1) -> { + return new BitSet(65536); + }); + } + + public void a(WorldGenStage.Features worldgenstage_features, BitSet bitset) { + this.t.put(worldgenstage_features, bitset); + } + + public void a(int i) { + this.d.addAndGet(i); + } + + public boolean ab_() { + return this.d.get() > 0; + } + + public void b(boolean flag) { + this.u = flag; + } +} diff --git a/src/main/java/net/minecraft/server/RandomPositionGenerator.java b/src/main/java/net/minecraft/server/RandomPositionGenerator.java new file mode 100644 index 000000000000..f2c4048c2b69 --- /dev/null +++ b/src/main/java/net/minecraft/server/RandomPositionGenerator.java @@ -0,0 +1,162 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; + +public class RandomPositionGenerator { + + @Nullable + public static Vec3D a(EntityCreature entitycreature, int i, int j) { + return c(entitycreature, i, j, (Vec3D) null); + } + + @Nullable + public static Vec3D b(EntityCreature entitycreature, int i, int j) { + return a(entitycreature, i, j, (Vec3D) null, false, 0.0D); + } + + @Nullable + public static Vec3D a(EntityCreature entitycreature, int i, int j, Vec3D vec3d) { + Vec3D vec3d1 = vec3d.a(entitycreature.locX, entitycreature.locY, entitycreature.locZ); + + return c(entitycreature, i, j, vec3d1); + } + + @Nullable + public static Vec3D a(EntityCreature entitycreature, int i, int j, Vec3D vec3d, double d0) { + Vec3D vec3d1 = vec3d.a(entitycreature.locX, entitycreature.locY, entitycreature.locZ); + + return a(entitycreature, i, j, vec3d1, true, d0); + } + + @Nullable + public static Vec3D b(EntityCreature entitycreature, int i, int j, Vec3D vec3d) { + Vec3D vec3d1 = (new Vec3D(entitycreature.locX, entitycreature.locY, entitycreature.locZ)).d(vec3d); + + return c(entitycreature, i, j, vec3d1); + } + + @Nullable + private static Vec3D c(EntityCreature entitycreature, int i, int j, @Nullable Vec3D vec3d) { + return a(entitycreature, i, j, vec3d, true, 1.5707963705062866D); + } + + @Nullable + private static Vec3D a(EntityCreature entitycreature, int i, int j, @Nullable Vec3D vec3d, boolean flag, double d0) { + NavigationAbstract navigationabstract = entitycreature.getNavigation(); + Random random = entitycreature.getRandom(); + boolean flag1; + + if (entitycreature.dw()) { + double d1 = entitycreature.dt().distanceSquared((double) MathHelper.floor(entitycreature.locX), (double) MathHelper.floor(entitycreature.locY), (double) MathHelper.floor(entitycreature.locZ)) + 4.0D; + double d2 = (double) (entitycreature.du() + (float) i); + + flag1 = d1 < d2 * d2; + } else { + flag1 = false; + } + + boolean flag2 = false; + float f = -99999.0F; + int k = 0; + int l = 0; + int i1 = 0; + + for (int j1 = 0; j1 < 10; ++j1) { + BlockPosition blockposition = a(random, i, j, vec3d, d0); + + if (blockposition != null) { + int k1 = blockposition.getX(); + int l1 = blockposition.getY(); + int i2 = blockposition.getZ(); + BlockPosition blockposition1; + + if (entitycreature.dw() && i > 1) { + blockposition1 = entitycreature.dt(); + if (entitycreature.locX > (double) blockposition1.getX()) { + k1 -= random.nextInt(i / 2); + } else { + k1 += random.nextInt(i / 2); + } + + if (entitycreature.locZ > (double) blockposition1.getZ()) { + i2 -= random.nextInt(i / 2); + } else { + i2 += random.nextInt(i / 2); + } + } + + blockposition1 = new BlockPosition((double) k1 + entitycreature.locX, (double) l1 + entitycreature.locY, (double) i2 + entitycreature.locZ); + if (!entitycreature.world.isLoaded(blockposition1)) continue; // Paper + if ((!flag1 || entitycreature.f(blockposition1)) && navigationabstract.a(blockposition1)) { + if (!flag) { + blockposition1 = a(blockposition1, entitycreature); + if (b(blockposition1, entitycreature)) { + continue; + } + } + + float f1 = entitycreature.a(blockposition1); + + if (f1 > f) { + f = f1; + k = k1; + l = l1; + i1 = i2; + flag2 = true; + } + } + } + } + + if (flag2) { + return new Vec3D((double) k + entitycreature.locX, (double) l + entitycreature.locY, (double) i1 + entitycreature.locZ); + } else { + return null; + } + } + + @Nullable + private static BlockPosition a(Random random, int i, int j, @Nullable Vec3D vec3d, double d0) { + if (vec3d != null && d0 < 3.141592653589793D) { + double d1 = MathHelper.c(vec3d.z, vec3d.x) - 1.5707963705062866D; + double d2 = d1 + (double) (2.0F * random.nextFloat() - 1.0F) * d0; + double d3 = Math.sqrt(random.nextDouble()) * (double) MathHelper.a * (double) i; + double d4 = -d3 * Math.sin(d2); + double d5 = d3 * Math.cos(d2); + + if (Math.abs(d4) <= (double) i && Math.abs(d5) <= (double) i) { + int k = random.nextInt(2 * j + 1) - j; + + return new BlockPosition(d4, (double) k, d5); + } else { + return null; + } + } else { + int l = random.nextInt(2 * i + 1) - i; + int i1 = random.nextInt(2 * j + 1) - j; + int j1 = random.nextInt(2 * i + 1) - i; + + return new BlockPosition(l, i1, j1); + } + } + + private static BlockPosition a(BlockPosition blockposition, EntityCreature entitycreature) { + if (!entitycreature.world.getType(blockposition).getMaterial().isBuildable()) { + return blockposition; + } else { + BlockPosition blockposition1; + + for (blockposition1 = blockposition.up(); blockposition1.getY() < entitycreature.world.getHeight() && entitycreature.world.getType(blockposition1).getMaterial().isBuildable(); blockposition1 = blockposition1.up()) { + ; + } + + return blockposition1; + } + } + + private static boolean b(BlockPosition blockposition, EntityCreature entitycreature) { + Fluid fluid = entitycreature.world.getFluidIfLoaded(blockposition); // Paper + return fluid != null && fluid.a(TagsFluid.WATER); // Paper + } +} diff --git a/src/main/java/net/minecraft/server/RecipeArmorDye.java b/src/main/java/net/minecraft/server/RecipeArmorDye.java new file mode 100644 index 000000000000..d4f74044b807 --- /dev/null +++ b/src/main/java/net/minecraft/server/RecipeArmorDye.java @@ -0,0 +1,125 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.List; + +public class RecipeArmorDye extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + + // CraftBukkit start - Delegate to new parent class with bogus info + public RecipeArmorDye(MinecraftKey minecraftkey) { + super(minecraftkey, "", new ItemStack(Items.LEATHER_HELMET, 0), NonNullList.a(RecipeItemStack.a, RecipeItemStack.a(Items.BONE_MEAL))); + } + // CraftBukkit end + + public boolean a(IInventory iinventory, World world) { + if (!(iinventory instanceof InventoryCrafting)) { + return false; + } else { + ItemStack itemstack = ItemStack.a; + List list = Lists.newArrayList(); + + for (int i = 0; i < iinventory.getSize(); ++i) { + ItemStack itemstack1 = iinventory.getItem(i); + + if (!itemstack1.isEmpty()) { + if (itemstack1.getItem() instanceof ItemArmorColorable) { + if (!itemstack.isEmpty()) { + return false; + } + + itemstack = itemstack1; + } else { + if (!(itemstack1.getItem() instanceof ItemDye)) { + return false; + } + + list.add(itemstack1); + } + } + } + + return !itemstack.isEmpty() && !list.isEmpty(); + } + } + + public ItemStack craftItem(IInventory iinventory) { + ItemStack itemstack = ItemStack.a; + int[] aint = new int[3]; + int i = 0; + int j = 0; + ItemArmorColorable itemarmorcolorable = null; + + int k; + float f; + int l; + + for (k = 0; k < iinventory.getSize(); ++k) { + ItemStack itemstack1 = iinventory.getItem(k); + + if (!itemstack1.isEmpty()) { + Item item = itemstack1.getItem(); + + if (item instanceof ItemArmorColorable) { + itemarmorcolorable = (ItemArmorColorable) item; + if (!itemstack.isEmpty()) { + return ItemStack.a; + } + + itemstack = itemstack1.cloneItemStack(); + itemstack.setCount(1); + if (itemarmorcolorable.e(itemstack1)) { + int i1 = itemarmorcolorable.f(itemstack); + + f = (float) (i1 >> 16 & 255) / 255.0F; + float f1 = (float) (i1 >> 8 & 255) / 255.0F; + float f2 = (float) (i1 & 255) / 255.0F; + + i = (int) ((float) i + Math.max(f, Math.max(f1, f2)) * 255.0F); + aint[0] = (int) ((float) aint[0] + f * 255.0F); + aint[1] = (int) ((float) aint[1] + f1 * 255.0F); + aint[2] = (int) ((float) aint[2] + f2 * 255.0F); + ++j; + } + } else { + if (!(item instanceof ItemDye)) { + return ItemStack.a; + } + + float[] afloat = ((ItemDye) item).d().d(); + int j1 = (int) (afloat[0] * 255.0F); + + l = (int) (afloat[1] * 255.0F); + int k1 = (int) (afloat[2] * 255.0F); + + i += Math.max(j1, Math.max(l, k1)); + aint[0] += j1; + aint[1] += l; + aint[2] += k1; + ++j; + } + } + } + + if (itemarmorcolorable == null) { + return ItemStack.a; + } else { + k = aint[0] / j; + int l1 = aint[1] / j; + int i2 = aint[2] / j; + float f3 = (float) i / (float) j; + + f = (float) Math.max(k, Math.max(l1, i2)); + k = (int) ((float) k * f3 / f); + l1 = (int) ((float) l1 * f3 / f); + i2 = (int) ((float) i2 * f3 / f); + l = (k << 8) + l1; + l = (l << 8) + i2; + itemarmorcolorable.a(itemstack, l); + return itemstack; + } + } + + public RecipeSerializer a() { + return RecipeSerializers.c; + } +} diff --git a/src/main/java/net/minecraft/server/RecipeBannerAdd.java b/src/main/java/net/minecraft/server/RecipeBannerAdd.java new file mode 100644 index 000000000000..29ea581ba36f --- /dev/null +++ b/src/main/java/net/minecraft/server/RecipeBannerAdd.java @@ -0,0 +1,176 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class RecipeBannerAdd extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + + // CraftBukkit start - Delegate to new parent class with bogus info + public RecipeBannerAdd(MinecraftKey minecraftkey) { + super(minecraftkey, "", new ItemStack(Items.WHITE_BANNER, 0), NonNullList.a(RecipeItemStack.a, RecipeItemStack.a(Items.WHITE_BANNER))); + } + // CraftBukkit end + + public boolean a(IInventory iinventory, World world) { + if (!(iinventory instanceof InventoryCrafting)) { + return false; + } else { + boolean flag = false; + + for (int i = 0; i < iinventory.getSize(); ++i) { + ItemStack itemstack = iinventory.getItem(i); + + if (itemstack.getItem() instanceof ItemBanner) { + if (flag) { + return false; + } + + if (TileEntityBanner.a(itemstack) >= 6) { + return false; + } + + flag = true; + } + } + + return flag && this.c(iinventory) != null; + } + } + + public ItemStack craftItem(IInventory iinventory) { + ItemStack itemstack = ItemStack.a; + + for (int i = 0; i < iinventory.getSize(); ++i) { + ItemStack itemstack1 = iinventory.getItem(i); + + if (!itemstack1.isEmpty() && itemstack1.getItem() instanceof ItemBanner) { + itemstack = itemstack1.cloneItemStack(); + itemstack.setCount(1); + break; + } + } + + EnumBannerPatternType enumbannerpatterntype = this.c(iinventory); + + if (enumbannerpatterntype != null) { + EnumColor enumcolor = EnumColor.WHITE; + + for (int j = 0; j < iinventory.getSize(); ++j) { + Item item = iinventory.getItem(j).getItem(); + + if (item instanceof ItemDye) { + enumcolor = ((ItemDye) item).d(); + break; + } + } + + NBTTagCompound nbttagcompound = itemstack.a("BlockEntityTag"); + NBTTagList nbttaglist; + + if (nbttagcompound.hasKeyOfType("Patterns", 9)) { + nbttaglist = nbttagcompound.getList("Patterns", 10); + } else { + nbttaglist = new NBTTagList(); + nbttagcompound.set("Patterns", nbttaglist); + } + + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.setString("Pattern", enumbannerpatterntype.b()); + nbttagcompound1.setInt("Color", enumcolor.getColorIndex()); + nbttaglist.add((NBTBase) nbttagcompound1); + } + + return itemstack; + } + + @Nullable + private EnumBannerPatternType c(IInventory iinventory) { + EnumBannerPatternType[] aenumbannerpatterntype = EnumBannerPatternType.values(); + int i = aenumbannerpatterntype.length; + + for (int j = 0; j < i; ++j) { + EnumBannerPatternType enumbannerpatterntype = aenumbannerpatterntype[j]; + + if (enumbannerpatterntype.d()) { + boolean flag = true; + int k; + + if (enumbannerpatterntype.e()) { + boolean flag1 = false; + boolean flag2 = false; + + for (k = 0; k < iinventory.getSize() && flag; ++k) { + ItemStack itemstack = iinventory.getItem(k); + + if (!itemstack.isEmpty() && !(itemstack.getItem() instanceof ItemBanner)) { + if (itemstack.getItem() instanceof ItemDye) { + if (flag2) { + flag = false; + break; + } + + flag2 = true; + } else { + if (flag1 || !itemstack.doMaterialsMatch(enumbannerpatterntype.f())) { + flag = false; + break; + } + + flag1 = true; + } + } + } + + if (!flag1 || !flag2) { + flag = false; + } + } else if (iinventory.getSize() != enumbannerpatterntype.c().length * enumbannerpatterntype.c()[0].length()) { + flag = false; + } else { + EnumColor enumcolor = null; + + for (int l = 0; l < iinventory.getSize() && flag; ++l) { + k = l / 3; + int i1 = l % 3; + ItemStack itemstack1 = iinventory.getItem(l); + Item item = itemstack1.getItem(); + + if (!itemstack1.isEmpty() && !(item instanceof ItemBanner)) { + if (!(item instanceof ItemDye)) { + flag = false; + break; + } + + EnumColor enumcolor1 = ((ItemDye) item).d(); + + if (enumcolor != null && enumcolor != enumcolor1) { + flag = false; + break; + } + + if (enumbannerpatterntype.c()[k].charAt(i1) == ' ') { + flag = false; + break; + } + + enumcolor = enumcolor1; + } else if (enumbannerpatterntype.c()[k].charAt(i1) != ' ') { + flag = false; + break; + } + } + } + + if (flag) { + return enumbannerpatterntype; + } + } + } + + return null; + } + + public RecipeSerializer a() { + return RecipeSerializers.m; + } +} diff --git a/src/main/java/net/minecraft/server/RecipeBannerDuplicate.java b/src/main/java/net/minecraft/server/RecipeBannerDuplicate.java new file mode 100644 index 000000000000..9e4d82279c27 --- /dev/null +++ b/src/main/java/net/minecraft/server/RecipeBannerDuplicate.java @@ -0,0 +1,93 @@ +package net.minecraft.server; + +public class RecipeBannerDuplicate extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + + // CraftBukkit start - Delegate to new parent class with bogus info + public RecipeBannerDuplicate(MinecraftKey minecraftkey) { + super(minecraftkey, "", new ItemStack(Items.WHITE_BANNER, 0), NonNullList.a(RecipeItemStack.a, RecipeItemStack.a(Items.WHITE_BANNER))); + } + // CraftBukkit end + + public boolean a(IInventory iinventory, World world) { + if (!(iinventory instanceof InventoryCrafting)) { + return false; + } else { + EnumColor enumcolor = null; + ItemStack itemstack = null; + ItemStack itemstack1 = null; + + for (int i = 0; i < iinventory.getSize(); ++i) { + ItemStack itemstack2 = iinventory.getItem(i); + Item item = itemstack2.getItem(); + + if (item instanceof ItemBanner) { + ItemBanner itembanner = (ItemBanner) item; + + if (enumcolor == null) { + enumcolor = itembanner.b(); + } else if (enumcolor != itembanner.b()) { + return false; + } + + boolean flag = TileEntityBanner.a(itemstack2) > 0; + + if (flag) { + if (itemstack != null) { + return false; + } + + itemstack = itemstack2; + } else { + if (itemstack1 != null) { + return false; + } + + itemstack1 = itemstack2; + } + } + } + + return itemstack != null && itemstack1 != null; + } + } + + public ItemStack craftItem(IInventory iinventory) { + for (int i = 0; i < iinventory.getSize(); ++i) { + ItemStack itemstack = iinventory.getItem(i); + + if (!itemstack.isEmpty() && TileEntityBanner.a(itemstack) > 0) { + ItemStack itemstack1 = itemstack.cloneItemStack(); + + itemstack1.setCount(1); + return itemstack1; + } + } + + return ItemStack.a; + } + + public NonNullList b(IInventory iinventory) { + NonNullList nonnulllist = NonNullList.a(iinventory.getSize(), ItemStack.a); + + for (int i = 0; i < nonnulllist.size(); ++i) { + ItemStack itemstack = iinventory.getItem(i); + + if (!itemstack.isEmpty()) { + if (itemstack.getItem().p()) { + nonnulllist.set(i, new ItemStack(itemstack.getItem().o())); + } else if (itemstack.hasTag() && TileEntityBanner.a(itemstack) > 0) { + ItemStack itemstack1 = itemstack.cloneItemStack(); + + itemstack1.setCount(1); + nonnulllist.set(i, itemstack1); + } + } + } + + return nonnulllist; + } + + public RecipeSerializer a() { + return RecipeSerializers.l; + } +} diff --git a/src/main/java/net/minecraft/server/RecipeBookClone.java b/src/main/java/net/minecraft/server/RecipeBookClone.java new file mode 100644 index 000000000000..0069a067b92a --- /dev/null +++ b/src/main/java/net/minecraft/server/RecipeBookClone.java @@ -0,0 +1,101 @@ +package net.minecraft.server; + +public class RecipeBookClone extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + + // CraftBukkit start - Delegate to new parent class with bogus info + public RecipeBookClone(MinecraftKey minecraftkey) { + super(minecraftkey, "", new ItemStack(Items.WRITTEN_BOOK, 0), NonNullList.a(RecipeItemStack.a, RecipeItemStack.a(Items.WRITABLE_BOOK))); + } + // CraftBukkit end + + public boolean a(IInventory iinventory, World world) { + if (!(iinventory instanceof InventoryCrafting)) { + return false; + } else { + int i = 0; + ItemStack itemstack = ItemStack.a; + + for (int j = 0; j < iinventory.getSize(); ++j) { + ItemStack itemstack1 = iinventory.getItem(j); + + if (!itemstack1.isEmpty()) { + if (itemstack1.getItem() == Items.WRITTEN_BOOK) { + if (!itemstack.isEmpty()) { + return false; + } + + itemstack = itemstack1; + } else { + if (itemstack1.getItem() != Items.WRITABLE_BOOK) { + return false; + } + + ++i; + } + } + } + + return !itemstack.isEmpty() && itemstack.hasTag() && i > 0; + } + } + + public ItemStack craftItem(IInventory iinventory) { + int i = 0; + ItemStack itemstack = ItemStack.a; + + for (int j = 0; j < iinventory.getSize(); ++j) { + ItemStack itemstack1 = iinventory.getItem(j); + + if (!itemstack1.isEmpty()) { + if (itemstack1.getItem() == Items.WRITTEN_BOOK) { + if (!itemstack.isEmpty()) { + return ItemStack.a; + } + + itemstack = itemstack1; + } else { + if (itemstack1.getItem() != Items.WRITABLE_BOOK) { + return ItemStack.a; + } + + ++i; + } + } + } + + if (!itemstack.isEmpty() && itemstack.hasTag() && i >= 1 && ItemWrittenBook.e(itemstack) < 2) { + ItemStack itemstack2 = new ItemStack(Items.WRITTEN_BOOK, i); + NBTTagCompound nbttagcompound = itemstack.getTag().clone(); + + nbttagcompound.setInt("generation", ItemWrittenBook.e(itemstack) + 1); + itemstack2.setTag(nbttagcompound); + return itemstack2; + } else { + return ItemStack.a; + } + } + + public NonNullList b(IInventory iinventory) { + NonNullList nonnulllist = NonNullList.a(iinventory.getSize(), ItemStack.a); + + for (int i = 0; i < nonnulllist.size(); ++i) { + ItemStack itemstack = iinventory.getItem(i); + + if (itemstack.getItem().p()) { + nonnulllist.set(i, new ItemStack(itemstack.getItem().o())); + } else if (itemstack.getItem() instanceof ItemWrittenBook) { + ItemStack itemstack1 = itemstack.cloneItemStack(); + + itemstack1.setCount(1); + nonnulllist.set(i, itemstack1); + break; + } + } + + return nonnulllist; + } + + public RecipeSerializer a() { + return RecipeSerializers.d; + } +} diff --git a/src/main/java/net/minecraft/server/RecipeBookServer.java b/src/main/java/net/minecraft/server/RecipeBookServer.java new file mode 100644 index 000000000000..4d9f3d36958c --- /dev/null +++ b/src/main/java/net/minecraft/server/RecipeBookServer.java @@ -0,0 +1,142 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class RecipeBookServer extends RecipeBook { + + private static final Logger g = LogManager.getLogger(); + private final CraftingManager h; + + public RecipeBookServer(CraftingManager craftingmanager) { + this.h = craftingmanager; + } + + public int a(Collection collection, EntityPlayer entityplayer) { + List list = Lists.newArrayList(); + int i = 0; + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + IRecipe irecipe = (IRecipe) iterator.next(); + MinecraftKey minecraftkey = irecipe.getKey(); + + if (!this.a.contains(minecraftkey) && !irecipe.c() && CraftEventFactory.handlePlayerRecipeListUpdateEvent(entityplayer, minecraftkey)) { // CraftBukkit + this.a(minecraftkey); + this.c(minecraftkey); + list.add(minecraftkey); + CriterionTriggers.f.a(entityplayer, irecipe); + ++i; + } + } + + this.a(PacketPlayOutRecipes.Action.ADD, entityplayer, list); + return i; + } + + public int b(Collection collection, EntityPlayer entityplayer) { + List list = Lists.newArrayList(); + int i = 0; + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + IRecipe irecipe = (IRecipe) iterator.next(); + MinecraftKey minecraftkey = irecipe.getKey(); + + if (this.a.contains(minecraftkey)) { + this.b(minecraftkey); + list.add(minecraftkey); + ++i; + } + } + + this.a(PacketPlayOutRecipes.Action.REMOVE, entityplayer, list); + return i; + } + + private void a(PacketPlayOutRecipes.Action packetplayoutrecipes_action, EntityPlayer entityplayer, List list) { + if (entityplayer.playerConnection == null) return; // SPIGOT-4478 during PlayerLoginEvent + entityplayer.playerConnection.sendPacket(new PacketPlayOutRecipes(packetplayoutrecipes_action, list, Collections.emptyList(), this.c, this.d, this.e, this.f)); + } + + public NBTTagCompound e() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setBoolean("isGuiOpen", this.c); + nbttagcompound.setBoolean("isFilteringCraftable", this.d); + nbttagcompound.setBoolean("isFurnaceGuiOpen", this.e); + nbttagcompound.setBoolean("isFurnaceFilteringCraftable", this.f); + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = this.a.iterator(); + + while (iterator.hasNext()) { + MinecraftKey minecraftkey = (MinecraftKey) iterator.next(); + + // Paper start - ignore missing recipes + IRecipe recipe = this.h.a(minecraftkey); + if (recipe == null) continue; + // Paper end + nbttaglist.add((NBTBase) (new NBTTagString(minecraftkey.toString()))); + } + + nbttagcompound.set("recipes", nbttaglist); + NBTTagList nbttaglist1 = new NBTTagList(); + Iterator iterator1 = this.b.iterator(); + + while (iterator1.hasNext()) { + MinecraftKey minecraftkey1 = (MinecraftKey) iterator1.next(); + + // Paper start - ignore missing recipes + IRecipe recipe = this.h.a(minecraftkey1); + if (recipe == null) continue; + // Paper end + nbttaglist1.add((NBTBase) (new NBTTagString(minecraftkey1.toString()))); + } + + nbttagcompound.set("toBeDisplayed", nbttaglist1); + return nbttagcompound; + } + + public void a(NBTTagCompound nbttagcompound) { + this.c = nbttagcompound.getBoolean("isGuiOpen"); + this.d = nbttagcompound.getBoolean("isFilteringCraftable"); + this.e = nbttagcompound.getBoolean("isFurnaceGuiOpen"); + this.f = nbttagcompound.getBoolean("isFurnaceFilteringCraftable"); + NBTTagList nbttaglist = nbttagcompound.getList("recipes", 8); + + for (int i = 0; i < nbttaglist.size(); ++i) { + MinecraftKey minecraftkey = new MinecraftKey(nbttaglist.getString(i)); + IRecipe irecipe = this.h.a(minecraftkey); + + if (irecipe == null) { + RecipeBookServer.g.error("Tried to load unrecognized recipe: {} removed now.", minecraftkey); + } else { + this.a(irecipe); + } + } + + NBTTagList nbttaglist1 = nbttagcompound.getList("toBeDisplayed", 8); + + for (int j = 0; j < nbttaglist1.size(); ++j) { + MinecraftKey minecraftkey1 = new MinecraftKey(nbttaglist1.getString(j)); + IRecipe irecipe1 = this.h.a(minecraftkey1); + + if (irecipe1 == null) { + RecipeBookServer.g.error("Tried to load unrecognized recipe: {} removed now.", minecraftkey1); + } else { + this.f(irecipe1); + } + } + + } + + public void a(EntityPlayer entityplayer) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutRecipes(PacketPlayOutRecipes.Action.INIT, this.a, this.b, this.c, this.d, this.e, this.f)); + } +} diff --git a/src/main/java/net/minecraft/server/RecipeFireworks.java b/src/main/java/net/minecraft/server/RecipeFireworks.java new file mode 100644 index 000000000000..a57f88aec399 --- /dev/null +++ b/src/main/java/net/minecraft/server/RecipeFireworks.java @@ -0,0 +1,84 @@ +package net.minecraft.server; + +public class RecipeFireworks extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + + private static final RecipeItemStack a = RecipeItemStack.a(Items.PAPER); + private static final RecipeItemStack b = RecipeItemStack.a(Items.GUNPOWDER); + private static final RecipeItemStack c = RecipeItemStack.a(Items.FIREWORK_STAR); + + // CraftBukkit start - Delegate to new parent class with bogus info + public RecipeFireworks(MinecraftKey minecraftkey) { + super(minecraftkey, "", new ItemStack(Items.FIREWORK_ROCKET, 0), NonNullList.a(RecipeItemStack.a, RecipeItemStack.a(Items.GUNPOWDER))); + } + // CraftBukkit end + + public boolean a(IInventory iinventory, World world) { + if (!(iinventory instanceof InventoryCrafting)) { + return false; + } else { + boolean flag = false; + int i = 0; + + for (int j = 0; j < iinventory.getSize(); ++j) { + ItemStack itemstack = iinventory.getItem(j); + + if (!itemstack.isEmpty()) { + if (RecipeFireworks.a.test(itemstack)) { + if (flag) { + return false; + } + + flag = true; + } else if (RecipeFireworks.b.test(itemstack)) { + ++i; + if (i > 3) { + return false; + } + } else if (!RecipeFireworks.c.test(itemstack)) { + return false; + } + } + } + + return flag && i >= 1; + } + } + + public ItemStack craftItem(IInventory iinventory) { + ItemStack itemstack = new ItemStack(Items.FIREWORK_ROCKET, 3); + NBTTagCompound nbttagcompound = itemstack.a("Fireworks"); + NBTTagList nbttaglist = new NBTTagList(); + int i = 0; + + for (int j = 0; j < iinventory.getSize(); ++j) { + ItemStack itemstack1 = iinventory.getItem(j); + + if (!itemstack1.isEmpty()) { + if (RecipeFireworks.b.test(itemstack1)) { + ++i; + } else if (RecipeFireworks.c.test(itemstack1)) { + NBTTagCompound nbttagcompound1 = itemstack1.b("Explosion"); + + if (nbttagcompound1 != null) { + nbttaglist.add((NBTBase) nbttagcompound1); + } + } + } + } + + nbttagcompound.setByte("Flight", (byte) i); + if (!nbttaglist.isEmpty()) { + nbttagcompound.set("Explosions", nbttaglist); + } + + return itemstack; + } + + public ItemStack d() { + return new ItemStack(Items.FIREWORK_ROCKET); + } + + public RecipeSerializer a() { + return RecipeSerializers.g; + } +} diff --git a/src/main/java/net/minecraft/server/RecipeFireworksFade.java b/src/main/java/net/minecraft/server/RecipeFireworksFade.java new file mode 100644 index 000000000000..c9af6b055b15 --- /dev/null +++ b/src/main/java/net/minecraft/server/RecipeFireworksFade.java @@ -0,0 +1,74 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.List; + +public class RecipeFireworksFade extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + + private static final RecipeItemStack a = RecipeItemStack.a(Items.FIREWORK_STAR); + + // CraftBukkit start - Delegate to new parent class with bogus info + public RecipeFireworksFade(MinecraftKey minecraftkey) { + super(minecraftkey, "", new ItemStack(Items.FIREWORK_STAR, 0), NonNullList.a(RecipeItemStack.a, RecipeItemStack.a(Items.FIREWORK_STAR, Items.BONE_MEAL))); + } + // CraftBukkit end + + public boolean a(IInventory iinventory, World world) { + if (!(iinventory instanceof InventoryCrafting)) { + return false; + } else { + boolean flag = false; + boolean flag1 = false; + + for (int i = 0; i < iinventory.getSize(); ++i) { + ItemStack itemstack = iinventory.getItem(i); + + if (!itemstack.isEmpty()) { + if (itemstack.getItem() instanceof ItemDye) { + flag = true; + } else { + if (!RecipeFireworksFade.a.test(itemstack)) { + return false; + } + + if (flag1) { + return false; + } + + flag1 = true; + } + } + } + + return flag1 && flag; + } + } + + public ItemStack craftItem(IInventory iinventory) { + List list = Lists.newArrayList(); + ItemStack itemstack = null; + + for (int i = 0; i < iinventory.getSize(); ++i) { + ItemStack itemstack1 = iinventory.getItem(i); + Item item = itemstack1.getItem(); + + if (item instanceof ItemDye) { + list.add(((ItemDye) item).d().f()); + } else if (RecipeFireworksFade.a.test(itemstack1)) { + itemstack = itemstack1.cloneItemStack(); + itemstack.setCount(1); + } + } + + if (itemstack != null && !list.isEmpty()) { + itemstack.a("Explosion").b("FadeColors", (List) list); + return itemstack; + } else { + return ItemStack.a; + } + } + + public RecipeSerializer a() { + return RecipeSerializers.i; + } +} diff --git a/src/main/java/net/minecraft/server/RecipeFireworksStar.java b/src/main/java/net/minecraft/server/RecipeFireworksStar.java new file mode 100644 index 000000000000..3f4d7b30342d --- /dev/null +++ b/src/main/java/net/minecraft/server/RecipeFireworksStar.java @@ -0,0 +1,119 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RecipeFireworksStar extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + + private static final RecipeItemStack a = RecipeItemStack.a(Items.FIRE_CHARGE, Items.FEATHER, Items.GOLD_NUGGET, Items.SKELETON_SKULL, Items.WITHER_SKELETON_SKULL, Items.CREEPER_HEAD, Items.PLAYER_HEAD, Items.DRAGON_HEAD, Items.ZOMBIE_HEAD); + private static final RecipeItemStack b = RecipeItemStack.a(Items.DIAMOND); + private static final RecipeItemStack c = RecipeItemStack.a(Items.GLOWSTONE_DUST); + private static final Map d = (Map) SystemUtils.a(Maps.newHashMap(), (hashmap) -> { // CraftBukkit - decompile error + hashmap.put(Items.FIRE_CHARGE, ItemFireworks.EffectType.LARGE_BALL); + hashmap.put(Items.FEATHER, ItemFireworks.EffectType.BURST); + hashmap.put(Items.GOLD_NUGGET, ItemFireworks.EffectType.STAR); + hashmap.put(Items.SKELETON_SKULL, ItemFireworks.EffectType.CREEPER); + hashmap.put(Items.WITHER_SKELETON_SKULL, ItemFireworks.EffectType.CREEPER); + hashmap.put(Items.CREEPER_HEAD, ItemFireworks.EffectType.CREEPER); + hashmap.put(Items.PLAYER_HEAD, ItemFireworks.EffectType.CREEPER); + hashmap.put(Items.DRAGON_HEAD, ItemFireworks.EffectType.CREEPER); + hashmap.put(Items.ZOMBIE_HEAD, ItemFireworks.EffectType.CREEPER); + }); + private static final RecipeItemStack e = RecipeItemStack.a(Items.GUNPOWDER); + + // CraftBukkit start - Delegate to new parent class with bogus info + public RecipeFireworksStar(MinecraftKey minecraftkey) { + super(minecraftkey, "", new ItemStack(Items.FIREWORK_STAR, 0), NonNullList.a(RecipeItemStack.a, RecipeItemStack.a(Items.GUNPOWDER))); + } + // CraftBukkit end + + public boolean a(IInventory iinventory, World world) { + if (!(iinventory instanceof InventoryCrafting)) { + return false; + } else { + boolean flag = false; + boolean flag1 = false; + boolean flag2 = false; + boolean flag3 = false; + boolean flag4 = false; + + for (int i = 0; i < iinventory.getSize(); ++i) { + ItemStack itemstack = iinventory.getItem(i); + + if (!itemstack.isEmpty()) { + if (RecipeFireworksStar.a.test(itemstack)) { + if (flag2) { + return false; + } + + flag2 = true; + } else if (RecipeFireworksStar.c.test(itemstack)) { + if (flag4) { + return false; + } + + flag4 = true; + } else if (RecipeFireworksStar.b.test(itemstack)) { + if (flag3) { + return false; + } + + flag3 = true; + } else if (RecipeFireworksStar.e.test(itemstack)) { + if (flag) { + return false; + } + + flag = true; + } else { + if (!(itemstack.getItem() instanceof ItemDye)) { + return false; + } + + flag1 = true; + } + } + } + + return flag && flag1; + } + } + + public ItemStack craftItem(IInventory iinventory) { + ItemStack itemstack = new ItemStack(Items.FIREWORK_STAR); + NBTTagCompound nbttagcompound = itemstack.a("Explosion"); + ItemFireworks.EffectType itemfireworks_effecttype = ItemFireworks.EffectType.SMALL_BALL; + List list = Lists.newArrayList(); + + for (int i = 0; i < iinventory.getSize(); ++i) { + ItemStack itemstack1 = iinventory.getItem(i); + + if (!itemstack1.isEmpty()) { + if (RecipeFireworksStar.a.test(itemstack1)) { + itemfireworks_effecttype = (ItemFireworks.EffectType) RecipeFireworksStar.d.get(itemstack1.getItem()); + } else if (RecipeFireworksStar.c.test(itemstack1)) { + nbttagcompound.setBoolean("Flicker", true); + } else if (RecipeFireworksStar.b.test(itemstack1)) { + nbttagcompound.setBoolean("Trail", true); + } else if (itemstack1.getItem() instanceof ItemDye) { + list.add(((ItemDye) itemstack1.getItem()).d().f()); + } + } + } + + nbttagcompound.b("Colors", (List) list); + nbttagcompound.setByte("Type", (byte) itemfireworks_effecttype.a()); + return itemstack; + } + + public ItemStack d() { + return new ItemStack(Items.FIREWORK_STAR); + } + + public RecipeSerializer a() { + return RecipeSerializers.h; + } +} diff --git a/src/main/java/net/minecraft/server/RecipeItemStack.java b/src/main/java/net/minecraft/server/RecipeItemStack.java new file mode 100644 index 000000000000..a4932bec97a4 --- /dev/null +++ b/src/main/java/net/minecraft/server/RecipeItemStack.java @@ -0,0 +1,263 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSyntaxException; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntComparators; +import it.unimi.dsi.fastutil.ints.IntList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import javax.annotation.Nullable; + +public final class RecipeItemStack implements Predicate { + + private static final Predicate b = (recipeitemstack_provider) -> { + return !recipeitemstack_provider.a().stream().allMatch(ItemStack::isEmpty); + }; + public static final RecipeItemStack a = new RecipeItemStack(Stream.empty()); + private final RecipeItemStack.Provider[] c; + public ItemStack[] choices; + private IntList e; + public boolean exact; // CraftBukkit + + public RecipeItemStack(Stream stream) { + this.c = (RecipeItemStack.Provider[]) stream.filter(RecipeItemStack.b).toArray((i) -> { + return new RecipeItemStack.Provider[i]; + }); + } + + public void buildChoices() { + if (this.choices == null) { + this.choices = (ItemStack[]) Arrays.stream(this.c).flatMap((recipeitemstack_provider) -> { + return recipeitemstack_provider.a().stream(); + }).distinct().toArray((i) -> { + return new ItemStack[i]; + }); + } + + } + + public boolean test(@Nullable ItemStack itemstack) { + if (itemstack == null) { + return false; + } else if (this.c.length == 0) { + return itemstack.isEmpty(); + } else { + this.buildChoices(); + ItemStack[] aitemstack = this.choices; + int i = aitemstack.length; + + for (int j = 0; j < i; ++j) { + ItemStack itemstack1 = aitemstack[j]; + + // CraftBukkit start + if (exact) { + if (ItemStack.equals(itemstack, itemstack1)) { + return true; + } + + continue; + } + // CraftBukkit end + if (itemstack1.getItem() == itemstack.getItem()) { + return true; + } + } + + return false; + } + } + + public IntList b() { + if (this.e == null) { + this.buildChoices(); + this.e = new IntArrayList(this.choices.length); + ItemStack[] aitemstack = this.choices; + int i = aitemstack.length; + + for (int j = 0; j < i; ++j) { + ItemStack itemstack = aitemstack[j]; + + this.e.add(AutoRecipeStackManager.c(itemstack)); + } + + this.e.sort(IntComparators.NATURAL_COMPARATOR); + } + + return this.e; + } + + public void a(PacketDataSerializer packetdataserializer) { + this.buildChoices(); + packetdataserializer.d(this.choices.length); + + for (int i = 0; i < this.choices.length; ++i) { + packetdataserializer.a(this.choices[i]); + } + + } + + public JsonElement c() { + if (this.c.length == 1) { + return this.c[0].b(); + } else { + JsonArray jsonarray = new JsonArray(); + RecipeItemStack.Provider[] arecipeitemstack_provider = this.c; + int i = arecipeitemstack_provider.length; + + for (int j = 0; j < i; ++j) { + RecipeItemStack.Provider recipeitemstack_provider = arecipeitemstack_provider[j]; + + jsonarray.add(recipeitemstack_provider.b()); + } + + return jsonarray; + } + } + + public boolean d() { + return this.c.length == 0 && (this.choices == null || this.choices.length == 0) && (this.e == null || this.e.isEmpty()); + } + + private static RecipeItemStack a(Stream stream) { + RecipeItemStack recipeitemstack = new RecipeItemStack(stream); + + return recipeitemstack.c.length == 0 ? RecipeItemStack.a : recipeitemstack; + } + + public static RecipeItemStack a(IMaterial... aimaterial) { + return a(Arrays.stream(aimaterial).map((imaterial) -> { + return new RecipeItemStack.StackProvider(new ItemStack(imaterial)); + })); + } + + public static RecipeItemStack a(Tag tag) { + return a(Stream.of(new RecipeItemStack.b(tag))); + } + + public static RecipeItemStack b(PacketDataSerializer packetdataserializer) { + int i = packetdataserializer.g(); + + return a(Stream.generate(() -> { + return new RecipeItemStack.StackProvider(packetdataserializer.k()); + }).limit((long) i)); + } + + public static RecipeItemStack a(@Nullable JsonElement jsonelement) { + if (jsonelement != null && !jsonelement.isJsonNull()) { + if (jsonelement.isJsonObject()) { + return a(Stream.of(a(jsonelement.getAsJsonObject()))); + } else if (jsonelement.isJsonArray()) { + JsonArray jsonarray = jsonelement.getAsJsonArray(); + + if (jsonarray.size() == 0) { + throw new JsonSyntaxException("Item array cannot be empty, at least one item must be defined"); + } else { + return a(StreamSupport.stream(jsonarray.spliterator(), false).map((jsonelement1) -> { + return a(ChatDeserializer.m(jsonelement1, "item")); + })); + } + } else { + throw new JsonSyntaxException("Expected item to be object or array of objects"); + } + } else { + throw new JsonSyntaxException("Item cannot be null"); + } + } + + public static RecipeItemStack.Provider a(JsonObject jsonobject) { + if (jsonobject.has("item") && jsonobject.has("tag")) { + throw new JsonParseException("An ingredient entry is either a tag or an item, not both"); + } else { + MinecraftKey minecraftkey; + + if (jsonobject.has("item")) { + minecraftkey = new MinecraftKey(ChatDeserializer.h(jsonobject, "item")); + Item item = (Item) IRegistry.ITEM.get(minecraftkey); + + if (item == null) { + throw new JsonSyntaxException("Unknown item '" + minecraftkey + "'"); + } else { + return new RecipeItemStack.StackProvider(new ItemStack(item)); + } + } else if (jsonobject.has("tag")) { + minecraftkey = new MinecraftKey(ChatDeserializer.h(jsonobject, "tag")); + Tag tag = TagsItem.a().a(minecraftkey); + + if (tag == null) { + throw new JsonSyntaxException("Unknown item tag '" + minecraftkey + "'"); + } else { + return new RecipeItemStack.b(tag); + } + } else { + throw new JsonParseException("An ingredient entry needs either a tag or an item"); + } + } + } + + static class b implements RecipeItemStack.Provider { + + private final Tag a; + + private b(Tag tag) { + this.a = tag; + } + + public Collection a() { + List list = Lists.newArrayList(); + Iterator iterator = this.a.a().iterator(); + + while (iterator.hasNext()) { + Item item = (Item) iterator.next(); + + list.add(new ItemStack(item)); + } + + return list; + } + + public JsonObject b() { + JsonObject jsonobject = new JsonObject(); + + jsonobject.addProperty("tag", this.a.c().toString()); + return jsonobject; + } + } + + public static class StackProvider implements RecipeItemStack.Provider { + + private final ItemStack a; + + public StackProvider(ItemStack itemstack) { + this.a = itemstack; + } + + public Collection a() { + return Collections.singleton(this.a); + } + + public JsonObject b() { + JsonObject jsonobject = new JsonObject(); + + jsonobject.addProperty("item", IRegistry.ITEM.getKey(this.a.getItem()).toString()); + return jsonobject; + } + } + + public interface Provider { + + Collection a(); + + JsonObject b(); + } +} diff --git a/src/main/java/net/minecraft/server/RecipeMapClone.java b/src/main/java/net/minecraft/server/RecipeMapClone.java new file mode 100644 index 000000000000..3e97edcd99b6 --- /dev/null +++ b/src/main/java/net/minecraft/server/RecipeMapClone.java @@ -0,0 +1,79 @@ +package net.minecraft.server; + +public class RecipeMapClone extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + + // CraftBukkit start - Delegate to new parent class + public RecipeMapClone(MinecraftKey minecraftkey) { + super(minecraftkey, "", new ItemStack(Items.MAP, 0), NonNullList.a(RecipeItemStack.a, RecipeItemStack.a(Items.MAP))); + } + // CraftBukkit end + + public boolean a(IInventory iinventory, World world) { + if (!(iinventory instanceof InventoryCrafting)) { + return false; + } else { + int i = 0; + ItemStack itemstack = ItemStack.a; + + for (int j = 0; j < iinventory.getSize(); ++j) { + ItemStack itemstack1 = iinventory.getItem(j); + + if (!itemstack1.isEmpty()) { + if (itemstack1.getItem() == Items.FILLED_MAP) { + if (!itemstack.isEmpty()) { + return false; + } + + itemstack = itemstack1; + } else { + if (itemstack1.getItem() != Items.MAP) { + return false; + } + + ++i; + } + } + } + + return !itemstack.isEmpty() && i > 0; + } + } + + public ItemStack craftItem(IInventory iinventory) { + int i = 0; + ItemStack itemstack = ItemStack.a; + + for (int j = 0; j < iinventory.getSize(); ++j) { + ItemStack itemstack1 = iinventory.getItem(j); + + if (!itemstack1.isEmpty()) { + if (itemstack1.getItem() == Items.FILLED_MAP) { + if (!itemstack.isEmpty()) { + return ItemStack.a; + } + + itemstack = itemstack1; + } else { + if (itemstack1.getItem() != Items.MAP) { + return ItemStack.a; + } + + ++i; + } + } + } + + if (!itemstack.isEmpty() && i >= 1) { + ItemStack itemstack2 = itemstack.cloneItemStack(); + + itemstack2.setCount(i + 1); + return itemstack2; + } else { + return ItemStack.a; + } + } + + public RecipeSerializer a() { + return RecipeSerializers.e; + } +} diff --git a/src/main/java/net/minecraft/server/RecipeRepair.java b/src/main/java/net/minecraft/server/RecipeRepair.java new file mode 100644 index 000000000000..91789e170825 --- /dev/null +++ b/src/main/java/net/minecraft/server/RecipeRepair.java @@ -0,0 +1,98 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.List; +import java.util.stream.Stream; + +public class RecipeRepair extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + + // CraftBukkit start - Delegate to new parent class + public RecipeRepair(MinecraftKey minecraftkey) { + super(minecraftkey, "", new ItemStack(Items.LEATHER_HELMET), NonNullList.a(RecipeItemStack.a, RecipeItemStack.a(Items.LEATHER_HELMET))); + } + // CraftBukkit end + + public boolean a(IInventory iinventory, World world) { + if (!(iinventory instanceof InventoryCrafting)) { + return false; + } else { + List list = Lists.newArrayList(); + + for (int i = 0; i < iinventory.getSize(); ++i) { + ItemStack itemstack = iinventory.getItem(i); + + if (!itemstack.isEmpty()) { + list.add(itemstack); + if (list.size() > 1) { + ItemStack itemstack1 = (ItemStack) list.get(0); + + if (itemstack.getItem() != itemstack1.getItem() || itemstack1.getCount() != 1 || itemstack.getCount() != 1 || !itemstack1.getItem().usesDurability()) { + return false; + } + } + } + } + + return list.size() == 2; + } + } + + public ItemStack craftItem(IInventory iinventory) { + List list = Lists.newArrayList(); + + ItemStack itemstack; + + for (int i = 0; i < iinventory.getSize(); ++i) { + itemstack = iinventory.getItem(i); + if (!itemstack.isEmpty()) { + list.add(itemstack); + if (list.size() > 1) { + ItemStack itemstack1 = (ItemStack) list.get(0); + + if (itemstack.getItem() != itemstack1.getItem() || itemstack1.getCount() != 1 || itemstack.getCount() != 1 || !itemstack1.getItem().usesDurability()) { + return ItemStack.a; + } + } + } + } + + if (list.size() == 2) { + ItemStack itemstack2 = (ItemStack) list.get(0); + + itemstack = (ItemStack) list.get(1); + if (itemstack2.getItem() == itemstack.getItem() && itemstack2.getCount() == 1 && itemstack.getCount() == 1 && itemstack2.getItem().usesDurability()) { + Item item = itemstack2.getItem(); + int j = item.getMaxDurability() - itemstack2.getDamage(); + int k = item.getMaxDurability() - itemstack.getDamage(); + int l = j + k + item.getMaxDurability() * 5 / 100; + int i1 = item.getMaxDurability() - l; + + if (i1 < 0) { + i1 = 0; + } + + ItemStack itemstack3 = new ItemStack(itemstack2.getItem()); + + itemstack3.setDamage(i1); + // CraftBukkit start - Construct a dummy repair recipe + if (iinventory instanceof InventoryCrafting) { + InventoryCrafting inventorycrafting = (InventoryCrafting) iinventory; + NonNullList ingredients = NonNullList.a(); + ingredients.add(new RecipeItemStack(Stream.of(new RecipeItemStack.StackProvider(itemstack2.cloneItemStack())))); + ingredients.add(new RecipeItemStack(Stream.of(new RecipeItemStack.StackProvider(itemstack.cloneItemStack())))); + ShapelessRecipes recipe = new ShapelessRecipes(new MinecraftKey("repairitem"), "", itemstack3.cloneItemStack(), ingredients); + inventorycrafting.setCurrentRecipe(recipe); + itemstack3 = org.bukkit.craftbukkit.event.CraftEventFactory.callPreCraftEvent(inventorycrafting, inventorycrafting.resultInventory, itemstack3, inventorycrafting.container.getBukkitView(), true); + } + // CraftBukkit end + return itemstack3; + } + } + + return ItemStack.a; + } + + public RecipeSerializer a() { + return RecipeSerializers.j; + } +} diff --git a/src/main/java/net/minecraft/server/RecipeShulkerBox.java b/src/main/java/net/minecraft/server/RecipeShulkerBox.java new file mode 100644 index 000000000000..fe1c6e79ba1e --- /dev/null +++ b/src/main/java/net/minecraft/server/RecipeShulkerBox.java @@ -0,0 +1,72 @@ +package net.minecraft.server; + +public class RecipeShulkerBox extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + + // CraftBukkit start - Delegate to new parent class with bogus info + public RecipeShulkerBox(MinecraftKey minecraftkey) { + super(minecraftkey, "", new ItemStack(Blocks.WHITE_SHULKER_BOX, 0), NonNullList.a(RecipeItemStack.a, RecipeItemStack.a(Items.BONE_MEAL))); + } + // CraftBukkit end + + public boolean a(IInventory iinventory, World world) { + if (!(iinventory instanceof InventoryCrafting)) { + return false; + } else { + int i = 0; + int j = 0; + + for (int k = 0; k < iinventory.getSize(); ++k) { + ItemStack itemstack = iinventory.getItem(k); + + if (!itemstack.isEmpty()) { + if (Block.asBlock(itemstack.getItem()) instanceof BlockShulkerBox) { + ++i; + } else { + if (!(itemstack.getItem() instanceof ItemDye)) { + return false; + } + + ++j; + } + + if (j > 1 || i > 1) { + return false; + } + } + } + + return i == 1 && j == 1; + } + } + + public ItemStack craftItem(IInventory iinventory) { + ItemStack itemstack = ItemStack.a; + ItemDye itemdye = (ItemDye) Items.BONE_MEAL; + + for (int i = 0; i < iinventory.getSize(); ++i) { + ItemStack itemstack1 = iinventory.getItem(i); + + if (!itemstack1.isEmpty()) { + Item item = itemstack1.getItem(); + + if (Block.asBlock(item) instanceof BlockShulkerBox) { + itemstack = itemstack1; + } else if (item instanceof ItemDye) { + itemdye = (ItemDye) item; + } + } + } + + ItemStack itemstack2 = BlockShulkerBox.b(itemdye.d()); + + if (itemstack.hasTag()) { + itemstack2.setTag(itemstack.getTag().clone()); + } + + return itemstack2; + } + + public RecipeSerializer a() { + return RecipeSerializers.o; + } +} diff --git a/src/main/java/net/minecraft/server/RecipeTippedArrow.java b/src/main/java/net/minecraft/server/RecipeTippedArrow.java new file mode 100644 index 000000000000..09f6136b0b85 --- /dev/null +++ b/src/main/java/net/minecraft/server/RecipeTippedArrow.java @@ -0,0 +1,62 @@ +package net.minecraft.server; + +import java.util.Collection; + +public class RecipeTippedArrow extends ShapedRecipes implements IRecipe { // CraftBukkit + + // CraftBukkit start + public RecipeTippedArrow(MinecraftKey minecraftkey) { + super(minecraftkey, "", 3, 3, NonNullList.a(RecipeItemStack.a, + RecipeItemStack.a(Items.ARROW), RecipeItemStack.a(Items.ARROW), RecipeItemStack.a(Items.ARROW), + RecipeItemStack.a(Items.ARROW), RecipeItemStack.a(Items.LINGERING_POTION), RecipeItemStack.a(Items.ARROW), + RecipeItemStack.a(Items.ARROW), RecipeItemStack.a(Items.ARROW), RecipeItemStack.a(Items.ARROW)), + new ItemStack(Items.TIPPED_ARROW, 8)); + } + // CraftBukkit end + + public boolean a(IInventory iinventory, World world) { + if (iinventory.U_() == 3 && iinventory.n() == 3) { + for (int i = 0; i < iinventory.U_(); ++i) { + for (int j = 0; j < iinventory.n(); ++j) { + ItemStack itemstack = iinventory.getItem(i + j * iinventory.U_()); + + if (itemstack.isEmpty()) { + return false; + } + + Item item = itemstack.getItem(); + + if (i == 1 && j == 1) { + if (item != Items.LINGERING_POTION) { + return false; + } + } else if (item != Items.ARROW) { + return false; + } + } + } + + return true; + } else { + return false; + } + } + + public ItemStack craftItem(IInventory iinventory) { + ItemStack itemstack = iinventory.getItem(1 + iinventory.U_()); + + if (itemstack.getItem() != Items.LINGERING_POTION) { + return ItemStack.a; + } else { + ItemStack itemstack1 = new ItemStack(Items.TIPPED_ARROW, 8); + + PotionUtil.a(itemstack1, PotionUtil.d(itemstack)); + PotionUtil.a(itemstack1, (Collection) PotionUtil.b(itemstack)); + return itemstack1; + } + } + + public RecipeSerializer a() { + return RecipeSerializers.k; + } +} diff --git a/src/main/java/net/minecraft/server/RecipiesShield.java b/src/main/java/net/minecraft/server/RecipiesShield.java new file mode 100644 index 000000000000..64dea7f07a5b --- /dev/null +++ b/src/main/java/net/minecraft/server/RecipiesShield.java @@ -0,0 +1,85 @@ +package net.minecraft.server; + +public class RecipiesShield extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + + // CraftBukkit start - Delegate to new parent class with bogus info + public RecipiesShield(MinecraftKey minecraftkey) { + super(minecraftkey, "", new ItemStack(Items.SHIELD, 0), NonNullList.a(RecipeItemStack.a, RecipeItemStack.a(Items.WHITE_BANNER))); + } + // CraftBukkit end + + public boolean a(IInventory iinventory, World world) { + if (!(iinventory instanceof InventoryCrafting)) { + return false; + } else { + ItemStack itemstack = ItemStack.a; + ItemStack itemstack1 = ItemStack.a; + + for (int i = 0; i < iinventory.getSize(); ++i) { + ItemStack itemstack2 = iinventory.getItem(i); + + if (!itemstack2.isEmpty()) { + if (itemstack2.getItem() instanceof ItemBanner) { + if (!itemstack1.isEmpty()) { + return false; + } + + itemstack1 = itemstack2; + } else { + if (itemstack2.getItem() != Items.SHIELD) { + return false; + } + + if (!itemstack.isEmpty()) { + return false; + } + + if (itemstack2.b("BlockEntityTag") != null) { + return false; + } + + itemstack = itemstack2; + } + } + } + + if (!itemstack.isEmpty() && !itemstack1.isEmpty()) { + return true; + } else { + return false; + } + } + } + + public ItemStack craftItem(IInventory iinventory) { + ItemStack itemstack = ItemStack.a; + ItemStack itemstack1 = ItemStack.a; + + for (int i = 0; i < iinventory.getSize(); ++i) { + ItemStack itemstack2 = iinventory.getItem(i); + + if (!itemstack2.isEmpty()) { + if (itemstack2.getItem() instanceof ItemBanner) { + itemstack = itemstack2; + } else if (itemstack2.getItem() == Items.SHIELD) { + itemstack1 = itemstack2.cloneItemStack(); + } + } + } + + if (itemstack1.isEmpty()) { + return itemstack1; + } else { + NBTTagCompound nbttagcompound = itemstack.b("BlockEntityTag"); + NBTTagCompound nbttagcompound1 = nbttagcompound == null ? new NBTTagCompound() : nbttagcompound.clone(); + + nbttagcompound1.setInt("Base", ((ItemBanner) itemstack.getItem()).b().getColorIndex()); + itemstack1.a("BlockEntityTag", (NBTBase) nbttagcompound1); + return itemstack1; + } + } + + public RecipeSerializer a() { + return RecipeSerializers.n; + } +} diff --git a/src/main/java/net/minecraft/server/RegionFile.java b/src/main/java/net/minecraft/server/RegionFile.java new file mode 100644 index 000000000000..d148ce497602 --- /dev/null +++ b/src/main/java/net/minecraft/server/RegionFile.java @@ -0,0 +1,531 @@ +package net.minecraft.server; + +import com.destroystokyo.paper.exception.ServerInternalException; +import com.google.common.collect.Lists; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.List; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; +import javax.annotation.Nullable; + +public class RegionFile { + + // Spigot start + // Minecraft is limited to 256 sections per chunk. So 1MB. This can easily be overriden. + // So we extend this to use the REAL size when the count is maxed by seeking to that section and reading the length. + private static final boolean ENABLE_EXTENDED_SAVE = Boolean.parseBoolean(System.getProperty("net.minecraft.server.RegionFile.enableExtendedSave", "true")); + // Spigot end + private static final byte[] a = new byte[4096]; + private final File b;private File getFile() { return b; } // Paper - OBFHELPER + private RandomAccessFile c;private RandomAccessFile getDataFile() { return c; } // Paper - OBFHELPER + private final int[] d = new int[1024];private int[] offsets = d; // Paper - OBFHELPER + private final int[] e = new int[1024];private int[] timestamps = e; // Paper - OBFHELPER + private List f; + private int g; + private long h; + + public RegionFile(File file) { + this.b = file; + this.g = 0; + + try { + if (file.exists()) { + this.h = file.lastModified(); + } + + this.c = new RandomAccessFile(file, "rw"); + if (this.c.length() < 8192L) { // Paper - headers should be 8192 + this.c.write(RegionFile.a); + this.c.write(RegionFile.a); + this.g += 8192; + } + + int i; + + if ((this.c.length() & 4095L) != 0L) { + for (i = 0; (long) i < (this.c.length() & 4095L); ++i) { + this.c.write(0); + } + } + + i = (int) this.c.length() / 4096; + this.f = Lists.newArrayListWithCapacity(i); + + int j; + + for (j = 0; j < i; ++j) { + this.f.add(true); + } + + this.f.set(0, false); + this.f.set(1, false); + this.c.seek(0L); + + int k; + // Paper Start + java.nio.ByteBuffer header = java.nio.ByteBuffer.allocate(8192); + while (header.hasRemaining()) { + if (this.c.getChannel().read(header) == -1) throw new java.io.EOFException(); + } + header.clear(); + java.nio.IntBuffer headerAsInts = header.asIntBuffer(); + initOversizedState(); + // Paper End + + for (j = 0; j < 1024; ++j) { + k = headerAsInts.get(); // Paper + this.d[j] = k; + // Spigot start + int length = k & 255; + if (length == 255) { + if ((k >> 8) <= this.f.size()) { + // We're maxed out, so we need to read the proper length from the section + this.c.seek((k >> 8) * 4096); + length = (this.c.readInt() + 4) / 4096 + 1; + this.c.seek(j * 4 + 4); // Go back to where we were + } + } + if (k > 0 && (k >> 8) > 1 && (k >> 8) + (length) <= this.f.size()) { // Paper >= 1 as 0/1 are the headers, and negative isnt valid + for (int l = 0; l < (length); ++l) { + // Spigot end + this.f.set((k >> 8) + l, false); + } + } + // Spigot start + else if (k != 0) { // Paper + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Invalid chunk: ({0}, {1}) Offset: {2} Length: {3} runs off end file. {4}", new Object[]{j % 32, (int) (j / 32), k >> 8, length, file}); // Paper + deleteChunk(j); // Paper + } + // Spigot end + } + + for (j = 0; j < 1024; ++j) { + k = headerAsInts.get(); // Paper + if (offsets[j] != 0) this.timestamps[j] = k; // Paper - don't set timestamp if it got 0'd above due to corruption + } + } catch (IOException ioexception) { + ioexception.printStackTrace(); + ServerInternalException.reportInternalException(ioexception); // Paper + } + + } + + @Nullable + public synchronized DataInputStream getReadStream(int i, int j) { return a(i, j); } @Nullable public synchronized DataInputStream a(int i, int j) { // Paper - OBFHELPER + if (this.e(i, j)) { + return null; + } else { + try { + int k = this.getOffset(i, j); + + if (k == 0) { + return null; + } else { + int l = k >> 8; + int i1 = k & 255; + // Spigot start + if (i1 == 255) { + this.c.seek(l * 4096); + i1 = (this.c.readInt() + 4) / 4096 + 1; + } + // Spigot end + + if (l + i1 > this.f.size()) { + return null; + } else { + this.c.seek((long) (l * 4096)); + int j1 = this.c.readInt(); + + if (j1 > 4096 * i1) { + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Invalid chunk: ({0}, {1}) Offset: {2} Invalid Size: {3}>{4} {5}", new Object[]{i, j, l, j1, i1 * 4096, this.b}); // Spigot + return null; + } else if (j1 <= 0) { + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Invalid chunk: ({0}, {1}) Offset: {2} Invalid Size: {3} {4}", new Object[]{i, j, l, j1, this.b}); // Spigot + return null; + } else { + byte b0 = this.c.readByte(); + byte[] abyte; + + if (b0 == 1) { + abyte = new byte[j1 - 1]; + this.c.read(abyte); + return new DataInputStream(new BufferedInputStream(new GZIPInputStream(new ByteArrayInputStream(abyte)))); + } else if (b0 == 2) { + abyte = new byte[j1 - 1]; + this.c.read(abyte); + return new DataInputStream(new BufferedInputStream(new InflaterInputStream(new ByteArrayInputStream(abyte)))); + } else { + return null; + } + } + } + } + } catch (IOException ioexception) { + return null; + } + } + } + + public boolean b(int i, int j) { + if (this.e(i, j)) { + return false; + } else { + int k = this.getOffset(i, j); + + if (k == 0) { + return false; + } else { + int l = k >> 8; + int i1 = k & 255; + + if (l + i1 > this.f.size()) { + return false; + } else { + try { + this.c.seek((long) (l * 4096)); + int j1 = this.c.readInt(); + + return j1 > 4096 * i1 ? false : j1 > 0; + } catch (IOException ioexception) { + return false; + } + } + } + } + } + + @Nullable + public DataOutputStream getWriteStream(int i, int j) { return c(i, j); } @Nullable public DataOutputStream c(int i, int j) { // Paper - OBFHELPER + return this.e(i, j) ? null : new DataOutputStream(new RegionFile.ChunkBuffer(i, j)); // Paper - remove middleware, move deflate to .close() for dynamic levels + } + + protected synchronized void a(int i, int j, byte[] abyte, int k) { + try { + int l = this.getOffset(i, j); + int i1 = l >> 8; + int j1 = l & 255; + // Spigot start + if (j1 == 255) { + this.c.seek(i1 * 4096); + j1 = (this.c.readInt() + 4) / 4096 + 1; + } + // Spigot end + int k1 = (k + 5) / 4096 + 1; + + if (k1 >= 256) { + // Spigot start + if (!USE_SPIGOT_OVERSIZED_METHOD) throw new ChunkTooLargeException(i, j, k1); // Paper - throw error instead + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING,"Large Chunk Detected: ({0}, {1}) Size: {2} {3}", new Object[]{i, j, k1, this.b}); + if (!ENABLE_EXTENDED_SAVE) return; + // Spigot end + } + + if (i1 != 0 && j1 == k1) { + this.a(i1, abyte, k); + } else { + int l1; + + for (l1 = 0; l1 < j1; ++l1) { + this.f.set(i1 + l1, true); + } + + l1 = this.f.indexOf(true); + int i2 = 0; + int j2; + + if (l1 != -1) { + for (j2 = l1; j2 < this.f.size(); ++j2) { + if (i2 != 0) { + if ((Boolean) this.f.get(j2)) { + ++i2; + } else { + i2 = 0; + } + } else if ((Boolean) this.f.get(j2)) { + l1 = j2; + i2 = 1; + } + + if (i2 >= k1) { + break; + } + } + } + + if (i2 >= k1) { + i1 = l1; + this.a(i, j, l1 << 8 | (k1 > 255 ? 255 : k1)); // Spigot + + for (j2 = 0; j2 < k1; ++j2) { + this.f.set(i1 + j2, false); + } + + this.a(i1, abyte, k); + } else { + this.c.seek(this.c.length()); + i1 = this.f.size(); + + for (j2 = 0; j2 < k1; ++j2) { + this.c.write(RegionFile.a); + this.f.add(false); + } + + this.g += 4096 * k1; + this.a(i1, abyte, k); + this.a(i, j, i1 << 8 | (k1 > 255 ? 255 : k1)); // Spigot + } + } + + this.b(i, j, (int) (SystemUtils.getTimeMillis() / 1000L)); + } catch (IOException ioexception) { + com.destroystokyo.paper.util.SneakyThrow.sneaky(ioexception); // Paper - we want the upper try/catch to retry this + } + + } + + private void a(int i, byte[] abyte, int j) throws IOException { + this.c.seek((long) (i * 4096)); + this.c.writeInt(j + 1); + this.c.writeByte(2); + this.c.write(abyte, 0, j); + } + + private boolean e(int i, int j) { + return i < 0 || i >= 32 || j < 0 || j >= 32; + } + + private synchronized int getOffset(int i, int j) { + return this.d[i + j * 32]; + } + + public boolean d(int i, int j) { + return this.getOffset(i, j) != 0; + } + + private void a(int i, int j, int k) throws IOException { + this.d[i + j * 32] = k; + this.c.seek((long) ((i + j * 32) * 4)); + this.c.writeInt(k); + } + + private void b(int i, int j, int k) throws IOException { + this.e[i + j * 32] = k; + this.c.seek((long) (4096 + (i + j * 32) * 4)); + this.c.writeInt(k); + } + + public void close() throws IOException { + if (this.c != null) { + this.c.close(); + } + + } + + // Paper start + public synchronized void deleteChunk(int j1) { + backup(); + int k = offsets[j1]; + int x = j1 & 1024; + int z = j1 >> 2; + int offset = (k >> 8); + int len = (k & 255); + String debug = "idx:" + + j1 + " - " + x + "," + z + " - offset: " + offset + " - len: " + len; + try { + timestamps[j1] = 0; + offsets[j1] = 0; + RandomAccessFile file = getDataFile(); + file.seek(j1 * 4); + file.writeInt(0); + // clear the timestamp + file.seek(4096 + j1 * 4); + file.writeInt(0); + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Deleted corrupt chunk (" + debug + ") " + getFile().getAbsolutePath(), e); + } catch (IOException e) { + + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Error deleting corrupt chunk (" + debug + ") " + getFile().getAbsolutePath(), e); + } + } + private boolean backedUp = false; + private synchronized void backup() { + if (backedUp) { + return; + } + backedUp = true; + File file = this.getFile(); + java.text.DateFormat formatter = new java.text.SimpleDateFormat("yyyy-MM-dd"); + java.util.Date today = new java.util.Date(); + File corrupt = new File(file.getParentFile(), file.getName() + "." + formatter.format(today) + ".corrupt"); + if (corrupt.exists()) { + return; + } + org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger(); + logger.error("Region file " + file.getAbsolutePath() + " was corrupt. Backing up to " + corrupt.getAbsolutePath() + " and repairing"); + try { + java.nio.file.Files.copy(file.toPath(), corrupt.toPath()); + + } catch (IOException e) { + logger.error("Error backing up corrupt file" + file.getAbsolutePath(), e); + } + } + + private final byte[] oversized = new byte[1024]; + private int oversizedCount = 0; + + private synchronized void initOversizedState() throws IOException { + File metaFile = getOversizedMetaFile(); + if (metaFile.exists()) { + final byte[] read = java.nio.file.Files.readAllBytes(metaFile.toPath()); + System.arraycopy(read, 0, oversized, 0, oversized.length); + for (byte temp : oversized) { + oversizedCount += temp; + } + } + } + + private static int getChunkIndex(int x, int z) { + return (x & 31) + (z & 31) * 32; + } + synchronized boolean isOversized(int x, int z) { + return this.oversized[getChunkIndex(x, z)] == 1; + } + synchronized void setOversized(int x, int z, boolean oversized) throws IOException { + final int offset = getChunkIndex(x, z); + boolean previous = this.oversized[offset] == 1; + this.oversized[offset] = (byte) (oversized ? 1 : 0); + if (!previous && oversized) { + oversizedCount++; + } else if (!oversized && previous) { + oversizedCount--; + } + if (previous && !oversized) { + File oversizedFile = getOversizedFile(x, z); + if (oversizedFile.exists()) { + oversizedFile.delete(); + } + } + if (oversizedCount > 0) { + if (previous != oversized) { + writeOversizedMeta(); + } + } else if (previous) { + File oversizedMetaFile = getOversizedMetaFile(); + if (oversizedMetaFile.exists()) { + oversizedMetaFile.delete(); + } + } + } + + private void writeOversizedMeta() throws IOException { + java.nio.file.Files.write(getOversizedMetaFile().toPath(), oversized); + } + + private File getOversizedMetaFile() { + return new File(getFile().getParentFile(), getFile().getName().replaceAll("\\.mca$", "") + ".oversized.nbt"); + } + + private File getOversizedFile(int x, int z) { + return new File(this.getFile().getParentFile(), this.getFile().getName().replaceAll("\\.mca$", "") + "_oversized_" + x + "_" + z + ".nbt"); + } + + void writeOversizedData(int x, int z, NBTTagCompound oversizedData) throws IOException { + File file = getOversizedFile(x, z); + try (DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new DeflaterOutputStream(new java.io.FileOutputStream(file), new java.util.zip.Deflater(java.util.zip.Deflater.BEST_COMPRESSION), 32 * 1024), 32 * 1024))) { + NBTCompressedStreamTools.writeNBT(oversizedData, out); + } + this.setOversized(x, z, true); + + } + + synchronized NBTTagCompound getOversizedData(int x, int z) throws IOException { + File file = getOversizedFile(x, z); + try (DataInputStream out = new DataInputStream(new BufferedInputStream(new InflaterInputStream(new java.io.FileInputStream(file))))) { + return NBTCompressedStreamTools.readNBT(out); + } + + } + + private static final boolean USE_SPIGOT_OVERSIZED_METHOD = Boolean.getBoolean("Paper.useSpigotExtendedSaveMethod"); // Paper + static { + if (USE_SPIGOT_OVERSIZED_METHOD) { + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "===================================="); + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Using Spigot Oversized Chunk save method. Warning this will result in extremely fragmented chunks, as well as making the entire region file unable to be to used in any other software but Forge or Spigot (not usable in Vanilla or CraftBukkit). Paper's method is highly recommended."); + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "===================================="); + } + } + public class ChunkTooLargeException extends RuntimeException { + public ChunkTooLargeException(int x, int z, int sectors) { + super("Chunk " + x + "," + z + " of " + getFile().toString() + " is too large (" + sectors + "/256)"); + } + } + private static class DirectByteArrayOutputStream extends ByteArrayOutputStream { + public DirectByteArrayOutputStream() { + super(); + } + + public DirectByteArrayOutputStream(int size) { + super(size); + } + + public byte[] getBuffer() { + return this.buf; + } + } + // Paper end + + class ChunkBuffer extends ByteArrayOutputStream { + + private final int b; + private final int c; + + public ChunkBuffer(int i, int j) { + super(8096); + this.b = i; + this.c = j; + } + + public void close() throws IOException { + // Paper start - apply dynamic compression + int origLength = this.count; + byte[] buf = this.buf; + DirectByteArrayOutputStream out = compressData(buf, origLength); + byte[] bytes = out.getBuffer(); + int length = out.size(); + + RegionFile.this.a(this.b, this.c, bytes, length); // Paper - change to bytes/length + // Paper end + } + } + + private static DirectByteArrayOutputStream compressData(byte[] buf, int length) throws IOException { + final java.util.zip.Deflater deflater; + if (length > 1024 * 512) { + deflater = new java.util.zip.Deflater(9); + } else if (length > 1024 * 128) { + deflater = new java.util.zip.Deflater(8); + } else { + deflater = new java.util.zip.Deflater(6); + } + + + deflater.setInput(buf, 0, length); + deflater.finish(); + + DirectByteArrayOutputStream out = new DirectByteArrayOutputStream(length); + byte[] buffer = new byte[1024 * (length > 1024 * 124 ? 32 : 16)]; + while (!deflater.finished()) { + out.write(buffer, 0, deflater.deflate(buffer)); + } + out.close(); + deflater.end(); + return out; + } +} diff --git a/src/main/java/net/minecraft/server/RegionFileCache.java b/src/main/java/net/minecraft/server/RegionFileCache.java new file mode 100644 index 000000000000..a17e76d839e9 --- /dev/null +++ b/src/main/java/net/minecraft/server/RegionFileCache.java @@ -0,0 +1,283 @@ +package net.minecraft.server; + +import com.destroystokyo.paper.exception.ServerInternalException; +import com.google.common.collect.Maps; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; +import javax.annotation.Nullable; +import com.destroystokyo.paper.PaperConfig; // Paper +import java.util.LinkedHashMap; // Paper + +public class RegionFileCache { + + public static final Map cache = new LinkedHashMap(PaperConfig.regionFileCacheSize, 0.75f, true); // Paper - HashMap -> LinkedHashMap + + public static synchronized RegionFile getRegionFile(File file, int i, int j) { return a(file, i, j); } // Paper - OBFHELPER + public static synchronized RegionFile a(File file, int i, int j) { + File file1 = new File(file, "region"); + File file2 = new File(file1, "r." + (i >> 5) + "." + (j >> 5) + ".mca"); + RegionFile regionfile = (RegionFile) RegionFileCache.cache.get(file2); + + if (regionfile != null) { + return regionfile; + } else { + if (!file1.exists()) { + file1.mkdirs(); + } + + if (RegionFileCache.cache.size() >= 256) { + trimCache(); + } + + RegionFile regionfile1 = new RegionFile(file2); + + RegionFileCache.cache.put(file2, regionfile1); + return regionfile1; + } + } + + // CraftBukkit start + public static synchronized RegionFile b(File file, int i, int j) { + File file1 = new File(file, "region"); + File file2 = new File(file1, "r." + (i >> 5) + "." + (j >> 5) + ".mca"); + RegionFile regionfile = (RegionFile) RegionFileCache.cache.get(file2); + + if (regionfile != null) { + return regionfile; + } else if (file1.exists() && file2.exists()) { + if (RegionFileCache.cache.size() >= 256) { + a(); + } + + RegionFile regionfile1 = new RegionFile(file2); + + RegionFileCache.cache.put(file2, regionfile1); + return regionfile1; + } else { + return null; + } + } + // CraftBukkit end + + // Paper Start + private static synchronized void trimCache() { + Iterator> itr = RegionFileCache.cache.entrySet().iterator(); + int count = RegionFileCache.cache.size() - PaperConfig.regionFileCacheSize; + while (count-- >= 0 && itr.hasNext()) { + try { + itr.next().getValue().close(); + } catch (IOException ioexception) { + ioexception.printStackTrace(); + ServerInternalException.reportInternalException(ioexception); + } + itr.remove(); + } + } + public static synchronized File getRegionFileName(File file, int i, int j) { + File file1 = new File(file, "region"); + return new File(file1, "r." + (i >> 5) + "." + (j >> 5) + ".mca"); + } + public static synchronized boolean hasRegionFile(File file, int i, int j) { + return RegionFileCache.cache.containsKey(getRegionFileName(file, i, j)); + } + private static void printOversizedLog(String msg, File file, int x, int z) { + org.apache.logging.log4j.LogManager.getLogger().fatal(msg + " (" + file.toString().replaceAll(".+[\\\\/]", "") + " - " + x + "," + z + ") Go clean it up to remove this message. /minecraft:tp " + (x<<4)+" 128 "+(z<<4) + " - DO NOT REPORT THIS TO PAPER - You may ask for help on Discord, but do not file an issue. These error messages can not be removed."); + } + + private static final int DEFAULT_SIZE_THRESHOLD = 1024 * 8; + private static final int OVERZEALOUS_THRESHOLD = 1024 * 2; + private static int SIZE_THRESHOLD = DEFAULT_SIZE_THRESHOLD; + private static void resetFilterThresholds() { + SIZE_THRESHOLD = Math.max(1024 * 4, Integer.getInteger("Paper.FilterThreshhold", DEFAULT_SIZE_THRESHOLD)); + } + static { + resetFilterThresholds(); + } + private static void writeRegion(File file, int x, int z, NBTTagCompound nbttagcompound) throws IOException { + RegionFile regionfile = getRegionFile(file, x, z); + + DataOutputStream out = regionfile.getWriteStream(x & 31, z & 31); + try { + NBTCompressedStreamTools.writeNBT(nbttagcompound, out); + out.close(); + regionfile.setOversized(x, z, false); + } catch (RegionFile.ChunkTooLargeException ignored) { + printOversizedLog("ChunkTooLarge! Someone is trying to duplicate.", file, x, z); + // Clone as we are now modifying it, don't want to corrupt the pending save state + nbttagcompound = nbttagcompound.clone(); + // Filter out TileEntities and Entities + NBTTagCompound oversizedData = filterChunkData(nbttagcompound); + //noinspection SynchronizationOnLocalVariableOrMethodParameter + synchronized (regionfile) { + out = regionfile.getWriteStream(x & 31, z & 31); + NBTCompressedStreamTools.writeNBT(nbttagcompound, out); + try { + out.close(); + // 2048 is below the min allowed, so it means we enter overzealous mode below + if (SIZE_THRESHOLD == OVERZEALOUS_THRESHOLD) { + resetFilterThresholds(); + } + } catch (RegionFile.ChunkTooLargeException e) { + printOversizedLog("ChunkTooLarge even after reduction. Trying in overzealous mode.", file, x, z); + // Eek, major fail. We have retry logic, so reduce threshholds and fall back + SIZE_THRESHOLD = OVERZEALOUS_THRESHOLD; + throw e; + } + + regionfile.writeOversizedData(x, z, oversizedData); + } + } + } + + private static NBTTagCompound filterChunkData(NBTTagCompound chunk) { + NBTTagCompound oversizedLevel = new NBTTagCompound(); + NBTTagCompound level = chunk.getCompound("Level"); + filterChunkList(level, oversizedLevel, "Entities"); + filterChunkList(level, oversizedLevel, "TileEntities"); + NBTTagCompound oversized = new NBTTagCompound(); + oversized.set("Level", oversizedLevel); + return oversized; + } + + private static void filterChunkList(NBTTagCompound level, NBTTagCompound extra, String key) { + NBTTagList list = level.getList(key, 10); + NBTTagList newList = extra.getList(key, 10); + for (Iterator iterator = list.list.iterator(); iterator.hasNext(); ) { + NBTBase object = iterator.next(); + if (getNBTSize(object) > SIZE_THRESHOLD) { + newList.add(object); + iterator.remove(); + } + } + level.set(key, list); + extra.set(key, newList); + } + + + private static NBTTagCompound readOversizedChunk(RegionFile regionfile, int i, int j) throws IOException { + synchronized (regionfile) { + try (DataInputStream datainputstream = regionfile.getReadStream(i & 31, j & 31)) { + NBTTagCompound oversizedData = regionfile.getOversizedData(i, j); + NBTTagCompound chunk = NBTCompressedStreamTools.readNBT(datainputstream); + if (oversizedData == null) { + return chunk; + } + NBTTagCompound oversizedLevel = oversizedData.getCompound("Level"); + NBTTagCompound level = chunk.getCompound("Level"); + + mergeChunkList(level, oversizedLevel, "Entities"); + mergeChunkList(level, oversizedLevel, "TileEntities"); + + chunk.set("Level", level); + + return chunk; + } catch (Throwable throwable) { + throwable.printStackTrace(); + throw throwable; + } + } + } + + private static void mergeChunkList(NBTTagCompound level, NBTTagCompound oversizedLevel, String key) { + NBTTagList levelList = level.getList(key, 10); + NBTTagList oversizedList = oversizedLevel.getList(key, 10); + + if (!oversizedList.isEmpty()) { + levelList.addAll(oversizedList); + level.set(key, levelList); + } + } + + private static int getNBTSize(NBTBase nbtBase) { + DataOutputStream test = new DataOutputStream(new org.apache.commons.io.output.NullOutputStream()); + try { + nbtBase.write(test); + return test.size(); + } catch (IOException e) { + e.printStackTrace(); + return 0; + } + } + + // Paper End + + public static synchronized void a() { + Iterator iterator = RegionFileCache.cache.values().iterator(); + + while (iterator.hasNext()) { + RegionFile regionfile = (RegionFile) iterator.next(); + + try { + if (regionfile != null) { + regionfile.close(); + } + } catch (IOException ioexception) { + ioexception.printStackTrace(); + ServerInternalException.reportInternalException(ioexception); // Paper + } + } + + RegionFileCache.cache.clear(); + } + + @Nullable + // CraftBukkit start - call sites hoisted for synchronization + public static NBTTagCompound read(File file, int i, int j) throws IOException { // Paper - remove synchronization + RegionFile regionfile = a(file, i, j); + // Paper start + if (regionfile.isOversized(i, j)) { + printOversizedLog("Loading Oversized Chunk!", file, i, j); + return readOversizedChunk(regionfile, i, j); + } + // Paper end + + DataInputStream datainputstream = regionfile.a(i & 31, j & 31); + + if (datainputstream == null) { + return null; + } + + return NBTCompressedStreamTools.a(datainputstream); + } + + @Nullable + public static void write(File file, int i, int j, NBTTagCompound nbttagcompound) throws IOException { + int attempts = 0; Exception laste = null; while (attempts++ < 5) { try { // Paper + writeRegion(file, i, j, nbttagcompound); // Paper - moved to own method + // Paper start +// RegionFile regionfile = a(file, i, j); +// +// DataOutputStream dataoutputstream = regionfile.c(i & 31, j & 31); +// NBTCompressedStreamTools.a(nbttagcompound, (java.io.DataOutput) dataoutputstream); +// dataoutputstream.close(); + // Paper end + // Paper start + laste = null; break; // Paper + } catch (Exception exception) { + //ChunkRegionLoader.a.error("Failed to save chunk", exception); // Paper + laste = exception; // Paper + } + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + if (laste != null) { + com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(laste); + MinecraftServer.LOGGER.error("Failed to save chunk", laste); + } + // Paper end + } + + public static boolean chunkExists(File file, int i, int j) { // Paper - remove synchronization + RegionFile regionfile = b(file, i, j); + + return regionfile != null ? regionfile.d(i & 31, j & 31) : false; + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/RegionLimitedWorldAccess.java b/src/main/java/net/minecraft/server/RegionLimitedWorldAccess.java new file mode 100644 index 000000000000..da6df06d84e6 --- /dev/null +++ b/src/main/java/net/minecraft/server/RegionLimitedWorldAccess.java @@ -0,0 +1,309 @@ +package net.minecraft.server; + +import java.util.Random; +import java.util.function.Predicate; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class RegionLimitedWorldAccess implements GeneratorAccess { + + private static final Logger a = LogManager.getLogger(); + private final ProtoChunk[] b; + private final int c; + private final int d; + private final int e; + private final int f; + private final World g; + private final long h; + private final int i; + private final WorldData j; + private final Random k; + private final WorldProvider l; + private final GeneratorSettings m; + private final TickList n = new TickListWorldGen<>((blockposition) -> { + return this.y(blockposition).k(); + }); + private final TickList o = new TickListWorldGen<>((blockposition) -> { + return this.y(blockposition).l(); + }); + + public RegionLimitedWorldAccess(ProtoChunk[] aprotochunk, int i, int j, int k, int l, World world) { + this.b = aprotochunk; + this.c = k; + this.d = l; + this.e = i; + this.f = j; + this.g = world.regionLimited(this); // Paper + this.h = world.getSeed(); + this.m = world.getChunkProvider().getChunkGenerator().getSettings(); + this.i = world.getSeaLevel(); + this.j = world.getWorldData(); + this.k = world.m(); + this.l = world.o(); + } + + public int a() { + return this.c; + } + + public int b() { + return this.d; + } + + public boolean a(int i, int j) { + ProtoChunk protochunk = this.b[0]; + ProtoChunk protochunk1 = this.b[this.b.length - 1]; + + return i >= protochunk.getPos().x && i <= protochunk1.getPos().x && j >= protochunk.getPos().z && j <= protochunk1.getPos().z; + } + + public IChunkAccess getChunkAt(int i, int j) { + if (this.a(i, j)) { + int k = i - this.b[0].getPos().x; + int l = j - this.b[0].getPos().z; + + return this.b[k + l * this.e]; + } else { + ProtoChunk protochunk = this.b[0]; + ProtoChunk protochunk1 = this.b[this.b.length - 1]; + + RegionLimitedWorldAccess.a.error("Requested chunk : {} {}", i, j); + RegionLimitedWorldAccess.a.error("Region bounds : {} {} | {} {}", protochunk.getPos().x, protochunk.getPos().z, protochunk1.getPos().x, protochunk1.getPos().z); + throw new RuntimeException(String.format("We are asking a region for a chunk out of bound | %s %s", i, j)); + } + } + + public IBlockData getType(BlockPosition blockposition) { + return this.y(blockposition).getType(blockposition); + } + + public Fluid getFluid(BlockPosition blockposition) { + return this.y(blockposition).getFluid(blockposition); + } + + @Nullable + public EntityHuman a(double d0, double d1, double d2, double d3, Predicate predicate) { + return null; + } + + public int c() { + return 0; + } + + public boolean isEmpty(BlockPosition blockposition) { + return this.getType(blockposition).isAir(); + } + + public BiomeBase getBiome(BlockPosition blockposition) { + BiomeBase biomebase = this.y(blockposition).getBiomeIndex()[blockposition.getX() & 15 | (blockposition.getZ() & 15) << 4]; + + if (biomebase == null) { + throw new RuntimeException(String.format("Biome is null @ %s", blockposition)); + } else { + return biomebase; + } + } + + public int getBrightness(EnumSkyBlock enumskyblock, BlockPosition blockposition) { + IChunkAccess ichunkaccess = this.y(blockposition); + + return ichunkaccess.a(enumskyblock, blockposition, this.o().g()); + } + + public int getLightLevel(BlockPosition blockposition, int i) { + return this.y(blockposition).a(blockposition, i, this.o().g()); + } + + public boolean isChunkLoaded(int i, int j, boolean flag) { + return this.a(i, j); + } + + public boolean setAir(BlockPosition blockposition, boolean flag) { + IBlockData iblockdata = this.getType(blockposition); + + if (iblockdata.isAir()) { + return false; + } else { + if (flag) { + iblockdata.a(this.g, blockposition, 0); + } + + return this.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 3); + } + } + + public boolean e(BlockPosition blockposition) { + return this.y(blockposition).c(blockposition); + } + + @Nullable + public TileEntity getTileEntity(BlockPosition blockposition) { + IChunkAccess ichunkaccess = this.y(blockposition); + TileEntity tileentity = ichunkaccess.getTileEntity(blockposition); + + if (tileentity != null) { + return tileentity; + } else { + NBTTagCompound nbttagcompound = ichunkaccess.g(blockposition); + + if (nbttagcompound != null) { + if ("DUMMY".equals(nbttagcompound.getString("id"))) { + tileentity = ((ITileEntity) this.getType(blockposition).getBlock()).a(this.g); + } else { + tileentity = TileEntity.create(nbttagcompound); + } + + if (tileentity != null) { + ichunkaccess.a(blockposition, tileentity); + return tileentity; + } + } + + if (ichunkaccess.getType(blockposition).getBlock() instanceof ITileEntity) { + RegionLimitedWorldAccess.a.warn("Tried to access a block entity before it was created. {}", blockposition); + } + + return null; + } + } + + public boolean setTypeAndData(BlockPosition blockposition, IBlockData iblockdata, int i) { + IChunkAccess ichunkaccess = this.y(blockposition); + IBlockData iblockdata1 = ichunkaccess.setType(blockposition, iblockdata, false); + Block block = iblockdata.getBlock(); + + if (block.isTileEntity()) { + if (ichunkaccess.i().d() == ChunkStatus.Type.LEVELCHUNK) { + ichunkaccess.a(blockposition, ((ITileEntity) block).a(this)); + } else { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setInt("x", blockposition.getX()); + nbttagcompound.setInt("y", blockposition.getY()); + nbttagcompound.setInt("z", blockposition.getZ()); + nbttagcompound.setString("id", "DUMMY"); + ichunkaccess.a(nbttagcompound); + } + } else if (iblockdata1 != null && iblockdata1.getBlock().isTileEntity()) { + ichunkaccess.d(blockposition); + } + + if (iblockdata.l(this, blockposition)) { + this.i(blockposition); + } + + return true; + } + + private void i(BlockPosition blockposition) { + this.y(blockposition).e(blockposition); + } + + // CraftBukkit start + public boolean addEntity(Entity entity) { + return addEntity(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT); + } + + @Override + public boolean addEntity(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) { + // CraftBukkit end + int i = MathHelper.floor(entity.locX / 16.0D); + int j = MathHelper.floor(entity.locZ / 16.0D); + + this.getChunkAt(i, j).a(entity); + return true; + } + + public boolean setAir(BlockPosition blockposition) { + return this.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 3); + } + + public void a(EnumSkyBlock enumskyblock, BlockPosition blockposition, int i) { + this.y(blockposition).a(enumskyblock, this.l.g(), blockposition, i); + } + + public WorldBorder getWorldBorder() { + return this.g.getWorldBorder(); + } + + public boolean a(@Nullable Entity entity, VoxelShape voxelshape) { + return true; + } + + public int a(BlockPosition blockposition, EnumDirection enumdirection) { + return this.getType(blockposition).b((IBlockAccess) this, blockposition, enumdirection); + } + + public boolean e() { + return false; + } + + @Deprecated + public World getMinecraftWorld() { + return this.g; + } + + public WorldData getWorldData() { + return this.j; + } + + public DifficultyDamageScaler getDamageScaler(BlockPosition blockposition) { + if (!this.a(blockposition.getX() >> 4, blockposition.getZ() >> 4)) { + throw new RuntimeException("We are asking a region for a chunk out of bound"); + } else { + return new DifficultyDamageScaler(this.g.getDifficulty(), this.g.getDayTime(), 0L, this.g.ah()); + } + } + + @Nullable + public PersistentCollection h() { + return this.g.h(); + } + + public IChunkProvider getChunkProvider() { + return this.g.getChunkProvider(); + } + + public IDataManager getDataManager() { + return this.g.getDataManager(); + } + + public long getSeed() { + return this.h; + } + + public TickList getBlockTickList() { + return this.n; + } + + public TickList getFluidTickList() { + return this.o; + } + + public int getSeaLevel() { + return this.i; + } + + public Random m() { + return this.k; + } + + public void update(BlockPosition blockposition, Block block) {} + + public int a(HeightMap.Type heightmap_type, int i, int j) { + return this.getChunkAt(i >> 4, j >> 4).a(heightmap_type, i & 15, j & 15) + 1; + } + + public void a(@Nullable EntityHuman entityhuman, BlockPosition blockposition, SoundEffect soundeffect, SoundCategory soundcategory, float f, float f1) {} + + public void addParticle(ParticleParam particleparam, double d0, double d1, double d2, double d3, double d4, double d5) {} + + public BlockPosition getSpawn() { + return this.g.getSpawn(); + } + + public WorldProvider o() { + return this.l; + } +} diff --git a/src/main/java/net/minecraft/server/Registry.java b/src/main/java/net/minecraft/server/Registry.java new file mode 100644 index 000000000000..9efec49d6640 --- /dev/null +++ b/src/main/java/net/minecraft/server/Registry.java @@ -0,0 +1,8 @@ +package net.minecraft.server; + +import java.util.Iterator; +public interface Registry extends Iterable { // Paper - decompile fix + + @Override + Iterator iterator(); // Paper - decompile fix +} diff --git a/src/main/java/net/minecraft/server/RegistryBlockID.java b/src/main/java/net/minecraft/server/RegistryBlockID.java new file mode 100644 index 000000000000..93935e7c772d --- /dev/null +++ b/src/main/java/net/minecraft/server/RegistryBlockID.java @@ -0,0 +1,63 @@ +package net.minecraft.server; + +import com.google.common.base.Predicates; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; + +public class RegistryBlockID implements Registry { + + private int a; + private final IdentityHashMap b; + private final List c; + + public RegistryBlockID() { + this(512); + } + + public RegistryBlockID(int i) { + this.c = Lists.newArrayListWithExpectedSize(i); + this.b = new IdentityHashMap(i); + } + + public void a(T t0, int i) { + this.b.put(t0, i); + + while (this.c.size() <= i) { + this.c.add(null); // Paper - decompile fix + } + + this.c.set(i, t0); + if (this.a <= i) { + this.a = i + 1; + } + + } + + public void b(T t0) { + this.a(t0, this.a); + } + + public int getId(T t0) { + Integer integer = (Integer) this.b.get(t0); + + return integer == null ? -1 : integer; + } + + @Nullable + public final T fromId(int i) { + return i >= 0 && i < this.c.size() ? this.c.get(i) : null; + } + + public Iterator iterator() { + return Iterators.filter(this.c.iterator(), Predicates.notNull()); + } + + public int size() { return this.a(); } // Paper - OBFHELPER + public int a() { + return this.b.size(); + } +} diff --git a/src/main/java/net/minecraft/server/RegistryID.java b/src/main/java/net/minecraft/server/RegistryID.java new file mode 100644 index 000000000000..37641fa8652d --- /dev/null +++ b/src/main/java/net/minecraft/server/RegistryID.java @@ -0,0 +1,166 @@ +package net.minecraft.server; + +import com.google.common.base.Predicates; +import com.google.common.collect.Iterators; +import java.util.Arrays; +import java.util.Iterator; +import javax.annotation.Nullable; + +public class RegistryID implements Registry { + + private static final Object a = null; + private K[] b; + private int[] c; + private K[] d; + private int e; + private int f; + private java.util.BitSet usedIds; // Paper + + public RegistryID(int i) { + i = (int) ((float) i / 0.8F); + this.b = (K[]) (new Object[i]); // Paper - decompile fix + this.c = new int[i]; + this.d = (K[]) (new Object[i]); // Paper - decompile fix + this.usedIds = new java.util.BitSet(); // Paper + } + + public int getId(@Nullable K k0) { + return this.c(this.b(k0, this.d(k0))); + } + + @Nullable + public K fromId(int i) { + return i >= 0 && i < this.d.length ? this.d[i] : null; + } + + private int c(int i) { + return i == -1 ? -1 : this.c[i]; + } + + public int c(K k0) { + int i = this.c(); + + this.a(k0, i); + return i; + } + + private int c() { + // Paper start + /* + while (this.e < this.d.length && this.d[this.e] != null) { + ++this.e; + } + */ + this.e = this.usedIds.nextClearBit(0); + // Paper end + + return this.e; + } + + private void d(int i) { + K[] ak = this.b; + int[] aint = this.c; + + this.b = (K[]) (new Object[i]); // Paper - decompile fix + this.c = new int[i]; + this.d = (K[]) (new Object[i]); // Paper - decompile fix + this.e = 0; + this.f = 0; + this.usedIds.clear(); // Paper + + for (int j = 0; j < ak.length; ++j) { + if (ak[j] != null) { + this.a(ak[j], aint[j]); + } + } + + } + + public void a(K k0, int i) { + int j = Math.max(i, this.f + 1); + int k; + + if ((float) j >= (float) this.b.length * 0.8F) { + for (k = this.b.length << 1; k < i; k <<= 1) { + ; + } + + this.d(k); + } + + k = this.e(this.d(k0)); + this.b[k] = k0; + this.c[k] = i; + this.d[i] = k0; + this.usedIds.set(i); // Paper + ++this.f; + if (i == this.e) { + ++this.e; + } + + } + + private int d(@Nullable K k0) { + return (MathHelper.f(System.identityHashCode(k0)) & Integer.MAX_VALUE) % this.b.length; + } + + private int b(@Nullable K k0, int i) { + int j; + + for (j = i; j < this.b.length; ++j) { + if (this.b[j] == k0) { + return j; + } + + if (this.b[j] == RegistryID.a) { + return -1; + } + } + + for (j = 0; j < i; ++j) { + if (this.b[j] == k0) { + return j; + } + + if (this.b[j] == RegistryID.a) { + return -1; + } + } + + return -1; + } + + private int e(int i) { + int j; + + for (j = i; j < this.b.length; ++j) { + if (this.b[j] == RegistryID.a) { + return j; + } + } + + for (j = 0; j < i; ++j) { + if (this.b[j] == RegistryID.a) { + return j; + } + } + + throw new RuntimeException("Overflowed :("); + } + + public Iterator iterator() { + return Iterators.filter(Iterators.forArray(this.d), Predicates.notNull()); + } + + public void a() { + Arrays.fill(this.b, (Object) null); + Arrays.fill(this.d, (Object) null); + this.e = 0; + this.f = 0; + this.usedIds.clear(); // Paper + } + + public int b() { + return this.f; + } +} diff --git a/src/main/java/net/minecraft/server/RegistryMaterials.java b/src/main/java/net/minecraft/server/RegistryMaterials.java new file mode 100644 index 000000000000..78de740acf7b --- /dev/null +++ b/src/main/java/net/minecraft/server/RegistryMaterials.java @@ -0,0 +1,102 @@ +package net.minecraft.server; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Random; +import java.util.Set; +import javax.annotation.Nullable; +import org.apache.commons.lang3.Validate; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class RegistryMaterials implements IRegistry { + + protected static final Logger a = LogManager.getLogger(); + protected final RegistryID b = new RegistryID(2048); // Paper - use bigger expected size to reduce collisions + protected final BiMap c = HashBiMap.create(2048); // Paper - use bigger expected size to reduce collisions + protected V[] d; // Paper - Decompile fix + private int x; + + public RegistryMaterials() {} + + public void a(int i, MinecraftKey minecraftkey, V v0) { + this.b.a(v0, i); + Validate.notNull(minecraftkey); + Validate.notNull(v0); + this.d = null; + if (this.c.containsKey(minecraftkey)) { + RegistryMaterials.a.debug("Adding duplicate key '{}' to registry", minecraftkey); + } + + this.c.put(minecraftkey, v0); + if (this.x <= i) { + this.x = i + 1; + } + + } + + public void a(MinecraftKey minecraftkey, V v0) { + this.a(this.x, minecraftkey, v0); + } + + @Nullable + public MinecraftKey getKey(V v0) { + return (MinecraftKey) this.c.inverse().get(v0); + } + + public V getOrDefault(@Nullable MinecraftKey minecraftkey) { + throw new UnsupportedOperationException("No default value"); + } + + public MinecraftKey b() { + throw new UnsupportedOperationException("No default key"); + } + + public int a(@Nullable V v0) { + return this.b.getId(v0); + } + + @Nullable + public V fromId(int i) { + return this.b.fromId(i); + } + + public Iterator iterator() { + return this.b.iterator(); + } + + @Nullable + public V get(@Nullable MinecraftKey minecraftkey) { + return this.c.get(minecraftkey); + } + + public Set keySet() { + return Collections.unmodifiableSet(this.c.keySet()); + } + + public boolean d() { + return this.c.isEmpty(); + } + + @Nullable + public V a(Random random) { + if (this.d == null) { + Collection collection = this.c.values(); + + if (collection.isEmpty()) { + return null; + } + + this.d = (V[]) collection.toArray(new Object[collection.size()]); // Paper - Decompile fix + } + + return this.d[random.nextInt(this.d.length)]; + } + + public boolean c(MinecraftKey minecraftkey) { + return this.c.containsKey(minecraftkey); + } +} diff --git a/src/main/java/net/minecraft/server/RemoteControlCommandListener.java b/src/main/java/net/minecraft/server/RemoteControlCommandListener.java new file mode 100644 index 000000000000..b61766292315 --- /dev/null +++ b/src/main/java/net/minecraft/server/RemoteControlCommandListener.java @@ -0,0 +1,52 @@ +package net.minecraft.server; + +public class RemoteControlCommandListener implements ICommandListener { + + private final StringBuffer a = new StringBuffer(); + private final MinecraftServer b; + + public RemoteControlCommandListener(MinecraftServer minecraftserver) { + this.b = minecraftserver; + } + + public void clearMessages() { + this.a.setLength(0); + } + + public String getMessages() { + return this.a.toString(); + } + + public CommandListenerWrapper f() { + WorldServer worldserver = this.b.getWorldServer(DimensionManager.OVERWORLD); + + return new CommandListenerWrapper(this, new Vec3D(worldserver.getSpawn()), Vec2F.a, worldserver, 4, "Recon", new ChatComponentText("Rcon"), this.b, (Entity) null); + } + + // CraftBukkit start - Send a String + public void sendMessage(String message) { + this.a.append(message); + } + + @Override + public org.bukkit.command.CommandSender getBukkitSender(CommandListenerWrapper wrapper) { + return b.remoteConsole; + } + // CraftBukkit end + + public void sendMessage(IChatBaseComponent ichatbasecomponent) { + this.a.append(ichatbasecomponent.getString()); + } + + public boolean a() { + return true; + } + + public boolean b() { + return true; + } + + public boolean B_() { + return this.b.k(); + } +} diff --git a/src/main/java/net/minecraft/server/RemoteControlListener.java b/src/main/java/net/minecraft/server/RemoteControlListener.java new file mode 100644 index 000000000000..7b82c4dea281 --- /dev/null +++ b/src/main/java/net/minecraft/server/RemoteControlListener.java @@ -0,0 +1,111 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketTimeoutException; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +public class RemoteControlListener extends RemoteConnectionThread { + + private int h; + private final int i; + private String j; + private ServerSocket k; + private final String l; + private Map m; + + public RemoteControlListener(IMinecraftServer iminecraftserver) { + super(iminecraftserver, "RCON Listener"); + this.h = iminecraftserver.a("rcon.port", 0); + this.l = iminecraftserver.a("rcon.password", ""); + this.j = iminecraftserver.a("rcon.ip", ((DedicatedServer) iminecraftserver).getServerIp()); // Paper + this.i = iminecraftserver.e_(); + if (0 == this.h) { + this.h = this.i + 10; + this.b("Setting default rcon port to " + this.h); + iminecraftserver.a("rcon.port", (Object) this.h); + if (this.l.isEmpty()) { + iminecraftserver.a("rcon.password", (Object) ""); + } + + iminecraftserver.c_(); + } + + if (this.j.isEmpty()) { + this.j = "0.0.0.0"; + } + + this.f(); + this.k = null; + } + + private void f() { + this.m = Maps.newHashMap(); + } + + private void g() { + Iterator iterator = this.m.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + + if (!((RemoteControlSession) entry.getValue()).c()) { + iterator.remove(); + } + } + + } + + public void run() { + this.b("RCON running on " + this.j + ":" + this.h); + + try { + while (this.a) { + try { + Socket socket = this.k.accept(); + + socket.setSoTimeout(500); + RemoteControlSession remotecontrolsession = new RemoteControlSession(this.b, socket); + + remotecontrolsession.a(); + this.m.put(socket.getRemoteSocketAddress(), remotecontrolsession); + this.g(); + } catch (SocketTimeoutException sockettimeoutexception) { + this.g(); + } catch (IOException ioexception) { + if (this.a) { + this.b("IO: " + ioexception.getMessage()); + } + } + } + } finally { + this.b(this.k); + } + + } + + public void a() { + if (this.l.isEmpty()) { + this.c("No rcon password set in '" + this.b.d_() + "', rcon disabled!"); + } else if (0 < this.h && 65535 >= this.h) { + if (!this.a) { + try { + this.k = new ServerSocket(this.h, 0, InetAddress.getByName(this.j)); + this.k.setSoTimeout(500); + super.a(); + } catch (IOException ioexception) { + this.c("Unable to initialise rcon on " + this.j + ":" + this.h + " : " + ioexception.getMessage()); + } + + } + } else { + this.c("Invalid rcon port " + this.h + " found in '" + this.b.d_() + "', rcon disabled!"); + } + } +} diff --git a/src/main/java/net/minecraft/server/SchedulerBatch.java b/src/main/java/net/minecraft/server/SchedulerBatch.java new file mode 100644 index 000000000000..f214a74a296e --- /dev/null +++ b/src/main/java/net/minecraft/server/SchedulerBatch.java @@ -0,0 +1,70 @@ +package net.minecraft.server; + +import java.util.concurrent.CompletableFuture; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class SchedulerBatch, R> { + + private static final Logger a = LogManager.getLogger(); + private final Scheduler b; + private boolean c; + private int d = 1000; + private final java.util.concurrent.locks.ReentrantLock lock = new java.util.concurrent.locks.ReentrantLock(true); // Paper + + public SchedulerBatch(Scheduler scheduler) { + this.b = scheduler; + } + + public void a() throws InterruptedException { + this.b.b(); + } + + public void startBatch() { b(); } // Paper - OBFHELPER + public void b() { + lock.lock(); // Paper + if (false && this.c) { // Paper + throw new RuntimeException("Batch already started."); + } else { + this.d = 1000; + this.c = true; + } + } + + public CompletableFuture add(K k0) { return a(k0); } // Paper - OBFHELPER + public CompletableFuture a(K k0) { + if (!this.c) { + throw new RuntimeException("Batch not properly started. Please use startBatch to create a new batch."); + } else { + CompletableFuture completablefuture = this.b.a(k0); + + --this.d; + if (this.d == 0) { + completablefuture = this.b.a(); + this.d = 1000; + } + + return completablefuture; + } + } + + public CompletableFuture executeBatch() { return c(); } // Paper - OBFHELPER + public CompletableFuture c() { + // Paper start + if (!lock.isHeldByCurrentThread()) { + throw new IllegalStateException("Current thread does not hold the write lock"); + } + try {// Paper end + if (false && !this.c) { // Paper + throw new RuntimeException("Batch not properly started. Please use startBatch to create a new batch."); + } else { + if (this.d != 1000) { + this.b.a(); + } + + this.c = false; + return this.b.c(); + } + } finally { lock.unlock(); } // Paper + } +} diff --git a/src/main/java/net/minecraft/server/ScoreboardServer.java b/src/main/java/net/minecraft/server/ScoreboardServer.java new file mode 100644 index 000000000000..cf89c32870a6 --- /dev/null +++ b/src/main/java/net/minecraft/server/ScoreboardServer.java @@ -0,0 +1,244 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import javax.annotation.Nullable; + +public class ScoreboardServer extends Scoreboard { + + private final MinecraftServer a; + private final Set b = Sets.newHashSet(); + private Runnable[] c = new Runnable[0]; + + public ScoreboardServer(MinecraftServer minecraftserver) { + this.a = minecraftserver; + } + + public void handleScoreChanged(ScoreboardScore scoreboardscore) { + super.handleScoreChanged(scoreboardscore); + if (this.b.contains(scoreboardscore.getObjective())) { + this.sendAll(new PacketPlayOutScoreboardScore(ScoreboardServer.Action.CHANGE, scoreboardscore.getObjective().getName(), scoreboardscore.getPlayerName(), scoreboardscore.getScore())); + } + + this.b(); + } + + public void handlePlayerRemoved(String s) { + super.handlePlayerRemoved(s); + this.sendAll(new PacketPlayOutScoreboardScore(ScoreboardServer.Action.REMOVE, (String) null, s, 0)); + this.b(); + } + + public void a(String s, ScoreboardObjective scoreboardobjective) { + super.a(s, scoreboardobjective); + if (this.b.contains(scoreboardobjective)) { + this.sendAll(new PacketPlayOutScoreboardScore(ScoreboardServer.Action.REMOVE, scoreboardobjective.getName(), s, 0)); + } + + this.b(); + } + + public void setDisplaySlot(int i, @Nullable ScoreboardObjective scoreboardobjective) { + ScoreboardObjective scoreboardobjective1 = this.getObjectiveForSlot(i); + + super.setDisplaySlot(i, scoreboardobjective); + if (scoreboardobjective1 != scoreboardobjective && scoreboardobjective1 != null) { + if (this.h(scoreboardobjective1) > 0) { + this.sendAll(new PacketPlayOutScoreboardDisplayObjective(i, scoreboardobjective)); + } else { + this.g(scoreboardobjective1); + } + } + + if (scoreboardobjective != null) { + if (this.b.contains(scoreboardobjective)) { + this.sendAll(new PacketPlayOutScoreboardDisplayObjective(i, scoreboardobjective)); + } else { + this.e(scoreboardobjective); + } + } + + this.b(); + } + + public boolean addPlayerToTeam(String s, ScoreboardTeam scoreboardteam) { + if (super.addPlayerToTeam(s, scoreboardteam)) { + this.sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, Arrays.asList(s), 3)); + this.b(); + return true; + } else { + return false; + } + } + + public void removePlayerFromTeam(String s, ScoreboardTeam scoreboardteam) { + super.removePlayerFromTeam(s, scoreboardteam); + this.sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, Arrays.asList(s), 4)); + this.b(); + } + + public void handleObjectiveAdded(ScoreboardObjective scoreboardobjective) { + super.handleObjectiveAdded(scoreboardobjective); + this.b(); + } + + public void handleObjectiveChanged(ScoreboardObjective scoreboardobjective) { + super.handleObjectiveChanged(scoreboardobjective); + if (this.b.contains(scoreboardobjective)) { + this.sendAll(new PacketPlayOutScoreboardObjective(scoreboardobjective, 2)); + } + + this.b(); + } + + public void handleObjectiveRemoved(ScoreboardObjective scoreboardobjective) { + super.handleObjectiveRemoved(scoreboardobjective); + if (this.b.contains(scoreboardobjective)) { + this.g(scoreboardobjective); + } + + this.b(); + } + + public void handleTeamAdded(ScoreboardTeam scoreboardteam) { + super.handleTeamAdded(scoreboardteam); + this.sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, 0)); + this.b(); + } + + public void handleTeamChanged(ScoreboardTeam scoreboardteam) { + super.handleTeamChanged(scoreboardteam); + this.sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, 2)); + this.b(); + } + + public void handleTeamRemoved(ScoreboardTeam scoreboardteam) { + super.handleTeamRemoved(scoreboardteam); + this.sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, 1)); + this.b(); + } + + public void a(Runnable runnable) { + this.c = (Runnable[]) Arrays.copyOf(this.c, this.c.length + 1); + this.c[this.c.length - 1] = runnable; + } + + protected void b() { + Runnable[] arunnable = this.c; + int i = arunnable.length; + + for (int j = 0; j < i; ++j) { + Runnable runnable = arunnable[j]; + + runnable.run(); + } + + } + + public List> getScoreboardScorePacketsForObjective(ScoreboardObjective scoreboardobjective) { + List> list = Lists.newArrayList(); + + list.add(new PacketPlayOutScoreboardObjective(scoreboardobjective, 0)); + + for (int i = 0; i < 19; ++i) { + if (this.getObjectiveForSlot(i) == scoreboardobjective) { + list.add(new PacketPlayOutScoreboardDisplayObjective(i, scoreboardobjective)); + } + } + + Iterator iterator = this.getScoresForObjective(scoreboardobjective).iterator(); + + while (iterator.hasNext()) { + ScoreboardScore scoreboardscore = (ScoreboardScore) iterator.next(); + + list.add(new PacketPlayOutScoreboardScore(ScoreboardServer.Action.CHANGE, scoreboardscore.getObjective().getName(), scoreboardscore.getPlayerName(), scoreboardscore.getScore())); + } + + return list; + } + + public void e(ScoreboardObjective scoreboardobjective) { + List> list = this.getScoreboardScorePacketsForObjective(scoreboardobjective); + Iterator iterator = this.a.getPlayerList().v().iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + if (entityplayer.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) { + Packet packet = (Packet) iterator1.next(); + + entityplayer.playerConnection.sendPacket(packet); + } + } + + this.b.add(scoreboardobjective); + } + + public List> f(ScoreboardObjective scoreboardobjective) { + List> list = Lists.newArrayList(); + + list.add(new PacketPlayOutScoreboardObjective(scoreboardobjective, 1)); + + for (int i = 0; i < 19; ++i) { + if (this.getObjectiveForSlot(i) == scoreboardobjective) { + list.add(new PacketPlayOutScoreboardDisplayObjective(i, scoreboardobjective)); + } + } + + return list; + } + + public void g(ScoreboardObjective scoreboardobjective) { + List> list = this.f(scoreboardobjective); + Iterator iterator = this.a.getPlayerList().v().iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + if (entityplayer.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) { + Packet packet = (Packet) iterator1.next(); + + entityplayer.playerConnection.sendPacket(packet); + } + } + + this.b.remove(scoreboardobjective); + } + + public int h(ScoreboardObjective scoreboardobjective) { + int i = 0; + + for (int j = 0; j < 19; ++j) { + if (this.getObjectiveForSlot(j) == scoreboardobjective) { + ++i; + } + } + + return i; + } + + // CraftBukkit start - Send to players + private void sendAll(Packet packet) { + for (EntityPlayer entityplayer : (List) this.a.getPlayerList().players) { + if (entityplayer.getBukkitEntity().getScoreboard().getHandle() == this) { + entityplayer.playerConnection.sendPacket(packet); + } + } + } + // CraftBukkit end + + public static enum Action { + + CHANGE, REMOVE; + + private Action() {} + } +} diff --git a/src/main/java/net/minecraft/server/SecondaryWorldServer.java b/src/main/java/net/minecraft/server/SecondaryWorldServer.java new file mode 100644 index 000000000000..cca30db6d34e --- /dev/null +++ b/src/main/java/net/minecraft/server/SecondaryWorldServer.java @@ -0,0 +1,62 @@ +package net.minecraft.server; + +public class SecondaryWorldServer extends WorldServer { + + // CraftBukkit start - Add WorldData, Environment and ChunkGenerator arguments + public SecondaryWorldServer(MinecraftServer minecraftserver, IDataManager idatamanager, DimensionManager dimensionmanager, WorldServer worldserver, MethodProfiler methodprofiler, WorldData worldData, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) { + super(minecraftserver, idatamanager, worldserver.h(), worldData, dimensionmanager, methodprofiler, env, gen); + // CraftBukkit end + /* CraftBukkit start + worldserver.getWorldBorder().a(new IWorldBorderListener() { + public void a(WorldBorder worldborder, double d0) { + SecondaryWorldServer.this.getWorldBorder().setSize(d0); + } + + public void a(WorldBorder worldborder, double d0, double d1, long i) { + SecondaryWorldServer.this.getWorldBorder().transitionSizeBetween(d0, d1, i); + } + + public void a(WorldBorder worldborder, double d0, double d1) { + SecondaryWorldServer.this.getWorldBorder().setCenter(d0, d1); + } + + public void a(WorldBorder worldborder, int i) { + SecondaryWorldServer.this.getWorldBorder().setWarningTime(i); + } + + public void b(WorldBorder worldborder, int i) { + SecondaryWorldServer.this.getWorldBorder().setWarningDistance(i); + } + + public void b(WorldBorder worldborder, double d0) { + SecondaryWorldServer.this.getWorldBorder().setDamageAmount(d0); + } + + public void c(WorldBorder worldborder, double d0) { + SecondaryWorldServer.this.getWorldBorder().setDamageBuffer(d0); + } + }); + // CraftBukkit end */ + } + + // protected void a() {} // CraftBukkit + + public SecondaryWorldServer i_() { + String s = PersistentVillage.a(this.worldProvider); + PersistentVillage persistentvillage = (PersistentVillage) this.a(DimensionManager.OVERWORLD, PersistentVillage::new, s); + + if (persistentvillage == null) { + this.villages = new PersistentVillage(this); + this.a(DimensionManager.OVERWORLD, s, (PersistentBase) this.villages); + } else { + this.villages = persistentvillage; + this.villages.a((World) this); + } + + return (SecondaryWorldServer) super.i_(); // CraftBukkit + } + + public void t_() { + this.worldProvider.k(); + } +} diff --git a/src/main/java/net/minecraft/server/ServerConnection.java b/src/main/java/net/minecraft/server/ServerConnection.java new file mode 100644 index 000000000000..baabcd19c4ca --- /dev/null +++ b/src/main/java/net/minecraft/server/ServerConnection.java @@ -0,0 +1,164 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.epoll.Epoll; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.epoll.EpollServerSocketChannel; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.ServerSocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.timeout.ReadTimeoutHandler; +import io.netty.util.concurrent.Future; +import java.io.IOException; +import java.net.InetAddress; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ServerConnection { + + private static final Logger d = LogManager.getLogger(); + public static final LazyInitVar a = new LazyInitVar<>(() -> { + return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Server IO #%d").setDaemon(true).build()); + }); + public static final LazyInitVar b = new LazyInitVar<>(() -> { + return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Server IO #%d").setDaemon(true).build()); + }); + private final MinecraftServer e; + public volatile boolean c; + private final List f = Collections.synchronizedList(Lists.newArrayList()); + private final List g = Collections.synchronizedList(Lists.newArrayList()); + // Paper start - prevent blocking on adding a new network manager while the server is ticking + private final List pending = Collections.synchronizedList(Lists.newArrayList()); + private void addPending() { + synchronized (pending) { + this.g.addAll(pending); // Paper - OBFHELPER - List of network managers + pending.clear(); + } + } + // Paper end + + public ServerConnection(MinecraftServer minecraftserver) { + this.e = minecraftserver; + this.c = true; + } + + public void a(@Nullable InetAddress inetaddress, int i) throws IOException { + List list = this.f; + + synchronized (this.f) { + Class oclass; + LazyInitVar lazyinitvar; + + if (Epoll.isAvailable() && this.e.V()) { + oclass = EpollServerSocketChannel.class; + lazyinitvar = ServerConnection.b; + ServerConnection.d.info("Using epoll channel type"); + } else { + oclass = NioServerSocketChannel.class; + lazyinitvar = ServerConnection.a; + ServerConnection.d.info("Using default channel type"); + } + + this.f.add(((ServerBootstrap) ((ServerBootstrap) (new ServerBootstrap()).channel(oclass)).childHandler(new ChannelInitializer() { + protected void initChannel(Channel channel) throws Exception { + try { + channel.config().setOption(ChannelOption.TCP_NODELAY, true); + } catch (ChannelException channelexception) { + ; + } + + channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30)).addLast("legacy_query", new LegacyPingHandler(ServerConnection.this)).addLast("splitter", new PacketSplitter()).addLast("decoder", new PacketDecoder(EnumProtocolDirection.SERVERBOUND)).addLast("prepender", new PacketPrepender()).addLast("encoder", new PacketEncoder(EnumProtocolDirection.CLIENTBOUND)); + NetworkManager networkmanager = new NetworkManager(EnumProtocolDirection.SERVERBOUND); + + pending.add(networkmanager); // Paper + channel.pipeline().addLast("packet_handler", networkmanager); + networkmanager.setPacketListener(new HandshakeListener(ServerConnection.this.e, networkmanager)); + } + }).group((EventLoopGroup) lazyinitvar.a()).localAddress(inetaddress, i)).bind().syncUninterruptibly()); + } + } + + public void b() { + this.c = false; + Iterator iterator = this.f.iterator(); + + while (iterator.hasNext()) { + ChannelFuture channelfuture = (ChannelFuture) iterator.next(); + + try { + channelfuture.channel().close().sync(); + } catch (InterruptedException interruptedexception) { + ServerConnection.d.error("Interrupted whilst closing channel"); + } + } + + } + + public void c() { + List list = this.g; + + synchronized (this.g) { + // Spigot Start + addPending(); // Paper + // This prevents players from 'gaming' the server, and strategically relogging to increase their position in the tick order + if ( org.spigotmc.SpigotConfig.playerShuffle > 0 && MinecraftServer.currentTick % org.spigotmc.SpigotConfig.playerShuffle == 0 ) + { + Collections.shuffle( this.g ); + } + // Spigot End + Iterator iterator = this.g.iterator(); + + while (iterator.hasNext()) { + NetworkManager networkmanager = (NetworkManager) iterator.next(); + + if (!networkmanager.h()) { + if (networkmanager.isConnected()) { + try { + networkmanager.a(); + } catch (Exception exception) { + if (networkmanager.isLocal()) { + CrashReport crashreport = CrashReport.a(exception, "Ticking memory connection"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Ticking connection"); + + crashreportsystemdetails.a("Connection", networkmanager::toString); + throw new ReportedException(crashreport); + } + + ServerConnection.d.warn("Failed to handle packet for {}", networkmanager.getSocketAddress(), exception); + ChatComponentText chatcomponenttext = new ChatComponentText("Internal server error"); + + networkmanager.sendPacket(new PacketPlayOutKickDisconnect(chatcomponenttext), (future) -> { + networkmanager.close(chatcomponenttext); + }); + networkmanager.stopReading(); + } + } else { + // Spigot Start + // Fix a race condition where a NetworkManager could be unregistered just before connection. + if (networkmanager.preparing) continue; + // Spigot End + iterator.remove(); + networkmanager.handleDisconnection(); + } + } + } + + } + } + + public MinecraftServer d() { + return this.e; + } +} diff --git a/src/main/java/net/minecraft/server/ServerPing.java b/src/main/java/net/minecraft/server/ServerPing.java new file mode 100644 index 000000000000..ea52e89bd965 --- /dev/null +++ b/src/main/java/net/minecraft/server/ServerPing.java @@ -0,0 +1,226 @@ +package net.minecraft.server; + +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.mojang.authlib.GameProfile; +import java.lang.reflect.Type; +import java.util.UUID; + +public class ServerPing { + + private IChatBaseComponent a; + private ServerPing.ServerPingPlayerSample b; + private ServerPing.ServerData c; + private String d; + + public ServerPing() {} + + public IChatBaseComponent a() { + return this.a; + } + + public void setMOTD(IChatBaseComponent ichatbasecomponent) { + this.a = ichatbasecomponent; + } + + public ServerPingPlayerSample getPlayers() { return b(); } // Paper - OBFHELPER + public ServerPing.ServerPingPlayerSample b() { + return this.b; + } + + public void setPlayerSample(ServerPing.ServerPingPlayerSample serverping_serverpingplayersample) { + this.b = serverping_serverpingplayersample; + } + + public ServerPing.ServerData getServerData() { + return this.c; + } + + public void setServerInfo(ServerPing.ServerData serverping_serverdata) { + this.c = serverping_serverdata; + } + + public void setFavicon(String s) { + this.d = s; + } + + public String d() { + return this.d; + } + + public static class Serializer implements JsonDeserializer, JsonSerializer { + + public Serializer() {} + + public ServerPing deserialize(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws JsonParseException { + JsonObject jsonobject = ChatDeserializer.m(jsonelement, "status"); + ServerPing serverping = new ServerPing(); + + if (jsonobject.has("description")) { + serverping.setMOTD((IChatBaseComponent) jsondeserializationcontext.deserialize(jsonobject.get("description"), IChatBaseComponent.class)); + } + + if (jsonobject.has("players")) { + serverping.setPlayerSample((ServerPing.ServerPingPlayerSample) jsondeserializationcontext.deserialize(jsonobject.get("players"), ServerPing.ServerPingPlayerSample.class)); + } + + if (jsonobject.has("version")) { + serverping.setServerInfo((ServerPing.ServerData) jsondeserializationcontext.deserialize(jsonobject.get("version"), ServerPing.ServerData.class)); + } + + if (jsonobject.has("favicon")) { + serverping.setFavicon(ChatDeserializer.h(jsonobject, "favicon")); + } + + return serverping; + } + + public JsonElement serialize(ServerPing serverping, Type type, JsonSerializationContext jsonserializationcontext) { + JsonObject jsonobject = new JsonObject(); + + if (serverping.a() != null) { + jsonobject.add("description", jsonserializationcontext.serialize(serverping.a())); + } + + if (serverping.b() != null) { + jsonobject.add("players", jsonserializationcontext.serialize(serverping.b())); + } + + if (serverping.getServerData() != null) { + jsonobject.add("version", jsonserializationcontext.serialize(serverping.getServerData())); + } + + if (serverping.d() != null) { + jsonobject.addProperty("favicon", serverping.d()); + } + + return jsonobject; + } + } + + public static class ServerData { + + private final String a; + private final int b; + + public ServerData(String s, int i) { + this.a = s; + this.b = i; + } + + public String a() { + return this.a; + } + + public int getProtocolVersion() { + return this.b; + } + + public static class Serializer implements JsonDeserializer, JsonSerializer { + + public Serializer() {} + + public ServerPing.ServerData deserialize(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws JsonParseException { + JsonObject jsonobject = ChatDeserializer.m(jsonelement, "version"); + + return new ServerPing.ServerData(ChatDeserializer.h(jsonobject, "name"), ChatDeserializer.n(jsonobject, "protocol")); + } + + public JsonElement serialize(ServerPing.ServerData serverping_serverdata, Type type, JsonSerializationContext jsonserializationcontext) { + JsonObject jsonobject = new JsonObject(); + + jsonobject.addProperty("name", serverping_serverdata.a()); + jsonobject.addProperty("protocol", serverping_serverdata.getProtocolVersion()); + return jsonobject; + } + } + } + + public static class ServerPingPlayerSample { + + private final int a; + private final int b; + private GameProfile[] c; + + public ServerPingPlayerSample(int i, int j) { + this.a = i; + this.b = j; + } + + public int a() { + return this.a; + } + + public int b() { + return this.b; + } + + public GameProfile[] getSample() { return c(); } // Paper - OBFHELPER + public GameProfile[] c() { + return this.c; + } + + public void setSample(GameProfile[] sample) { a(sample); } // Paper - OBFHELPER + public void a(GameProfile[] agameprofile) { + this.c = agameprofile; + } + + public static class Serializer implements JsonDeserializer, JsonSerializer { + + public Serializer() {} + + public ServerPing.ServerPingPlayerSample deserialize(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws JsonParseException { + JsonObject jsonobject = ChatDeserializer.m(jsonelement, "players"); + ServerPing.ServerPingPlayerSample serverping_serverpingplayersample = new ServerPing.ServerPingPlayerSample(ChatDeserializer.n(jsonobject, "max"), ChatDeserializer.n(jsonobject, "online")); + + if (ChatDeserializer.d(jsonobject, "sample")) { + JsonArray jsonarray = ChatDeserializer.u(jsonobject, "sample"); + + if (jsonarray.size() > 0) { + GameProfile[] agameprofile = new GameProfile[jsonarray.size()]; + + for (int i = 0; i < agameprofile.length; ++i) { + JsonObject jsonobject1 = ChatDeserializer.m(jsonarray.get(i), "player[" + i + "]"); + String s = ChatDeserializer.h(jsonobject1, "id"); + + agameprofile[i] = new GameProfile(UUID.fromString(s), ChatDeserializer.h(jsonobject1, "name")); + } + + serverping_serverpingplayersample.a(agameprofile); + } + } + + return serverping_serverpingplayersample; + } + + public JsonElement serialize(ServerPing.ServerPingPlayerSample serverping_serverpingplayersample, Type type, JsonSerializationContext jsonserializationcontext) { + JsonObject jsonobject = new JsonObject(); + + jsonobject.addProperty("max", serverping_serverpingplayersample.a()); + jsonobject.addProperty("online", serverping_serverpingplayersample.b()); + if (serverping_serverpingplayersample.c() != null && serverping_serverpingplayersample.c().length > 0) { + JsonArray jsonarray = new JsonArray(); + + for (int i = 0; i < serverping_serverpingplayersample.c().length; ++i) { + JsonObject jsonobject1 = new JsonObject(); + UUID uuid = serverping_serverpingplayersample.c()[i].getId(); + + jsonobject1.addProperty("id", uuid == null ? "" : uuid.toString()); + jsonobject1.addProperty("name", serverping_serverpingplayersample.c()[i].getName()); + jsonarray.add(jsonobject1); + } + + jsonobject.add("sample", jsonarray); + } + + return jsonobject; + } + } + } +} diff --git a/src/main/java/net/minecraft/server/ServerStatisticManager.java b/src/main/java/net/minecraft/server/ServerStatisticManager.java new file mode 100644 index 000000000000..d622983b239f --- /dev/null +++ b/src/main/java/net/minecraft/server/ServerStatisticManager.java @@ -0,0 +1,248 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonReader; +import com.mojang.datafixers.DataFixTypes; +import com.mojang.datafixers.DataFixer; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectIterator; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; +import org.apache.commons.io.FileUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ServerStatisticManager extends StatisticManager { + + private static final Logger b = LogManager.getLogger(); + private final MinecraftServer c; + private final File d; + private final Set> e = Sets.newHashSet(); + private int f = -300; + + public ServerStatisticManager(MinecraftServer minecraftserver, File file) { + this.c = minecraftserver; + this.d = file; + // Spigot start + for ( Map.Entry entry : org.spigotmc.SpigotConfig.forcedStats.entrySet() ) + { + Statistic wrapper = StatisticList.CUSTOM.b( entry.getKey() ); + this.a.put( wrapper, entry.getValue().intValue() ); + } + // Spigot end + if (file.isFile()) { + try { + this.a(minecraftserver.az(), FileUtils.readFileToString(file)); + } catch (IOException ioexception) { + ServerStatisticManager.b.error("Couldn't read statistics file {}", file, ioexception); + } catch (JsonParseException jsonparseexception) { + ServerStatisticManager.b.error("Couldn't parse statistics file {}", file, jsonparseexception); + } + } + + } + + public void a() { + if ( org.spigotmc.SpigotConfig.disableStatSaving ) return; // Spigot + try { + FileUtils.writeStringToFile(this.d, this.b()); + } catch (IOException ioexception) { + ServerStatisticManager.b.error("Couldn't save stats", ioexception); + } + + } + + public void setStatistic(EntityHuman entityhuman, Statistic statistic, int i) { + if ( org.spigotmc.SpigotConfig.disableStatSaving ) return; // Spigot + super.setStatistic(entityhuman, statistic, i); + this.e.add(statistic); + } + + private Set> d() { + Set> set = Sets.newHashSet(this.e); + + this.e.clear(); + return set; + } + + public void a(DataFixer datafixer, String s) { + try { + JsonReader jsonreader = new JsonReader(new StringReader(s)); + Throwable throwable = null; + + try { + jsonreader.setLenient(false); + JsonElement jsonelement = Streams.parse(jsonreader); + + if (!jsonelement.isJsonNull()) { + NBTTagCompound nbttagcompound = a(jsonelement.getAsJsonObject()); + + if (!nbttagcompound.hasKeyOfType("DataVersion", 99)) { + nbttagcompound.setInt("DataVersion", 1343); + } + + nbttagcompound = GameProfileSerializer.a(datafixer, DataFixTypes.STATS, nbttagcompound, nbttagcompound.getInt("DataVersion")); + if (nbttagcompound.hasKeyOfType("stats", 10)) { + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("stats"); + Iterator iterator = nbttagcompound1.getKeys().iterator(); + + while (iterator.hasNext()) { + String s1 = (String) iterator.next(); + + if (nbttagcompound1.hasKeyOfType(s1, 10)) { + StatisticWrapper statisticwrapper = (StatisticWrapper) IRegistry.STATS.get(new MinecraftKey(s1)); + + if (statisticwrapper == null) { + ServerStatisticManager.b.warn("Invalid statistic type in {}: Don't know what {} is", this.d, s1); + } else { + NBTTagCompound nbttagcompound2 = nbttagcompound1.getCompound(s1); + Iterator iterator1 = nbttagcompound2.getKeys().iterator(); + + while (iterator1.hasNext()) { + String s2 = (String) iterator1.next(); + + if (nbttagcompound2.hasKeyOfType(s2, 99)) { + Statistic statistic = this.a(statisticwrapper, s2); + + if (statistic == null) { + ServerStatisticManager.b.warn("Invalid statistic in {}: Don't know what {} is", this.d, s2); + } else { + this.a.put(statistic, nbttagcompound2.getInt(s2)); + } + } else { + ServerStatisticManager.b.warn("Invalid statistic value in {}: Don't know what {} is for key {}", this.d, nbttagcompound2.get(s2), s2); + } + } + } + } + } + } + } else { + ServerStatisticManager.b.error("Unable to parse Stat data from {}", this.d); + } + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (jsonreader != null) { + if (throwable != null) { + try { + jsonreader.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + jsonreader.close(); + } + } + + } + } catch (IOException | JsonParseException jsonparseexception) { + ServerStatisticManager.b.error("Unable to parse Stat data from {}", this.d, jsonparseexception); + } + + } + + @Nullable + private Statistic a(StatisticWrapper statisticwrapper, String s) { + MinecraftKey minecraftkey = MinecraftKey.a(s); + + if (minecraftkey == null) { + return null; + } else { + T t0 = statisticwrapper.a().get(minecraftkey); + + return t0 == null ? null : statisticwrapper.b(t0); + } + } + + private static NBTTagCompound a(JsonObject jsonobject) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + Iterator iterator = jsonobject.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + JsonElement jsonelement = (JsonElement) entry.getValue(); + + if (jsonelement.isJsonObject()) { + nbttagcompound.set((String) entry.getKey(), a(jsonelement.getAsJsonObject())); + } else if (jsonelement.isJsonPrimitive()) { + JsonPrimitive jsonprimitive = jsonelement.getAsJsonPrimitive(); + + if (jsonprimitive.isNumber()) { + nbttagcompound.setInt((String) entry.getKey(), jsonprimitive.getAsInt()); + } + } + } + + return nbttagcompound; + } + + protected String b() { + Map, JsonObject> map = Maps.newHashMap(); + ObjectIterator objectiterator = this.a.object2IntEntrySet().iterator(); + + while (objectiterator.hasNext()) { + it.unimi.dsi.fastutil.objects.Object2IntMap.Entry> it_unimi_dsi_fastutil_objects_object2intmap_entry = (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry) objectiterator.next(); + Statistic statistic = (Statistic) it_unimi_dsi_fastutil_objects_object2intmap_entry.getKey(); + + ((JsonObject) map.computeIfAbsent(statistic.a(), (statisticwrapper) -> { + return new JsonObject(); + })).addProperty(b(statistic).toString(), it_unimi_dsi_fastutil_objects_object2intmap_entry.getIntValue()); + } + + JsonObject jsonobject = new JsonObject(); + Iterator iterator = map.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry, JsonObject> entry = (Entry) iterator.next(); + + jsonobject.add(IRegistry.STATS.getKey(entry.getKey()).toString(), (JsonElement) entry.getValue()); + } + + JsonObject jsonobject1 = new JsonObject(); + + jsonobject1.add("stats", jsonobject); + jsonobject1.addProperty("DataVersion", 1631); + return jsonobject1.toString(); + } + + private static MinecraftKey b(Statistic statistic) { + return statistic.a().a().getKey(statistic.b()); + } + + public void c() { + this.e.addAll(this.a.keySet()); + } + + public void a(EntityPlayer entityplayer) { + int i = this.c.ah(); + Object2IntMap> object2intmap = new Object2IntOpenHashMap(); + + if (i - this.f > 300) { + this.f = i; + Iterator iterator = this.d().iterator(); + + while (iterator.hasNext()) { + Statistic statistic = (Statistic) iterator.next(); + + object2intmap.put(statistic, this.getStatisticValue(statistic)); + } + } + + entityplayer.playerConnection.sendPacket(new PacketPlayOutStatistic(object2intmap)); + } +} diff --git a/src/main/java/net/minecraft/server/ShapeDetector.java b/src/main/java/net/minecraft/server/ShapeDetector.java new file mode 100644 index 000000000000..8716f5fd9526 --- /dev/null +++ b/src/main/java/net/minecraft/server/ShapeDetector.java @@ -0,0 +1,175 @@ +package net.minecraft.server; + +import com.google.common.base.MoreObjects; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import java.util.Iterator; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public class ShapeDetector { + + private final Predicate[][][] a; + private final int b; + private final int c; + private final int d; + + public ShapeDetector(Predicate[][][] apredicate) { + this.a = apredicate; + this.b = apredicate.length; + if (this.b > 0) { + this.c = apredicate[0].length; + if (this.c > 0) { + this.d = apredicate[0][0].length; + } else { + this.d = 0; + } + } else { + this.c = 0; + this.d = 0; + } + + } + + public int a() { + return this.b; + } + + public int b() { + return this.c; + } + + public int c() { + return this.d; + } + + @Nullable + private ShapeDetector.ShapeDetectorCollection a(BlockPosition blockposition, EnumDirection enumdirection, EnumDirection enumdirection1, LoadingCache loadingcache) { + for (int i = 0; i < this.d; ++i) { + for (int j = 0; j < this.c; ++j) { + for (int k = 0; k < this.b; ++k) { + if (!this.a[k][j][i].test(loadingcache.getUnchecked(a(blockposition, enumdirection, enumdirection1, i, j, k)))) { + return null; + } + } + } + } + + return new ShapeDetector.ShapeDetectorCollection(blockposition, enumdirection, enumdirection1, loadingcache, this.d, this.c, this.b); + } + + @Nullable + public ShapeDetector.ShapeDetectorCollection a(IWorldReader iworldreader, BlockPosition blockposition) { + LoadingCache loadingcache = a(iworldreader, false); + int i = Math.max(Math.max(this.d, this.c), this.b); + Iterator iterator = BlockPosition.a(blockposition, blockposition.a(i - 1, i - 1, i - 1)).iterator(); + + while (iterator.hasNext()) { + BlockPosition blockposition1 = (BlockPosition) iterator.next(); + EnumDirection[] aenumdirection = EnumDirection.values(); + int j = aenumdirection.length; + + for (int k = 0; k < j; ++k) { + EnumDirection enumdirection = aenumdirection[k]; + EnumDirection[] aenumdirection1 = EnumDirection.values(); + int l = aenumdirection1.length; + + for (int i1 = 0; i1 < l; ++i1) { + EnumDirection enumdirection1 = aenumdirection1[i1]; + + if (enumdirection1 != enumdirection && enumdirection1 != enumdirection.opposite()) { + ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = this.a(blockposition1, enumdirection, enumdirection1, loadingcache); + + if (shapedetector_shapedetectorcollection != null) { + return shapedetector_shapedetectorcollection; + } + } + } + } + } + + return null; + } + + public static LoadingCache a(IWorldReader iworldreader, boolean flag) { + return CacheBuilder.newBuilder().build(new ShapeDetector.BlockLoader(iworldreader, flag)); + } + + protected static BlockPosition a(BlockPosition blockposition, EnumDirection enumdirection, EnumDirection enumdirection1, int i, int j, int k) { + if (enumdirection != enumdirection1 && enumdirection != enumdirection1.opposite()) { + BaseBlockPosition baseblockposition = new BaseBlockPosition(enumdirection.getAdjacentX(), enumdirection.getAdjacentY(), enumdirection.getAdjacentZ()); + BaseBlockPosition baseblockposition1 = new BaseBlockPosition(enumdirection1.getAdjacentX(), enumdirection1.getAdjacentY(), enumdirection1.getAdjacentZ()); + BaseBlockPosition baseblockposition2 = baseblockposition.d(baseblockposition1); + + return blockposition.a(baseblockposition1.getX() * -j + baseblockposition2.getX() * i + baseblockposition.getX() * k, baseblockposition1.getY() * -j + baseblockposition2.getY() * i + baseblockposition.getY() * k, baseblockposition1.getZ() * -j + baseblockposition2.getZ() * i + baseblockposition.getZ() * k); + } else { + throw new IllegalArgumentException("Invalid forwards & up combination"); + } + } + + public static class ShapeDetectorCollection { + + private final BlockPosition a; + private final EnumDirection b; + private final EnumDirection c; + private final LoadingCache d; + private final int e; + private final int f; + private final int g; + + public ShapeDetectorCollection(BlockPosition blockposition, EnumDirection enumdirection, EnumDirection enumdirection1, LoadingCache loadingcache, int i, int j, int k) { + this.a = blockposition; + this.b = enumdirection; + this.c = enumdirection1; + this.d = loadingcache; + this.e = i; + this.f = j; + this.g = k; + } + + public BlockPosition a() { + return this.a; + } + + public EnumDirection getFacing() { + return this.b; + } + + public EnumDirection c() { + return this.c; + } + + public final int getWidth() { return this.d(); } // Paper - OBFHELPER + public int d() { + return this.e; + } + + public int e() { + return this.f; + } + + public ShapeDetectorBlock a(int i, int j, int k) { + return (ShapeDetectorBlock) this.d.getUnchecked(ShapeDetector.a(this.a, this.getFacing(), this.c(), i, j, k)); + } + + public String toString() { + return MoreObjects.toStringHelper(this).add("up", this.c).add("forwards", this.b).add("frontTopLeft", this.a).toString(); + } + } + + static class BlockLoader extends CacheLoader { + + private final IWorldReader a; + private final boolean b; + + public BlockLoader(IWorldReader iworldreader, boolean flag) { + this.a = iworldreader; + this.b = flag; + } + + public ShapeDetectorBlock load(BlockPosition blockposition) throws Exception { + return new ShapeDetectorBlock(this.a, blockposition, this.b); + } + } +} diff --git a/src/main/java/net/minecraft/server/ShapedRecipes.java b/src/main/java/net/minecraft/server/ShapedRecipes.java new file mode 100644 index 000000000000..b8f4ddeb9cde --- /dev/null +++ b/src/main/java/net/minecraft/server/ShapedRecipes.java @@ -0,0 +1,373 @@ +package net.minecraft.server; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSyntaxException; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +// CraftBukkit start +import java.util.ArrayList; +import java.util.List; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.inventory.CraftRecipe; +import org.bukkit.craftbukkit.inventory.CraftShapedRecipe; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.inventory.RecipeChoice; +// CraftBukkit end + +public class ShapedRecipes implements IRecipe { + + private final int width; + private final int height; + private final NonNullList items; + private final ItemStack result; + private final MinecraftKey key; + private final String group; + + public ShapedRecipes(MinecraftKey minecraftkey, String s, int i, int j, NonNullList nonnulllist, ItemStack itemstack) { + this.key = minecraftkey; + this.group = s; + this.width = i; + this.height = j; + this.items = nonnulllist; + this.result = itemstack; + } + + // CraftBukkit start + public org.bukkit.inventory.ShapedRecipe toBukkitRecipe() { + CraftItemStack result = CraftItemStack.asCraftMirror(this.result); + CraftShapedRecipe recipe = new CraftShapedRecipe(result, this); + recipe.setGroup(this.group); + + switch (this.height) { + case 1: + switch (this.width) { + case 1: + recipe.shape("a"); + break; + case 2: + recipe.shape("ab"); + break; + case 3: + recipe.shape("abc"); + break; + } + break; + case 2: + switch (this.width) { + case 1: + recipe.shape("a","b"); + break; + case 2: + recipe.shape("ab","cd"); + break; + case 3: + recipe.shape("abc","def"); + break; + } + break; + case 3: + switch (this.width) { + case 1: + recipe.shape("a","b","c"); + break; + case 2: + recipe.shape("ab","cd","ef"); + break; + case 3: + recipe.shape("abc","def","ghi"); + break; + } + break; + } + char c = 'a'; + for (RecipeItemStack list : this.items) { + RecipeChoice choice = CraftRecipe.toBukkit(list); + if (choice != null) { + recipe.setIngredient(c, choice); + } + + c++; + } + return recipe; + } + // CraftBukkit end + + public MinecraftKey getKey() { + return this.key; + } + + public RecipeSerializer a() { + return RecipeSerializers.a; + } + + public ItemStack d() { + return this.result; + } + + public NonNullList e() { + return this.items; + } + + public boolean a(IInventory iinventory, World world) { + if (!(iinventory instanceof InventoryCrafting)) { + return false; + } else { + for (int i = 0; i <= iinventory.U_() - this.width; ++i) { + for (int j = 0; j <= iinventory.n() - this.height; ++j) { + if (this.a(iinventory, i, j, true)) { + return true; + } + + if (this.a(iinventory, i, j, false)) { + return true; + } + } + } + + return false; + } + } + + private boolean a(IInventory iinventory, int i, int j, boolean flag) { + for (int k = 0; k < iinventory.U_(); ++k) { + for (int l = 0; l < iinventory.n(); ++l) { + int i1 = k - i; + int j1 = l - j; + RecipeItemStack recipeitemstack = RecipeItemStack.a; + + if (i1 >= 0 && j1 >= 0 && i1 < this.width && j1 < this.height) { + if (flag) { + recipeitemstack = (RecipeItemStack) this.items.get(this.width - i1 - 1 + j1 * this.width); + } else { + recipeitemstack = (RecipeItemStack) this.items.get(i1 + j1 * this.width); + } + } + + if (!recipeitemstack.test(iinventory.getItem(k + l * iinventory.U_()))) { + return false; + } + } + } + + return true; + } + + public ItemStack craftItem(IInventory iinventory) { + return this.d().cloneItemStack(); + } + + public int g() { + return this.width; + } + + public int h() { + return this.height; + } + + private static NonNullList b(String[] astring, Map map, int i, int j) { + NonNullList nonnulllist = NonNullList.a(i * j, RecipeItemStack.a); + Set set = Sets.newHashSet(map.keySet()); + + set.remove(" "); + + for (int k = 0; k < astring.length; ++k) { + for (int l = 0; l < astring[k].length(); ++l) { + String s = astring[k].substring(l, l + 1); + RecipeItemStack recipeitemstack = (RecipeItemStack) map.get(s); + + if (recipeitemstack == null) { + throw new JsonSyntaxException("Pattern references symbol '" + s + "' but it's not defined in the key"); + } + + set.remove(s); + nonnulllist.set(l + i * k, recipeitemstack); + } + } + + if (!set.isEmpty()) { + throw new JsonSyntaxException("Key defines symbols that aren't used in pattern: " + set); + } else { + return nonnulllist; + } + } + + @VisibleForTesting + static String[] a(String... astring) { + int i = Integer.MAX_VALUE; + int j = 0; + int k = 0; + int l = 0; + + for (int i1 = 0; i1 < astring.length; ++i1) { + String s = astring[i1]; + + i = Math.min(i, a(s)); + int j1 = b(s); + + j = Math.max(j, j1); + if (j1 < 0) { + if (k == i1) { + ++k; + } + + ++l; + } else { + l = 0; + } + } + + if (astring.length == l) { + return new String[0]; + } else { + String[] astring1 = new String[astring.length - l - k]; + + for (int k1 = 0; k1 < astring1.length; ++k1) { + astring1[k1] = astring[k1 + k].substring(i, j + 1); + } + + return astring1; + } + } + + private static int a(String s) { + int i; + + for (i = 0; i < s.length() && s.charAt(i) == ' '; ++i) { + ; + } + + return i; + } + + private static int b(String s) { + int i; + + for (i = s.length() - 1; i >= 0 && s.charAt(i) == ' '; --i) { + ; + } + + return i; + } + + private static String[] b(JsonArray jsonarray) { + String[] astring = new String[jsonarray.size()]; + + if (astring.length > 3) { + throw new JsonSyntaxException("Invalid pattern: too many rows, 3 is maximum"); + } else if (astring.length == 0) { + throw new JsonSyntaxException("Invalid pattern: empty pattern not allowed"); + } else { + for (int i = 0; i < astring.length; ++i) { + String s = ChatDeserializer.a(jsonarray.get(i), "pattern[" + i + "]"); + + if (s.length() > 3) { + throw new JsonSyntaxException("Invalid pattern: too many columns, 3 is maximum"); + } + + if (i > 0 && astring[0].length() != s.length()) { + throw new JsonSyntaxException("Invalid pattern: each row must be the same width"); + } + + astring[i] = s; + } + + return astring; + } + } + + private static Map c(JsonObject jsonobject) { + Map map = Maps.newHashMap(); + Iterator iterator = jsonobject.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + + if (((String) entry.getKey()).length() != 1) { + throw new JsonSyntaxException("Invalid key entry: '" + (String) entry.getKey() + "' is an invalid symbol (must be 1 character only)."); + } + + if (" ".equals(entry.getKey())) { + throw new JsonSyntaxException("Invalid key entry: ' ' is a reserved symbol."); + } + + map.put(entry.getKey(), RecipeItemStack.a((JsonElement) entry.getValue())); + } + + map.put(" ", RecipeItemStack.a); + return map; + } + + public static ItemStack a(JsonObject jsonobject) { + String s = ChatDeserializer.h(jsonobject, "item"); + Item item = (Item) IRegistry.ITEM.get(new MinecraftKey(s)); + + if (item == null) { + throw new JsonSyntaxException("Unknown item '" + s + "'"); + } else if (jsonobject.has("data")) { + throw new JsonParseException("Disallowed data tag found"); + } else { + int i = ChatDeserializer.a(jsonobject, "count", 1); + + return new ItemStack(item, i); + } + } + + public static class a implements RecipeSerializer { + + public a() {} + + public ShapedRecipes a(MinecraftKey minecraftkey, JsonObject jsonobject) { + String s = ChatDeserializer.a(jsonobject, "group", ""); + Map map = ShapedRecipes.c(ChatDeserializer.t(jsonobject, "key")); + String[] astring = ShapedRecipes.a(ShapedRecipes.b(ChatDeserializer.u(jsonobject, "pattern"))); + int i = astring[0].length(); + int j = astring.length; + NonNullList nonnulllist = ShapedRecipes.b(astring, map, i, j); + ItemStack itemstack = ShapedRecipes.a(ChatDeserializer.t(jsonobject, "result")); + + return new ShapedRecipes(minecraftkey, s, i, j, nonnulllist, itemstack); + } + + public String a() { + return "crafting_shaped"; + } + + public ShapedRecipes a(MinecraftKey minecraftkey, PacketDataSerializer packetdataserializer) { + int i = packetdataserializer.g(); + int j = packetdataserializer.g(); + String s = packetdataserializer.e(32767); + NonNullList nonnulllist = NonNullList.a(i * j, RecipeItemStack.a); + + for (int k = 0; k < nonnulllist.size(); ++k) { + nonnulllist.set(k, RecipeItemStack.b(packetdataserializer)); + } + + ItemStack itemstack = packetdataserializer.k(); + + return new ShapedRecipes(minecraftkey, s, i, j, nonnulllist, itemstack); + } + + public void a(PacketDataSerializer packetdataserializer, ShapedRecipes shapedrecipes) { + packetdataserializer.d(shapedrecipes.width); + packetdataserializer.d(shapedrecipes.height); + packetdataserializer.a(shapedrecipes.group); + Iterator iterator = shapedrecipes.items.iterator(); + + while (iterator.hasNext()) { + RecipeItemStack recipeitemstack = (RecipeItemStack) iterator.next(); + + recipeitemstack.a(packetdataserializer); + } + + packetdataserializer.a(shapedrecipes.result); + } + } +} diff --git a/src/main/java/net/minecraft/server/ShapelessRecipes.java b/src/main/java/net/minecraft/server/ShapelessRecipes.java new file mode 100644 index 000000000000..ea4083a45a14 --- /dev/null +++ b/src/main/java/net/minecraft/server/ShapelessRecipes.java @@ -0,0 +1,178 @@ +package net.minecraft.server; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import it.unimi.dsi.fastutil.ints.IntList; +import java.util.Iterator; +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.inventory.CraftRecipe; +import org.bukkit.craftbukkit.inventory.CraftShapelessRecipe; +// CraftBukkit end + +public class ShapelessRecipes implements IRecipe { + + private final MinecraftKey key; + private final String group; + private final ItemStack result; + private final NonNullList ingredients; + + public ShapelessRecipes(MinecraftKey minecraftkey, String s, ItemStack itemstack, NonNullList nonnulllist) { + this.key = minecraftkey; + this.group = s; + this.result = itemstack; + this.ingredients = nonnulllist; + } + + // CraftBukkit start + @SuppressWarnings("unchecked") + public org.bukkit.inventory.ShapelessRecipe toBukkitRecipe() { + CraftItemStack result = CraftItemStack.asCraftMirror(this.result); + CraftShapelessRecipe recipe = new CraftShapelessRecipe(result, this); + recipe.setGroup(this.group); + + for (RecipeItemStack list : this.ingredients) { + recipe.addIngredient(CraftRecipe.toBukkit(list)); + } + return recipe; + } + // CraftBukkit end + + public MinecraftKey getKey() { + return this.key; + } + + public RecipeSerializer a() { + return RecipeSerializers.b; + } + + public ItemStack d() { + return this.result; + } + + public NonNullList e() { + return this.ingredients; + } + + public boolean a(IInventory iinventory, World world) { + if (!(iinventory instanceof InventoryCrafting)) { + return false; + } else { + AutoRecipeStackManager autorecipestackmanager = new AutoRecipeStackManager(); + int i = 0; + + // Paper start + java.util.List providedItems = new java.util.ArrayList<>(); + co.aikar.util.Counter matchedProvided = new co.aikar.util.Counter<>(); + co.aikar.util.Counter matchedIngredients = new co.aikar.util.Counter<>(); + // Paper end + for (int j = 0; j < iinventory.n(); ++j) { + for (int k = 0; k < iinventory.U_(); ++k) { + ItemStack itemstack = iinventory.getItem(k + j * iinventory.U_()); + + if (!itemstack.isEmpty()) { + // Paper start + itemstack = itemstack.cloneItemStack(); + providedItems.add(itemstack); + for (RecipeItemStack ingredient : ingredients) { + if (ingredient.test(itemstack)) { + matchedProvided.increment(itemstack); + matchedIngredients.increment(ingredient); + } + } + // Paper end + } + } + } + // Paper start + java.util.List ingredients = new java.util.ArrayList<>(this.ingredients); + providedItems.sort(java.util.Comparator.comparingInt((ItemStack c) -> (int) matchedProvided.getCount(c)).reversed()); + ingredients.sort(java.util.Comparator.comparingInt((RecipeItemStack c) -> (int) matchedIngredients.getCount(c))); + + PROVIDED: + for (ItemStack provided : providedItems) { + for (Iterator itIngredient = ingredients.iterator(); itIngredient.hasNext(); ) { + RecipeItemStack ingredient = itIngredient.next(); + if (ingredient.test(provided)) { + itIngredient.remove(); + continue PROVIDED; + } + } + return false; + } + return ingredients.isEmpty(); + // Paper end + } + } + + public ItemStack craftItem(IInventory iinventory) { + return this.result.cloneItemStack(); + } + + public static class a implements RecipeSerializer { + + public a() {} + + public ShapelessRecipes a(MinecraftKey minecraftkey, JsonObject jsonobject) { + String s = ChatDeserializer.a(jsonobject, "group", ""); + NonNullList nonnulllist = a(ChatDeserializer.u(jsonobject, "ingredients")); + + if (nonnulllist.isEmpty()) { + throw new JsonParseException("No ingredients for shapeless recipe"); + } else if (nonnulllist.size() > 9) { + throw new JsonParseException("Too many ingredients for shapeless recipe"); + } else { + ItemStack itemstack = ShapedRecipes.a(ChatDeserializer.t(jsonobject, "result")); + + return new ShapelessRecipes(minecraftkey, s, itemstack, nonnulllist); + } + } + + private static NonNullList a(JsonArray jsonarray) { + NonNullList nonnulllist = NonNullList.a(); + + for (int i = 0; i < jsonarray.size(); ++i) { + RecipeItemStack recipeitemstack = RecipeItemStack.a(jsonarray.get(i)); + + if (!recipeitemstack.d()) { + nonnulllist.add(recipeitemstack); + } + } + + return nonnulllist; + } + + public String a() { + return "crafting_shapeless"; + } + + public ShapelessRecipes a(MinecraftKey minecraftkey, PacketDataSerializer packetdataserializer) { + String s = packetdataserializer.e(32767); + int i = packetdataserializer.g(); + NonNullList nonnulllist = NonNullList.a(i, RecipeItemStack.a); + + for (int j = 0; j < nonnulllist.size(); ++j) { + nonnulllist.set(j, RecipeItemStack.b(packetdataserializer)); + } + + ItemStack itemstack = packetdataserializer.k(); + + return new ShapelessRecipes(minecraftkey, s, itemstack, nonnulllist); + } + + public void a(PacketDataSerializer packetdataserializer, ShapelessRecipes shapelessrecipes) { + packetdataserializer.a(shapelessrecipes.group); + packetdataserializer.d(shapelessrecipes.ingredients.size()); + Iterator iterator = shapelessrecipes.ingredients.iterator(); + + while (iterator.hasNext()) { + RecipeItemStack recipeitemstack = (RecipeItemStack) iterator.next(); + + recipeitemstack.a(packetdataserializer); + } + + packetdataserializer.a(shapelessrecipes.result); + } + } +} diff --git a/src/main/java/net/minecraft/server/SlotFurnaceResult.java b/src/main/java/net/minecraft/server/SlotFurnaceResult.java new file mode 100644 index 000000000000..d70c4dda5a73 --- /dev/null +++ b/src/main/java/net/minecraft/server/SlotFurnaceResult.java @@ -0,0 +1,97 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.Map.Entry; +// CraftBukkit start +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.FurnaceExtractEvent; +// CraftBukkit end + +public class SlotFurnaceResult extends Slot { + + private final EntityHuman a;public EntityHuman getPlayer() { return a; } // Paper OBFHELPER + private int b; + + public SlotFurnaceResult(EntityHuman entityhuman, IInventory iinventory, int i, int j, int k) { + super(iinventory, i, j, k); + this.a = entityhuman; + } + + public boolean isAllowed(ItemStack itemstack) { + return false; + } + + public ItemStack a(int i) { + if (this.hasItem()) { + this.b += Math.min(i, this.getItem().getCount()); + } + + return super.a(i); + } + + public ItemStack a(EntityHuman entityhuman, ItemStack itemstack) { + this.c(itemstack); + super.a(entityhuman, itemstack); + return itemstack; + } + + protected void a(ItemStack itemstack, int i) { + this.b += i; + this.c(itemstack); + } + + protected void c(ItemStack itemstack) { + itemstack.a(this.a.world, this.a, this.b); + if (!this.a.world.isClientSide) { + Iterator iterator = ((TileEntityFurnace) this.inventory).q().entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + FurnaceRecipe furnacerecipe = (FurnaceRecipe) this.a.world.getCraftingManager().a((MinecraftKey) entry.getKey()); + float f; + + if (furnacerecipe != null) { + f = furnacerecipe.g(); + } else { + f = 0.0F; + } + + int i = (Integer) entry.getValue(); + int j; + + if (f == 0.0F) { + i = 0; + } else if (f < 1.0F) { + j = MathHelper.d((float) i * f); + if (j < MathHelper.f((float) i * f) && Math.random() < (double) ((float) i * f - (float) j)) { + ++j; + } + + i = j; + } + + // CraftBukkit start - fire FurnaceExtractEvent + Player player = (Player) a.getBukkitEntity(); + TileEntityFurnace furnace = ((TileEntityFurnace) this.inventory); + org.bukkit.block.Block block = a.world.getWorld().getBlockAt(furnace.position.getX(), furnace.position.getY(), furnace.position.getZ()); + + if (b != 0) { + FurnaceExtractEvent event = new FurnaceExtractEvent(player, block, org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(itemstack.getItem()), b, i); + a.world.getServer().getPluginManager().callEvent(event); + i = event.getExpToDrop(); + } + // CraftBukkit end + + while (i > 0) { + j = EntityExperienceOrb.getOrbValue(i); + i -= j; + this.a.world.addEntity(new EntityExperienceOrb(this.a.world, this.a.locX, this.a.locY + 0.5D, this.a.locZ + 0.5D, j, org.bukkit.entity.ExperienceOrb.SpawnReason.FURNACE, getPlayer())); // Paper + } + } + + ((RecipeHolder) this.inventory).d(this.a); + } + + this.b = 0; + } +} diff --git a/src/main/java/net/minecraft/server/SpawnerCreature.java b/src/main/java/net/minecraft/server/SpawnerCreature.java new file mode 100644 index 000000000000..e2bf17f44252 --- /dev/null +++ b/src/main/java/net/minecraft/server/SpawnerCreature.java @@ -0,0 +1,358 @@ +package net.minecraft.server; + +import com.google.common.collect.Sets; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.Set; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import com.destroystokyo.paper.exception.ServerInternalException; +import org.bukkit.craftbukkit.util.LongHash; +import org.bukkit.craftbukkit.util.LongHashSet; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +// CraftBukkit end + +public final class SpawnerCreature { + + private static final Logger a = LogManager.getLogger(); + private static final int b = (int) Math.pow(17.0D, 2.0D); + private final LongHashSet c = new LongHashSet(); // CraftBukkit + + public SpawnerCreature() {} + + public int a(WorldServer worldserver, boolean flag, boolean flag1, boolean flag2) { + if (!flag && !flag1) { + return 0; + } else { + this.c.clear(); + int i = 0; + Iterator iterator = worldserver.players.iterator(); + + int j; + int k; + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + if (!entityhuman.isSpectator() && entityhuman.affectsSpawning) { // Paper + int l = MathHelper.floor(entityhuman.locX / 16.0D); + + j = MathHelper.floor(entityhuman.locZ / 16.0D); + boolean flag3 = true; + // Spigot Start + byte b0 = worldserver.spigotConfig.mobSpawnRange; + b0 = ( b0 > entityhuman.getViewDistance() ) ? (byte) entityhuman.getViewDistance() : b0; // Paper - Use player view distance API + b0 = ( b0 > 8 ) ? 8 : b0; + // Paper start + com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event; + event = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent( + (org.bukkit.entity.Player) entityhuman.getBukkitEntity(), b0); + if (!event.callEvent()) { + continue; + } + b0 = event.getSpawnRadius(); + // Paperr end + + for (int i1 = -b0; i1 <= b0; ++i1) { + for (k = -b0; k <= b0; ++k) { + boolean flag4 = i1 == -b0 || i1 == b0 || k == -b0 || k == b0; + // Spigot End + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i1 + l, k + j); + + // CraftBukkit start - use LongHash and LongHashSet + long chunkCoords = LongHash.toLong(chunkcoordintpair.x, chunkcoordintpair.z); + if (!this.c.contains(chunkCoords)) { + ++i; + if (!flag4 && worldserver.getWorldBorder().isInBounds(chunkcoordintpair)) { + PlayerChunk playerchunk = worldserver.getPlayerChunkMap().getChunk(chunkcoordintpair.x, chunkcoordintpair.z); + + if (playerchunk != null && playerchunk.e()) { + this.c.add(chunkCoords); + // CraftBukkit end + } + } + } + } + } + } + } + + int j1 = 0; + BlockPosition blockposition = worldserver.getSpawn(); + EnumCreatureType[] aenumcreaturetype = EnumCreatureType.values(); + + j = aenumcreaturetype.length; + + for (int k1 = 0; k1 < j; ++k1) { + EnumCreatureType enumcreaturetype = aenumcreaturetype[k1]; + + // CraftBukkit start - Use per-world spawn limits + int limit = enumcreaturetype.b(); + switch (enumcreaturetype) { + case MONSTER: + limit = worldserver.getWorld().getMonsterSpawnLimit(); + break; + case CREATURE: + limit = worldserver.getWorld().getAnimalSpawnLimit(); + break; + case WATER_CREATURE: + limit = worldserver.getWorld().getWaterAnimalSpawnLimit(); + break; + case AMBIENT: + limit = worldserver.getWorld().getAmbientSpawnLimit(); + break; + } + + if (limit == 0) { + continue; + } + // CraftBukkit end + + if ((!enumcreaturetype.c() || flag1) && (enumcreaturetype.c() || flag) && (!enumcreaturetype.d() || flag2)) { + k = limit * i / SpawnerCreature.b; // CraftBukkit - use per-world limits + int l1 = worldserver.entityList.getCreatureCount(enumcreaturetype); // Paper - entity count cache + + if (l1 <= k) { + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + Iterator iterator1 = this.c.iterator(); + + label137: + while (iterator1.hasNext()) { + // CraftBukkit start = use LongHash and LongObjectHashMap + long key = ((Long) iterator1.next()).longValue(); + BlockPosition blockposition1 = getRandomPosition(worldserver, LongHash.msw(key), LongHash.lsw(key)); + // CraftBukkit + int i2 = blockposition1.getX(); + int j2 = blockposition1.getY(); + int k2 = blockposition1.getZ(); + IBlockData iblockdata = worldserver.getTypeIfLoadedAndInBounds(blockposition1); // Paper - don't load chunks for mob spawn + + if (iblockdata != null && !iblockdata.isOccluding()) { // Paper - don't load chunks for mob spawn + int l2 = 0; + int i3 = 0; + + while (i3 < 3) { + int j3 = i2; + int k3 = j2; + int l3 = k2; + boolean flag5 = true; + BiomeBase.BiomeMeta biomebase_biomemeta = null; + GroupDataEntity groupdataentity = null; + int i4 = MathHelper.f(Math.random() * 4.0D); + int j4 = 0; + int k4 = 0; + + while (true) { + if (k4 < i4) { + label130: + { + j3 += worldserver.random.nextInt(6) - worldserver.random.nextInt(6); + k3 += worldserver.random.nextInt(1) - worldserver.random.nextInt(1); + l3 += worldserver.random.nextInt(6) - worldserver.random.nextInt(6); + blockposition_mutableblockposition.c(j3, k3, l3); + float f = (float) j3 + 0.5F; + float f1 = (float) l3 + 0.5F; + EntityHuman entityhuman1 = worldserver.a((double) f, (double) f1, -1.0D); + + if (entityhuman1 != null) { + double d0 = entityhuman1.d((double) f, (double) k3, (double) f1); + + if (d0 > 576.0D && blockposition.distanceSquared((double) f, (double) k3, (double) f1) >= 576.0D) { + if (biomebase_biomemeta == null) { + biomebase_biomemeta = worldserver.a(enumcreaturetype, (BlockPosition) blockposition_mutableblockposition); + if (biomebase_biomemeta == null) { + break label130; + } + + i4 = biomebase_biomemeta.c + worldserver.random.nextInt(1 + biomebase_biomemeta.d - biomebase_biomemeta.c); + } + + if (worldserver.isLoadedAndInBounds(blockposition_mutableblockposition) && worldserver.a(enumcreaturetype, biomebase_biomemeta, (BlockPosition) blockposition_mutableblockposition)) { // Paper - don't load chunks for mob spawn + EntityPositionTypes.Surface entitypositiontypes_surface = EntityPositionTypes.a(biomebase_biomemeta.b); + if (entitypositiontypes_surface != null && a(entitypositiontypes_surface, worldserver, blockposition_mutableblockposition, biomebase_biomemeta.b)) { + EntityInsentient entityinsentient; + + // Paper start + com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event; + EntityTypes cls = biomebase_biomemeta.b; + org.bukkit.entity.EntityType type = EntityTypes.clsToTypeMap.get(cls); + if (type != null) { + event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent( + MCUtil.toLocation(worldserver, blockposition_mutableblockposition), + type, SpawnReason.NATURAL + ); + if (!event.callEvent()) { + if (event.shouldAbortSpawn()) { + continue label137; // right above the iterator for c (Chunk Pos Set) + } + j1 += l2; + ++j4; + continue; + } + } + // Paper end + + + try { + entityinsentient = (EntityInsentient) biomebase_biomemeta.b.a((World) worldserver); + } catch (Exception exception) { + SpawnerCreature.a.warn("Failed to create mob", exception); + ServerInternalException.reportInternalException(exception); // Paper + return j1; + } + + entityinsentient.setPositionRotation((double) f, (double) k3, (double) f1, worldserver.random.nextFloat() * 360.0F, 0.0F); + if ((d0 <= 16384.0D || !entityinsentient.isTypeNotPersistent()) && entityinsentient.a((GeneratorAccess) worldserver, false) && entityinsentient.a((IWorldReader) worldserver)) { + groupdataentity = entityinsentient.prepare(worldserver.getDamageScaler(new BlockPosition(entityinsentient)), groupdataentity, (NBTTagCompound) null); + if (entityinsentient.a((IWorldReader) worldserver)) { + // CraftBukkit start + if (worldserver.addEntity(entityinsentient, SpawnReason.NATURAL)) { + ++l2; + ++j4; + } + // CraftBukkit end + } else { + entityinsentient.die(); + } + + if (l2 >= entityinsentient.dg()) { + continue label137; + } + + if (entityinsentient.c(j4)) { + break label130; + } + } + + j1 += l2; + } + } + } + } + + ++k4; + continue; + } + } + + ++i3; + break; + } + } + } + } + } + } + } + + return j1; + } + } + + private static BlockPosition getRandomPosition(World world, int i, int j) { + Chunk chunk = world.getChunkAt(i, j); + int k = i * 16 + world.random.nextInt(16); + int l = j * 16 + world.random.nextInt(16); + int i1 = chunk.a(HeightMap.Type.LIGHT_BLOCKING, k, l) + 1; + int j1 = world.random.nextInt(i1 + 1); + + return new BlockPosition(k, j1, l); + } + + public static boolean a(IBlockData iblockdata, Fluid fluid) { + return iblockdata.k() ? false : (iblockdata.isPowerSource() ? false : (!fluid.e() ? false : !iblockdata.a(TagsBlock.RAILS))); + } + + public static boolean a(EntityPositionTypes.Surface entitypositiontypes_surface, IWorldReader iworldreader, BlockPosition blockposition, @Nullable EntityTypes entitytypes) { + if (entitytypes != null && iworldreader.getWorldBorder().a(blockposition)) { + IBlockData iblockdata = iworldreader.getType(blockposition); + Fluid fluid = iworldreader.getFluid(blockposition); + + switch (entitypositiontypes_surface) { + case IN_WATER: + return fluid.a(TagsFluid.WATER) && iworldreader.getFluid(blockposition.down()).a(TagsFluid.WATER) && !iworldreader.getType(blockposition.up()).isOccluding(); + case ON_GROUND: + default: + IBlockData iblockdata1 = iworldreader.getType(blockposition.down()); + + if (!iblockdata1.q() && (entitytypes == null || !EntityPositionTypes.a(entitytypes, iblockdata1))) { + return false; + } else { + Block block = iblockdata1.getBlock(); + boolean flag = block != Blocks.BEDROCK && block != Blocks.BARRIER; + + return flag && a(iblockdata, fluid) && a(iworldreader.getType(blockposition.up()), iworldreader.getFluid(blockposition.up())); + } + } + } else { + return false; + } + } + + public static void a(GeneratorAccess generatoraccess, BiomeBase biomebase, int i, int j, Random random) { + List list = biomebase.getMobs(EnumCreatureType.CREATURE); + + if (!list.isEmpty()) { + int k = i << 4; + int l = j << 4; + + while (random.nextFloat() < biomebase.e()) { + BiomeBase.BiomeMeta biomebase_biomemeta = (BiomeBase.BiomeMeta) WeightedRandom.a(random, list); + int i1 = biomebase_biomemeta.c + random.nextInt(1 + biomebase_biomemeta.d - biomebase_biomemeta.c); + GroupDataEntity groupdataentity = null; + int j1 = k + random.nextInt(16); + int k1 = l + random.nextInt(16); + int l1 = j1; + int i2 = k1; + + for (int j2 = 0; j2 < i1; ++j2) { + boolean flag = false; + + for (int k2 = 0; !flag && k2 < 4; ++k2) { + BlockPosition blockposition = a(generatoraccess, biomebase_biomemeta.b, j1, k1); + + if (a(EntityPositionTypes.Surface.ON_GROUND, generatoraccess, blockposition, biomebase_biomemeta.b)) { + EntityInsentient entityinsentient; + + try { + entityinsentient = (EntityInsentient) biomebase_biomemeta.b.a(generatoraccess.getMinecraftWorld()); + } catch (Exception exception) { + SpawnerCreature.a.warn("Failed to create mob", exception); + ServerInternalException.reportInternalException(exception); // Paper + continue; + } + + double d0 = MathHelper.a((double) j1, (double) k + (double) entityinsentient.width, (double) k + 16.0D - (double) entityinsentient.width); + double d1 = MathHelper.a((double) k1, (double) l + (double) entityinsentient.width, (double) l + 16.0D - (double) entityinsentient.width); + + entityinsentient.setPositionRotation(d0, (double) blockposition.getY(), d1, random.nextFloat() * 360.0F, 0.0F); + if (entityinsentient.a(generatoraccess, false) && entityinsentient.a((IWorldReader) generatoraccess)) { + groupdataentity = entityinsentient.prepare(generatoraccess.getDamageScaler(new BlockPosition(entityinsentient)), groupdataentity, (NBTTagCompound) null); + generatoraccess.addEntity(entityinsentient, SpawnReason.CHUNK_GEN); // CraftBukkit + flag = true; + } + } + + j1 += random.nextInt(5) - random.nextInt(5); + + for (k1 += random.nextInt(5) - random.nextInt(5); j1 < k || j1 >= k + 16 || k1 < l || k1 >= l + 16; k1 = i2 + random.nextInt(5) - random.nextInt(5)) { + j1 = l1 + random.nextInt(5) - random.nextInt(5); + } + } + } + } + + } + } + + private static BlockPosition a(GeneratorAccess generatoraccess, @Nullable EntityTypes entitytypes, int i, int j) { + BlockPosition blockposition = new BlockPosition(i, generatoraccess.a(EntityPositionTypes.b(entitytypes), i, j), j); + BlockPosition blockposition1 = blockposition.down(); + + return generatoraccess.getType(blockposition1).a((IBlockAccess) generatoraccess, blockposition1, PathMode.LAND) ? blockposition1 : blockposition; + } +} diff --git a/src/main/java/net/minecraft/server/StatisticManager.java b/src/main/java/net/minecraft/server/StatisticManager.java new file mode 100644 index 000000000000..ec42262422ee --- /dev/null +++ b/src/main/java/net/minecraft/server/StatisticManager.java @@ -0,0 +1,32 @@ +package net.minecraft.server; + +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntMaps; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; + +public class StatisticManager { + + protected final Object2IntMap> a = Object2IntMaps.synchronize(new Object2IntOpenHashMap()); + + public StatisticManager() { + this.a.defaultReturnValue(0); + } + + public void b(EntityHuman entityhuman, Statistic statistic, int i) { + // CraftBukkit start - fire Statistic events + org.bukkit.event.Cancellable cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.handleStatisticsIncrease(entityhuman, statistic, this.getStatisticValue(statistic), i); + if (cancellable != null && cancellable.isCancelled()) { + return; + } + // CraftBukkit end + this.setStatistic(entityhuman, statistic, this.getStatisticValue(statistic) + i); + } + + public void setStatistic(EntityHuman entityhuman, Statistic statistic, int i) { + this.a.put(statistic, i); + } + + public int getStatisticValue(Statistic statistic) { + return this.a.getInt(statistic); + } +} diff --git a/src/main/java/net/minecraft/server/StructureGenerator.java b/src/main/java/net/minecraft/server/StructureGenerator.java new file mode 100644 index 000000000000..2ea5ac3161ae --- /dev/null +++ b/src/main/java/net/minecraft/server/StructureGenerator.java @@ -0,0 +1,247 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.LongIterator; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSet; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public abstract class StructureGenerator extends WorldGenerator { + + private static final Logger b = LogManager.getLogger(); + public static final StructureStart a = new StructureStart() { + public boolean b() { + return false; + } + }; + + public StructureGenerator() {} + + public boolean generate(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, Random random, BlockPosition blockposition, C c0) { + if (!this.a(generatoraccess)) { + return false; + } else { + int i = this.b(); + int j = blockposition.getX() >> 4; + int k = blockposition.getZ() >> 4; + int l = j << 4; + int i1 = k << 4; + long j1 = ChunkCoordIntPair.a(j, k); + boolean flag = false; + + for (int k1 = j - i; k1 <= j + i; ++k1) { + for (int l1 = k - i; l1 <= k + i; ++l1) { + long i2 = ChunkCoordIntPair.a(k1, l1); + StructureStart structurestart = this.a(generatoraccess, chunkgenerator, (SeededRandom) random, i2); + + if (structurestart != StructureGenerator.a && structurestart.c().a(l, i1, l + 15, i1 + 15)) { + ((LongSet) chunkgenerator.getStructureCache(this).computeIfAbsent(j1, (j2) -> { + return new LongOpenHashSet(); + })).add(i2); + generatoraccess.getChunkProvider().a(j, k, true).a(this.a(), i2); + structurestart.a(generatoraccess, random, new StructureBoundingBox(l, i1, l + 15, i1 + 15), new ChunkCoordIntPair(j, k)); + structurestart.b(new ChunkCoordIntPair(j, k)); + flag = true; + } + } + } + + return flag; + } + } + + protected StructureStart a(GeneratorAccess generatoraccess, BlockPosition blockposition) { + List list = this.a(generatoraccess, blockposition.getX() >> 4, blockposition.getZ() >> 4); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + StructureStart structurestart = (StructureStart) iterator.next(); + + if (structurestart.b() && structurestart.c().b((BaseBlockPosition) blockposition)) { + Iterator iterator1 = structurestart.d().iterator(); + + while (iterator1.hasNext()) { + StructurePiece structurepiece = (StructurePiece) iterator1.next(); + + if (structurepiece.d().b((BaseBlockPosition) blockposition)) { + return structurestart; + } + } + } + } + + return StructureGenerator.a; + } + + public boolean b(GeneratorAccess generatoraccess, BlockPosition blockposition) { + List list = this.a(generatoraccess, blockposition.getX() >> 4, blockposition.getZ() >> 4); + Iterator iterator = list.iterator(); + + StructureStart structurestart; + + do { + if (!iterator.hasNext()) { + return false; + } + + structurestart = (StructureStart) iterator.next(); + } while (!structurestart.b() || !structurestart.c().b((BaseBlockPosition) blockposition)); + + return true; + } + + public boolean c(GeneratorAccess generatoraccess, BlockPosition blockposition) { + return this.a(generatoraccess, blockposition).b(); + } + + @Nullable + public BlockPosition getNearestGeneratedFeature(World world, ChunkGenerator chunkgenerator, BlockPosition blockposition, int i, boolean flag) { + if (!chunkgenerator.getWorldChunkManager().a(this)) { + return null; + } else { + int j = blockposition.getX() >> 4; + int k = blockposition.getZ() >> 4; + int l = 0; + SeededRandom seededrandom = new SeededRandom(); + + while (l <= i) { + int i1 = -l; + + while (true) { + if (i1 <= l) { + boolean flag1 = i1 == -l || i1 == l; + + for (int j1 = -l; j1 <= l; ++j1) { + boolean flag2 = j1 == -l || j1 == l; + + if (flag1 || flag2) { + ChunkCoordIntPair chunkcoordintpair = this.a(chunkgenerator, seededrandom, j, k, i1, j1); + if (!world.getWorldBorder().isChunkInBounds(chunkcoordintpair.x, chunkcoordintpair.z)) { continue; } // Paper + StructureStart structurestart = this.a(world, chunkgenerator, seededrandom, chunkcoordintpair.a()); + + if (structurestart != StructureGenerator.a) { + if (flag && structurestart.g()) { + structurestart.h(); + return structurestart.a(); + } + + if (!flag) { + return structurestart.a(); + } + } + + if (l == 0) { + break; + } + } + } + + if (l != 0) { + ++i1; + continue; + } + } + + ++l; + break; + } + } + + return null; + } + } + + private List a(GeneratorAccess generatoraccess, int i, int j) { + List list = Lists.newArrayList(); + Long2ObjectMap long2objectmap = generatoraccess.getChunkProvider().getChunkGenerator().getStructureStartCache(this); + Long2ObjectMap long2objectmap1 = generatoraccess.getChunkProvider().getChunkGenerator().getStructureCache(this); + long k = ChunkCoordIntPair.a(i, j); + LongSet longset = (LongSet) long2objectmap1.get(k); + + if (longset == null) { + longset = generatoraccess.getChunkProvider().a(i, j, true).b(this.a()); + long2objectmap1.put(k, longset); + } + + LongIterator longiterator = longset.iterator(); + + while (longiterator.hasNext()) { + Long olong = (Long) longiterator.next(); + StructureStart structurestart = (StructureStart) long2objectmap.get(olong); + + if (structurestart != null) { + list.add(structurestart); + } else { + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(olong); + IChunkAccess ichunkaccess = generatoraccess.getChunkProvider().a(chunkcoordintpair.x, chunkcoordintpair.z, true); + + structurestart = ichunkaccess.a(this.a()); + if (structurestart != null) { + long2objectmap.put(olong, structurestart); + list.add(structurestart); + } + } + } + + return list; + } + + private StructureStart a(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, SeededRandom seededrandom, long i) { + if (!chunkgenerator.getWorldChunkManager().a(this)) { + return StructureGenerator.a; + } else { + Long2ObjectMap long2objectmap = chunkgenerator.getStructureStartCache(this); + StructureStart structurestart = (StructureStart) long2objectmap.get(i); + + if (structurestart != null) { + return structurestart; + } else { + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i); + IChunkAccess ichunkaccess = generatoraccess.getChunkProvider().getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z, false, false); // CraftBukkit - don't load chunks + + if (ichunkaccess != null) { + structurestart = ichunkaccess.a(this.a()); + if (structurestart != null) { + long2objectmap.put(i, structurestart); + return structurestart; + } + } + + if (this.a(chunkgenerator, seededrandom, chunkcoordintpair.x, chunkcoordintpair.z)) { + StructureStart structurestart1 = this.a(generatoraccess, chunkgenerator, seededrandom, chunkcoordintpair.x, chunkcoordintpair.z); + + structurestart = structurestart1.b() ? structurestart1 : StructureGenerator.a; + } else { + structurestart = StructureGenerator.a; + } + + if (structurestart.b()) { + generatoraccess.getChunkProvider().a(chunkcoordintpair.x, chunkcoordintpair.z, true).a(this.a(), structurestart); + } + + long2objectmap.put(i, structurestart); + return structurestart; + } + } + } + + protected ChunkCoordIntPair a(ChunkGenerator chunkgenerator, Random random, int i, int j, int k, int l) { + return new ChunkCoordIntPair(i + k, j + l); + } + + protected abstract boolean a(ChunkGenerator chunkgenerator, Random random, int i, int j); + + protected abstract boolean a(GeneratorAccess generatoraccess); + + protected abstract StructureStart a(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, SeededRandom seededrandom, int i, int j); + + protected abstract String a(); + + public abstract int b(); +} diff --git a/src/main/java/net/minecraft/server/StructurePiece.java b/src/main/java/net/minecraft/server/StructurePiece.java new file mode 100644 index 000000000000..945a005e90c0 --- /dev/null +++ b/src/main/java/net/minecraft/server/StructurePiece.java @@ -0,0 +1,491 @@ +package net.minecraft.server; + +import com.google.common.collect.ImmutableSet; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.Set; +import javax.annotation.Nullable; + +public abstract class StructurePiece { + + protected static final IBlockData m = Blocks.CAVE_AIR.getBlockData(); + protected StructureBoundingBox n; + @Nullable + private EnumDirection a; + private EnumBlockMirror b; + private EnumBlockRotation c; + protected int o; + private static final Set d = ImmutableSet.builder().add(Blocks.NETHER_BRICK_FENCE).add(Blocks.TORCH).add(Blocks.WALL_TORCH).add(Blocks.OAK_FENCE).add(Blocks.SPRUCE_FENCE).add(Blocks.DARK_OAK_FENCE).add(Blocks.ACACIA_FENCE).add(Blocks.BIRCH_FENCE).add(Blocks.JUNGLE_FENCE).add(Blocks.LADDER).add(Blocks.IRON_BARS).build(); // Paper - decompile error + + public StructurePiece() {} + + protected StructurePiece(int i) { + this.o = i; + } + + public final NBTTagCompound c() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setString("id", WorldGenFactory.a(this)); + nbttagcompound.set("BB", this.n.g()); + EnumDirection enumdirection = this.f(); + + nbttagcompound.setInt("O", enumdirection == null ? -1 : enumdirection.get2DRotationValue()); + nbttagcompound.setInt("GD", this.o); + this.a(nbttagcompound); + return nbttagcompound; + } + + protected abstract void a(NBTTagCompound nbttagcompound); + + public void a(GeneratorAccess generatoraccess, NBTTagCompound nbttagcompound) { + if (nbttagcompound.hasKey("BB")) { + this.n = new StructureBoundingBox(nbttagcompound.getIntArray("BB")); + } + + int i = nbttagcompound.getInt("O"); + + this.a(i == -1 ? null : EnumDirection.fromType2(i)); + this.o = nbttagcompound.getInt("GD"); + this.a(nbttagcompound, generatoraccess.getDataManager().h()); + } + + protected abstract void a(NBTTagCompound nbttagcompound, DefinedStructureManager definedstructuremanager); + + public void a(StructurePiece structurepiece, List list, Random random) {} + + public abstract boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair); + + public StructureBoundingBox d() { + return this.n; + } + + public int e() { + return this.o; + } + + public static StructurePiece a(List list, StructureBoundingBox structureboundingbox) { + StructurePiece structurepiece; // Paper + synchronized (list) { // Paper - synchronize main structure list + Iterator iterator = list.iterator(); + + //StructurePiece structurepiece; // Paper - move up + + do { + if (!iterator.hasNext()) { + return null; + } + + structurepiece = (StructurePiece) iterator.next(); + } while (structurepiece.d() == null || !structurepiece.d().a(structureboundingbox)); + } // Paper + return structurepiece; + } + + protected boolean a(IBlockAccess iblockaccess, StructureBoundingBox structureboundingbox) { + int i = Math.max(this.n.a - 1, structureboundingbox.a); + int j = Math.max(this.n.b - 1, structureboundingbox.b); + int k = Math.max(this.n.c - 1, structureboundingbox.c); + int l = Math.min(this.n.d + 1, structureboundingbox.d); + int i1 = Math.min(this.n.e + 1, structureboundingbox.e); + int j1 = Math.min(this.n.f + 1, structureboundingbox.f); + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + int k1; + int l1; + + for (k1 = i; k1 <= l; ++k1) { + for (l1 = k; l1 <= j1; ++l1) { + if (iblockaccess.getType(blockposition_mutableblockposition.c(k1, j, l1)).getMaterial().isLiquid()) { + return true; + } + + if (iblockaccess.getType(blockposition_mutableblockposition.c(k1, i1, l1)).getMaterial().isLiquid()) { + return true; + } + } + } + + for (k1 = i; k1 <= l; ++k1) { + for (l1 = j; l1 <= i1; ++l1) { + if (iblockaccess.getType(blockposition_mutableblockposition.c(k1, l1, k)).getMaterial().isLiquid()) { + return true; + } + + if (iblockaccess.getType(blockposition_mutableblockposition.c(k1, l1, j1)).getMaterial().isLiquid()) { + return true; + } + } + } + + for (k1 = k; k1 <= j1; ++k1) { + for (l1 = j; l1 <= i1; ++l1) { + if (iblockaccess.getType(blockposition_mutableblockposition.c(i, l1, k1)).getMaterial().isLiquid()) { + return true; + } + + if (iblockaccess.getType(blockposition_mutableblockposition.c(l, l1, k1)).getMaterial().isLiquid()) { + return true; + } + } + } + + return false; + } + + protected int a(int i, int j) { + EnumDirection enumdirection = this.f(); + + if (enumdirection == null) { + return i; + } else { + switch (enumdirection) { + case NORTH: + case SOUTH: + return this.n.a + i; + case WEST: + return this.n.d - j; + case EAST: + return this.n.a + j; + default: + return i; + } + } + } + + protected int d(int i) { + return this.f() == null ? i : i + this.n.b; + } + + protected int b(int i, int j) { + EnumDirection enumdirection = this.f(); + + if (enumdirection == null) { + return j; + } else { + switch (enumdirection) { + case NORTH: + return this.n.f - j; + case SOUTH: + return this.n.c + j; + case WEST: + case EAST: + return this.n.c + i; + default: + return j; + } + } + } + + protected void a(GeneratorAccess generatoraccess, IBlockData iblockdata, int i, int j, int k, StructureBoundingBox structureboundingbox) { + BlockPosition blockposition = new BlockPosition(this.a(i, k), this.d(j), this.b(i, k)); + + if (structureboundingbox.b((BaseBlockPosition) blockposition)) { + if (this.b != EnumBlockMirror.NONE) { + iblockdata = iblockdata.a(this.b); + } + + if (this.c != EnumBlockRotation.NONE) { + iblockdata = iblockdata.a(this.c); + } + + generatoraccess.setTypeAndData(blockposition, iblockdata, 2); + Fluid fluid = generatoraccess.getFluid(blockposition); + + if (!fluid.e()) { + generatoraccess.getFluidTickList().a(blockposition, fluid.c(), 0); + } + + if (StructurePiece.d.contains(iblockdata.getBlock())) { + generatoraccess.y(blockposition).e(blockposition); + } + + } + } + + protected IBlockData a(IBlockAccess iblockaccess, int i, int j, int k, StructureBoundingBox structureboundingbox) { + int l = this.a(i, k); + int i1 = this.d(j); + int j1 = this.b(i, k); + BlockPosition blockposition = new BlockPosition(l, i1, j1); + + return !structureboundingbox.b((BaseBlockPosition) blockposition) ? Blocks.AIR.getBlockData() : iblockaccess.getType(blockposition); + } + + protected boolean a(IWorldReader iworldreader, int i, int j, int k, StructureBoundingBox structureboundingbox) { + int l = this.a(i, k); + int i1 = this.d(j + 1); + int j1 = this.b(i, k); + BlockPosition blockposition = new BlockPosition(l, i1, j1); + + return !structureboundingbox.b((BaseBlockPosition) blockposition) ? false : i1 < iworldreader.a(HeightMap.Type.OCEAN_FLOOR_WG, l, j1); + } + + protected void b(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox, int i, int j, int k, int l, int i1, int j1) { + for (int k1 = j; k1 <= i1; ++k1) { + for (int l1 = i; l1 <= l; ++l1) { + for (int i2 = k; i2 <= j1; ++i2) { + this.a(generatoraccess, Blocks.AIR.getBlockData(), l1, k1, i2, structureboundingbox); + } + } + } + + } + + protected void a(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox, int i, int j, int k, int l, int i1, int j1, IBlockData iblockdata, IBlockData iblockdata1, boolean flag) { + for (int k1 = j; k1 <= i1; ++k1) { + for (int l1 = i; l1 <= l; ++l1) { + for (int i2 = k; i2 <= j1; ++i2) { + if (!flag || !this.a((IBlockAccess) generatoraccess, l1, k1, i2, structureboundingbox).isAir()) { + if (k1 != j && k1 != i1 && l1 != i && l1 != l && i2 != k && i2 != j1) { + this.a(generatoraccess, iblockdata1, l1, k1, i2, structureboundingbox); + } else { + this.a(generatoraccess, iblockdata, l1, k1, i2, structureboundingbox); + } + } + } + } + } + + } + + protected void a(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox, int i, int j, int k, int l, int i1, int j1, boolean flag, Random random, StructurePiece.StructurePieceBlockSelector structurepiece_structurepieceblockselector) { + for (int k1 = j; k1 <= i1; ++k1) { + for (int l1 = i; l1 <= l; ++l1) { + for (int i2 = k; i2 <= j1; ++i2) { + if (!flag || !this.a((IBlockAccess) generatoraccess, l1, k1, i2, structureboundingbox).isAir()) { + structurepiece_structurepieceblockselector.a(random, l1, k1, i2, k1 == j || k1 == i1 || l1 == i || l1 == l || i2 == k || i2 == j1); + this.a(generatoraccess, structurepiece_structurepieceblockselector.a(), l1, k1, i2, structureboundingbox); + } + } + } + } + + } + + protected void a(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox, Random random, float f, int i, int j, int k, int l, int i1, int j1, IBlockData iblockdata, IBlockData iblockdata1, boolean flag, boolean flag1) { + for (int k1 = j; k1 <= i1; ++k1) { + for (int l1 = i; l1 <= l; ++l1) { + for (int i2 = k; i2 <= j1; ++i2) { + if (random.nextFloat() <= f && (!flag || !this.a((IBlockAccess) generatoraccess, l1, k1, i2, structureboundingbox).isAir()) && (!flag1 || this.a((IWorldReader) generatoraccess, l1, k1, i2, structureboundingbox))) { + if (k1 != j && k1 != i1 && l1 != i && l1 != l && i2 != k && i2 != j1) { + this.a(generatoraccess, iblockdata1, l1, k1, i2, structureboundingbox); + } else { + this.a(generatoraccess, iblockdata, l1, k1, i2, structureboundingbox); + } + } + } + } + } + + } + + protected void a(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox, Random random, float f, int i, int j, int k, IBlockData iblockdata) { + if (random.nextFloat() < f) { + this.a(generatoraccess, iblockdata, i, j, k, structureboundingbox); + } + + } + + protected void a(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox, int i, int j, int k, int l, int i1, int j1, IBlockData iblockdata, boolean flag) { + float f = (float) (l - i + 1); + float f1 = (float) (i1 - j + 1); + float f2 = (float) (j1 - k + 1); + float f3 = (float) i + f / 2.0F; + float f4 = (float) k + f2 / 2.0F; + + for (int k1 = j; k1 <= i1; ++k1) { + float f5 = (float) (k1 - j) / f1; + + for (int l1 = i; l1 <= l; ++l1) { + float f6 = ((float) l1 - f3) / (f * 0.5F); + + for (int i2 = k; i2 <= j1; ++i2) { + float f7 = ((float) i2 - f4) / (f2 * 0.5F); + + if (!flag || !this.a((IBlockAccess) generatoraccess, l1, k1, i2, structureboundingbox).isAir()) { + float f8 = f6 * f6 + f5 * f5 + f7 * f7; + + if (f8 <= 1.05F) { + this.a(generatoraccess, iblockdata, l1, k1, i2, structureboundingbox); + } + } + } + } + } + + } + + protected void a(GeneratorAccess generatoraccess, int i, int j, int k, StructureBoundingBox structureboundingbox) { + BlockPosition blockposition = new BlockPosition(this.a(i, k), this.d(j), this.b(i, k)); + + if (structureboundingbox.b((BaseBlockPosition) blockposition)) { + while (!generatoraccess.isEmpty(blockposition) && blockposition.getY() < 255) { + generatoraccess.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 2); + blockposition = blockposition.up(); + } + + } + } + + protected void b(GeneratorAccess generatoraccess, IBlockData iblockdata, int i, int j, int k, StructureBoundingBox structureboundingbox) { + int l = this.a(i, k); + int i1 = this.d(j); + int j1 = this.b(i, k); + + if (structureboundingbox.b((BaseBlockPosition) (new BlockPosition(l, i1, j1)))) { + while ((generatoraccess.isEmpty(new BlockPosition(l, i1, j1)) || generatoraccess.getType(new BlockPosition(l, i1, j1)).getMaterial().isLiquid()) && i1 > 1) { + generatoraccess.setTypeAndData(new BlockPosition(l, i1, j1), iblockdata, 2); + --i1; + } + + } + } + + protected boolean a(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox, Random random, int i, int j, int k, MinecraftKey minecraftkey) { + BlockPosition blockposition = new BlockPosition(this.a(i, k), this.d(j), this.b(i, k)); + + return this.a(generatoraccess, structureboundingbox, random, blockposition, minecraftkey, (IBlockData) null); + } + + public static IBlockData a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) { + EnumDirection enumdirection = null; + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection1 = (EnumDirection) iterator.next(); + BlockPosition blockposition1 = blockposition.shift(enumdirection1); + IBlockData iblockdata1 = iblockaccess.getType(blockposition1); + + if (iblockdata1.getBlock() == Blocks.CHEST) { + return iblockdata; + } + + if (iblockdata1.f(iblockaccess, blockposition1)) { + if (enumdirection != null) { + enumdirection = null; + break; + } + + enumdirection = enumdirection1; + } + } + + if (enumdirection != null) { + return (IBlockData) iblockdata.set(BlockFacingHorizontal.FACING, enumdirection.opposite()); + } else { + EnumDirection enumdirection2 = (EnumDirection) iblockdata.get(BlockFacingHorizontal.FACING); + BlockPosition blockposition2 = blockposition.shift(enumdirection2); + + if (iblockaccess.getType(blockposition2).f(iblockaccess, blockposition2)) { + enumdirection2 = enumdirection2.opposite(); + blockposition2 = blockposition.shift(enumdirection2); + } + + if (iblockaccess.getType(blockposition2).f(iblockaccess, blockposition2)) { + enumdirection2 = enumdirection2.e(); + blockposition2 = blockposition.shift(enumdirection2); + } + + if (iblockaccess.getType(blockposition2).f(iblockaccess, blockposition2)) { + enumdirection2 = enumdirection2.opposite(); + blockposition.shift(enumdirection2); + } + + return (IBlockData) iblockdata.set(BlockFacingHorizontal.FACING, enumdirection2); + } + } + + protected boolean a(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox, Random random, BlockPosition blockposition, MinecraftKey minecraftkey, @Nullable IBlockData iblockdata) { + if (structureboundingbox.b((BaseBlockPosition) blockposition) && generatoraccess.getType(blockposition).getBlock() != Blocks.CHEST) { + if (iblockdata == null) { + iblockdata = a((IBlockAccess) generatoraccess, blockposition, Blocks.CHEST.getBlockData()); + } + + generatoraccess.setTypeAndData(blockposition, iblockdata, 2); + TileEntity tileentity = generatoraccess.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityChest) { + ((TileEntityChest) tileentity).setLootTable(minecraftkey, random.nextLong()); + } + + return true; + } else { + return false; + } + } + + protected boolean a(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox, Random random, int i, int j, int k, EnumDirection enumdirection, MinecraftKey minecraftkey) { + BlockPosition blockposition = new BlockPosition(this.a(i, k), this.d(j), this.b(i, k)); + + if (structureboundingbox.b((BaseBlockPosition) blockposition) && generatoraccess.getType(blockposition).getBlock() != Blocks.DISPENSER) { + this.a(generatoraccess, (IBlockData) Blocks.DISPENSER.getBlockData().set(BlockDispenser.FACING, enumdirection), i, j, k, structureboundingbox); + TileEntity tileentity = generatoraccess.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityDispenser) { + ((TileEntityDispenser) tileentity).setLootTable(minecraftkey, random.nextLong()); + } + + return true; + } else { + return false; + } + } + + protected void a(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox, Random random, int i, int j, int k, EnumDirection enumdirection, BlockDoor blockdoor) { + this.a(generatoraccess, (IBlockData) blockdoor.getBlockData().set(BlockDoor.FACING, enumdirection), i, j, k, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) blockdoor.getBlockData().set(BlockDoor.FACING, enumdirection)).set(BlockDoor.HALF, BlockPropertyDoubleBlockHalf.UPPER), i, j + 1, k, structureboundingbox); + } + + public void a(int i, int j, int k) { + this.n.a(i, j, k); + } + + @Nullable + public EnumDirection f() { + return this.a; + } + + public void a(@Nullable EnumDirection enumdirection) { + this.a = enumdirection; + if (enumdirection == null) { + this.c = EnumBlockRotation.NONE; + this.b = EnumBlockMirror.NONE; + } else { + switch (enumdirection) { + case SOUTH: + this.b = EnumBlockMirror.LEFT_RIGHT; + this.c = EnumBlockRotation.NONE; + break; + case WEST: + this.b = EnumBlockMirror.LEFT_RIGHT; + this.c = EnumBlockRotation.CLOCKWISE_90; + break; + case EAST: + this.b = EnumBlockMirror.NONE; + this.c = EnumBlockRotation.CLOCKWISE_90; + break; + default: + this.b = EnumBlockMirror.NONE; + this.c = EnumBlockRotation.NONE; + } + } + + } + + public abstract static class StructurePieceBlockSelector { + + protected IBlockData a; + + protected StructurePieceBlockSelector() { + this.a = Blocks.AIR.getBlockData(); + } + + public abstract void a(Random random, int i, int j, int k, boolean flag); + + public IBlockData a() { + return this.a; + } + } +} diff --git a/src/main/java/net/minecraft/server/StructureStart.java b/src/main/java/net/minecraft/server/StructureStart.java new file mode 100644 index 000000000000..8b08efe1f404 --- /dev/null +++ b/src/main/java/net/minecraft/server/StructureStart.java @@ -0,0 +1,194 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Iterator; +import java.util.List; +import java.util.Random; + +public abstract class StructureStart { + + protected final List a = java.util.Collections.synchronizedList(Lists.newArrayList()); // Paper + protected StructureBoundingBox b; + protected int c; + protected int d; + private BiomeBase e; + private int f; + + public StructureStart() {} + + public StructureStart(int i, int j, BiomeBase biomebase, SeededRandom seededrandom, long k) { + this.c = i; + this.d = j; + this.e = biomebase; + seededrandom.c(k, this.c, this.d); + } + + public StructureBoundingBox c() { + return this.b; + } + + public List d() { + return this.a; + } + + public void a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + List list = this.a; + + synchronized (this.a) { + Iterator iterator = this.a.iterator(); + + while (iterator.hasNext()) { + StructurePiece structurepiece = (StructurePiece) iterator.next(); + + if (structurepiece.d().a(structureboundingbox) && !structurepiece.a(generatoraccess, random, structureboundingbox, chunkcoordintpair)) { + iterator.remove(); + } + } + + this.a((IBlockAccess) generatoraccess); + } + } + + protected void a(IBlockAccess iblockaccess) { + this.b = StructureBoundingBox.a(); + synchronized (this.a) { // Paper - synchronize + Iterator iterator = this.a.iterator(); + + while (iterator.hasNext()) { + StructurePiece structurepiece = (StructurePiece) iterator.next(); + + this.b.b(structurepiece.d()); + }} // Paper + + } + + public NBTTagCompound a(int i, int j) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + if (this.b()) { + nbttagcompound.setString("id", WorldGenFactory.a(this)); + nbttagcompound.setString("biome", IRegistry.BIOME.getKey(this.e).toString()); + nbttagcompound.setInt("ChunkX", i); + nbttagcompound.setInt("ChunkZ", j); + nbttagcompound.setInt("references", this.f); + nbttagcompound.set("BB", this.b.g()); + NBTTagList nbttaglist = new NBTTagList(); + List list = this.a; + + synchronized (this.a) { + Iterator iterator = this.a.iterator(); + + while (iterator.hasNext()) { + StructurePiece structurepiece = (StructurePiece) iterator.next(); + + nbttaglist.add((NBTBase) structurepiece.c()); + } + } + + nbttagcompound.set("Children", nbttaglist); + this.a(nbttagcompound); + return nbttagcompound; + } else { + nbttagcompound.setString("id", "INVALID"); + return nbttagcompound; + } + } + + public void a(NBTTagCompound nbttagcompound) {} + + public void a(GeneratorAccess generatoraccess, NBTTagCompound nbttagcompound) { + this.c = nbttagcompound.getInt("ChunkX"); + this.d = nbttagcompound.getInt("ChunkZ"); + this.f = nbttagcompound.getInt("references"); + this.e = nbttagcompound.hasKey("biome") ? (BiomeBase) IRegistry.BIOME.get(new MinecraftKey(nbttagcompound.getString("biome"))) : generatoraccess.getChunkProvider().getChunkGenerator().getWorldChunkManager().getBiome(new BlockPosition((this.c << 4) + 9, 0, (this.d << 4) + 9), Biomes.PLAINS); + if (nbttagcompound.hasKey("BB")) { + this.b = new StructureBoundingBox(nbttagcompound.getIntArray("BB")); + } + + NBTTagList nbttaglist = nbttagcompound.getList("Children", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + this.a.add(WorldGenFactory.b(nbttaglist.getCompound(i), generatoraccess)); + } + + this.b(nbttagcompound); + } + + public void b(NBTTagCompound nbttagcompound) {} + + protected void a(IWorldReader iworldreader, Random random, int i) { + int j = iworldreader.getSeaLevel() - i; + int k = this.b.d() + 1; + + if (k < j) { + k += random.nextInt(j - k); + } + + int l = k - this.b.e; + + this.b.a(0, l, 0); + synchronized (this.a) { // Paper - synchronize + Iterator iterator = this.a.iterator(); + + while (iterator.hasNext()) { + StructurePiece structurepiece = (StructurePiece) iterator.next(); + + structurepiece.a(0, l, 0); + }} // Paper + + } + + protected void a(IBlockAccess iblockaccess, Random random, int i, int j) { + int k = j - i + 1 - this.b.d(); + int l; + + if (k > 1) { + l = i + random.nextInt(k); + } else { + l = i; + } + + int i1 = l - this.b.b; + + this.b.a(0, i1, 0); + synchronized (this.a) { + Iterator iterator = this.a.iterator(); + + while (iterator.hasNext()) { + StructurePiece structurepiece = (StructurePiece) iterator.next(); + + structurepiece.a(0, i1, 0); + }} // Paper + + } + + public boolean b() { + return true; + } + + public void b(ChunkCoordIntPair chunkcoordintpair) {} + + public int e() { + return this.c; + } + + public int f() { + return this.d; + } + + public BlockPosition a() { + return new BlockPosition(this.c << 4, 0, this.d << 4); + } + + public boolean g() { + return this.f < this.i(); + } + + public void h() { + ++this.f; + } + + protected int i() { + return 1; + } +} diff --git a/src/main/java/net/minecraft/server/SystemUtils.java b/src/main/java/net/minecraft/server/SystemUtils.java new file mode 100644 index 000000000000..5e71d2ac2735 --- /dev/null +++ b/src/main/java/net/minecraft/server/SystemUtils.java @@ -0,0 +1,201 @@ +package net.minecraft.server; + +import com.google.common.collect.Iterators; +import it.unimi.dsi.fastutil.Hash.Strategy; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.nio.file.InvalidPathException; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; +import java.util.function.Consumer; +import java.util.function.LongSupplier; +import java.util.function.Supplier; +import java.util.regex.Pattern; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class SystemUtils { + + public static LongSupplier a = System::nanoTime; + private static final Logger b = LogManager.getLogger(); + private static final Pattern c = Pattern.compile(".*\\.|(?:CON|PRN|AUX|NUL|COM1|COM2|COM3|COM4|COM5|COM6|COM7|COM8|COM9|LPT1|LPT2|LPT3|LPT4|LPT5|LPT6|LPT7|LPT8|LPT9)(?:\\..*)?", 2); + + public static Collector, ?, Map> a() { + return Collectors.toMap(Entry::getKey, Entry::getValue); + } + + public static > String a(IBlockState iblockstate, T object) { + return iblockstate.a(object); // Paper - decompile fix + } + + public static String a(String s, @Nullable MinecraftKey minecraftkey) { + return minecraftkey == null ? s + ".unregistered_sadface" : s + '.' + minecraftkey.b() + '.' + minecraftkey.getKey().replace('/', '.'); + } + + public static long getMonotonicMillis() { + return getMonotonicNanos() / 1000000L; + } + + public static long getMonotonicNanos() { + return System.nanoTime(); // Paper + } + + public static long getTimeMillis() { + return Instant.now().toEpochMilli(); + } + + public static SystemUtils.OS e() { + String s = System.getProperty("os.name").toLowerCase(Locale.ROOT); + + return s.contains("win") ? SystemUtils.OS.WINDOWS : (s.contains("mac") ? SystemUtils.OS.OSX : (s.contains("solaris") ? SystemUtils.OS.SOLARIS : (s.contains("sunos") ? SystemUtils.OS.SOLARIS : (s.contains("linux") ? SystemUtils.OS.LINUX : (s.contains("unix") ? SystemUtils.OS.LINUX : SystemUtils.OS.UNKNOWN))))); + } + + public static Stream f() { + RuntimeMXBean runtimemxbean = ManagementFactory.getRuntimeMXBean(); + + return runtimemxbean.getInputArguments().stream().filter((s) -> { + return s.startsWith("-X"); + }); + } + + public static boolean a(java.nio.file.Path java_nio_file_path) { + java.nio.file.Path java_nio_file_path1 = java_nio_file_path.normalize(); + + return java_nio_file_path1.equals(java_nio_file_path); + } + + public static boolean b(java.nio.file.Path java_nio_file_path) { + Iterator iterator = java_nio_file_path.iterator(); + + java.nio.file.Path java_nio_file_path1; + + do { + if (!iterator.hasNext()) { + return true; + } + + java_nio_file_path1 = (java.nio.file.Path) iterator.next(); + } while (!SystemUtils.c.matcher(java_nio_file_path1.toString()).matches()); + + return false; + } + + public static java.nio.file.Path a(java.nio.file.Path java_nio_file_path, String s, String s1) { + String s2 = s + s1; + java.nio.file.Path java_nio_file_path1 = Paths.get(s2); + + if (java_nio_file_path1.endsWith(s1)) { + throw new InvalidPathException(s2, "empty resource name"); + } else { + return java_nio_file_path.resolve(java_nio_file_path1); + } + } + + @Nullable + public static V a(FutureTask futuretask, Logger logger) { + try { + futuretask.run(); + return futuretask.get(); + } catch (ExecutionException executionexception) { + logger.fatal("Error executing task", executionexception.getCause() != null ? executionexception.getCause() : executionexception); // Paper + } catch (InterruptedException interruptedexception) { + logger.fatal("Error executing task", interruptedexception); + } + + return null; + } + + public static T a(List list) { + return list.get(list.size() - 1); + } + + public static T a(Iterable iterable, @Nullable T t0) { + Iterator iterator = iterable.iterator(); + T t1 = iterator.next(); + + if (t0 != null) { + Object object = t1; + + while (object != t0) { + if (iterator.hasNext()) { + object = iterator.next(); + } + } + + if (iterator.hasNext()) { + return iterator.next(); + } + } + + return t1; + } + + public static T b(Iterable iterable, @Nullable T t0) { + Iterator iterator = iterable.iterator(); + + T object; // Paper - decompile fix + T object1; // Paper - decompile fix + + for (object1 = null; iterator.hasNext(); object1 = object) { + object = iterator.next(); + if (object == t0) { + if (object1 == null) { + object1 = iterator.hasNext() ? Iterators.getLast(iterator) : t0; + } + break; + } + } + + return object1; + } + + public static T a(Supplier supplier) { + return supplier.get(); + } + + public static T a(T t0, Consumer consumer) { + consumer.accept(t0); + return t0; + } + + public static Strategy g() { + return (Strategy) IdentityHashingStrategy.INSTANCE; // Paper - decompile fix + } + + static enum IdentityHashingStrategy implements Strategy { + + INSTANCE; + + private IdentityHashingStrategy() {} + + public int hashCode(Object object) { + return System.identityHashCode(object); + } + + public boolean equals(Object object, Object object1) { + return object == object1; + } + } + + public static enum OS { + + LINUX, SOLARIS, WINDOWS { + }, + OSX { + }, + UNKNOWN; + + private OS() {} + } +} diff --git a/src/main/java/net/minecraft/server/TagRegistry.java b/src/main/java/net/minecraft/server/TagRegistry.java new file mode 100644 index 000000000000..27d0a2880849 --- /dev/null +++ b/src/main/java/net/minecraft/server/TagRegistry.java @@ -0,0 +1,62 @@ +package net.minecraft.server; + +public class TagRegistry implements IResourcePackListener { + + private final TagsServer a; + private final TagsServer b; + private final TagsServer c; + + public TagRegistry() { + this.a = new TagsServer<>(IRegistry.BLOCK, "tags/blocks", "block"); + this.b = new TagsServer<>(IRegistry.ITEM, "tags/items", "item"); + this.c = new TagsServer<>(IRegistry.FLUID, "tags/fluids", "fluid"); + } + + public TagsServer a() { + return this.a; + } + + public TagsServer b() { + return this.b; + } + + public TagsServer c() { + return this.c; + } + + public void d() { + this.a.b(); + this.b.b(); + this.c.b(); + } + + public void a(IResourceManager iresourcemanager) { + this.d(); + this.a.a(iresourcemanager); + this.b.a(iresourcemanager); + this.c.a(iresourcemanager); + TagsBlock.a((Tags) this.a); + TagsItem.a((Tags) this.b); + TagsFluid.a((Tags) this.c); + // CraftBukkit start + this.a.version++; + this.b.version++; + this.c.version++; + // CraftBukkit end + } + + public void a(PacketDataSerializer packetdataserializer) { + this.a.a(packetdataserializer); + this.b.a(packetdataserializer); + this.c.a(packetdataserializer); + } + + public static TagRegistry b(PacketDataSerializer packetdataserializer) { + TagRegistry tagregistry = new TagRegistry(); + + tagregistry.a().b(packetdataserializer); + tagregistry.b().b(packetdataserializer); + tagregistry.c().b(packetdataserializer); + return tagregistry; + } +} diff --git a/src/main/java/net/minecraft/server/TagsServer.java b/src/main/java/net/minecraft/server/TagsServer.java new file mode 100644 index 000000000000..b9c1a3f27446 --- /dev/null +++ b/src/main/java/net/minecraft/server/TagsServer.java @@ -0,0 +1,55 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; + +public class TagsServer extends Tags { + + private final IRegistry a; + public int version; // CraftBukkit + + public TagsServer(IRegistry iregistry, String s, String s1) { + super(iregistry::c, iregistry::get, s, false, s1); + this.a = iregistry; + } + + public void a(PacketDataSerializer packetdataserializer) { + packetdataserializer.d(this.c().size()); + Iterator iterator = this.c().entrySet().iterator(); + + while (iterator.hasNext()) { + Entry> entry = (Entry) iterator.next(); + + packetdataserializer.a((MinecraftKey) entry.getKey()); + packetdataserializer.d(((Tag) entry.getValue()).a().size()); + Iterator iterator1 = ((Tag) entry.getValue()).a().iterator(); + + while (iterator1.hasNext()) { + T t0 = (T) iterator1.next(); // CraftBukkit - decompile error + + packetdataserializer.d(this.a.a(t0)); + } + } + + } + + public void b(PacketDataSerializer packetdataserializer) { + int i = packetdataserializer.g(); + + for (int j = 0; j < i; ++j) { + MinecraftKey minecraftkey = packetdataserializer.l(); + int k = packetdataserializer.g(); + List list = Lists.newArrayList(); + + for (int l = 0; l < k; ++l) { + list.add(this.a.fromId(packetdataserializer.g())); + } + + this.c().put(minecraftkey, (Tag) Tag.a.a().a((Collection) list).b(minecraftkey)); // CraftBukkit - decompile error + } + + } +} diff --git a/src/main/java/net/minecraft/server/TickListServer.java b/src/main/java/net/minecraft/server/TickListServer.java new file mode 100644 index 000000000000..6571fc595279 --- /dev/null +++ b/src/main/java/net/minecraft/server/TickListServer.java @@ -0,0 +1,234 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import org.bukkit.craftbukkit.util.HashTreeSet; // CraftBukkit + +public class TickListServer implements TickList { + + protected final Predicate a; + protected final Function b; + protected final Function c; + // protected final Set> nextTickListHash = Sets.newHashSet(); + protected final HashTreeSet nextTickList = new HashTreeSet<>(); // CraftBukkit - HashTreeSet + private final WorldServer f; + private final List> g = Lists.newArrayList(); + private final Consumer> h; + + public TickListServer(WorldServer worldserver, Predicate predicate, Function function, Function function1, Consumer> consumer, String timingsType) { // Paper + this.a = predicate; + this.b = function; + this.c = function1; + this.f = worldserver; + this.h = consumer; + // Paper start + timingCleanup = co.aikar.timings.WorldTimingsHandler.getTickList(worldserver, timingsType + " - Cleanup"); + timingTicking = co.aikar.timings.WorldTimingsHandler.getTickList(worldserver, timingsType + " - Ticking"); + } + private final co.aikar.timings.Timing timingCleanup; // Paper + private final co.aikar.timings.Timing timingTicking; // Paper + // Paper end + + public void a() { + int i = this.nextTickList.size(); + + if (false) { // CraftBukkit + throw new IllegalStateException("TickNextTick list out of synch"); + } else { + if (i > 65536) { + // CraftBukkit start - If the server has too much to process over time, try to alleviate that + if (i > 20 * 65536) { + i = i / 20; + } else { + i = 65536; + } + // CraftBukkit end + } + + this.f.methodProfiler.enter("cleaning"); + timingCleanup.startTiming(); // Paper + NextTickListEntry nextticklistentry; // CraftBukkit - decompile error + + for (int j = 0; j < i; ++j) { + nextticklistentry = (NextTickListEntry) this.nextTickList.first(); + if (nextticklistentry.b > this.f.getTime()) { + break; + } + + this.nextTickList.remove(nextticklistentry); + // this.nextTickListHash.remove(nextticklistentry); // CraftBukkit - use nextTickList + this.g.add(nextticklistentry); + } + timingCleanup.stopTiming(); // Paper + + this.f.methodProfiler.exit(); + this.f.methodProfiler.enter("ticking"); + timingTicking.startTiming(); // Paper + Iterator iterator = this.g.iterator(); + + while (iterator.hasNext()) { + nextticklistentry = (NextTickListEntry) iterator.next(); + iterator.remove(); + boolean flag = false; + + if (this.f.areChunksLoadedBetween(nextticklistentry.a.a(0, 0, 0), nextticklistentry.a.a(0, 0, 0))) { + try { + this.h.accept(nextticklistentry); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Exception while ticking"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Block being ticked"); + + CrashReportSystemDetails.a(crashreportsystemdetails, nextticklistentry.a, (IBlockData) null); + throw new ReportedException(crashreport); + } + } else { + this.a(nextticklistentry.a, nextticklistentry.a(), 0); + } + } + + this.f.methodProfiler.exit(); + this.g.clear(); + timingTicking.stopTiming(); // Paper + } + } + + public boolean b(BlockPosition blockposition, T t0) { + return this.g.contains(new NextTickListEntry<>(blockposition, t0)); + } + + public List> a(Chunk chunk, boolean flag) { + ChunkCoordIntPair chunkcoordintpair = chunk.getPos(); + int i = (chunkcoordintpair.x << 4) - 2; + int j = i + 16 + 2; + int k = (chunkcoordintpair.z << 4) - 2; + int l = k + 16 + 2; + + return this.a(new StructureBoundingBox(i, 0, k, j, 256, l), flag); + } + + public List> a(StructureBoundingBox structureboundingbox, boolean flag) { + List> list = null; + + for (int i = 0; i < 2; ++i) { + Iterator iterator; + + if (i == 0) { + iterator = this.nextTickList.iterator(); + } else { + iterator = this.g.iterator(); + } + + while (iterator.hasNext()) { + NextTickListEntry nextticklistentry = (NextTickListEntry) iterator.next(); + BlockPosition blockposition = nextticklistentry.a; + + if (blockposition.getX() >= structureboundingbox.a && blockposition.getX() < structureboundingbox.d && blockposition.getZ() >= structureboundingbox.c && blockposition.getZ() < structureboundingbox.f) { + if (flag) { + if (i == 0) { + // this.nextTickListHash.remove(nextticklistentry); // CraftBukkit - removed + } + + iterator.remove(); + } + + if (list == null) { + list = Lists.newArrayList(); + } + + list.add(nextticklistentry); + } + } + } + + return (List) (list == null ? Collections.emptyList() : list); + } + + public void a(StructureBoundingBox structureboundingbox, BlockPosition blockposition) { + List> list = this.a(structureboundingbox, false); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + NextTickListEntry nextticklistentry = (NextTickListEntry) iterator.next(); + + if (structureboundingbox.b((BaseBlockPosition) nextticklistentry.a)) { + BlockPosition blockposition1 = nextticklistentry.a.a((BaseBlockPosition) blockposition); + + this.b(blockposition1, nextticklistentry.a(), (int) (nextticklistentry.b - this.f.getWorldData().getTime()), nextticklistentry.c); + } + } + + } + + public NBTTagList a(Chunk chunk) { + List> list = this.a(chunk, false); + long i = this.f.getTime(); + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + NextTickListEntry nextticklistentry = (NextTickListEntry) iterator.next(); + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setString("i", ((MinecraftKey) this.b.apply(nextticklistentry.a())).toString()); + nbttagcompound.setInt("x", nextticklistentry.a.getX()); + nbttagcompound.setInt("y", nextticklistentry.a.getY()); + nbttagcompound.setInt("z", nextticklistentry.a.getZ()); + nbttagcompound.setInt("t", (int) (nextticklistentry.b - i)); + nbttagcompound.setInt("p", nextticklistentry.c.a()); + nbttaglist.add((NBTBase) nbttagcompound); + } + + return nbttaglist; + } + + public void a(NBTTagList nbttaglist) { + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound = nbttaglist.getCompound(i); + T t0 = this.c.apply(new MinecraftKey(nbttagcompound.getString("i"))); + + if (t0 != null) { + this.b(new BlockPosition(nbttagcompound.getInt("x"), nbttagcompound.getInt("y"), nbttagcompound.getInt("z")), t0, nbttagcompound.getInt("t"), TickListPriority.a(nbttagcompound.getInt("p"))); + } + } + + } + + public boolean a(BlockPosition blockposition, T t0) { + return this.nextTickList.contains(new NextTickListEntry<>(blockposition, t0)); // CraftBukkit + } + + public void a(BlockPosition blockposition, T t0, int i, TickListPriority ticklistpriority) { + if (!this.a.test(t0)) { + if (this.f.isLoaded(blockposition)) { + this.c(blockposition, t0, i, ticklistpriority); + } + + } + } + + protected void b(BlockPosition blockposition, T t0, int i, TickListPriority ticklistpriority) { + if (!this.a.test(t0)) { + this.c(blockposition, t0, i, ticklistpriority); + } + + } + + private void c(BlockPosition blockposition, T t0, int i, TickListPriority ticklistpriority) { + NextTickListEntry nextticklistentry = new NextTickListEntry<>(blockposition, t0, (long) i + this.f.getTime(), ticklistpriority); + + // CraftBukkit - use nextTickList + if (!this.nextTickList.contains(nextticklistentry)) { + this.nextTickList.add(nextticklistentry); + } + + } +} diff --git a/src/main/java/net/minecraft/server/TileEntity.java b/src/main/java/net/minecraft/server/TileEntity.java new file mode 100644 index 000000000000..d67fd92d9df9 --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntity.java @@ -0,0 +1,230 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import co.aikar.timings.MinecraftTimings; // Paper +import co.aikar.timings.Timing; // Paper +import org.bukkit.inventory.InventoryHolder; // CraftBukkit + +public abstract class TileEntity implements KeyedObject { // Paper + + public Timing tickTimer = MinecraftTimings.getTileEntityTimings(this); // Paper + boolean isLoadingStructure = false; // Paper + private static final Logger a = LogManager.getLogger(); + private final TileEntityTypes e; public TileEntityTypes getTileEntityType() { return e; } // Paper - OBFHELPER + protected World world; + protected BlockPosition position; + protected boolean d; + @Nullable + private IBlockData f; + + public TileEntity(TileEntityTypes tileentitytypes) { + this.position = BlockPosition.ZERO; + this.e = tileentitytypes; + } + + // Paper start + private String tileEntityKeyString = null; + private MinecraftKey tileEntityKey = null; + + @Override + public MinecraftKey getMinecraftKey() { + if (tileEntityKey == null) { + tileEntityKey = TileEntityTypes.a(this.getTileEntityType()); + tileEntityKeyString = tileEntityKey != null ? tileEntityKey.toString() : null; + } + return tileEntityKey; + } + + @Override + public String getMinecraftKeyString() { + getMinecraftKey(); // Try to load if it doesn't exists. + return tileEntityKeyString; + } + + private java.lang.ref.WeakReference currentChunk = null; + public Chunk getCurrentChunk() { + final Chunk chunk = currentChunk != null ? currentChunk.get() : null; + return chunk != null && chunk.isLoaded() ? chunk : null; + } + public void setCurrentChunk(Chunk chunk) { + this.currentChunk = chunk != null ? new java.lang.ref.WeakReference<>(chunk) : null; + } + static boolean IGNORE_TILE_UPDATES = false; + // Paper end + + @Nullable + public World getWorld() { + return this.world; + } + + public void setWorld(World world) { + this.world = world; + } + + public boolean hasWorld() { + return this.world != null; + } + + public void load(NBTTagCompound nbttagcompound) { + this.position = new BlockPosition(nbttagcompound.getInt("x"), nbttagcompound.getInt("y"), nbttagcompound.getInt("z")); + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + return this.d(nbttagcompound); + } + + private NBTTagCompound d(NBTTagCompound nbttagcompound) { + MinecraftKey minecraftkey = TileEntityTypes.a(this.C()); + + if (minecraftkey == null) { + throw new RuntimeException(this.getClass() + " is missing a mapping! This is a bug!"); + } else { + nbttagcompound.setString("id", minecraftkey.toString()); + nbttagcompound.setInt("x", this.position.getX()); + nbttagcompound.setInt("y", this.position.getY()); + nbttagcompound.setInt("z", this.position.getZ()); + return nbttagcompound; + } + } + + // CraftBukkit start + @Nullable + public static TileEntity create(NBTTagCompound nbttagcompound) { + return create(nbttagcompound, null); + } + + @Nullable + public static TileEntity create(NBTTagCompound nbttagcompound, @Nullable World world) { + // CraftBukkit end + TileEntity tileentity = null; + String s = nbttagcompound.getString("id"); + + try { + tileentity = TileEntityTypes.a(s); + } catch (Throwable throwable) { + TileEntity.a.error("Failed to create block entity {}", s, throwable); + } + + if (tileentity != null) { + try { + tileentity.setWorld(world); // CraftBukkit + tileentity.load(nbttagcompound); + } catch (Throwable throwable1) { + TileEntity.a.error("Failed to load data for block entity {}", s, throwable1); + tileentity = null; + } + } else { + TileEntity.a.warn("Skipping BlockEntity with id {}", s); + } + + return tileentity; + } + + public void update() { + if (this.world != null) { + if (IGNORE_TILE_UPDATES) return; // Paper + this.f = this.world.getType(this.position); + this.world.b(this.position, this); + if (!this.f.isAir()) { + this.world.updateAdjacentComparators(this.position, this.f.getBlock()); + } + } + + } + + public BlockPosition getPosition() { + return this.position; + } + + public IBlockData getBlock() { + if (this.f == null) { + this.f = this.world.getType(this.position); + } + + return this.f; + } + + @Nullable + public PacketPlayOutTileEntityData getUpdatePacket() { + return null; + } + + public NBTTagCompound aa_() { + return this.d(new NBTTagCompound()); + } + + public boolean x() { + return this.d; + } + + public void y() { + this.d = true; + } + + public void z() { + this.d = false; + } + + public boolean c(int i, int j) { + return false; + } + + public void invalidateBlockCache() { + this.f = null; + } + + public void a(CrashReportSystemDetails crashreportsystemdetails) { + crashreportsystemdetails.a("Name", () -> { + return IRegistry.BLOCK_ENTITY_TYPE.getKey(this.C()) + " // " + this.getClass().getCanonicalName(); + }); + if (this.world != null) { + // Paper start - Prevent TileEntity and Entity crashes + IBlockData block = this.getBlock(); + if (block != null) { + CrashReportSystemDetails.a(crashreportsystemdetails, this.position, block); + } + // Paper end + CrashReportSystemDetails.a(crashreportsystemdetails, this.position, this.world.getType(this.position)); + } + } + + public void setPosition(BlockPosition blockposition) { + this.position = blockposition.h(); + } + + public boolean isFilteredNBT() { + return false; + } + + public void a(EnumBlockRotation enumblockrotation) {} + + public void a(EnumBlockMirror enumblockmirror) {} + + public TileEntityTypes C() { + return this.e; + } + + // CraftBukkit start - add method + // Paper start + public InventoryHolder getOwner() { + return getOwner(true); + } + public InventoryHolder getOwner(boolean useSnapshot) { + // Paper end + if (world == null) return null; + // Spigot start + org.bukkit.block.Block block = world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()); + if (block == null) { + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "No block for owner at %s %d %d %d", new Object[]{world.getWorld(), position.getX(), position.getY(), position.getZ()}); + return null; + } + // Spigot end + org.bukkit.block.BlockState state = block.getState(useSnapshot); // Paper + if (state instanceof InventoryHolder) return (InventoryHolder) state; + return null; + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/TileEntityBanner.java b/src/main/java/net/minecraft/server/TileEntityBanner.java new file mode 100644 index 000000000000..dfee7332ea2b --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntityBanner.java @@ -0,0 +1,152 @@ +package net.minecraft.server; + +import java.util.List; +import java.util.function.Supplier; +import javax.annotation.Nullable; + +public class TileEntityBanner extends TileEntity implements INamableTileEntity { + + private IChatBaseComponent a; + public EnumColor color; + public NBTTagList patterns; + private boolean g; + private List h; + private List i; + private String j; + + public TileEntityBanner() { + super(TileEntityTypes.BANNER); + this.color = EnumColor.WHITE; + } + + public TileEntityBanner(EnumColor enumcolor) { + this(); + this.color = enumcolor; + } + + public void a(ItemStack itemstack, EnumColor enumcolor) { + this.patterns = null; + NBTTagCompound nbttagcompound = itemstack.b("BlockEntityTag"); + + if (nbttagcompound != null && nbttagcompound.hasKeyOfType("Patterns", 9)) { + this.patterns = nbttagcompound.getList("Patterns", 10).clone(); + // CraftBukkit start + while (this.patterns.size() > 20) { + this.patterns.remove(20); + } + // CraftBukkit end + } + + this.color = enumcolor; + this.h = null; + this.i = null; + this.j = ""; + this.g = true; + this.a = itemstack.hasName() ? itemstack.getName() : null; + } + + public IChatBaseComponent getDisplayName() { + return (IChatBaseComponent) (this.a != null ? this.a : new ChatMessage("block.minecraft.banner", new Object[0])); + } + + public boolean hasCustomName() { + return this.a != null; + } + + @Nullable + public IChatBaseComponent getCustomName() { + return this.a; + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + if (this.patterns != null) { + nbttagcompound.set("Patterns", this.patterns); + } + + if (this.a != null) { + nbttagcompound.setString("CustomName", IChatBaseComponent.ChatSerializer.a(this.a)); + } + + return nbttagcompound; + } + + public void load(NBTTagCompound nbttagcompound) { + super.load(nbttagcompound); + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.a = MCUtil.getBaseComponentFromNbt("CustomName", nbttagcompound); // Paper - Catch ParseException + } + + if (this.hasWorld()) { + this.color = ((BlockBannerAbstract) this.getBlock().getBlock()).b(); + } else { + this.color = null; + } + + this.patterns = nbttagcompound.getList("Patterns", 10); + // CraftBukkit start + while (this.patterns.size() > 20) { + this.patterns.remove(20); + } + // CraftBukkit end + this.h = null; + this.i = null; + this.j = null; + this.g = true; + } + + @Nullable + public PacketPlayOutTileEntityData getUpdatePacket() { + return new PacketPlayOutTileEntityData(this.position, 6, this.aa_()); + } + + public NBTTagCompound aa_() { + return this.save(new NBTTagCompound()); + } + + public static int a(ItemStack itemstack) { + NBTTagCompound nbttagcompound = itemstack.b("BlockEntityTag"); + + return nbttagcompound != null && nbttagcompound.hasKey("Patterns") ? nbttagcompound.getList("Patterns", 10).size() : 0; + } + + public static void b(ItemStack itemstack) { + NBTTagCompound nbttagcompound = itemstack.b("BlockEntityTag"); + + if (nbttagcompound != null && nbttagcompound.hasKeyOfType("Patterns", 9)) { + NBTTagList nbttaglist = nbttagcompound.getList("Patterns", 10); + + if (!nbttaglist.isEmpty()) { + nbttaglist.remove(nbttaglist.size() - 1); + if (nbttaglist.isEmpty()) { + itemstack.c("BlockEntityTag"); + } + + } + } + } + + public ItemStack a(IBlockData iblockdata) { + ItemStack itemstack = new ItemStack(BlockBanner.a(this.a(() -> { + return iblockdata; + }))); + + if (this.patterns != null && !this.patterns.isEmpty()) { + itemstack.a("BlockEntityTag").set("Patterns", this.patterns.clone()); + } + + if (this.a != null) { + itemstack.a(this.a); + } + + return itemstack; + } + + public EnumColor a(Supplier supplier) { + if (this.color == null) { + this.color = ((BlockBannerAbstract) ((IBlockData) supplier.get()).getBlock()).b(); + } + + return this.color; + } +} diff --git a/src/main/java/net/minecraft/server/TileEntityBeacon.java b/src/main/java/net/minecraft/server/TileEntityBeacon.java new file mode 100644 index 000000000000..c6bd54a2c912 --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntityBeacon.java @@ -0,0 +1,491 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.potion.CraftPotionUtil; +import org.bukkit.entity.HumanEntity; +import org.bukkit.potion.PotionEffect; +// CraftBukkit end + +// Paper start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.entity.Player; +import com.destroystokyo.paper.event.block.BeaconEffectEvent; +// Paper end + +public class TileEntityBeacon extends TileEntityContainer implements IWorldInventory, ITickable { + + public static final MobEffectList[][] a = new MobEffectList[][] { { MobEffects.FASTER_MOVEMENT, MobEffects.FASTER_DIG}, { MobEffects.RESISTANCE, MobEffects.JUMP}, { MobEffects.INCREASE_DAMAGE}, { MobEffects.REGENERATION}}; + private static final Set e = (Set) Arrays.stream(TileEntityBeacon.a).flatMap(Arrays::stream).collect(Collectors.toSet()); + private final List f = Lists.newArrayList(); + private boolean i; + private boolean j; + public int levels = -1; + @Nullable + public MobEffectList primaryEffect; + @Nullable + public MobEffectList secondaryEffect; + private ItemStack inventorySlot; + private IChatBaseComponent o; + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public List getContents() { + return Arrays.asList(this.inventorySlot); + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + + public PotionEffect getPrimaryEffect() { + return (this.primaryEffect != null) ? CraftPotionUtil.toBukkit(new MobEffect(this.primaryEffect, getLevel(), getAmplification(), true, true)) : null; + } + + public PotionEffect getSecondaryEffect() { + return (hasSecondaryEffect()) ? CraftPotionUtil.toBukkit(new MobEffect(this.secondaryEffect, getLevel(), getAmplification(), true, true)) : null; + } + // CraftBukkit end + + public TileEntityBeacon() { + super(TileEntityTypes.BEACON); + this.inventorySlot = ItemStack.a; + } + + public void tick() { + if (this.world.getTime() % 80L == 0L) { + this.p(); + if (this.i) { + this.a(SoundEffects.BLOCK_BEACON_AMBIENT); + } + } + + if (!this.world.isClientSide && this.i != this.j) { + this.j = this.i; + this.a(this.i ? SoundEffects.BLOCK_BEACON_ACTIVATE : SoundEffects.BLOCK_BEACON_DEACTIVATE); + } + + } + + public void p() { + if (this.world != null) { + this.E(); + this.applyEffects(); + } + + } + + public void a(SoundEffect soundeffect) { + this.world.a((EntityHuman) null, this.position, soundeffect, SoundCategory.BLOCKS, 1.0F, 1.0F); + } + + // CraftBukkit start - split into components + private byte getAmplification() { + { + byte b0 = 0; + + if (this.levels >= 4 && this.primaryEffect == this.secondaryEffect) { + b0 = 1; + } + + return b0; + } + } + + private int getLevel() { + { + int i = (9 + this.levels * 2) * 20; + return i; + } + } + + public List getHumansInRange() { + { + double d0 = (double) (this.levels * 10 + 10); + + int j = this.position.getX(); + int k = this.position.getY(); + int l = this.position.getZ(); + AxisAlignedBB axisalignedbb = (new AxisAlignedBB((double) j, (double) k, (double) l, (double) (j + 1), (double) (k + 1), (double) (l + 1))).g(d0).b(0.0D, (double) this.world.getHeight(), 0.0D); + List list = this.world.a(EntityHuman.class, axisalignedbb); + + return list; + } + } + + private void applyEffect(List list, MobEffectList effects, int i, int b0) { + // Paper - BeaconEffectEvent + applyEffect(list, effects, i, b0, true); + } + + private void applyEffect(List list, MobEffectList effects, int i, int b0, boolean isPrimary) { + // Paper - BeaconEffectEvent + { + Iterator iterator = list.iterator(); + + EntityHuman entityhuman; + + // Paper start - BeaconEffectEvent + org.bukkit.block.Block block = world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()); + PotionEffect effect = CraftPotionUtil.toBukkit(new MobEffect(effects, i, b0, true, true)); + // Paper end + + while (iterator.hasNext()) { + entityhuman = (EntityHuman) iterator.next(); + + // Paper start - BeaconEffectEvent + BeaconEffectEvent event = new BeaconEffectEvent(block, effect, (Player) entityhuman.getBukkitEntity(), isPrimary); + if (CraftEventFactory.callEvent(event).isCancelled()) continue; + PotionEffect eventEffect = event.getEffect(); + entityhuman.addEffect(new MobEffect(MobEffectList.fromId(eventEffect.getType().getId()), eventEffect.getDuration(), eventEffect.getAmplifier(), true, true), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.BEACON); + // Paper end + } + } + } + + private boolean hasSecondaryEffect() { + { + if (this.levels >= 4 && this.primaryEffect != this.secondaryEffect && this.secondaryEffect != null) { + return true; + } + + return false; + } + } + + private void applyEffects() { + if (this.i && this.levels > 0 && !this.world.isClientSide && this.primaryEffect != null) { + byte b0 = getAmplification(); + + int i = getLevel(); + List list = getHumansInRange(); + + applyEffect(list, this.primaryEffect, i, b0, true); // Paper - BeaconEffectEvent + + if (hasSecondaryEffect()) { + applyEffect(list, this.secondaryEffect, i, 0, false); // Paper - BeaconEffectEvent + } + } + + } + // CraftBukkit end + + private void E() { + int i = this.position.getX(); + int j = this.position.getY(); + int k = this.position.getZ(); + int l = this.levels; + + this.levels = 0; + this.f.clear(); + this.i = true; + TileEntityBeacon.BeaconColorTracker tileentitybeacon_beaconcolortracker = new TileEntityBeacon.BeaconColorTracker(EnumColor.WHITE.d()); + + this.f.add(tileentitybeacon_beaconcolortracker); + boolean flag = true; + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + int i1; + + for (i1 = j + 1; i1 < 256; ++i1) { + IBlockData iblockdata = this.world.getType(blockposition_mutableblockposition.c(i, i1, k)); + Block block = iblockdata.getBlock(); + float[] afloat; + + if (block instanceof BlockStainedGlass) { + afloat = ((BlockStainedGlass) block).d().d(); + } else { + if (!(block instanceof BlockStainedGlassPane)) { + if (iblockdata.b(this.world, blockposition_mutableblockposition) >= 15 && block != Blocks.BEDROCK) { + this.i = false; + this.f.clear(); + break; + } + + tileentitybeacon_beaconcolortracker.a(); + continue; + } + + afloat = ((BlockStainedGlassPane) block).d().d(); + } + + if (!flag) { + afloat = new float[] { (tileentitybeacon_beaconcolortracker.b()[0] + afloat[0]) / 2.0F, (tileentitybeacon_beaconcolortracker.b()[1] + afloat[1]) / 2.0F, (tileentitybeacon_beaconcolortracker.b()[2] + afloat[2]) / 2.0F}; + } + + if (Arrays.equals(afloat, tileentitybeacon_beaconcolortracker.b())) { + tileentitybeacon_beaconcolortracker.a(); + } else { + tileentitybeacon_beaconcolortracker = new TileEntityBeacon.BeaconColorTracker(afloat); + this.f.add(tileentitybeacon_beaconcolortracker); + } + + flag = false; + } + + if (this.i) { + for (i1 = 1; i1 <= 4; this.levels = i1++) { + int j1 = j - i1; + + if (j1 < 0) { + break; + } + + boolean flag1 = true; + + for (int k1 = i - i1; k1 <= i + i1 && flag1; ++k1) { + for (int l1 = k - i1; l1 <= k + i1; ++l1) { + Block block1 = this.world.getType(new BlockPosition(k1, j1, l1)).getBlock(); + + if (block1 != Blocks.EMERALD_BLOCK && block1 != Blocks.GOLD_BLOCK && block1 != Blocks.DIAMOND_BLOCK && block1 != Blocks.IRON_BLOCK) { + flag1 = false; + break; + } + } + } + + if (!flag1) { + break; + } + } + + if (this.levels == 0) { + this.i = false; + } + } + + if (!this.world.isClientSide && l < this.levels) { + Iterator iterator = this.world.a(EntityPlayer.class, (new AxisAlignedBB((double) i, (double) j, (double) k, (double) i, (double) (j - 4), (double) k)).grow(10.0D, 5.0D, 10.0D)).iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + CriterionTriggers.l.a(entityplayer, this); + } + } + + } + + public int s() { + return this.levels; + } + + @Nullable + public PacketPlayOutTileEntityData getUpdatePacket() { + return new PacketPlayOutTileEntityData(this.position, 3, this.aa_()); + } + + public NBTTagCompound aa_() { + return this.save(new NBTTagCompound()); + } + + @Nullable + private static MobEffectList e(int i) { + MobEffectList mobeffectlist = MobEffectList.fromId(i); + + return TileEntityBeacon.e.contains(mobeffectlist) ? mobeffectlist : null; + } + + public void load(NBTTagCompound nbttagcompound) { + super.load(nbttagcompound); + // Craftbukkit start - persist manually set non-default beacon effects (SPIGOT-3598) + this.primaryEffect = MobEffectList.fromId(nbttagcompound.getInt("Primary")); + this.secondaryEffect = MobEffectList.fromId(nbttagcompound.getInt("Secondary")); + // Craftbukkit end + this.levels = nbttagcompound.getInt("Levels"); + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + nbttagcompound.setInt("Primary", MobEffectList.getId(this.primaryEffect)); + nbttagcompound.setInt("Secondary", MobEffectList.getId(this.secondaryEffect)); + nbttagcompound.setInt("Levels", this.levels); + return nbttagcompound; + } + + public int getSize() { + return 1; + } + + public boolean P_() { + return this.inventorySlot.isEmpty(); + } + + public ItemStack getItem(int i) { + return i == 0 ? this.inventorySlot : ItemStack.a; + } + + public ItemStack splitStack(int i, int j) { + if (i == 0 && !this.inventorySlot.isEmpty()) { + if (j >= this.inventorySlot.getCount()) { + ItemStack itemstack = this.inventorySlot; + + this.inventorySlot = ItemStack.a; + return itemstack; + } else { + return this.inventorySlot.cloneAndSubtract(j); + } + } else { + return ItemStack.a; + } + } + + public ItemStack splitWithoutUpdate(int i) { + if (i == 0) { + ItemStack itemstack = this.inventorySlot; + + this.inventorySlot = ItemStack.a; + return itemstack; + } else { + return ItemStack.a; + } + } + + public void setItem(int i, ItemStack itemstack) { + if (i == 0) { + this.inventorySlot = itemstack; + } + + } + + public IChatBaseComponent getDisplayName() { + return (IChatBaseComponent) (this.o != null ? this.o : new ChatMessage("container.beacon", new Object[0])); + } + + public boolean hasCustomName() { + return this.o != null; + } + + @Nullable + public IChatBaseComponent getCustomName() { + return this.o; + } + + public void setCustomName(@Nullable IChatBaseComponent ichatbasecomponent) { + this.o = ichatbasecomponent; + } + + public int getMaxStackSize() { + return 1; + } + + public boolean a(EntityHuman entityhuman) { + return this.world.getTileEntity(this.position) != this ? false : entityhuman.d((double) this.position.getX() + 0.5D, (double) this.position.getY() + 0.5D, (double) this.position.getZ() + 0.5D) <= 64.0D; + } + + public void startOpen(EntityHuman entityhuman) {} + + public void closeContainer(EntityHuman entityhuman) {} + + public boolean b(int i, ItemStack itemstack) { + return itemstack.getItem() == Items.EMERALD || itemstack.getItem() == Items.DIAMOND || itemstack.getItem() == Items.GOLD_INGOT || itemstack.getItem() == Items.IRON_INGOT; + } + + public String getContainerName() { + return "minecraft:beacon"; + } + + public Container createContainer(PlayerInventory playerinventory, EntityHuman entityhuman) { + return new ContainerBeacon(playerinventory, this); + } + + public int getProperty(int i) { + switch (i) { + case 0: + return this.levels; + case 1: + return MobEffectList.getId(this.primaryEffect); + case 2: + return MobEffectList.getId(this.secondaryEffect); + default: + return 0; + } + } + + public void setProperty(int i, int j) { + switch (i) { + case 0: + this.levels = j; + break; + case 1: + this.primaryEffect = e(j); + break; + case 2: + this.secondaryEffect = e(j); + } + + if (!this.world.isClientSide && i == 1 && this.i) { + this.a(SoundEffects.BLOCK_BEACON_POWER_SELECT); + } + + } + + public int h() { + return 3; + } + + public void clear() { + this.inventorySlot = ItemStack.a; + } + + public boolean c(int i, int j) { + if (i == 1) { + this.p(); + return true; + } else { + return super.c(i, j); + } + } + + public int[] getSlotsForFace(EnumDirection enumdirection) { + return new int[0]; + } + + public boolean canPlaceItemThroughFace(int i, ItemStack itemstack, @Nullable EnumDirection enumdirection) { + return false; + } + + public boolean canTakeItemThroughFace(int i, ItemStack itemstack, EnumDirection enumdirection) { + return false; + } + + public static class BeaconColorTracker { + + private final float[] a; + private int b; + + public BeaconColorTracker(float[] afloat) { + this.a = afloat; + this.b = 1; + } + + protected void a() { + ++this.b; + } + + public float[] b() { + return this.a; + } + } +} diff --git a/src/main/java/net/minecraft/server/TileEntityBrewingStand.java b/src/main/java/net/minecraft/server/TileEntityBrewingStand.java new file mode 100644 index 000000000000..ff8a5926e1b0 --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntityBrewingStand.java @@ -0,0 +1,347 @@ +package net.minecraft.server; + +import java.util.Arrays; +import java.util.Iterator; +import javax.annotation.Nullable; + +// CraftBukkit start +import java.util.List; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.BrewEvent; +import org.bukkit.event.inventory.BrewingStandFuelEvent; +import org.bukkit.inventory.InventoryHolder; +// CraftBukkit end + +public class TileEntityBrewingStand extends TileEntityContainer implements IWorldInventory, ITickable { + + private static final int[] a = new int[] { 3}; + private static final int[] e = new int[] { 0, 1, 2, 3}; + private static final int[] f = new int[] { 0, 1, 2, 4}; + private NonNullList items; + private int brewTime; + private boolean[] i; + private Item j; + private IChatBaseComponent k; + private int fuelLevel; + // CraftBukkit start - add fields and methods + private int lastTick = MinecraftServer.currentTick; + public List transaction = new java.util.ArrayList(); + private int maxStack = 64; + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public List getContents() { + return this.items; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + public TileEntityBrewingStand() { + super(TileEntityTypes.BREWING_STAND); + this.items = NonNullList.a(5, ItemStack.a); + } + + public IChatBaseComponent getDisplayName() { + return (IChatBaseComponent) (this.k != null ? this.k : new ChatMessage("container.brewing", new Object[0])); + } + + public boolean hasCustomName() { + return this.k != null; + } + + @Nullable + public IChatBaseComponent getCustomName() { + return this.k; + } + + public void setCustomName(@Nullable IChatBaseComponent ichatbasecomponent) { + this.k = ichatbasecomponent; + } + + public int getSize() { + return this.items.size(); + } + + public boolean P_() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + public void tick() { + ItemStack itemstack = (ItemStack) this.items.get(4); + + if (this.fuelLevel <= 0 && itemstack.getItem() == Items.BLAZE_POWDER) { + // CraftBukkit start + BrewingStandFuelEvent event = new BrewingStandFuelEvent(world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()), CraftItemStack.asCraftMirror(itemstack), 20); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + + this.fuelLevel = event.getFuelPower(); + if (this.fuelLevel > 0 && event.isConsuming()) { + itemstack.subtract(1); + } + // CraftBukkit end + this.update(); + } + + boolean flag = this.q(); + boolean flag1 = this.brewTime > 0; + ItemStack itemstack1 = (ItemStack) this.items.get(3); + + // CraftBukkit start - Use wall time instead of ticks for brewing + int elapsedTicks = MinecraftServer.currentTick - this.lastTick; + this.lastTick = MinecraftServer.currentTick; + + if (flag1) { + this.brewTime -= elapsedTicks; + boolean flag2 = this.brewTime <= 0; // == -> <= + // CraftBukkit end + + if (flag2 && flag) { + this.r(); + this.update(); + } else if (!flag) { + this.brewTime = 0; + this.update(); + } else if (this.j != itemstack1.getItem()) { + this.brewTime = 0; + this.update(); + } + } else if (flag && this.fuelLevel > 0) { + --this.fuelLevel; + this.brewTime = 400; + this.j = itemstack1.getItem(); + this.update(); + } + + if (!this.world.isClientSide) { + boolean[] aboolean = this.p(); + + if (!Arrays.equals(aboolean, this.i)) { + this.i = aboolean; + IBlockData iblockdata = this.world.getType(this.getPosition()); + + if (!(iblockdata.getBlock() instanceof BlockBrewingStand)) { + return; + } + + for (int i = 0; i < BlockBrewingStand.HAS_BOTTLE.length; ++i) { + iblockdata = (IBlockData) iblockdata.set(BlockBrewingStand.HAS_BOTTLE[i], aboolean[i]); + } + + this.world.setTypeAndData(this.position, iblockdata, 2); + } + } + + } + + public boolean[] p() { + boolean[] aboolean = new boolean[3]; + + for (int i = 0; i < 3; ++i) { + if (!((ItemStack) this.items.get(i)).isEmpty()) { + aboolean[i] = true; + } + } + + return aboolean; + } + + private boolean q() { + ItemStack itemstack = (ItemStack) this.items.get(3); + + if (itemstack.isEmpty()) { + return false; + } else if (!PotionBrewer.a(itemstack)) { + return false; + } else { + for (int i = 0; i < 3; ++i) { + ItemStack itemstack1 = (ItemStack) this.items.get(i); + + if (!itemstack1.isEmpty() && PotionBrewer.a(itemstack1, itemstack)) { + return true; + } + } + + return false; + } + } + + private void r() { + ItemStack itemstack = (ItemStack) this.items.get(3); + // CraftBukkit start + InventoryHolder owner = this.getOwner(); + if (owner != null) { + BrewEvent event = new BrewEvent(world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()), (org.bukkit.inventory.BrewerInventory) owner.getInventory(), this.fuelLevel); + org.bukkit.Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + } + // CraftBukkit end + + for (int i = 0; i < 3; ++i) { + this.items.set(i, PotionBrewer.d(itemstack, (ItemStack) this.items.get(i))); + } + + itemstack.subtract(1); + BlockPosition blockposition = this.getPosition(); + + if (itemstack.getItem().p()) { + ItemStack itemstack1 = new ItemStack(itemstack.getItem().o()); + + if (itemstack.isEmpty()) { + itemstack = itemstack1; + } else { + InventoryUtils.dropItem(this.world, (double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), itemstack1); + } + } + + this.items.set(3, itemstack); + this.world.triggerEffect(1035, blockposition, 0); + } + + public void load(NBTTagCompound nbttagcompound) { + super.load(nbttagcompound); + this.items = NonNullList.a(this.getSize(), ItemStack.a); + ContainerUtil.b(nbttagcompound, this.items); + this.brewTime = nbttagcompound.getShort("BrewTime"); + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.k = MCUtil.getBaseComponentFromNbt("CustomName", nbttagcompound); // Paper - Catch ParseException + } + + this.fuelLevel = nbttagcompound.getByte("Fuel"); + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + nbttagcompound.setShort("BrewTime", (short) this.brewTime); + ContainerUtil.a(nbttagcompound, this.items); + if (this.k != null) { + nbttagcompound.setString("CustomName", IChatBaseComponent.ChatSerializer.a(this.k)); + } + + nbttagcompound.setByte("Fuel", (byte) this.fuelLevel); + return nbttagcompound; + } + + public ItemStack getItem(int i) { + return i >= 0 && i < this.items.size() ? (ItemStack) this.items.get(i) : ItemStack.a; + } + + public ItemStack splitStack(int i, int j) { + return ContainerUtil.a(this.items, i, j); + } + + public ItemStack splitWithoutUpdate(int i) { + return ContainerUtil.a(this.items, i); + } + + public void setItem(int i, ItemStack itemstack) { + if (i >= 0 && i < this.items.size()) { + this.items.set(i, itemstack); + } + + } + + public int getMaxStackSize() { + return this.maxStack; // CraftBukkit + } + + public boolean a(EntityHuman entityhuman) { + return this.world.getTileEntity(this.position) != this ? false : entityhuman.d((double) this.position.getX() + 0.5D, (double) this.position.getY() + 0.5D, (double) this.position.getZ() + 0.5D) <= 64.0D; + } + + public void startOpen(EntityHuman entityhuman) {} + + public void closeContainer(EntityHuman entityhuman) {} + + public boolean b(int i, ItemStack itemstack) { + if (i == 3) { + return PotionBrewer.a(itemstack); + } else { + Item item = itemstack.getItem(); + + return i == 4 ? item == Items.BLAZE_POWDER : (item == Items.POTION || item == Items.SPLASH_POTION || item == Items.LINGERING_POTION || item == Items.GLASS_BOTTLE) && this.getItem(i).isEmpty(); + } + } + + public int[] getSlotsForFace(EnumDirection enumdirection) { + return enumdirection == EnumDirection.UP ? TileEntityBrewingStand.a : (enumdirection == EnumDirection.DOWN ? TileEntityBrewingStand.e : TileEntityBrewingStand.f); + } + + public boolean canPlaceItemThroughFace(int i, ItemStack itemstack, @Nullable EnumDirection enumdirection) { + return this.b(i, itemstack); + } + + public boolean canTakeItemThroughFace(int i, ItemStack itemstack, EnumDirection enumdirection) { + return i == 3 ? itemstack.getItem() == Items.GLASS_BOTTLE : true; + } + + public String getContainerName() { + return "minecraft:brewing_stand"; + } + + public Container createContainer(PlayerInventory playerinventory, EntityHuman entityhuman) { + return new ContainerBrewingStand(playerinventory, this); + } + + public int getProperty(int i) { + switch (i) { + case 0: + return this.brewTime; + case 1: + return this.fuelLevel; + default: + return 0; + } + } + + public void setProperty(int i, int j) { + switch (i) { + case 0: + this.brewTime = j; + break; + case 1: + this.fuelLevel = j; + } + + } + + public int h() { + return 2; + } + + public void clear() { + this.items.clear(); + } +} diff --git a/src/main/java/net/minecraft/server/TileEntityChest.java b/src/main/java/net/minecraft/server/TileEntityChest.java new file mode 100644 index 000000000000..77c012946a37 --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntityChest.java @@ -0,0 +1,311 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class TileEntityChest extends TileEntityLootable { // Paper - Remove ITickable + + private NonNullList items; + protected float a; + protected float e; + protected int f; + private int k; + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public List getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + protected TileEntityChest(TileEntityTypes tileentitytypes) { + super(tileentitytypes); + this.items = NonNullList.a(27, ItemStack.a); + } + + public TileEntityChest() { + this(TileEntityTypes.CHEST); + } + + public int getSize() { + return 27; + } + + public boolean P_() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + public IChatBaseComponent getDisplayName() { + IChatBaseComponent ichatbasecomponent = this.getCustomName(); + + return (IChatBaseComponent) (ichatbasecomponent != null ? ichatbasecomponent : new ChatMessage("container.chest", new Object[0])); + } + + public void load(NBTTagCompound nbttagcompound) { + super.load(nbttagcompound); + this.items = NonNullList.a(this.getSize(), ItemStack.a); + if (!this.d(nbttagcompound)) { + ContainerUtil.b(nbttagcompound, this.items); + } + + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.i = MCUtil.getBaseComponentFromNbt("CustomName", nbttagcompound); // Paper - Catch ParseException + } + + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + if (!this.e(nbttagcompound)) { + ContainerUtil.a(nbttagcompound, this.items); + } + + IChatBaseComponent ichatbasecomponent = this.getCustomName(); + + if (ichatbasecomponent != null) { + nbttagcompound.setString("CustomName", IChatBaseComponent.ChatSerializer.a(ichatbasecomponent)); + } + + return nbttagcompound; + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public void tick() { + int i = this.position.getX(); + int j = this.position.getY(); + int k = this.position.getZ(); + + ++this.k; + // Paper start + } + private void doOpenLogic() { + float f; + int i = this.position.getX(); + int j = this.position.getY(); + int k = this.position.getZ(); + if (false && !this.world.isClientSide && this.f != 0 && (this.k + i + j + k) % 200 == 0) { // Paper - disable block + // Paper end + this.f = 0; + f = 5.0F; + List list = this.world.a(EntityHuman.class, new AxisAlignedBB((double) ((float) i - 5.0F), (double) ((float) j - 5.0F), (double) ((float) k - 5.0F), (double) ((float) (i + 1) + 5.0F), (double) ((float) (j + 1) + 5.0F), (double) ((float) (k + 1) + 5.0F))); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + if (entityhuman.activeContainer instanceof ContainerChest) { + IInventory iinventory = ((ContainerChest) entityhuman.activeContainer).d(); + + if (iinventory == this || iinventory instanceof InventoryLargeChest && ((InventoryLargeChest) iinventory).a((IInventory) this)) { + ++this.f; + } + } + } + } + + if (this.f == 1 && this.a == 0.0F) { // check == 1 instead of > 0, first open + this.a(SoundEffects.BLOCK_CHEST_OPEN); + } + // Paper start + } + private void doCloseLogic() { + this.e = this.a; + // Paper end + + if (this.f == 0/* && this.a > 0.0F || this.f > 0 && this.a < 1.0F*/) { // Paper disable all but player count check + /* // Paper disable animation stuff + float f1 = this.a; + + if (this.f > 0) { + this.a += 0.1F; + } else { + this.a -= 0.1F; + } + + if (this.a > 1.0F) { + this.a = 1.0F; + } + + float f2 = 0.5F; + + + if (this.a < 0.5F && f1 >= 0.5F) { + */ + // add some delay + MCUtil.scheduleTask(10, () -> { + if (this.f == 0) this.a(SoundEffects.BLOCK_CHEST_CLOSE); + }); + // } // Paper end + + if (this.a < 0.0F) { + this.a = 0.0F; + } + } + + } + + private void a(SoundEffect soundeffect) { + BlockPropertyChestType blockpropertychesttype = (BlockPropertyChestType) this.getBlock().get(BlockChest.b); + + if (blockpropertychesttype != BlockPropertyChestType.LEFT) { + double d0 = (double) this.position.getX() + 0.5D; + double d1 = (double) this.position.getY() + 0.5D; + double d2 = (double) this.position.getZ() + 0.5D; + + if (blockpropertychesttype == BlockPropertyChestType.RIGHT) { + EnumDirection enumdirection = BlockChest.k(this.getBlock()); + + d0 += (double) enumdirection.getAdjacentX() * 0.5D; + d2 += (double) enumdirection.getAdjacentZ() * 0.5D; + } + + this.world.a((EntityHuman) null, d0, d1, d2, soundeffect, SoundCategory.BLOCKS, 0.5F, this.world.random.nextFloat() * 0.1F + 0.9F); + } + } + + public boolean c(int i, int j) { + if (i == 1) { + this.f = j; + return true; + } else { + return super.c(i, j); + } + } + + public void startOpen(EntityHuman entityhuman) { + if (!entityhuman.isSpectator()) { + if (this.f < 0) { + this.f = 0; + } + int oldPower = Math.max(0, Math.min(15, this.f)); // CraftBukkit - Get power before new viewer is added + + ++this.f; + if (this.world == null) return; // CraftBukkit + doOpenLogic(); // Paper + + // CraftBukkit start - Call redstone event + if (this.getBlock() == Blocks.TRAPPED_CHEST) { + int newPower = Math.max(0, Math.min(15, this.f)); + + if (oldPower != newPower) { + org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(world, position, oldPower, newPower); + } + } + // CraftBukkit end + this.p(); + } + + } + + public void closeContainer(EntityHuman entityhuman) { + if (!entityhuman.isSpectator()) { + int oldPower = Math.max(0, Math.min(15, this.f)); // CraftBukkit - Get power before new viewer is added + --this.f; + + // CraftBukkit start - Call redstone event + doCloseLogic(); // Paper + if (this.getBlock() == Blocks.TRAPPED_CHEST) { + int newPower = Math.max(0, Math.min(15, this.f)); + + if (oldPower != newPower) { + org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(world, position, oldPower, newPower); + } + } + // CraftBukkit end + this.p(); + } + + } + + protected void p() { + Block block = this.getBlock().getBlock(); + + if (block instanceof BlockChest) { + this.world.playBlockAction(this.position, block, 1, this.f); + this.world.applyPhysics(this.position, block); + } + + } + + public String getContainerName() { + return "minecraft:chest"; + } + + public Container createContainer(PlayerInventory playerinventory, EntityHuman entityhuman) { + this.d(entityhuman); + return new ContainerChest(playerinventory, this, entityhuman); + } + + protected NonNullList q() { + return this.items; + } + + protected void a(NonNullList nonnulllist) { + this.items = nonnulllist; + } + + public static int a(IBlockAccess iblockaccess, BlockPosition blockposition) { + IBlockData iblockdata = iblockaccess.getType(blockposition); + + if (iblockdata.getBlock().isTileEntity()) { + TileEntity tileentity = iblockaccess.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityChest) { + return ((TileEntityChest) tileentity).f; + } + } + + return 0; + } + + public static void a(TileEntityChest tileentitychest, TileEntityChest tileentitychest1) { + NonNullList nonnulllist = tileentitychest.q(); + + tileentitychest.a(tileentitychest1.q()); + tileentitychest1.a(nonnulllist); + } + + // CraftBukkit start + @Override + public boolean isFilteredNBT() { + return false; // Paper + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/TileEntityCommand.java b/src/main/java/net/minecraft/server/TileEntityCommand.java new file mode 100644 index 000000000000..f949781178ff --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntityCommand.java @@ -0,0 +1,159 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class TileEntityCommand extends TileEntity { + + private boolean a; + private boolean e; + private boolean f; + private boolean g; + private final CommandBlockListenerAbstract h = new CommandBlockListenerAbstract() { + // CraftBukkit start + @Override + public org.bukkit.command.CommandSender getBukkitSender(CommandListenerWrapper wrapper) { + return new org.bukkit.craftbukkit.command.CraftBlockCommandSender(wrapper, TileEntityCommand.this); + } + // CraftBukkit end + + public void setCommand(String s) { + super.setCommand(s); + TileEntityCommand.this.update(); + } + + public WorldServer d() { + return (WorldServer) TileEntityCommand.this.world; + } + + public void e() { + IBlockData iblockdata = TileEntityCommand.this.world.getType(TileEntityCommand.this.position); + + this.d().notify(TileEntityCommand.this.position, iblockdata, iblockdata, 3); + } + + public CommandListenerWrapper getWrapper() { + return new CommandListenerWrapper(this, new Vec3D((double) TileEntityCommand.this.position.getX() + 0.5D, (double) TileEntityCommand.this.position.getY() + 0.5D, (double) TileEntityCommand.this.position.getZ() + 0.5D), Vec2F.a, this.d(), 2, this.getName().getString(), this.getName(), this.d().getMinecraftServer(), (Entity) null); + } + }; + + public TileEntityCommand() { + super(TileEntityTypes.COMMAND_BLOCK); + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + this.h.a(nbttagcompound); + nbttagcompound.setBoolean("powered", this.d()); + nbttagcompound.setBoolean("conditionMet", this.f()); + nbttagcompound.setBoolean("auto", this.e()); + return nbttagcompound; + } + + public void load(NBTTagCompound nbttagcompound) { + super.load(nbttagcompound); + this.h.b(nbttagcompound); + this.a = nbttagcompound.getBoolean("powered"); + this.f = nbttagcompound.getBoolean("conditionMet"); + this.b(nbttagcompound.getBoolean("auto")); + } + + @Nullable + public PacketPlayOutTileEntityData getUpdatePacket() { + if (this.i()) { + this.c(false); + NBTTagCompound nbttagcompound = this.save(new NBTTagCompound()); + + return new PacketPlayOutTileEntityData(this.position, 2, nbttagcompound); + } else { + return null; + } + } + + public boolean isFilteredNBT() { + return true; + } + + public CommandBlockListenerAbstract getCommandBlock() { + return this.h; + } + + public void a(boolean flag) { + this.a = flag; + } + + public boolean d() { + return this.a; + } + + public boolean e() { + return this.e; + } + + public void b(boolean flag) { + boolean flag1 = this.e; + + this.e = flag; + if (!flag1 && flag && !this.a && this.world != null && this.j() != TileEntityCommand.Type.SEQUENCE) { + Block block = this.getBlock().getBlock(); + + if (block instanceof BlockCommand) { + this.h(); + this.world.getBlockTickList().a(this.position, block, block.a((IWorldReader) this.world)); + } + } + + } + + public boolean f() { + return this.f; + } + + public boolean h() { + this.f = true; + if (this.k()) { + BlockPosition blockposition = this.position.shift(((EnumDirection) this.world.getType(this.position).get(BlockCommand.a)).opposite()); + + if (this.world.getType(blockposition).getBlock() instanceof BlockCommand) { + TileEntity tileentity = this.world.getTileEntity(blockposition); + + this.f = tileentity instanceof TileEntityCommand && ((TileEntityCommand) tileentity).getCommandBlock().i() > 0; + } else { + this.f = false; + } + } + + return this.f; + } + + public boolean i() { + return this.g; + } + + public void c(boolean flag) { + this.g = flag; + } + + public TileEntityCommand.Type j() { + Block block = this.getBlock().getBlock(); + + return block == Blocks.COMMAND_BLOCK ? TileEntityCommand.Type.REDSTONE : (block == Blocks.REPEATING_COMMAND_BLOCK ? TileEntityCommand.Type.AUTO : (block == Blocks.CHAIN_COMMAND_BLOCK ? TileEntityCommand.Type.SEQUENCE : TileEntityCommand.Type.REDSTONE)); + } + + public boolean k() { + IBlockData iblockdata = this.world.getType(this.getPosition()); + + return iblockdata.getBlock() instanceof BlockCommand ? (Boolean) iblockdata.get(BlockCommand.b) : false; + } + + public void z() { + this.invalidateBlockCache(); + super.z(); + } + + public static enum Type { + + SEQUENCE, AUTO, REDSTONE; + + private Type() {} + } +} diff --git a/src/main/java/net/minecraft/server/TileEntityConduit.java b/src/main/java/net/minecraft/server/TileEntityConduit.java new file mode 100644 index 000000000000..1f9ee125cd0e --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntityConduit.java @@ -0,0 +1,290 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.UUID; +import javax.annotation.Nullable; +// CraftBukkit start +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.event.CraftEventFactory; +// CraftBukkit end + +public class TileEntityConduit extends TileEntity implements ITickable { + + private static final Block[] e = new Block[] { Blocks.PRISMARINE, Blocks.PRISMARINE_BRICKS, Blocks.SEA_LANTERN, Blocks.DARK_PRISMARINE}; + public int a; + private float f; + private boolean g; + private boolean h; + private final List i; + private EntityLiving target; + private UUID k; + private long l; + + public TileEntityConduit() { + this(TileEntityTypes.CONDUIT); + } + + public TileEntityConduit(TileEntityTypes tileentitytypes) { + super(tileentitytypes); + this.i = Lists.newArrayList(); + } + + public void load(NBTTagCompound nbttagcompound) { + super.load(nbttagcompound); + if (nbttagcompound.hasKey("target_uuid")) { + this.k = GameProfileSerializer.b(nbttagcompound.getCompound("target_uuid")); + } else { + this.k = null; + } + + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + if (this.target != null) { + nbttagcompound.set("target_uuid", GameProfileSerializer.a(this.target.getUniqueID())); + } + + return nbttagcompound; + } + + @Nullable + public PacketPlayOutTileEntityData getUpdatePacket() { + return new PacketPlayOutTileEntityData(this.position, 5, this.aa_()); + } + + public NBTTagCompound aa_() { + return this.save(new NBTTagCompound()); + } + + public void tick() { + ++this.a; + long i = this.world.getTime(); + + if (i % 40L == 0L) { + this.a(this.f()); + if (!this.world.isClientSide && this.c()) { + this.h(); + this.i(); + } + } + + if (i % 80L == 0L && this.c()) { + this.a(SoundEffects.BLOCK_CONDUIT_AMBIENT); + } + + if (i > this.l && this.c()) { + this.l = i + 60L + (long) this.world.m().nextInt(40); + this.a(SoundEffects.BLOCK_CONDUIT_AMBIENT_SHORT); + } + + if (this.world.isClientSide) { + this.j(); + this.m(); + if (this.c()) { + ++this.f; + } + } + + } + + private boolean f() { + this.i.clear(); + + int i; + int j; + int k; + + for (i = -1; i <= 1; ++i) { + for (j = -1; j <= 1; ++j) { + for (k = -1; k <= 1; ++k) { + BlockPosition blockposition = this.position.a(i, j, k); + + if (!this.world.B(blockposition)) { + return false; + } + } + } + } + + for (i = -2; i <= 2; ++i) { + for (j = -2; j <= 2; ++j) { + for (k = -2; k <= 2; ++k) { + int l = Math.abs(i); + int i1 = Math.abs(j); + int j1 = Math.abs(k); + + if ((l > 1 || i1 > 1 || j1 > 1) && (i == 0 && (i1 == 2 || j1 == 2) || j == 0 && (l == 2 || j1 == 2) || k == 0 && (l == 2 || i1 == 2))) { + BlockPosition blockposition1 = this.position.a(i, j, k); + IBlockData iblockdata = this.world.getType(blockposition1); + Block[] ablock = TileEntityConduit.e; + int k1 = ablock.length; + + for (int l1 = 0; l1 < k1; ++l1) { + Block block = ablock[l1]; + + if (iblockdata.getBlock() == block) { + this.i.add(blockposition1); + } + } + } + } + } + } + + this.b(this.i.size() >= 42); + return this.i.size() >= 16; + } + + private void h() { + int i = this.i.size(); + int j = i / 7 * 16; + int k = this.position.getX(); + int l = this.position.getY(); + int i1 = this.position.getZ(); + AxisAlignedBB axisalignedbb = (new AxisAlignedBB((double) k, (double) l, (double) i1, (double) (k + 1), (double) (l + 1), (double) (i1 + 1))).g((double) j).b(0.0D, (double) this.world.getHeight(), 0.0D); + List list = this.world.a(EntityHuman.class, axisalignedbb); + + if (!list.isEmpty()) { + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + if (this.position.m(new BlockPosition(entityhuman)) <= (double) j && entityhuman.ao()) { + entityhuman.addEffect(new MobEffect(MobEffects.CONDUIT_POWER, 260, 0, true, true), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONDUIT); // CraftBukkit + } + } + + } + } + + private void i() { + EntityLiving entityliving = this.target; + int i = this.i.size(); + + if (i < 42) { + this.target = null; + } else if (this.target == null && this.k != null) { + this.target = this.l(); + this.k = null; + } else if (this.target == null) { + List list = this.world.a(EntityLiving.class, this.k(), (java.util.function.Predicate) (entityliving1) -> { // CraftBukkit - decompile error + return entityliving1 instanceof IMonster && entityliving1.ao(); + }); + + if (!list.isEmpty()) { + this.target = (EntityLiving) list.get(this.world.random.nextInt(list.size())); + } + } else if (!this.target.isAlive() || this.position.m(new BlockPosition(this.target)) > 8.0D) { + this.target = null; + } + + if (this.target != null) { + // CraftBukkit start + CraftEventFactory.blockDamage = CraftBlock.at(this.world, this.position); + if (this.target.damageEntity(DamageSource.MAGIC, 4.0F)) { + this.world.a((EntityHuman) null, this.target.locX, this.target.locY, this.target.locZ, SoundEffects.BLOCK_CONDUIT_ATTACK_TARGET, SoundCategory.BLOCKS, 1.0F, 1.0F); + } + CraftEventFactory.blockDamage = null; + // CraftBukkit end + } + + if (entityliving != this.target) { + IBlockData iblockdata = this.getBlock(); + + this.world.notify(this.position, iblockdata, iblockdata, 2); + } + + } + + private void j() { + if (this.k == null) { + this.target = null; + } else if (this.target == null || !this.target.getUniqueID().equals(this.k)) { + this.target = this.l(); + if (this.target == null) { + this.k = null; + } + } + + } + + private AxisAlignedBB k() { + int i = this.position.getX(); + int j = this.position.getY(); + int k = this.position.getZ(); + + return (new AxisAlignedBB((double) i, (double) j, (double) k, (double) (i + 1), (double) (j + 1), (double) (k + 1))).g(8.0D); + } + + @Nullable + private EntityLiving l() { + List list = this.world.a(EntityLiving.class, this.k(), (java.util.function.Predicate) (entityliving) -> { // CraftBukkit - decompile error + return entityliving.getUniqueID().equals(this.k); + }); + + return list.size() == 1 ? (EntityLiving) list.get(0) : null; + } + + private void m() { + Random random = this.world.random; + float f = MathHelper.sin((float) (this.a + 35) * 0.1F) / 2.0F + 0.5F; + + f = (f * f + f) * 0.3F; + Vec3D vec3d = new Vec3D((double) ((float) this.position.getX() + 0.5F), (double) ((float) this.position.getY() + 1.5F + f), (double) ((float) this.position.getZ() + 0.5F)); + Iterator iterator = this.i.iterator(); + + float f1; + float f2; + + while (iterator.hasNext()) { + BlockPosition blockposition = (BlockPosition) iterator.next(); + + if (random.nextInt(50) == 0) { + f1 = -0.5F + random.nextFloat(); + f2 = -2.0F + random.nextFloat(); + float f3 = -0.5F + random.nextFloat(); + BlockPosition blockposition1 = blockposition.b(this.position); + Vec3D vec3d1 = (new Vec3D((double) f1, (double) f2, (double) f3)).add((double) blockposition1.getX(), (double) blockposition1.getY(), (double) blockposition1.getZ()); + + this.world.addParticle(Particles.W, vec3d.x, vec3d.y, vec3d.z, vec3d1.x, vec3d1.y, vec3d1.z); + } + } + + if (this.target != null) { + Vec3D vec3d2 = new Vec3D(this.target.locX, this.target.locY + (double) this.target.getHeadHeight(), this.target.locZ); + float f4 = (-0.5F + random.nextFloat()) * (3.0F + this.target.width); + + f1 = -1.0F + random.nextFloat() * this.target.length; + f2 = (-0.5F + random.nextFloat()) * (3.0F + this.target.width); + Vec3D vec3d3 = new Vec3D((double) f4, (double) f1, (double) f2); + + this.world.addParticle(Particles.W, vec3d2.x, vec3d2.y, vec3d2.z, vec3d3.x, vec3d3.y, vec3d3.z); + } + + } + + public boolean c() { + return this.g; + } + + private void a(boolean flag) { + if (flag != this.g) { + this.a(flag ? SoundEffects.BLOCK_CONDUIT_ACTIVATE : SoundEffects.BLOCK_CONDUIT_DEACTIVATE); + } + + this.g = flag; + } + + private void b(boolean flag) { + this.h = flag; + } + + public void a(SoundEffect soundeffect) { + this.world.a((EntityHuman) null, this.position, soundeffect, SoundCategory.BLOCKS, 1.0F, 1.0F); + } +} diff --git a/src/main/java/net/minecraft/server/TileEntityContainer.java b/src/main/java/net/minecraft/server/TileEntityContainer.java new file mode 100644 index 000000000000..cff9f1a249b4 --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntityContainer.java @@ -0,0 +1,45 @@ +package net.minecraft.server; + +public abstract class TileEntityContainer extends TileEntity implements ITileInventory { + + private ChestLock a; + + protected TileEntityContainer(TileEntityTypes tileentitytypes) { + super(tileentitytypes); + this.a = ChestLock.a; + } + + public void load(NBTTagCompound nbttagcompound) { + super.load(nbttagcompound); + this.a = ChestLock.b(nbttagcompound); + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + if (this.a != null) { + this.a.a(nbttagcompound); + } + + return nbttagcompound; + } + + public boolean isLocked() { + return this.a != null && !this.a.a(); + } + + public ChestLock getLock() { + return this.a; + } + + public void setLock(ChestLock chestlock) { + this.a = chestlock; + } + + // CraftBukkit start + @Override + public org.bukkit.Location getLocation() { + if (world == null) return null; + return new org.bukkit.Location(world.getWorld(), position.getX(), position.getY(), position.getZ()); + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/TileEntityDispenser.java b/src/main/java/net/minecraft/server/TileEntityDispenser.java new file mode 100644 index 000000000000..21bd156e91c4 --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntityDispenser.java @@ -0,0 +1,150 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.Random; +// CraftBukkit start +import java.util.List; + +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class TileEntityDispenser extends TileEntityLootable { + + private static final Random a = new Random(); + private NonNullList items; + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public List getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + protected TileEntityDispenser(TileEntityTypes tileentitytypes) { + super(tileentitytypes); + this.items = NonNullList.a(9, ItemStack.a); + } + + public TileEntityDispenser() { + this(TileEntityTypes.DISPENSER); + } + + public int getSize() { + return 9; + } + + public boolean P_() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + public int p() { + this.d((EntityHuman) null); + int i = -1; + int j = 1; + + for (int k = 0; k < this.items.size(); ++k) { + if (!((ItemStack) this.items.get(k)).isEmpty() && TileEntityDispenser.a.nextInt(j++) == 0) { + i = k; + } + } + + return i; + } + + public int addItem(ItemStack itemstack) { + for (int i = 0; i < this.items.size(); ++i) { + if (((ItemStack) this.items.get(i)).isEmpty()) { + this.setItem(i, itemstack); + return i; + } + } + + return -1; + } + + public IChatBaseComponent getDisplayName() { + IChatBaseComponent ichatbasecomponent = this.getCustomName(); + + return (IChatBaseComponent) (ichatbasecomponent != null ? ichatbasecomponent : new ChatMessage("container.dispenser", new Object[0])); + } + + public void load(NBTTagCompound nbttagcompound) { + super.load(nbttagcompound); + this.items = NonNullList.a(this.getSize(), ItemStack.a); + if (!this.d(nbttagcompound)) { + ContainerUtil.b(nbttagcompound, this.items); + } + + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.i = MCUtil.getBaseComponentFromNbt("CustomName", nbttagcompound); // Paper - Catch ParseException + } + + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + if (!this.e(nbttagcompound)) { + ContainerUtil.a(nbttagcompound, this.items); + } + + IChatBaseComponent ichatbasecomponent = this.getCustomName(); + + if (ichatbasecomponent != null) { + nbttagcompound.setString("CustomName", IChatBaseComponent.ChatSerializer.a(ichatbasecomponent)); + } + + return nbttagcompound; + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public String getContainerName() { + return "minecraft:dispenser"; + } + + public Container createContainer(PlayerInventory playerinventory, EntityHuman entityhuman) { + this.d(entityhuman); + return new ContainerDispenser(playerinventory, this); + } + + protected NonNullList q() { + return this.items; + } + + protected void a(NonNullList nonnulllist) { + this.items = nonnulllist; + } +} diff --git a/src/main/java/net/minecraft/server/TileEntityEndGateway.java b/src/main/java/net/minecraft/server/TileEntityEndGateway.java new file mode 100644 index 000000000000..2af225021bbb --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntityEndGateway.java @@ -0,0 +1,271 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +// CraftBukkit start +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.event.player.PlayerTeleportEvent; +// CraftBukkit end + +public class TileEntityEndGateway extends TileEntityEnderPortal implements ITickable { + + private static final Logger a = LogManager.getLogger(); + public long age; + private int f; + public BlockPosition exitPortal; + public boolean exactTeleport; + + public TileEntityEndGateway() { + super(TileEntityTypes.END_GATEWAY); + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + nbttagcompound.setLong("Age", this.age); + if (this.exitPortal != null) { + nbttagcompound.set("ExitPortal", GameProfileSerializer.a(this.exitPortal)); + } + + if (this.exactTeleport) { + nbttagcompound.setBoolean("ExactTeleport", this.exactTeleport); + } + + return nbttagcompound; + } + + public void load(NBTTagCompound nbttagcompound) { + super.load(nbttagcompound); + this.age = nbttagcompound.getLong("Age"); + if (nbttagcompound.hasKeyOfType("ExitPortal", 10)) { + this.exitPortal = GameProfileSerializer.c(nbttagcompound.getCompound("ExitPortal")); + } + + this.exactTeleport = nbttagcompound.getBoolean("ExactTeleport"); + } + + public void tick() { + boolean flag = this.c(); + boolean flag1 = this.d(); + + ++this.age; + if (flag1) { + --this.f; + } else if (!this.world.isClientSide) { + List list = this.world.a(Entity.class, new AxisAlignedBB(this.getPosition())); + + if (!list.isEmpty()) { + this.a((Entity) list.get(0)); + } + + if (this.age % 2400L == 0L) { + this.f(); + } + } + + if (flag != this.c() || flag1 != this.d()) { + this.update(); + } + + } + + public boolean c() { + return this.age < 200L; + } + + public boolean d() { + return this.f > 0; + } + + @Nullable + public PacketPlayOutTileEntityData getUpdatePacket() { + return new PacketPlayOutTileEntityData(this.position, 8, this.aa_()); + } + + public NBTTagCompound aa_() { + return this.save(new NBTTagCompound()); + } + + public void f() { + if (!this.world.isClientSide) { + this.f = 40; + this.world.playBlockAction(this.getPosition(), this.getBlock().getBlock(), 1, 0); + this.update(); + } + + } + + public boolean c(int i, int j) { + if (i == 1) { + this.f = 40; + return true; + } else { + return super.c(i, j); + } + } + + public void a(Entity entity) { + if (!this.world.isClientSide && !this.d()) { + this.f = 100; + if (this.exitPortal == null && this.world.worldProvider instanceof WorldProviderTheEnd) { + this.j(); + } + + if (this.exitPortal != null) { + BlockPosition blockposition = this.exactTeleport ? this.exitPortal : this.i(); + + // CraftBukkit start - Fire PlayerTeleportEvent + if (entity instanceof EntityPlayer) { + org.bukkit.craftbukkit.entity.CraftPlayer player = (CraftPlayer) entity.getBukkitEntity(); + org.bukkit.Location location = new Location(world.getWorld(), (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D); + location.setPitch(player.getLocation().getPitch()); + location.setYaw(player.getLocation().getYaw()); + + PlayerTeleportEvent teleEvent = new com.destroystokyo.paper.event.player.PlayerTeleportEndGatewayEvent(player, player.getLocation(), location, new org.bukkit.craftbukkit.block.CraftEndGateway(MCUtil.toLocation(world, this.getPosition()).getBlock())); // Paper + Bukkit.getPluginManager().callEvent(teleEvent); + if (teleEvent.isCancelled()) { + return; + } + + ((EntityPlayer) entity).playerConnection.teleport(teleEvent.getTo()); + this.f(); // CraftBukkit - call at end of method + return; + + } + // CraftBukkit end + // Paper start - EntityTeleportEndGatewayEvent - replicated from above + org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity = entity.getBukkitEntity(); + org.bukkit.Location location = new Location(world.getWorld(), (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D); + location.setPitch(bukkitEntity.getLocation().getPitch()); + location.setYaw(bukkitEntity.getLocation().getYaw()); + + com.destroystokyo.paper.event.entity.EntityTeleportEndGatewayEvent event = new com.destroystokyo.paper.event.entity.EntityTeleportEndGatewayEvent(bukkitEntity, bukkitEntity.getLocation(), location, new org.bukkit.craftbukkit.block.CraftEndGateway(MCUtil.toLocation(world, this.getPosition()).getBlock())); + if (!event.callEvent()) { + return; + } + + entity.enderTeleportTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ()); + // Paper end - EntityTeleportEndGatewayEvent + } + + this.f(); + } + } + + private BlockPosition i() { + BlockPosition blockposition = a(this.world, this.exitPortal, 5, false); + + TileEntityEndGateway.a.debug("Best exit position for portal at {} is {}", this.exitPortal, blockposition); + return blockposition.up(); + } + + private void j() { + Vec3D vec3d = (new Vec3D((double) this.getPosition().getX(), 0.0D, (double) this.getPosition().getZ())).a(); + Vec3D vec3d1 = vec3d.a(1024.0D); + + int i; + + for (i = 16; a(this.world, vec3d1).b() > 0 && i-- > 0; vec3d1 = vec3d1.e(vec3d.a(-16.0D))) { + TileEntityEndGateway.a.debug("Skipping backwards past nonempty chunk at {}", vec3d1); + } + + for (i = 16; a(this.world, vec3d1).b() == 0 && i-- > 0; vec3d1 = vec3d1.e(vec3d.a(16.0D))) { + TileEntityEndGateway.a.debug("Skipping forward past empty chunk at {}", vec3d1); + } + + TileEntityEndGateway.a.debug("Found chunk at {}", vec3d1); + Chunk chunk = a(this.world, vec3d1); + + this.exitPortal = a(chunk); + if (this.exitPortal == null) { + this.exitPortal = new BlockPosition(vec3d1.x + 0.5D, 75.0D, vec3d1.z + 0.5D); + TileEntityEndGateway.a.debug("Failed to find suitable block, settling on {}", this.exitPortal); + (new WorldGenEndIsland()).a(this.world, this.world.getChunkProvider().getChunkGenerator(), new Random(this.exitPortal.asLong()), this.exitPortal, WorldGenFeatureConfiguration.e); + } else { + TileEntityEndGateway.a.debug("Found block at {}", this.exitPortal); + } + + this.exitPortal = a(this.world, this.exitPortal, 16, true); + TileEntityEndGateway.a.debug("Creating portal at {}", this.exitPortal); + this.exitPortal = this.exitPortal.up(10); + this.c(this.exitPortal); + this.update(); + } + + private static BlockPosition a(IBlockAccess iblockaccess, BlockPosition blockposition, int i, boolean flag) { + BlockPosition blockposition1 = null; + + for (int j = -i; j <= i; ++j) { + for (int k = -i; k <= i; ++k) { + if (j != 0 || k != 0 || flag) { + for (int l = 255; l > (blockposition1 == null ? 0 : blockposition1.getY()); --l) { + BlockPosition blockposition2 = new BlockPosition(blockposition.getX() + j, l, blockposition.getZ() + k); + IBlockData iblockdata = iblockaccess.getType(blockposition2); + + if (iblockdata.k() && (flag || iblockdata.getBlock() != Blocks.BEDROCK)) { + blockposition1 = blockposition2; + break; + } + } + } + } + } + + return blockposition1 == null ? blockposition : blockposition1; + } + + private static Chunk a(World world, Vec3D vec3d) { + return world.getChunkAt(MathHelper.floor(vec3d.x / 16.0D), MathHelper.floor(vec3d.z / 16.0D)); + } + + @Nullable + private static BlockPosition a(Chunk chunk) { + BlockPosition blockposition = new BlockPosition(chunk.locX * 16, 30, chunk.locZ * 16); + int i = chunk.b() + 16 - 1; + BlockPosition blockposition1 = new BlockPosition(chunk.locX * 16 + 16 - 1, i, chunk.locZ * 16 + 16 - 1); + BlockPosition blockposition2 = null; + double d0 = 0.0D; + Iterator iterator = BlockPosition.a(blockposition, blockposition1).iterator(); + + while (iterator.hasNext()) { + BlockPosition blockposition3 = (BlockPosition) iterator.next(); + IBlockData iblockdata = chunk.getType(blockposition3); + + if (iblockdata.getBlock() == Blocks.END_STONE && !chunk.getType(blockposition3.up(1)).k() && !chunk.getType(blockposition3.up(2)).k()) { + double d1 = blockposition3.g(0.0D, 0.0D, 0.0D); + + if (blockposition2 == null || d1 < d0) { + blockposition2 = blockposition3; + d0 = d1; + } + } + } + + return blockposition2; + } + + private void c(BlockPosition blockposition) { + WorldGenerator.ax.generate(this.world, this.world.getChunkProvider().getChunkGenerator(), new Random(), blockposition, new WorldGenEndGatewayConfiguration(false)); + TileEntity tileentity = this.world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityEndGateway) { + TileEntityEndGateway tileentityendgateway = (TileEntityEndGateway) tileentity; + + tileentityendgateway.exitPortal = new BlockPosition(this.getPosition()); + tileentityendgateway.update(); + } else { + TileEntityEndGateway.a.warn("Couldn't save exit portal at {}", blockposition); + } + + } + + public void b(BlockPosition blockposition) { + this.exactTeleport = true; + this.exitPortal = blockposition; + } +} diff --git a/src/main/java/net/minecraft/server/TileEntityEnderChest.java b/src/main/java/net/minecraft/server/TileEntityEnderChest.java new file mode 100644 index 000000000000..f2df6f3950fb --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntityEnderChest.java @@ -0,0 +1,109 @@ +package net.minecraft.server; + +public class TileEntityEnderChest extends TileEntity { // Paper - Remove ITickable + + public float a; + public float e; + public int f; + private int g; + + public TileEntityEnderChest() { + super(TileEntityTypes.ENDER_CHEST); + } + + public void tick() { + if (++this.g % 20 * 4 == 0) { + this.world.playBlockAction(this.position, Blocks.ENDER_CHEST, 1, this.f); + } + + this.e = this.a; + // Paper start + /* + int i = this.position.getX(); + int j = this.position.getY(); + int k = this.position.getZ(); + float f = 0.1F; + double d0; + + */ + // Paper start + } + private void doOpenLogic() { + int i = this.position.getX(); + int j = this.position.getY(); + int k = this.position.getZ(); + // Paper end + if (this.f > 0 && this.a == 0.0F) { + double d1 = (double) i + 0.5D; + + double d0 = (double) k + 0.5D; // Paper + this.world.a((EntityHuman) null, d1, (double) j + 0.5D, d0, SoundEffects.BLOCK_ENDER_CHEST_OPEN, SoundCategory.BLOCKS, 0.5F, this.world.random.nextFloat() * 0.1F + 0.9F); + } + // Paper start + } + private void doCloseLogic() { + int i = this.position.getX(); + int j = this.position.getY(); + int k = this.position.getZ(); + this.e = this.a; + double d0; + // Paper end + if (this.f == 0 && this.a > 0.0F || this.f > 0 && this.a < 1.0F) { + float f1 = this.a; + + if (this.f > 0) { + this.a += 0.1F; + } else { + this.a -= 0.1F; + } + + if (this.a > 1.0F) { + this.a = 1.0F; + } + + float f2 = 0.5F; + + if (this.a < 0.5F && f1 >= 0.5F) { + d0 = (double) i + 0.5D; + double d2 = (double) k + 0.5D; + + this.world.a((EntityHuman) null, d0, (double) j + 0.5D, d2, SoundEffects.BLOCK_ENDER_CHEST_CLOSE, SoundCategory.BLOCKS, 0.5F, this.world.random.nextFloat() * 0.1F + 0.9F); + } + + if (this.a < 0.0F) { + this.a = 0.0F; + } + } + + } + + public boolean c(int i, int j) { + if (i == 1) { + this.f = j; + return true; + } else { + return super.c(i, j); + } + } + + public void y() { + this.invalidateBlockCache(); + super.y(); + } + + public void c() { + ++this.f; + this.world.playBlockAction(this.position, Blocks.ENDER_CHEST, 1, this.f); + doOpenLogic(); // Paper + } + + public void d() { + --this.f; + this.world.playBlockAction(this.position, Blocks.ENDER_CHEST, 1, this.f); + doCloseLogic(); // Paper + } + + public boolean a(EntityHuman entityhuman) { + return this.world.getTileEntity(this.position) != this ? false : entityhuman.d((double) this.position.getX() + 0.5D, (double) this.position.getY() + 0.5D, (double) this.position.getZ() + 0.5D) <= 64.0D; + } +} diff --git a/src/main/java/net/minecraft/server/TileEntityFurnace.java b/src/main/java/net/minecraft/server/TileEntityFurnace.java new file mode 100644 index 000000000000..538ca9bbb483 --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntityFurnace.java @@ -0,0 +1,566 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import javax.annotation.Nullable; +// CraftBukkit start +import java.util.List; + +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.FurnaceBurnEvent; +import org.bukkit.event.inventory.FurnaceSmeltEvent; +// CraftBukkit end + +public class TileEntityFurnace extends TileEntityContainer implements IWorldInventory, RecipeHolder, AutoRecipeOutput, ITickable { + + private static final int[] a = new int[] { 0}; + private static final int[] e = new int[] { 2, 1}; + private static final int[] f = new int[] { 1}; + private NonNullList items; + private int burnTime; + private int ticksForCurrentFuel; + public double cookSpeedMultiplier = 1.0; // Paper - cook speed multiplier API + private int cookTime; + private int cookTimeTotal; + private IChatBaseComponent l; + private final Map m; + + private static void a(Map map, Tag tag, int i) { + Iterator iterator = tag.a().iterator(); + + while (iterator.hasNext()) { + Item item = (Item) iterator.next(); + + map.put(item, i); + } + + } + + private static void a(Map map, IMaterial imaterial, int i) { + map.put(imaterial.getItem(), i); + } + + public static Map p() { + Map map = Maps.newLinkedHashMap(); + + a(map, (IMaterial) Items.LAVA_BUCKET, 20000); + a(map, (IMaterial) Blocks.COAL_BLOCK, 16000); + a(map, (IMaterial) Items.BLAZE_ROD, 2400); + a(map, (IMaterial) Items.COAL, 1600); + a(map, (IMaterial) Items.CHARCOAL, 1600); + a(map, TagsItem.LOGS, 300); + a(map, TagsItem.PLANKS, 300); + a(map, TagsItem.WOODEN_STAIRS, 300); + a(map, TagsItem.WOODEN_SLABS, 150); + a(map, TagsItem.WOODEN_TRAPDOORS, 300); + a(map, TagsItem.WOODEN_PRESSURE_PLATES, 300); + a(map, (IMaterial) Blocks.OAK_FENCE, 300); + a(map, (IMaterial) Blocks.BIRCH_FENCE, 300); + a(map, (IMaterial) Blocks.SPRUCE_FENCE, 300); + a(map, (IMaterial) Blocks.JUNGLE_FENCE, 300); + a(map, (IMaterial) Blocks.DARK_OAK_FENCE, 300); + a(map, (IMaterial) Blocks.ACACIA_FENCE, 300); + a(map, (IMaterial) Blocks.OAK_FENCE_GATE, 300); + a(map, (IMaterial) Blocks.BIRCH_FENCE_GATE, 300); + a(map, (IMaterial) Blocks.SPRUCE_FENCE_GATE, 300); + a(map, (IMaterial) Blocks.JUNGLE_FENCE_GATE, 300); + a(map, (IMaterial) Blocks.DARK_OAK_FENCE_GATE, 300); + a(map, (IMaterial) Blocks.ACACIA_FENCE_GATE, 300); + a(map, (IMaterial) Blocks.NOTE_BLOCK, 300); + a(map, (IMaterial) Blocks.BOOKSHELF, 300); + a(map, (IMaterial) Blocks.JUKEBOX, 300); + a(map, (IMaterial) Blocks.CHEST, 300); + a(map, (IMaterial) Blocks.TRAPPED_CHEST, 300); + a(map, (IMaterial) Blocks.CRAFTING_TABLE, 300); + a(map, (IMaterial) Blocks.DAYLIGHT_DETECTOR, 300); + a(map, TagsItem.BANNERS, 300); + a(map, (IMaterial) Items.BOW, 300); + a(map, (IMaterial) Items.FISHING_ROD, 300); + a(map, (IMaterial) Blocks.LADDER, 300); + a(map, (IMaterial) Items.SIGN, 200); + a(map, (IMaterial) Items.WOODEN_SHOVEL, 200); + a(map, (IMaterial) Items.WOODEN_SWORD, 200); + a(map, (IMaterial) Items.WOODEN_HOE, 200); + a(map, (IMaterial) Items.WOODEN_AXE, 200); + a(map, (IMaterial) Items.WOODEN_PICKAXE, 200); + a(map, TagsItem.WOODEN_DOORS, 200); + a(map, TagsItem.BOATS, 200); + a(map, TagsItem.WOOL, 100); + a(map, TagsItem.WOODEN_BUTTONS, 100); + a(map, (IMaterial) Items.STICK, 100); + a(map, TagsItem.SAPLINGS, 100); + a(map, (IMaterial) Items.BOWL, 100); + a(map, TagsItem.CARPETS, 67); + a(map, (IMaterial) Blocks.DRIED_KELP_BLOCK, 4001); + return map; + } + + // CraftBukkit start - add fields and methods + private int maxStack = MAX_STACK; + public List transaction = new java.util.ArrayList(); + + public List getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + public TileEntityFurnace() { + super(TileEntityTypes.FURNACE); + this.items = NonNullList.a(3, ItemStack.a); + this.m = Maps.newHashMap(); + } + + public int getSize() { + return this.items.size(); + } + + public boolean P_() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + public ItemStack getItem(int i) { + return (ItemStack) this.items.get(i); + } + + public ItemStack splitStack(int i, int j) { + return ContainerUtil.a(this.items, i, j); + } + + public ItemStack splitWithoutUpdate(int i) { + return ContainerUtil.a(this.items, i); + } + + public void setItem(int i, ItemStack itemstack) { + ItemStack itemstack1 = (ItemStack) this.items.get(i); + boolean flag = !itemstack.isEmpty() && itemstack.doMaterialsMatch(itemstack1) && ItemStack.equals(itemstack, itemstack1); + + this.items.set(i, itemstack); + if (itemstack.getCount() > this.getMaxStackSize()) { + itemstack.setCount(this.getMaxStackSize()); + } + + if (i == 0 && !flag) { + this.cookTimeTotal = this.s(); + this.cookTime = 0; + this.update(); + } + + } + + public IChatBaseComponent getDisplayName() { + return (IChatBaseComponent) (this.l != null ? this.l : new ChatMessage("container.furnace", new Object[0])); + } + + public boolean hasCustomName() { + return this.l != null; + } + + @Nullable + public IChatBaseComponent getCustomName() { + return this.l; + } + + public void setCustomName(@Nullable IChatBaseComponent ichatbasecomponent) { + this.l = ichatbasecomponent; + } + + public void load(NBTTagCompound nbttagcompound) { + super.load(nbttagcompound); + this.items = NonNullList.a(this.getSize(), ItemStack.a); + ContainerUtil.b(nbttagcompound, this.items); + this.burnTime = nbttagcompound.getShort("BurnTime"); + this.cookTime = nbttagcompound.getShort("CookTime"); + this.cookTimeTotal = nbttagcompound.getShort("CookTimeTotal"); + this.ticksForCurrentFuel = fuelTime((ItemStack) this.items.get(1)); + short short0 = nbttagcompound.getShort("RecipesUsedSize"); + + for (int i = 0; i < short0; ++i) { + MinecraftKey minecraftkey = new MinecraftKey(nbttagcompound.getString("RecipeLocation" + i)); + int j = nbttagcompound.getInt("RecipeAmount" + i); + + this.m.put(minecraftkey, j); + } + + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.l = MCUtil.getBaseComponentFromNbt("CustomName", nbttagcompound); // Paper - Catch ParseException + } + + // Paper start - cook speed API + if (nbttagcompound.hasKey("Paper.CookSpeedMultiplier")) { + this.cookSpeedMultiplier = nbttagcompound.getDouble("Paper.CookSpeedMultiplier"); + } + // Paper end + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + nbttagcompound.setShort("BurnTime", (short) this.burnTime); + nbttagcompound.setShort("CookTime", (short) this.cookTime); + nbttagcompound.setShort("CookTimeTotal", (short) this.cookTimeTotal); + nbttagcompound.setDouble("Paper.CookSpeedMultiplier", this.cookSpeedMultiplier); // Paper - cook speed multiplier API + ContainerUtil.a(nbttagcompound, this.items); + nbttagcompound.setShort("RecipesUsedSize", (short) this.m.size()); + int i = 0; + + for (Iterator iterator = this.m.entrySet().iterator(); iterator.hasNext(); ++i) { + Entry entry = (Entry) iterator.next(); + + nbttagcompound.setString("RecipeLocation" + i, ((MinecraftKey) entry.getKey()).toString()); + nbttagcompound.setInt("RecipeAmount" + i, (Integer) entry.getValue()); + } + + if (this.l != null) { + nbttagcompound.setString("CustomName", IChatBaseComponent.ChatSerializer.a(this.l)); + } + + return nbttagcompound; + } + + public int getMaxStackSize() { + return 64; + } + + private boolean isBurning() { + return this.burnTime > 0; + } + + public void tick() { + boolean flag = this.isBurning(); + boolean flag1 = false; + + if (this.isBurning()) { + --this.burnTime; + } + + if (!this.world.isClientSide) { + ItemStack itemstack = (ItemStack) this.items.get(1); + + if (!this.isBurning() && (itemstack.isEmpty() || ((ItemStack) this.items.get(0)).isEmpty())) { + if (!this.isBurning() && this.cookTime > 0) { + this.cookTime = MathHelper.clamp(this.cookTime - 2, 0, this.cookTimeTotal); + } + } else { + IRecipe irecipe = this.world.getCraftingManager().b(this, this.world); + + if (!this.isBurning() && this.canBurn(irecipe)) { + // CraftBukkit start + CraftItemStack fuel = CraftItemStack.asCraftMirror(itemstack); + + FurnaceBurnEvent furnaceBurnEvent = new FurnaceBurnEvent(CraftBlock.at(this.world, this.position), fuel, fuelTime(itemstack)); + this.world.getServer().getPluginManager().callEvent(furnaceBurnEvent); + + if (furnaceBurnEvent.isCancelled()) { + return; + } + + this.burnTime = furnaceBurnEvent.getBurnTime(); + this.ticksForCurrentFuel = this.burnTime; + if (this.isBurning() && furnaceBurnEvent.isBurning()) { + // CraftBukkit end + flag1 = true; + if (!itemstack.isEmpty()) { + Item item = itemstack.getItem(); + + itemstack.subtract(1); + if (itemstack.isEmpty()) { + Item item1 = item.o(); + + this.items.set(1, item1 == null ? ItemStack.a : new ItemStack(item1)); + } + } + } + } + + if (this.isBurning() && this.canBurn(irecipe)) { + this.cookTime += cookSpeedMultiplier; // Paper - cook speed multiplier API + if (this.cookTime >= this.cookTimeTotal) { // Paper - cook speed multiplier API + this.cookTime = 0; + this.cookTimeTotal = this.s(); + this.burn(irecipe); + flag1 = true; + } + } else { + this.cookTime = 0; + } + } + + if (flag != this.isBurning()) { + flag1 = true; + this.world.setTypeAndData(this.position, (IBlockData) this.world.getType(this.position).set(BlockFurnace.LIT, this.isBurning()), 3); + } + } + + if (flag1) { + this.update(); + } + + } + + private int s() { + FurnaceRecipe furnacerecipe = (this.hasWorld()) ? (FurnaceRecipe) this.world.getCraftingManager().b(this, this.world) : null; // CraftBukkit - SPIGOT-4302 + + return furnacerecipe != null ? furnacerecipe.h() : 200; + } + + private boolean canBurn(@Nullable IRecipe irecipe) { + if (!((ItemStack) this.items.get(0)).isEmpty() && irecipe != null) { + ItemStack itemstack = irecipe.d(); + + if (itemstack.isEmpty()) { + return false; + } else { + ItemStack itemstack1 = (ItemStack) this.items.get(2); + + return itemstack1.isEmpty() ? true : (!itemstack1.doMaterialsMatch(itemstack) ? false : (itemstack1.getCount() < this.getMaxStackSize() && itemstack1.getCount() < itemstack1.getMaxStackSize() ? true : itemstack1.getCount() < itemstack.getMaxStackSize())); + } + } else { + return false; + } + } + + private void burn(@Nullable IRecipe irecipe) { + if (irecipe != null && this.canBurn(irecipe)) { + ItemStack itemstack = (ItemStack) this.items.get(0); + ItemStack itemstack1 = irecipe.d(); + ItemStack itemstack2 = (ItemStack) this.items.get(2); + + // CraftBukkit start - fire FurnaceSmeltEvent + CraftItemStack source = CraftItemStack.asCraftMirror(itemstack); + org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemstack1); + + FurnaceSmeltEvent furnaceSmeltEvent = new FurnaceSmeltEvent(this.world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()), source, result); + this.world.getServer().getPluginManager().callEvent(furnaceSmeltEvent); + + if (furnaceSmeltEvent.isCancelled()) { + return; + } + + result = furnaceSmeltEvent.getResult(); + itemstack1 = CraftItemStack.asNMSCopy(result); + + if (!itemstack1.isEmpty()) { + if (itemstack2.isEmpty()) { + this.items.set(2, itemstack1.cloneItemStack()); + } else if (CraftItemStack.asCraftMirror(itemstack2).isSimilar(result)) { + itemstack2.add(itemstack1.getCount()); + } else { + return; + } + } + + /* + if (itemstack2.isEmpty()) { + this.items.set(2, itemstack1.cloneItemStack()); + } else if (itemstack2.getItem() == itemstack1.getItem()) { + itemstack2.add(1); + } + */ + // CraftBukkit end + + if (!this.world.isClientSide) { + this.a(this.world, (EntityPlayer) null, irecipe); + } + + if (itemstack.getItem() == Blocks.WET_SPONGE.getItem() && !((ItemStack) this.items.get(1)).isEmpty() && ((ItemStack) this.items.get(1)).getItem() == Items.BUCKET) { + this.items.set(1, new ItemStack(Items.WATER_BUCKET)); + } + + itemstack.subtract(1); + } + } + + private static int fuelTime(ItemStack itemstack) { + if (itemstack.isEmpty()) { + return 0; + } else { + Item item = itemstack.getItem(); + + return (Integer) p().getOrDefault(item, 0); + } + } + + public static boolean isFuel(ItemStack itemstack) { + return p().containsKey(itemstack.getItem()); + } + + public boolean a(EntityHuman entityhuman) { + return this.world.getTileEntity(this.position) != this ? false : entityhuman.d((double) this.position.getX() + 0.5D, (double) this.position.getY() + 0.5D, (double) this.position.getZ() + 0.5D) <= 64.0D; + } + + public void startOpen(EntityHuman entityhuman) {} + + public void closeContainer(EntityHuman entityhuman) {} + + public boolean b(int i, ItemStack itemstack) { + if (i == 2) { + return false; + } else if (i != 1) { + return true; + } else { + ItemStack itemstack1 = (ItemStack) this.items.get(1); + + return isFuel(itemstack) || SlotFurnaceFuel.d_(itemstack) && itemstack1.getItem() != Items.BUCKET; + } + } + + public int[] getSlotsForFace(EnumDirection enumdirection) { + return enumdirection == EnumDirection.DOWN ? TileEntityFurnace.e : (enumdirection == EnumDirection.UP ? TileEntityFurnace.a : TileEntityFurnace.f); + } + + public boolean canPlaceItemThroughFace(int i, ItemStack itemstack, @Nullable EnumDirection enumdirection) { + return this.b(i, itemstack); + } + + public boolean canTakeItemThroughFace(int i, ItemStack itemstack, EnumDirection enumdirection) { + if (enumdirection == EnumDirection.DOWN && i == 1) { + Item item = itemstack.getItem(); + + if (item != Items.WATER_BUCKET && item != Items.BUCKET) { + return false; + } + } + + return true; + } + + public String getContainerName() { + return "minecraft:furnace"; + } + + public Container createContainer(PlayerInventory playerinventory, EntityHuman entityhuman) { + return new ContainerFurnace(playerinventory, this); + } + + public int getProperty(int i) { + switch (i) { + case 0: + return this.burnTime; + case 1: + return this.ticksForCurrentFuel; + case 2: + return this.cookTime; + case 3: + return this.cookTimeTotal; + default: + return 0; + } + } + + public void setProperty(int i, int j) { + switch (i) { + case 0: + this.burnTime = j; + break; + case 1: + this.ticksForCurrentFuel = j; + break; + case 2: + this.cookTime = j; + break; + case 3: + this.cookTimeTotal = j; + } + + } + + public int h() { + return 4; + } + + public void clear() { + this.items.clear(); + } + + public void a(AutoRecipeStackManager autorecipestackmanager) { + Iterator iterator = this.items.iterator(); + + while (iterator.hasNext()) { + ItemStack itemstack = (ItemStack) iterator.next(); + + autorecipestackmanager.b(itemstack); + } + + } + + public void a(IRecipe irecipe) { + if (this.m.containsKey(irecipe.getKey())) { + this.m.put(irecipe.getKey(), (Integer) this.m.get(irecipe.getKey()) + 1); + } else { + this.m.put(irecipe.getKey(), 1); + } + + } + + @Nullable + public IRecipe i() { + return null; + } + + public Map q() { + return this.m; + } + + public boolean a(World world, EntityPlayer entityplayer, @Nullable IRecipe irecipe) { + if (irecipe != null) { + this.a(irecipe); + return true; + } else { + return false; + } + } + + public void d(EntityHuman entityhuman) { + if (!this.world.getGameRules().getBoolean("doLimitedCrafting")) { + List list = Lists.newArrayList(); + Iterator iterator = this.m.keySet().iterator(); + + while (iterator.hasNext()) { + MinecraftKey minecraftkey = (MinecraftKey) iterator.next(); + IRecipe irecipe = entityhuman.world.getCraftingManager().a(minecraftkey); + + if (irecipe != null) { + list.add(irecipe); + } + } + + entityhuman.discoverRecipes(list); + } + + this.m.clear(); + } +} diff --git a/src/main/java/net/minecraft/server/TileEntityHopper.java b/src/main/java/net/minecraft/server/TileEntityHopper.java new file mode 100644 index 000000000000..544dde428963 --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntityHopper.java @@ -0,0 +1,753 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryMoveItemEvent; +import org.bukkit.event.inventory.InventoryPickupItemEvent; +import org.bukkit.inventory.Inventory; +// CraftBukkit end + +public class TileEntityHopper extends TileEntityLootable implements IHopper, ITickable { + + private NonNullList items; + private int f; + private long j; + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public List getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + public TileEntityHopper() { + super(TileEntityTypes.HOPPER); + this.items = NonNullList.a(5, ItemStack.a); + this.f = -1; + } + + public void load(NBTTagCompound nbttagcompound) { + super.load(nbttagcompound); + this.items = NonNullList.a(this.getSize(), ItemStack.a); + if (!this.d(nbttagcompound)) { + ContainerUtil.b(nbttagcompound, this.items); + } + + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.setCustomName(MCUtil.getBaseComponentFromNbt("CustomName", nbttagcompound)); // Paper - Catch ParseException + } + + this.f = nbttagcompound.getInt("TransferCooldown"); + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + if (!this.e(nbttagcompound)) { + ContainerUtil.a(nbttagcompound, this.items); + } + + nbttagcompound.setInt("TransferCooldown", this.f); + IChatBaseComponent ichatbasecomponent = this.getCustomName(); + + if (ichatbasecomponent != null) { + nbttagcompound.setString("CustomName", IChatBaseComponent.ChatSerializer.a(ichatbasecomponent)); + } + + return nbttagcompound; + } + + public int getSize() { + return this.items.size(); + } + + public ItemStack splitStack(int i, int j) { + this.d((EntityHuman) null); + return ContainerUtil.a(this.q(), i, j); + } + + public void setItem(int i, ItemStack itemstack) { + this.d((EntityHuman) null); + this.q().set(i, itemstack); + if (itemstack.getCount() > this.getMaxStackSize()) { + itemstack.setCount(this.getMaxStackSize()); + } + + } + + public IChatBaseComponent getDisplayName() { + return (IChatBaseComponent) (this.i != null ? this.i : new ChatMessage("container.hopper", new Object[0])); + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public void tick() { + if (this.world != null && !this.world.isClientSide) { + --this.f; + this.j = this.world.getTime(); + if (!this.E()) { + this.setCooldown(0); + // Spigot start + boolean result = this.a(() -> { + return a((IHopper) this); + }); + if (!result && this.world.spigotConfig.hopperCheck > 1) { + this.setCooldown(this.world.spigotConfig.hopperCheck); + } + // Spigot end + } + + } + } + + private boolean a(Supplier supplier) { + if (this.world != null && !this.world.isClientSide) { + if (!this.E() && (Boolean) this.getBlock().get(BlockHopper.ENABLED)) { + boolean flag = false; + + if (!this.p()) { + flag = this.s(); + } + + if (!this.r()) { + flag |= (Boolean) supplier.get(); + } + + if (flag) { + this.setCooldown(world.spigotConfig.hopperTransfer); // Spigot + this.update(); + return true; + } + } + + return false; + } else { + return false; + } + } + + private boolean p() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + public boolean P_() { + return this.p(); + } + + private boolean r() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (!itemstack.isEmpty() && itemstack.getCount() == itemstack.getMaxStackSize()); + + return false; + } + + // Paper start - Optimize Hoppers + private static boolean skipPullModeEventFire = false; + private static boolean skipPushModeEventFire = false; + static boolean skipHopperEvents = false; + + private boolean hopperPush(IInventory iinventory, EnumDirection enumdirection) { + skipPushModeEventFire = skipHopperEvents; + boolean foundItem = false; + for (int i = 0; i < this.getSize(); ++i) { + if (!this.getItem(i).isEmpty()) { + foundItem = true; + ItemStack origItemStack = this.getItem(i); + ItemStack itemstack = origItemStack; + + final int origCount = origItemStack.getCount(); + final int moved = Math.min(world.spigotConfig.hopperAmount, origCount); + origItemStack.setCount(moved); + + // We only need to fire the event once to give protection plugins a chance to cancel this event + // Because nothing uses getItem, every event call should end up the same result. + if (!skipPushModeEventFire) { + itemstack = callPushMoveEvent(iinventory, itemstack); + if (itemstack == null) { // cancelled + origItemStack.setCount(origCount); + return false; + } + } + final ItemStack itemstack2 = addItem(this, iinventory, itemstack, enumdirection); + final int remaining = itemstack2.getCount(); + if (remaining != moved) { + origItemStack = origItemStack.cloneItemStack(true); + origItemStack.setCount(origCount - moved + remaining); + this.setItem(i, origItemStack); + iinventory.update(); + return true; + } + origItemStack.setCount(origCount); + } + } + if (foundItem && world.paperConfig.cooldownHopperWhenFull) { // Inventory was full - cooldown + this.setCooldown(world.spigotConfig.hopperTransfer); + } + return false; + } + + private static boolean hopperPull(IHopper ihopper, IInventory iinventory, int i) { + ItemStack origItemStack = iinventory.getItem(i); + ItemStack itemstack = origItemStack; + final int origCount = origItemStack.getCount(); + final World world = ihopper.getWorld(); + final int moved = Math.min(world.spigotConfig.hopperAmount, origCount); + itemstack.setCount(moved); + + if (!skipPullModeEventFire) { + itemstack = callPullMoveEvent(ihopper, iinventory, itemstack); + if (itemstack == null) { // cancelled + origItemStack.setCount(origCount); + // Drastically improve performance by returning true. + // No plugin could of relied on the behavior of false as the other call + // site for IMIE did not exhibit the same behavior + return true; + } + } + + final ItemStack itemstack2 = addItem(iinventory, ihopper, itemstack, null); + final int remaining = itemstack2.getCount(); + if (remaining != moved) { + origItemStack = origItemStack.cloneItemStack(true); + origItemStack.setCount(origCount - moved + remaining); + IGNORE_TILE_UPDATES = true; + iinventory.setItem(i, origItemStack); + IGNORE_TILE_UPDATES = false; + iinventory.update(); + return true; + } + origItemStack.setCount(origCount); + + if (world.paperConfig.cooldownHopperWhenFull) { + cooldownHopper(ihopper); + } + + return false; + } + + private ItemStack callPushMoveEvent(IInventory iinventory, ItemStack itemstack) { + Inventory destinationInventory = getInventory(iinventory); + InventoryMoveItemEvent event = new InventoryMoveItemEvent(this.getOwner(false).getInventory(), + CraftItemStack.asCraftMirror(itemstack), destinationInventory, true); + boolean result = event.callEvent(); + if (!event.calledGetItem && !event.calledSetItem) { + skipPushModeEventFire = true; + } + if (!result) { + cooldownHopper(this); + return null; + } + + if (event.calledSetItem) { + return CraftItemStack.asNMSCopy(event.getItem()); + } else { + return itemstack; + } + } + + private static ItemStack callPullMoveEvent(IHopper hopper, IInventory iinventory, ItemStack itemstack) { + Inventory sourceInventory = getInventory(iinventory); + Inventory destination = getInventory(hopper); + + InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, + // Mirror is safe as we no plugins ever use this item + CraftItemStack.asCraftMirror(itemstack), destination, false); + boolean result = event.callEvent(); + if (!event.calledGetItem && !event.calledSetItem) { + skipPullModeEventFire = true; + } + if (!result) { + cooldownHopper(hopper); + return null; + } + + if (event.calledSetItem) { + return CraftItemStack.asNMSCopy(event.getItem()); + } else { + return itemstack; + } + } + + private static Inventory getInventory(IInventory iinventory) { + Inventory sourceInventory;// Have to special case large chests as they work oddly + if (iinventory instanceof InventoryLargeChest) { + sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory); + } else if (iinventory instanceof TileEntity) { + sourceInventory = ((TileEntity) iinventory).getOwner(false).getInventory(); + } else { + sourceInventory = iinventory.getOwner().getInventory(); + } + return sourceInventory; + } + + private static void cooldownHopper(IHopper hopper) { + if (hopper instanceof TileEntityHopper) { + ((TileEntityHopper) hopper).setCooldown(hopper.getWorld().spigotConfig.hopperTransfer); + } else if (hopper instanceof EntityMinecartHopper) { + ((EntityMinecartHopper) hopper).setCooldown(hopper.getWorld().spigotConfig.hopperTransfer / 2); + } + } + + // Paper end + private boolean s() { + IInventory iinventory = this.D(); + + if (iinventory == null) { + return false; + } else { + EnumDirection enumdirection = ((EnumDirection) this.getBlock().get(BlockHopper.FACING)).opposite(); + + if (this.a(iinventory, enumdirection)) { + return false; + } else { + return hopperPush(iinventory, enumdirection); /* // Paper - disable rest + for (int i = 0; i < this.getSize(); ++i) { + if (!this.getItem(i).isEmpty()) { + ItemStack itemstack = this.getItem(i).cloneItemStack(); + // ItemStack itemstack1 = addItem(this, iinventory, this.splitStack(i, 1), enumdirection); + + // CraftBukkit start - Call event when pushing items into other inventories + CraftItemStack oitemstack = CraftItemStack.asCraftMirror(this.splitStack(i, world.spigotConfig.hopperAmount)); // Spigot + + Inventory destinationInventory; + // Have to special case large chests as they work oddly + if (iinventory instanceof InventoryLargeChest) { + destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory); + } else { + destinationInventory = iinventory.getOwner().getInventory(); + } + + InventoryMoveItemEvent event = new InventoryMoveItemEvent(this.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true); + this.getWorld().getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + this.setItem(i, itemstack); + this.setCooldown(world.spigotConfig.hopperTransfer); // Spigot + return false; + } + int origCount = event.getItem().getAmount(); // Spigot + ItemStack itemstack1 = addItem(this, iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection); + // CraftBukkit end + + if (itemstack1.isEmpty()) { + iinventory.update(); + return true; + } + + itemstack.subtract(origCount - itemstack1.getCount()); // Spigot + this.setItem(i, itemstack); + } + } + + return false;*/ // Paper - end commenting out replaced block for Hopper Optimizations + } + } + } + + private boolean a(IInventory iinventory, EnumDirection enumdirection) { + if (iinventory instanceof IWorldInventory) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory; + int[] aint = iworldinventory.getSlotsForFace(enumdirection); + int[] aint1 = aint; + int i = aint.length; + + for (int j = 0; j < i; ++j) { + int k = aint1[j]; + ItemStack itemstack = iworldinventory.getItem(k); + + if (itemstack.isEmpty() || itemstack.getCount() != itemstack.getMaxStackSize()) { + return false; + } + } + } else { + int l = iinventory.getSize(); + + for (int i1 = 0; i1 < l; ++i1) { + ItemStack itemstack1 = iinventory.getItem(i1); + + if (itemstack1.isEmpty() || itemstack1.getCount() != itemstack1.getMaxStackSize()) { + return false; + } + } + } + + return true; + } + + private static boolean b(IInventory iinventory, EnumDirection enumdirection) { + if (iinventory instanceof IWorldInventory) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory; + int[] aint = iworldinventory.getSlotsForFace(enumdirection); + int[] aint1 = aint; + int i = aint.length; + + for (int j = 0; j < i; ++j) { + int k = aint1[j]; + + if (!iworldinventory.getItem(k).isEmpty()) { + return false; + } + } + } else { + int l = iinventory.getSize(); + + for (int i1 = 0; i1 < l; ++i1) { + if (!iinventory.getItem(i1).isEmpty()) { + return false; + } + } + } + + return true; + } + + public static boolean a(IHopper ihopper) { + IInventory iinventory = b(ihopper); + + if (iinventory != null) { + EnumDirection enumdirection = EnumDirection.DOWN; + + if (b(iinventory, enumdirection)) { + return false; + } + skipPullModeEventFire = skipHopperEvents; // Paper + + if (iinventory instanceof IWorldInventory) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory; + int[] aint = iworldinventory.getSlotsForFace(enumdirection); + int[] aint1 = aint; + int i = aint.length; + + for (int j = 0; j < i; ++j) { + int k = aint1[j]; + + if (a(ihopper, iinventory, k, enumdirection)) { + return true; + } + } + } else { + int l = iinventory.getSize(); + + for (int i1 = 0; i1 < l; ++i1) { + if (a(ihopper, iinventory, i1, enumdirection)) { + return true; + } + } + } + } else { + Iterator iterator = c(ihopper).iterator(); + + while (iterator.hasNext()) { + EntityItem entityitem = (EntityItem) iterator.next(); + + if (a((IInventory) ihopper, entityitem)) { + return true; + } + } + } + + return false; + } + + private static boolean a(IHopper ihopper, IInventory iinventory, int i, EnumDirection enumdirection) { + ItemStack itemstack = iinventory.getItem(i); + + if (!itemstack.isEmpty() && b(iinventory, itemstack, i, enumdirection)) { + return hopperPull(ihopper, iinventory, i); /* // Paper - disable rest + ItemStack itemstack1 = itemstack.cloneItemStack(); + // ItemStack itemstack2 = addItem(iinventory, ihopper, iinventory.splitStack(i, 1), (EnumDirection) null); + // CraftBukkit start - Call event on collection of items from inventories into the hopper + CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.splitStack(i, ihopper.getWorld().spigotConfig.hopperAmount)); // Spigot + + Inventory sourceInventory; + // Have to special case large chests as they work oddly + if (iinventory instanceof InventoryLargeChest) { + sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory); + } else { + sourceInventory = iinventory.getOwner().getInventory(); + } + + InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), ihopper.getOwner().getInventory(), false); + + ihopper.getWorld().getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + iinventory.setItem(i, itemstack1); + + if (ihopper instanceof TileEntityHopper) { + ((TileEntityHopper) ihopper).setCooldown(ihopper.getWorld().spigotConfig.hopperTransfer); // Spigot + } else if (ihopper instanceof EntityMinecartHopper) { + ((EntityMinecartHopper) ihopper).setCooldown(ihopper.getWorld().spigotConfig.hopperTransfer / 2); // Spigot + } + return false; + } + int origCount = event.getItem().getAmount(); // Spigot + ItemStack itemstack2 = addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null); + // CraftBukkit end + + if (itemstack2.isEmpty()) { + iinventory.update(); + return true; + } + + itemstack1.subtract(origCount - itemstack2.getCount()); // Spigot + iinventory.setItem(i, itemstack1);*/ // Paper - end commenting out replaced block for Hopper Optimizations + } + + return false; + } + + public static boolean a(IInventory iinventory, EntityItem entityitem) { + boolean flag = false; + // CraftBukkit start + InventoryPickupItemEvent event = new InventoryPickupItemEvent(getInventory(iinventory), (org.bukkit.entity.Item) entityitem.getBukkitEntity()); // Paper - use getInventory() to avoid snapshot creation + entityitem.world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + // CraftBukkit end + ItemStack itemstack = entityitem.getItemStack().cloneItemStack(); + ItemStack itemstack1 = addItem((IInventory) null, iinventory, itemstack, (EnumDirection) null); + + if (itemstack1.isEmpty()) { + flag = true; + entityitem.die(); + } else { + entityitem.setItemStack(itemstack1); + } + + return flag; + } + + public static ItemStack addItem(@Nullable IInventory iinventory, IInventory iinventory1, ItemStack itemstack, @Nullable EnumDirection enumdirection) { + if (iinventory1 instanceof IWorldInventory && enumdirection != null) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory1; + int[] aint = iworldinventory.getSlotsForFace(enumdirection); + + for (int i = 0; i < aint.length && !itemstack.isEmpty(); ++i) { + itemstack = a(iinventory, iinventory1, itemstack, aint[i], enumdirection); + } + } else { + int j = iinventory1.getSize(); + + for (int k = 0; k < j && !itemstack.isEmpty(); ++k) { + itemstack = a(iinventory, iinventory1, itemstack, k, enumdirection); + } + } + + return itemstack; + } + + private static boolean a(IInventory iinventory, ItemStack itemstack, int i, @Nullable EnumDirection enumdirection) { + return !iinventory.b(i, itemstack) ? false : !(iinventory instanceof IWorldInventory) || ((IWorldInventory) iinventory).canPlaceItemThroughFace(i, itemstack, enumdirection); + } + + private static boolean b(IInventory iinventory, ItemStack itemstack, int i, EnumDirection enumdirection) { + return !(iinventory instanceof IWorldInventory) || ((IWorldInventory) iinventory).canTakeItemThroughFace(i, itemstack, enumdirection); + } + + private static ItemStack a(@Nullable IInventory iinventory, IInventory iinventory1, ItemStack itemstack, int i, @Nullable EnumDirection enumdirection) { + ItemStack itemstack1 = iinventory1.getItem(i); + + if (a(iinventory1, itemstack, i, enumdirection)) { + boolean flag = false; + boolean flag1 = iinventory1.P_(); + + if (itemstack1.isEmpty()) { + IGNORE_TILE_UPDATES = true; // Paper + iinventory1.setItem(i, itemstack); + IGNORE_TILE_UPDATES = false; // Paper + itemstack = ItemStack.a; + flag = true; + } else if (a(itemstack1, itemstack)) { + int j = itemstack.getMaxStackSize() - itemstack1.getCount(); + int k = Math.min(itemstack.getCount(), j); + + itemstack.subtract(k); + itemstack1.add(k); + flag = k > 0; + } + + if (flag) { + if (flag1 && iinventory1 instanceof TileEntityHopper) { + TileEntityHopper tileentityhopper = (TileEntityHopper) iinventory1; + + if (!tileentityhopper.J()) { + byte b0 = 0; + + if (iinventory instanceof TileEntityHopper) { + TileEntityHopper tileentityhopper1 = (TileEntityHopper) iinventory; + + if (tileentityhopper.j >= tileentityhopper1.j) { + b0 = 1; + } + } + + tileentityhopper.setCooldown(tileentityhopper.world.spigotConfig.hopperTransfer - b0); // Spigot + } + } + + iinventory1.update(); + } + } + + return itemstack; + } + + @Nullable + private IInventory D() { + EnumDirection enumdirection = (EnumDirection) this.getBlock().get(BlockHopper.FACING); + + return a(this.getWorld(), this.position.shift(enumdirection)); + } + + @Nullable + public static IInventory b(IHopper ihopper) { + return a(ihopper.getWorld(), ihopper.G(), ihopper.H() + 1.0D, ihopper.I()); + } + + public static List c(IHopper ihopper) { + return (List) ihopper.i().d().stream().flatMap((axisalignedbb) -> { + return ihopper.getWorld().a(EntityItem.class, axisalignedbb.d(ihopper.G() - 0.5D, ihopper.H() - 0.5D, ihopper.I() - 0.5D), IEntitySelector.a).stream(); + }).collect(Collectors.toList()); + } + + @Nullable + public static IInventory a(World world, BlockPosition blockposition) { + return a(world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D); + } + + @Nullable + public static IInventory a(World world, double d0, double d1, double d2) { + Object object = null; + BlockPosition blockposition = new BlockPosition(d0, d1, d2); + if ( !world.isLoaded( blockposition ) ) return null; // Spigot + IBlockData iblockdata = world.getType(blockposition); + Block block = iblockdata.getBlock(); + + if (block.isTileEntity()) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof IInventory) { + object = (IInventory) tileentity; + if (object instanceof TileEntityChest && block instanceof BlockChest) { + object = ((BlockChest) block).getInventory(iblockdata, world, blockposition, true); + } + } + } + + if (object == null) { + List list = world.getEntities((Entity) null, new AxisAlignedBB(d0 - 0.5D, d1 - 0.5D, d2 - 0.5D, d0 + 0.5D, d1 + 0.5D, d2 + 0.5D), IEntitySelector.d); + + if (!list.isEmpty()) { + object = (IInventory) list.get(world.random.nextInt(list.size())); + } + } + + return (IInventory) object; + } + + private static boolean a(ItemStack itemstack, ItemStack itemstack1) { + return itemstack.getItem() != itemstack1.getItem() ? false : (itemstack.getDamage() != itemstack1.getDamage() ? false : (itemstack.getCount() > itemstack.getMaxStackSize() ? false : ItemStack.equals(itemstack, itemstack1))); + } + + public double G() { + return (double) this.position.getX() + 0.5D; + } + + public double H() { + return (double) this.position.getY() + 0.5D; + } + + public double I() { + return (double) this.position.getZ() + 0.5D; + } + + private void setCooldown(int i) { + this.f = i; + } + + private boolean E() { + return this.f > 0; + } + + private boolean J() { + return this.f > 8; + } + + public String getContainerName() { + return "minecraft:hopper"; + } + + public Container createContainer(PlayerInventory playerinventory, EntityHuman entityhuman) { + this.d(entityhuman); + return new ContainerHopper(playerinventory, this, entityhuman); + } + + protected NonNullList q() { + return this.items; + } + + protected void a(NonNullList nonnulllist) { + this.items = nonnulllist; + } + + public void a(Entity entity) { + if (entity instanceof EntityItem) { + BlockPosition blockposition = this.getPosition(); + + if (VoxelShapes.c(VoxelShapes.a(entity.getBoundingBox().d((double) (-blockposition.getX()), (double) (-blockposition.getY()), (double) (-blockposition.getZ()))), this.i(), OperatorBoolean.AND)) { + this.a(() -> { + return a((IInventory) this, (EntityItem) entity); + }); + } + } + + } +} diff --git a/src/main/java/net/minecraft/server/TileEntityLootable.java b/src/main/java/net/minecraft/server/TileEntityLootable.java new file mode 100644 index 000000000000..60f0b5046a5a --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntityLootable.java @@ -0,0 +1,158 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; + +public abstract class TileEntityLootable extends TileEntityContainer implements ILootable { + + protected MinecraftKey g; public MinecraftKey getLootTableKey() { return g; } public void setLootTable(MinecraftKey key) { g = key; } // Paper - OBFHELPER + protected long h; public long getSeed() { return h; } public void setSeed(long seed) { h = seed; } // Paper - OBFHELPER + protected IChatBaseComponent i; + public final com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData = new com.destroystokyo.paper.loottable.PaperLootableInventoryData(new com.destroystokyo.paper.loottable.PaperTileEntityLootableInventory(this)); // Paper + + protected TileEntityLootable(TileEntityTypes tileentitytypes) { + super(tileentitytypes); + } + + public static void a(IBlockAccess iblockaccess, Random random, BlockPosition blockposition, MinecraftKey minecraftkey) { + TileEntity tileentity = iblockaccess.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityLootable) { + ((TileEntityLootable) tileentity).setLootTable(minecraftkey, random.nextLong()); + } + + } + + protected boolean d(NBTTagCompound nbttagcompound) { + lootableData.loadNbt(nbttagcompound); // Paper + if (nbttagcompound.hasKeyOfType("LootTable", 8)) { + this.g = new MinecraftKey(nbttagcompound.getString("LootTable")); + this.h = nbttagcompound.getLong("LootTableSeed"); + return false; // Paper - always load the items, table may still remain + } else { + return false; + } + } + + protected boolean e(NBTTagCompound nbttagcompound) { + lootableData.saveNbt(nbttagcompound); // Paper + if (this.g == null) { + return false; + } else { + nbttagcompound.setString("LootTable", this.g.toString()); + if (this.h != 0L) { + nbttagcompound.setLong("LootTableSeed", this.h); + } + + return false; // Paper - always save the items, table may still remain + } + } + + public void d(@Nullable EntityHuman entityhuman) { + if (lootableData.shouldReplenish(entityhuman) && this.world.getMinecraftServer() != null) { // Paper + LootTable loottable = this.world.getMinecraftServer().getLootTableRegistry().getLootTable(this.g); + + lootableData.processRefill(entityhuman); // Paper + Random random; + + if (this.h == 0L) { + random = new Random(); + } else { + random = new Random(this.h); + } + + LootTableInfo.Builder loottableinfo_builder = new LootTableInfo.Builder((WorldServer) this.world); + + loottableinfo_builder.position(this.position); + if (entityhuman != null) { + loottableinfo_builder.luck(entityhuman.dJ()); + } + + loottable.fillInventory(this, random, loottableinfo_builder.build()); + } + + } + + public MinecraftKey getLootTable() { + return this.g; + } + + public void setLootTable(MinecraftKey minecraftkey, long i) { + this.g = minecraftkey; + this.h = i; + } + + public boolean hasCustomName() { + return this.i != null; + } + + public void setCustomName(@Nullable IChatBaseComponent ichatbasecomponent) { + this.i = ichatbasecomponent; + } + + @Nullable + public IChatBaseComponent getCustomName() { + return this.i; + } + + public ItemStack getItem(int i) { + this.d((EntityHuman) null); + return (ItemStack) this.q().get(i); + } + + public ItemStack splitStack(int i, int j) { + this.d((EntityHuman) null); + ItemStack itemstack = ContainerUtil.a(this.q(), i, j); + + if (!itemstack.isEmpty()) { + this.update(); + } + + return itemstack; + } + + public ItemStack splitWithoutUpdate(int i) { + this.d((EntityHuman) null); + return ContainerUtil.a(this.q(), i); + } + + public void setItem(int i, @Nullable ItemStack itemstack) { + this.d((EntityHuman) null); + this.q().set(i, itemstack); + if (itemstack.getCount() > this.getMaxStackSize()) { + itemstack.setCount(this.getMaxStackSize()); + } + + this.update(); + } + + public boolean a(EntityHuman entityhuman) { + return this.world.getTileEntity(this.position) != this ? false : entityhuman.d((double) this.position.getX() + 0.5D, (double) this.position.getY() + 0.5D, (double) this.position.getZ() + 0.5D) <= 64.0D; + } + + public void startOpen(EntityHuman entityhuman) {} + + public void closeContainer(EntityHuman entityhuman) {} + + public boolean b(int i, ItemStack itemstack) { + return true; + } + + public int getProperty(int i) { + return 0; + } + + public void setProperty(int i, int j) {} + + public int h() { + return 0; + } + + public void clear() { + this.q().clear(); + } + + protected abstract NonNullList q(); + + protected abstract void a(NonNullList nonnulllist); +} diff --git a/src/main/java/net/minecraft/server/TileEntityShulkerBox.java b/src/main/java/net/minecraft/server/TileEntityShulkerBox.java new file mode 100644 index 000000000000..296b8dd56d0c --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntityShulkerBox.java @@ -0,0 +1,347 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.stream.IntStream; +import javax.annotation.Nullable; +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +// CraftBukkit end + +public class TileEntityShulkerBox extends TileEntityLootable implements IWorldInventory, ITickable { + + private static final int[] a = IntStream.range(0, 27).toArray(); + private NonNullList e; + private boolean f; + private int j; + private TileEntityShulkerBox.AnimationPhase k; + private float l; + private float m; + private EnumColor n; + private boolean o; + private boolean p; + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public List getContents() { + return this.e; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + public TileEntityShulkerBox(@Nullable EnumColor enumcolor) { + super(TileEntityTypes.SHULKER_BOX); + this.e = NonNullList.a(27, ItemStack.a); + this.k = TileEntityShulkerBox.AnimationPhase.CLOSED; + this.n = enumcolor; + } + + public TileEntityShulkerBox() { + this((EnumColor) null); + this.o = true; + } + + public void tick() { + this.p(); + if (this.k == TileEntityShulkerBox.AnimationPhase.OPENING || this.k == TileEntityShulkerBox.AnimationPhase.CLOSING) { + this.H(); + } + + } + + protected void p() { + this.m = this.l; + switch (this.k) { + case CLOSED: + this.l = 0.0F; + break; + case OPENING: + this.l += 0.1F; + if (this.l >= 1.0F) { + this.H(); + this.k = TileEntityShulkerBox.AnimationPhase.OPENED; + this.l = 1.0F; + } + break; + case CLOSING: + this.l -= 0.1F; + if (this.l <= 0.0F) { + this.k = TileEntityShulkerBox.AnimationPhase.CLOSED; + this.l = 0.0F; + } + break; + case OPENED: + this.l = 1.0F; + } + + } + + public TileEntityShulkerBox.AnimationPhase r() { + return this.k; + } + + public AxisAlignedBB a(IBlockData iblockdata) { + return this.b((EnumDirection) iblockdata.get(BlockShulkerBox.a)); + } + + public AxisAlignedBB b(EnumDirection enumdirection) { + return VoxelShapes.b().getBoundingBox().b((double) (0.5F * this.a(1.0F) * (float) enumdirection.getAdjacentX()), (double) (0.5F * this.a(1.0F) * (float) enumdirection.getAdjacentY()), (double) (0.5F * this.a(1.0F) * (float) enumdirection.getAdjacentZ())); + } + + private AxisAlignedBB c(EnumDirection enumdirection) { + EnumDirection enumdirection1 = enumdirection.opposite(); + + return this.b(enumdirection).a((double) enumdirection1.getAdjacentX(), (double) enumdirection1.getAdjacentY(), (double) enumdirection1.getAdjacentZ()); + } + + private void H() { + IBlockData iblockdata = this.world.getType(this.getPosition()); + + if (iblockdata.getBlock() instanceof BlockShulkerBox) { + EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockShulkerBox.a); + AxisAlignedBB axisalignedbb = this.c(enumdirection).a(this.position); + List list = this.world.getEntities((Entity) null, axisalignedbb); + + if (!list.isEmpty()) { + for (int i = 0; i < list.size(); ++i) { + Entity entity = (Entity) list.get(i); + + if (entity.getPushReaction() != EnumPistonReaction.IGNORE) { + double d0 = 0.0D; + double d1 = 0.0D; + double d2 = 0.0D; + AxisAlignedBB axisalignedbb1 = entity.getBoundingBox(); + + switch (enumdirection.k()) { + case X: + if (enumdirection.c() == EnumDirection.EnumAxisDirection.POSITIVE) { + d0 = axisalignedbb.maxX - axisalignedbb1.minX; + } else { + d0 = axisalignedbb1.maxX - axisalignedbb.minX; + } + + d0 += 0.01D; + break; + case Y: + if (enumdirection.c() == EnumDirection.EnumAxisDirection.POSITIVE) { + d1 = axisalignedbb.maxY - axisalignedbb1.minY; + } else { + d1 = axisalignedbb1.maxY - axisalignedbb.minY; + } + + d1 += 0.01D; + break; + case Z: + if (enumdirection.c() == EnumDirection.EnumAxisDirection.POSITIVE) { + d2 = axisalignedbb.maxZ - axisalignedbb1.minZ; + } else { + d2 = axisalignedbb1.maxZ - axisalignedbb.minZ; + } + + d2 += 0.01D; + } + + entity.move(EnumMoveType.SHULKER_BOX, d0 * (double) enumdirection.getAdjacentX(), d1 * (double) enumdirection.getAdjacentY(), d2 * (double) enumdirection.getAdjacentZ()); + } + } + + } + } + } + + public int getSize() { + return this.e.size(); + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public boolean c(int i, int j) { + if (i == 1) { + this.j = j; + if (j == 0) { + this.k = TileEntityShulkerBox.AnimationPhase.CLOSING; + } + + if (j == 1) { + this.k = TileEntityShulkerBox.AnimationPhase.OPENING; + } + + return true; + } else { + return super.c(i, j); + } + } + + public void startOpen(EntityHuman entityhuman) { + if (!entityhuman.isSpectator()) { + if (this.j < 0) { + this.j = 0; + } + + ++this.j; + this.world.playBlockAction(this.position, this.getBlock().getBlock(), 1, this.j); + if (this.j == 1) { + this.world.a((EntityHuman) null, this.position, SoundEffects.BLOCK_SHULKER_BOX_OPEN, SoundCategory.BLOCKS, 0.5F, this.world.random.nextFloat() * 0.1F + 0.9F); + } + } + + } + + public void closeContainer(EntityHuman entityhuman) { + if (!entityhuman.isSpectator()) { + --this.j; + this.world.playBlockAction(this.position, this.getBlock().getBlock(), 1, this.j); + if (this.j <= 0) { + this.world.a((EntityHuman) null, this.position, SoundEffects.BLOCK_SHULKER_BOX_CLOSE, SoundCategory.BLOCKS, 0.5F, this.world.random.nextFloat() * 0.1F + 0.9F); + } + } + + } + + public Container createContainer(PlayerInventory playerinventory, EntityHuman entityhuman) { + return new ContainerShulkerBox(playerinventory, this, entityhuman); + } + + public String getContainerName() { + return "minecraft:shulker_box"; + } + + public IChatBaseComponent getDisplayName() { + IChatBaseComponent ichatbasecomponent = this.getCustomName(); + + return (IChatBaseComponent) (ichatbasecomponent != null ? ichatbasecomponent : new ChatMessage("container.shulkerBox", new Object[0])); + } + + public void load(NBTTagCompound nbttagcompound) { + super.load(nbttagcompound); + this.f(nbttagcompound); + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + return this.g(nbttagcompound); + } + + public void f(NBTTagCompound nbttagcompound) { + this.e = NonNullList.a(this.getSize(), ItemStack.a); + if (!this.d(nbttagcompound) && nbttagcompound.hasKeyOfType("Items", 9)) { + ContainerUtil.b(nbttagcompound, this.e); + } + + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.i = MCUtil.getBaseComponentFromNbt("CustomName", nbttagcompound); // Paper - Catch ParseException + } + + } + + public NBTTagCompound g(NBTTagCompound nbttagcompound) { + if (!this.e(nbttagcompound)) { + ContainerUtil.a(nbttagcompound, this.e, false); + } + + IChatBaseComponent ichatbasecomponent = this.getCustomName(); + + if (ichatbasecomponent != null) { + nbttagcompound.setString("CustomName", IChatBaseComponent.ChatSerializer.a(ichatbasecomponent)); + } + + if (!nbttagcompound.hasKey("Lock") && this.isLocked()) { + this.getLock().a(nbttagcompound); + } + + return nbttagcompound; + } + + protected NonNullList q() { + return this.e; + } + + protected void a(NonNullList nonnulllist) { + this.e = nonnulllist; + } + + public boolean P_() { + Iterator iterator = this.e.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + public int[] getSlotsForFace(EnumDirection enumdirection) { + return TileEntityShulkerBox.a; + } + + public boolean canPlaceItemThroughFace(int i, ItemStack itemstack, @Nullable EnumDirection enumdirection) { + return !(Block.asBlock(itemstack.getItem()) instanceof BlockShulkerBox); + } + + public boolean canTakeItemThroughFace(int i, ItemStack itemstack, EnumDirection enumdirection) { + return true; + } + + public void clear() { + this.f = true; + super.clear(); + } + + public boolean s() { + return this.f; + } + + public float a(float f) { + return this.m + (this.l - this.m) * f; + } + + @Nullable + public PacketPlayOutTileEntityData getUpdatePacket() { + return new PacketPlayOutTileEntityData(this.position, 10, this.aa_()); + } + + public boolean E() { + return this.p; + } + + public void a(boolean flag) { + this.p = flag; + } + + public boolean G() { + return !this.E() || !this.P_() || this.hasCustomName() || this.g != null; + } + + public static enum AnimationPhase { + + CLOSED, OPENING, OPENED, CLOSING; + + private AnimationPhase() {} + } +} diff --git a/src/main/java/net/minecraft/server/TileEntitySign.java b/src/main/java/net/minecraft/server/TileEntitySign.java new file mode 100644 index 000000000000..d0a91f675509 --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntitySign.java @@ -0,0 +1,192 @@ +package net.minecraft.server; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import javax.annotation.Nullable; + +public class TileEntitySign extends TileEntity implements ICommandListener { + + public final IChatBaseComponent[] lines = new IChatBaseComponent[] { new ChatComponentText(""), new ChatComponentText(""), new ChatComponentText(""), new ChatComponentText("")}; + public int e = -1; + public boolean isEditable = true; + private EntityHuman g; + private final String[] h = new String[4]; + + // Paper start - Strip invalid unicode from signs on load + private static final boolean keepInvalidUnicode = Boolean.getBoolean("Paper.keepInvalidUnicode"); // Allow people to keep their bad unicode if they really want it + private boolean privateUnicodeRemoved = false; + public java.util.UUID signEditor; + private static final boolean CONVERT_LEGACY_SIGNS = Boolean.getBoolean("convertLegacySigns"); + // Paper end + + public TileEntitySign() { + super(TileEntityTypes.SIGN); + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + + for (int i = 0; i < 4; ++i) { + String s = IChatBaseComponent.ChatSerializer.a(this.lines[i]); + + nbttagcompound.setString("Text" + (i + 1), s); + } + + // CraftBukkit start + if (CONVERT_LEGACY_SIGNS) { // Paper + nbttagcompound.setBoolean("Bukkit.isConverted", true); + } + // CraftBukkit end + + // Paper start - Only remove private area unicode once + if (this.privateUnicodeRemoved) { + nbttagcompound.setBoolean("Paper.RemovedPrivateUnicode", true); + } + // Paper end + + return nbttagcompound; + } + + public void load(NBTTagCompound nbttagcompound) { + this.isEditable = false; + super.load(nbttagcompound); + + // Paper start - Keep track, only do it once per sign + this.privateUnicodeRemoved = nbttagcompound.getBoolean("Paper.RemovedPrivateUnicode"); + boolean ranUnicodeRemoval = false; + // Paper end + + // CraftBukkit start - Add an option to convert signs correctly + // This is done with a flag instead of all the time because + // we have no way to tell whether a sign is from 1.7.10 or 1.8 + + boolean oldSign = Boolean.getBoolean("convertLegacySigns") && !nbttagcompound.getBoolean("Bukkit.isConverted"); + + for (int i = 0; i < 4; ++i) { + String s = nbttagcompound.getString("Text" + (i + 1)); + if (s != null && s.length() > 2048) { + s = "\"\""; + } + + // Paper start - Strip private use area unicode from signs + if (s != null && !keepInvalidUnicode && !this.privateUnicodeRemoved) { + StringBuilder builder = new StringBuilder(); + for (char character : s.toCharArray()) { + if (Character.UnicodeBlock.of(character) != Character.UnicodeBlock.PRIVATE_USE_AREA) { + builder.append(character); + } + } + s = builder.toString(); + ranUnicodeRemoval = true; + } + // Paper end + + try { + //IChatBaseComponent ichatbasecomponent = IChatBaseComponent.ChatSerializer.a(s); // Paper - move down - the old format might throw a json error + + if (oldSign && !isLoadingStructure) { // Paper - saved structures will be in the new format, but will not have isConverted + lines[i] = org.bukkit.craftbukkit.util.CraftChatMessage.fromString(s)[0]; + continue; + } + // CraftBukkit end + IChatBaseComponent ichatbasecomponent = IChatBaseComponent.ChatSerializer.a(s); // Paper - after old sign + + if (this.world instanceof WorldServer) { + try { + this.lines[i] = ChatComponentUtils.filterForDisplay(this.a((EntityPlayer) null), ichatbasecomponent, (Entity) null); + } catch (CommandSyntaxException commandsyntaxexception) { + this.lines[i] = ichatbasecomponent; + } + } else { + this.lines[i] = ichatbasecomponent; + } + } catch (com.google.gson.JsonParseException jsonparseexception) { + this.lines[i] = new ChatComponentText(s); + } + + this.h[i] = null; + } + + if (ranUnicodeRemoval) this.privateUnicodeRemoved = true; // Paper - Flag to write NBT + } + + public void a(int i, IChatBaseComponent ichatbasecomponent) { + this.lines[i] = ichatbasecomponent; + this.h[i] = null; + } + + @Nullable + public PacketPlayOutTileEntityData getUpdatePacket() { + return new PacketPlayOutTileEntityData(this.position, 9, this.aa_()); + } + + public NBTTagCompound aa_() { + return this.save(new NBTTagCompound()); + } + + public boolean isFilteredNBT() { + return true; + } + + public boolean d() { + return this.isEditable; + } + + public void a(EntityHuman entityhuman) { + // Paper start + //this.g = entityhuman; + signEditor = entityhuman != null ? entityhuman.getUniqueID() : null; + // Paper end + } + + public EntityHuman e() { + return this.g; + } + + public boolean b(EntityHuman entityhuman) { + IChatBaseComponent[] aichatbasecomponent = this.lines; + int i = aichatbasecomponent.length; + + for (int j = 0; j < i; ++j) { + IChatBaseComponent ichatbasecomponent = aichatbasecomponent[j]; + ChatModifier chatmodifier = ichatbasecomponent == null ? null : ichatbasecomponent.getChatModifier(); + + if (chatmodifier != null && chatmodifier.h() != null) { + ChatClickable chatclickable = chatmodifier.h(); + + if (chatclickable.a() == ChatClickable.EnumClickAction.RUN_COMMAND) { + entityhuman.bK().getCommandDispatcher().a(this.a((EntityPlayer) entityhuman), chatclickable.b()); + } + } + } + + return true; + } + + public void sendMessage(IChatBaseComponent ichatbasecomponent) {} + + // CraftBukkit start + @Override + public org.bukkit.command.CommandSender getBukkitSender(CommandListenerWrapper wrapper) { + return wrapper.getEntity() != null ? wrapper.getEntity().getBukkitSender(wrapper) : new org.bukkit.craftbukkit.command.CraftBlockCommandSender(wrapper, this); + } + // CraftBukkit end + + public CommandListenerWrapper a(@Nullable EntityPlayer entityplayer) { + String s = entityplayer == null ? "Sign" : entityplayer.getDisplayName().getString(); + Object object = entityplayer == null ? new ChatComponentText("Sign") : entityplayer.getScoreboardDisplayName(); + + return new CommandListenerWrapper(this, new Vec3D((double) this.position.getX() + 0.5D, (double) this.position.getY() + 0.5D, (double) this.position.getZ() + 0.5D), Vec2F.a, (WorldServer) this.world, 2, s, (IChatBaseComponent) object, this.world.getMinecraftServer(), entityplayer); + } + + public boolean a() { + return false; + } + + public boolean b() { + return false; + } + + public boolean B_() { + return false; + } +} diff --git a/src/main/java/net/minecraft/server/TileEntitySkull.java b/src/main/java/net/minecraft/server/TileEntitySkull.java new file mode 100644 index 000000000000..6511bfda8d2d --- /dev/null +++ b/src/main/java/net/minecraft/server/TileEntitySkull.java @@ -0,0 +1,273 @@ +package net.minecraft.server; + +import com.google.common.collect.Iterables; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.mojang.authlib.properties.Property; +import java.util.UUID; +import javax.annotation.Nullable; + +// Spigot start +import com.google.common.base.Predicate; +import com.google.common.cache.LoadingCache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.util.concurrent.Futures; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.mojang.authlib.Agent; +import com.mojang.authlib.ProfileLookupCallback; +import java.util.concurrent.Callable; +// Spigot end + +public class TileEntitySkull extends TileEntity /*implements ITickable*/ { // Paper - remove tickable + + private GameProfile a; + private int e; + private boolean f; + public boolean drop = true; + private static UserCache userCache; + private static MinecraftSessionService sessionService; + // Spigot start + public static final ExecutorService executor = Executors.newFixedThreadPool(3, + new ThreadFactoryBuilder() + .setNameFormat("Head Conversion Thread - %1$d") + .build() + ); + public static final LoadingCache skinCache = CacheBuilder.newBuilder() + .maximumSize( 5000 ) + .expireAfterAccess( 60, TimeUnit.MINUTES ) + .build( new CacheLoader() + { + @Override + public GameProfile load(String key) throws Exception + { + final GameProfile[] profiles = new GameProfile[1]; + ProfileLookupCallback gameProfileLookup = new ProfileLookupCallback() { + + @Override + public void onProfileLookupSucceeded(GameProfile gp) { + profiles[0] = gp; + } + + @Override + public void onProfileLookupFailed(GameProfile gp, Exception excptn) { + profiles[0] = gp; + } + }; + + MinecraftServer.getServer().getGameProfileRepository().findProfilesByNames(new String[] { key }, Agent.MINECRAFT, gameProfileLookup); + + GameProfile profile = profiles[ 0 ]; + if (profile == null) { + UUID uuid = EntityHuman.a(new GameProfile(null, key)); + profile = new GameProfile(uuid, key); + + gameProfileLookup.onProfileLookupSucceeded(profile); + } else + { + + Property property = Iterables.getFirst( profile.getProperties().get( "textures" ), null ); + + if ( property == null ) + { + profile = TileEntitySkull.sessionService.fillProfileProperties( profile, true ); + } + } + + + return profile; + } + } ); + // Spigot end + + public TileEntitySkull() { + super(TileEntityTypes.SKULL); + } + + public static void a(UserCache usercache) { + TileEntitySkull.userCache = usercache; + } + + public static void a(MinecraftSessionService minecraftsessionservice) { + TileEntitySkull.sessionService = minecraftsessionservice; + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + if (this.a != null) { + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + GameProfileSerializer.serialize(nbttagcompound1, this.a); + nbttagcompound.set("Owner", nbttagcompound1); + } + + return nbttagcompound; + } + + public void load(NBTTagCompound nbttagcompound) { + super.load(nbttagcompound); + if (nbttagcompound.hasKeyOfType("Owner", 10)) { + this.setGameProfile(GameProfileSerializer.deserialize(nbttagcompound.getCompound("Owner"))); + } else if (nbttagcompound.hasKeyOfType("ExtraType", 8)) { + String s = nbttagcompound.getString("ExtraType"); + + if (!UtilColor.b(s)) { + this.setGameProfile(new GameProfile((UUID) null, s)); + } + } + + } + + public void tick() { + Block block = this.getBlock().getBlock(); + + if (block == Blocks.DRAGON_HEAD || block == Blocks.DRAGON_WALL_HEAD) { + if (this.world.isBlockIndirectlyPowered(this.position)) { + this.f = true; + ++this.e; + } else { + this.f = false; + } + } + + } + + @Nullable + public GameProfile getGameProfile() { + return this.a; + } + + // Paper start + static NBTTagCompound sanitizeTileEntityUUID(NBTTagCompound cmp) { + NBTTagCompound owner = cmp.getCompound("Owner"); + if (!owner.isEmpty()) { + sanitizeUUID(owner); + } + return cmp; + } + + static void sanitizeUUID(NBTTagCompound owner) { + NBTTagCompound properties = owner.getCompound("Properties"); + NBTTagList list = null; + if (!properties.isEmpty()) { + list = properties.getList("textures", 10); + } + + if (list != null && !list.isEmpty()) { + String textures = ((NBTTagCompound)list.get(0)).getString("Value"); + if (textures != null && textures.length() > 3) { + String uuid = UUID.nameUUIDFromBytes(textures.getBytes()).toString(); + owner.setString("Id", uuid); + return; + } + } + owner.setString("Id", UUID.randomUUID().toString()); + } + // Paper end + + @Nullable + public PacketPlayOutTileEntityData getUpdatePacket() { + return new PacketPlayOutTileEntityData(this.position, 4, sanitizeTileEntityUUID(this.aa_())); // Paper + } + + public NBTTagCompound aa_() { + return this.save(new NBTTagCompound()); + } + + public void setGameProfile(@Nullable GameProfile gameprofile) { + this.a = gameprofile; + this.f(); + } + + private void f() { + // Spigot start + GameProfile profile = this.getGameProfile(); + b(profile, new Predicate() { + + @Override + public boolean apply(GameProfile input) { + a = input; + update(); + return false; + } + }, false); + // Spigot end + } + + // Spigot start - Support async lookups + public static Future b(final GameProfile gameprofile, final Predicate callback, boolean sync) { + if (gameprofile != null && !UtilColor.b(gameprofile.getName())) { + if (gameprofile.isComplete() && gameprofile.getProperties().containsKey("textures")) { + callback.apply(gameprofile); + } else if (MinecraftServer.getServer() == null) { + callback.apply(gameprofile); + } else { + GameProfile profile = skinCache.getIfPresent(gameprofile.getName().toLowerCase(java.util.Locale.ROOT)); + if (profile != null && Iterables.getFirst(profile.getProperties().get("textures"), (Object) null) != null) { + callback.apply(profile); + + return Futures.immediateFuture(profile); + } else { + Callable callable = new Callable() { + @Override + public GameProfile call() { + final GameProfile profile = skinCache.getUnchecked(gameprofile.getName().toLowerCase(java.util.Locale.ROOT)); + MinecraftServer.getServer().processQueue.add(new Runnable() { + @Override + public void run() { + if (profile == null) { + callback.apply(gameprofile); + } else { + callback.apply(profile); + } + } + }); + return profile; + } + }; + if (sync) { + try { + return Futures.immediateFuture(callable.call()); + } catch (Exception ex) { + com.google.common.base.Throwables.throwIfUnchecked(ex); + throw new RuntimeException(ex); // Not possible + } + } else { + return executor.submit(callable); + } + } + } + } else { + callback.apply(gameprofile); + } + + return Futures.immediateFuture(gameprofile); + } + // Spigot end + + // CraftBukkit start + public static void a(IBlockAccess iblockaccess, BlockPosition blockposition) { + setShouldDrop(iblockaccess, blockposition, false); + } + + public static void setShouldDrop(IBlockAccess iblockaccess, BlockPosition blockposition, boolean flag) { + // CraftBukkit end + TileEntity tileentity = iblockaccess.getTileEntity(blockposition); + + if (tileentity instanceof TileEntitySkull) { + TileEntitySkull tileentityskull = (TileEntitySkull) tileentity; + + tileentityskull.drop = flag; // CraftBukkit + } + + } + + public boolean shouldDrop() { + return this.drop; + } +} diff --git a/src/main/java/net/minecraft/server/UserCache.java b/src/main/java/net/minecraft/server/UserCache.java new file mode 100644 index 000000000000..059665836232 --- /dev/null +++ b/src/main/java/net/minecraft/server/UserCache.java @@ -0,0 +1,357 @@ +package net.minecraft.server; + +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.io.Files; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.mojang.authlib.Agent; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.GameProfileRepository; +import com.mojang.authlib.ProfileLookupCallback; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.Reader; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; +import javax.annotation.Nullable; +import org.apache.commons.io.IOUtils; + +public class UserCache { + + public static final SimpleDateFormat a = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); + private static boolean c; + private final Map d = Maps.newHashMap();private final Map nameCache = d; // Paper - OBFHELPER + private final Map e = Maps.newHashMap(); + private final Deque f = new java.util.concurrent.LinkedBlockingDeque(); // CraftBukkit + private final GameProfileRepository g; + protected final Gson b; + private final File h; + private static final ParameterizedType i = new ParameterizedType() { + public Type[] getActualTypeArguments() { + return new Type[] { UserCache.UserCacheEntry.class}; + } + + public Type getRawType() { + return List.class; + } + + public Type getOwnerType() { + return null; + } + }; + + public UserCache(GameProfileRepository gameprofilerepository, File file) { + this.g = gameprofilerepository; + this.h = file; + GsonBuilder gsonbuilder = new GsonBuilder(); + + gsonbuilder.registerTypeHierarchyAdapter(UserCache.UserCacheEntry.class, new UserCache.BanEntrySerializer()); + this.b = gsonbuilder.create(); + this.b(); + } + + private static GameProfile a(GameProfileRepository gameprofilerepository, String s) { + final GameProfile[] agameprofile = new GameProfile[1]; + ProfileLookupCallback profilelookupcallback = new ProfileLookupCallback() { + public void onProfileLookupSucceeded(GameProfile gameprofile) { + agameprofile[0] = gameprofile; + } + + public void onProfileLookupFailed(GameProfile gameprofile, Exception exception) { + agameprofile[0] = null; + } + }; + + gameprofilerepository.findProfilesByNames(new String[] { s}, Agent.MINECRAFT, profilelookupcallback); + if (!d() && agameprofile[0] == null && !org.apache.commons.lang3.StringUtils.isBlank(s)) { // Paper - Don't lookup a profile with a blank name + UUID uuid = EntityHuman.a(new GameProfile((UUID) null, s)); + GameProfile gameprofile = new GameProfile(uuid, s); + + profilelookupcallback.onProfileLookupSucceeded(gameprofile); + } + + return agameprofile[0]; + } + + public static void a(boolean flag) { + UserCache.c = flag; + } + + private static boolean d() { + return UserCache.c; + } + + public void a(GameProfile gameprofile) { + this.a(gameprofile, (Date) null); + } + + private synchronized void a(GameProfile gameprofile, Date date) { // Paper - synchronize + UUID uuid = gameprofile.getId(); + + if (date == null) { + Calendar calendar = Calendar.getInstance(); + + calendar.setTime(new Date()); + calendar.add(2, 1); + date = calendar.getTime(); + } + + UserCache.UserCacheEntry usercache_usercacheentry = new UserCache.UserCacheEntry(gameprofile, date); + + //if (this.e.containsKey(uuid)) { // Paper + UserCache.UserCacheEntry usercache_usercacheentry1 = (UserCache.UserCacheEntry) this.e.get(uuid); + if (usercache_usercacheentry1 != null) { // Paper + + this.d.remove(usercache_usercacheentry1.a().getName().toLowerCase(Locale.ROOT)); + this.f.remove(gameprofile); + } + + this.d.put(gameprofile.getName().toLowerCase(Locale.ROOT), usercache_usercacheentry); + this.e.put(uuid, usercache_usercacheentry); + this.f.addFirst(gameprofile); + if( !org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly ) this.c(); // Spigot - skip saving if disabled + } + + @Nullable + public synchronized GameProfile getProfile(String s) { // Paper - synchronize + String s1 = s.toLowerCase(Locale.ROOT); + UserCache.UserCacheEntry usercache_usercacheentry = (UserCache.UserCacheEntry) this.d.get(s1); + + if (usercache_usercacheentry != null && (new Date()).getTime() >= usercache_usercacheentry.c.getTime()) { + this.e.remove(usercache_usercacheentry.a().getId()); + this.d.remove(usercache_usercacheentry.a().getName().toLowerCase(Locale.ROOT)); + this.f.remove(usercache_usercacheentry.a()); + usercache_usercacheentry = null; + } + + GameProfile gameprofile; + + if (usercache_usercacheentry != null) { + gameprofile = usercache_usercacheentry.a(); + this.f.remove(gameprofile); + this.f.addFirst(gameprofile); + } else { + gameprofile = a(this.g, s); // Spigot - use correct case for offline players + if (gameprofile != null) { + this.a(gameprofile); + usercache_usercacheentry = (UserCache.UserCacheEntry) this.d.get(s1); + } + } + + if( !org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly ) this.c(); // Spigot - skip saving if disabled + return usercache_usercacheentry == null ? null : usercache_usercacheentry.a(); + } + + // Paper start + @Nullable public GameProfile getProfileIfCached(String name) { + UserCache.UserCacheEntry entry = this.nameCache.get(name.toLowerCase(Locale.ROOT)); + return entry == null ? null : entry.getProfile(); + } + // Paper end + + @Nullable public GameProfile getProfile(UUID uuid) { return a(uuid); } // Paper - OBFHELPER + @Nullable + public synchronized GameProfile a(UUID uuid) { // Paper - synchronize + UserCache.UserCacheEntry usercache_usercacheentry = (UserCache.UserCacheEntry) this.e.get(uuid); + + return usercache_usercacheentry == null ? null : usercache_usercacheentry.a(); + } + + private UserCache.UserCacheEntry b(UUID uuid) { + UserCache.UserCacheEntry usercache_usercacheentry = (UserCache.UserCacheEntry) this.e.get(uuid); + + if (usercache_usercacheentry != null) { + GameProfile gameprofile = usercache_usercacheentry.a(); + + this.f.remove(gameprofile); + this.f.addFirst(gameprofile); + } + + return usercache_usercacheentry; + } + + public void b() { + BufferedReader bufferedreader = null; + + try { + bufferedreader = Files.newReader(this.h, StandardCharsets.UTF_8); + List list = (List) ChatDeserializer.a(this.b, (Reader) bufferedreader, (Type) UserCache.i); + + this.d.clear(); + this.e.clear(); + this.f.clear(); + if (list != null) { + Iterator iterator = Lists.reverse(list).iterator(); + + while (iterator.hasNext()) { + UserCache.UserCacheEntry usercache_usercacheentry = (UserCache.UserCacheEntry) iterator.next(); + + if (usercache_usercacheentry != null) { + this.a(usercache_usercacheentry.a(), usercache_usercacheentry.b()); + } + } + } + } catch (FileNotFoundException filenotfoundexception) { + ; + // Spigot Start + } catch (com.google.gson.JsonSyntaxException ex) { + JsonList.a.warn( "Usercache.json is corrupted or has bad formatting. Deleting it to prevent further issues." ); + this.h.delete(); + // Spigot End + } catch (JsonParseException jsonparseexception) { + ; + } finally { + IOUtils.closeQuietly(bufferedreader); + } + + } + + // Paper start + public void c() { + c(true); + } + public void c(boolean asyncSave) { + // Paper end + String s = this.b.toJson(this.a(org.spigotmc.SpigotConfig.userCacheCap)); + Runnable save = () -> { + + BufferedWriter bufferedwriter = null; + + try { + bufferedwriter = Files.newWriter(this.h, StandardCharsets.UTF_8); + bufferedwriter.write(s); + return; + } catch (FileNotFoundException filenotfoundexception) { + return; + } catch (IOException ioexception) { + ; + } finally { + IOUtils.closeQuietly(bufferedwriter); + } + // Paper start + }; + if (asyncSave) { + MCUtil.scheduleAsyncTask(save); + } else { + save.run(); + } + // Paper end + + } + + private List a(int i) { + List list = Lists.newArrayList(); + List list1 = Lists.newArrayList(Iterators.limit(this.f.iterator(), i)); + Iterator iterator = list1.iterator(); + + while (iterator.hasNext()) { + GameProfile gameprofile = (GameProfile) iterator.next(); + UserCache.UserCacheEntry usercache_usercacheentry = this.b(gameprofile.getId()); + + if (usercache_usercacheentry != null) { + list.add(usercache_usercacheentry); + } + } + + return list; + } + + class UserCacheEntry { + + private final GameProfile b;public GameProfile getProfile() { return b; } // Paper - OBFHELPER + private final Date c; + + private UserCacheEntry(GameProfile gameprofile, Date date) { + this.b = gameprofile; + this.c = date; + } + + public GameProfile a() { + return this.b; + } + + public Date b() { + return this.c; + } + } + + class BanEntrySerializer implements JsonDeserializer, JsonSerializer { + + private BanEntrySerializer() {} + + public JsonElement serialize(UserCache.UserCacheEntry usercache_usercacheentry, Type type, JsonSerializationContext jsonserializationcontext) { + JsonObject jsonobject = new JsonObject(); + + jsonobject.addProperty("name", usercache_usercacheentry.a().getName()); + UUID uuid = usercache_usercacheentry.a().getId(); + + jsonobject.addProperty("uuid", uuid == null ? "" : uuid.toString()); + jsonobject.addProperty("expiresOn", UserCache.a.format(usercache_usercacheentry.b())); + return jsonobject; + } + + public UserCache.UserCacheEntry deserialize(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws JsonParseException { + if (jsonelement.isJsonObject()) { + JsonObject jsonobject = jsonelement.getAsJsonObject(); + JsonElement jsonelement1 = jsonobject.get("name"); + JsonElement jsonelement2 = jsonobject.get("uuid"); + JsonElement jsonelement3 = jsonobject.get("expiresOn"); + + if (jsonelement1 != null && jsonelement2 != null) { + String s = jsonelement2.getAsString(); + String s1 = jsonelement1.getAsString(); + Date date = null; + + if (jsonelement3 != null) { + try { + date = UserCache.a.parse(jsonelement3.getAsString()); + } catch (ParseException parseexception) { + date = null; + } + } + + if (s1 != null && s != null) { + UUID uuid; + + try { + uuid = UUID.fromString(s); + } catch (Throwable throwable) { + return null; + } + + return UserCache.this.new UserCacheEntry(new GameProfile(uuid, s1), date); + } else { + return null; + } + } else { + return null; + } + } else { + return null; + } + } + } +} diff --git a/src/main/java/net/minecraft/server/Village.java b/src/main/java/net/minecraft/server/Village.java new file mode 100644 index 000000000000..1363c53ff008 --- /dev/null +++ b/src/main/java/net/minecraft/server/Village.java @@ -0,0 +1,493 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mojang.authlib.GameProfile; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import javax.annotation.Nullable; + +public class Village { + + private World a; private World getWorld() { return a; } // Paper - OBFHELPER + private final List b = Lists.newArrayList(); + private BlockPosition c; + private BlockPosition d;private BlockPosition getCenter() { return d; } // Paper - OBFHELPER + private int e; + private int f; + private int g; + private int h; + private int i; + private final Map j; + private final List k; + private int l; + + private Village() { // Paper - Nothing should call this - world needs to be set. + this.c = BlockPosition.ZERO; + this.d = BlockPosition.ZERO; + this.j = Maps.newHashMap(); + this.k = Lists.newArrayList(); + } + + public Village(World world) { + this.c = BlockPosition.ZERO; + this.d = BlockPosition.ZERO; + this.j = Maps.newHashMap(); + this.k = Lists.newArrayList(); + this.a = world; + } + + public void a(World world) { + this.a = world; + } + + public void a(int i) { + // Paper - don't tick village if chunk isn't loaded + Chunk chunk = getWorld().getChunkIfLoaded(getCenter()); + if (chunk == null || !chunk.areNeighborsLoaded(1)) { + return; + } + // Paper end + this.g = i; + this.m(); + this.l(); + if (i % 20 == 0) { + this.k(); + } + + if (i % 30 == 0) { + this.j(); + } + + int j = this.h / 10; + + if (this.l < j && this.b.size() > 20 && this.a.random.nextInt(7000) == 0) { + Entity entity = this.f(this.d); + + if (entity != null) { + ++this.l; + } + } + + } + + @Nullable + private Entity f(BlockPosition blockposition) { + for (int i = 0; i < 10; ++i) { + BlockPosition blockposition1 = blockposition.a(this.a.random.nextInt(16) - 8, this.a.random.nextInt(6) - 3, this.a.random.nextInt(16) - 8); + + if (this.a(blockposition1)) { + EntityIronGolem entityirongolem = (EntityIronGolem) EntityTypes.IRON_GOLEM.b(this.a, (NBTTagCompound) null, (IChatBaseComponent) null, (EntityHuman) null, blockposition1, false, false); + + if (entityirongolem != null) { + if (entityirongolem.a((GeneratorAccess) this.a, false) && entityirongolem.a((IWorldReader) this.a)) { + this.a.addEntity(entityirongolem, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_DEFENSE); // CraftBukkit + return entityirongolem; + } + + entityirongolem.die(); + } + } + } + + return null; + } + + private void j() { + List list = this.a.a(EntityIronGolem.class, new AxisAlignedBB((double) (this.d.getX() - this.e), (double) (this.d.getY() - 4), (double) (this.d.getZ() - this.e), (double) (this.d.getX() + this.e), (double) (this.d.getY() + 4), (double) (this.d.getZ() + this.e))); + + this.l = list.size(); + } + + private void k() { + List list = this.a.a(EntityVillager.class, new AxisAlignedBB((double) (this.d.getX() - this.e), (double) (this.d.getY() - 4), (double) (this.d.getZ() - this.e), (double) (this.d.getX() + this.e), (double) (this.d.getY() + 4), (double) (this.d.getZ() + this.e))); + + this.h = list.size(); + if (this.h == 0) { + this.j.clear(); + } + + } + + public BlockPosition a() { + return this.d; + } + + public int b() { + return this.e; + } + + public int c() { + return this.b.size(); + } + + public int d() { + return this.g - this.f; + } + + public int e() { + return this.h; + } + + public boolean a(BlockPosition blockposition) { + return this.d.n(blockposition) < (double) (this.e * this.e); + } + + public List f() { + return this.b; + } + + public VillageDoor b(BlockPosition blockposition) { + VillageDoor villagedoor = null; + int i = Integer.MAX_VALUE; + Iterator iterator = this.b.iterator(); + + while (iterator.hasNext()) { + VillageDoor villagedoor1 = (VillageDoor) iterator.next(); + int j = villagedoor1.a(blockposition); + + if (j < i) { + villagedoor = villagedoor1; + i = j; + } + } + + return villagedoor; + } + + public VillageDoor c(BlockPosition blockposition) { + VillageDoor villagedoor = null; + int i = Integer.MAX_VALUE; + Iterator iterator = this.b.iterator(); + + while (iterator.hasNext()) { + VillageDoor villagedoor1 = (VillageDoor) iterator.next(); + int j = villagedoor1.a(blockposition); + + if (j > 256) { + j *= 1000; + } else { + j = villagedoor1.c(); + } + + if (j < i) { + BlockPosition blockposition1 = villagedoor1.d(); + EnumDirection enumdirection = villagedoor1.j(); + + if (this.a.getType(blockposition1.shift(enumdirection, 1)).a((IBlockAccess) this.a, blockposition1.shift(enumdirection, 1), PathMode.LAND) && this.a.getType(blockposition1.shift(enumdirection, -1)).a((IBlockAccess) this.a, blockposition1.shift(enumdirection, -1), PathMode.LAND) && this.a.getType(blockposition1.up().shift(enumdirection, 1)).a((IBlockAccess) this.a, blockposition1.up().shift(enumdirection, 1), PathMode.LAND) && this.a.getType(blockposition1.up().shift(enumdirection, -1)).a((IBlockAccess) this.a, blockposition1.up().shift(enumdirection, -1), PathMode.LAND)) { + villagedoor = villagedoor1; + i = j; + } + } + } + + return villagedoor; + } + + @Nullable + public VillageDoor e(BlockPosition blockposition) { + if (this.d.n(blockposition) > (double) (this.e * this.e)) { + return null; + } else { + Iterator iterator = this.b.iterator(); + + VillageDoor villagedoor; + + do { + if (!iterator.hasNext()) { + return null; + } + + villagedoor = (VillageDoor) iterator.next(); + } while (villagedoor.d().getX() != blockposition.getX() || villagedoor.d().getZ() != blockposition.getZ() || Math.abs(villagedoor.d().getY() - blockposition.getY()) > 1); + + return villagedoor; + } + } + + public void a(VillageDoor villagedoor) { + this.b.add(villagedoor); + this.c = this.c.a((BaseBlockPosition) villagedoor.d()); + this.n(); + this.f = villagedoor.h(); + } + + public boolean g() { + return this.b.isEmpty(); + } + + public void a(EntityLiving entityliving) { + Iterator iterator = this.k.iterator(); + + Village.Aggressor village_aggressor; + + do { + if (!iterator.hasNext()) { + this.k.add(new Village.Aggressor(entityliving, this.g)); + return; + } + + village_aggressor = (Village.Aggressor) iterator.next(); + } while (village_aggressor.a != entityliving); + + village_aggressor.b = this.g; + } + + @Nullable + public EntityLiving b(EntityLiving entityliving) { + double d0 = Double.MAX_VALUE; + Village.Aggressor village_aggressor = null; + + for (int i = 0; i < this.k.size(); ++i) { + Village.Aggressor village_aggressor1 = (Village.Aggressor) this.k.get(i); + double d1 = village_aggressor1.a.h(entityliving); + + if (d1 <= d0) { + village_aggressor = village_aggressor1; + d0 = d1; + } + } + + return village_aggressor == null ? null : village_aggressor.a; + } + + public EntityHuman c(EntityLiving entityliving) { + double d0 = Double.MAX_VALUE; + EntityHuman entityhuman = null; + Iterator iterator = this.j.keySet().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + if (this.d(s)) { + EntityHuman entityhuman1 = this.a.a(s); + + if (entityhuman1 != null) { + double d1 = entityhuman1.h(entityliving); + + if (d1 <= d0) { + entityhuman = entityhuman1; + d0 = d1; + } + } + } + } + + return entityhuman; + } + + private void l() { + Iterator iterator = this.k.iterator(); + + while (iterator.hasNext()) { + Village.Aggressor village_aggressor = (Village.Aggressor) iterator.next(); + + if (!village_aggressor.a.isAlive() || Math.abs(this.g - village_aggressor.b) > 300) { + iterator.remove(); + } + } + + } + + private void m() { + boolean flag = false; + boolean flag1 = this.a.random.nextInt(50) == 0; + Iterator iterator = this.b.iterator(); + + while (iterator.hasNext()) { + VillageDoor villagedoor = (VillageDoor) iterator.next(); + // Paper start - don't remove doors from unloaded chunks + if (!getWorld().isLoaded(villagedoor.getPosition())) { + villagedoor.setLastSeen(villagedoor.getLastSeen() + 1); + continue; + } + // Paper end + + if (flag1) { + villagedoor.a(); + } + + if (!this.g(villagedoor.d()) || Math.abs(this.g - villagedoor.h()) > 1200) { + this.c = this.c.b(villagedoor.d()); + flag = true; + villagedoor.a(true); + iterator.remove(); + } + } + + if (flag) { + this.n(); + } + + } + + private boolean g(BlockPosition blockposition) { + IBlockData iblockdata = this.a.paperConfig.villagesLoadChunks ? this.a.getType(blockposition) : this.a.getTypeIfLoaded(blockposition); // Paper + if (iblockdata == null) return false; // Paper + + Block block = iblockdata.getBlock(); + + return block instanceof BlockDoor ? iblockdata.getMaterial() == Material.WOOD : false; + } + + private void n() { + int i = this.b.size(); + + if (i == 0) { + this.d = BlockPosition.ZERO; + this.e = 0; + } else { + this.d = new BlockPosition(this.c.getX() / i, this.c.getY() / i, this.c.getZ() / i); + int j = 0; + + VillageDoor villagedoor; + + for (Iterator iterator = this.b.iterator(); iterator.hasNext(); j = Math.max(villagedoor.a(this.d), j)) { + villagedoor = (VillageDoor) iterator.next(); + } + + this.e = Math.max(32, (int) Math.sqrt((double) j) + 1); + } + } + + public int a(String s) { + Integer integer = (Integer) this.j.get(s); + + return integer == null ? 0 : integer; + } + + public int a(String s, int i) { + int j = this.a(s); + int k = MathHelper.clamp(j + i, -30, 10); + + this.j.put(s, k); + return k; + } + + public boolean d(String s) { + return this.a(s) <= -15; + } + + public void a(NBTTagCompound nbttagcompound) { + this.h = nbttagcompound.getInt("PopSize"); + this.e = nbttagcompound.getInt("Radius"); + this.l = nbttagcompound.getInt("Golems"); + this.f = nbttagcompound.getInt("Stable"); + this.g = nbttagcompound.getInt("Tick"); + this.i = nbttagcompound.getInt("MTick"); + this.d = new BlockPosition(nbttagcompound.getInt("CX"), nbttagcompound.getInt("CY"), nbttagcompound.getInt("CZ")); + this.c = new BlockPosition(nbttagcompound.getInt("ACX"), nbttagcompound.getInt("ACY"), nbttagcompound.getInt("ACZ")); + NBTTagList nbttaglist = nbttagcompound.getList("Doors", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.getCompound(i); + VillageDoor villagedoor = new VillageDoor(new BlockPosition(nbttagcompound1.getInt("X"), nbttagcompound1.getInt("Y"), nbttagcompound1.getInt("Z")), nbttagcompound1.getInt("IDX"), nbttagcompound1.getInt("IDZ"), nbttagcompound1.getInt("TS")); + + this.b.add(villagedoor); + } + + NBTTagList nbttaglist1 = nbttagcompound.getList("Players", 10); + + for (int j = 0; j < nbttaglist1.size(); ++j) { + NBTTagCompound nbttagcompound2 = nbttaglist1.getCompound(j); + + if (nbttagcompound2.hasKey("UUID") && this.a != null && this.a.getMinecraftServer() != null) { + UserCache usercache = this.a.getMinecraftServer().getUserCache(); + GameProfile gameprofile = usercache.a(UUID.fromString(nbttagcompound2.getString("UUID"))); + + if (gameprofile != null) { + this.j.put(gameprofile.getName(), nbttagcompound2.getInt("S")); + } + } else { + this.j.put(nbttagcompound2.getString("Name"), nbttagcompound2.getInt("S")); + } + } + + } + + public void b(NBTTagCompound nbttagcompound) { + nbttagcompound.setInt("PopSize", this.h); + nbttagcompound.setInt("Radius", this.e); + nbttagcompound.setInt("Golems", this.l); + nbttagcompound.setInt("Stable", this.f); + nbttagcompound.setInt("Tick", this.g); + nbttagcompound.setInt("MTick", this.i); + nbttagcompound.setInt("CX", this.d.getX()); + nbttagcompound.setInt("CY", this.d.getY()); + nbttagcompound.setInt("CZ", this.d.getZ()); + nbttagcompound.setInt("ACX", this.c.getX()); + nbttagcompound.setInt("ACY", this.c.getY()); + nbttagcompound.setInt("ACZ", this.c.getZ()); + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = this.b.iterator(); + + while (iterator.hasNext()) { + VillageDoor villagedoor = (VillageDoor) iterator.next(); + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.setInt("X", villagedoor.d().getX()); + nbttagcompound1.setInt("Y", villagedoor.d().getY()); + nbttagcompound1.setInt("Z", villagedoor.d().getZ()); + nbttagcompound1.setInt("IDX", villagedoor.f()); + nbttagcompound1.setInt("IDZ", villagedoor.g()); + nbttagcompound1.setInt("TS", villagedoor.h()); + nbttaglist.add((NBTBase) nbttagcompound1); + } + + nbttagcompound.set("Doors", nbttaglist); + NBTTagList nbttaglist1 = new NBTTagList(); + Iterator iterator1 = this.j.keySet().iterator(); + + while (iterator1.hasNext()) { + String s = (String) iterator1.next(); + NBTTagCompound nbttagcompound2 = new NBTTagCompound(); + UserCache usercache = this.a.getMinecraftServer().getUserCache(); + + try { + GameProfile gameprofile = usercache.getProfile(s); + + if (gameprofile != null) { + nbttagcompound2.setString("UUID", gameprofile.getId().toString()); + nbttagcompound2.setInt("S", (Integer) this.j.get(s)); + nbttaglist1.add((NBTBase) nbttagcompound2); + } + } catch (RuntimeException runtimeexception) { + ; + } + } + + nbttagcompound.set("Players", nbttaglist1); + } + + public void h() { + this.i = this.g; + } + + public boolean i() { + return this.i == 0 || this.g - this.i >= 3600; + } + + public void b(int i) { + Iterator iterator = this.j.keySet().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + this.a(s, i); + } + + } + + class Aggressor { + + public EntityLiving a; + public int b; + + Aggressor(EntityLiving entityliving, int i) { + this.a = entityliving; + this.b = i; + } + } +} diff --git a/src/main/java/net/minecraft/server/VillageDoor.java b/src/main/java/net/minecraft/server/VillageDoor.java new file mode 100644 index 000000000000..1edffc462920 --- /dev/null +++ b/src/main/java/net/minecraft/server/VillageDoor.java @@ -0,0 +1,96 @@ +package net.minecraft.server; + +public class VillageDoor { + + private final BlockPosition a; + private final BlockPosition b; + private final EnumDirection c; + private int d; + private boolean e; + private int f; + + public VillageDoor(BlockPosition blockposition, int i, int j, int k) { + this(blockposition, a(i, j), k); + } + + private static EnumDirection a(int i, int j) { + return i < 0 ? EnumDirection.WEST : (i > 0 ? EnumDirection.EAST : (j < 0 ? EnumDirection.NORTH : EnumDirection.SOUTH)); + } + + public VillageDoor(BlockPosition blockposition, EnumDirection enumdirection, int i) { + this.a = blockposition.h(); + this.c = enumdirection; + this.b = blockposition.shift(enumdirection, 2); + this.d = i; + } + + public int b(int i, int j, int k) { + return (int) this.a.distanceSquared((double) i, (double) j, (double) k); + } + + public int a(BlockPosition blockposition) { + return (int) blockposition.n(this.d()); + } + + public int b(BlockPosition blockposition) { + return (int) this.b.n(blockposition); + } + + public boolean c(BlockPosition blockposition) { + int i = blockposition.getX() - this.a.getX(); + int j = blockposition.getZ() - this.a.getY(); + + return i * this.c.getAdjacentX() + j * this.c.getAdjacentZ() >= 0; + } + + public void a() { + this.f = 0; + } + + public void b() { + ++this.f; + } + + public int c() { + return this.f; + } + + public BlockPosition getPosition() { return d(); } // Paper - OBFHELPER + public BlockPosition d() { + return this.a; + } + + public BlockPosition e() { + return this.b; + } + + public int f() { + return this.c.getAdjacentX() * 2; + } + + public int g() { + return this.c.getAdjacentZ() * 2; + } + + public int getLastSeen() { return h(); } // Paper - OBFHELPER + public int h() { + return this.d; + } + + public void setLastSeen(int i) { a(i); } // Paper - OBFHELPER + public void a(int i) { + this.d = i; + } + + public boolean i() { + return this.e; + } + + public void a(boolean flag) { + this.e = flag; + } + + public EnumDirection j() { + return this.c; + } +} diff --git a/src/main/java/net/minecraft/server/VillageSiege.java b/src/main/java/net/minecraft/server/VillageSiege.java new file mode 100644 index 000000000000..509d62f6b60a --- /dev/null +++ b/src/main/java/net/minecraft/server/VillageSiege.java @@ -0,0 +1,166 @@ +package net.minecraft.server; + +import com.destroystokyo.paper.exception.ServerInternalException; + +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; + +public class VillageSiege { + + private final World a; + private boolean b; + private int c = -1; + private int d; + private int e; + private Village f; + private int g; + private int h; + private int i; + + public VillageSiege(World world) { + this.a = world; + } + + public void a() { + if (this.a.L()) { + this.c = 0; + } else if (this.c != 2) { + if (this.c == 0) { + float f = this.a.k(0.0F); + + if ((double) f < 0.5D || (double) f > 0.501D) { + return; + } + + this.c = this.a.random.nextInt(10) == 0 ? 1 : 2; + this.b = false; + if (this.c == 2) { + return; + } + } + + if (this.c != -1) { + if (!this.b) { + if (!this.b()) { + return; + } + + this.b = true; + } + + if (this.e > 0) { + --this.e; + } else { + this.e = 2; + if (this.d > 0) { + this.c(); + --this.d; + } else { + this.c = 2; + } + + } + } + } + } + + private boolean b() { + List list = this.a.players; + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + if (!entityhuman.isSpectator()) { + this.f = this.a.af().getClosestVillage(new BlockPosition(entityhuman), 1); + if (this.f != null && this.f.c() >= 10 && this.f.d() >= 20 && this.f.e() >= 20) { + BlockPosition blockposition = this.f.a(); + float f = (float) this.f.b(); + boolean flag = false; + int i = 0; + + while (true) { + if (i < 10) { + float f1 = this.a.random.nextFloat() * 6.2831855F; + + this.g = blockposition.getX() + (int) ((double) (MathHelper.cos(f1) * f) * 0.9D); + this.h = blockposition.getY(); + this.i = blockposition.getZ() + (int) ((double) (MathHelper.sin(f1) * f) * 0.9D); + flag = false; + Iterator iterator1 = this.a.af().getVillages().iterator(); + + while (iterator1.hasNext()) { + Village village = (Village) iterator1.next(); + + if (village != this.f && village.a(new BlockPosition(this.g, this.h, this.i))) { + flag = true; + break; + } + } + + if (flag) { + ++i; + continue; + } + } + + if (flag) { + return false; + } + + Vec3D vec3d = this.a(new BlockPosition(this.g, this.h, this.i)); + + if (vec3d != null) { + this.e = 0; + this.d = 20; + return true; + } + break; + } + } + } + } + + return false; + } + + private boolean c() { + Vec3D vec3d = this.a(new BlockPosition(this.g, this.h, this.i)); + + if (vec3d == null) { + return false; + } else { + EntityZombie entityzombie; + + try { + entityzombie = EntityTypes.ZOMBIE.create(this.a); // Paper + entityzombie.prepare(this.a.getDamageScaler(new BlockPosition(entityzombie)), (GroupDataEntity) null, (NBTTagCompound) null); + } catch (Exception exception) { + exception.printStackTrace(); + ServerInternalException.reportInternalException(exception); // Paper + return false; + } + + entityzombie.setPositionRotation(vec3d.x, vec3d.y, vec3d.z, this.a.random.nextFloat() * 360.0F, 0.0F); + this.a.addEntity(entityzombie, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_INVASION); // CraftBukkit + BlockPosition blockposition = this.f.a(); + + entityzombie.a(blockposition, this.f.b()); + return true; + } + } + + @Nullable + private Vec3D a(BlockPosition blockposition) { + for (int i = 0; i < 10; ++i) { + BlockPosition blockposition1 = blockposition.a(this.a.random.nextInt(16) - 8, this.a.random.nextInt(6) - 3, this.a.random.nextInt(16) - 8); + + if (this.f.a(blockposition1) && SpawnerCreature.a(EntityPositionTypes.Surface.ON_GROUND, this.a, blockposition1, (EntityTypes) null)) { + return new Vec3D((double) blockposition1.getX(), (double) blockposition1.getY(), (double) blockposition1.getZ()); + } + } + + return null; + } +} diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java new file mode 100644 index 000000000000..4d3f88431f82 --- /dev/null +++ b/src/main/java/net/minecraft/server/World.java @@ -0,0 +1,3115 @@ + package net.minecraft.server; + +import co.aikar.timings.Timings; +import com.destroystokyo.paper.antixray.ChunkPacketBlockController; // Paper - Anti-Xray +import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray; // Paper - Anti-Xray +import com.destroystokyo.paper.event.server.ServerExceptionEvent; +import com.destroystokyo.paper.exception.ServerInternalException; +import com.google.common.base.MoreObjects; +import com.google.common.collect.Lists; +import it.unimi.dsi.fastutil.longs.LongSet; +import it.unimi.dsi.fastutil.longs.LongSets; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import com.google.common.collect.Maps; +import java.util.ArrayList; +import java.util.HashMap; // Paper +import java.util.Map; +import org.bukkit.Bukkit; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.block.CraftBlockState; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.util.LongHashSet; // Paper +import org.bukkit.event.block.BlockPhysicsEvent; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.generator.ChunkGenerator; +// CraftBukkit end + +public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAccess, AutoCloseable, Cloneable { // Paper + + protected static final Logger e = LogManager.getLogger(); + private static final EnumDirection[] a = EnumDirection.values(); + private int b = 63; + // Spigot start - guard entity list from removals + public final com.destroystokyo.paper.PaperWorldEntityList entityList = new com.destroystokyo.paper.PaperWorldEntityList(this); + /* // Paper start + { + @Override + public Entity remove(int index) + { + guard(); + return super.remove( index ); + } + + @Override + public boolean remove(Object o) + { + guard(); + return super.remove( o ); + } + + private void guard() + { + if ( guardEntityList ) + { + throw new java.util.ConcurrentModificationException(); + } + } + }; + */ // Paper end + // Spigot end + protected final Set g = com.google.common.collect.Sets.newHashSet(); public Set getEntityUnloadQueue() { return g; };// Paper - OBFHELPER + //public final List tileEntityList = Lists.newArrayList(); // Paper - remove unused list + public final List tileEntityListTick = Lists.newArrayList(); + private final List c = Lists.newArrayList(); + private final Set tileEntityListUnload = com.google.common.collect.Sets.newHashSet(); // Paper + public final List players = Lists.newArrayList(); + public final Map playersByName = Maps.newHashMap(); // Paper - World EntityHuman Lookup Optimizations + public final List k = Lists.newArrayList(); + protected final IntHashMap entitiesById = new IntHashMap<>(); + private final long F = 16777215L; + private int G; public int getSkylightSubtracted() { return this.G; } public void setSkylightSubtracted(int value) { this.G = value;} // Paper - OBFHELPER + protected int m = (new Random()).nextInt(); + protected final int n = 1013904223; + protected float o; + protected float p; + protected float q; + protected float r; + private int H; + public final Random random = new Random(); + public WorldProvider worldProvider; + protected NavigationListener u = new NavigationListener(); + protected List v; + protected IChunkProvider chunkProvider; + protected final IDataManager dataManager; + public WorldData worldData; + @Nullable + public final PersistentCollection worldMaps; + protected PersistentVillage villages; + public final MethodProfiler methodProfiler; + public final boolean isClientSide; + // Paper start - yes this is hacky as shit + RegionLimitedWorldAccess regionLimited; + World originalWorld; + public World regionLimited(RegionLimitedWorldAccess limitedWorldAccess) { + try { + World clone = (World) super.clone(); + clone.regionLimited = limitedWorldAccess; + clone.originalWorld = this; + return clone; + } catch (CloneNotSupportedException e1) { + } + return null; + } + ChunkCoordIntPair[] strongholdCoords; + final java.util.concurrent.atomic.AtomicBoolean + strongholdInit = new java.util.concurrent.atomic.AtomicBoolean + (false); + // Paper end + public boolean allowMonsters; + public boolean allowAnimals; + private boolean J; + private final WorldBorder K; + int[] E; + + // CraftBukkit start Added the following + private final CraftWorld world; + public boolean pvpMode; + public boolean keepSpawnInMemory = true; + public ChunkGenerator generator; + public static final boolean DEBUG_ENTITIES = Boolean.getBoolean("debug.entities"); // Paper + + public boolean captureBlockStates = false; + public boolean captureTreeGeneration = false; + public ArrayList capturedBlockStates = new ArrayList() { + @Override + public boolean add(CraftBlockState blockState) { + Iterator blockStateIterator = this.iterator(); + while (blockStateIterator.hasNext()) { + BlockState blockState1 = blockStateIterator.next(); + if (blockState1.getLocation().equals(blockState.getLocation())) { + return false; + } + } + + return super.add(blockState); + } + }; + public List captureDrops; + public long ticksPerAnimalSpawns; + public long ticksPerMonsterSpawns; + public boolean populating; + private int tickPosition; + public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot + + public final com.destroystokyo.paper.PaperWorldConfig paperConfig; // Paper + public final ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray + + public final co.aikar.timings.WorldTimingsHandler timings; // Paper + public boolean guardEntityList; // Spigot // Paper - public + public static BlockPosition lastPhysicsProblem; // Spigot + public static boolean haveWeSilencedAPhysicsCrash; + public static String blockLocation; + private org.spigotmc.TickLimiter entityLimiter; + private org.spigotmc.TickLimiter tileLimiter; + private int tileTickPosition; + public final Map explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions + + public CraftWorld getWorld() { + return this.world; + } + + public CraftServer getServer() { + return (CraftServer) Bukkit.getServer(); + } + + public Chunk getChunkIfLoaded(int x, int z) { + return ((ChunkProviderServer) this.chunkProvider).chunks.get(ChunkCoordIntPair.a(x, z)); // Paper - optimize getChunkIfLoaded + } + + protected World(IDataManager idatamanager, @Nullable PersistentCollection persistentcollection, WorldData worlddata, WorldProvider worldprovider, MethodProfiler methodprofiler, boolean flag, ChunkGenerator gen, org.bukkit.World.Environment env) { + this.spigotConfig = new org.spigotmc.SpigotWorldConfig( worlddata.getName() ); // Spigot + this.paperConfig = new com.destroystokyo.paper.PaperWorldConfig(worlddata.getName(), this.spigotConfig); // Paper + this.chunkPacketBlockController = this.paperConfig.antiXray ? new ChunkPacketBlockControllerAntiXray(this.paperConfig) : ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray + this.generator = gen; + this.world = new CraftWorld((WorldServer) this, gen, env); + this.ticksPerAnimalSpawns = this.getServer().getTicksPerAnimalSpawns(); // CraftBukkit + this.ticksPerMonsterSpawns = this.getServer().getTicksPerMonsterSpawns(); // CraftBukkit + // CraftBukkit end + this.v = Lists.newArrayList(new IWorldAccess[] { this.u}); + this.allowMonsters = true; + this.allowAnimals = true; + this.E = new int['\u8000']; + this.dataManager = idatamanager; + this.worldMaps = persistentcollection; + this.methodProfiler = methodprofiler; + this.worldData = worlddata; + this.worldProvider = worldprovider; + this.isClientSide = flag; + this.K = worldprovider.getWorldBorder(); + // CraftBukkit start + getWorldBorder().world = (WorldServer) this; + // From PlayerList.setPlayerFileData + getWorldBorder().a(new IWorldBorderListener() { + public void a(WorldBorder worldborder, double d0) { + getServer().getHandle().sendAll(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_SIZE), worldborder.world); + } + + public void a(WorldBorder worldborder, double d0, double d1, long i) { + getServer().getHandle().sendAll(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.LERP_SIZE), worldborder.world); + } + + public void a(WorldBorder worldborder, double d0, double d1) { + getServer().getHandle().sendAll(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_CENTER), worldborder.world); + } + + public void a(WorldBorder worldborder, int i) { + getServer().getHandle().sendAll(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_WARNING_TIME), worldborder.world); + } + + public void b(WorldBorder worldborder, int i) { + getServer().getHandle().sendAll(new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_WARNING_BLOCKS), worldborder.world); + } + + public void b(WorldBorder worldborder, double d0) {} + + public void c(WorldBorder worldborder, double d0) {} + }); + this.getServer().addWorld(this.world); + // CraftBukkit end + timings = new co.aikar.timings.WorldTimingsHandler(this); // Paper - code below can generate new world and access timings + this.keepSpawnInMemory = this.paperConfig.keepSpawnInMemory; // Paper + this.entityLimiter = new org.spigotmc.TickLimiter(spigotConfig.entityMaxTickTime); + this.tileLimiter = new org.spigotmc.TickLimiter(spigotConfig.tileMaxTickTime); + } + + public BiomeBase getBiome(BlockPosition blockposition) { + if (this.isLoaded(blockposition)) { + Chunk chunk = this.getChunkAtWorldCoords(blockposition); + + try { + return chunk.getBiome(blockposition); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Getting biome"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Coordinates of biome request"); + + crashreportsystemdetails.a("Location", () -> { + return CrashReportSystemDetails.a(blockposition); + }); + throw new ReportedException(crashreport); + } + } else { + return this.chunkProvider.getChunkGenerator().getWorldChunkManager().getBiome(blockposition, Biomes.PLAINS); + } + } + + protected abstract IChunkProvider r(); + + public void a(WorldSettings worldsettings) { + this.worldData.d(true); + } + + public boolean e() { + return this.isClientSide; + } + + @Nullable + public MinecraftServer getMinecraftServer() { + return null; + } + + public IBlockData i(BlockPosition blockposition) { + BlockPosition blockposition1; + + for (blockposition1 = new BlockPosition(blockposition.getX(), this.getSeaLevel(), blockposition.getZ()); !this.isEmpty(blockposition1.up()); blockposition1 = blockposition1.up()) { + ; + } + + return this.getType(blockposition1); + } + + public static boolean isValidLocation(BlockPosition blockposition) { + return blockposition.isValidLocation(); // Paper + } + + public static boolean k(BlockPosition blockposition) { + return blockposition.isInvalidYLocation(); // Paper + } + + public boolean isEmpty(BlockPosition blockposition) { + return this.getType(blockposition).isAir(); + } + + public boolean isLoaded(BlockPosition blockposition) { + return getChunkIfLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4) != null; // Paper + } + + // Paper start + public boolean isLoadedAndInBounds(BlockPosition blockposition) { + return getWorldBorder().isInBounds(blockposition) && getChunkIfLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4) != null; + } + public Chunk getChunkIfLoaded(BlockPosition blockposition) { + return getChunkIfLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4); + } + // test if meets light level, return faster + // logic copied from below + public boolean isLightLevel(BlockPosition blockposition, int level) { + if (blockposition.isValidLocation()) { + if (this.getType(blockposition).c(this, blockposition)) { + int sky = getSkylightSubtracted(); + if (this.getLightLevel(blockposition.up(), sky) >= level) { + return true; + } + if (this.getLightLevel(blockposition.east(), sky) >= level) { + return true; + } + if (this.getLightLevel(blockposition.west(), sky) >= level) { + return true; + } + if (this.getLightLevel(blockposition.south(), sky) >= level) { + return true; + } + if (this.getLightLevel(blockposition.north(), sky) >= level) { + return true; + } + return false; + } else { + if (blockposition.getY() >= 256) { + blockposition = new BlockPosition(blockposition.getX(), 255, blockposition.getZ()); + } + + Chunk chunk = this.getChunkAtWorldCoords(blockposition); + return chunk.getLightSubtracted(blockposition, this.getSkylightSubtracted()) >= level; + } + } else { + return true; + } + } + // reduces need to do isLoaded before getType + public IBlockData getTypeIfLoadedAndInBounds(BlockPosition blockposition) { + return getWorldBorder().isInBounds(blockposition) ? getTypeIfLoaded(blockposition) : null; + } + public IBlockData getTypeIfLoaded(BlockPosition blockposition) { + // CraftBukkit start - tree generation + if (captureTreeGeneration) { + for (CraftBlockState previous : capturedBlockStates) { + if (previous.getX() == blockposition.getX() && previous.getY() == blockposition.getY() && previous.getZ() == blockposition.getZ()) { + return previous.getHandle(); + } + } + } + // CraftBukkit end + Chunk chunk = this.getChunkIfLoaded(blockposition); + if (chunk != null) { + return blockposition.isValidLocation() ? chunk.getBlockData(blockposition) : Blocks.AIR.getBlockData(); // Paper + } + return null; + } + public Block getBlockIfLoaded(BlockPosition blockposition) { + IBlockData type = getTypeIfLoaded(blockposition); + if (type == null) { + return null; + } + return type.getBlock(); + } + public Material getMaterialIfLoaded(BlockPosition blockposition) { + IBlockData type = getTypeIfLoaded(blockposition); + if (type == null) { + return null; + } + return type.getBlock().material; + } + // Paper end + + public Chunk getChunkAtWorldCoords(BlockPosition blockposition) { + return this.getChunkAt(blockposition.getX() >> 4, blockposition.getZ() >> 4); + } + + public Chunk getChunkAt(int i, int j) { + Chunk chunk = this.chunkProvider.getChunkAt(i, j, true, true); + + if (chunk == null) { + throw new IllegalStateException("Should always be able to create a chunk!"); + } else { + return chunk; + } + } + + public boolean setTypeAndData(BlockPosition blockposition, IBlockData iblockdata, int i) { + // CraftBukkit start - tree generation + if (this.captureTreeGeneration) { + CraftBlockState blockstate = null; + Iterator it = capturedBlockStates.iterator(); + while (it.hasNext()) { + CraftBlockState previous = it.next(); + if (previous.getPosition().equals(blockposition)) { + blockstate = previous; + it.remove(); + break; + } + } + if (blockstate == null) { + blockstate = org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(this, blockposition, i); + } + blockstate.setData(iblockdata); + this.capturedBlockStates.add(blockstate); + return true; + } + // CraftBukkit end + if (blockposition.isInvalidYLocation()) { // Paper + return false; + } else if (!this.isClientSide && this.worldData.getType() == WorldType.DEBUG_ALL_BLOCK_STATES) { + return false; + } else { + Chunk chunk = this.getChunkAtWorldCoords(blockposition); + Block block = iblockdata.getBlock(); + + // CraftBukkit start - capture blockstates + CraftBlockState blockstate = null; + if (this.captureBlockStates) { + blockstate = (CraftBlockState) world.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()).getState(); // Paper - use CB getState to get a suitable snapshot + this.capturedBlockStates.add(blockstate); + } + // CraftBukkit end + + IBlockData iblockdata1 = chunk.setType(blockposition, iblockdata, (i & 64) != 0, (i & 1024) == 0); // CraftBukkit custom NO_PLACE flag + this.chunkPacketBlockController.onBlockChange(this, blockposition, iblockdata, iblockdata1, i); // Paper - Anti-Xray + + if (iblockdata1 == null) { + // CraftBukkit start - remove blockstate if failed + if (this.captureBlockStates) { + this.capturedBlockStates.remove(blockstate); + } + // CraftBukkit end + return false; + } else { + IBlockData iblockdata2 = this.getType(blockposition); + + if (iblockdata2.b(this, blockposition) != iblockdata1.b(this, blockposition) || iblockdata2.e() != iblockdata1.e()) { + this.methodProfiler.enter("checkLight"); + chunk.runOrQueueLightUpdate(() -> this.r(blockposition)); // Paper - Queue light update + this.methodProfiler.exit(); + } + + /* + if (iblockdata2 == iblockdata) { + if (iblockdata1 != iblockdata2) { + this.a(blockposition, blockposition); + } + + if ((i & 2) != 0 && (!this.isClientSide || (i & 4) == 0) && chunk.isReady()) { + this.notify(blockposition, iblockdata1, iblockdata, i); + } + + if (!this.isClientSide && (i & 1) != 0) { + this.update(blockposition, iblockdata1.getBlock()); + if (iblockdata.isComplexRedstone()) { + this.updateAdjacentComparators(blockposition, block); + } + } + + if ((i & 16) == 0) { + int j = i & -2; + + iblockdata1.b(this, blockposition, j); + iblockdata.a((GeneratorAccess) this, blockposition, j); + iblockdata.b(this, blockposition, j); + } + } + */ + + // CraftBukkit start + if (!this.captureBlockStates) { // Don't notify clients or update physics while capturing blockstates + // Modularize client and physic updates + // Spigot start + try { + notifyAndUpdatePhysics(blockposition, chunk, iblockdata1, iblockdata, iblockdata2, i); + } catch (StackOverflowError ex) { + lastPhysicsProblem = new BlockPosition(blockposition); + } + // Spigot end + } + // CraftBukkit end + + return true; + } + } + } + + // CraftBukkit start - Split off from above in order to directly send client and physic updates + public void notifyAndUpdatePhysics(BlockPosition blockposition, Chunk chunk, IBlockData oldBlock, IBlockData newBlock, IBlockData actualBlock, int i) { + IBlockData iblockdata = newBlock; + IBlockData iblockdata1 = oldBlock; + IBlockData iblockdata2 = actualBlock; + if (iblockdata2 == iblockdata) { + if (iblockdata1 != iblockdata2) { + this.a(blockposition, blockposition); + } + + if ((i & 2) != 0 && (!this.isClientSide || (i & 4) == 0) && (chunk == null || chunk.isReady())) { // allow chunk to be null here as chunk.isReady() is false when we send our notification during block placement + this.notify(blockposition, iblockdata1, iblockdata, i); + } + + if (!this.isClientSide && (i & 1) != 0) { + this.update(blockposition, iblockdata1.getBlock()); + if (iblockdata.isComplexRedstone()) { + this.updateAdjacentComparators(blockposition, newBlock.getBlock()); + } + } + + if ((i & 16) == 0) { + int j = i & -2; + + // CraftBukkit start + iblockdata1.b(this, blockposition, j); // Don't call an event for the old block to limit event spam + CraftWorld world = ((WorldServer) this).getWorld(); + if (world != null && ((WorldServer)this).hasPhysicsEvent) { // Paper + BlockPhysicsEvent event = new BlockPhysicsEvent(world.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), CraftBlockData.fromData(iblockdata)); + this.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + } + // CraftBukkit end + iblockdata.a((GeneratorAccess) this, blockposition, j); + iblockdata.b(this, blockposition, j); + } + } + } + // CraftBukkit end + + public boolean setAir(BlockPosition blockposition) { + Fluid fluid = this.getFluid(blockposition); + + return this.setTypeAndData(blockposition, fluid.i(), 3); + } + + public boolean setAir(BlockPosition blockposition, boolean flag) { + IBlockData iblockdata = this.getType(blockposition); + + if (iblockdata.isAir()) { + return false; + } else { + Fluid fluid = this.getFluid(blockposition); + // Paper start - while the above setAir method is named same and looks very similar + // they are NOT used with same intent and the above should not fire this event. The above method is more of a BlockSetToAirEvent, + // it doesn't imply destruction of a block that plays a sound effect / drops an item. + boolean playEffect = true; + if (com.destroystokyo.paper.event.block.BlockDestroyEvent.getHandlerList().getRegisteredListeners().length > 0) { + com.destroystokyo.paper.event.block.BlockDestroyEvent event = new com.destroystokyo.paper.event.block.BlockDestroyEvent(MCUtil.toBukkitBlock(this, blockposition), fluid.i().createCraftBlockData(), flag); + if (!event.callEvent()) { + return false; + } + playEffect = event.playEffect(); + } + // Paper end + + if (playEffect) this.triggerEffect(2001, blockposition, Block.getCombinedId(iblockdata)); // Paper + if (flag) { + iblockdata.a(this, blockposition, 0); + } + + return this.setTypeAndData(blockposition, fluid.i(), 3); + } + } + + public boolean setTypeUpdate(BlockPosition blockposition, IBlockData iblockdata) { + return this.setTypeAndData(blockposition, iblockdata, 3); + } + + public void notify(BlockPosition blockposition, IBlockData iblockdata, IBlockData iblockdata1, int i) { + for (int j = 0; j < this.v.size(); ++j) { + ((IWorldAccess) this.v.get(j)).a(this, blockposition, iblockdata, iblockdata1, i); + } + + } + + public void update(BlockPosition blockposition, Block block) { + if (this.worldData.getType() != WorldType.DEBUG_ALL_BLOCK_STATES) { + // CraftBukkit start + if (populating) { + return; + } + // CraftBukkit end + this.applyPhysics(blockposition, block); + } + + } + + public void a(int i, int j, int k, int l) { + int i1; + + if (k > l) { + i1 = l; + l = k; + k = i1; + } + + if (this.worldProvider.g()) { + Chunk chunk = getChunkIfLoaded(i >> 4, j >> 4); // Paper + for (i1 = k; chunk != null && i1 <= l; ++i1) { // Paper + this.updateBrightness(EnumSkyBlock.SKY, new BlockPosition(i, i1, j), chunk); // Paper + } + } + + this.a(i, k, j, i, l, j); + } + + public void a(BlockPosition blockposition, BlockPosition blockposition1) { + this.a(blockposition.getX(), blockposition.getY(), blockposition.getZ(), blockposition1.getX(), blockposition1.getY(), blockposition1.getZ()); + } + + public void a(int i, int j, int k, int l, int i1, int j1) { + for (int k1 = 0; k1 < this.v.size(); ++k1) { + ((IWorldAccess) this.v.get(k1)).a(i, j, k, l, i1, j1); + } + + } + + public void applyPhysics(BlockPosition blockposition, Block block) { + if (captureBlockStates) { return; } // Paper - Cancel all physics during placement + this.a(blockposition.west(), block, blockposition); + this.a(blockposition.east(), block, blockposition); + this.a(blockposition.down(), block, blockposition); + this.a(blockposition.up(), block, blockposition); + this.a(blockposition.north(), block, blockposition); + this.a(blockposition.south(), block, blockposition); + } + + public void a(BlockPosition blockposition, Block block, EnumDirection enumdirection) { + if (enumdirection != EnumDirection.WEST) { + this.a(blockposition.west(), block, blockposition); + } + + if (enumdirection != EnumDirection.EAST) { + this.a(blockposition.east(), block, blockposition); + } + + if (enumdirection != EnumDirection.DOWN) { + this.a(blockposition.down(), block, blockposition); + } + + if (enumdirection != EnumDirection.UP) { + this.a(blockposition.up(), block, blockposition); + } + + if (enumdirection != EnumDirection.NORTH) { + this.a(blockposition.north(), block, blockposition); + } + + if (enumdirection != EnumDirection.SOUTH) { + this.a(blockposition.south(), block, blockposition); + } + + } + + public void neighborChanged(BlockPosition pos, Block blockIn, BlockPosition fromPos) { a(pos, blockIn, fromPos); } // Paper - OBFHELPER + public void a(BlockPosition blockposition, Block block, BlockPosition blockposition1) { + if (!this.isClientSide) { + IBlockData iblockdata = this.getType(blockposition); + + try { + // CraftBukkit start + CraftWorld world = ((WorldServer) this).getWorld(); + if (world != null && ((WorldServer)this).hasPhysicsEvent) { // Paper + BlockPhysicsEvent event = new BlockPhysicsEvent(world.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), CraftBlockData.fromData(iblockdata), world.getBlockAt(blockposition1.getX(), blockposition1.getY(), blockposition1.getZ())); + this.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + } + // CraftBukkit end + iblockdata.doPhysics(this, blockposition, block, blockposition1); + // Spigot Start + } catch (StackOverflowError ex) { + lastPhysicsProblem = new BlockPosition(blockposition); + // Spigot End + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Exception while updating neighbours"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Block being updated"); + + crashreportsystemdetails.a("Source block type", () -> { + try { + return String.format("ID #%s (%s // %s)", IRegistry.BLOCK.getKey(block), block.m(), block.getClass().getCanonicalName()); + } catch (Throwable throwable1) { + return "ID #" + IRegistry.BLOCK.getKey(block); + } + }); + CrashReportSystemDetails.a(crashreportsystemdetails, blockposition, iblockdata); + throw new ReportedException(crashreport); + } + } + } + + public boolean e(BlockPosition blockposition) { + return this.getChunkAtWorldCoords(blockposition).c(blockposition); + } + + public int getLightLevel(BlockPosition blockposition, int i) { + if (blockposition.getX() >= -30000000 && blockposition.getZ() >= -30000000 && blockposition.getX() < 30000000 && blockposition.getZ() < 30000000) { + if (blockposition.getY() < 0) { + return 0; + } else { + if (blockposition.getY() >= 256) { + blockposition = new BlockPosition(blockposition.getX(), 255, blockposition.getZ()); + } + if (!this.isLoaded(blockposition)) return 0; // Paper + + return this.getChunkAtWorldCoords(blockposition).a(blockposition, i); + } + } else { + return 15; + } + } + + public int a(HeightMap.Type heightmap_type, int i, int j) { + int k; + + if (i >= -30000000 && j >= -30000000 && i < 30000000 && j < 30000000) { + if (this.isChunkLoaded(i >> 4, j >> 4, true)) { + k = this.getChunkAt(i >> 4, j >> 4).a(heightmap_type, i & 15, j & 15) + 1; + } else { + k = 0; + } + } else { + k = this.getSeaLevel() + 1; + } + + return k; + } + + @Deprecated + public int d(int i, int j) { + if (i >= -30000000 && j >= -30000000 && i < 30000000 && j < 30000000) { + if (!this.isChunkLoaded(i >> 4, j >> 4, true)) { + return 0; + } else { + Chunk chunk = this.getChunkAt(i >> 4, j >> 4); + + return chunk.D(); + } + } else { + return this.getSeaLevel() + 1; + } + } + + public int getBrightness(EnumSkyBlock enumskyblock, BlockPosition blockposition) { + if (blockposition.getY() < 0) { + blockposition = new BlockPosition(blockposition.getX(), 0, blockposition.getZ()); + } + + Chunk chunk; // Paper + return !blockposition.isValidLocation() ? enumskyblock.c : ((chunk = this.getChunkIfLoaded(blockposition)) == null ? enumskyblock.c : chunk.getBrightness(enumskyblock, blockposition)); // Paper - optimize ifChunkLoaded + } + + public void a(EnumSkyBlock enumskyblock, BlockPosition blockposition, int i) { + if (blockposition.isValidLocation()) { // Paper + if (this.isLoaded(blockposition)) { + this.getChunkAtWorldCoords(blockposition).a(enumskyblock, blockposition, i); + this.m(blockposition); + } + } + } + + public void m(BlockPosition blockposition) { + for (int i = 0; i < this.v.size(); ++i) { + ((IWorldAccess) this.v.get(i)).a(blockposition); + } + + } + + // Paper - async variant + public java.util.concurrent.CompletableFuture getTypeAsync(BlockPosition blockposition) { + int x = blockposition.getX(); + int z = blockposition.getZ(); + if (captureTreeGeneration) { + Iterator it = capturedBlockStates.iterator(); + while (it.hasNext()) { + CraftBlockState previous = it.next(); + if (previous.getX() == x && previous.getY() == blockposition.getY() && previous.getZ() == z) { + return java.util.concurrent.CompletableFuture.completedFuture(previous.getHandle()); + } + } + } + if (blockposition.isInvalidYLocation()) { + return java.util.concurrent.CompletableFuture.completedFuture(Blocks.VOID_AIR.getBlockData()); + } else { + java.util.concurrent.CompletableFuture future = new java.util.concurrent.CompletableFuture<>(); + ((ChunkProviderServer) chunkProvider).getChunkAt(x << 4, z << 4, true, true, (chunk) -> { + future.complete(chunk.getType(blockposition)); + }); + return future; + } + } + // Paper end + + public IBlockData getType(BlockPosition blockposition) { + // CraftBukkit start - tree generation + if (captureTreeGeneration) { // If any of this logic updates, update async variant above + Iterator it = capturedBlockStates.iterator(); + while (it.hasNext()) { // If any of this logic updates, update async variant above + CraftBlockState previous = it.next(); + if (previous.getX() == blockposition.getX() && previous.getY() == blockposition.getY() && previous.getZ() == blockposition.getZ()) { + return previous.getHandle(); // If any of this logic updates, update async variant above + } + } // If any of this logic updates, update async variant above + } + // CraftBukkit end + if (blockposition.isInvalidYLocation()) { // Paper + return Blocks.VOID_AIR.getBlockData(); + } else { + Chunk chunk = this.getChunkAtWorldCoords(blockposition); + + return chunk.getType(blockposition); + } + } + // Paper start + public Fluid getFluidIfLoaded(BlockPosition blockposition) { + if (blockposition.isInvalidYLocation()) { // Paper + return getFluid(blockposition); + } else { + Chunk chunk = this.getChunkIfLoaded(blockposition); + + return chunk != null ? chunk.getFluid(blockposition) : null; + } + } + // Paper end + public Fluid getFluid(BlockPosition blockposition) { + if (blockposition.isInvalidYLocation()) { // Paper + return FluidTypes.EMPTY.i(); + } else { + Chunk chunk = this.getChunkAtWorldCoords(blockposition); + + return chunk.getFluid(blockposition); + } + } + + public boolean isDayTime() { return L(); } // Paper - OBFHELPER + public boolean L() { + return this.G < 4; + } + + @Nullable + public MovingObjectPosition rayTrace(Vec3D vec3d, Vec3D vec3d1) { + return this.rayTrace(vec3d, vec3d1, FluidCollisionOption.NEVER, false, false); + } + + @Nullable + public MovingObjectPosition rayTrace(Vec3D vec3d, Vec3D vec3d1, FluidCollisionOption fluidcollisionoption) { + return this.rayTrace(vec3d, vec3d1, fluidcollisionoption, false, false); + } + + @Nullable + public MovingObjectPosition rayTrace(Vec3D vec3d, Vec3D vec3d1, FluidCollisionOption fluidcollisionoption, boolean flag, boolean flag1) { + double d0 = vec3d.x; + double d1 = vec3d.y; + double d2 = vec3d.z; + + if (!Double.isNaN(d0) && !Double.isNaN(d1) && !Double.isNaN(d2)) { + if (!Double.isNaN(vec3d1.x) && !Double.isNaN(vec3d1.y) && !Double.isNaN(vec3d1.z)) { + int i = MathHelper.floor(vec3d1.x); + int j = MathHelper.floor(vec3d1.y); + int k = MathHelper.floor(vec3d1.z); + int l = MathHelper.floor(d0); + int i1 = MathHelper.floor(d1); + int j1 = MathHelper.floor(d2); + BlockPosition blockposition = new BlockPosition(l, i1, j1); + IBlockData iblockdata = this.getTypeIfLoaded(blockposition); // Paper + if (iblockdata == null) return null; // Paper + Fluid fluid = this.getFluid(blockposition); + boolean flag2; + boolean flag3; + + if (!flag || !iblockdata.getCollisionShape(this, blockposition).isEmpty()) { + flag2 = iblockdata.getBlock().isCollidable(iblockdata); + flag3 = fluidcollisionoption.predicate.test(fluid); + if (flag2 || flag3) { + MovingObjectPosition movingobjectposition = null; + + if (flag2) { + movingobjectposition = Block.rayTrace(iblockdata, this, blockposition, vec3d, vec3d1); + } + + if (movingobjectposition == null && flag3) { + movingobjectposition = VoxelShapes.create(0.0D, 0.0D, 0.0D, 1.0D, (double) fluid.getHeight(), 1.0D).rayTrace(vec3d, vec3d1, blockposition); + } + + if (movingobjectposition != null) { + return movingobjectposition; + } + } + } + + MovingObjectPosition movingobjectposition1 = null; + int k1 = 200; + + while (k1-- >= 0) { + if (Double.isNaN(d0) || Double.isNaN(d1) || Double.isNaN(d2)) { + return null; + } + + if (l == i && i1 == j && j1 == k) { + return flag1 ? movingobjectposition1 : null; + } + + flag2 = true; + flag3 = true; + boolean flag4 = true; + double d3 = 999.0D; + double d4 = 999.0D; + double d5 = 999.0D; + + if (i > l) { + d3 = (double) l + 1.0D; + } else if (i < l) { + d3 = (double) l + 0.0D; + } else { + flag2 = false; + } + + if (j > i1) { + d4 = (double) i1 + 1.0D; + } else if (j < i1) { + d4 = (double) i1 + 0.0D; + } else { + flag3 = false; + } + + if (k > j1) { + d5 = (double) j1 + 1.0D; + } else if (k < j1) { + d5 = (double) j1 + 0.0D; + } else { + flag4 = false; + } + + double d6 = 999.0D; + double d7 = 999.0D; + double d8 = 999.0D; + double d9 = vec3d1.x - d0; + double d10 = vec3d1.y - d1; + double d11 = vec3d1.z - d2; + + if (flag2) { + d6 = (d3 - d0) / d9; + } + + if (flag3) { + d7 = (d4 - d1) / d10; + } + + if (flag4) { + d8 = (d5 - d2) / d11; + } + + if (d6 == -0.0D) { + d6 = -1.0E-4D; + } + + if (d7 == -0.0D) { + d7 = -1.0E-4D; + } + + if (d8 == -0.0D) { + d8 = -1.0E-4D; + } + + EnumDirection enumdirection; + + if (d6 < d7 && d6 < d8) { + enumdirection = i > l ? EnumDirection.WEST : EnumDirection.EAST; + d0 = d3; + d1 += d10 * d6; + d2 += d11 * d6; + } else if (d7 < d8) { + enumdirection = j > i1 ? EnumDirection.DOWN : EnumDirection.UP; + d0 += d9 * d7; + d1 = d4; + d2 += d11 * d7; + } else { + enumdirection = k > j1 ? EnumDirection.NORTH : EnumDirection.SOUTH; + d0 += d9 * d8; + d1 += d10 * d8; + d2 = d5; + } + + l = MathHelper.floor(d0) - (enumdirection == EnumDirection.EAST ? 1 : 0); + i1 = MathHelper.floor(d1) - (enumdirection == EnumDirection.UP ? 1 : 0); + j1 = MathHelper.floor(d2) - (enumdirection == EnumDirection.SOUTH ? 1 : 0); + blockposition = new BlockPosition(l, i1, j1); + IBlockData iblockdata1 = this.getTypeIfLoaded(blockposition); // Paper + if (iblockdata1 == null) return null; // Paper + Fluid fluid1 = this.getFluid(blockposition); + + if (!flag || iblockdata1.getMaterial() == Material.PORTAL || !iblockdata1.getCollisionShape(this, blockposition).isEmpty()) { + boolean flag5 = iblockdata1.getBlock().isCollidable(iblockdata1); + boolean flag6 = fluidcollisionoption.predicate.test(fluid1); + + if (!flag5 && !flag6) { + movingobjectposition1 = new MovingObjectPosition(MovingObjectPosition.EnumMovingObjectType.MISS, new Vec3D(d0, d1, d2), enumdirection, blockposition); + } else { + MovingObjectPosition movingobjectposition2 = null; + + if (flag5) { + movingobjectposition2 = Block.rayTrace(iblockdata1, this, blockposition, vec3d, vec3d1); + } + + if (movingobjectposition2 == null && flag6) { + movingobjectposition2 = VoxelShapes.create(0.0D, 0.0D, 0.0D, 1.0D, (double) fluid1.getHeight(), 1.0D).rayTrace(vec3d, vec3d1, blockposition); + } + + if (movingobjectposition2 != null) { + return movingobjectposition2; + } + } + } + } + + return flag1 ? movingobjectposition1 : null; + } else { + return null; + } + } else { + return null; + } + } + + public void a(@Nullable EntityHuman entityhuman, BlockPosition blockposition, SoundEffect soundeffect, SoundCategory soundcategory, float f, float f1) { + this.a(entityhuman, (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, soundeffect, soundcategory, f, f1); + } + + // Paper start - OBFHELPER + public final void sendSoundEffect(@Nullable EntityHuman fromEntity, double x, double y, double z, SoundEffect soundeffect, SoundCategory soundcategory, float volume, float pitch) { + this.a(fromEntity, x, y, z, soundeffect, soundcategory, volume, pitch); + } + // Paper end + + public void a(@Nullable EntityHuman entityhuman, double d0, double d1, double d2, SoundEffect soundeffect, SoundCategory soundcategory, float f, float f1) { + for (int i = 0; i < this.v.size(); ++i) { + ((IWorldAccess) this.v.get(i)).a(entityhuman, soundeffect, soundcategory, d0, d1, d2, f, f1); + } + + } + + public void a(double d0, double d1, double d2, SoundEffect soundeffect, SoundCategory soundcategory, float f, float f1, boolean flag) {} + + public void a(BlockPosition blockposition, @Nullable SoundEffect soundeffect) { + for (int i = 0; i < this.v.size(); ++i) { + ((IWorldAccess) this.v.get(i)).a(soundeffect, blockposition); + } + + } + + public void addParticle(ParticleParam particleparam, double d0, double d1, double d2, double d3, double d4, double d5) { + for (int i = 0; i < this.v.size(); ++i) { + ((IWorldAccess) this.v.get(i)).a(particleparam, particleparam.b().e(), d0, d1, d2, d3, d4, d5); + } + + } + + public void b(ParticleParam particleparam, double d0, double d1, double d2, double d3, double d4, double d5) { + for (int i = 0; i < this.v.size(); ++i) { + ((IWorldAccess) this.v.get(i)).a(particleparam, false, true, d0, d1, d2, d3, d4, d5); + } + + } + + public boolean strikeLightning(Entity entity) { + this.k.add(entity); + return true; + } + + public boolean addEntity(Entity entity) { + // CraftBukkit start - Used for entities other than creatures + return addEntity(entity, SpawnReason.DEFAULT); + } + + public boolean addEntity(Entity entity, SpawnReason spawnReason) { // Changed signature, added SpawnReason + // Paper start + if (regionLimited != null) { + return regionLimited.addEntity(entity, spawnReason); + } + // Paper end + org.spigotmc.AsyncCatcher.catchOp( "entity add"); // Spigot + if (entity.valid) { MinecraftServer.LOGGER.error("Attempted Double World add on " + entity, new Throwable()); return true; } // Paper + if (!CraftEventFactory.doEntityAddEventCalling(this, entity, spawnReason)) { + return false; + } + // CraftBukkit end + + int i = MathHelper.floor(entity.locX / 16.0D); + int j = MathHelper.floor(entity.locZ / 16.0D); + boolean flag = true; // Paper - always load chunks for entity adds + + // Paper start - Set origin location when the entity is being added to the world + if (entity.origin == null) { + entity.origin = entity.getBukkitEntity().getLocation(); + } + // Paper end + + if (entity instanceof EntityHuman) { + flag = true; + } + + if (!flag && !this.isChunkLoaded(i, j, false)) { + return false; + } else { + if (entity instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) entity; + + this.players.add(entityhuman); + this.playersByName.put(entityhuman.getName(), entityhuman); + // Paper end + this.everyoneSleeping(); + } + + this.getChunkAt(i, j).a(entity); + if (entity.dead) return false; // Paper - don't add dead entities, chunk registration may of killed it + this.entityList.add(entity); + this.b(entity); + return true; + } + } + + protected void b(Entity entity) { + for (int i = 0; i < this.v.size(); ++i) { + ((IWorldAccess) this.v.get(i)).a(entity); + } + + entity.valid = true; // CraftBukkit + entity.shouldBeRemoved = false; // Paper - shouldn't be removed after being re-added + new com.destroystokyo.paper.event.entity.EntityAddToWorldEvent(entity.getBukkitEntity()).callEvent(); // Paper - fire while valid + } + + protected void c(Entity entity) { + for (int i = 0; i < this.v.size(); ++i) { + ((IWorldAccess) this.v.get(i)).b(entity); + } + + new com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent(entity.getBukkitEntity()).callEvent(); // Paper - fire while valid + entity.valid = false; // CraftBukkit + } + + public void kill(Entity entity) { + org.spigotmc.AsyncCatcher.catchOp( "entity kill"); // Spigot + if (entity.isVehicle()) { + entity.ejectPassengers(); + } + + if (entity.isPassenger()) { + entity.stopRiding(); + } + + entity.die(); + if (entity instanceof EntityHuman) { + this.players.remove(entity); + this.playersByName.remove(entity.getName()); // Paper - World EntityHuman Lookup Optimizations + // Spigot start + for ( WorldPersistentData worldData : worldMaps.worldMap.values() ) + { + for (Object o : worldData.data.values() ) + { + if ( o instanceof WorldMap ) + { + WorldMap map = (WorldMap) o; + map.humans.remove( (EntityHuman) entity ); + for ( Iterator iter = (Iterator) map.h.iterator(); iter.hasNext(); ) + { + if ( iter.next().trackee == entity ) + { + map.decorations.remove(entity.getDisplayName().getString()); // Paper + iter.remove(); + } + } + } + } + } + // Spigot end + this.everyoneSleeping(); + this.c(entity); + } + + } + + public void removeEntity(Entity entity) { + org.spigotmc.AsyncCatcher.catchOp( "entity remove"); // Spigot + entity.b(false); + entity.die(); + if (entity instanceof EntityHuman) { + this.players.remove(entity); + this.playersByName.remove(entity.getName()); // Paper - World EntityHuman Lookup Optimizations + this.everyoneSleeping(); + } + + // if (!guardEntityList) { // Spigot - It will get removed after the tick if we are ticking // Paper - move down + int i = entity.chunkX; + int j = entity.chunkZ; + + if (entity.inChunk && this.isChunkLoaded(i, j, true)) { + this.getChunkAt(i, j).b(entity); + } + entity.shouldBeRemoved = true; // Paper + entityList.updateEntityCount(entity, -1); // Paper + + if (!guardEntityList) { // Spigot - It will get removed after the tick if we are ticking // Paper - always remove from current chunk above + // CraftBukkit start - Decrement loop variable field if we've already ticked this entity + int index = this.entityList.indexOf(entity); + if (index != -1) { + if (index <= this.tickPosition) { + this.tickPosition--; + } + this.entityList.remove(index); + } + // CraftBukkit end + } // Spigot + this.c(entity); + } + + public void addIWorldAccess(IWorldAccess iworldaccess) { + this.v.add(iworldaccess); + } + + public int a(float f) { + float f1 = this.k(f); + float f2 = 1.0F - (MathHelper.cos(f1 * 6.2831855F) * 2.0F + 0.5F); + + f2 = MathHelper.a(f2, 0.0F, 1.0F); + f2 = 1.0F - f2; + f2 = (float) ((double) f2 * (1.0D - (double) (this.i(f) * 5.0F) / 16.0D)); + f2 = (float) ((double) f2 * (1.0D - (double) (this.g(f) * 5.0F) / 16.0D)); + f2 = 1.0F - f2; + return (int) (f2 * 11.0F); + } + + public float c(float f) { + float f1 = this.k(f); + + return f1 * 6.2831855F; + } + + public void tickEntities() { + this.methodProfiler.enter("entities"); + this.methodProfiler.enter("global"); + + Entity entity; + int i; + + for (i = 0; i < this.k.size(); ++i) { + entity = (Entity) this.k.get(i); + // CraftBukkit start - Fixed an NPE + if (entity == null) { + continue; + } + // CraftBukkit end + + try { + ++entity.ticksLived; + entity.tick(); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Ticking entity"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being ticked"); + + if (entity == null) { + crashreportsystemdetails.a("Entity", (Object) "~~NULL~~"); + } else { + entity.appendEntityCrashDetails(crashreportsystemdetails); + } + + throw new ReportedException(crashreport); + } + + if (entity.dead) { + this.k.remove(i--); + } + } + + this.methodProfiler.exitEnter("remove"); + timings.entityRemoval.startTiming(); // Paper + this.entityList.removeAll(this.g); + + int j; + // Paper start - Set based removal lists + for (Entity e : this.g) { + /* + j = e.getChunkZ(); + int k = e.getChunkX(); + + if (e.inChunk && this.isChunkLoaded(k, j, true)) { + this.getChunkAt(k, j).b(e); + }*/ + Chunk chunk = e.inChunk ? e.getCurrentChunk() : null; + if (chunk != null) chunk.removeEntity(e); + } + + for (Entity e : this.g) { + this.c(e); + } + // Paper end + + this.g.clear(); + this.p_(); + timings.entityRemoval.stopTiming(); // Paper + this.methodProfiler.exitEnter("regular"); + + CrashReport crashreport1; + CrashReportSystemDetails crashreportsystemdetails1; + + org.spigotmc.ActivationRange.activateEntities(this); // Spigot + timings.entityTick.startTiming(); // Spigot + guardEntityList = true; // Spigot + // CraftBukkit start - Use field for loop variable + co.aikar.timings.TimingHistory.entityTicks += this.entityList.size(); // Paper + int entitiesThisCycle = 0; + // Paper start - Disable tick limiters + //if (tickPosition < 0) tickPosition = 0; + for (tickPosition = 0; tickPosition < entityList.size(); tickPosition++) { + // Paper end + tickPosition = (tickPosition < entityList.size()) ? tickPosition : 0; + entity = (Entity) this.entityList.get(this.tickPosition); + // CraftBukkit end + Entity entity1 = entity.getVehicle(); + + if (entity1 != null) { + if (!entity1.dead && entity1.w(entity)) { + continue; + } + + entity.stopRiding(); + } + + this.methodProfiler.enter("tick"); + if (!entity.dead && !(entity instanceof EntityPlayer)) { + try { + entity.tickTimer.startTiming(); // Paper + this.g(entity); + entity.tickTimer.stopTiming(); // Paper + } catch (Throwable throwable1) { + entity.tickTimer.stopTiming(); + // Paper start - Prevent tile entity and entity crashes + String msg = "Entity threw exception at " + entity.world.getWorld().getName() + ":" + entity.locX + "," + entity.locY + "," + entity.locZ; + System.err.println(msg); + throwable1.printStackTrace(); + getServer().getPluginManager().callEvent(new ServerExceptionEvent(new ServerInternalException(msg, throwable1))); + entity.dead = true; + continue; + // Paper end + } + } + + this.methodProfiler.exit(); + this.methodProfiler.enter("remove"); + if (entity.dead) { + // Paper start + /* + j = entity.chunkX; + int l = entity.chunkZ; + + if (entity.inChunk && this.isChunkLoaded(j, l, true)) { + this.getChunkAt(j, l).b(entity); + }*/ + Chunk chunk = entity.inChunk ? entity.getCurrentChunk() : null; + if (chunk != null) chunk.removeEntity(entity); + // Paper end + + guardEntityList = false; // Spigot + this.entityList.remove(this.tickPosition--); // CraftBukkit - Use field for loop variable + guardEntityList = true; // Spigot + this.c(entity); + } + + this.methodProfiler.exit(); + } + guardEntityList = false; // Spigot + + timings.entityTick.stopTiming(); // Spigot + this.methodProfiler.exitEnter("blockEntities"); + timings.tileEntityTick.startTiming(); // Spigot + if (!this.tileEntityListUnload.isEmpty()) { + // Paper start - Use alternate implementation with faster contains + java.util.Set toRemove = java.util.Collections.newSetFromMap(new java.util.IdentityHashMap<>()); + toRemove.addAll(tileEntityListUnload); + this.tileEntityListTick.removeAll(toRemove); + // Paper end + //this.tileEntityList.removeAll(this.tileEntityListUnload); // Paper - remove unused list + this.tileEntityListUnload.clear(); + } + + this.J = true; + // Spigot start + // Iterator iterator = this.tileEntityListTick.iterator(); + int tilesThisCycle = 0; + for (tileTickPosition = 0; tileTickPosition < tileEntityListTick.size(); tileTickPosition++) { // Paper - Disable tick limiters + tileTickPosition = (tileTickPosition < tileEntityListTick.size()) ? tileTickPosition : 0; + TileEntity tileentity = (TileEntity) this.tileEntityListTick.get(tileTickPosition); + // Spigot start + if (tileentity == null) { + getServer().getLogger().severe("Spigot has detected a null entity and has removed it, preventing a crash"); + tilesThisCycle--; + this.tileEntityListTick.remove(tileTickPosition--); + continue; + } + // Spigot end + + if (!tileentity.x() && tileentity.hasWorld()) { + BlockPosition blockposition = tileentity.getPosition(); + + // Paper start - Skip ticking in chunks scheduled for unload + net.minecraft.server.Chunk chunk = tileentity.getCurrentChunk(); + boolean shouldTick = chunk != null; + if(this.paperConfig.skipEntityTickingInChunksScheduledForUnload) + shouldTick = shouldTick && chunk.scheduledForUnload == null; + if (shouldTick && this.K.a(blockposition)) { + // Paper end + try { + this.methodProfiler.a(() -> { + return String.valueOf(TileEntityTypes.a(tileentity.C())); + }); + tileentity.tickTimer.startTiming(); // Spigot + ((ITickable) tileentity).tick(); + this.methodProfiler.exit(); + } catch (Throwable throwable2) { + // Paper start - Prevent tile entity and entity crashes + String msg = "TileEntity threw exception at " + tileentity.world.getWorld().getName() + ":" + tileentity.position.getX() + "," + tileentity.position.getY() + "," + tileentity.position.getZ(); + System.err.println(msg); + throwable2.printStackTrace(); + getServer().getPluginManager().callEvent(new ServerExceptionEvent(new ServerInternalException(msg, throwable2))); + tilesThisCycle--; + this.tileEntityListTick.remove(tileTickPosition--); + continue; + // Paper end + } + // Spigot start + finally { + tileentity.tickTimer.stopTiming(); + } + // Spigot end + } + } + + if (tileentity.x()) { + tilesThisCycle--; + this.tileEntityListTick.remove(tileTickPosition--); + //this.tileEntityList.remove(tileentity); // Paper - remove unused list + // Paper start + net.minecraft.server.Chunk chunk = tileentity.getCurrentChunk(); + if (chunk != null) { + chunk.removeTileEntity(tileentity.getPosition()); + // Paper end + } + } + } + + timings.tileEntityTick.stopTiming(); // Spigot + timings.tileEntityPending.startTiming(); // Spigot + this.J = false; + this.methodProfiler.exitEnter("pendingBlockEntities"); + if (!this.c.isEmpty()) { + for (int i1 = 0; i1 < this.c.size(); ++i1) { + TileEntity tileentity1 = (TileEntity) this.c.get(i1); + + if (!tileentity1.x()) { + /* CraftBukkit start - Order matters, moved down + if (!this.tileEntityList.contains(tileentity1)) { + this.a(tileentity1); + } + // CraftBukkit end */ + + if (this.isLoaded(tileentity1.getPosition())) { + Chunk chunk = this.getChunkAtWorldCoords(tileentity1.getPosition()); + IBlockData iblockdata = chunk.getType(tileentity1.getPosition()); + + chunk.a(tileentity1.getPosition(), tileentity1); + this.notify(tileentity1.getPosition(), iblockdata, iblockdata, 3); + // CraftBukkit start + // From above, don't screw this up - SPIGOT-1746 + if (true) { // Paper - remove unused list + this.a(tileentity1); + } + // CraftBukkit end + } + } + } + + this.c.clear(); + } + + timings.tileEntityPending.stopTiming(); // Spigot + co.aikar.timings.TimingHistory.tileEntityTicks += this.tileEntityListTick.size(); // Paper + this.methodProfiler.exit(); + this.methodProfiler.exit(); + } + + protected void p_() {} + + public boolean a(TileEntity tileentity) { + boolean flag = true; // Paper - remove unused list + + if (flag && tileentity instanceof ITickable && !this.tileEntityListTick.contains(tileentity)) { // Paper + this.tileEntityListTick.add(tileentity); + } + + if (this.isClientSide) { + BlockPosition blockposition = tileentity.getPosition(); + IBlockData iblockdata = this.getType(blockposition); + + this.notify(blockposition, iblockdata, iblockdata, 2); + } + + return flag; + } + + public void a(Collection collection) { + if (this.J) { + this.c.addAll(collection); + } else { + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + TileEntity tileentity = (TileEntity) iterator.next(); + + this.a(tileentity); + } + } + + } + + public void g(Entity entity) { + this.entityJoinedWorld(entity, true); + } + + public void entityJoinedWorld(Entity entity, boolean flag) { + int i; + int j; + + // CraftBukkit start - check if chunks are loaded as done in previous versions + // TODO: Go back to Vanilla behaviour when comfortable + // Spigot start + // Chunk startingChunk = this.getChunkIfLoaded(MathHelper.floor(entity.locX) >> 4, MathHelper.floor(entity.locZ) >> 4); + if (flag && !org.spigotmc.ActivationRange.checkIfActive(entity)) { + entity.ticksLived++; + entity.inactiveTick(); + // Spigot end + return; + } + // CraftBukkit end + + entity.N = entity.locX; + entity.O = entity.locY; + entity.P = entity.locZ; + entity.lastYaw = entity.yaw; + entity.lastPitch = entity.pitch; + if (flag && entity.inChunk) { + ++entity.ticksLived; + ++co.aikar.timings.TimingHistory.activatedEntityTicks; // Paper + if (entity.isPassenger()) { + entity.aH(); + } else { + this.methodProfiler.a(() -> { + return IRegistry.ENTITY_TYPE.getKey(entity.P()).toString(); + }); + entity.tick(); + entity.postTick(); // CraftBukkit + this.methodProfiler.exit(); + } + } + + this.methodProfiler.enter("chunkCheck"); + if (Double.isNaN(entity.locX) || Double.isInfinite(entity.locX)) { + entity.locX = entity.N; + } + + if (Double.isNaN(entity.locY) || Double.isInfinite(entity.locY)) { + entity.locY = entity.O; + } + + if (Double.isNaN(entity.locZ) || Double.isInfinite(entity.locZ)) { + entity.locZ = entity.P; + } + + if (Double.isNaN((double) entity.pitch) || Double.isInfinite((double) entity.pitch)) { + entity.pitch = entity.lastPitch; + } + + if (Double.isNaN((double) entity.yaw) || Double.isInfinite((double) entity.yaw)) { + entity.yaw = entity.lastYaw; + } + + i = MathHelper.floor(entity.locX / 16.0D); + j = Math.min(15, Math.max(0, MathHelper.floor(entity.locY / 16.0D))); // Paper - stay consistent with chunk add/remove behavior + int k = MathHelper.floor(entity.locZ / 16.0D); + + if (!entity.inChunk || entity.chunkX != i || entity.chunkY != j || entity.chunkZ != k) { + if (entity.inChunk && this.isChunkLoaded(entity.chunkX, entity.chunkZ, true)) { + this.getChunkAt(entity.chunkX, entity.chunkZ).a(entity, entity.chunkY); + } + + if (!entity.valid && !entity.bN() && !this.isChunkLoaded(i, k, true)) { // Paper - always load chunks to register valid entities location + entity.inChunk = false; + } else { + this.getChunkAt(i, k).a(entity); + } + } + + this.methodProfiler.exit(); + if (flag && entity.inChunk) { + Iterator iterator = entity.bP().iterator(); + + while (iterator.hasNext()) { + Entity entity1 = (Entity) iterator.next(); + + if (!entity1.dead && entity1.getVehicle() == entity) { + this.g(entity1); + } else { + entity1.stopRiding(); + } + } + } + } + + // Paper start - Based on method below + /** + * @param entity causing the action ex. block placer + * @param voxelshape area to search within + * @return if there are no visible players colliding + */ + public boolean checkNoVisiblePlayerCollisions(@Nullable Entity entity, VoxelShape voxelshape) { + if (voxelshape.isEmpty()) { + return true; + } else { + List list = this.getEntities((Entity) null, voxelshape.getBoundingBox()); + + for (int i = 0; i < list.size(); ++i) { + Entity entity1 = (Entity) list.get(i); + + if (entity instanceof EntityPlayer && entity1 instanceof EntityPlayer) { + if (!((EntityPlayer) entity).getBukkitEntity().canSee(((EntityPlayer) entity1).getBukkitEntity())) { + continue; + } + } + + if (!entity1.dead && entity1.blocksEntitySpawning()) { + return false; + } + } + + return true; + } + } + // Paper end + + public boolean a(@Nullable Entity entity, VoxelShape voxelshape) { + if (voxelshape.isEmpty()) { + return true; + } else { + List list = this.getEntities((Entity) null, voxelshape.getBoundingBox()); + + for (int i = 0; i < list.size(); ++i) { + Entity entity1 = (Entity) list.get(i); + + if (!entity1.dead && entity1.j && entity1 != entity && (entity == null || !entity1.x(entity)) && VoxelShapes.c(voxelshape, VoxelShapes.a(entity1.getBoundingBox()), OperatorBoolean.AND)) { + return false; + } + } + + return true; + } + } + + // Paper start - Prevent armor stands from doing entity lookups + @Override + public boolean getCubes(@Nullable Entity entity, AxisAlignedBB axisAlignedBB) { + if (entity instanceof EntityArmorStand && !entity.world.paperConfig.armorStandEntityLookups) return false; + return GeneratorAccess.super.getCubes(entity, axisAlignedBB); + } + // Paper end + + public boolean a(AxisAlignedBB axisalignedbb) { + int i = MathHelper.floor(axisalignedbb.minX); + int j = MathHelper.f(axisalignedbb.maxX); + int k = MathHelper.floor(axisalignedbb.minY); + int l = MathHelper.f(axisalignedbb.maxY); + int i1 = MathHelper.floor(axisalignedbb.minZ); + int j1 = MathHelper.f(axisalignedbb.maxZ); + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + for (int k1 = i; k1 < j; ++k1) { + for (int l1 = k; l1 < l; ++l1) { + for (int i2 = i1; i2 < j1; ++i2) { + IBlockData iblockdata = this.getType(blockposition_b.c(k1, l1, i2)); + + if (!iblockdata.isAir()) { + boolean flag = true; + + return flag; + } + } + } + } + + return false; + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + } + + public boolean b(AxisAlignedBB axisalignedbb) { + int i = MathHelper.floor(axisalignedbb.minX); + int j = MathHelper.f(axisalignedbb.maxX); + int k = MathHelper.floor(axisalignedbb.minY); + int l = MathHelper.f(axisalignedbb.maxY); + int i1 = MathHelper.floor(axisalignedbb.minZ); + int j1 = MathHelper.f(axisalignedbb.maxZ); + + if (this.isAreaLoaded(i, k, i1, j, l, j1, true)) { + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + for (int k1 = i; k1 < j; ++k1) { + for (int l1 = k; l1 < l; ++l1) { + for (int i2 = i1; i2 < j1; ++i2) { + Block block = this.getType(blockposition_b.c(k1, l1, i2)).getBlock(); + + if (block == Blocks.FIRE || block == Blocks.LAVA) { + boolean flag = true; + + return flag; + } + } + } + } + + return false; + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + } else { + return false; + } + } + + @Nullable + public IBlockData a(AxisAlignedBB axisalignedbb, Block block) { + int i = MathHelper.floor(axisalignedbb.minX); + int j = MathHelper.f(axisalignedbb.maxX); + int k = MathHelper.floor(axisalignedbb.minY); + int l = MathHelper.f(axisalignedbb.maxY); + int i1 = MathHelper.floor(axisalignedbb.minZ); + int j1 = MathHelper.f(axisalignedbb.maxZ); + + if (this.isAreaLoaded(i, k, i1, j, l, j1, true)) { + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + for (int k1 = i; k1 < j; ++k1) { + for (int l1 = k; l1 < l; ++l1) { + for (int i2 = i1; i2 < j1; ++i2) { + IBlockData iblockdata = this.getType(blockposition_b.c(k1, l1, i2)); + + if (iblockdata.getBlock() == block) { + IBlockData iblockdata1 = iblockdata; + + return iblockdata1; + } + } + } + } + + return null; + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + } else { + return null; + } + } + + public boolean a(AxisAlignedBB axisalignedbb, Material material) { + int i = MathHelper.floor(axisalignedbb.minX); + int j = MathHelper.f(axisalignedbb.maxX); + int k = MathHelper.floor(axisalignedbb.minY); + int l = MathHelper.f(axisalignedbb.maxY); + int i1 = MathHelper.floor(axisalignedbb.minZ); + int j1 = MathHelper.f(axisalignedbb.maxZ); + MaterialPredicate materialpredicate = MaterialPredicate.a(material); + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + for (int k1 = i; k1 < j; ++k1) { + for (int l1 = k; l1 < l; ++l1) { + for (int i2 = i1; i2 < j1; ++i2) { + if (materialpredicate.test(this.getType(blockposition_b.c(k1, l1, i2)))) { + boolean flag = true; + + return flag; + } + } + } + } + + return false; + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + } + + public Explosion explode(@Nullable Entity entity, double d0, double d1, double d2, float f, boolean flag) { + return this.createExplosion(entity, (DamageSource) null, d0, d1, d2, f, false, flag); + } + + public Explosion createExplosion(@Nullable Entity entity, double d0, double d1, double d2, float f, boolean flag, boolean flag1) { + return this.createExplosion(entity, (DamageSource) null, d0, d1, d2, f, flag, flag1); + } + + public Explosion createExplosion(@Nullable Entity entity, @Nullable DamageSource damagesource, double d0, double d1, double d2, float f, boolean flag, boolean flag1) { + Explosion explosion = new Explosion(this, entity, d0, d1, d2, f, flag, flag1); + + if (damagesource != null) { + explosion.a(damagesource); + } + + explosion.a(); + explosion.a(true); + return explosion; + } + + public float a(Vec3D vec3d, AxisAlignedBB axisalignedbb) { + double d0 = 1.0D / ((axisalignedbb.maxX - axisalignedbb.minX) * 2.0D + 1.0D); + double d1 = 1.0D / ((axisalignedbb.maxY - axisalignedbb.minY) * 2.0D + 1.0D); + double d2 = 1.0D / ((axisalignedbb.maxZ - axisalignedbb.minZ) * 2.0D + 1.0D); + double d3 = (1.0D - Math.floor(1.0D / d0) * d0) / 2.0D; + double d4 = (1.0D - Math.floor(1.0D / d2) * d2) / 2.0D; + + if (d0 >= 0.0D && d1 >= 0.0D && d2 >= 0.0D) { + int i = 0; + int j = 0; + + for (float f = 0.0F; f <= 1.0F; f = (float) ((double) f + d0)) { + for (float f1 = 0.0F; f1 <= 1.0F; f1 = (float) ((double) f1 + d1)) { + for (float f2 = 0.0F; f2 <= 1.0F; f2 = (float) ((double) f2 + d2)) { + double d5 = axisalignedbb.minX + (axisalignedbb.maxX - axisalignedbb.minX) * (double) f; + double d6 = axisalignedbb.minY + (axisalignedbb.maxY - axisalignedbb.minY) * (double) f1; + double d7 = axisalignedbb.minZ + (axisalignedbb.maxZ - axisalignedbb.minZ) * (double) f2; + + if (this.rayTrace(new Vec3D(d5 + d3, d6, d7 + d4), vec3d) == null) { + ++i; + } + + ++j; + } + } + } + + return (float) i / (float) j; + } else { + return 0.0F; + } + } + + public boolean douseFire(@Nullable EntityHuman entityhuman, BlockPosition blockposition, EnumDirection enumdirection) { + blockposition = blockposition.shift(enumdirection); + if (this.getType(blockposition).getBlock() == Blocks.FIRE) { + this.a(entityhuman, 1009, blockposition, 0); + this.setAir(blockposition); + return true; + } else { + return false; + } + } + + public Map capturedTileEntities = Maps.newHashMap(); + @Nullable + public TileEntity getTileEntity(BlockPosition blockposition) { + if (blockposition.isInvalidYLocation()) { // Paper + return null; + } else { + // CraftBukkit start + if (capturedTileEntities.containsKey(blockposition)) { + return capturedTileEntities.get(blockposition); + } + // CraftBukkit end + + TileEntity tileentity = null; + + if (this.J) { + tileentity = this.E(blockposition); + } + + if (tileentity == null) { + tileentity = this.getChunkAtWorldCoords(blockposition).a(blockposition, Chunk.EnumTileEntityState.IMMEDIATE); + } + + if (tileentity == null) { + tileentity = this.E(blockposition); + } + + return tileentity; + } + } + + @Nullable + private TileEntity E(BlockPosition blockposition) { + for (int i = 0; i < this.c.size(); ++i) { + TileEntity tileentity = (TileEntity) this.c.get(i); + + if (!tileentity.x() && tileentity.getPosition().equals(blockposition)) { + return tileentity; + } + } + + return null; + } + + public void setTileEntity(BlockPosition blockposition, @Nullable TileEntity tileentity) { + if (!blockposition.isInvalidYLocation()) { // Paper + if (tileentity != null && !tileentity.x()) { + // CraftBukkit start + if (captureBlockStates) { + tileentity.setWorld(this); + tileentity.setPosition(blockposition); + capturedTileEntities.put(blockposition, tileentity); + return; + } + // CraftBukkit end + if (this.J) { + tileentity.setPosition(blockposition); + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + TileEntity tileentity1 = (TileEntity) iterator.next(); + + if (tileentity1.getPosition().equals(blockposition)) { + tileentity1.y(); + iterator.remove(); + } + } + + tileentity.setWorld(this); // Spigot - No null worlds + this.c.add(tileentity); + } else { + this.getChunkAtWorldCoords(blockposition).a(blockposition, tileentity); + this.a(tileentity); + } + } + + } + } + + public void n(BlockPosition blockposition) { + TileEntity tileentity = this.getTileEntity(blockposition); + + if (tileentity != null && this.J) { + tileentity.y(); + this.c.remove(tileentity); + } else { + if (tileentity != null) { + this.c.remove(tileentity); + //this.tileEntityList.remove(tileentity); // Paper - remove unused list + this.tileEntityListTick.remove(tileentity); + } + + this.getChunkAtWorldCoords(blockposition).d(blockposition); + } + + } + + public void b(TileEntity tileentity) { + this.tileEntityListUnload.add(tileentity); + } + + public boolean o(BlockPosition blockposition) { + return Block.a(this.getType(blockposition).getCollisionShape(this, blockposition)); + } + + public boolean p(BlockPosition blockposition) { + if (blockposition.isInvalidYLocation()) { // Paper + return false; + } else { + Chunk chunk = this.getChunkIfLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4); // Paper - optimize ifLoaded + + return chunk != null && !chunk.isEmpty(); + } + } + + public boolean q(BlockPosition blockposition) { + return this.p(blockposition) && this.getType(blockposition).q(); + } + + public void P() { + int i = this.a(1.0F); + + if (i != this.G) { + this.G = i; + } + + } + + public void setSpawnFlags(boolean flag, boolean flag1) { + this.allowMonsters = flag; + this.allowAnimals = flag1; + } + + public void doTick(BooleanSupplier booleansupplier) { + this.K.r(); + this.w(); + } + + protected void Q() { + if (this.worldData.hasStorm()) { + this.p = 1.0F; + if (this.worldData.isThundering()) { + this.r = 1.0F; + } + } + + } + + public void close() { + this.chunkProvider.close(); + } + + protected void w() { + if (this.worldProvider.g()) { + if (!this.isClientSide) { + boolean flag = this.getGameRules().getBoolean("doWeatherCycle"); + + if (flag) { + int i = this.worldData.z(); + + if (i > 0) { + --i; + this.worldData.g(i); + this.worldData.setThunderDuration(this.worldData.isThundering() ? 1 : 2); + this.worldData.setWeatherDuration(this.worldData.hasStorm() ? 1 : 2); + } + + int j = this.worldData.getThunderDuration(); + + if (j <= 0) { + if (this.worldData.isThundering()) { + this.worldData.setThunderDuration(this.random.nextInt(12000) + 3600); + } else { + this.worldData.setThunderDuration(this.random.nextInt(168000) + 12000); + } + } else { + --j; + this.worldData.setThunderDuration(j); + if (j <= 0) { + this.worldData.setThundering(!this.worldData.isThundering()); + } + } + + int k = this.worldData.getWeatherDuration(); + + if (k <= 0) { + if (this.worldData.hasStorm()) { + this.worldData.setWeatherDuration(this.random.nextInt(12000) + 12000); + } else { + this.worldData.setWeatherDuration(this.random.nextInt(168000) + 12000); + } + } else { + --k; + this.worldData.setWeatherDuration(k); + if (k <= 0) { + this.worldData.setStorm(!this.worldData.hasStorm()); + } + } + } + + this.q = this.r; + if (this.worldData.isThundering()) { + this.r = (float) ((double) this.r + 0.01D); + } else { + this.r = (float) ((double) this.r - 0.01D); + } + + this.r = MathHelper.a(this.r, 0.0F, 1.0F); + this.o = this.p; + if (this.worldData.hasStorm()) { + this.p = (float) ((double) this.p + 0.01D); + } else { + this.p = (float) ((double) this.p - 0.01D); + } + + this.p = MathHelper.a(this.p, 0.0F, 1.0F); + + // CraftBukkit start + for (int idx = 0; idx < this.players.size(); ++idx) { + if (((EntityPlayer) this.players.get(idx)).world == this) { + ((EntityPlayer) this.players.get(idx)).tickWeather(); + } + } + // CraftBukkit end + } + } + } + + protected void n_() {} + + public boolean r(BlockPosition blockposition) { + boolean flag = false; + + if (this.worldProvider.g()) { + flag |= this.c(EnumSkyBlock.SKY, blockposition); + } + + flag |= this.c(EnumSkyBlock.BLOCK, blockposition); + return flag; + } + + private int a(BlockPosition blockposition, EnumSkyBlock enumskyblock) { + if (enumskyblock == EnumSkyBlock.SKY && this.e(blockposition)) { + return 15; + } else { + IBlockData iblockdata = this.getType(blockposition); + int i = enumskyblock == EnumSkyBlock.SKY ? 0 : iblockdata.e(); + int j = iblockdata.b(this, blockposition); + + if (j >= 15 && iblockdata.e() > 0) { + j = 1; + } + + if (j < 1) { + j = 1; + } + + if (j >= 15) { + return 0; + } else if (i >= 14) { + return i; + } else { + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + EnumDirection[] aenumdirection = World.a; + int k = aenumdirection.length; + + for (int l = 0; l < k; ++l) { + EnumDirection enumdirection = aenumdirection[l]; + + blockposition_b.g(blockposition).c(enumdirection); + int i1 = this.getBrightness(enumskyblock, blockposition_b) - j; + + if (i1 > i) { + i = i1; + } + + if (i >= 14) { + int j1 = i; + + return j1; + } + } + + return i; + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + } + } + } + + public boolean c(EnumSkyBlock enumskyblock, BlockPosition blockposition) { + // CraftBukkit start - Use neighbor cache instead of looking up + Chunk chunk = this.getChunkIfLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4); + // Paper start - optimize light updates where chunk is known + return updateBrightness(enumskyblock, blockposition, chunk); + } + public boolean updateBrightness(EnumSkyBlock enumskyblock, BlockPosition blockposition, Chunk chunk) { + // Paper end + if (chunk == null || !chunk.areNeighborsLoaded(1) /*!this.areChunksLoaded(blockposition, 17, false)*/) { + // CraftBukkit end + return false; + } else { + int i = 0; + int j = 0; + + this.methodProfiler.enter("getBrightness"); + int k = this.getBrightness(enumskyblock, blockposition); + int l = this.a(blockposition, enumskyblock); + int i1 = blockposition.getX(); + int j1 = blockposition.getY(); + int k1 = blockposition.getZ(); + int l1; + int i2; + int j2; + int k2; + int l2; + int i3; + int j3; + int k3; + + if (l > k) { + this.E[j++] = 133152; + } else if (l < k) { + this.E[j++] = 133152 | k << 18; + + while (i < j) { + l1 = this.E[i++]; + i2 = (l1 & 63) - 32 + i1; + j2 = (l1 >> 6 & 63) - 32 + j1; + k2 = (l1 >> 12 & 63) - 32 + k1; + int l3 = l1 >> 18 & 15; + BlockPosition blockposition1 = new BlockPosition(i2, j2, k2); + + l2 = this.getBrightness(enumskyblock, blockposition1); + if (l2 == l3) { + this.a(enumskyblock, blockposition1, 0); + if (l3 > 0) { + i3 = MathHelper.a(i2 - i1); + j3 = MathHelper.a(j2 - j1); + k3 = MathHelper.a(k2 - k1); + if (i3 + j3 + k3 < 17) { + BlockPosition.b blockposition_b = BlockPosition.b.r(); + Throwable throwable = null; + + try { + EnumDirection[] aenumdirection = World.a; + int i4 = aenumdirection.length; + + for (int j4 = 0; j4 < i4; ++j4) { + EnumDirection enumdirection = aenumdirection[j4]; + int k4 = i2 + enumdirection.getAdjacentX(); + int l4 = j2 + enumdirection.getAdjacentY(); + int i5 = k2 + enumdirection.getAdjacentZ(); + + blockposition_b.c(k4, l4, i5); + int j5 = Math.max(1, this.getType(blockposition_b).b(this, blockposition_b)); + + l2 = this.getBrightness(enumskyblock, blockposition_b); + if (l2 == l3 - j5 && j < this.E.length) { + this.E[j++] = k4 - i1 + 32 | l4 - j1 + 32 << 6 | i5 - k1 + 32 << 12 | l3 - j5 << 18; + } + } + } catch (Throwable throwable1) { + throwable = throwable1; + throw throwable1; + } finally { + if (blockposition_b != null) { + if (throwable != null) { + try { + blockposition_b.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + blockposition_b.close(); + } + } + + } + } + } + } + } + + i = 0; + } + + this.methodProfiler.exit(); + this.methodProfiler.enter("checkedPosition < toCheckCount"); + + while (i < j) { + l1 = this.E[i++]; + i2 = (l1 & 63) - 32 + i1; + j2 = (l1 >> 6 & 63) - 32 + j1; + k2 = (l1 >> 12 & 63) - 32 + k1; + BlockPosition blockposition2 = new BlockPosition(i2, j2, k2); + int k5 = this.getBrightness(enumskyblock, blockposition2); + + l2 = this.a(blockposition2, enumskyblock); + if (l2 != k5) { + this.a(enumskyblock, blockposition2, l2); + if (l2 > k5) { + i3 = Math.abs(i2 - i1); + j3 = Math.abs(j2 - j1); + k3 = Math.abs(k2 - k1); + boolean flag = j < this.E.length - 6; + + if (i3 + j3 + k3 < 17 && flag) { + if (this.getBrightness(enumskyblock, blockposition2.west()) < l2) { + this.E[j++] = i2 - 1 - i1 + 32 + (j2 - j1 + 32 << 6) + (k2 - k1 + 32 << 12); + } + + if (this.getBrightness(enumskyblock, blockposition2.east()) < l2) { + this.E[j++] = i2 + 1 - i1 + 32 + (j2 - j1 + 32 << 6) + (k2 - k1 + 32 << 12); + } + + if (this.getBrightness(enumskyblock, blockposition2.down()) < l2) { + this.E[j++] = i2 - i1 + 32 + (j2 - 1 - j1 + 32 << 6) + (k2 - k1 + 32 << 12); + } + + if (this.getBrightness(enumskyblock, blockposition2.up()) < l2) { + this.E[j++] = i2 - i1 + 32 + (j2 + 1 - j1 + 32 << 6) + (k2 - k1 + 32 << 12); + } + + if (this.getBrightness(enumskyblock, blockposition2.north()) < l2) { + this.E[j++] = i2 - i1 + 32 + (j2 - j1 + 32 << 6) + (k2 - 1 - k1 + 32 << 12); + } + + if (this.getBrightness(enumskyblock, blockposition2.south()) < l2) { + this.E[j++] = i2 - i1 + 32 + (j2 - j1 + 32 << 6) + (k2 + 1 - k1 + 32 << 12); + } + } + } + } + } + + this.methodProfiler.exit(); + return true; + } + } + + public Stream a(@Nullable Entity entity, VoxelShape voxelshape, VoxelShape voxelshape1, Set set) { + Stream stream = IIBlockAccess.super.a(entity, voxelshape, voxelshape1, set); // CraftBukkit - decompile error + + return entity == null ? stream : Stream.concat(stream, this.a(entity, voxelshape, set)); + } + + public List getEntities(@Nullable Entity entity, AxisAlignedBB axisalignedbb, @Nullable Predicate predicate) { + List list = Lists.newArrayList(); + int i = MathHelper.floor((axisalignedbb.minX - 2.0D) / 16.0D); + int j = MathHelper.floor((axisalignedbb.maxX + 2.0D) / 16.0D); + int k = MathHelper.floor((axisalignedbb.minZ - 2.0D) / 16.0D); + int l = MathHelper.floor((axisalignedbb.maxZ + 2.0D) / 16.0D); + + for (int i1 = i; i1 <= j; ++i1) { + for (int j1 = k; j1 <= l; ++j1) { + if (this.isChunkLoaded(i1, j1, true)) { + this.getChunkAt(i1, j1).a(entity, axisalignedbb, list, predicate); + } + } + } + + return list; + } + + public List a(Class oclass, Predicate predicate) { + List list = Lists.newArrayList(); + Iterator iterator = this.entityList.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + if (entity.shouldBeRemoved) continue; // Paper + + if (oclass.isAssignableFrom(entity.getClass()) && predicate.test((T) entity)) { // CraftBukkit - decompile error + list.add((T) entity); // CraftBukkit - decompile error + } + } + + return list; + } + + public List b(Class oclass, Predicate predicate) { + List list = Lists.newArrayList(); + Iterator iterator = this.players.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + if (oclass.isAssignableFrom(entity.getClass()) && predicate.test((T) entity)) { // CraftBukkit - decompile error + list.add((T) entity); // CraftBukkit - decompile error + } + } + + return list; + } + + public List a(Class oclass, AxisAlignedBB axisalignedbb) { + return this.a(oclass, axisalignedbb, IEntitySelector.f); + } + + public List a(Class oclass, AxisAlignedBB axisalignedbb, @Nullable Predicate predicate) { + int i = MathHelper.floor((axisalignedbb.minX - 2.0D) / 16.0D); + int j = MathHelper.f((axisalignedbb.maxX + 2.0D) / 16.0D); + int k = MathHelper.floor((axisalignedbb.minZ - 2.0D) / 16.0D); + int l = MathHelper.f((axisalignedbb.maxZ + 2.0D) / 16.0D); + List list = Lists.newArrayList(); + + for (int i1 = i; i1 < j; ++i1) { + for (int j1 = k; j1 < l; ++j1) { + if (this.isChunkLoaded(i1, j1, true)) { + this.getChunkAt(i1, j1).a(oclass, axisalignedbb, list, predicate); + } + } + } + + return list; + } + + @Nullable + public T a(Class oclass, AxisAlignedBB axisalignedbb, T t0) { + List list = this.a(oclass, axisalignedbb); + T t1 = null; + double d0 = Double.MAX_VALUE; + + for (int i = 0; i < list.size(); ++i) { + T t2 = (T) list.get(i); // CraftBukkit - decompile error + + if (t2 != t0 && IEntitySelector.f.test(t2)) { + double d1 = t0.h(t2); + + if (d1 <= d0) { + t1 = t2; + d0 = d1; + } + } + } + + return t1; + } + + @Nullable + public Entity getEntity(int i) { + return (Entity) this.entitiesById.get(i); + } + + public void b(BlockPosition blockposition, TileEntity tileentity) { + if (this.isLoaded(blockposition)) { + this.getChunkAtWorldCoords(blockposition).markDirty(); + } + + } + + public int a(Class oclass, int i) { + int j = 0; + Iterator iterator = this.entityList.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + if (entity.shouldBeRemoved) continue; // Paper + // CraftBukkit start - Split out persistent check, don't apply it to special persistent mobs + if (entity instanceof EntityInsentient) { + EntityInsentient entityinsentient = (EntityInsentient) entity; + if (entityinsentient.isTypeNotPersistent() && entityinsentient.isPersistent()) { + continue; + } + } + + if (true || !(entity instanceof EntityInsentient) || !((EntityInsentient) entity).isPersistent()) { + // CraftBukkit end + if (oclass.isAssignableFrom(entity.getClass())) { + ++j; + } + + if (j > i) { + return j; + } + } + } + + return j; + } + + public void addChunkEntities(Stream collection) { a(collection); } // Paper - OBFHELPER + public void a(Stream stream) { + org.spigotmc.AsyncCatcher.catchOp( "entity world add"); // Spigot + stream.forEach((entity) -> { + if (entity == null || entity.dead || entity.valid) { // Paper - prevent adding already added or dead entities + return; + } + this.entityList.add(entity); + this.b(entity); + }); + } + + public void b(Collection collection) { + this.g.addAll(collection); + } + + public int getSeaLevel() { + return this.b; + } + + public World getMinecraftWorld() { + return this; + } + + public void b(int i) { + this.b = i; + } + + public int a(BlockPosition blockposition, EnumDirection enumdirection) { + return this.getType(blockposition).b((IBlockAccess) this, blockposition, enumdirection); + } + + public WorldType S() { + return this.worldData.getType(); + } + + public int getBlockPower(BlockPosition blockposition) { + byte b0 = 0; + int i = Math.max(b0, this.a(blockposition.down(), EnumDirection.DOWN)); + + if (i >= 15) { + return i; + } else { + i = Math.max(i, this.a(blockposition.up(), EnumDirection.UP)); + if (i >= 15) { + return i; + } else { + i = Math.max(i, this.a(blockposition.north(), EnumDirection.NORTH)); + if (i >= 15) { + return i; + } else { + i = Math.max(i, this.a(blockposition.south(), EnumDirection.SOUTH)); + if (i >= 15) { + return i; + } else { + i = Math.max(i, this.a(blockposition.west(), EnumDirection.WEST)); + if (i >= 15) { + return i; + } else { + i = Math.max(i, this.a(blockposition.east(), EnumDirection.EAST)); + return i >= 15 ? i : i; + } + } + } + } + } + } + + public boolean isBlockFacePowered(BlockPosition blockposition, EnumDirection enumdirection) { + return this.getBlockFacePower(blockposition, enumdirection) > 0; + } + + public int getBlockFacePower(BlockPosition blockposition, EnumDirection enumdirection) { + IBlockData iblockdata = this.getType(blockposition); + + return iblockdata.isOccluding() ? this.getBlockPower(blockposition) : iblockdata.a((IBlockAccess) this, blockposition, enumdirection); + } + + public boolean isBlockIndirectlyPowered(BlockPosition blockposition) { + return this.getBlockFacePower(blockposition.down(), EnumDirection.DOWN) > 0 ? true : (this.getBlockFacePower(blockposition.up(), EnumDirection.UP) > 0 ? true : (this.getBlockFacePower(blockposition.north(), EnumDirection.NORTH) > 0 ? true : (this.getBlockFacePower(blockposition.south(), EnumDirection.SOUTH) > 0 ? true : (this.getBlockFacePower(blockposition.west(), EnumDirection.WEST) > 0 ? true : this.getBlockFacePower(blockposition.east(), EnumDirection.EAST) > 0)))); + } + + public int isBlockIndirectlyGettingPowered(BlockPosition pos) { return u(pos); } // Paper - OBFHELPER + public int u(BlockPosition blockposition) { + int i = 0; + EnumDirection[] aenumdirection = World.a; + int j = aenumdirection.length; + + for (int k = 0; k < j; ++k) { + EnumDirection enumdirection = aenumdirection[k]; + int l = this.getBlockFacePower(blockposition.shift(enumdirection), enumdirection); + + if (l >= 15) { + return 15; + } + + if (l > i) { + i = l; + } + } + + return i; + } + + @Nullable + public EntityHuman a(double d0, double d1, double d2, double d3, Predicate predicate) { + double d4 = -1.0D; + EntityHuman entityhuman = null; + + for (int i = 0; i < this.players.size(); ++i) { + EntityHuman entityhuman1 = (EntityHuman) this.players.get(i); + // CraftBukkit start - Fixed an NPE + if (entityhuman1 == null || entityhuman1.dead) { + continue; + } + // CraftBukkit end + + if (predicate.test(entityhuman1)) { + double d5 = entityhuman1.d(d0, d1, d2); + + if ((d3 < 0.0D || d5 < d3 * d3) && (d4 == -1.0D || d5 < d4)) { + d4 = d5; + entityhuman = entityhuman1; + } + } + } + + return entityhuman; + } + + public boolean isPlayerNearby(double d0, double d1, double d2, double d3) { + for (int i = 0; i < this.players.size(); ++i) { + EntityHuman entityhuman = (EntityHuman) this.players.get(i); + + if (IEntitySelector.f.test(entityhuman) && entityhuman.affectsSpawning) { // Paper - Affects Spawning API + double d4 = entityhuman.d(d0, d1, d2); + + if (d3 < 0.0D || d4 < d3 * d3) { + return true; + } + } + } + + return false; + } + + public boolean b(double d0, double d1, double d2, double d3) { + Iterator iterator = this.players.iterator(); + + double d4; + + do { + EntityHuman entityhuman; + + do { + do { + if (!iterator.hasNext()) { + return false; + } + + entityhuman = (EntityHuman) iterator.next(); + } while (!IEntitySelector.f.test(entityhuman)); + } while (!IEntitySelector.b.test(entityhuman)); + + d4 = entityhuman.d(d0, d1, d2); + } while (d3 >= 0.0D && d4 >= d3 * d3); + + return true; + } + + @Nullable + public EntityHuman a(double d0, double d1, double d2) { + double d3 = -1.0D; + EntityHuman entityhuman = null; + + for (int i = 0; i < this.players.size(); ++i) { + EntityHuman entityhuman1 = (EntityHuman) this.players.get(i); + + if (IEntitySelector.f.test(entityhuman1)) { + double d4 = entityhuman1.d(d0, entityhuman1.locY, d1); + + if ((d2 < 0.0D || d4 < d2 * d2) && (d3 == -1.0D || d4 < d3)) { + d3 = d4; + entityhuman = entityhuman1; + } + } + } + + return entityhuman; + } + + @Nullable + public EntityHuman a(Entity entity, double d0, double d1) { + return this.a(entity.locX, entity.locY, entity.locZ, d0, d1, (Function) null, (Predicate) null); + } + + @Nullable + public EntityHuman a(BlockPosition blockposition, double d0, double d1) { + return this.a((double) ((float) blockposition.getX() + 0.5F), (double) ((float) blockposition.getY() + 0.5F), (double) ((float) blockposition.getZ() + 0.5F), d0, d1, (Function) null, (Predicate) null); + } + + @Nullable + public EntityHuman a(double d0, double d1, double d2, double d3, double d4, @Nullable Function function, @Nullable Predicate predicate) { + double d5 = -1.0D; + EntityHuman entityhuman = null; + + for (int i = 0; i < this.players.size(); ++i) { + EntityHuman entityhuman1 = (EntityHuman) this.players.get(i); + + // Paper start + // move distance check up, if set, check distance^2 is less than XZlimit^2, continue + // 4th method param is XZlimit (at least at the time of commit) + double d6 = entityhuman1.d(d0, entityhuman1.locY, d2); + if (d3 < 0.0D || d6 < d3 * d3) + if (!entityhuman1.abilities.isInvulnerable && entityhuman1.isAlive() && !entityhuman1.isSpectator() && (predicate == null || predicate.test(entityhuman1))) { + // Paper end + double d7 = d3; + + if (entityhuman1.isSneaking()) { + d7 = d3 * 0.800000011920929D; + } + + if (entityhuman1.isInvisible()) { + float f = entityhuman1.dk(); + + if (f < 0.1F) { + f = 0.1F; + } + + d7 *= (double) (0.7F * f); + } + + if (function != null) { + d7 *= (Double) MoreObjects.firstNonNull(function.apply(entityhuman1), 1.0D); + } + + if ((d4 < 0.0D || Math.abs(entityhuman1.locY - d1) < d4 * d4) && (d3 < 0.0D || d6 < d7 * d7) && (d5 == -1.0D || d6 < d5)) { + d5 = d6; + entityhuman = entityhuman1; + } + } + } + + return entityhuman; + } + + @Nullable + public EntityHuman a(String s) { + // Paper start - World EntityHuman Lookup Optimizations + /* + for (int i = 0; i < this.players.size(); ++i) { + EntityHuman entityhuman = (EntityHuman) this.players.get(i); + + if (s.equals(entityhuman.getDisplayName().getString())) { + return entityhuman; + } + } + + return null; + */ + return this.playersByName.get(s); + // Paper end + } + + @Nullable + public EntityHuman b(UUID uuid) { + // Paper start - World EntityHuman Lookup Optimizations + /* + for (int i = 0; i < this.players.size(); ++i) { + EntityHuman entityhuman = (EntityHuman) this.players.get(i); + + if (uuid.equals(entityhuman.getUniqueID())) { + return entityhuman; + } + } + + return null; + */ + Entity entity = ((WorldServer)this).entitiesByUUID.get(uuid); + return entity instanceof EntityHuman ? (EntityHuman) entity : null; + // Paper end + } + + public void checkSession() throws ExceptionWorldConflict { + this.dataManager.checkSession(); + } + + public long getSeed() { + return this.worldData.getSeed(); + } + + public long getTime() { + return this.worldData.getTime(); + } + + public long getDayTime() { + return this.worldData.getDayTime(); + } + + public void setDayTime(long i) { + this.worldData.setDayTime(i); + } + + public BlockPosition getSpawn() { + BlockPosition blockposition = new BlockPosition(this.worldData.b(), this.worldData.c(), this.worldData.d()); + + if (!this.getWorldBorder().a(blockposition)) { + blockposition = this.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING, new BlockPosition(this.getWorldBorder().getCenterX(), 0.0D, this.getWorldBorder().getCenterZ())); + } + + return blockposition; + } + + public void v(BlockPosition blockposition) { + this.worldData.setSpawn(blockposition); + } + + public boolean a(EntityHuman entityhuman, BlockPosition blockposition) { + return true; + } + + public void broadcastEntityEffect(Entity entity, byte b0) {} + + public IChunkProvider getChunkProvider() { + return this.chunkProvider; + } + + public void playBlockAction(BlockPosition blockposition, Block block, int i, int j) { + this.getType(blockposition).a(this, blockposition, i, j); + } + + public IDataManager getDataManager() { + return this.dataManager; + } + + public WorldData getWorldData() { + return this.worldData; + } + + public GameRules getGameRules() { + return this.worldData.w(); + } + + public void everyoneSleeping() {} + + // CraftBukkit start + // Calls the method that checks to see if players are sleeping + // Called by CraftPlayer.setPermanentSleeping() + public void checkSleepStatus() { + if (!this.isClientSide) { + this.everyoneSleeping(); + } + } + // CraftBukkit end + + public float g(float f) { + return (this.q + (this.r - this.q) * f) * this.i(f); + } + + public float i(float f) { + return this.o + (this.p - this.o) * f; + } + + public boolean Y() { + return this.worldProvider.g() && !this.worldProvider.h() ? (double) this.g(1.0F) > 0.9D : false; + } + + public boolean isRaining() { + return (double) this.i(1.0F) > 0.2D; + } + + public boolean isRainingAt(BlockPosition blockposition) { + return !this.isRaining() ? false : (!this.e(blockposition) ? false : (this.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING, blockposition).getY() > blockposition.getY() ? false : this.getBiome(blockposition).c() == BiomeBase.Precipitation.RAIN)); + } + + public boolean x(BlockPosition blockposition) { + BiomeBase biomebase = this.getBiome(blockposition); + + return biomebase.d(); + } + + @Nullable + public PersistentCollection h() { + return this.worldMaps; + } + + public void a(int i, BlockPosition blockposition, int j) { + for (int k = 0; k < this.v.size(); ++k) { + ((IWorldAccess) this.v.get(k)).a(i, blockposition, j); + } + + } + + public void triggerEffect(int i, BlockPosition blockposition, int j) { + this.a((EntityHuman) null, i, blockposition, j); + } + + public void a(@Nullable EntityHuman entityhuman, int i, BlockPosition blockposition, int j) { + try { + for (int k = 0; k < this.v.size(); ++k) { + ((IWorldAccess) this.v.get(k)).a(entityhuman, i, blockposition, j); + } + + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Playing level event"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Level event being played"); + + crashreportsystemdetails.a("Block coordinates", (Object) CrashReportSystemDetails.a(blockposition)); + crashreportsystemdetails.a("Event source", (Object) entityhuman); + crashreportsystemdetails.a("Event type", (Object) i); + crashreportsystemdetails.a("Event data", (Object) j); + throw new ReportedException(crashreport); + } + } + + public int getHeight() { + return 256; + } + + public int ab() { + return this.worldProvider.h() ? 128 : 256; + } + + public CrashReportSystemDetails a(CrashReport crashreport) { + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Affected level", 1); + + crashreportsystemdetails.a("Level name", (Object) (this.worldData == null ? "????" : this.worldData.getName())); + crashreportsystemdetails.a("All players", () -> { + return this.players.size() + " total; " + this.players; + }); + crashreportsystemdetails.a("Chunk stats", () -> { + return this.chunkProvider.getName(); + }); + + try { + this.worldData.a(crashreportsystemdetails); + } catch (Throwable throwable) { + crashreportsystemdetails.a("Level Data Unobtainable", throwable); + } + + return crashreportsystemdetails; + } + + public void c(int i, BlockPosition blockposition, int j) { + for (int k = 0; k < this.v.size(); ++k) { + IWorldAccess iworldaccess = (IWorldAccess) this.v.get(k); + + iworldaccess.b(i, blockposition, j); + } + + } + + public abstract Scoreboard getScoreboard(); + + public void updateAdjacentComparators(BlockPosition blockposition, Block block) { + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + BlockPosition blockposition1 = blockposition.shift(enumdirection); + + if (this.isLoaded(blockposition1)) { + IBlockData iblockdata = this.getType(blockposition1); + + if (iblockdata.getBlock() == Blocks.COMPARATOR) { + iblockdata.doPhysics(this, blockposition1, block, blockposition); + } else if (iblockdata.isOccluding()) { + blockposition1 = blockposition1.shift(enumdirection); + iblockdata = this.getType(blockposition1); + if (iblockdata.getBlock() == Blocks.COMPARATOR) { + iblockdata.doPhysics(this, blockposition1, block, blockposition); + } + } + } + } + + } + + public DifficultyDamageScaler getDamageScaler(BlockPosition blockposition) { + long i = 0L; + float f = 0.0F; + + if (this.isLoaded(blockposition)) { + f = this.ah(); + i = this.getChunkAtWorldCoords(blockposition).m(); + } + + return new DifficultyDamageScaler(this.getDifficulty(), this.getDayTime(), i, f); + } + + public int c() { + return this.G; + } + + public void c(int i) { + this.G = i; + } + + public void d(int i) { + this.H = i; + } + + public PersistentVillage af() { + return this.villages; + } + + public WorldBorder getWorldBorder() { + return this.K; + } + + public boolean isSpawnChunk(int i, int j) { return e(i, j); } // Paper - OBFHELPER + public boolean e(int i, int j) { + BlockPosition blockposition = this.getSpawn(); + int k = i * 16 + 8 - blockposition.getX(); + int l = j * 16 + 8 - blockposition.getZ(); + boolean flag = true; + short keepLoadedRange = paperConfig.keepLoadedRange; // Paper + + return k >= -keepLoadedRange && k <= keepLoadedRange && l >= -keepLoadedRange && l <= keepLoadedRange && this.keepSpawnInMemory; // CraftBukkit - Added 'this.keepSpawnInMemory' // Paper - Re-add range var + } + + public LongSet ag() { + ForcedChunk forcedchunk = (ForcedChunk) this.a(this.worldProvider.getDimensionManager(), ForcedChunk::new, "chunks"); + + return (LongSet) (forcedchunk != null ? LongSets.unmodifiable(forcedchunk.a()) : LongSets.EMPTY_SET); + } + + public boolean isForceLoaded(int i, int j) { + ForcedChunk forcedchunk = (ForcedChunk) this.a(this.worldProvider.getDimensionManager(), ForcedChunk::new, "chunks"); + + return forcedchunk != null && forcedchunk.a().contains(ChunkCoordIntPair.a(i, j)); + } + + public boolean setForceLoaded(int i, int j, boolean flag) { + String s = "chunks"; + ForcedChunk forcedchunk = (ForcedChunk) this.a(this.worldProvider.getDimensionManager(), ForcedChunk::new, "chunks"); + + if (forcedchunk == null) { + forcedchunk = new ForcedChunk("chunks"); + this.a(this.worldProvider.getDimensionManager(), "chunks", (PersistentBase) forcedchunk); + } + + long k = ChunkCoordIntPair.a(i, j); + boolean flag1; + + if (flag) { + flag1 = forcedchunk.a().add(k); + if (flag1) { + this.getChunkAt(i, j); + } + } else { + flag1 = forcedchunk.a().remove(k); + } + + forcedchunk.a(flag1); + return flag1; + } + + public void a(Packet packet) { + throw new UnsupportedOperationException("Can't send packets to server unless you're on the client."); + } + + @Nullable + public BlockPosition a(String s, BlockPosition blockposition, int i, boolean flag) { + return null; + } + + public WorldProvider o() { + return this.worldProvider; + } + + public Random m() { + return this.random; + } + + public abstract CraftingManager getCraftingManager(); + + public abstract TagRegistry F(); +} diff --git a/src/main/java/net/minecraft/server/WorldBorder.java b/src/main/java/net/minecraft/server/WorldBorder.java new file mode 100644 index 000000000000..a2e856952ea3 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldBorder.java @@ -0,0 +1,359 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Iterator; +import java.util.List; + +public class WorldBorder { + + private final List a = Lists.newArrayList(); + private double b = 0.2D; + private double c = 5.0D; + private int d = 15; + private int e = 5; + private double f; + private double g; + private int h = 29999984; + private WorldBorder.a i = new WorldBorder.c(6.0E7D); + public WorldServer world; // CraftBukkit + + public WorldBorder() {} + + public boolean isInBounds(BlockPosition blockposition) { return a(blockposition); }public boolean a(BlockPosition blockposition) { // Paper - OBFHELPER + return (double) (blockposition.getX() + 1) > this.b() && (double) blockposition.getX() < this.d() && (double) (blockposition.getZ() + 1) > this.c() && (double) blockposition.getZ() < this.e(); + } + + // Paper start + private final BlockPosition.MutableBlockPosition mutPos = new BlockPosition.MutableBlockPosition(); + public boolean isBlockInBounds(int chunkX, int chunkZ) { + mutPos.setValues(chunkX, 64, chunkZ); + return isInBounds(mutPos); + } + public boolean isChunkInBounds(int chunkX, int chunkZ) { + mutPos.setValues(((chunkX << 4) + 15), 64, (chunkZ << 4) + 15); + return isInBounds(mutPos); + } + // Paper end + + public boolean isInBounds(ChunkCoordIntPair chunkcoordintpair) { + return (double) chunkcoordintpair.f() > this.b() && (double) chunkcoordintpair.d() < this.d() && (double) chunkcoordintpair.g() > this.c() && (double) chunkcoordintpair.e() < this.e(); + } + + public boolean a(AxisAlignedBB axisalignedbb) { + return axisalignedbb.maxX > this.b() && axisalignedbb.minX < this.d() && axisalignedbb.maxZ > this.c() && axisalignedbb.minZ < this.e(); + } + + public double a(Entity entity) { + return this.b(entity.locX, entity.locZ); + } + + public double b(double d0, double d1) { + double d2 = d1 - this.c(); + double d3 = this.e() - d1; + double d4 = d0 - this.b(); + double d5 = this.d() - d0; + double d6 = Math.min(d4, d5); + + d6 = Math.min(d6, d2); + return Math.min(d6, d3); + } + + public double b() { + return this.i.a(); + } + + public double c() { + return this.i.c(); + } + + public double d() { + return this.i.b(); + } + + public double e() { + return this.i.d(); + } + + public double getCenterX() { + return this.f; + } + + public double getCenterZ() { + return this.g; + } + + public void setCenter(double d0, double d1) { + this.f = d0; + this.g = d1; + this.i.k(); + Iterator iterator = this.k().iterator(); + + while (iterator.hasNext()) { + IWorldBorderListener iworldborderlistener = (IWorldBorderListener) iterator.next(); + + iworldborderlistener.a(this, d0, d1); + } + + } + + public double getSize() { + return this.i.e(); + } + + public long i() { + return this.i.g(); + } + + public double j() { + return this.i.h(); + } + + public void setSize(double d0) { + this.i = new WorldBorder.c(d0); + Iterator iterator = this.k().iterator(); + + while (iterator.hasNext()) { + IWorldBorderListener iworldborderlistener = (IWorldBorderListener) iterator.next(); + + iworldborderlistener.a(this, d0); + } + + } + + public void transitionSizeBetween(double d0, double d1, long i) { + this.i = (WorldBorder.a) (d0 != d1 ? new WorldBorder.b(d0, d1, i) : new WorldBorder.c(d1)); + Iterator iterator = this.k().iterator(); + + while (iterator.hasNext()) { + IWorldBorderListener iworldborderlistener = (IWorldBorderListener) iterator.next(); + + iworldborderlistener.a(this, d0, d1, i); + } + + } + + protected List k() { + return Lists.newArrayList(this.a); + } + + public void a(IWorldBorderListener iworldborderlistener) { + if (a.contains(iworldborderlistener)) return; // CraftBukkit + this.a.add(iworldborderlistener); + } + + public void a(int i) { + this.h = i; + this.i.j(); + } + + public int l() { + return this.h; + } + + public double getDamageBuffer() { + return this.c; + } + + public void setDamageBuffer(double d0) { + this.c = d0; + Iterator iterator = this.k().iterator(); + + while (iterator.hasNext()) { + IWorldBorderListener iworldborderlistener = (IWorldBorderListener) iterator.next(); + + iworldborderlistener.c(this, d0); + } + + } + + public double getDamageAmount() { + return this.b; + } + + public void setDamageAmount(double d0) { + this.b = d0; + Iterator iterator = this.k().iterator(); + + while (iterator.hasNext()) { + IWorldBorderListener iworldborderlistener = (IWorldBorderListener) iterator.next(); + + iworldborderlistener.b(this, d0); + } + + } + + public int getWarningTime() { + return this.d; + } + + public void setWarningTime(int i) { + this.d = i; + Iterator iterator = this.k().iterator(); + + while (iterator.hasNext()) { + IWorldBorderListener iworldborderlistener = (IWorldBorderListener) iterator.next(); + + iworldborderlistener.a(this, i); + } + + } + + public int getWarningDistance() { + return this.e; + } + + public void setWarningDistance(int i) { + this.e = i; + Iterator iterator = this.k().iterator(); + + while (iterator.hasNext()) { + IWorldBorderListener iworldborderlistener = (IWorldBorderListener) iterator.next(); + + iworldborderlistener.b(this, i); + } + + } + + public void r() { + this.i = this.i.l(); + } + + class c implements WorldBorder.a { + + private final double b; + private double c; + private double d; + private double e; + private double f; + + public c(double d0) { + this.b = d0; + this.m(); + } + + public double a() { + return this.c; + } + + public double b() { + return this.e; + } + + public double c() { + return this.d; + } + + public double d() { + return this.f; + } + + public double e() { + return this.b; + } + + public long g() { + return 0L; + } + + public double h() { + return this.b; + } + + private void m() { + this.c = Math.max(WorldBorder.this.getCenterX() - this.b / 2.0D, (double) (-WorldBorder.this.h)); + this.d = Math.max(WorldBorder.this.getCenterZ() - this.b / 2.0D, (double) (-WorldBorder.this.h)); + this.e = Math.min(WorldBorder.this.getCenterX() + this.b / 2.0D, (double) WorldBorder.this.h); + this.f = Math.min(WorldBorder.this.getCenterZ() + this.b / 2.0D, (double) WorldBorder.this.h); + } + + public void j() { + this.m(); + } + + public void k() { + this.m(); + } + + public WorldBorder.a l() { + return this; + } + } + + class b implements WorldBorder.a { + + private final double b; + private final double c; + private final long d; + private final long e; + private final double f; + + private b(double d0, double d1, long i) { + this.b = d0; + this.c = d1; + this.f = (double) i; + this.e = SystemUtils.getMonotonicMillis(); + this.d = this.e + i; + } + + public double a() { + return Math.max(WorldBorder.this.getCenterX() - this.e() / 2.0D, (double) (-WorldBorder.this.h)); + } + + public double c() { + return Math.max(WorldBorder.this.getCenterZ() - this.e() / 2.0D, (double) (-WorldBorder.this.h)); + } + + public double b() { + return Math.min(WorldBorder.this.getCenterX() + this.e() / 2.0D, (double) WorldBorder.this.h); + } + + public double d() { + return Math.min(WorldBorder.this.getCenterZ() + this.e() / 2.0D, (double) WorldBorder.this.h); + } + + public double e() { + double d0 = (double) (SystemUtils.getMonotonicMillis() - this.e) / this.f; + + return d0 < 1.0D ? this.b + (this.c - this.b) * d0 : this.c; + } + + public long g() { + return this.d - SystemUtils.getMonotonicMillis(); + } + + public double h() { + return this.c; + } + + public void k() {} + + public void j() {} + + public WorldBorder.a l() { + return (WorldBorder.a) (this.g() <= 0L ? WorldBorder.this.new c(this.c) : this); + } + } + + interface a { + + double a(); + + double b(); + + double c(); + + double d(); + + double e(); + + long g(); + + double h(); + + void j(); + + void k(); + + WorldBorder.a l(); + } +} diff --git a/src/main/java/net/minecraft/server/WorldData.java b/src/main/java/net/minecraft/server/WorldData.java new file mode 100644 index 000000000000..b5fb95293e5d --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldData.java @@ -0,0 +1,768 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.mojang.datafixers.DataFixTypes; +import com.mojang.datafixers.DataFixer; +import com.mojang.datafixers.Dynamic; +import com.mojang.datafixers.types.JsonOps; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; +// CraftBukkit start +import org.bukkit.Bukkit; +import org.bukkit.event.weather.ThunderChangeEvent; +import org.bukkit.event.weather.WeatherChangeEvent; +// CraftBukkit end + +public class WorldData { + + private String b; + private int c; + private boolean d; + public static final EnumDifficulty a = EnumDifficulty.NORMAL; + private long e; + private WorldType f; + private NBTTagCompound g; + @Nullable + private String h; + private int i; + private int j; + private int k; + private long l; + private long m; + private long n; + private long o; + @Nullable + private final DataFixer p; + private final int q; + private boolean r; + private NBTTagCompound s; + private int t; + private String levelName; + private int v; + private int w; + private boolean x; + private int y; + private boolean z; + private int A; + private EnumGamemode B; + private boolean C; + private boolean D; + private boolean E; + private boolean F; + private EnumDifficulty G; + private boolean H; + private double I; + private double J; + private double K; + private long L; + private double M; + private double N; + private double O; + private int P; + private int Q; + private final Set R; + private final Set S; + private final Map T; + private NBTTagCompound U; + private final GameRules V; + public WorldServer world; // CraftBukkit + + protected WorldData() { + this.f = WorldType.NORMAL; + this.g = new NBTTagCompound(); + this.K = 6.0E7D; + this.N = 5.0D; + this.O = 0.2D; + this.P = 5; + this.Q = 15; + this.R = Sets.newHashSet(); + this.S = Sets.newLinkedHashSet(); + this.T = Maps.newIdentityHashMap(); + this.V = new GameRules(); + this.p = null; + this.q = 1631; + this.b(new NBTTagCompound()); + } + + public WorldData(NBTTagCompound nbttagcompound, DataFixer datafixer, int i, @Nullable NBTTagCompound nbttagcompound1) { + this.f = WorldType.NORMAL; + this.g = new NBTTagCompound(); + this.K = 6.0E7D; + this.N = 5.0D; + this.O = 0.2D; + this.P = 5; + this.Q = 15; + this.R = Sets.newHashSet(); + this.S = Sets.newLinkedHashSet(); + this.T = Maps.newIdentityHashMap(); + this.V = new GameRules(); + this.p = datafixer; + NBTTagCompound nbttagcompound2; + + if (nbttagcompound.hasKeyOfType("Version", 10)) { + nbttagcompound2 = nbttagcompound.getCompound("Version"); + this.b = nbttagcompound2.getString("Name"); + this.c = nbttagcompound2.getInt("Id"); + this.d = nbttagcompound2.getBoolean("Snapshot"); + } + + this.e = com.destroystokyo.paper.PaperConfig.seedOverride.getOrDefault(nbttagcompound.getString("LevelName"), nbttagcompound.getLong("RandomSeed")); // Paper + if (nbttagcompound.hasKeyOfType("generatorName", 8)) { + String s = nbttagcompound.getString("generatorName"); + + this.f = WorldType.getType(s); + if (this.f == null) { + this.f = WorldType.NORMAL; + } else if (this.f == WorldType.CUSTOMIZED) { + this.h = nbttagcompound.getString("generatorOptions"); + } else if (this.f.h()) { + int j = 0; + + if (nbttagcompound.hasKeyOfType("generatorVersion", 99)) { + j = nbttagcompound.getInt("generatorVersion"); + } + + this.f = this.f.a(j); + } + + this.b(nbttagcompound.getCompound("generatorOptions")); + } + + this.B = EnumGamemode.getById(nbttagcompound.getInt("GameType")); + if (nbttagcompound.hasKeyOfType("legacy_custom_options", 8)) { + this.h = nbttagcompound.getString("legacy_custom_options"); + } + + if (nbttagcompound.hasKeyOfType("MapFeatures", 99)) { + this.C = nbttagcompound.getBoolean("MapFeatures"); + } else { + this.C = true; + } + + this.i = nbttagcompound.getInt("SpawnX"); + this.j = nbttagcompound.getInt("SpawnY"); + this.k = nbttagcompound.getInt("SpawnZ"); + this.l = nbttagcompound.getLong("Time"); + if (nbttagcompound.hasKeyOfType("DayTime", 99)) { + this.m = nbttagcompound.getLong("DayTime"); + } else { + this.m = this.l; + } + + this.n = nbttagcompound.getLong("LastPlayed"); + this.o = nbttagcompound.getLong("SizeOnDisk"); + this.levelName = nbttagcompound.getString("LevelName"); + this.v = nbttagcompound.getInt("version"); + this.w = nbttagcompound.getInt("clearWeatherTime"); + this.y = nbttagcompound.getInt("rainTime"); + this.x = nbttagcompound.getBoolean("raining"); + this.A = nbttagcompound.getInt("thunderTime"); + this.z = nbttagcompound.getBoolean("thundering"); + this.D = nbttagcompound.getBoolean("hardcore"); + if (nbttagcompound.hasKeyOfType("initialized", 99)) { + this.F = nbttagcompound.getBoolean("initialized"); + } else { + this.F = true; + } + + if (nbttagcompound.hasKeyOfType("allowCommands", 99)) { + this.E = nbttagcompound.getBoolean("allowCommands"); + } else { + this.E = this.B == EnumGamemode.CREATIVE; + } + + this.q = i; + if (nbttagcompound1 != null) { + this.s = nbttagcompound1; + } + + if (nbttagcompound.hasKeyOfType("GameRules", 10)) { + this.V.a(nbttagcompound.getCompound("GameRules")); + } + + if (nbttagcompound.hasKeyOfType("Difficulty", 99)) { + this.G = EnumDifficulty.getById(nbttagcompound.getByte("Difficulty")); + } + + if (nbttagcompound.hasKeyOfType("DifficultyLocked", 1)) { + this.H = nbttagcompound.getBoolean("DifficultyLocked"); + } + + if (nbttagcompound.hasKeyOfType("BorderCenterX", 99)) { + this.I = nbttagcompound.getDouble("BorderCenterX"); + } + + if (nbttagcompound.hasKeyOfType("BorderCenterZ", 99)) { + this.J = nbttagcompound.getDouble("BorderCenterZ"); + } + + if (nbttagcompound.hasKeyOfType("BorderSize", 99)) { + this.K = nbttagcompound.getDouble("BorderSize"); + } + + if (nbttagcompound.hasKeyOfType("BorderSizeLerpTime", 99)) { + this.L = nbttagcompound.getLong("BorderSizeLerpTime"); + } + + if (nbttagcompound.hasKeyOfType("BorderSizeLerpTarget", 99)) { + this.M = nbttagcompound.getDouble("BorderSizeLerpTarget"); + } + + if (nbttagcompound.hasKeyOfType("BorderSafeZone", 99)) { + this.N = nbttagcompound.getDouble("BorderSafeZone"); + } + + if (nbttagcompound.hasKeyOfType("BorderDamagePerBlock", 99)) { + this.O = nbttagcompound.getDouble("BorderDamagePerBlock"); + } + + if (nbttagcompound.hasKeyOfType("BorderWarningBlocks", 99)) { + this.P = nbttagcompound.getInt("BorderWarningBlocks"); + } + + if (nbttagcompound.hasKeyOfType("BorderWarningTime", 99)) { + this.Q = nbttagcompound.getInt("BorderWarningTime"); + } + + if (nbttagcompound.hasKeyOfType("DimensionData", 10)) { + nbttagcompound2 = nbttagcompound.getCompound("DimensionData"); + Iterator iterator = nbttagcompound2.getKeys().iterator(); + + while (iterator.hasNext()) { + String s1 = (String) iterator.next(); + + this.T.put(DimensionManager.a(Integer.parseInt(s1)), nbttagcompound2.getCompound(s1)); + } + } + + if (nbttagcompound.hasKeyOfType("DataPacks", 10)) { + nbttagcompound2 = nbttagcompound.getCompound("DataPacks"); + NBTTagList nbttaglist = nbttagcompound2.getList("Disabled", 8); + + for (int k = 0; k < nbttaglist.size(); ++k) { + this.R.add(nbttaglist.getString(k)); + } + + NBTTagList nbttaglist1 = nbttagcompound2.getList("Enabled", 8); + + for (int l = 0; l < nbttaglist1.size(); ++l) { + this.S.add(nbttaglist1.getString(l)); + } + } + + if (nbttagcompound.hasKeyOfType("CustomBossEvents", 10)) { + this.U = nbttagcompound.getCompound("CustomBossEvents"); + } + + } + + public WorldData(WorldSettings worldsettings, String s) { + this.f = WorldType.NORMAL; + this.g = new NBTTagCompound(); + this.K = 6.0E7D; + this.N = 5.0D; + this.O = 0.2D; + this.P = 5; + this.Q = 15; + this.R = Sets.newHashSet(); + this.S = Sets.newLinkedHashSet(); + this.T = Maps.newIdentityHashMap(); + this.V = new GameRules(); + this.p = null; + this.q = 1631; + this.a(worldsettings); + this.levelName = s; + this.G = WorldData.a; + this.F = false; + } + + public void a(WorldSettings worldsettings) { + this.e = worldsettings.d(); + this.B = worldsettings.e(); + this.C = worldsettings.g(); + this.D = worldsettings.f(); + this.f = worldsettings.h(); + this.b((NBTTagCompound) Dynamic.convert(JsonOps.INSTANCE, DynamicOpsNBT.a, worldsettings.j())); + this.E = worldsettings.i(); + } + + public NBTTagCompound a(@Nullable NBTTagCompound nbttagcompound) { + this.Q(); + if (nbttagcompound == null) { + nbttagcompound = this.s; + } + + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + this.a(nbttagcompound1, nbttagcompound); + return nbttagcompound1; + } + + private void a(NBTTagCompound nbttagcompound, NBTTagCompound nbttagcompound1) { + NBTTagCompound nbttagcompound2 = new NBTTagCompound(); + + nbttagcompound2.setString("Name", "1.13.2"); + nbttagcompound2.setInt("Id", 1631); + nbttagcompound2.setBoolean("Snapshot", false); + nbttagcompound.set("Version", nbttagcompound2); + nbttagcompound.setInt("DataVersion", 1631); + if (org.bukkit.craftbukkit.util.CraftMagicNumbers.INSTANCE.getDataVersion() != 1631) throw new AssertionError(); // CraftBukkit - sentinel + nbttagcompound.setLong("RandomSeed", this.e); + nbttagcompound.setString("generatorName", this.f.b()); + nbttagcompound.setInt("generatorVersion", this.f.getVersion()); + if (!this.g.isEmpty()) { + nbttagcompound.set("generatorOptions", this.g); + } + + if (this.h != null) { + nbttagcompound.setString("legacy_custom_options", this.h); + } + + nbttagcompound.setInt("GameType", this.B.getId()); + nbttagcompound.setBoolean("MapFeatures", this.C); + nbttagcompound.setInt("SpawnX", this.i); + nbttagcompound.setInt("SpawnY", this.j); + nbttagcompound.setInt("SpawnZ", this.k); + nbttagcompound.setLong("Time", this.l); + nbttagcompound.setLong("DayTime", this.m); + nbttagcompound.setLong("SizeOnDisk", this.o); + nbttagcompound.setLong("LastPlayed", SystemUtils.getTimeMillis()); + nbttagcompound.setString("LevelName", this.levelName); + nbttagcompound.setInt("version", this.v); + nbttagcompound.setInt("clearWeatherTime", this.w); + nbttagcompound.setInt("rainTime", this.y); + nbttagcompound.setBoolean("raining", this.x); + nbttagcompound.setInt("thunderTime", this.A); + nbttagcompound.setBoolean("thundering", this.z); + nbttagcompound.setBoolean("hardcore", this.D); + nbttagcompound.setBoolean("allowCommands", this.E); + nbttagcompound.setBoolean("initialized", this.F); + nbttagcompound.setDouble("BorderCenterX", this.I); + nbttagcompound.setDouble("BorderCenterZ", this.J); + nbttagcompound.setDouble("BorderSize", this.K); + nbttagcompound.setLong("BorderSizeLerpTime", this.L); + nbttagcompound.setDouble("BorderSafeZone", this.N); + nbttagcompound.setDouble("BorderDamagePerBlock", this.O); + nbttagcompound.setDouble("BorderSizeLerpTarget", this.M); + nbttagcompound.setDouble("BorderWarningBlocks", (double) this.P); + nbttagcompound.setDouble("BorderWarningTime", (double) this.Q); + if (this.G != null) { + nbttagcompound.setByte("Difficulty", (byte) this.G.a()); + } + + nbttagcompound.setBoolean("DifficultyLocked", this.H); + nbttagcompound.set("GameRules", this.V.a()); + NBTTagCompound nbttagcompound3 = new NBTTagCompound(); + Iterator iterator = this.T.entrySet().iterator(); + + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + + nbttagcompound3.set(String.valueOf(((DimensionManager) entry.getKey()).getDimensionID()), (NBTBase) entry.getValue()); + } + + nbttagcompound.set("DimensionData", nbttagcompound3); + if (nbttagcompound1 != null) { + nbttagcompound.set("Player", nbttagcompound1); + } + + NBTTagCompound nbttagcompound4 = new NBTTagCompound(); + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator1 = this.S.iterator(); + + while (iterator1.hasNext()) { + String s = (String) iterator1.next(); + + nbttaglist.add((NBTBase) (new NBTTagString(s))); + } + + nbttagcompound4.set("Enabled", nbttaglist); + NBTTagList nbttaglist1 = new NBTTagList(); + Iterator iterator2 = this.R.iterator(); + + while (iterator2.hasNext()) { + String s1 = (String) iterator2.next(); + + nbttaglist1.add((NBTBase) (new NBTTagString(s1))); + } + + nbttagcompound4.set("Disabled", nbttaglist1); + nbttagcompound.set("DataPacks", nbttagcompound4); + if (this.U != null) { + nbttagcompound.set("CustomBossEvents", this.U); + } + + nbttagcompound.setString("Bukkit.Version", Bukkit.getName() + "/" + Bukkit.getVersion() + "/" + Bukkit.getBukkitVersion()); // CraftBukkit + } + + public long getSeed() { + return this.e; + } + + public int b() { + return this.i; + } + + public int c() { + return this.j; + } + + public int d() { + return this.k; + } + + public long getTime() { + return this.l; + } + + public long getDayTime() { + return this.m; + } + + private void Q() { + if (!this.r && this.s != null) { + if (this.q < 1631) { + if (this.p == null) { + throw new NullPointerException("Fixer Upper not set inside LevelData, and the player tag is not upgraded."); + } + + this.s = GameProfileSerializer.a(this.p, DataFixTypes.PLAYER, this.s, this.q); + } + + this.t = this.s.getInt("Dimension"); + this.r = true; + } + } + + public NBTTagCompound h() { + this.Q(); + return this.s; + } + + public void setTime(long i) { + this.l = i; + } + + public void setDayTime(long i) { + this.m = i; + } + + public void setSpawn(BlockPosition blockposition) { + this.i = blockposition.getX(); + this.j = blockposition.getY(); + this.k = blockposition.getZ(); + } + + public String getName() { + return this.levelName; + } + + public void a(String s) { + this.levelName = s; + } + + public int k() { + return this.v; + } + + public void d(int i) { + this.v = i; + } + + public int z() { + return this.w; + } + + public void g(int i) { + this.w = i; + } + + public boolean isThundering() { + return this.z; + } + + public void setThundering(boolean flag) { + // CraftBukkit start + org.bukkit.World world = Bukkit.getWorld(getName()); + if (world != null) { + ThunderChangeEvent thunder = new ThunderChangeEvent(world, flag); + Bukkit.getServer().getPluginManager().callEvent(thunder); + if (thunder.isCancelled()) { + return; + } + } + // CraftBukkit end + this.z = flag; + } + + public int getThunderDuration() { + return this.A; + } + + public void setThunderDuration(int i) { + this.A = i; + } + + public boolean hasStorm() { + return this.x; + } + + public void setStorm(boolean flag) { + // CraftBukkit start + org.bukkit.World world = Bukkit.getWorld(getName()); + if (world != null) { + WeatherChangeEvent weather = new WeatherChangeEvent(world, flag); + Bukkit.getServer().getPluginManager().callEvent(weather); + if (weather.isCancelled()) { + return; + } + } + // CraftBukkit end + this.x = flag; + } + + public int getWeatherDuration() { + return this.y; + } + + public void setWeatherDuration(int i) { + this.y = i; + } + + public EnumGamemode getGameType() { + return this.B; + } + + public boolean shouldGenerateMapFeatures() { + return this.C; + } + + public void f(boolean flag) { + this.C = flag; + } + + public void setGameType(EnumGamemode enumgamemode) { + this.B = enumgamemode; + } + + public boolean isHardcore() { + return this.D; + } + + public void g(boolean flag) { + this.D = flag; + } + + public WorldType getType() { + return this.f; + } + + public void a(WorldType worldtype) { + this.f = worldtype; + } + + public NBTTagCompound getGeneratorOptions() { + return this.g; + } + + public void b(NBTTagCompound nbttagcompound) { + this.g = nbttagcompound; + } + + public boolean u() { + return this.E; + } + + public void c(boolean flag) { + this.E = flag; + } + + public boolean v() { + return this.F; + } + + public void d(boolean flag) { + this.F = flag; + } + + public GameRules w() { + return this.V; + } + + public double B() { + return this.I; + } + + public double C() { + return this.J; + } + + public double D() { + return this.K; + } + + public void a(double d0) { + this.K = d0; + } + + public long E() { + return this.L; + } + + public void c(long i) { + this.L = i; + } + + public double F() { + return this.M; + } + + public void b(double d0) { + this.M = d0; + } + + public void c(double d0) { + this.J = d0; + } + + public void d(double d0) { + this.I = d0; + } + + public double G() { + return this.N; + } + + public void e(double d0) { + this.N = d0; + } + + public double H() { + return this.O; + } + + public void f(double d0) { + this.O = d0; + } + + public int I() { + return this.P; + } + + public int J() { + return this.Q; + } + + public void h(int i) { + this.P = i; + } + + public void i(int i) { + this.Q = i; + } + + public EnumDifficulty getDifficulty() { + return this.G; + } + + public void setDifficulty(EnumDifficulty enumdifficulty) { + this.G = enumdifficulty; + // CraftBukkit start + PacketPlayOutServerDifficulty packet = new PacketPlayOutServerDifficulty(this.getDifficulty(), this.isDifficultyLocked()); + for (EntityPlayer player : (java.util.List) (java.util.List) world.players) { + player.playerConnection.sendPacket(packet); + } + // CraftBukkit end + } + + public boolean isDifficultyLocked() { + return this.H; + } + + public void e(boolean flag) { + this.H = flag; + } + + public void a(CrashReportSystemDetails crashreportsystemdetails) { + crashreportsystemdetails.a("Level seed", () -> { + return String.valueOf(this.getSeed()); + }); + crashreportsystemdetails.a("Level generator", () -> { + return String.format("ID %02d - %s, ver %d. Features enabled: %b", this.f.i(), this.f.name(), this.f.getVersion(), this.C); + }); + crashreportsystemdetails.a("Level generator options", () -> { + return this.g.toString(); + }); + crashreportsystemdetails.a("Level spawn location", () -> { + return CrashReportSystemDetails.a(this.i, this.j, this.k); + }); + crashreportsystemdetails.a("Level time", () -> { + return String.format("%d game time, %d day time", this.l, this.m); + }); + crashreportsystemdetails.a("Level dimension", () -> { + return String.valueOf(this.t); + }); + crashreportsystemdetails.a("Level storage version", () -> { + String s = "Unknown?"; + + try { + switch (this.v) { + case 19132: + s = "McRegion"; + break; + case 19133: + s = "Anvil"; + } + } catch (Throwable throwable) { + ; + } + + return String.format("0x%05X - %s", this.v, s); + }); + crashreportsystemdetails.a("Level weather", () -> { + return String.format("Rain time: %d (now: %b), thunder time: %d (now: %b)", this.y, this.x, this.A, this.z); + }); + crashreportsystemdetails.a("Level game mode", () -> { + return String.format("Game mode: %s (ID %d). Hardcore: %b. Cheats: %b", this.B.b(), this.B.getId(), this.D, this.E); + }); + } + + public NBTTagCompound a(DimensionManager dimensionmanager) { + NBTTagCompound nbttagcompound = (NBTTagCompound) this.T.get(dimensionmanager); + + return nbttagcompound == null ? new NBTTagCompound() : nbttagcompound; + } + + public void a(DimensionManager dimensionmanager, NBTTagCompound nbttagcompound) { + this.T.put(dimensionmanager, nbttagcompound); + } + + public Set N() { + return this.R; + } + + public Set O() { + return this.S; + } + + @Nullable + public NBTTagCompound P() { + return this.U; + } + + public void c(@Nullable NBTTagCompound nbttagcompound) { + this.U = nbttagcompound; + } + + // CraftBukkit start - Check if the name stored in NBT is the correct one + public void checkName( String name ) { + if ( !this.levelName.equals( name ) ) { + this.levelName = name; + } + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/WorldGenEndCityPieces.java b/src/main/java/net/minecraft/server/WorldGenEndCityPieces.java new file mode 100644 index 000000000000..0a223cfe5a40 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenEndCityPieces.java @@ -0,0 +1,287 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Iterator; +import java.util.List; +import java.util.Random; + +public class WorldGenEndCityPieces { + + private static final DefinedStructureInfo a = (new DefinedStructureInfo()).a(true); + private static final DefinedStructureInfo b = (new DefinedStructureInfo()).a(true).a(Blocks.AIR); + private static final WorldGenEndCityPieces.PieceGenerator c = new WorldGenEndCityPieces.PieceGenerator() { + public void a() {} + + public boolean a(DefinedStructureManager definedstructuremanager, int i, WorldGenEndCityPieces.Piece worldgenendcitypieces_piece, BlockPosition blockposition, List list, Random random) { + if (i > 8) { + return false; + } else { + EnumBlockRotation enumblockrotation = worldgenendcitypieces_piece.b.c(); + WorldGenEndCityPieces.Piece worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece, blockposition, "base_floor", enumblockrotation, true)); + int j = random.nextInt(3); + + if (j == 0) { + WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(-1, 4, -1), "base_roof", enumblockrotation, true)); + } else if (j == 1) { + worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(-1, 0, -1), "second_floor_2", enumblockrotation, false)); + worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(-1, 8, -1), "second_roof", enumblockrotation, false)); + WorldGenEndCityPieces.b(definedstructuremanager, WorldGenEndCityPieces.e, i + 1, worldgenendcitypieces_piece1, (BlockPosition) null, list, random); + } else if (j == 2) { + worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(-1, 0, -1), "second_floor_2", enumblockrotation, false)); + worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(-1, 4, -1), "third_floor_2", enumblockrotation, false)); + worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(-1, 8, -1), "third_roof", enumblockrotation, true)); + WorldGenEndCityPieces.b(definedstructuremanager, WorldGenEndCityPieces.e, i + 1, worldgenendcitypieces_piece1, (BlockPosition) null, list, random); + } + + return true; + } + } + }; + private static final List> d = Lists.newArrayList(new Tuple[] { new Tuple<>(EnumBlockRotation.NONE, new BlockPosition(1, -1, 0)), new Tuple<>(EnumBlockRotation.CLOCKWISE_90, new BlockPosition(6, -1, 1)), new Tuple<>(EnumBlockRotation.COUNTERCLOCKWISE_90, new BlockPosition(0, -1, 5)), new Tuple<>(EnumBlockRotation.CLOCKWISE_180, new BlockPosition(5, -1, 6))}); + private static final WorldGenEndCityPieces.PieceGenerator e = new WorldGenEndCityPieces.PieceGenerator() { + public void a() {} + + public boolean a(DefinedStructureManager definedstructuremanager, int i, WorldGenEndCityPieces.Piece worldgenendcitypieces_piece, BlockPosition blockposition, List list, Random random) { + EnumBlockRotation enumblockrotation = worldgenendcitypieces_piece.b.c(); + WorldGenEndCityPieces.Piece worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece, new BlockPosition(3 + random.nextInt(2), -3, 3 + random.nextInt(2)), "tower_base", enumblockrotation, true)); + + worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(0, 7, 0), "tower_piece", enumblockrotation, true)); + WorldGenEndCityPieces.Piece worldgenendcitypieces_piece2 = random.nextInt(3) == 0 ? worldgenendcitypieces_piece1 : null; + int j = 1 + random.nextInt(3); + + for (int k = 0; k < j; ++k) { + worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(0, 4, 0), "tower_piece", enumblockrotation, true)); + if (k < j - 1 && random.nextBoolean()) { + worldgenendcitypieces_piece2 = worldgenendcitypieces_piece1; + } + } + + if (worldgenendcitypieces_piece2 != null) { + Iterator iterator = WorldGenEndCityPieces.d.iterator(); + + while (iterator.hasNext()) { + Tuple tuple = (Tuple) iterator.next(); + + if (random.nextBoolean()) { + WorldGenEndCityPieces.Piece worldgenendcitypieces_piece3 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece2, (BlockPosition) tuple.b(), "bridge_end", enumblockrotation.a((EnumBlockRotation) tuple.a()), true)); + + WorldGenEndCityPieces.b(definedstructuremanager, WorldGenEndCityPieces.f, i + 1, worldgenendcitypieces_piece3, (BlockPosition) null, list, random); + } + } + + WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(-1, 4, -1), "tower_top", enumblockrotation, true)); + } else { + if (i != 7) { + return WorldGenEndCityPieces.b(definedstructuremanager, WorldGenEndCityPieces.h, i + 1, worldgenendcitypieces_piece1, (BlockPosition) null, list, random); + } + + WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(-1, 4, -1), "tower_top", enumblockrotation, true)); + } + + return true; + } + }; + private static final WorldGenEndCityPieces.PieceGenerator f = new WorldGenEndCityPieces.PieceGenerator() { + public boolean a; + + public void a() { + this.a = false; + } + + public boolean a(DefinedStructureManager definedstructuremanager, int i, WorldGenEndCityPieces.Piece worldgenendcitypieces_piece, BlockPosition blockposition, List list, Random random) { + EnumBlockRotation enumblockrotation = worldgenendcitypieces_piece.b.c(); + int j = random.nextInt(4) + 1; + WorldGenEndCityPieces.Piece worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece, new BlockPosition(0, 0, -4), "bridge_piece", enumblockrotation, true)); + + worldgenendcitypieces_piece1.o = -1; + byte b0 = 0; + + for (int k = 0; k < j; ++k) { + if (random.nextBoolean()) { + worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(0, b0, -4), "bridge_piece", enumblockrotation, true)); + b0 = 0; + } else { + if (random.nextBoolean()) { + worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(0, b0, -4), "bridge_steep_stairs", enumblockrotation, true)); + } else { + worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(0, b0, -8), "bridge_gentle_stairs", enumblockrotation, true)); + } + + b0 = 4; + } + } + + if (!this.a && random.nextInt(10 - i) == 0) { + WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(-8 + random.nextInt(8), b0, -70 + random.nextInt(10)), "ship", enumblockrotation, true)); + this.a = true; + } else if (!WorldGenEndCityPieces.b(definedstructuremanager, WorldGenEndCityPieces.c, i + 1, worldgenendcitypieces_piece1, new BlockPosition(-3, b0 + 1, -11), list, random)) { + return false; + } + + worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(4, b0, 0), "bridge_end", enumblockrotation.a(EnumBlockRotation.CLOCKWISE_180), true)); + worldgenendcitypieces_piece1.o = -1; + return true; + } + }; + private static final List> g = Lists.newArrayList(new Tuple[] { new Tuple<>(EnumBlockRotation.NONE, new BlockPosition(4, -1, 0)), new Tuple<>(EnumBlockRotation.CLOCKWISE_90, new BlockPosition(12, -1, 4)), new Tuple<>(EnumBlockRotation.COUNTERCLOCKWISE_90, new BlockPosition(0, -1, 8)), new Tuple<>(EnumBlockRotation.CLOCKWISE_180, new BlockPosition(8, -1, 12))}); + private static final WorldGenEndCityPieces.PieceGenerator h = new WorldGenEndCityPieces.PieceGenerator() { + public void a() {} + + public boolean a(DefinedStructureManager definedstructuremanager, int i, WorldGenEndCityPieces.Piece worldgenendcitypieces_piece, BlockPosition blockposition, List list, Random random) { + EnumBlockRotation enumblockrotation = worldgenendcitypieces_piece.b.c(); + WorldGenEndCityPieces.Piece worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece, new BlockPosition(-3, 4, -3), "fat_tower_base", enumblockrotation, true)); + + worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(0, 4, 0), "fat_tower_middle", enumblockrotation, true)); + + for (int j = 0; j < 2 && random.nextInt(3) != 0; ++j) { + worldgenendcitypieces_piece1 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(0, 8, 0), "fat_tower_middle", enumblockrotation, true)); + Iterator iterator = WorldGenEndCityPieces.g.iterator(); + + while (iterator.hasNext()) { + Tuple tuple = (Tuple) iterator.next(); + + if (random.nextBoolean()) { + WorldGenEndCityPieces.Piece worldgenendcitypieces_piece2 = WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, (BlockPosition) tuple.b(), "bridge_end", enumblockrotation.a((EnumBlockRotation) tuple.a()), true)); + + WorldGenEndCityPieces.b(definedstructuremanager, WorldGenEndCityPieces.f, i + 1, worldgenendcitypieces_piece2, (BlockPosition) null, list, random); + } + } + } + + WorldGenEndCityPieces.b(list, WorldGenEndCityPieces.b(definedstructuremanager, worldgenendcitypieces_piece1, new BlockPosition(-2, 8, -2), "fat_tower_top", enumblockrotation, true)); + return true; + } + }; + + public static void a() { + WorldGenFactory.a(WorldGenEndCityPieces.Piece.class, "ECP"); + } + + private static WorldGenEndCityPieces.Piece b(DefinedStructureManager definedstructuremanager, WorldGenEndCityPieces.Piece worldgenendcitypieces_piece, BlockPosition blockposition, String s, EnumBlockRotation enumblockrotation, boolean flag) { + WorldGenEndCityPieces.Piece worldgenendcitypieces_piece1 = new WorldGenEndCityPieces.Piece(definedstructuremanager, s, worldgenendcitypieces_piece.c, enumblockrotation, flag); + BlockPosition blockposition1 = worldgenendcitypieces_piece.a.a(worldgenendcitypieces_piece.b, blockposition, worldgenendcitypieces_piece1.b, BlockPosition.ZERO); + + worldgenendcitypieces_piece1.a(blockposition1.getX(), blockposition1.getY(), blockposition1.getZ()); + return worldgenendcitypieces_piece1; + } + + public static void a(DefinedStructureManager definedstructuremanager, BlockPosition blockposition, EnumBlockRotation enumblockrotation, List list, Random random) { + WorldGenEndCityPieces.h.a(); + WorldGenEndCityPieces.c.a(); + WorldGenEndCityPieces.f.a(); + WorldGenEndCityPieces.e.a(); + WorldGenEndCityPieces.Piece worldgenendcitypieces_piece = b(list, new WorldGenEndCityPieces.Piece(definedstructuremanager, "base_floor", blockposition, enumblockrotation, true)); + + worldgenendcitypieces_piece = b(list, b(definedstructuremanager, worldgenendcitypieces_piece, new BlockPosition(-1, 0, -1), "second_floor_1", enumblockrotation, false)); + worldgenendcitypieces_piece = b(list, b(definedstructuremanager, worldgenendcitypieces_piece, new BlockPosition(-1, 4, -1), "third_floor_1", enumblockrotation, false)); + worldgenendcitypieces_piece = b(list, b(definedstructuremanager, worldgenendcitypieces_piece, new BlockPosition(-1, 8, -1), "third_roof", enumblockrotation, true)); + b(definedstructuremanager, WorldGenEndCityPieces.e, 1, worldgenendcitypieces_piece, (BlockPosition) null, list, random); + } + + private static WorldGenEndCityPieces.Piece b(List list, WorldGenEndCityPieces.Piece worldgenendcitypieces_piece) { + list.add(worldgenendcitypieces_piece); + return worldgenendcitypieces_piece; + } + + private static boolean b(DefinedStructureManager definedstructuremanager, WorldGenEndCityPieces.PieceGenerator worldgenendcitypieces_piecegenerator, int i, WorldGenEndCityPieces.Piece worldgenendcitypieces_piece, BlockPosition blockposition, List list, Random random) { + if (i > 8) { + return false; + } else { + List list1 = Lists.newArrayList(); + + if (worldgenendcitypieces_piecegenerator.a(definedstructuremanager, i, worldgenendcitypieces_piece, blockposition, list1, random)) { + boolean flag = false; + int j = random.nextInt(); + Iterator iterator = list1.iterator(); + + while (iterator.hasNext()) { + StructurePiece structurepiece = (StructurePiece) iterator.next(); + + structurepiece.o = j; + StructurePiece structurepiece1 = StructurePiece.a(list, structurepiece.d()); + + if (structurepiece1 != null && structurepiece1.o != worldgenendcitypieces_piece.o) { + flag = true; + break; + } + } + + if (!flag) { + list.addAll(list1); + return true; + } + } + + return false; + } + } + + interface PieceGenerator { + + void a(); + + boolean a(DefinedStructureManager definedstructuremanager, int i, WorldGenEndCityPieces.Piece worldgenendcitypieces_piece, BlockPosition blockposition, List list, Random random); + } + + public static class Piece extends DefinedStructurePiece { + + private String d; + private EnumBlockRotation e; + private boolean f; + + public Piece() {} + + public Piece(DefinedStructureManager definedstructuremanager, String s, BlockPosition blockposition, EnumBlockRotation enumblockrotation, boolean flag) { + super(0); + this.d = s; + this.c = blockposition; + this.e = enumblockrotation; + this.f = flag; + this.a(definedstructuremanager); + } + + private void a(DefinedStructureManager definedstructuremanager) { + DefinedStructure definedstructure = definedstructuremanager.a(new MinecraftKey("end_city/" + this.d)); + DefinedStructureInfo definedstructureinfo = (this.f ? WorldGenEndCityPieces.a : WorldGenEndCityPieces.b).a().a(this.e); + + this.a(definedstructure, this.c, definedstructureinfo); + } + + protected void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + nbttagcompound.setString("Template", this.d); + nbttagcompound.setString("Rot", this.e.name()); + nbttagcompound.setBoolean("OW", this.f); + } + + protected void a(NBTTagCompound nbttagcompound, DefinedStructureManager definedstructuremanager) { + super.a(nbttagcompound, definedstructuremanager); + this.d = nbttagcompound.getString("Template"); + this.e = EnumBlockRotation.valueOf(nbttagcompound.getString("Rot")); + this.f = nbttagcompound.getBoolean("OW"); + this.a(definedstructuremanager); + } + + protected void a(String s, BlockPosition blockposition, GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox) { + if (s.startsWith("Chest")) { + BlockPosition blockposition1 = blockposition.down(); + + if (structureboundingbox.b((BaseBlockPosition) blockposition1)) { + TileEntityLootable.a(generatoraccess, random, blockposition1, LootTables.c); + } + } else if (s.startsWith("Sentry")) { + EntityShulker entityshulker = EntityTypes.SHULKER.create(generatoraccess.getMinecraftWorld()); // Paper + + entityshulker.setPosition((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D); + entityshulker.g(blockposition); + generatoraccess.addEntity(entityshulker); + } else if (s.startsWith("Elytra")) { + EntityItemFrame entityitemframe = new EntityItemFrame(generatoraccess.getMinecraftWorld(), blockposition, this.e.a(EnumDirection.SOUTH)); + + entityitemframe.setItem(new ItemStack(Items.ELYTRA)); + generatoraccess.addEntity(entityitemframe); + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldGenFeatureDesertPyramid.java b/src/main/java/net/minecraft/server/WorldGenFeatureDesertPyramid.java new file mode 100644 index 000000000000..d38758276585 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenFeatureDesertPyramid.java @@ -0,0 +1,39 @@ +package net.minecraft.server; + +public class WorldGenFeatureDesertPyramid extends WorldGenFeatureRandomScattered { + + public WorldGenFeatureDesertPyramid() {} + + protected String a() { + return "Desert_Pyramid"; + } + + public int b() { + return 3; + } + + protected StructureStart a(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, SeededRandom seededrandom, int i, int j) { + BiomeBase biomebase = chunkgenerator.getWorldChunkManager().getBiome(new BlockPosition((i << 4) + 9, 0, (j << 4) + 9), Biomes.PLAINS); + + return new WorldGenFeatureDesertPyramid.a(generatoraccess, seededrandom, i, j, biomebase); + } + + // Spigot start + protected int c(World world) { + return world.spigotConfig.desertSeed; + // Spigot end + } + + public static class a extends StructureStart { + + public a() {} + + public a(GeneratorAccess generatoraccess, SeededRandom seededrandom, int i, int j, BiomeBase biomebase) { + super(i, j, biomebase, seededrandom, generatoraccess.getSeed()); + WorldGenDesertPyramidPiece worldgendesertpyramidpiece = new WorldGenDesertPyramidPiece(seededrandom, i * 16, j * 16); + + this.a.add(worldgendesertpyramidpiece); + this.a((IBlockAccess) generatoraccess); + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldGenFeatureIgloo.java b/src/main/java/net/minecraft/server/WorldGenFeatureIgloo.java new file mode 100644 index 000000000000..de8be1aa9974 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenFeatureIgloo.java @@ -0,0 +1,44 @@ +package net.minecraft.server; + +public class WorldGenFeatureIgloo extends WorldGenFeatureRandomScattered { + + public WorldGenFeatureIgloo() {} + + protected String a() { + return "Igloo"; + } + + public int b() { + return 3; + } + + protected StructureStart a(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, SeededRandom seededrandom, int i, int j) { + BiomeBase biomebase = chunkgenerator.getWorldChunkManager().getBiome(new BlockPosition((i << 4) + 9, 0, (j << 4) + 9), Biomes.PLAINS); + + return new WorldGenFeatureIgloo.a(generatoraccess, chunkgenerator, seededrandom, i, j, biomebase); + } + + // Spigot start + protected int c(World world) { + return world.spigotConfig.iglooSeed; + // Spigot end + } + + public static class a extends StructureStart { + + public a() {} + + public a(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, SeededRandom seededrandom, int i, int j, BiomeBase biomebase) { + super(i, j, biomebase, seededrandom, generatoraccess.getSeed()); + WorldGenFeatureIglooConfiguration worldgenfeatureiglooconfiguration = (WorldGenFeatureIglooConfiguration) chunkgenerator.getFeatureConfiguration(biomebase, WorldGenerator.j); + int k = i * 16; + int l = j * 16; + BlockPosition blockposition = new BlockPosition(k, 90, l); + EnumBlockRotation enumblockrotation = EnumBlockRotation.values()[seededrandom.nextInt(EnumBlockRotation.values().length)]; + DefinedStructureManager definedstructuremanager = generatoraccess.getDataManager().h(); + + WorldGenIglooPiece.a(definedstructuremanager, blockposition, enumblockrotation, this.a, seededrandom, worldgenfeatureiglooconfiguration); + this.a((IBlockAccess) generatoraccess); + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldGenFeatureJunglePyramid.java b/src/main/java/net/minecraft/server/WorldGenFeatureJunglePyramid.java new file mode 100644 index 000000000000..c10f79864f26 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenFeatureJunglePyramid.java @@ -0,0 +1,39 @@ +package net.minecraft.server; + +public class WorldGenFeatureJunglePyramid extends WorldGenFeatureRandomScattered { + + public WorldGenFeatureJunglePyramid() {} + + protected String a() { + return "Jungle_Pyramid"; + } + + public int b() { + return 3; + } + + protected StructureStart a(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, SeededRandom seededrandom, int i, int j) { + BiomeBase biomebase = chunkgenerator.getWorldChunkManager().getBiome(new BlockPosition((i << 4) + 9, 0, (j << 4) + 9), Biomes.PLAINS); + + return new WorldGenFeatureJunglePyramid.a(generatoraccess, seededrandom, i, j, biomebase); + } + + // Spigot start + protected int c(World world) { + return world.spigotConfig.jungleSeed; + // Spigot end + } + + public static class a extends StructureStart { + + public a() {} + + public a(GeneratorAccess generatoraccess, SeededRandom seededrandom, int i, int j, BiomeBase biomebase) { + super(i, j, biomebase, seededrandom, generatoraccess.getSeed()); + WorldGenJunglePyramidPiece worldgenjunglepyramidpiece = new WorldGenJunglePyramidPiece(seededrandom, i * 16, j * 16); + + this.a.add(worldgenjunglepyramidpiece); + this.a((IBlockAccess) generatoraccess); + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldGenFeatureOceanRuin.java b/src/main/java/net/minecraft/server/WorldGenFeatureOceanRuin.java new file mode 100644 index 000000000000..ad3dc7199435 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenFeatureOceanRuin.java @@ -0,0 +1,61 @@ +package net.minecraft.server; + +import java.util.Random; + +public class WorldGenFeatureOceanRuin extends WorldGenFeatureRandomScattered { + + public WorldGenFeatureOceanRuin() {} + + public String a() { + return "Ocean_Ruin"; + } + + public int b() { + return 3; + } + + protected int a(ChunkGenerator chunkgenerator) { + return chunkgenerator.getSettings().l(); + } + + protected int b(ChunkGenerator chunkgenerator) { + return chunkgenerator.getSettings().m(); + } + + protected StructureStart a(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, SeededRandom seededrandom, int i, int j) { + BiomeBase biomebase = chunkgenerator.getWorldChunkManager().getBiome(new BlockPosition((i << 4) + 9, 0, (j << 4) + 9), (BiomeBase) null); + + return new WorldGenFeatureOceanRuin.a(generatoraccess, chunkgenerator, seededrandom, i, j, biomebase); + } + + // Spigot start + protected int c(World world) { + return world.spigotConfig.oceanSeed; + // Spigot end + } + + public static enum Temperature { + + WARM, COLD; + + private Temperature() {} + } + + public static class a extends StructureStart { + + public a() {} + + public a(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, SeededRandom seededrandom, int i, int j, BiomeBase biomebase) { + super(i, j, biomebase, seededrandom, generatoraccess.getSeed()); + WorldGenFeatureOceanRuinConfiguration worldgenfeatureoceanruinconfiguration = (WorldGenFeatureOceanRuinConfiguration) chunkgenerator.getFeatureConfiguration(biomebase, WorldGenerator.o); + int k = i * 16; + int l = j * 16; + BlockPosition blockposition = new BlockPosition(k, 90, l); + EnumBlockRotation enumblockrotation = EnumBlockRotation.values()[seededrandom.nextInt(EnumBlockRotation.values().length)]; + DefinedStructureManager definedstructuremanager = generatoraccess.getDataManager().h(); + + WorldGenFeatureOceanRuinPieces.a(definedstructuremanager, blockposition, enumblockrotation, this.a, (Random) seededrandom, worldgenfeatureoceanruinconfiguration); + this.a((IBlockAccess) generatoraccess); + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldGenFeatureOceanRuinPieces.java b/src/main/java/net/minecraft/server/WorldGenFeatureOceanRuinPieces.java new file mode 100644 index 000000000000..abeb4aa025c1 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenFeatureOceanRuinPieces.java @@ -0,0 +1,218 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Iterator; +import java.util.List; +import java.util.Random; + +public class WorldGenFeatureOceanRuinPieces { + + private static final MinecraftKey[] a = new MinecraftKey[] { new MinecraftKey("underwater_ruin/warm_1"), new MinecraftKey("underwater_ruin/warm_2"), new MinecraftKey("underwater_ruin/warm_3"), new MinecraftKey("underwater_ruin/warm_4"), new MinecraftKey("underwater_ruin/warm_5"), new MinecraftKey("underwater_ruin/warm_6"), new MinecraftKey("underwater_ruin/warm_7"), new MinecraftKey("underwater_ruin/warm_8")}; + private static final MinecraftKey[] b = new MinecraftKey[] { new MinecraftKey("underwater_ruin/brick_1"), new MinecraftKey("underwater_ruin/brick_2"), new MinecraftKey("underwater_ruin/brick_3"), new MinecraftKey("underwater_ruin/brick_4"), new MinecraftKey("underwater_ruin/brick_5"), new MinecraftKey("underwater_ruin/brick_6"), new MinecraftKey("underwater_ruin/brick_7"), new MinecraftKey("underwater_ruin/brick_8")}; + private static final MinecraftKey[] c = new MinecraftKey[] { new MinecraftKey("underwater_ruin/cracked_1"), new MinecraftKey("underwater_ruin/cracked_2"), new MinecraftKey("underwater_ruin/cracked_3"), new MinecraftKey("underwater_ruin/cracked_4"), new MinecraftKey("underwater_ruin/cracked_5"), new MinecraftKey("underwater_ruin/cracked_6"), new MinecraftKey("underwater_ruin/cracked_7"), new MinecraftKey("underwater_ruin/cracked_8")}; + private static final MinecraftKey[] d = new MinecraftKey[] { new MinecraftKey("underwater_ruin/mossy_1"), new MinecraftKey("underwater_ruin/mossy_2"), new MinecraftKey("underwater_ruin/mossy_3"), new MinecraftKey("underwater_ruin/mossy_4"), new MinecraftKey("underwater_ruin/mossy_5"), new MinecraftKey("underwater_ruin/mossy_6"), new MinecraftKey("underwater_ruin/mossy_7"), new MinecraftKey("underwater_ruin/mossy_8")}; + private static final MinecraftKey[] e = new MinecraftKey[] { new MinecraftKey("underwater_ruin/big_brick_1"), new MinecraftKey("underwater_ruin/big_brick_2"), new MinecraftKey("underwater_ruin/big_brick_3"), new MinecraftKey("underwater_ruin/big_brick_8")}; + private static final MinecraftKey[] f = new MinecraftKey[] { new MinecraftKey("underwater_ruin/big_mossy_1"), new MinecraftKey("underwater_ruin/big_mossy_2"), new MinecraftKey("underwater_ruin/big_mossy_3"), new MinecraftKey("underwater_ruin/big_mossy_8")}; + private static final MinecraftKey[] g = new MinecraftKey[] { new MinecraftKey("underwater_ruin/big_cracked_1"), new MinecraftKey("underwater_ruin/big_cracked_2"), new MinecraftKey("underwater_ruin/big_cracked_3"), new MinecraftKey("underwater_ruin/big_cracked_8")}; + private static final MinecraftKey[] h = new MinecraftKey[] { new MinecraftKey("underwater_ruin/big_warm_4"), new MinecraftKey("underwater_ruin/big_warm_5"), new MinecraftKey("underwater_ruin/big_warm_6"), new MinecraftKey("underwater_ruin/big_warm_7")}; + + public static void a() { + WorldGenFactory.a(WorldGenFeatureOceanRuinPieces.a.class, "ORP"); + } + + private static MinecraftKey a(Random random) { + return WorldGenFeatureOceanRuinPieces.a[random.nextInt(WorldGenFeatureOceanRuinPieces.a.length)]; + } + + private static MinecraftKey b(Random random) { + return WorldGenFeatureOceanRuinPieces.h[random.nextInt(WorldGenFeatureOceanRuinPieces.h.length)]; + } + + public static void a(DefinedStructureManager definedstructuremanager, BlockPosition blockposition, EnumBlockRotation enumblockrotation, List list, Random random, WorldGenFeatureOceanRuinConfiguration worldgenfeatureoceanruinconfiguration) { + boolean flag = random.nextFloat() <= worldgenfeatureoceanruinconfiguration.b; + float f = flag ? 0.9F : 0.8F; + + a(definedstructuremanager, blockposition, enumblockrotation, list, random, worldgenfeatureoceanruinconfiguration, flag, f); + if (flag && random.nextFloat() <= worldgenfeatureoceanruinconfiguration.c) { + a(definedstructuremanager, random, enumblockrotation, blockposition, worldgenfeatureoceanruinconfiguration, list); + } + + } + + private static void a(DefinedStructureManager definedstructuremanager, Random random, EnumBlockRotation enumblockrotation, BlockPosition blockposition, WorldGenFeatureOceanRuinConfiguration worldgenfeatureoceanruinconfiguration, List list) { + int i = blockposition.getX(); + int j = blockposition.getZ(); + BlockPosition blockposition1 = DefinedStructure.a(new BlockPosition(15, 0, 15), EnumBlockMirror.NONE, enumblockrotation, new BlockPosition(0, 0, 0)).a(i, 0, j); + StructureBoundingBox structureboundingbox = StructureBoundingBox.a(i, 0, j, blockposition1.getX(), 0, blockposition1.getZ()); + BlockPosition blockposition2 = new BlockPosition(Math.min(i, blockposition1.getX()), 0, Math.min(j, blockposition1.getZ())); + List list1 = a(random, blockposition2.getX(), blockposition2.getZ()); + int k = MathHelper.nextInt(random, 4, 8); + + for (int l = 0; l < k; ++l) { + if (!list1.isEmpty()) { + int i1 = random.nextInt(list1.size()); + BlockPosition blockposition3 = (BlockPosition) list1.remove(i1); + int j1 = blockposition3.getX(); + int k1 = blockposition3.getZ(); + EnumBlockRotation enumblockrotation1 = EnumBlockRotation.values()[random.nextInt(EnumBlockRotation.values().length)]; + BlockPosition blockposition4 = DefinedStructure.a(new BlockPosition(5, 0, 6), EnumBlockMirror.NONE, enumblockrotation1, new BlockPosition(0, 0, 0)).a(j1, 0, k1); + StructureBoundingBox structureboundingbox1 = StructureBoundingBox.a(j1, 0, k1, blockposition4.getX(), 0, blockposition4.getZ()); + + if (!structureboundingbox1.a(structureboundingbox)) { + a(definedstructuremanager, blockposition3, enumblockrotation1, list, random, worldgenfeatureoceanruinconfiguration, false, 0.8F); + } + } + } + + } + + private static List a(Random random, int i, int j) { + List list = Lists.newArrayList(); + + list.add(new BlockPosition(i - 16 + MathHelper.nextInt(random, 1, 8), 90, j + 16 + MathHelper.nextInt(random, 1, 7))); + list.add(new BlockPosition(i - 16 + MathHelper.nextInt(random, 1, 8), 90, j + MathHelper.nextInt(random, 1, 7))); + list.add(new BlockPosition(i - 16 + MathHelper.nextInt(random, 1, 8), 90, j - 16 + MathHelper.nextInt(random, 4, 8))); + list.add(new BlockPosition(i + MathHelper.nextInt(random, 1, 7), 90, j + 16 + MathHelper.nextInt(random, 1, 7))); + list.add(new BlockPosition(i + MathHelper.nextInt(random, 1, 7), 90, j - 16 + MathHelper.nextInt(random, 4, 6))); + list.add(new BlockPosition(i + 16 + MathHelper.nextInt(random, 1, 7), 90, j + 16 + MathHelper.nextInt(random, 3, 8))); + list.add(new BlockPosition(i + 16 + MathHelper.nextInt(random, 1, 7), 90, j + MathHelper.nextInt(random, 1, 7))); + list.add(new BlockPosition(i + 16 + MathHelper.nextInt(random, 1, 7), 90, j - 16 + MathHelper.nextInt(random, 4, 8))); + return list; + } + + private static void a(DefinedStructureManager definedstructuremanager, BlockPosition blockposition, EnumBlockRotation enumblockrotation, List list, Random random, WorldGenFeatureOceanRuinConfiguration worldgenfeatureoceanruinconfiguration, boolean flag, float f) { + if (worldgenfeatureoceanruinconfiguration.a == WorldGenFeatureOceanRuin.Temperature.WARM) { + MinecraftKey minecraftkey = flag ? b(random) : a(random); + + list.add(new WorldGenFeatureOceanRuinPieces.a(definedstructuremanager, minecraftkey, blockposition, enumblockrotation, f, worldgenfeatureoceanruinconfiguration.a, flag)); + } else if (worldgenfeatureoceanruinconfiguration.a == WorldGenFeatureOceanRuin.Temperature.COLD) { + MinecraftKey[] aminecraftkey = flag ? WorldGenFeatureOceanRuinPieces.e : WorldGenFeatureOceanRuinPieces.b; + MinecraftKey[] aminecraftkey1 = flag ? WorldGenFeatureOceanRuinPieces.g : WorldGenFeatureOceanRuinPieces.c; + MinecraftKey[] aminecraftkey2 = flag ? WorldGenFeatureOceanRuinPieces.f : WorldGenFeatureOceanRuinPieces.d; + int i = random.nextInt(aminecraftkey.length); + + list.add(new WorldGenFeatureOceanRuinPieces.a(definedstructuremanager, aminecraftkey[i], blockposition, enumblockrotation, f, worldgenfeatureoceanruinconfiguration.a, flag)); + list.add(new WorldGenFeatureOceanRuinPieces.a(definedstructuremanager, aminecraftkey1[i], blockposition, enumblockrotation, 0.7F, worldgenfeatureoceanruinconfiguration.a, flag)); + list.add(new WorldGenFeatureOceanRuinPieces.a(definedstructuremanager, aminecraftkey2[i], blockposition, enumblockrotation, 0.5F, worldgenfeatureoceanruinconfiguration.a, flag)); + } + + } + + public static class a extends DefinedStructurePiece { + + private WorldGenFeatureOceanRuin.Temperature d; + private float e; + private MinecraftKey f; + private EnumBlockRotation g; + private boolean h; + + public a() {} + + public a(DefinedStructureManager definedstructuremanager, MinecraftKey minecraftkey, BlockPosition blockposition, EnumBlockRotation enumblockrotation, float f, WorldGenFeatureOceanRuin.Temperature worldgenfeatureoceanruin_temperature, boolean flag) { + super(0); + this.f = minecraftkey; + this.c = blockposition; + this.g = enumblockrotation; + this.e = f; + this.d = worldgenfeatureoceanruin_temperature; + this.h = flag; + this.a(definedstructuremanager); + } + + private void a(DefinedStructureManager definedstructuremanager) { + DefinedStructure definedstructure = definedstructuremanager.a(this.f); + DefinedStructureInfo definedstructureinfo = (new DefinedStructureInfo()).a(this.g).a(EnumBlockMirror.NONE).a(Blocks.AIR); + + this.a(definedstructure, this.c, definedstructureinfo); + } + + protected void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + nbttagcompound.setString("Template", this.f.toString()); + nbttagcompound.setString("Rot", this.g.name()); + nbttagcompound.setFloat("Integrity", this.e); + nbttagcompound.setString("BiomeType", this.d.toString()); + nbttagcompound.setBoolean("IsLarge", this.h); + } + + protected void a(NBTTagCompound nbttagcompound, DefinedStructureManager definedstructuremanager) { + super.a(nbttagcompound, definedstructuremanager); + this.f = new MinecraftKey(nbttagcompound.getString("Template")); + this.g = EnumBlockRotation.valueOf(nbttagcompound.getString("Rot")); + this.e = nbttagcompound.getFloat("Integrity"); + this.d = WorldGenFeatureOceanRuin.Temperature.valueOf(nbttagcompound.getString("BiomeType")); + this.h = nbttagcompound.getBoolean("IsLarge"); + this.a(definedstructuremanager); + } + + protected void a(String s, BlockPosition blockposition, GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox) { + if ("chest".equals(s)) { + generatoraccess.setTypeAndData(blockposition, (IBlockData) Blocks.CHEST.getBlockData().set(BlockChest.c, generatoraccess.getFluid(blockposition).a(TagsFluid.WATER)), 2); + TileEntity tileentity = generatoraccess.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityChest) { + ((TileEntityChest) tileentity).setLootTable(this.h ? LootTables.q : LootTables.p, random.nextLong()); + } + } else if ("drowned".equals(s)) { + EntityDrowned entitydrowned = EntityTypes.DROWNED.create(generatoraccess.getMinecraftWorld()); // Paper + entitydrowned.di(); + entitydrowned.setPositionRotation(blockposition, 0.0F, 0.0F); + entitydrowned.prepare(generatoraccess.getDamageScaler(blockposition), (GroupDataEntity) null, (NBTTagCompound) null); + generatoraccess.addEntity(entitydrowned); + if (blockposition.getY() > generatoraccess.getSeaLevel()) { + generatoraccess.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 2); + } else { + generatoraccess.setTypeAndData(blockposition, Blocks.WATER.getBlockData(), 2); + } + } + + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + this.b.a(this.e); + int i = generatoraccess.a(HeightMap.Type.OCEAN_FLOOR_WG, this.c.getX(), this.c.getZ()); + + this.c = new BlockPosition(this.c.getX(), i, this.c.getZ()); + BlockPosition blockposition = DefinedStructure.a(new BlockPosition(this.a.a().getX() - 1, 0, this.a.a().getZ() - 1), EnumBlockMirror.NONE, this.g, new BlockPosition(0, 0, 0)).a((BaseBlockPosition) this.c); + + this.c = new BlockPosition(this.c.getX(), this.a(this.c, (IBlockAccess) generatoraccess, blockposition), this.c.getZ()); + return super.a(generatoraccess, random, structureboundingbox, chunkcoordintpair); + } + + private int a(BlockPosition blockposition, IBlockAccess iblockaccess, BlockPosition blockposition1) { + int i = blockposition.getY(); + int j = 512; + int k = i - 1; + int l = 0; + Iterator iterator = BlockPosition.a(blockposition, blockposition1).iterator(); + + while (iterator.hasNext()) { + BlockPosition blockposition2 = (BlockPosition) iterator.next(); + int i1 = blockposition2.getX(); + int j1 = blockposition2.getZ(); + int k1 = blockposition.getY() - 1; + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(i1, k1, j1); + IBlockData iblockdata = iblockaccess.getType(blockposition_mutableblockposition); + + for (Fluid fluid = iblockaccess.getFluid(blockposition_mutableblockposition); (iblockdata.isAir() || fluid.a(TagsFluid.WATER) || iblockdata.getBlock().a(TagsBlock.ICE)) && k1 > 1; fluid = iblockaccess.getFluid(blockposition_mutableblockposition)) { + --k1; + blockposition_mutableblockposition.c(i1, k1, j1); + iblockdata = iblockaccess.getType(blockposition_mutableblockposition); + } + + j = Math.min(j, k1); + if (k1 < k - 2) { + ++l; + } + } + + int l1 = Math.abs(blockposition.getX() - blockposition1.getX()); + + if (k - j > 2 && l > l1 - 2) { + i = j + 1; + } + + return i; + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldGenFeatureRandomScattered.java b/src/main/java/net/minecraft/server/WorldGenFeatureRandomScattered.java new file mode 100644 index 000000000000..c9c4e49b1b0e --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenFeatureRandomScattered.java @@ -0,0 +1,56 @@ +package net.minecraft.server; + +import java.util.Random; + +public abstract class WorldGenFeatureRandomScattered extends StructureGenerator { + + public WorldGenFeatureRandomScattered() {} + + protected ChunkCoordIntPair a(ChunkGenerator chunkgenerator, Random random, int i, int j, int k, int l) { + int i1 = this.a(chunkgenerator); + int j1 = this.b(chunkgenerator); + int k1 = i + i1 * k; + int l1 = j + i1 * l; + int i2 = k1 < 0 ? k1 - i1 + 1 : k1; + int j2 = l1 < 0 ? l1 - i1 + 1 : l1; + int k2 = i2 / i1; + int l2 = j2 / i1; + + ((SeededRandom) random).a(chunkgenerator.getSeed(), k2, l2, this.c(chunkgenerator.getWorld())); // Spigot + k2 *= i1; + l2 *= i1; + k2 += random.nextInt(i1 - j1); + l2 += random.nextInt(i1 - j1); + return new ChunkCoordIntPair(k2, l2); + } + + protected boolean a(ChunkGenerator chunkgenerator, Random random, int i, int j) { + ChunkCoordIntPair chunkcoordintpair = this.a(chunkgenerator, random, i, j, 0, 0); + + if (i == chunkcoordintpair.x && j == chunkcoordintpair.z) { + BiomeBase biomebase = chunkgenerator.getWorldChunkManager().getBiome(new BlockPosition(i * 16 + 9, 0, j * 16 + 9), (BiomeBase) null); + + if (chunkgenerator.canSpawnStructure(biomebase, this)) { + return true; + } + } + + return false; + } + + protected int a(ChunkGenerator chunkgenerator) { + return chunkgenerator.getSettings().h(); + } + + protected int b(ChunkGenerator chunkgenerator) { + return chunkgenerator.getSettings().i(); + } + + protected boolean a(GeneratorAccess generatoraccess) { + return generatoraccess.getWorldData().shouldGenerateMapFeatures(); + } + + protected abstract StructureStart a(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, SeededRandom seededrandom, int i, int j); + + protected abstract int c(World world); // Spigot +} diff --git a/src/main/java/net/minecraft/server/WorldGenFeatureShipwreck.java b/src/main/java/net/minecraft/server/WorldGenFeatureShipwreck.java new file mode 100644 index 000000000000..f3b185d58567 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenFeatureShipwreck.java @@ -0,0 +1,49 @@ +package net.minecraft.server; + +public class WorldGenFeatureShipwreck extends WorldGenFeatureRandomScattered { + + public WorldGenFeatureShipwreck() {} + + protected String a() { + return "Shipwreck"; + } + + public int b() { + return 3; + } + + protected StructureStart a(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, SeededRandom seededrandom, int i, int j) { + BiomeBase biomebase = chunkgenerator.getWorldChunkManager().getBiome(new BlockPosition((i << 4) + 9, 0, (j << 4) + 9), (BiomeBase) null); + + return new WorldGenFeatureShipwreck.a(generatoraccess, chunkgenerator, seededrandom, i, j, biomebase); + } + + // Spigot start + protected int c(World world) { + return world.spigotConfig.shipwreckSeed; + // Spigot end + } + + protected int a(ChunkGenerator chunkgenerator) { + return chunkgenerator.getSettings().j(); + } + + protected int b(ChunkGenerator chunkgenerator) { + return chunkgenerator.getSettings().k(); + } + + public static class a extends StructureStart { + + public a() {} + + public a(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, SeededRandom seededrandom, int i, int j, BiomeBase biomebase) { + super(i, j, biomebase, seededrandom, generatoraccess.getSeed()); + WorldGenFeatureShipwreckConfiguration worldgenfeatureshipwreckconfiguration = (WorldGenFeatureShipwreckConfiguration) chunkgenerator.getFeatureConfiguration(biomebase, WorldGenerator.k); + EnumBlockRotation enumblockrotation = EnumBlockRotation.values()[seededrandom.nextInt(EnumBlockRotation.values().length)]; + BlockPosition blockposition = new BlockPosition(i * 16, 90, j * 16); + + WorldGenShipwreck.a(generatoraccess.getDataManager().h(), blockposition, enumblockrotation, this.a, seededrandom, worldgenfeatureshipwreckconfiguration); + this.a((IBlockAccess) generatoraccess); + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldGenFeatureSwampHut.java b/src/main/java/net/minecraft/server/WorldGenFeatureSwampHut.java new file mode 100644 index 000000000000..f0788eb5d35b --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenFeatureSwampHut.java @@ -0,0 +1,60 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.List; + +public class WorldGenFeatureSwampHut extends WorldGenFeatureRandomScattered { + + private static final List b = Lists.newArrayList(new BiomeBase.BiomeMeta[] { new BiomeBase.BiomeMeta(EntityTypes.WITCH, 1, 1, 1)}); + + public WorldGenFeatureSwampHut() {} + + protected String a() { + return "Swamp_Hut"; + } + + public int b() { + return 3; + } + + protected StructureStart a(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, SeededRandom seededrandom, int i, int j) { + BiomeBase biomebase = chunkgenerator.getWorldChunkManager().getBiome(new BlockPosition((i << 4) + 9, 0, (j << 4) + 9), Biomes.PLAINS); + + return new WorldGenFeatureSwampHut.a(generatoraccess, seededrandom, i, j, biomebase); + } + + // Spigot start + protected int c(World world) { + return world.spigotConfig.swampSeed; + // Spigot end + } + + public List d() { + return WorldGenFeatureSwampHut.b; + } + + public boolean d(GeneratorAccess generatoraccess, BlockPosition blockposition) { + StructureStart structurestart = this.a(generatoraccess, blockposition); + + if (structurestart != WorldGenFeatureSwampHut.a && structurestart instanceof WorldGenFeatureSwampHut.a && !structurestart.d().isEmpty()) { + StructurePiece structurepiece = (StructurePiece) structurestart.d().get(0); + + return structurepiece instanceof WorldGenWitchHut; + } else { + return false; + } + } + + public static class a extends StructureStart { + + public a() {} + + public a(GeneratorAccess generatoraccess, SeededRandom seededrandom, int i, int j, BiomeBase biomebase) { + super(i, j, biomebase, seededrandom, generatoraccess.getSeed()); + WorldGenWitchHut worldgenwitchhut = new WorldGenWitchHut(seededrandom, i * 16, j * 16); + + this.a.add(worldgenwitchhut); + this.a((IBlockAccess) generatoraccess); + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldGenGroundBush.java b/src/main/java/net/minecraft/server/WorldGenGroundBush.java new file mode 100644 index 000000000000..14dc3af54f9e --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenGroundBush.java @@ -0,0 +1,58 @@ +package net.minecraft.server; + +import java.util.Random; +import java.util.Set; + +public class WorldGenGroundBush extends WorldGenTreeAbstract { + + private final IBlockData a; + private final IBlockData b; + + public WorldGenGroundBush(IBlockData iblockdata, IBlockData iblockdata1) { + super(false); + this.b = iblockdata; + this.a = iblockdata1; + } + + public boolean a(Set set, GeneratorAccess generatoraccess, Random random, BlockPosition blockposition) { + for (IBlockData iblockdata = generatoraccess.getType(blockposition); (iblockdata.isAir() || iblockdata.a(TagsBlock.LEAVES)) && blockposition.getY() > 0; iblockdata = generatoraccess.getType(blockposition)) { + blockposition = blockposition.down(); + } + + Block block = generatoraccess.getType(blockposition).getBlock(); + + if (Block.d(block) || block == Blocks.GRASS_BLOCK) { + blockposition = blockposition.up(); + this.a(set, generatoraccess, blockposition, this.b); + + for (int i = blockposition.getY(); i <= blockposition.getY() + 2; ++i) { + int j = i - blockposition.getY(); + int k = 2 - j; + + for (int l = blockposition.getX() - k; l <= blockposition.getX() + k; ++l) { + int i1 = l - blockposition.getX(); + + for (int j1 = blockposition.getZ() - k; j1 <= blockposition.getZ() + k; ++j1) { + int k1 = j1 - blockposition.getZ(); + + if (Math.abs(i1) != k || Math.abs(k1) != k || random.nextInt(2) != 0) { + BlockPosition blockposition1 = new BlockPosition(l, i, j1); + IBlockData iblockdata1 = generatoraccess.getType(blockposition1); + + if (iblockdata1.isAir() || iblockdata1.a(TagsBlock.LEAVES)) { + this.a(generatoraccess, blockposition1, this.a); + } + } + } + } + } + // CraftBukkit start - Return false if gen was unsuccessful + } else { + return false; + } + // CraftBukkit end + + + return true; + } +} diff --git a/src/main/java/net/minecraft/server/WorldGenMegaTreeProvider.java b/src/main/java/net/minecraft/server/WorldGenMegaTreeProvider.java new file mode 100644 index 000000000000..fd7eb1f88701 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenMegaTreeProvider.java @@ -0,0 +1,55 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; + +public abstract class WorldGenMegaTreeProvider extends WorldGenTreeProvider { + + public WorldGenMegaTreeProvider() {} + + public boolean a(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, Random random) { + for (int i = 0; i >= -1; --i) { + for (int j = 0; j >= -1; --j) { + if (a(iblockdata, generatoraccess, blockposition, i, j)) { + return this.a(generatoraccess, blockposition, iblockdata, random, i, j); + } + } + } + + return super.a(generatoraccess, blockposition, iblockdata, random); + } + + @Nullable + protected abstract WorldGenTreeAbstract a(Random random); + + public boolean a(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, Random random, int i, int j) { + WorldGenTreeAbstract worldgentreeabstract = this.a(random); + + if (worldgentreeabstract == null) { + return false; + } else { + setTreeType(worldgentreeabstract); // CraftBukkit + IBlockData iblockdata1 = Blocks.AIR.getBlockData(); + + generatoraccess.setTypeAndData(blockposition.a(i, 0, j), iblockdata1, 4); + generatoraccess.setTypeAndData(blockposition.a(i + 1, 0, j), iblockdata1, 4); + generatoraccess.setTypeAndData(blockposition.a(i, 0, j + 1), iblockdata1, 4); + generatoraccess.setTypeAndData(blockposition.a(i + 1, 0, j + 1), iblockdata1, 4); + if (worldgentreeabstract.generate(generatoraccess, generatoraccess.getChunkProvider().getChunkGenerator(), random, blockposition.a(i, 0, j), WorldGenFeatureConfiguration.e)) { + return true; + } else { + generatoraccess.setTypeAndData(blockposition.a(i, 0, j), iblockdata, 4); + generatoraccess.setTypeAndData(blockposition.a(i + 1, 0, j), iblockdata, 4); + generatoraccess.setTypeAndData(blockposition.a(i, 0, j + 1), iblockdata, 4); + generatoraccess.setTypeAndData(blockposition.a(i + 1, 0, j + 1), iblockdata, 4); + return false; + } + } + } + + public static boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, int i, int j) { + Block block = iblockdata.getBlock(); + + return block == iblockaccess.getType(blockposition.a(i, 0, j)).getBlock() && block == iblockaccess.getType(blockposition.a(i + 1, 0, j)).getBlock() && block == iblockaccess.getType(blockposition.a(i, 0, j + 1)).getBlock() && block == iblockaccess.getType(blockposition.a(i + 1, 0, j + 1)).getBlock(); + } +} diff --git a/src/main/java/net/minecraft/server/WorldGenMonument.java b/src/main/java/net/minecraft/server/WorldGenMonument.java new file mode 100644 index 000000000000..9a1795ad6cee --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenMonument.java @@ -0,0 +1,159 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.Set; + +public class WorldGenMonument extends StructureGenerator { + + private static final List b = Lists.newArrayList(new BiomeBase.BiomeMeta[] { new BiomeBase.BiomeMeta(EntityTypes.GUARDIAN, 1, 2, 4)}); + + public WorldGenMonument() {} + + protected ChunkCoordIntPair a(ChunkGenerator chunkgenerator, Random random, int i, int j, int k, int l) { + int i1 = chunkgenerator.getSettings().c(); + int j1 = chunkgenerator.getSettings().d(); + int k1 = i + i1 * k; + int l1 = j + i1 * l; + int i2 = k1 < 0 ? k1 - i1 + 1 : k1; + int j2 = l1 < 0 ? l1 - i1 + 1 : l1; + int k2 = i2 / i1; + int l2 = j2 / i1; + + ((SeededRandom) random).a(chunkgenerator.getSeed(), k2, l2, chunkgenerator.getWorld().spigotConfig.monumentSeed); // Spigot + k2 *= i1; + l2 *= i1; + k2 += (random.nextInt(i1 - j1) + random.nextInt(i1 - j1)) / 2; + l2 += (random.nextInt(i1 - j1) + random.nextInt(i1 - j1)) / 2; + return new ChunkCoordIntPair(k2, l2); + } + + protected boolean a(ChunkGenerator chunkgenerator, Random random, int i, int j) { + ChunkCoordIntPair chunkcoordintpair = this.a(chunkgenerator, random, i, j, 0, 0); + + if (i == chunkcoordintpair.x && j == chunkcoordintpair.z) { + Set set = chunkgenerator.getWorldChunkManager().a(i * 16 + 9, j * 16 + 9, 16); + Iterator iterator = set.iterator(); + + BiomeBase biomebase; + + do { + if (!iterator.hasNext()) { + Set set1 = chunkgenerator.getWorldChunkManager().a(i * 16 + 9, j * 16 + 9, 29); + Iterator iterator1 = set1.iterator(); + + BiomeBase biomebase1; + + do { + if (!iterator1.hasNext()) { + return true; + } + + biomebase1 = (BiomeBase) iterator1.next(); + } while (biomebase1.p() == BiomeBase.Geography.OCEAN || biomebase1.p() == BiomeBase.Geography.RIVER); + + return false; + } + + biomebase = (BiomeBase) iterator.next(); + } while (chunkgenerator.canSpawnStructure(biomebase, WorldGenerator.n)); + + return false; + } else { + return false; + } + } + + protected boolean a(GeneratorAccess generatoraccess) { + return generatoraccess.getWorldData().shouldGenerateMapFeatures(); + } + + protected StructureStart a(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, SeededRandom seededrandom, int i, int j) { + BiomeBase biomebase = chunkgenerator.getWorldChunkManager().getBiome(new BlockPosition((i << 4) + 9, 0, (j << 4) + 9), Biomes.b); + + return new WorldGenMonument.a(generatoraccess, seededrandom, i, j, biomebase); + } + + protected String a() { + return "Monument"; + } + + public int b() { + return 8; + } + + public List d() { + return WorldGenMonument.b; + } + + public static class a extends StructureStart { + + private final Set e = Sets.newHashSet(); + private boolean f; + + public a() {} + + public a(GeneratorAccess generatoraccess, SeededRandom seededrandom, int i, int j, BiomeBase biomebase) { + super(i, j, biomebase, seededrandom, generatoraccess.getSeed()); + this.b(generatoraccess, seededrandom, i, j); + } + + private void b(IBlockAccess iblockaccess, Random random, int i, int j) { + int k = i * 16 - 29; + int l = j * 16 - 29; + EnumDirection enumdirection = EnumDirection.EnumDirectionLimit.HORIZONTAL.a(random); + + this.a.add(new WorldGenMonumentPieces.WorldGenMonumentPiece1(random, k, l, enumdirection)); + this.a(iblockaccess); + this.f = true; + } + + public void a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (!this.f) { + this.a.clear(); + this.b(generatoraccess, random, this.e(), this.f()); + } + + super.a(generatoraccess, random, structureboundingbox, chunkcoordintpair); + } + + public void b(ChunkCoordIntPair chunkcoordintpair) { + super.b(chunkcoordintpair); + this.e.add(chunkcoordintpair); + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = this.e.iterator(); + + while (iterator.hasNext()) { + ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair) iterator.next(); + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + nbttagcompound1.setInt("X", chunkcoordintpair.x); + nbttagcompound1.setInt("Z", chunkcoordintpair.z); + nbttaglist.add((NBTBase) nbttagcompound1); + } + + nbttagcompound.set("Processed", nbttaglist); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + if (nbttagcompound.hasKeyOfType("Processed", 9)) { + NBTTagList nbttaglist = nbttagcompound.getList("Processed", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + NBTTagCompound nbttagcompound1 = nbttaglist.getCompound(i); + + this.e.add(new ChunkCoordIntPair(nbttagcompound1.getInt("X"), nbttagcompound1.getInt("Z"))); + } + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldGenMonumentPieces.java b/src/main/java/net/minecraft/server/WorldGenMonumentPieces.java new file mode 100644 index 000000000000..493a86e1bf69 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenMonumentPieces.java @@ -0,0 +1,1938 @@ +package net.minecraft.server; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.Set; + +public class WorldGenMonumentPieces { + + public static void a() { + WorldGenFactory.a(WorldGenMonumentPieces.WorldGenMonumentPiece1.class, "OMB"); + WorldGenFactory.a(WorldGenMonumentPieces.WorldGenMonumentPiece2.class, "OMCR"); + WorldGenFactory.a(WorldGenMonumentPieces.WorldGenMonumentPiece3.class, "OMDXR"); + WorldGenFactory.a(WorldGenMonumentPieces.WorldGenMonumentPiece4.class, "OMDXYR"); + WorldGenFactory.a(WorldGenMonumentPieces.WorldGenMonumentPiece5.class, "OMDYR"); + WorldGenFactory.a(WorldGenMonumentPieces.WorldGenMonumentPiece6.class, "OMDYZR"); + WorldGenFactory.a(WorldGenMonumentPieces.WorldGenMonumentPiece7.class, "OMDZR"); + WorldGenFactory.a(WorldGenMonumentPieces.WorldGenMonumentPieceEntry.class, "OMEntry"); + WorldGenFactory.a(WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.class, "OMPenthouse"); + WorldGenFactory.a(WorldGenMonumentPieces.WorldGenMonumentPieceSimple.class, "OMSimple"); + WorldGenFactory.a(WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.class, "OMSimpleT"); + } + + static class WorldGenMonumentPieceSelector4 implements WorldGenMonumentPieces.IWorldGenMonumentPieceSelector { + + private WorldGenMonumentPieceSelector4() {} + + public boolean a(WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker) { + if (worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.NORTH.a()] && !worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.NORTH.a()].d && worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.UP.a()] && !worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.UP.a()].d) { + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker1 = worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.NORTH.a()]; + + return worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.UP.a()] && !worldgenmonumentpieces_worldgenmonumentstatetracker1.b[EnumDirection.UP.a()].d; + } else { + return false; + } + } + + public WorldGenMonumentPieces.WorldGenMonumentPiece a(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, Random random) { + worldgenmonumentpieces_worldgenmonumentstatetracker.d = true; + worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.NORTH.a()].d = true; + worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.UP.a()].d = true; + worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.NORTH.a()].b[EnumDirection.UP.a()].d = true; + return new WorldGenMonumentPieces.WorldGenMonumentPiece6(enumdirection, worldgenmonumentpieces_worldgenmonumentstatetracker, random); + } + } + + static class WorldGenMonumentPieceSelector6 implements WorldGenMonumentPieces.IWorldGenMonumentPieceSelector { + + private WorldGenMonumentPieceSelector6() {} + + public boolean a(WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker) { + if (worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.EAST.a()] && !worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.EAST.a()].d && worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.UP.a()] && !worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.UP.a()].d) { + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker1 = worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.EAST.a()]; + + return worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.UP.a()] && !worldgenmonumentpieces_worldgenmonumentstatetracker1.b[EnumDirection.UP.a()].d; + } else { + return false; + } + } + + public WorldGenMonumentPieces.WorldGenMonumentPiece a(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, Random random) { + worldgenmonumentpieces_worldgenmonumentstatetracker.d = true; + worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.EAST.a()].d = true; + worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.UP.a()].d = true; + worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.EAST.a()].b[EnumDirection.UP.a()].d = true; + return new WorldGenMonumentPieces.WorldGenMonumentPiece4(enumdirection, worldgenmonumentpieces_worldgenmonumentstatetracker, random); + } + } + + static class WorldGenMonumentPieceSelector3 implements WorldGenMonumentPieces.IWorldGenMonumentPieceSelector { + + private WorldGenMonumentPieceSelector3() {} + + public boolean a(WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker) { + return worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.NORTH.a()] && !worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.NORTH.a()].d; + } + + public WorldGenMonumentPieces.WorldGenMonumentPiece a(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, Random random) { + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker1 = worldgenmonumentpieces_worldgenmonumentstatetracker; + + if (!worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.NORTH.a()] || worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.NORTH.a()].d) { + worldgenmonumentpieces_worldgenmonumentstatetracker1 = worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.SOUTH.a()]; + } + + worldgenmonumentpieces_worldgenmonumentstatetracker1.d = true; + worldgenmonumentpieces_worldgenmonumentstatetracker1.b[EnumDirection.NORTH.a()].d = true; + return new WorldGenMonumentPieces.WorldGenMonumentPiece7(enumdirection, worldgenmonumentpieces_worldgenmonumentstatetracker1, random); + } + } + + static class WorldGenMonumentPieceSelector7 implements WorldGenMonumentPieces.IWorldGenMonumentPieceSelector { + + private WorldGenMonumentPieceSelector7() {} + + public boolean a(WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker) { + return worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.EAST.a()] && !worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.EAST.a()].d; + } + + public WorldGenMonumentPieces.WorldGenMonumentPiece a(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, Random random) { + worldgenmonumentpieces_worldgenmonumentstatetracker.d = true; + worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.EAST.a()].d = true; + return new WorldGenMonumentPieces.WorldGenMonumentPiece3(enumdirection, worldgenmonumentpieces_worldgenmonumentstatetracker, random); + } + } + + static class WorldGenMonumentPieceSelector5 implements WorldGenMonumentPieces.IWorldGenMonumentPieceSelector { + + private WorldGenMonumentPieceSelector5() {} + + public boolean a(WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker) { + return worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.UP.a()] && !worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.UP.a()].d; + } + + public WorldGenMonumentPieces.WorldGenMonumentPiece a(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, Random random) { + worldgenmonumentpieces_worldgenmonumentstatetracker.d = true; + worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.UP.a()].d = true; + return new WorldGenMonumentPieces.WorldGenMonumentPiece5(enumdirection, worldgenmonumentpieces_worldgenmonumentstatetracker, random); + } + } + + static class WorldGenMonumentPieceSelector1 implements WorldGenMonumentPieces.IWorldGenMonumentPieceSelector { + + private WorldGenMonumentPieceSelector1() {} + + public boolean a(WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker) { + return !worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.WEST.a()] && !worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.EAST.a()] && !worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.NORTH.a()] && !worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.SOUTH.a()] && !worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.UP.a()]; + } + + public WorldGenMonumentPieces.WorldGenMonumentPiece a(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, Random random) { + worldgenmonumentpieces_worldgenmonumentstatetracker.d = true; + return new WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT(enumdirection, worldgenmonumentpieces_worldgenmonumentstatetracker, random); + } + } + + static class WorldGenMonumentPieceSelector2 implements WorldGenMonumentPieces.IWorldGenMonumentPieceSelector { + + private WorldGenMonumentPieceSelector2() {} + + public boolean a(WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker) { + return true; + } + + public WorldGenMonumentPieces.WorldGenMonumentPiece a(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, Random random) { + worldgenmonumentpieces_worldgenmonumentstatetracker.d = true; + return new WorldGenMonumentPieces.WorldGenMonumentPieceSimple(enumdirection, worldgenmonumentpieces_worldgenmonumentstatetracker, random); + } + } + + interface IWorldGenMonumentPieceSelector { + + boolean a(WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker); + + WorldGenMonumentPieces.WorldGenMonumentPiece a(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, Random random); + } + + static class WorldGenMonumentStateTracker { + + private final int a; + private final WorldGenMonumentPieces.WorldGenMonumentStateTracker[] b = new WorldGenMonumentPieces.WorldGenMonumentStateTracker[6]; + private final boolean[] c = new boolean[6]; + private boolean d; + private boolean e; + private int f; + + public WorldGenMonumentStateTracker(int i) { + this.a = i; + } + + public void a(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker) { + this.b[enumdirection.a()] = worldgenmonumentpieces_worldgenmonumentstatetracker; + worldgenmonumentpieces_worldgenmonumentstatetracker.b[enumdirection.opposite().a()] = this; + } + + public void a() { + for (int i = 0; i < 6; ++i) { + this.c[i] = this.b[i] != null; + } + + } + + public boolean a(int i) { + if (this.e) { + return true; + } else { + this.f = i; + + for (int j = 0; j < 6; ++j) { + if (this.b[j] != null && this.c[j] && this.b[j].f != i && this.b[j].a(i)) { + return true; + } + } + + return false; + } + } + + public boolean b() { + return this.a >= 75; + } + + public int c() { + int i = 0; + + for (int j = 0; j < 6; ++j) { + if (this.c[j]) { + ++i; + } + } + + return i; + } + } + + public static class WorldGenMonumentPiecePenthouse extends WorldGenMonumentPieces.WorldGenMonumentPiece { + + public WorldGenMonumentPiecePenthouse() {} + + public WorldGenMonumentPiecePenthouse(EnumDirection enumdirection, StructureBoundingBox structureboundingbox) { + super(enumdirection, structureboundingbox); + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + this.a(generatoraccess, structureboundingbox, 2, -1, 2, 11, -1, 11, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, false); + this.a(generatoraccess, structureboundingbox, 0, -1, 0, 1, -1, 11, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.a, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.a, false); + this.a(generatoraccess, structureboundingbox, 12, -1, 0, 13, -1, 11, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.a, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.a, false); + this.a(generatoraccess, structureboundingbox, 2, -1, 0, 11, -1, 1, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.a, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.a, false); + this.a(generatoraccess, structureboundingbox, 2, -1, 12, 11, -1, 13, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.a, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.a, false); + this.a(generatoraccess, structureboundingbox, 0, 0, 0, 0, 0, 13, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, false); + this.a(generatoraccess, structureboundingbox, 13, 0, 0, 13, 0, 13, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, false); + this.a(generatoraccess, structureboundingbox, 1, 0, 0, 12, 0, 0, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, false); + this.a(generatoraccess, structureboundingbox, 1, 0, 13, 12, 0, 13, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, false); + + for (int i = 2; i <= 11; i += 3) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.e, 0, 0, i, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.e, 13, 0, i, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.e, i, 0, 0, structureboundingbox); + } + + this.a(generatoraccess, structureboundingbox, 2, 0, 3, 4, 0, 9, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, false); + this.a(generatoraccess, structureboundingbox, 9, 0, 3, 11, 0, 9, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, false); + this.a(generatoraccess, structureboundingbox, 4, 0, 9, 9, 0, 11, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, 5, 0, 8, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, 8, 0, 8, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, 10, 0, 10, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, 3, 0, 10, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 3, 0, 3, 3, 0, 7, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.c, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.c, false); + this.a(generatoraccess, structureboundingbox, 10, 0, 3, 10, 0, 7, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.c, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.c, false); + this.a(generatoraccess, structureboundingbox, 6, 0, 10, 7, 0, 10, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.c, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.c, false); + byte b0 = 3; + + for (int j = 0; j < 2; ++j) { + for (int k = 2; k <= 8; k += 3) { + this.a(generatoraccess, structureboundingbox, b0, 0, k, b0, 2, k, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, false); + } + + b0 = 10; + } + + this.a(generatoraccess, structureboundingbox, 5, 0, 10, 5, 2, 10, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, false); + this.a(generatoraccess, structureboundingbox, 8, 0, 10, 8, 2, 10, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.b, false); + this.a(generatoraccess, structureboundingbox, 6, -1, 7, 7, -1, 8, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.c, WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse.c, false); + this.a(generatoraccess, structureboundingbox, 6, -1, 3, 7, -1, 4); + this.a(generatoraccess, structureboundingbox, 6, 1, 6); + return true; + } + } + + public static class WorldGenMonumentPiece8 extends WorldGenMonumentPieces.WorldGenMonumentPiece { + + private int p; + + public WorldGenMonumentPiece8() {} + + public WorldGenMonumentPiece8(EnumDirection enumdirection, StructureBoundingBox structureboundingbox, int i) { + super(enumdirection, structureboundingbox); + this.p = i & 1; + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (this.p == 0) { + int i; + + for (i = 0; i < 4; ++i) { + this.a(generatoraccess, structureboundingbox, 10 - i, 3 - i, 20 - i, 12 + i, 3 - i, 20, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + } + + this.a(generatoraccess, structureboundingbox, 7, 0, 6, 15, 0, 16, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, structureboundingbox, 6, 0, 6, 6, 3, 20, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, structureboundingbox, 16, 0, 6, 16, 3, 20, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, structureboundingbox, 7, 1, 7, 7, 1, 20, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, structureboundingbox, 15, 1, 7, 15, 1, 20, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, structureboundingbox, 7, 1, 6, 9, 3, 6, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, structureboundingbox, 13, 1, 6, 15, 3, 6, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, structureboundingbox, 8, 1, 7, 9, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, structureboundingbox, 13, 1, 7, 14, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, structureboundingbox, 9, 0, 5, 13, 0, 5, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, structureboundingbox, 10, 0, 7, 12, 0, 7, WorldGenMonumentPieces.WorldGenMonumentPiece8.c, WorldGenMonumentPieces.WorldGenMonumentPiece8.c, false); + this.a(generatoraccess, structureboundingbox, 8, 0, 10, 8, 0, 12, WorldGenMonumentPieces.WorldGenMonumentPiece8.c, WorldGenMonumentPieces.WorldGenMonumentPiece8.c, false); + this.a(generatoraccess, structureboundingbox, 14, 0, 10, 14, 0, 12, WorldGenMonumentPieces.WorldGenMonumentPiece8.c, WorldGenMonumentPieces.WorldGenMonumentPiece8.c, false); + + for (i = 18; i >= 7; i -= 3) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, 6, 3, i, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, 16, 3, i, structureboundingbox); + } + + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, 10, 0, 10, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, 12, 0, 10, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, 10, 0, 12, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, 12, 0, 12, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, 8, 3, 6, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, 14, 3, 6, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, 4, 2, 4, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, 4, 1, 4, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, 4, 0, 4, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, 18, 2, 4, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, 18, 1, 4, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, 18, 0, 4, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, 4, 2, 18, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, 4, 1, 18, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, 4, 0, 18, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, 18, 2, 18, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, 18, 1, 18, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, 18, 0, 18, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, 9, 7, 20, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, 13, 7, 20, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 6, 0, 21, 7, 4, 21, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, structureboundingbox, 15, 0, 21, 16, 4, 21, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, structureboundingbox, 11, 2, 16); + } else if (this.p == 1) { + this.a(generatoraccess, structureboundingbox, 9, 3, 18, 13, 3, 20, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, structureboundingbox, 9, 0, 18, 9, 2, 18, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, structureboundingbox, 13, 0, 18, 13, 2, 18, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + byte b0 = 9; + boolean flag = true; + boolean flag1 = true; + + int j; + + for (j = 0; j < 2; ++j) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, b0, 6, 20, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, b0, 5, 20, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, b0, 4, 20, structureboundingbox); + b0 = 13; + } + + this.a(generatoraccess, structureboundingbox, 7, 3, 7, 15, 3, 14, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + b0 = 10; + + for (j = 0; j < 2; ++j) { + this.a(generatoraccess, structureboundingbox, b0, 0, 10, b0, 6, 10, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, structureboundingbox, b0, 0, 12, b0, 6, 12, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, b0, 0, 10, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, b0, 0, 12, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, b0, 4, 10, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece8.e, b0, 4, 12, structureboundingbox); + b0 = 12; + } + + b0 = 8; + + for (j = 0; j < 2; ++j) { + this.a(generatoraccess, structureboundingbox, b0, 0, 7, b0, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + this.a(generatoraccess, structureboundingbox, b0, 0, 14, b0, 2, 14, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, WorldGenMonumentPieces.WorldGenMonumentPiece8.b, false); + b0 = 14; + } + + this.a(generatoraccess, structureboundingbox, 8, 3, 8, 8, 3, 13, WorldGenMonumentPieces.WorldGenMonumentPiece8.c, WorldGenMonumentPieces.WorldGenMonumentPiece8.c, false); + this.a(generatoraccess, structureboundingbox, 14, 3, 8, 14, 3, 13, WorldGenMonumentPieces.WorldGenMonumentPiece8.c, WorldGenMonumentPieces.WorldGenMonumentPiece8.c, false); + this.a(generatoraccess, structureboundingbox, 11, 5, 13); + } + + return true; + } + } + + public static class WorldGenMonumentPiece2 extends WorldGenMonumentPieces.WorldGenMonumentPiece { + + public WorldGenMonumentPiece2() {} + + public WorldGenMonumentPiece2(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, Random random) { + super(1, enumdirection, worldgenmonumentpieces_worldgenmonumentstatetracker, 2, 2, 2); + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + this.a(generatoraccess, structureboundingbox, 1, 8, 0, 14, 8, 14, WorldGenMonumentPieces.WorldGenMonumentPiece2.a); + boolean flag = true; + IBlockData iblockdata = WorldGenMonumentPieces.WorldGenMonumentPiece2.b; + + this.a(generatoraccess, structureboundingbox, 0, 7, 0, 0, 7, 15, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 15, 7, 0, 15, 7, 15, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 7, 0, 15, 7, 0, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 7, 15, 14, 7, 15, iblockdata, iblockdata, false); + + int i; + + for (i = 1; i <= 6; ++i) { + iblockdata = WorldGenMonumentPieces.WorldGenMonumentPiece2.b; + if (i == 2 || i == 6) { + iblockdata = WorldGenMonumentPieces.WorldGenMonumentPiece2.a; + } + + for (int j = 0; j <= 15; j += 15) { + this.a(generatoraccess, structureboundingbox, j, i, 0, j, i, 1, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, j, i, 6, j, i, 9, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, j, i, 14, j, i, 15, iblockdata, iblockdata, false); + } + + this.a(generatoraccess, structureboundingbox, 1, i, 0, 1, i, 0, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 6, i, 0, 9, i, 0, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 14, i, 0, 14, i, 0, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, i, 15, 14, i, 15, iblockdata, iblockdata, false); + } + + this.a(generatoraccess, structureboundingbox, 6, 3, 6, 9, 6, 9, WorldGenMonumentPieces.WorldGenMonumentPiece2.c, WorldGenMonumentPieces.WorldGenMonumentPiece2.c, false); + this.a(generatoraccess, structureboundingbox, 7, 4, 7, 8, 5, 8, Blocks.GOLD_BLOCK.getBlockData(), Blocks.GOLD_BLOCK.getBlockData(), false); + + for (i = 3; i <= 6; i += 3) { + for (int k = 6; k <= 9; k += 3) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece2.e, k, i, 6, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece2.e, k, i, 9, structureboundingbox); + } + } + + this.a(generatoraccess, structureboundingbox, 5, 1, 6, 5, 2, 6, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 5, 1, 9, 5, 2, 9, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 10, 1, 6, 10, 2, 6, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 10, 1, 9, 10, 2, 9, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 6, 1, 5, 6, 2, 5, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 9, 1, 5, 9, 2, 5, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 6, 1, 10, 6, 2, 10, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 9, 1, 10, 9, 2, 10, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 5, 2, 5, 5, 6, 5, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 5, 2, 10, 5, 6, 10, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 10, 2, 5, 10, 6, 5, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 10, 2, 10, 10, 6, 10, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 5, 7, 1, 5, 7, 6, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 10, 7, 1, 10, 7, 6, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 5, 7, 9, 5, 7, 14, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 10, 7, 9, 10, 7, 14, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 1, 7, 5, 6, 7, 5, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 1, 7, 10, 6, 7, 10, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 9, 7, 5, 14, 7, 5, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 9, 7, 10, 14, 7, 10, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 2, 1, 2, 2, 1, 3, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 3, 1, 2, 3, 1, 2, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 13, 1, 2, 13, 1, 3, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 12, 1, 2, 12, 1, 2, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 2, 1, 12, 2, 1, 13, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 3, 1, 13, 3, 1, 13, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 13, 1, 12, 13, 1, 13, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + this.a(generatoraccess, structureboundingbox, 12, 1, 13, 12, 1, 13, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, WorldGenMonumentPieces.WorldGenMonumentPiece2.b, false); + return true; + } + } + + public static class WorldGenMonumentPiece6 extends WorldGenMonumentPieces.WorldGenMonumentPiece { + + public WorldGenMonumentPiece6() {} + + public WorldGenMonumentPiece6(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, Random random) { + super(1, enumdirection, worldgenmonumentpieces_worldgenmonumentstatetracker, 1, 2, 2); + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker = this.l.b[EnumDirection.NORTH.a()]; + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker1 = this.l; + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker2 = worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.UP.a()]; + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker3 = worldgenmonumentpieces_worldgenmonumentstatetracker1.b[EnumDirection.UP.a()]; + + if (this.l.a / 25 > 0) { + this.a(generatoraccess, structureboundingbox, 0, 8, worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.DOWN.a()]); + this.a(generatoraccess, structureboundingbox, 0, 0, worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.DOWN.a()]); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker3.b[EnumDirection.UP.a()] == null) { + this.a(generatoraccess, structureboundingbox, 1, 8, 1, 6, 8, 7, WorldGenMonumentPieces.WorldGenMonumentPiece6.a); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker2.b[EnumDirection.UP.a()] == null) { + this.a(generatoraccess, structureboundingbox, 1, 8, 8, 6, 8, 14, WorldGenMonumentPieces.WorldGenMonumentPiece6.a); + } + + IBlockData iblockdata; + int i; + + for (i = 1; i <= 7; ++i) { + iblockdata = WorldGenMonumentPieces.WorldGenMonumentPiece6.b; + if (i == 2 || i == 6) { + iblockdata = WorldGenMonumentPieces.WorldGenMonumentPiece6.a; + } + + this.a(generatoraccess, structureboundingbox, 0, i, 0, 0, i, 15, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 7, i, 0, 7, i, 15, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, i, 0, 6, i, 0, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, i, 15, 6, i, 15, iblockdata, iblockdata, false); + } + + for (i = 1; i <= 7; ++i) { + iblockdata = WorldGenMonumentPieces.WorldGenMonumentPiece6.c; + if (i == 2 || i == 6) { + iblockdata = WorldGenMonumentPieces.WorldGenMonumentPiece6.e; + } + + this.a(generatoraccess, structureboundingbox, 3, i, 7, 4, i, 8, iblockdata, iblockdata, false); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.SOUTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 1, 0, 4, 2, 0); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.EAST.a()]) { + this.a(generatoraccess, structureboundingbox, 7, 1, 3, 7, 2, 4); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.WEST.a()]) { + this.a(generatoraccess, structureboundingbox, 0, 1, 3, 0, 2, 4); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.NORTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 1, 15, 4, 2, 15); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.WEST.a()]) { + this.a(generatoraccess, structureboundingbox, 0, 1, 11, 0, 2, 12); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.EAST.a()]) { + this.a(generatoraccess, structureboundingbox, 7, 1, 11, 7, 2, 12); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker3.c[EnumDirection.SOUTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 5, 0, 4, 6, 0); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker3.c[EnumDirection.EAST.a()]) { + this.a(generatoraccess, structureboundingbox, 7, 5, 3, 7, 6, 4); + this.a(generatoraccess, structureboundingbox, 5, 4, 2, 6, 4, 5, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, false); + this.a(generatoraccess, structureboundingbox, 6, 1, 2, 6, 3, 2, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, false); + this.a(generatoraccess, structureboundingbox, 6, 1, 5, 6, 3, 5, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, false); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker3.c[EnumDirection.WEST.a()]) { + this.a(generatoraccess, structureboundingbox, 0, 5, 3, 0, 6, 4); + this.a(generatoraccess, structureboundingbox, 1, 4, 2, 2, 4, 5, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 2, 1, 3, 2, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 5, 1, 3, 5, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, false); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker2.c[EnumDirection.NORTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 5, 15, 4, 6, 15); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker2.c[EnumDirection.WEST.a()]) { + this.a(generatoraccess, structureboundingbox, 0, 5, 11, 0, 6, 12); + this.a(generatoraccess, structureboundingbox, 1, 4, 10, 2, 4, 13, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 10, 1, 3, 10, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 13, 1, 3, 13, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, false); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker2.c[EnumDirection.EAST.a()]) { + this.a(generatoraccess, structureboundingbox, 7, 5, 11, 7, 6, 12); + this.a(generatoraccess, structureboundingbox, 5, 4, 10, 6, 4, 13, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, false); + this.a(generatoraccess, structureboundingbox, 6, 1, 10, 6, 3, 10, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, false); + this.a(generatoraccess, structureboundingbox, 6, 1, 13, 6, 3, 13, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, WorldGenMonumentPieces.WorldGenMonumentPiece6.b, false); + } + + return true; + } + } + + public static class WorldGenMonumentPiece4 extends WorldGenMonumentPieces.WorldGenMonumentPiece { + + public WorldGenMonumentPiece4() {} + + public WorldGenMonumentPiece4(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, Random random) { + super(1, enumdirection, worldgenmonumentpieces_worldgenmonumentstatetracker, 2, 2, 1); + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker = this.l.b[EnumDirection.EAST.a()]; + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker1 = this.l; + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker2 = worldgenmonumentpieces_worldgenmonumentstatetracker1.b[EnumDirection.UP.a()]; + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker3 = worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.UP.a()]; + + if (this.l.a / 25 > 0) { + this.a(generatoraccess, structureboundingbox, 8, 0, worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.DOWN.a()]); + this.a(generatoraccess, structureboundingbox, 0, 0, worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.DOWN.a()]); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker2.b[EnumDirection.UP.a()] == null) { + this.a(generatoraccess, structureboundingbox, 1, 8, 1, 7, 8, 6, WorldGenMonumentPieces.WorldGenMonumentPiece4.a); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker3.b[EnumDirection.UP.a()] == null) { + this.a(generatoraccess, structureboundingbox, 8, 8, 1, 14, 8, 6, WorldGenMonumentPieces.WorldGenMonumentPiece4.a); + } + + for (int i = 1; i <= 7; ++i) { + IBlockData iblockdata = WorldGenMonumentPieces.WorldGenMonumentPiece4.b; + + if (i == 2 || i == 6) { + iblockdata = WorldGenMonumentPieces.WorldGenMonumentPiece4.a; + } + + this.a(generatoraccess, structureboundingbox, 0, i, 0, 0, i, 7, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 15, i, 0, 15, i, 7, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, i, 0, 15, i, 0, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, i, 7, 14, i, 7, iblockdata, iblockdata, false); + } + + this.a(generatoraccess, structureboundingbox, 2, 1, 3, 2, 7, 4, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, false); + this.a(generatoraccess, structureboundingbox, 3, 1, 2, 4, 7, 2, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, false); + this.a(generatoraccess, structureboundingbox, 3, 1, 5, 4, 7, 5, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, false); + this.a(generatoraccess, structureboundingbox, 13, 1, 3, 13, 7, 4, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, false); + this.a(generatoraccess, structureboundingbox, 11, 1, 2, 12, 7, 2, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, false); + this.a(generatoraccess, structureboundingbox, 11, 1, 5, 12, 7, 5, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, false); + this.a(generatoraccess, structureboundingbox, 5, 1, 3, 5, 3, 4, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, false); + this.a(generatoraccess, structureboundingbox, 10, 1, 3, 10, 3, 4, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, false); + this.a(generatoraccess, structureboundingbox, 5, 7, 2, 10, 7, 5, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, false); + this.a(generatoraccess, structureboundingbox, 5, 5, 2, 5, 7, 2, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, false); + this.a(generatoraccess, structureboundingbox, 10, 5, 2, 10, 7, 2, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, false); + this.a(generatoraccess, structureboundingbox, 5, 5, 5, 5, 7, 5, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, false); + this.a(generatoraccess, structureboundingbox, 10, 5, 5, 10, 7, 5, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, 6, 6, 2, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, 9, 6, 2, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, 6, 6, 5, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, 9, 6, 5, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 5, 4, 3, 6, 4, 4, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, false); + this.a(generatoraccess, structureboundingbox, 9, 4, 3, 10, 4, 4, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, WorldGenMonumentPieces.WorldGenMonumentPiece4.b, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece4.e, 5, 4, 2, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece4.e, 5, 4, 5, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece4.e, 10, 4, 2, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece4.e, 10, 4, 5, structureboundingbox); + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.SOUTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 1, 0, 4, 2, 0); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.NORTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 1, 7, 4, 2, 7); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.WEST.a()]) { + this.a(generatoraccess, structureboundingbox, 0, 1, 3, 0, 2, 4); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.SOUTH.a()]) { + this.a(generatoraccess, structureboundingbox, 11, 1, 0, 12, 2, 0); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.NORTH.a()]) { + this.a(generatoraccess, structureboundingbox, 11, 1, 7, 12, 2, 7); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.EAST.a()]) { + this.a(generatoraccess, structureboundingbox, 15, 1, 3, 15, 2, 4); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker2.c[EnumDirection.SOUTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 5, 0, 4, 6, 0); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker2.c[EnumDirection.NORTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 5, 7, 4, 6, 7); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker2.c[EnumDirection.WEST.a()]) { + this.a(generatoraccess, structureboundingbox, 0, 5, 3, 0, 6, 4); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker3.c[EnumDirection.SOUTH.a()]) { + this.a(generatoraccess, structureboundingbox, 11, 5, 0, 12, 6, 0); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker3.c[EnumDirection.NORTH.a()]) { + this.a(generatoraccess, structureboundingbox, 11, 5, 7, 12, 6, 7); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker3.c[EnumDirection.EAST.a()]) { + this.a(generatoraccess, structureboundingbox, 15, 5, 3, 15, 6, 4); + } + + return true; + } + } + + public static class WorldGenMonumentPiece7 extends WorldGenMonumentPieces.WorldGenMonumentPiece { + + public WorldGenMonumentPiece7() {} + + public WorldGenMonumentPiece7(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, Random random) { + super(1, enumdirection, worldgenmonumentpieces_worldgenmonumentstatetracker, 1, 1, 2); + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker = this.l.b[EnumDirection.NORTH.a()]; + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker1 = this.l; + + if (this.l.a / 25 > 0) { + this.a(generatoraccess, structureboundingbox, 0, 8, worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.DOWN.a()]); + this.a(generatoraccess, structureboundingbox, 0, 0, worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.DOWN.a()]); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.b[EnumDirection.UP.a()] == null) { + this.a(generatoraccess, structureboundingbox, 1, 4, 1, 6, 4, 7, WorldGenMonumentPieces.WorldGenMonumentPiece7.a); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.UP.a()] == null) { + this.a(generatoraccess, structureboundingbox, 1, 4, 8, 6, 4, 14, WorldGenMonumentPieces.WorldGenMonumentPiece7.a); + } + + this.a(generatoraccess, structureboundingbox, 0, 3, 0, 0, 3, 15, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 7, 3, 0, 7, 3, 15, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 1, 3, 0, 7, 3, 0, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 1, 3, 15, 6, 3, 15, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 0, 2, 0, 0, 2, 15, WorldGenMonumentPieces.WorldGenMonumentPiece7.a, WorldGenMonumentPieces.WorldGenMonumentPiece7.a, false); + this.a(generatoraccess, structureboundingbox, 7, 2, 0, 7, 2, 15, WorldGenMonumentPieces.WorldGenMonumentPiece7.a, WorldGenMonumentPieces.WorldGenMonumentPiece7.a, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 0, 7, 2, 0, WorldGenMonumentPieces.WorldGenMonumentPiece7.a, WorldGenMonumentPieces.WorldGenMonumentPiece7.a, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 15, 6, 2, 15, WorldGenMonumentPieces.WorldGenMonumentPiece7.a, WorldGenMonumentPieces.WorldGenMonumentPiece7.a, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 0, 0, 1, 15, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 7, 1, 0, 7, 1, 15, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 0, 7, 1, 0, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 15, 6, 1, 15, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 1, 1, 1, 2, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 6, 1, 1, 6, 1, 2, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 1, 3, 1, 1, 3, 2, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 6, 3, 1, 6, 3, 2, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 13, 1, 1, 14, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 6, 1, 13, 6, 1, 14, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 1, 3, 13, 1, 3, 14, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 6, 3, 13, 6, 3, 14, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 2, 1, 6, 2, 3, 6, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 5, 1, 6, 5, 3, 6, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 2, 1, 9, 2, 3, 9, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 5, 1, 9, 5, 3, 9, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 3, 2, 6, 4, 2, 6, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 3, 2, 9, 4, 2, 9, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 2, 2, 7, 2, 2, 8, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, structureboundingbox, 5, 2, 7, 5, 2, 8, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece7.e, 2, 2, 5, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece7.e, 5, 2, 5, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece7.e, 2, 2, 10, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece7.e, 5, 2, 10, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, 2, 3, 5, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, 5, 3, 5, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, 2, 3, 10, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece7.b, 5, 3, 10, structureboundingbox); + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.SOUTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 1, 0, 4, 2, 0); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.EAST.a()]) { + this.a(generatoraccess, structureboundingbox, 7, 1, 3, 7, 2, 4); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.WEST.a()]) { + this.a(generatoraccess, structureboundingbox, 0, 1, 3, 0, 2, 4); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.NORTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 1, 15, 4, 2, 15); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.WEST.a()]) { + this.a(generatoraccess, structureboundingbox, 0, 1, 11, 0, 2, 12); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.EAST.a()]) { + this.a(generatoraccess, structureboundingbox, 7, 1, 11, 7, 2, 12); + } + + return true; + } + } + + public static class WorldGenMonumentPiece3 extends WorldGenMonumentPieces.WorldGenMonumentPiece { + + public WorldGenMonumentPiece3() {} + + public WorldGenMonumentPiece3(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, Random random) { + super(1, enumdirection, worldgenmonumentpieces_worldgenmonumentstatetracker, 2, 1, 1); + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker = this.l.b[EnumDirection.EAST.a()]; + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker1 = this.l; + + if (this.l.a / 25 > 0) { + this.a(generatoraccess, structureboundingbox, 8, 0, worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.DOWN.a()]); + this.a(generatoraccess, structureboundingbox, 0, 0, worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.DOWN.a()]); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.b[EnumDirection.UP.a()] == null) { + this.a(generatoraccess, structureboundingbox, 1, 4, 1, 7, 4, 6, WorldGenMonumentPieces.WorldGenMonumentPiece3.a); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.UP.a()] == null) { + this.a(generatoraccess, structureboundingbox, 8, 4, 1, 14, 4, 6, WorldGenMonumentPieces.WorldGenMonumentPiece3.a); + } + + this.a(generatoraccess, structureboundingbox, 0, 3, 0, 0, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, false); + this.a(generatoraccess, structureboundingbox, 15, 3, 0, 15, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, false); + this.a(generatoraccess, structureboundingbox, 1, 3, 0, 15, 3, 0, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, false); + this.a(generatoraccess, structureboundingbox, 1, 3, 7, 14, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, false); + this.a(generatoraccess, structureboundingbox, 0, 2, 0, 0, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPiece3.a, WorldGenMonumentPieces.WorldGenMonumentPiece3.a, false); + this.a(generatoraccess, structureboundingbox, 15, 2, 0, 15, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPiece3.a, WorldGenMonumentPieces.WorldGenMonumentPiece3.a, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 0, 15, 2, 0, WorldGenMonumentPieces.WorldGenMonumentPiece3.a, WorldGenMonumentPieces.WorldGenMonumentPiece3.a, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 7, 14, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPiece3.a, WorldGenMonumentPieces.WorldGenMonumentPiece3.a, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 0, 0, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, false); + this.a(generatoraccess, structureboundingbox, 15, 1, 0, 15, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 0, 15, 1, 0, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 7, 14, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, false); + this.a(generatoraccess, structureboundingbox, 5, 1, 0, 10, 1, 4, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, false); + this.a(generatoraccess, structureboundingbox, 6, 2, 0, 9, 2, 3, WorldGenMonumentPieces.WorldGenMonumentPiece3.a, WorldGenMonumentPieces.WorldGenMonumentPiece3.a, false); + this.a(generatoraccess, structureboundingbox, 5, 3, 0, 10, 3, 4, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, WorldGenMonumentPieces.WorldGenMonumentPiece3.b, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece3.e, 6, 2, 3, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece3.e, 9, 2, 3, structureboundingbox); + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.SOUTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 1, 0, 4, 2, 0); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.NORTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 1, 7, 4, 2, 7); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.WEST.a()]) { + this.a(generatoraccess, structureboundingbox, 0, 1, 3, 0, 2, 4); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.SOUTH.a()]) { + this.a(generatoraccess, structureboundingbox, 11, 1, 0, 12, 2, 0); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.NORTH.a()]) { + this.a(generatoraccess, structureboundingbox, 11, 1, 7, 12, 2, 7); + } + + if (worldgenmonumentpieces_worldgenmonumentstatetracker.c[EnumDirection.EAST.a()]) { + this.a(generatoraccess, structureboundingbox, 15, 1, 3, 15, 2, 4); + } + + return true; + } + } + + public static class WorldGenMonumentPiece5 extends WorldGenMonumentPieces.WorldGenMonumentPiece { + + public WorldGenMonumentPiece5() {} + + public WorldGenMonumentPiece5(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, Random random) { + super(1, enumdirection, worldgenmonumentpieces_worldgenmonumentstatetracker, 1, 2, 1); + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (this.l.a / 25 > 0) { + this.a(generatoraccess, structureboundingbox, 0, 0, this.l.c[EnumDirection.DOWN.a()]); + } + + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker = this.l.b[EnumDirection.UP.a()]; + + if (worldgenmonumentpieces_worldgenmonumentstatetracker.b[EnumDirection.UP.a()] == null) { + this.a(generatoraccess, structureboundingbox, 1, 8, 1, 6, 8, 6, WorldGenMonumentPieces.WorldGenMonumentPiece5.a); + } + + this.a(generatoraccess, structureboundingbox, 0, 4, 0, 0, 4, 7, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 7, 4, 0, 7, 4, 7, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 1, 4, 0, 6, 4, 0, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 1, 4, 7, 6, 4, 7, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 2, 4, 1, 2, 4, 2, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 1, 4, 2, 1, 4, 2, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 5, 4, 1, 5, 4, 2, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 6, 4, 2, 6, 4, 2, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 2, 4, 5, 2, 4, 6, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 1, 4, 5, 1, 4, 5, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 5, 4, 5, 5, 4, 6, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 6, 4, 5, 6, 4, 5, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker1 = this.l; + + for (int i = 1; i <= 5; i += 4) { + byte b0 = 0; + + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.SOUTH.a()]) { + this.a(generatoraccess, structureboundingbox, 2, i, b0, 2, i + 2, b0, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 5, i, b0, 5, i + 2, b0, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 3, i + 2, b0, 4, i + 2, b0, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + } else { + this.a(generatoraccess, structureboundingbox, 0, i, b0, 7, i + 2, b0, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 0, i + 1, b0, 7, i + 1, b0, WorldGenMonumentPieces.WorldGenMonumentPiece5.a, WorldGenMonumentPieces.WorldGenMonumentPiece5.a, false); + } + + b0 = 7; + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.NORTH.a()]) { + this.a(generatoraccess, structureboundingbox, 2, i, b0, 2, i + 2, b0, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 5, i, b0, 5, i + 2, b0, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 3, i + 2, b0, 4, i + 2, b0, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + } else { + this.a(generatoraccess, structureboundingbox, 0, i, b0, 7, i + 2, b0, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, 0, i + 1, b0, 7, i + 1, b0, WorldGenMonumentPieces.WorldGenMonumentPiece5.a, WorldGenMonumentPieces.WorldGenMonumentPiece5.a, false); + } + + byte b1 = 0; + + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.WEST.a()]) { + this.a(generatoraccess, structureboundingbox, b1, i, 2, b1, i + 2, 2, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, b1, i, 5, b1, i + 2, 5, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, b1, i + 2, 3, b1, i + 2, 4, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + } else { + this.a(generatoraccess, structureboundingbox, b1, i, 0, b1, i + 2, 7, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, b1, i + 1, 0, b1, i + 1, 7, WorldGenMonumentPieces.WorldGenMonumentPiece5.a, WorldGenMonumentPieces.WorldGenMonumentPiece5.a, false); + } + + b1 = 7; + if (worldgenmonumentpieces_worldgenmonumentstatetracker1.c[EnumDirection.EAST.a()]) { + this.a(generatoraccess, structureboundingbox, b1, i, 2, b1, i + 2, 2, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, b1, i, 5, b1, i + 2, 5, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, b1, i + 2, 3, b1, i + 2, 4, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + } else { + this.a(generatoraccess, structureboundingbox, b1, i, 0, b1, i + 2, 7, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, WorldGenMonumentPieces.WorldGenMonumentPiece5.b, false); + this.a(generatoraccess, structureboundingbox, b1, i + 1, 0, b1, i + 1, 7, WorldGenMonumentPieces.WorldGenMonumentPiece5.a, WorldGenMonumentPieces.WorldGenMonumentPiece5.a, false); + } + + worldgenmonumentpieces_worldgenmonumentstatetracker1 = worldgenmonumentpieces_worldgenmonumentstatetracker; + } + + return true; + } + } + + public static class WorldGenMonumentPieceSimpleT extends WorldGenMonumentPieces.WorldGenMonumentPiece { + + public WorldGenMonumentPieceSimpleT() {} + + public WorldGenMonumentPieceSimpleT(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, Random random) { + super(1, enumdirection, worldgenmonumentpieces_worldgenmonumentstatetracker, 1, 1, 1); + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (this.l.a / 25 > 0) { + this.a(generatoraccess, structureboundingbox, 0, 0, this.l.c[EnumDirection.DOWN.a()]); + } + + if (this.l.b[EnumDirection.UP.a()] == null) { + this.a(generatoraccess, structureboundingbox, 1, 4, 1, 6, 4, 6, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.a); + } + + for (int i = 1; i <= 6; ++i) { + for (int j = 1; j <= 6; ++j) { + if (random.nextInt(3) != 0) { + int k = 2 + (random.nextInt(4) == 0 ? 0 : 1); + IBlockData iblockdata = Blocks.WET_SPONGE.getBlockData(); + + this.a(generatoraccess, structureboundingbox, i, k, j, i, 3, j, iblockdata, iblockdata, false); + } + } + } + + this.a(generatoraccess, structureboundingbox, 0, 1, 0, 0, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.b, false); + this.a(generatoraccess, structureboundingbox, 7, 1, 0, 7, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.b, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 0, 6, 1, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.b, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 7, 6, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.b, false); + this.a(generatoraccess, structureboundingbox, 0, 2, 0, 0, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.c, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.c, false); + this.a(generatoraccess, structureboundingbox, 7, 2, 0, 7, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.c, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.c, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 0, 6, 2, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.c, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.c, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 7, 6, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.c, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.c, false); + this.a(generatoraccess, structureboundingbox, 0, 3, 0, 0, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.b, false); + this.a(generatoraccess, structureboundingbox, 7, 3, 0, 7, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.b, false); + this.a(generatoraccess, structureboundingbox, 1, 3, 0, 6, 3, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.b, false); + this.a(generatoraccess, structureboundingbox, 1, 3, 7, 6, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.b, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 3, 0, 2, 4, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.c, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.c, false); + this.a(generatoraccess, structureboundingbox, 7, 1, 3, 7, 2, 4, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.c, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.c, false); + this.a(generatoraccess, structureboundingbox, 3, 1, 0, 4, 2, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.c, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.c, false); + this.a(generatoraccess, structureboundingbox, 3, 1, 7, 4, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.c, WorldGenMonumentPieces.WorldGenMonumentPieceSimpleT.c, false); + if (this.l.c[EnumDirection.SOUTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 1, 0, 4, 2, 0); + } + + return true; + } + } + + public static class WorldGenMonumentPieceSimple extends WorldGenMonumentPieces.WorldGenMonumentPiece { + + private int p; + + public WorldGenMonumentPieceSimple() {} + + public WorldGenMonumentPieceSimple(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, Random random) { + super(1, enumdirection, worldgenmonumentpieces_worldgenmonumentstatetracker, 1, 1, 1); + this.p = random.nextInt(3); + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (this.l.a / 25 > 0) { + this.a(generatoraccess, structureboundingbox, 0, 0, this.l.c[EnumDirection.DOWN.a()]); + } + + if (this.l.b[EnumDirection.UP.a()] == null) { + this.a(generatoraccess, structureboundingbox, 1, 4, 1, 6, 4, 6, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a); + } + + boolean flag = this.p != 0 && random.nextBoolean() && !this.l.c[EnumDirection.DOWN.a()] && !this.l.c[EnumDirection.UP.a()] && this.l.c() > 1; + + if (this.p == 0) { + this.a(generatoraccess, structureboundingbox, 0, 1, 0, 2, 1, 2, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 0, 3, 0, 2, 3, 2, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 0, 2, 0, 0, 2, 2, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 0, 2, 2, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.e, 1, 2, 1, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 5, 1, 0, 7, 1, 2, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 5, 3, 0, 7, 3, 2, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 7, 2, 0, 7, 2, 2, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, structureboundingbox, 5, 2, 0, 6, 2, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.e, 6, 2, 1, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 0, 1, 5, 2, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 0, 3, 5, 2, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 0, 2, 5, 0, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 7, 2, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.e, 1, 2, 6, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 5, 1, 5, 7, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 5, 3, 5, 7, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 7, 2, 5, 7, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, structureboundingbox, 5, 2, 7, 6, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.e, 6, 2, 6, structureboundingbox); + if (this.l.c[EnumDirection.SOUTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 3, 0, 4, 3, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + } else { + this.a(generatoraccess, structureboundingbox, 3, 3, 0, 4, 3, 1, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 3, 2, 0, 4, 2, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, structureboundingbox, 3, 1, 0, 4, 1, 1, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + } + + if (this.l.c[EnumDirection.NORTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 3, 7, 4, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + } else { + this.a(generatoraccess, structureboundingbox, 3, 3, 6, 4, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 3, 2, 7, 4, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, structureboundingbox, 3, 1, 6, 4, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + } + + if (this.l.c[EnumDirection.WEST.a()]) { + this.a(generatoraccess, structureboundingbox, 0, 3, 3, 0, 3, 4, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + } else { + this.a(generatoraccess, structureboundingbox, 0, 3, 3, 1, 3, 4, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 0, 2, 3, 0, 2, 4, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 3, 1, 1, 4, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + } + + if (this.l.c[EnumDirection.EAST.a()]) { + this.a(generatoraccess, structureboundingbox, 7, 3, 3, 7, 3, 4, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + } else { + this.a(generatoraccess, structureboundingbox, 6, 3, 3, 7, 3, 4, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 7, 2, 3, 7, 2, 4, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, structureboundingbox, 6, 1, 3, 7, 1, 4, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + } + } else if (this.p == 1) { + this.a(generatoraccess, structureboundingbox, 2, 1, 2, 2, 3, 2, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 2, 1, 5, 2, 3, 5, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 5, 1, 5, 5, 3, 5, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 5, 1, 2, 5, 3, 2, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.e, 2, 2, 2, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.e, 2, 2, 5, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.e, 5, 2, 5, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.e, 5, 2, 2, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 0, 1, 0, 1, 3, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 1, 0, 3, 1, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 7, 1, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 6, 0, 3, 6, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 6, 1, 7, 7, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 7, 1, 6, 7, 3, 6, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 6, 1, 0, 7, 3, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 7, 1, 1, 7, 3, 1, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, 1, 2, 0, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, 0, 2, 1, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, 1, 2, 7, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, 0, 2, 6, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, 6, 2, 7, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, 7, 2, 6, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, 6, 2, 0, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, 7, 2, 1, structureboundingbox); + if (!this.l.c[EnumDirection.SOUTH.a()]) { + this.a(generatoraccess, structureboundingbox, 1, 3, 0, 6, 3, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 0, 6, 2, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 0, 6, 1, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + } + + if (!this.l.c[EnumDirection.NORTH.a()]) { + this.a(generatoraccess, structureboundingbox, 1, 3, 7, 6, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 7, 6, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 7, 6, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + } + + if (!this.l.c[EnumDirection.WEST.a()]) { + this.a(generatoraccess, structureboundingbox, 0, 3, 1, 0, 3, 6, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 0, 2, 1, 0, 2, 6, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 1, 0, 1, 6, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + } + + if (!this.l.c[EnumDirection.EAST.a()]) { + this.a(generatoraccess, structureboundingbox, 7, 3, 1, 7, 3, 6, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 7, 2, 1, 7, 2, 6, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, structureboundingbox, 7, 1, 1, 7, 1, 6, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + } + } else if (this.p == 2) { + this.a(generatoraccess, structureboundingbox, 0, 1, 0, 0, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 7, 1, 0, 7, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 0, 6, 1, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 7, 6, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 0, 2, 0, 0, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.c, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.c, false); + this.a(generatoraccess, structureboundingbox, 7, 2, 0, 7, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.c, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.c, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 0, 6, 2, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.c, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.c, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 7, 6, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.c, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.c, false); + this.a(generatoraccess, structureboundingbox, 0, 3, 0, 0, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 7, 3, 0, 7, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 1, 3, 0, 6, 3, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 1, 3, 7, 6, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 3, 0, 2, 4, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.c, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.c, false); + this.a(generatoraccess, structureboundingbox, 7, 1, 3, 7, 2, 4, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.c, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.c, false); + this.a(generatoraccess, structureboundingbox, 3, 1, 0, 4, 2, 0, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.c, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.c, false); + this.a(generatoraccess, structureboundingbox, 3, 1, 7, 4, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.c, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.c, false); + if (this.l.c[EnumDirection.SOUTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 1, 0, 4, 2, 0); + } + + if (this.l.c[EnumDirection.NORTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 1, 7, 4, 2, 7); + } + + if (this.l.c[EnumDirection.WEST.a()]) { + this.a(generatoraccess, structureboundingbox, 0, 1, 3, 0, 2, 4); + } + + if (this.l.c[EnumDirection.EAST.a()]) { + this.a(generatoraccess, structureboundingbox, 7, 1, 3, 7, 2, 4); + } + } + + if (flag) { + this.a(generatoraccess, structureboundingbox, 3, 1, 3, 4, 1, 4, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + this.a(generatoraccess, structureboundingbox, 3, 2, 3, 4, 2, 4, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.a, false); + this.a(generatoraccess, structureboundingbox, 3, 3, 3, 4, 3, 4, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, WorldGenMonumentPieces.WorldGenMonumentPieceSimple.b, false); + } + + return true; + } + } + + public static class WorldGenMonumentPieceEntry extends WorldGenMonumentPieces.WorldGenMonumentPiece { + + public WorldGenMonumentPieceEntry() {} + + public WorldGenMonumentPieceEntry(EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker) { + super(1, enumdirection, worldgenmonumentpieces_worldgenmonumentstatetracker, 1, 1, 1); + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + this.a(generatoraccess, structureboundingbox, 0, 3, 0, 2, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, false); + this.a(generatoraccess, structureboundingbox, 5, 3, 0, 7, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, false); + this.a(generatoraccess, structureboundingbox, 0, 2, 0, 1, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, false); + this.a(generatoraccess, structureboundingbox, 6, 2, 0, 7, 2, 7, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 0, 0, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, false); + this.a(generatoraccess, structureboundingbox, 7, 1, 0, 7, 1, 7, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 7, 7, 3, 7, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 0, 2, 3, 0, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, false); + this.a(generatoraccess, structureboundingbox, 5, 1, 0, 6, 3, 0, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, WorldGenMonumentPieces.WorldGenMonumentPieceEntry.b, false); + if (this.l.c[EnumDirection.NORTH.a()]) { + this.a(generatoraccess, structureboundingbox, 3, 1, 7, 4, 2, 7); + } + + if (this.l.c[EnumDirection.WEST.a()]) { + this.a(generatoraccess, structureboundingbox, 0, 1, 3, 1, 2, 4); + } + + if (this.l.c[EnumDirection.EAST.a()]) { + this.a(generatoraccess, structureboundingbox, 6, 1, 3, 7, 2, 4); + } + + return true; + } + } + + public static class WorldGenMonumentPiece1 extends WorldGenMonumentPieces.WorldGenMonumentPiece { + + private WorldGenMonumentPieces.WorldGenMonumentStateTracker p; + private WorldGenMonumentPieces.WorldGenMonumentStateTracker q; + private final List r = Lists.newArrayList(); + + public WorldGenMonumentPiece1() {} + + public WorldGenMonumentPiece1(Random random, int i, int j, EnumDirection enumdirection) { + super(0); + this.a(enumdirection); + EnumDirection enumdirection1 = this.f(); + + if (enumdirection1.k() == EnumDirection.EnumAxis.Z) { + this.n = new StructureBoundingBox(i, 39, j, i + 58 - 1, 61, j + 58 - 1); + } else { + this.n = new StructureBoundingBox(i, 39, j, i + 58 - 1, 61, j + 58 - 1); + } + + List list = this.a(random); + + this.p.d = true; + this.r.add(new WorldGenMonumentPieces.WorldGenMonumentPieceEntry(enumdirection1, this.p)); + this.r.add(new WorldGenMonumentPieces.WorldGenMonumentPiece2(enumdirection1, this.q, random)); + List list1 = Lists.newArrayList(); + + list1.add(new WorldGenMonumentPieces.WorldGenMonumentPieceSelector6()); + list1.add(new WorldGenMonumentPieces.WorldGenMonumentPieceSelector4()); + list1.add(new WorldGenMonumentPieces.WorldGenMonumentPieceSelector3()); + list1.add(new WorldGenMonumentPieces.WorldGenMonumentPieceSelector7()); + list1.add(new WorldGenMonumentPieces.WorldGenMonumentPieceSelector5()); + list1.add(new WorldGenMonumentPieces.WorldGenMonumentPieceSelector1()); + list1.add(new WorldGenMonumentPieces.WorldGenMonumentPieceSelector2()); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker = (WorldGenMonumentPieces.WorldGenMonumentStateTracker) iterator.next(); + + if (!worldgenmonumentpieces_worldgenmonumentstatetracker.d && !worldgenmonumentpieces_worldgenmonumentstatetracker.b()) { + Iterator iterator1 = list1.iterator(); + + while (iterator1.hasNext()) { + WorldGenMonumentPieces.IWorldGenMonumentPieceSelector worldgenmonumentpieces_iworldgenmonumentpieceselector = (WorldGenMonumentPieces.IWorldGenMonumentPieceSelector) iterator1.next(); + + if (worldgenmonumentpieces_iworldgenmonumentpieceselector.a(worldgenmonumentpieces_worldgenmonumentstatetracker)) { + this.r.add(worldgenmonumentpieces_iworldgenmonumentpieceselector.a(enumdirection1, worldgenmonumentpieces_worldgenmonumentstatetracker, random)); + break; + } + } + } + } + + int k = this.n.b; + int l = this.a(9, 22); + int i1 = this.b(9, 22); + Iterator iterator2 = this.r.iterator(); + + while (iterator2.hasNext()) { + WorldGenMonumentPieces.WorldGenMonumentPiece worldgenmonumentpieces_worldgenmonumentpiece = (WorldGenMonumentPieces.WorldGenMonumentPiece) iterator2.next(); + + worldgenmonumentpieces_worldgenmonumentpiece.d().a(l, k, i1); + } + + StructureBoundingBox structureboundingbox = StructureBoundingBox.a(this.a(1, 1), this.d(1), this.b(1, 1), this.a(23, 21), this.d(8), this.b(23, 21)); + StructureBoundingBox structureboundingbox1 = StructureBoundingBox.a(this.a(34, 1), this.d(1), this.b(34, 1), this.a(56, 21), this.d(8), this.b(56, 21)); + StructureBoundingBox structureboundingbox2 = StructureBoundingBox.a(this.a(22, 22), this.d(13), this.b(22, 22), this.a(35, 35), this.d(17), this.b(35, 35)); + int j1 = random.nextInt(); + + this.r.add(new WorldGenMonumentPieces.WorldGenMonumentPiece8(enumdirection1, structureboundingbox, j1++)); + this.r.add(new WorldGenMonumentPieces.WorldGenMonumentPiece8(enumdirection1, structureboundingbox1, j1++)); + this.r.add(new WorldGenMonumentPieces.WorldGenMonumentPiecePenthouse(enumdirection1, structureboundingbox2)); + } + + private List a(Random random) { + WorldGenMonumentPieces.WorldGenMonumentStateTracker[] aworldgenmonumentpieces_worldgenmonumentstatetracker = new WorldGenMonumentPieces.WorldGenMonumentStateTracker[75]; + + boolean flag; + int i; + int j; + int k; + + for (j = 0; j < 5; ++j) { + for (k = 0; k < 4; ++k) { + flag = false; + i = b(j, 0, k); + aworldgenmonumentpieces_worldgenmonumentstatetracker[i] = new WorldGenMonumentPieces.WorldGenMonumentStateTracker(i); + } + } + + for (j = 0; j < 5; ++j) { + for (k = 0; k < 4; ++k) { + flag = true; + i = b(j, 1, k); + aworldgenmonumentpieces_worldgenmonumentstatetracker[i] = new WorldGenMonumentPieces.WorldGenMonumentStateTracker(i); + } + } + + for (j = 1; j < 4; ++j) { + for (k = 0; k < 2; ++k) { + flag = true; + i = b(j, 2, k); + aworldgenmonumentpieces_worldgenmonumentstatetracker[i] = new WorldGenMonumentPieces.WorldGenMonumentStateTracker(i); + } + } + + this.p = aworldgenmonumentpieces_worldgenmonumentstatetracker[WorldGenMonumentPieces.WorldGenMonumentPiece1.h]; + + int l; + int i1; + int j1; + int k1; + int l1; + + for (j = 0; j < 5; ++j) { + for (k = 0; k < 5; ++k) { + for (int i2 = 0; i2 < 3; ++i2) { + i = b(j, i2, k); + if (aworldgenmonumentpieces_worldgenmonumentstatetracker[i] != null) { + EnumDirection[] aenumdirection = EnumDirection.values(); + + l = aenumdirection.length; + + for (i1 = 0; i1 < l; ++i1) { + EnumDirection enumdirection = aenumdirection[i1]; + + j1 = j + enumdirection.getAdjacentX(); + k1 = i2 + enumdirection.getAdjacentY(); + l1 = k + enumdirection.getAdjacentZ(); + if (j1 >= 0 && j1 < 5 && l1 >= 0 && l1 < 5 && k1 >= 0 && k1 < 3) { + int j2 = b(j1, k1, l1); + + if (aworldgenmonumentpieces_worldgenmonumentstatetracker[j2] != null) { + if (l1 == k) { + aworldgenmonumentpieces_worldgenmonumentstatetracker[i].a(enumdirection, aworldgenmonumentpieces_worldgenmonumentstatetracker[j2]); + } else { + aworldgenmonumentpieces_worldgenmonumentstatetracker[i].a(enumdirection.opposite(), aworldgenmonumentpieces_worldgenmonumentstatetracker[j2]); + } + } + } + } + } + } + } + } + + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker = new WorldGenMonumentPieces.WorldGenMonumentStateTracker(1003); + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker1 = new WorldGenMonumentPieces.WorldGenMonumentStateTracker(1001); + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker2 = new WorldGenMonumentPieces.WorldGenMonumentStateTracker(1002); + + aworldgenmonumentpieces_worldgenmonumentstatetracker[WorldGenMonumentPieces.WorldGenMonumentPiece1.i].a(EnumDirection.UP, worldgenmonumentpieces_worldgenmonumentstatetracker); + aworldgenmonumentpieces_worldgenmonumentstatetracker[WorldGenMonumentPieces.WorldGenMonumentPiece1.j].a(EnumDirection.SOUTH, worldgenmonumentpieces_worldgenmonumentstatetracker1); + aworldgenmonumentpieces_worldgenmonumentstatetracker[WorldGenMonumentPieces.WorldGenMonumentPiece1.k].a(EnumDirection.SOUTH, worldgenmonumentpieces_worldgenmonumentstatetracker2); + worldgenmonumentpieces_worldgenmonumentstatetracker.d = true; + worldgenmonumentpieces_worldgenmonumentstatetracker1.d = true; + worldgenmonumentpieces_worldgenmonumentstatetracker2.d = true; + this.p.e = true; + this.q = aworldgenmonumentpieces_worldgenmonumentstatetracker[b(random.nextInt(4), 0, 2)]; + this.q.d = true; + this.q.b[EnumDirection.EAST.a()].d = true; + this.q.b[EnumDirection.NORTH.a()].d = true; + this.q.b[EnumDirection.EAST.a()].b[EnumDirection.NORTH.a()].d = true; + this.q.b[EnumDirection.UP.a()].d = true; + this.q.b[EnumDirection.EAST.a()].b[EnumDirection.UP.a()].d = true; + this.q.b[EnumDirection.NORTH.a()].b[EnumDirection.UP.a()].d = true; + this.q.b[EnumDirection.EAST.a()].b[EnumDirection.NORTH.a()].b[EnumDirection.UP.a()].d = true; + List list = Lists.newArrayList(); + WorldGenMonumentPieces.WorldGenMonumentStateTracker[] aworldgenmonumentpieces_worldgenmonumentstatetracker1 = aworldgenmonumentpieces_worldgenmonumentstatetracker; + + l = aworldgenmonumentpieces_worldgenmonumentstatetracker.length; + + for (i1 = 0; i1 < l; ++i1) { + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker3 = aworldgenmonumentpieces_worldgenmonumentstatetracker1[i1]; + + if (worldgenmonumentpieces_worldgenmonumentstatetracker3 != null) { + worldgenmonumentpieces_worldgenmonumentstatetracker3.a(); + list.add(worldgenmonumentpieces_worldgenmonumentstatetracker3); + } + } + + worldgenmonumentpieces_worldgenmonumentstatetracker.a(); + Collections.shuffle(list, random); + int k2 = 1; + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker4 = (WorldGenMonumentPieces.WorldGenMonumentStateTracker) iterator.next(); + int l2 = 0; + + j1 = 0; + + while (l2 < 2 && j1 < 5) { + ++j1; + k1 = random.nextInt(6); + if (worldgenmonumentpieces_worldgenmonumentstatetracker4.c[k1]) { + l1 = EnumDirection.fromType1(k1).opposite().a(); + worldgenmonumentpieces_worldgenmonumentstatetracker4.c[k1] = false; + worldgenmonumentpieces_worldgenmonumentstatetracker4.b[k1].c[l1] = false; + if (worldgenmonumentpieces_worldgenmonumentstatetracker4.a(k2++) && worldgenmonumentpieces_worldgenmonumentstatetracker4.b[k1].a(k2++)) { + ++l2; + } else { + worldgenmonumentpieces_worldgenmonumentstatetracker4.c[k1] = true; + worldgenmonumentpieces_worldgenmonumentstatetracker4.b[k1].c[l1] = true; + } + } + } + } + + list.add(worldgenmonumentpieces_worldgenmonumentstatetracker); + list.add(worldgenmonumentpieces_worldgenmonumentstatetracker1); + list.add(worldgenmonumentpieces_worldgenmonumentstatetracker2); + return list; + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + int i = Math.max(generatoraccess.getSeaLevel(), 64) - this.n.b; + + this.a(generatoraccess, structureboundingbox, 0, 0, 0, 58, i, 58); + this.a(false, 0, generatoraccess, random, structureboundingbox); + this.a(true, 33, generatoraccess, random, structureboundingbox); + this.a(generatoraccess, random, structureboundingbox); + this.b(generatoraccess, random, structureboundingbox); + this.c(generatoraccess, random, structureboundingbox); + this.d(generatoraccess, random, structureboundingbox); + this.e(generatoraccess, random, structureboundingbox); + this.f(generatoraccess, random, structureboundingbox); + + int j; + + for (j = 0; j < 7; ++j) { + int k = 0; + + while (k < 7) { + if (k == 0 && j == 3) { + k = 6; + } + + int l = j * 9; + int i1 = k * 9; + + for (int j1 = 0; j1 < 4; ++j1) { + for (int k1 = 0; k1 < 4; ++k1) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, l + j1, 0, i1 + k1, structureboundingbox); + this.b(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, l + j1, -1, i1 + k1, structureboundingbox); + } + } + + if (j != 0 && j != 6) { + k += 6; + } else { + ++k; + } + } + } + + for (j = 0; j < 5; ++j) { + this.a(generatoraccess, structureboundingbox, -1 - j, 0 + j * 2, -1 - j, -1 - j, 23, 58 + j); + this.a(generatoraccess, structureboundingbox, 58 + j, 0 + j * 2, -1 - j, 58 + j, 23, 58 + j); + this.a(generatoraccess, structureboundingbox, 0 - j, 0 + j * 2, -1 - j, 57 + j, 23, -1 - j); + this.a(generatoraccess, structureboundingbox, 0 - j, 0 + j * 2, 58 + j, 57 + j, 23, 58 + j); + } + + Iterator iterator = this.r.iterator(); + + while (iterator.hasNext()) { + WorldGenMonumentPieces.WorldGenMonumentPiece worldgenmonumentpieces_worldgenmonumentpiece = (WorldGenMonumentPieces.WorldGenMonumentPiece) iterator.next(); + + if (worldgenmonumentpieces_worldgenmonumentpiece.d().a(structureboundingbox)) { + worldgenmonumentpieces_worldgenmonumentpiece.a(generatoraccess, random, structureboundingbox, chunkcoordintpair); + } + } + + return true; + } + + private void a(boolean flag, int i, GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox) { + boolean flag1 = true; + + if (this.a(structureboundingbox, i, 0, i + 23, 20)) { + this.a(generatoraccess, structureboundingbox, i + 0, 0, 0, i + 24, 0, 20, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, i + 0, 1, 0, i + 24, 10, 20); + + int j; + + for (j = 0; j < 4; ++j) { + this.a(generatoraccess, structureboundingbox, i + j, j + 1, j, i + j, j + 1, 20, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, structureboundingbox, i + j + 7, j + 5, j + 7, i + j + 7, j + 5, 20, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, structureboundingbox, i + 17 - j, j + 5, j + 7, i + 17 - j, j + 5, 20, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, structureboundingbox, i + 24 - j, j + 1, j, i + 24 - j, j + 1, 20, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, structureboundingbox, i + j + 1, j + 1, j, i + 23 - j, j + 1, j, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, structureboundingbox, i + j + 8, j + 5, j + 7, i + 16 - j, j + 5, j + 7, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + } + + this.a(generatoraccess, structureboundingbox, i + 4, 4, 4, i + 6, 4, 20, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, i + 7, 4, 4, i + 17, 4, 6, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, i + 18, 4, 4, i + 20, 4, 20, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, i + 11, 8, 11, i + 13, 8, 20, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i + 12, 9, 12, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i + 12, 9, 15, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i + 12, 9, 18, structureboundingbox); + j = i + (flag ? 19 : 5); + int k = i + (flag ? 5 : 19); + + int l; + + for (l = 20; l >= 5; l -= 3) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, j, 5, l, structureboundingbox); + } + + for (l = 19; l >= 7; l -= 3) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, k, 5, l, structureboundingbox); + } + + for (l = 0; l < 4; ++l) { + int i1 = flag ? i + 24 - (17 - l * 3) : i + 17 - l * 3; + + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i1, 5, 5, structureboundingbox); + } + + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, k, 5, 5, structureboundingbox); + this.a(generatoraccess, structureboundingbox, i + 11, 1, 12, i + 13, 7, 12, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, i + 12, 1, 11, i + 12, 7, 13, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + } + + } + + private void a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox) { + if (this.a(structureboundingbox, 22, 5, 35, 17)) { + this.a(generatoraccess, structureboundingbox, 25, 0, 0, 32, 8, 20); + + for (int i = 0; i < 4; ++i) { + this.a(generatoraccess, structureboundingbox, 24, 2, 5 + i * 4, 24, 4, 5 + i * 4, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, structureboundingbox, 22, 4, 5 + i * 4, 23, 4, 5 + i * 4, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 25, 5, 5 + i * 4, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 26, 6, 5 + i * 4, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.e, 26, 5, 5 + i * 4, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 33, 2, 5 + i * 4, 33, 4, 5 + i * 4, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, structureboundingbox, 34, 4, 5 + i * 4, 35, 4, 5 + i * 4, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 32, 5, 5 + i * 4, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 31, 6, 5 + i * 4, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.e, 31, 5, 5 + i * 4, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 27, 6, 5 + i * 4, 30, 6, 5 + i * 4, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + } + } + + } + + private void b(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox) { + if (this.a(structureboundingbox, 15, 20, 42, 21)) { + this.a(generatoraccess, structureboundingbox, 15, 0, 21, 42, 0, 21, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 26, 1, 21, 31, 3, 21); + this.a(generatoraccess, structureboundingbox, 21, 12, 21, 36, 12, 21, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 17, 11, 21, 40, 11, 21, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 16, 10, 21, 41, 10, 21, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 15, 7, 21, 42, 9, 21, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 16, 6, 21, 41, 6, 21, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 17, 5, 21, 40, 5, 21, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 21, 4, 21, 36, 4, 21, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 22, 3, 21, 26, 3, 21, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 31, 3, 21, 35, 3, 21, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 23, 2, 21, 25, 2, 21, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 32, 2, 21, 34, 2, 21, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 28, 4, 20, 29, 4, 21, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 27, 3, 21, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 30, 3, 21, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 26, 2, 21, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 31, 2, 21, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 25, 1, 21, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 32, 1, 21, structureboundingbox); + + int i; + + for (i = 0; i < 7; ++i) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.c, 28 - i, 6 + i, 21, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.c, 29 + i, 6 + i, 21, structureboundingbox); + } + + for (i = 0; i < 4; ++i) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.c, 28 - i, 9 + i, 21, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.c, 29 + i, 9 + i, 21, structureboundingbox); + } + + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.c, 28, 12, 21, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.c, 29, 12, 21, structureboundingbox); + + for (i = 0; i < 3; ++i) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.c, 22 - i * 2, 8, 21, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.c, 22 - i * 2, 9, 21, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.c, 35 + i * 2, 8, 21, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.c, 35 + i * 2, 9, 21, structureboundingbox); + } + + this.a(generatoraccess, structureboundingbox, 15, 13, 21, 42, 15, 21); + this.a(generatoraccess, structureboundingbox, 15, 1, 21, 15, 6, 21); + this.a(generatoraccess, structureboundingbox, 16, 1, 21, 16, 5, 21); + this.a(generatoraccess, structureboundingbox, 17, 1, 21, 20, 4, 21); + this.a(generatoraccess, structureboundingbox, 21, 1, 21, 21, 3, 21); + this.a(generatoraccess, structureboundingbox, 22, 1, 21, 22, 2, 21); + this.a(generatoraccess, structureboundingbox, 23, 1, 21, 24, 1, 21); + this.a(generatoraccess, structureboundingbox, 42, 1, 21, 42, 6, 21); + this.a(generatoraccess, structureboundingbox, 41, 1, 21, 41, 5, 21); + this.a(generatoraccess, structureboundingbox, 37, 1, 21, 40, 4, 21); + this.a(generatoraccess, structureboundingbox, 36, 1, 21, 36, 3, 21); + this.a(generatoraccess, structureboundingbox, 33, 1, 21, 34, 1, 21); + this.a(generatoraccess, structureboundingbox, 35, 1, 21, 35, 2, 21); + } + + } + + private void c(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox) { + if (this.a(structureboundingbox, 21, 21, 36, 36)) { + this.a(generatoraccess, structureboundingbox, 21, 0, 22, 36, 0, 36, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 21, 1, 22, 36, 23, 36); + + for (int i = 0; i < 4; ++i) { + this.a(generatoraccess, structureboundingbox, 21 + i, 13 + i, 21 + i, 36 - i, 13 + i, 21 + i, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, structureboundingbox, 21 + i, 13 + i, 36 - i, 36 - i, 13 + i, 36 - i, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, structureboundingbox, 21 + i, 13 + i, 22 + i, 21 + i, 13 + i, 35 - i, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, structureboundingbox, 36 - i, 13 + i, 22 + i, 36 - i, 13 + i, 35 - i, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + } + + this.a(generatoraccess, structureboundingbox, 25, 16, 25, 32, 16, 32, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 25, 17, 25, 25, 19, 25, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, structureboundingbox, 32, 17, 25, 32, 19, 25, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, structureboundingbox, 25, 17, 32, 25, 19, 32, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, structureboundingbox, 32, 17, 32, 32, 19, 32, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 26, 20, 26, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 27, 21, 27, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.e, 27, 20, 27, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 26, 20, 31, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 27, 21, 30, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.e, 27, 20, 30, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 31, 20, 31, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 30, 21, 30, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.e, 30, 20, 30, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 31, 20, 26, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, 30, 21, 27, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.e, 30, 20, 27, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 28, 21, 27, 29, 21, 27, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 27, 21, 28, 27, 21, 29, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 28, 21, 30, 29, 21, 30, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 30, 21, 28, 30, 21, 29, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + } + + } + + private void d(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox) { + int i; + + if (this.a(structureboundingbox, 0, 21, 6, 58)) { + this.a(generatoraccess, structureboundingbox, 0, 0, 21, 6, 0, 57, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 21, 6, 7, 57); + this.a(generatoraccess, structureboundingbox, 4, 4, 21, 6, 4, 53, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + + for (i = 0; i < 4; ++i) { + this.a(generatoraccess, structureboundingbox, i, i + 1, 21, i, i + 1, 57 - i, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + } + + for (i = 23; i < 53; i += 3) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, 5, 5, i, structureboundingbox); + } + + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, 5, 5, 52, structureboundingbox); + + for (i = 0; i < 4; ++i) { + this.a(generatoraccess, structureboundingbox, i, i + 1, 21, i, i + 1, 57 - i, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + } + + this.a(generatoraccess, structureboundingbox, 4, 1, 52, 6, 3, 52, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 5, 1, 51, 5, 3, 53, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + } + + if (this.a(structureboundingbox, 51, 21, 58, 58)) { + this.a(generatoraccess, structureboundingbox, 51, 0, 21, 57, 0, 57, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 51, 1, 21, 57, 7, 57); + this.a(generatoraccess, structureboundingbox, 51, 4, 21, 53, 4, 53, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + + for (i = 0; i < 4; ++i) { + this.a(generatoraccess, structureboundingbox, 57 - i, i + 1, 21, 57 - i, i + 1, 57 - i, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + } + + for (i = 23; i < 53; i += 3) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, 52, 5, i, structureboundingbox); + } + + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, 52, 5, 52, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 51, 1, 52, 53, 3, 52, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 52, 1, 51, 52, 3, 53, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + } + + if (this.a(structureboundingbox, 0, 51, 57, 57)) { + this.a(generatoraccess, structureboundingbox, 7, 0, 51, 50, 0, 57, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 7, 1, 51, 50, 10, 57); + + for (i = 0; i < 4; ++i) { + this.a(generatoraccess, structureboundingbox, i + 1, i + 1, 57 - i, 56 - i, i + 1, 57 - i, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + } + } + + } + + private void e(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox) { + int i; + + if (this.a(structureboundingbox, 7, 21, 13, 50)) { + this.a(generatoraccess, structureboundingbox, 7, 0, 21, 13, 0, 50, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 7, 1, 21, 13, 10, 50); + this.a(generatoraccess, structureboundingbox, 11, 8, 21, 13, 8, 53, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + + for (i = 0; i < 4; ++i) { + this.a(generatoraccess, structureboundingbox, i + 7, i + 5, 21, i + 7, i + 5, 54, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + } + + for (i = 21; i <= 45; i += 3) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, 12, 9, i, structureboundingbox); + } + } + + if (this.a(structureboundingbox, 44, 21, 50, 54)) { + this.a(generatoraccess, structureboundingbox, 44, 0, 21, 50, 0, 50, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 44, 1, 21, 50, 10, 50); + this.a(generatoraccess, structureboundingbox, 44, 8, 21, 46, 8, 53, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + + for (i = 0; i < 4; ++i) { + this.a(generatoraccess, structureboundingbox, 50 - i, i + 5, 21, 50 - i, i + 5, 54, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + } + + for (i = 21; i <= 45; i += 3) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, 45, 9, i, structureboundingbox); + } + } + + if (this.a(structureboundingbox, 8, 44, 49, 54)) { + this.a(generatoraccess, structureboundingbox, 14, 0, 44, 43, 0, 50, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 14, 1, 44, 43, 10, 50); + + for (i = 12; i <= 45; i += 3) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i, 9, 45, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i, 9, 52, structureboundingbox); + if (i == 12 || i == 18 || i == 24 || i == 33 || i == 39 || i == 45) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i, 9, 47, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i, 9, 50, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i, 10, 45, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i, 10, 46, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i, 10, 51, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i, 10, 52, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i, 11, 47, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i, 11, 50, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i, 12, 48, structureboundingbox); + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i, 12, 49, structureboundingbox); + } + } + + for (i = 0; i < 3; ++i) { + this.a(generatoraccess, structureboundingbox, 8 + i, 5 + i, 54, 49 - i, 5 + i, 54, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + } + + this.a(generatoraccess, structureboundingbox, 11, 8, 54, 46, 8, 54, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + this.a(generatoraccess, structureboundingbox, 14, 8, 44, 43, 8, 53, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + } + + } + + private void f(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox) { + int i; + + if (this.a(structureboundingbox, 14, 21, 20, 43)) { + this.a(generatoraccess, structureboundingbox, 14, 0, 21, 20, 0, 43, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 14, 1, 22, 20, 14, 43); + this.a(generatoraccess, structureboundingbox, 18, 12, 22, 20, 12, 39, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 18, 12, 21, 20, 12, 21, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + + for (i = 0; i < 4; ++i) { + this.a(generatoraccess, structureboundingbox, i + 14, i + 9, 21, i + 14, i + 9, 43 - i, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + } + + for (i = 23; i <= 39; i += 3) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, 19, 13, i, structureboundingbox); + } + } + + if (this.a(structureboundingbox, 37, 21, 43, 43)) { + this.a(generatoraccess, structureboundingbox, 37, 0, 21, 43, 0, 43, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 37, 1, 22, 43, 14, 43); + this.a(generatoraccess, structureboundingbox, 37, 12, 22, 39, 12, 39, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 37, 12, 21, 39, 12, 21, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + + for (i = 0; i < 4; ++i) { + this.a(generatoraccess, structureboundingbox, 43 - i, i + 9, 21, 43 - i, i + 9, 43 - i, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + } + + for (i = 23; i <= 39; i += 3) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, 38, 13, i, structureboundingbox); + } + } + + if (this.a(structureboundingbox, 15, 37, 42, 43)) { + this.a(generatoraccess, structureboundingbox, 21, 0, 37, 36, 0, 43, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + this.a(generatoraccess, structureboundingbox, 21, 1, 37, 36, 14, 43); + this.a(generatoraccess, structureboundingbox, 21, 12, 37, 36, 12, 39, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, WorldGenMonumentPieces.WorldGenMonumentPiece1.a, false); + + for (i = 0; i < 4; ++i) { + this.a(generatoraccess, structureboundingbox, 15 + i, i + 9, 43 - i, 42 - i, i + 9, 43 - i, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, WorldGenMonumentPieces.WorldGenMonumentPiece1.b, false); + } + + for (i = 21; i <= 36; i += 3) { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece1.d, i, 13, 38, structureboundingbox); + } + } + + } + } + + public abstract static class WorldGenMonumentPiece extends StructurePiece { + + protected static final IBlockData a = Blocks.PRISMARINE.getBlockData(); + protected static final IBlockData b = Blocks.PRISMARINE_BRICKS.getBlockData(); + protected static final IBlockData c = Blocks.DARK_PRISMARINE.getBlockData(); + protected static final IBlockData d = WorldGenMonumentPieces.WorldGenMonumentPiece.b; + protected static final IBlockData e = Blocks.SEA_LANTERN.getBlockData(); + protected static final IBlockData f = Blocks.WATER.getBlockData(); + protected static final Set g = ImmutableSet.builder().add(Blocks.ICE).add(Blocks.PACKED_ICE).add(Blocks.BLUE_ICE).add(WorldGenMonumentPieces.WorldGenMonumentPiece.f.getBlock()).build(); // Paper - decompile fix + protected static final int h = b(2, 0, 0); + protected static final int i = b(2, 2, 0); + protected static final int j = b(0, 1, 0); + protected static final int k = b(4, 1, 0); + protected WorldGenMonumentPieces.WorldGenMonumentStateTracker l; + + protected static final int b(int i, int j, int k) { + return j * 25 + k * 5 + i; + } + + public WorldGenMonumentPiece() { + super(0); + } + + public WorldGenMonumentPiece(int i) { + super(i); + } + + public WorldGenMonumentPiece(EnumDirection enumdirection, StructureBoundingBox structureboundingbox) { + super(1); + this.a(enumdirection); + this.n = structureboundingbox; + } + + protected WorldGenMonumentPiece(int i, EnumDirection enumdirection, WorldGenMonumentPieces.WorldGenMonumentStateTracker worldgenmonumentpieces_worldgenmonumentstatetracker, int j, int k, int l) { + super(i); + this.a(enumdirection); + this.l = worldgenmonumentpieces_worldgenmonumentstatetracker; + int i1 = worldgenmonumentpieces_worldgenmonumentstatetracker.a; + int j1 = i1 % 5; + int k1 = i1 / 5 % 5; + int l1 = i1 / 25; + + if (enumdirection != EnumDirection.NORTH && enumdirection != EnumDirection.SOUTH) { + this.n = new StructureBoundingBox(0, 0, 0, l * 8 - 1, k * 4 - 1, j * 8 - 1); + } else { + this.n = new StructureBoundingBox(0, 0, 0, j * 8 - 1, k * 4 - 1, l * 8 - 1); + } + + switch (enumdirection) { + case NORTH: + this.n.a(j1 * 8, l1 * 4, -(k1 + l) * 8 + 1); + break; + case SOUTH: + this.n.a(j1 * 8, l1 * 4, k1 * 8); + break; + case WEST: + this.n.a(-(k1 + l) * 8 + 1, l1 * 4, j1 * 8); + break; + default: + this.n.a(k1 * 8, l1 * 4, j1 * 8); + } + + } + + protected void a(NBTTagCompound nbttagcompound) {} + + protected void a(NBTTagCompound nbttagcompound, DefinedStructureManager definedstructuremanager) {} + + protected void a(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox, int i, int j, int k, int l, int i1, int j1) { + for (int k1 = j; k1 <= i1; ++k1) { + for (int l1 = i; l1 <= l; ++l1) { + for (int i2 = k; i2 <= j1; ++i2) { + IBlockData iblockdata = this.a((IBlockAccess) generatoraccess, l1, k1, i2, structureboundingbox); + + if (!WorldGenMonumentPieces.WorldGenMonumentPiece.g.contains(iblockdata.getBlock())) { + if (this.d(k1) >= generatoraccess.getSeaLevel() && iblockdata != WorldGenMonumentPieces.WorldGenMonumentPiece.f) { + this.a(generatoraccess, Blocks.AIR.getBlockData(), l1, k1, i2, structureboundingbox); + } else { + this.a(generatoraccess, WorldGenMonumentPieces.WorldGenMonumentPiece.f, l1, k1, i2, structureboundingbox); + } + } + } + } + } + + } + + protected void a(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox, int i, int j, boolean flag) { + if (flag) { + this.a(generatoraccess, structureboundingbox, i + 0, 0, j + 0, i + 2, 0, j + 8 - 1, WorldGenMonumentPieces.WorldGenMonumentPiece.a, WorldGenMonumentPieces.WorldGenMonumentPiece.a, false); + this.a(generatoraccess, structureboundingbox, i + 5, 0, j + 0, i + 8 - 1, 0, j + 8 - 1, WorldGenMonumentPieces.WorldGenMonumentPiece.a, WorldGenMonumentPieces.WorldGenMonumentPiece.a, false); + this.a(generatoraccess, structureboundingbox, i + 3, 0, j + 0, i + 4, 0, j + 2, WorldGenMonumentPieces.WorldGenMonumentPiece.a, WorldGenMonumentPieces.WorldGenMonumentPiece.a, false); + this.a(generatoraccess, structureboundingbox, i + 3, 0, j + 5, i + 4, 0, j + 8 - 1, WorldGenMonumentPieces.WorldGenMonumentPiece.a, WorldGenMonumentPieces.WorldGenMonumentPiece.a, false); + this.a(generatoraccess, structureboundingbox, i + 3, 0, j + 2, i + 4, 0, j + 2, WorldGenMonumentPieces.WorldGenMonumentPiece.b, WorldGenMonumentPieces.WorldGenMonumentPiece.b, false); + this.a(generatoraccess, structureboundingbox, i + 3, 0, j + 5, i + 4, 0, j + 5, WorldGenMonumentPieces.WorldGenMonumentPiece.b, WorldGenMonumentPieces.WorldGenMonumentPiece.b, false); + this.a(generatoraccess, structureboundingbox, i + 2, 0, j + 3, i + 2, 0, j + 4, WorldGenMonumentPieces.WorldGenMonumentPiece.b, WorldGenMonumentPieces.WorldGenMonumentPiece.b, false); + this.a(generatoraccess, structureboundingbox, i + 5, 0, j + 3, i + 5, 0, j + 4, WorldGenMonumentPieces.WorldGenMonumentPiece.b, WorldGenMonumentPieces.WorldGenMonumentPiece.b, false); + } else { + this.a(generatoraccess, structureboundingbox, i + 0, 0, j + 0, i + 8 - 1, 0, j + 8 - 1, WorldGenMonumentPieces.WorldGenMonumentPiece.a, WorldGenMonumentPieces.WorldGenMonumentPiece.a, false); + } + + } + + protected void a(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox, int i, int j, int k, int l, int i1, int j1, IBlockData iblockdata) { + for (int k1 = j; k1 <= i1; ++k1) { + for (int l1 = i; l1 <= l; ++l1) { + for (int i2 = k; i2 <= j1; ++i2) { + if (this.a((IBlockAccess) generatoraccess, l1, k1, i2, structureboundingbox) == WorldGenMonumentPieces.WorldGenMonumentPiece.f) { + this.a(generatoraccess, iblockdata, l1, k1, i2, structureboundingbox); + } + } + } + } + + } + + protected boolean a(StructureBoundingBox structureboundingbox, int i, int j, int k, int l) { + int i1 = this.a(i, j); + int j1 = this.b(i, j); + int k1 = this.a(k, l); + int l1 = this.b(k, l); + + return structureboundingbox.a(Math.min(i1, k1), Math.min(j1, l1), Math.max(i1, k1), Math.max(j1, l1)); + } + + protected boolean a(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox, int i, int j, int k) { + int l = this.a(i, k); + int i1 = this.d(j); + int j1 = this.b(i, k); + + if (structureboundingbox.b((BaseBlockPosition) (new BlockPosition(l, i1, j1)))) { + EntityGuardianElder entityguardianelder = EntityTypes.ELDER_GUARDIAN.create(generatoraccess.getMinecraftWorld()); // Paper + + entityguardianelder.heal(entityguardianelder.getMaxHealth()); + entityguardianelder.setPositionRotation((double) l + 0.5D, (double) i1, (double) j1 + 0.5D, 0.0F, 0.0F); + entityguardianelder.prepare(generatoraccess.getDamageScaler(new BlockPosition(entityguardianelder)), (GroupDataEntity) null, (NBTTagCompound) null); + generatoraccess.addEntity(entityguardianelder); + return true; + } else { + return false; + } + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldGenStronghold.java b/src/main/java/net/minecraft/server/WorldGenStronghold.java new file mode 100644 index 000000000000..d0eaa9e9f8d8 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenStronghold.java @@ -0,0 +1,221 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.objects.ObjectIterator; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import javax.annotation.Nullable; + +public class WorldGenStronghold extends StructureGenerator { + + // Paper start - no shared state + //private boolean b; + //private ChunkCoordIntPair[] c; + //private long d; + // Paper end + + public WorldGenStronghold() {} + + protected boolean a(ChunkGenerator chunkgenerator, Random random, int i, int j) { + // Paper start + /*if (this.d != chunkgenerator.getSeed()) { + this.c(); + }*/ + + synchronized (chunkgenerator.getWorld().strongholdInit) { + if (chunkgenerator.getWorld().strongholdInit.compareAndSet(false, true)) { // Paper + this.a(chunkgenerator); + //this.b = true; + }} // Paper + // Paper end + + ChunkCoordIntPair[] achunkcoordintpair = chunkgenerator.getWorld().strongholdCoords; // Paper + int k = achunkcoordintpair.length; + + for (int l = 0; l < k; ++l) { + ChunkCoordIntPair chunkcoordintpair = achunkcoordintpair[l]; + + if (i == chunkcoordintpair.x && j == chunkcoordintpair.z) { + return true; + } + } + + return false; + } + + private void c() { + //this.b = false; // Paper + //this.c = null; // Paper + } + + protected boolean a(GeneratorAccess generatoraccess) { + return generatoraccess.getWorldData().shouldGenerateMapFeatures(); + } + + protected StructureStart a(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, SeededRandom seededrandom, int i, int j) { + BiomeBase biomebase = chunkgenerator.getWorldChunkManager().getBiome(new BlockPosition((i << 4) + 9, 0, (j << 4) + 9), Biomes.b); + byte b0 = 0; + int k = b0 + 1; + + WorldGenStronghold.a worldgenstronghold_a; + + for (worldgenstronghold_a = new WorldGenStronghold.a(generatoraccess, seededrandom, i, j, biomebase, b0); worldgenstronghold_a.d().isEmpty() || ((WorldGenStrongholdPieces.WorldGenStrongholdStart) worldgenstronghold_a.d().get(0)).b == null; worldgenstronghold_a = new WorldGenStronghold.a(generatoraccess, seededrandom, i, j, biomebase, k++)) { + ; + } + + return worldgenstronghold_a; + } + + protected String a() { + return "Stronghold"; + } + + public int b() { + return 8; + } + + @Nullable + public BlockPosition getNearestGeneratedFeature(World world, ChunkGenerator chunkgenerator, BlockPosition blockposition, int i, boolean flag) { + if (!chunkgenerator.getWorldChunkManager().a(this)) { + return null; + } else { + // Paper start + /*if (this.d != chunkgenerator.getSeed()) { + this.c(); + }*/ + + synchronized (chunkgenerator.getWorld().strongholdInit) { + if (chunkgenerator.getWorld().strongholdInit.compareAndSet(false, true)) { // Paper + this.a(chunkgenerator); + //this.b = true; + }} // Paper + // Paper end + + BlockPosition blockposition1 = null; + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(0, 0, 0); + double d0 = Double.MAX_VALUE; + // Paper start + /* + ChunkCoordIntPair[] achunkcoordintpair = this.c; + int j = achunkcoordintpair.length; + + for (int k = 0; k < j; ++k) { + */ + for (ChunkCoordIntPair chunkcoordintpair : world.strongholdCoords) { + // Paper end + + blockposition_mutableblockposition.c((chunkcoordintpair.x << 4) + 8, 32, (chunkcoordintpair.z << 4) + 8); + double d1 = blockposition_mutableblockposition.n(blockposition); + + if (blockposition1 == null) { + blockposition1 = new BlockPosition(blockposition_mutableblockposition); + d0 = d1; + } else if (d1 < d0) { + blockposition1 = new BlockPosition(blockposition_mutableblockposition); + d0 = d1; + } + } + + return blockposition1; + } + } + + private void a(ChunkGenerator chunkgenerator) { + //this.d = chunkgenerator.getSeed(); // Paper + List list = Lists.newArrayList(); + Iterator iterator = IRegistry.BIOME.iterator(); + + while (iterator.hasNext()) { + BiomeBase biomebase = (BiomeBase) iterator.next(); + + if (biomebase != null && chunkgenerator.canSpawnStructure(biomebase, WorldGenerator.m)) { + list.add(biomebase); + } + } + + int i = chunkgenerator.getSettings().e(); + int j = chunkgenerator.getSettings().f(); + int k = chunkgenerator.getSettings().g(); + + ChunkCoordIntPair[] strongholdCoords = chunkgenerator.getWorld().strongholdCoords = new ChunkCoordIntPair[j]; // Paper + int l = 0; + Long2ObjectMap long2objectmap = chunkgenerator.getStructureStartCache(this); + + synchronized (long2objectmap) { + ObjectIterator objectiterator = long2objectmap.values().iterator(); + + while (objectiterator.hasNext()) { + StructureStart structurestart = (StructureStart) objectiterator.next(); + + if (l < strongholdCoords.length) { // Paper + strongholdCoords[l++] = new ChunkCoordIntPair(structurestart.e(), structurestart.f()); // Paper + } + } + } + + Random random = new Random(); + + random.setSeed(chunkgenerator.getSeed()); + double d0 = random.nextDouble() * 3.141592653589793D * 2.0D; + int i1 = long2objectmap.size(); + + if (i1 < strongholdCoords.length) { // Paper + int j1 = 0; + int k1 = 0; + + for (int l1 = 0; l1 < strongholdCoords.length; ++l1) { // Paper + double d1 = (double) (4 * i + i * k1 * 6) + (random.nextDouble() - 0.5D) * (double) i * 2.5D; + int i2 = (int) Math.round(Math.cos(d0) * d1); + int j2 = (int) Math.round(Math.sin(d0) * d1); + BlockPosition blockposition = chunkgenerator.getWorldChunkManager().a((i2 << 4) + 8, (j2 << 4) + 8, 112, list, random); + + if (blockposition != null) { + i2 = blockposition.getX() >> 4; + j2 = blockposition.getZ() >> 4; + } + + if (l1 >= i1) { + strongholdCoords[l1] = new ChunkCoordIntPair(i2, j2); // Paper + } + + d0 += 6.283185307179586D / (double) k; + ++j1; + if (j1 == k) { + ++k1; + j1 = 0; + k += 2 * k / (k1 + 1); + k = Math.min(k, strongholdCoords.length - l1); // Paper + d0 += random.nextDouble() * 3.141592653589793D * 2.0D; + } + } + } + + } + + public static class a extends StructureStart { + + public a() {} + + public a(GeneratorAccess generatoraccess, SeededRandom seededrandom, int i, int j, BiomeBase biomebase, int k) { + super(i, j, biomebase, seededrandom, generatoraccess.getSeed() + (long) k); + WorldGenStrongholdPieces.b(); + WorldGenStrongholdPieces.WorldGenStrongholdStart worldgenstrongholdpieces_worldgenstrongholdstart = new WorldGenStrongholdPieces.WorldGenStrongholdStart(0, seededrandom, (i << 4) + 2, (j << 4) + 2); + + this.a.add(worldgenstrongholdpieces_worldgenstrongholdstart); + worldgenstrongholdpieces_worldgenstrongholdstart.a((StructurePiece) worldgenstrongholdpieces_worldgenstrongholdstart, this.a, (Random) seededrandom); + List list = worldgenstrongholdpieces_worldgenstrongholdstart.c; + + while (!list.isEmpty()) { + int l = seededrandom.nextInt(list.size()); + StructurePiece structurepiece = (StructurePiece) list.remove(l); + + structurepiece.a((StructurePiece) worldgenstrongholdpieces_worldgenstrongholdstart, this.a, (Random) seededrandom); + } + + this.a((IBlockAccess) generatoraccess); + this.a(generatoraccess, seededrandom, 10); + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldGenTreeProvider.java b/src/main/java/net/minecraft/server/WorldGenTreeProvider.java new file mode 100644 index 000000000000..b410d80f6800 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenTreeProvider.java @@ -0,0 +1,56 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; +import org.bukkit.TreeType; // CraftBukkit + +public abstract class WorldGenTreeProvider { + + public WorldGenTreeProvider() {} + + @Nullable + protected abstract WorldGenTreeAbstract b(Random random); + + public boolean a(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, Random random) { + WorldGenTreeAbstract worldgentreeabstract = this.b(random); + + if (worldgentreeabstract == null) { + return false; + } else { + setTreeType(worldgentreeabstract); // CraftBukkit + generatoraccess.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 4); + if (worldgentreeabstract.generate(generatoraccess, generatoraccess.getChunkProvider().getChunkGenerator(), random, blockposition, WorldGenFeatureConfiguration.e)) { + return true; + } else { + generatoraccess.setTypeAndData(blockposition, iblockdata, 4); + return false; + } + } + } + + // CraftBukkit start + protected void setTreeType(WorldGenTreeAbstract worldgentreeabstract) { + if (worldgentreeabstract instanceof WorldGenAcaciaTree) { + BlockSapling.treeType = TreeType.ACACIA; + } else if (worldgentreeabstract instanceof WorldGenBigTree) { + BlockSapling.treeType = TreeType.BIG_TREE; + } else if (worldgentreeabstract instanceof WorldGenForest) { + BlockSapling.treeType = TreeType.BIRCH; + } else if (worldgentreeabstract instanceof WorldGenForestTree) { + BlockSapling.treeType = TreeType.DARK_OAK; + } else if (worldgentreeabstract instanceof WorldGenJungleTree) { + BlockSapling.treeType = TreeType.JUNGLE; + } else if (worldgentreeabstract instanceof WorldGenMegaTree) { + BlockSapling.treeType = TreeType.MEGA_REDWOOD; + } else if (worldgentreeabstract instanceof WorldGenTaiga1) { + BlockSapling.treeType = TreeType.REDWOOD; + } else if (worldgentreeabstract instanceof WorldGenTaiga2) { + BlockSapling.treeType = TreeType.REDWOOD; + } else if (worldgentreeabstract instanceof WorldGenTrees) { + BlockSapling.treeType = TreeType.TREE; + } else { + throw new IllegalArgumentException("Unknown tree generator " + worldgentreeabstract); + } + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/WorldGenVillage.java b/src/main/java/net/minecraft/server/WorldGenVillage.java new file mode 100644 index 000000000000..ca41a90cf680 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenVillage.java @@ -0,0 +1,121 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import java.util.Random; + +public class WorldGenVillage extends StructureGenerator { + + public WorldGenVillage() {} + + public String a() { + return "Village"; + } + + public int b() { + return 8; + } + + protected boolean a(GeneratorAccess generatoraccess) { + return generatoraccess.getWorldData().shouldGenerateMapFeatures(); + } + + protected ChunkCoordIntPair a(ChunkGenerator chunkgenerator, Random random, int i, int j, int k, int l) { + int i1 = chunkgenerator.getSettings().a(); + int j1 = chunkgenerator.getSettings().b(); + int k1 = i + i1 * k; + int l1 = j + i1 * l; + int i2 = k1 < 0 ? k1 - i1 + 1 : k1; + int j2 = l1 < 0 ? l1 - i1 + 1 : l1; + int k2 = i2 / i1; + int l2 = j2 / i1; + + ((SeededRandom) random).a(chunkgenerator.getSeed(), k2, l2, chunkgenerator.getWorld().spigotConfig.villageSeed); // Spigot + k2 *= i1; + l2 *= i1; + k2 += random.nextInt(i1 - j1); + l2 += random.nextInt(i1 - j1); + return new ChunkCoordIntPair(k2, l2); + } + + protected boolean a(ChunkGenerator chunkgenerator, Random random, int i, int j) { + ChunkCoordIntPair chunkcoordintpair = this.a(chunkgenerator, random, i, j, 0, 0); + + if (i == chunkcoordintpair.x && j == chunkcoordintpair.z) { + BiomeBase biomebase = chunkgenerator.getWorldChunkManager().getBiome(new BlockPosition((i << 4) + 9, 0, (j << 4) + 9), Biomes.b); + + return chunkgenerator.canSpawnStructure(biomebase, WorldGenerator.e); + } else { + return false; + } + } + + protected StructureStart a(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, SeededRandom seededrandom, int i, int j) { + BiomeBase biomebase = chunkgenerator.getWorldChunkManager().getBiome(new BlockPosition((i << 4) + 9, 0, (j << 4) + 9), Biomes.b); + + return new WorldGenVillage.a(generatoraccess, chunkgenerator, seededrandom, i, j, biomebase); + } + + public static class a extends StructureStart { + + private boolean e; + + public a() {} + + public a(GeneratorAccess generatoraccess, ChunkGenerator chunkgenerator, SeededRandom seededrandom, int i, int j, BiomeBase biomebase) { + super(i, j, biomebase, seededrandom, generatoraccess.getSeed()); + WorldGenFeatureVillageConfiguration worldgenfeaturevillageconfiguration = (WorldGenFeatureVillageConfiguration) chunkgenerator.getFeatureConfiguration(biomebase, WorldGenerator.e); + List list = WorldGenVillagePieces.a(seededrandom, worldgenfeaturevillageconfiguration.a); + WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece = new WorldGenVillagePieces.WorldGenVillageStartPiece(0, seededrandom, (i << 4) + 2, (j << 4) + 2, list, worldgenfeaturevillageconfiguration); + + this.a.add(worldgenvillagepieces_worldgenvillagestartpiece); + worldgenvillagepieces_worldgenvillagestartpiece.a((StructurePiece) worldgenvillagepieces_worldgenvillagestartpiece, this.a, (Random) seededrandom); + List list1 = worldgenvillagepieces_worldgenvillagestartpiece.e; + List list2 = worldgenvillagepieces_worldgenvillagestartpiece.d; + + int k; + + while (!list1.isEmpty() || !list2.isEmpty()) { + StructurePiece structurepiece; + + if (list1.isEmpty()) { + k = seededrandom.nextInt(list2.size()); + structurepiece = (StructurePiece) list2.remove(k); + structurepiece.a((StructurePiece) worldgenvillagepieces_worldgenvillagestartpiece, this.a, (Random) seededrandom); + } else { + k = seededrandom.nextInt(list1.size()); + structurepiece = (StructurePiece) list1.remove(k); + structurepiece.a((StructurePiece) worldgenvillagepieces_worldgenvillagestartpiece, this.a, (Random) seededrandom); + } + } + + this.a((IBlockAccess) generatoraccess); + k = 0; + Iterator iterator = this.a.iterator(); + + while (iterator.hasNext()) { + StructurePiece structurepiece1 = (StructurePiece) iterator.next(); + + if (!(structurepiece1 instanceof WorldGenVillagePieces.WorldGenVillageRoadPiece)) { + ++k; + } + } + + this.e = k > 2; + } + + public boolean b() { + return this.e; + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + nbttagcompound.setBoolean("Valid", this.e); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + this.e = nbttagcompound.getBoolean("Valid"); + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldGenVillagePieces.java b/src/main/java/net/minecraft/server/WorldGenVillagePieces.java new file mode 100644 index 000000000000..967e33b3d788 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenVillagePieces.java @@ -0,0 +1,1814 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import javax.annotation.Nullable; + +public class WorldGenVillagePieces { + + public static void a() { + WorldGenFactory.a(WorldGenVillagePieces.WorldGenVillageLibrary.class, "ViBH"); + WorldGenFactory.a(WorldGenVillagePieces.WorldGenVillageFarm2.class, "ViDF"); + WorldGenFactory.a(WorldGenVillagePieces.WorldGenVillageFarm.class, "ViF"); + WorldGenFactory.a(WorldGenVillagePieces.WorldGenVillageLight.class, "ViL"); + WorldGenFactory.a(WorldGenVillagePieces.WorldGenVillageButcher.class, "ViPH"); + WorldGenFactory.a(WorldGenVillagePieces.WorldGenVillageHouse.class, "ViSH"); + WorldGenFactory.a(WorldGenVillagePieces.WorldGenVillageHut.class, "ViSmH"); + WorldGenFactory.a(WorldGenVillagePieces.WorldGenVillageTemple.class, "ViST"); + WorldGenFactory.a(WorldGenVillagePieces.WorldGenVillageBlacksmith.class, "ViS"); + WorldGenFactory.a(WorldGenVillagePieces.WorldGenVillageStartPiece.class, "ViStart"); + WorldGenFactory.a(WorldGenVillagePieces.WorldGenVillageRoad.class, "ViSR"); + WorldGenFactory.a(WorldGenVillagePieces.WorldGenVillageHouse2.class, "ViTRH"); + WorldGenFactory.a(WorldGenVillagePieces.WorldGenVillageWell.class, "ViW"); + } + + public static List a(Random random, int i) { + List list = Lists.newArrayList(); + + list.add(new WorldGenVillagePieces.WorldGenVillagePieceWeight(WorldGenVillagePieces.WorldGenVillageHouse.class, 4, MathHelper.nextInt(random, 2 + i, 4 + i * 2))); + list.add(new WorldGenVillagePieces.WorldGenVillagePieceWeight(WorldGenVillagePieces.WorldGenVillageTemple.class, 20, MathHelper.nextInt(random, 0 + i, 1 + i))); + list.add(new WorldGenVillagePieces.WorldGenVillagePieceWeight(WorldGenVillagePieces.WorldGenVillageLibrary.class, 20, MathHelper.nextInt(random, 0 + i, 2 + i))); + list.add(new WorldGenVillagePieces.WorldGenVillagePieceWeight(WorldGenVillagePieces.WorldGenVillageHut.class, 3, MathHelper.nextInt(random, 2 + i, 5 + i * 3))); + list.add(new WorldGenVillagePieces.WorldGenVillagePieceWeight(WorldGenVillagePieces.WorldGenVillageButcher.class, 15, MathHelper.nextInt(random, 0 + i, 2 + i))); + list.add(new WorldGenVillagePieces.WorldGenVillagePieceWeight(WorldGenVillagePieces.WorldGenVillageFarm2.class, 3, MathHelper.nextInt(random, 1 + i, 4 + i))); + list.add(new WorldGenVillagePieces.WorldGenVillagePieceWeight(WorldGenVillagePieces.WorldGenVillageFarm.class, 3, MathHelper.nextInt(random, 2 + i, 4 + i * 2))); + list.add(new WorldGenVillagePieces.WorldGenVillagePieceWeight(WorldGenVillagePieces.WorldGenVillageBlacksmith.class, 15, MathHelper.nextInt(random, 0, 1 + i))); + list.add(new WorldGenVillagePieces.WorldGenVillagePieceWeight(WorldGenVillagePieces.WorldGenVillageHouse2.class, 8, MathHelper.nextInt(random, 0 + i, 3 + i * 2))); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + if (((WorldGenVillagePieces.WorldGenVillagePieceWeight) iterator.next()).d == 0) { + iterator.remove(); + } + } + + return list; + } + + private static int a(List list) { + boolean flag = false; + int i = 0; + + WorldGenVillagePieces.WorldGenVillagePieceWeight worldgenvillagepieces_worldgenvillagepieceweight; + + for (Iterator iterator = list.iterator(); iterator.hasNext(); i += worldgenvillagepieces_worldgenvillagepieceweight.b) { + worldgenvillagepieces_worldgenvillagepieceweight = (WorldGenVillagePieces.WorldGenVillagePieceWeight) iterator.next(); + if (worldgenvillagepieces_worldgenvillagepieceweight.d > 0 && worldgenvillagepieces_worldgenvillagepieceweight.c < worldgenvillagepieces_worldgenvillagepieceweight.d) { + flag = true; + } + } + + return flag ? i : -1; + } + + private static WorldGenVillagePieces.WorldGenVillagePiece a(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, WorldGenVillagePieces.WorldGenVillagePieceWeight worldgenvillagepieces_worldgenvillagepieceweight, List list, Random random, int i, int j, int k, EnumDirection enumdirection, int l) { + Class oclass = worldgenvillagepieces_worldgenvillagepieceweight.a; + Object object = null; + + if (oclass == WorldGenVillagePieces.WorldGenVillageHouse.class) { + object = WorldGenVillagePieces.WorldGenVillageHouse.a(worldgenvillagepieces_worldgenvillagestartpiece, list, random, i, j, k, enumdirection, l); + } else if (oclass == WorldGenVillagePieces.WorldGenVillageTemple.class) { + object = WorldGenVillagePieces.WorldGenVillageTemple.a(worldgenvillagepieces_worldgenvillagestartpiece, list, random, i, j, k, enumdirection, l); + } else if (oclass == WorldGenVillagePieces.WorldGenVillageLibrary.class) { + object = WorldGenVillagePieces.WorldGenVillageLibrary.a(worldgenvillagepieces_worldgenvillagestartpiece, list, random, i, j, k, enumdirection, l); + } else if (oclass == WorldGenVillagePieces.WorldGenVillageHut.class) { + object = WorldGenVillagePieces.WorldGenVillageHut.a(worldgenvillagepieces_worldgenvillagestartpiece, list, random, i, j, k, enumdirection, l); + } else if (oclass == WorldGenVillagePieces.WorldGenVillageButcher.class) { + object = WorldGenVillagePieces.WorldGenVillageButcher.a(worldgenvillagepieces_worldgenvillagestartpiece, list, random, i, j, k, enumdirection, l); + } else if (oclass == WorldGenVillagePieces.WorldGenVillageFarm2.class) { + object = WorldGenVillagePieces.WorldGenVillageFarm2.a(worldgenvillagepieces_worldgenvillagestartpiece, list, random, i, j, k, enumdirection, l); + } else if (oclass == WorldGenVillagePieces.WorldGenVillageFarm.class) { + object = WorldGenVillagePieces.WorldGenVillageFarm.a(worldgenvillagepieces_worldgenvillagestartpiece, list, random, i, j, k, enumdirection, l); + } else if (oclass == WorldGenVillagePieces.WorldGenVillageBlacksmith.class) { + object = WorldGenVillagePieces.WorldGenVillageBlacksmith.a(worldgenvillagepieces_worldgenvillagestartpiece, list, random, i, j, k, enumdirection, l); + } else if (oclass == WorldGenVillagePieces.WorldGenVillageHouse2.class) { + object = WorldGenVillagePieces.WorldGenVillageHouse2.a(worldgenvillagepieces_worldgenvillagestartpiece, list, random, i, j, k, enumdirection, l); + } + + return (WorldGenVillagePieces.WorldGenVillagePiece) object; + } + + private static WorldGenVillagePieces.WorldGenVillagePiece c(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, List list, Random random, int i, int j, int k, EnumDirection enumdirection, int l) { + int i1 = a(worldgenvillagepieces_worldgenvillagestartpiece.c); + + if (i1 <= 0) { + return null; + } else { + int j1 = 0; + + while (j1 < 5) { + ++j1; + int k1 = random.nextInt(i1); + Iterator iterator = worldgenvillagepieces_worldgenvillagestartpiece.c.iterator(); + + while (iterator.hasNext()) { + WorldGenVillagePieces.WorldGenVillagePieceWeight worldgenvillagepieces_worldgenvillagepieceweight = (WorldGenVillagePieces.WorldGenVillagePieceWeight) iterator.next(); + + k1 -= worldgenvillagepieces_worldgenvillagepieceweight.b; + if (k1 < 0) { + if (!worldgenvillagepieces_worldgenvillagepieceweight.a(l) || worldgenvillagepieces_worldgenvillagepieceweight == worldgenvillagepieces_worldgenvillagestartpiece.b && worldgenvillagepieces_worldgenvillagestartpiece.c.size() > 1) { + break; + } + + WorldGenVillagePieces.WorldGenVillagePiece worldgenvillagepieces_worldgenvillagepiece = a(worldgenvillagepieces_worldgenvillagestartpiece, worldgenvillagepieces_worldgenvillagepieceweight, list, random, i, j, k, enumdirection, l); + + if (worldgenvillagepieces_worldgenvillagepiece != null) { + ++worldgenvillagepieces_worldgenvillagepieceweight.c; + worldgenvillagepieces_worldgenvillagestartpiece.b = worldgenvillagepieces_worldgenvillagepieceweight; + if (!worldgenvillagepieces_worldgenvillagepieceweight.a()) { + worldgenvillagepieces_worldgenvillagestartpiece.c.remove(worldgenvillagepieces_worldgenvillagepieceweight); + } + + return worldgenvillagepieces_worldgenvillagepiece; + } + } + } + } + + StructureBoundingBox structureboundingbox = WorldGenVillagePieces.WorldGenVillageLight.a(worldgenvillagepieces_worldgenvillagestartpiece, list, random, i, j, k, enumdirection); + + if (structureboundingbox != null) { + return new WorldGenVillagePieces.WorldGenVillageLight(worldgenvillagepieces_worldgenvillagestartpiece, l, random, structureboundingbox, enumdirection); + } else { + return null; + } + } + } + + private static StructurePiece d(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, List list, Random random, int i, int j, int k, EnumDirection enumdirection, int l) { + if (l > 50) { + return null; + } else if (Math.abs(i - worldgenvillagepieces_worldgenvillagestartpiece.d().a) <= 112 && Math.abs(k - worldgenvillagepieces_worldgenvillagestartpiece.d().c) <= 112) { + WorldGenVillagePieces.WorldGenVillagePiece worldgenvillagepieces_worldgenvillagepiece = c(worldgenvillagepieces_worldgenvillagestartpiece, list, random, i, j, k, enumdirection, l + 1); + + if (worldgenvillagepieces_worldgenvillagepiece != null) { + list.add(worldgenvillagepieces_worldgenvillagepiece); + worldgenvillagepieces_worldgenvillagestartpiece.d.add(worldgenvillagepieces_worldgenvillagepiece); + return worldgenvillagepieces_worldgenvillagepiece; + } else { + return null; + } + } else { + return null; + } + } + + private static StructurePiece e(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, List list, Random random, int i, int j, int k, EnumDirection enumdirection, int l) { + if (l > 3 + worldgenvillagepieces_worldgenvillagestartpiece.a) { + return null; + } else if (Math.abs(i - worldgenvillagepieces_worldgenvillagestartpiece.d().a) <= 112 && Math.abs(k - worldgenvillagepieces_worldgenvillagestartpiece.d().c) <= 112) { + StructureBoundingBox structureboundingbox = WorldGenVillagePieces.WorldGenVillageRoad.a(worldgenvillagepieces_worldgenvillagestartpiece, list, random, i, j, k, enumdirection); + + if (structureboundingbox != null && structureboundingbox.b > 10) { + WorldGenVillagePieces.WorldGenVillageRoad worldgenvillagepieces_worldgenvillageroad = new WorldGenVillagePieces.WorldGenVillageRoad(worldgenvillagepieces_worldgenvillagestartpiece, l, random, structureboundingbox, enumdirection); + + list.add(worldgenvillagepieces_worldgenvillageroad); + worldgenvillagepieces_worldgenvillagestartpiece.e.add(worldgenvillagepieces_worldgenvillageroad); + return worldgenvillagepieces_worldgenvillageroad; + } else { + return null; + } + } else { + return null; + } + } + + public static class WorldGenVillageLight extends WorldGenVillagePieces.WorldGenVillagePiece { + + public WorldGenVillageLight() {} + + public WorldGenVillageLight(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, int i, Random random, StructureBoundingBox structureboundingbox, EnumDirection enumdirection) { + super(worldgenvillagepieces_worldgenvillagestartpiece, i); + this.a(enumdirection); + this.n = structureboundingbox; + } + + public static StructureBoundingBox a(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, List list, Random random, int i, int j, int k, EnumDirection enumdirection) { + StructureBoundingBox structureboundingbox = StructureBoundingBox.a(i, j, k, 0, 0, 0, 3, 4, 2, enumdirection); + + return StructurePiece.a(list, structureboundingbox) != null ? null : structureboundingbox; + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (this.f < 0) { + this.f = this.a(generatoraccess, structureboundingbox); + if (this.f < 0) { + return true; + } + + this.n.a(0, this.f - this.n.e + 4 - 1, 0); + } + + IBlockData iblockdata = this.a(Blocks.OAK_FENCE.getBlockData()); + + this.a(generatoraccess, structureboundingbox, 0, 0, 0, 2, 3, 1, Blocks.AIR.getBlockData(), Blocks.AIR.getBlockData(), false); + this.a(generatoraccess, iblockdata, 1, 0, 0, structureboundingbox); + this.a(generatoraccess, iblockdata, 1, 1, 0, structureboundingbox); + this.a(generatoraccess, iblockdata, 1, 2, 0, structureboundingbox); + this.a(generatoraccess, Blocks.BLACK_WOOL.getBlockData(), 1, 3, 0, structureboundingbox); + this.a(generatoraccess, EnumDirection.EAST, 2, 3, 0, structureboundingbox); + this.a(generatoraccess, EnumDirection.NORTH, 1, 3, 1, structureboundingbox); + this.a(generatoraccess, EnumDirection.WEST, 0, 3, 0, structureboundingbox); + this.a(generatoraccess, EnumDirection.SOUTH, 1, 3, -1, structureboundingbox); + return true; + } + } + + public static class WorldGenVillageFarm2 extends WorldGenVillagePieces.WorldGenVillagePiece { + + private IBlockData a; + private IBlockData b; + private IBlockData c; + private IBlockData d; + + public WorldGenVillageFarm2() {} + + public WorldGenVillageFarm2(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, int i, Random random, StructureBoundingBox structureboundingbox, EnumDirection enumdirection) { + super(worldgenvillagepieces_worldgenvillagestartpiece, i); + this.a(enumdirection); + this.n = structureboundingbox; + this.a = WorldGenVillagePieces.WorldGenVillageFarm.b(random); + this.b = WorldGenVillagePieces.WorldGenVillageFarm.b(random); + this.c = WorldGenVillagePieces.WorldGenVillageFarm.b(random); + this.d = WorldGenVillagePieces.WorldGenVillageFarm.b(random); + } + + protected void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + nbttagcompound.set("CA", GameProfileSerializer.a(this.a)); + nbttagcompound.set("CB", GameProfileSerializer.a(this.b)); + nbttagcompound.set("CC", GameProfileSerializer.a(this.c)); + nbttagcompound.set("CD", GameProfileSerializer.a(this.d)); + } + + protected void a(NBTTagCompound nbttagcompound, DefinedStructureManager definedstructuremanager) { + super.a(nbttagcompound, definedstructuremanager); + this.a = GameProfileSerializer.d(nbttagcompound.getCompound("CA")); + this.b = GameProfileSerializer.d(nbttagcompound.getCompound("CB")); + this.c = GameProfileSerializer.d(nbttagcompound.getCompound("CC")); + this.d = GameProfileSerializer.d(nbttagcompound.getCompound("CD")); + if (!(this.a.getBlock() instanceof BlockCrops)) { + this.a = Blocks.WHEAT.getBlockData(); + } + + if (!(this.b.getBlock() instanceof BlockCrops)) { + this.b = Blocks.CARROTS.getBlockData(); + } + + if (!(this.c.getBlock() instanceof BlockCrops)) { + this.c = Blocks.POTATOES.getBlockData(); + } + + if (!(this.d.getBlock() instanceof BlockCrops)) { + this.d = Blocks.BEETROOTS.getBlockData(); + } + + } + + public static WorldGenVillagePieces.WorldGenVillageFarm2 a(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, List list, Random random, int i, int j, int k, EnumDirection enumdirection, int l) { + StructureBoundingBox structureboundingbox = StructureBoundingBox.a(i, j, k, 0, 0, 0, 13, 4, 9, enumdirection); + + return a(structureboundingbox) && StructurePiece.a(list, structureboundingbox) == null ? new WorldGenVillagePieces.WorldGenVillageFarm2(worldgenvillagepieces_worldgenvillagestartpiece, l, random, structureboundingbox, enumdirection) : null; + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (this.f < 0) { + this.f = this.a(generatoraccess, structureboundingbox); + if (this.f < 0) { + return true; + } + + this.n.a(0, this.f - this.n.e + 4 - 1, 0); + } + + IBlockData iblockdata = this.a(Blocks.OAK_LOG.getBlockData()); + + this.a(generatoraccess, structureboundingbox, 0, 1, 0, 12, 4, 8, Blocks.AIR.getBlockData(), Blocks.AIR.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 1, 0, 1, 2, 0, 7, Blocks.FARMLAND.getBlockData(), Blocks.FARMLAND.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 4, 0, 1, 5, 0, 7, Blocks.FARMLAND.getBlockData(), Blocks.FARMLAND.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 7, 0, 1, 8, 0, 7, Blocks.FARMLAND.getBlockData(), Blocks.FARMLAND.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 10, 0, 1, 11, 0, 7, Blocks.FARMLAND.getBlockData(), Blocks.FARMLAND.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 0, 0, 0, 0, 0, 8, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 6, 0, 0, 6, 0, 8, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 12, 0, 0, 12, 0, 8, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 0, 0, 11, 0, 0, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 0, 8, 11, 0, 8, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 3, 0, 1, 3, 0, 7, Blocks.WATER.getBlockData(), Blocks.WATER.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 9, 0, 1, 9, 0, 7, Blocks.WATER.getBlockData(), Blocks.WATER.getBlockData(), false); + + int i; + + for (i = 1; i <= 7; ++i) { + BlockCrops blockcrops = (BlockCrops) this.a.getBlock(); + int j = blockcrops.e(); + int k = j / 3; + + this.a(generatoraccess, (IBlockData) this.a.set(blockcrops.d(), MathHelper.nextInt(random, k, j)), 1, 1, i, structureboundingbox); + this.a(generatoraccess, (IBlockData) this.a.set(blockcrops.d(), MathHelper.nextInt(random, k, j)), 2, 1, i, structureboundingbox); + blockcrops = (BlockCrops) this.b.getBlock(); + int l = blockcrops.e(); + int i1 = l / 3; + + this.a(generatoraccess, (IBlockData) this.b.set(blockcrops.d(), MathHelper.nextInt(random, i1, l)), 4, 1, i, structureboundingbox); + this.a(generatoraccess, (IBlockData) this.b.set(blockcrops.d(), MathHelper.nextInt(random, i1, l)), 5, 1, i, structureboundingbox); + blockcrops = (BlockCrops) this.c.getBlock(); + int j1 = blockcrops.e(); + int k1 = j1 / 3; + + this.a(generatoraccess, (IBlockData) this.c.set(blockcrops.d(), MathHelper.nextInt(random, k1, j1)), 7, 1, i, structureboundingbox); + this.a(generatoraccess, (IBlockData) this.c.set(blockcrops.d(), MathHelper.nextInt(random, k1, j1)), 8, 1, i, structureboundingbox); + blockcrops = (BlockCrops) this.d.getBlock(); + int l1 = blockcrops.e(); + int i2 = l1 / 3; + + this.a(generatoraccess, (IBlockData) this.d.set(blockcrops.d(), MathHelper.nextInt(random, i2, l1)), 10, 1, i, structureboundingbox); + this.a(generatoraccess, (IBlockData) this.d.set(blockcrops.d(), MathHelper.nextInt(random, i2, l1)), 11, 1, i, structureboundingbox); + } + + for (i = 0; i < 9; ++i) { + for (int j2 = 0; j2 < 13; ++j2) { + this.a(generatoraccess, j2, 4, i, structureboundingbox); + this.b(generatoraccess, Blocks.DIRT.getBlockData(), j2, -1, i, structureboundingbox); + } + } + + return true; + } + } + + public static class WorldGenVillageFarm extends WorldGenVillagePieces.WorldGenVillagePiece { + + private IBlockData a; + private IBlockData b; + + public WorldGenVillageFarm() {} + + public WorldGenVillageFarm(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, int i, Random random, StructureBoundingBox structureboundingbox, EnumDirection enumdirection) { + super(worldgenvillagepieces_worldgenvillagestartpiece, i); + this.a(enumdirection); + this.n = structureboundingbox; + this.a = b(random); + this.b = b(random); + } + + protected void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + nbttagcompound.set("CA", GameProfileSerializer.a(this.a)); + nbttagcompound.set("CB", GameProfileSerializer.a(this.b)); + } + + protected void a(NBTTagCompound nbttagcompound, DefinedStructureManager definedstructuremanager) { + super.a(nbttagcompound, definedstructuremanager); + this.a = GameProfileSerializer.d(nbttagcompound.getCompound("CA")); + this.b = GameProfileSerializer.d(nbttagcompound.getCompound("CB")); + } + + private static IBlockData b(Random random) { + switch (random.nextInt(10)) { + case 0: + case 1: + return Blocks.CARROTS.getBlockData(); + case 2: + case 3: + return Blocks.POTATOES.getBlockData(); + case 4: + return Blocks.BEETROOTS.getBlockData(); + default: + return Blocks.WHEAT.getBlockData(); + } + } + + public static WorldGenVillagePieces.WorldGenVillageFarm a(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, List list, Random random, int i, int j, int k, EnumDirection enumdirection, int l) { + StructureBoundingBox structureboundingbox = StructureBoundingBox.a(i, j, k, 0, 0, 0, 7, 4, 9, enumdirection); + + return a(structureboundingbox) && StructurePiece.a(list, structureboundingbox) == null ? new WorldGenVillagePieces.WorldGenVillageFarm(worldgenvillagepieces_worldgenvillagestartpiece, l, random, structureboundingbox, enumdirection) : null; + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (this.f < 0) { + this.f = this.a(generatoraccess, structureboundingbox); + if (this.f < 0) { + return true; + } + + this.n.a(0, this.f - this.n.e + 4 - 1, 0); + } + + IBlockData iblockdata = this.a(Blocks.OAK_LOG.getBlockData()); + + this.a(generatoraccess, structureboundingbox, 0, 1, 0, 6, 4, 8, Blocks.AIR.getBlockData(), Blocks.AIR.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 1, 0, 1, 2, 0, 7, Blocks.FARMLAND.getBlockData(), Blocks.FARMLAND.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 4, 0, 1, 5, 0, 7, Blocks.FARMLAND.getBlockData(), Blocks.FARMLAND.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 0, 0, 0, 0, 0, 8, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 6, 0, 0, 6, 0, 8, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 0, 0, 5, 0, 0, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 0, 8, 5, 0, 8, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 3, 0, 1, 3, 0, 7, Blocks.WATER.getBlockData(), Blocks.WATER.getBlockData(), false); + + int i; + + for (i = 1; i <= 7; ++i) { + BlockCrops blockcrops = (BlockCrops) this.a.getBlock(); + int j = blockcrops.e(); + int k = j / 3; + + this.a(generatoraccess, (IBlockData) this.a.set(blockcrops.d(), MathHelper.nextInt(random, k, j)), 1, 1, i, structureboundingbox); + this.a(generatoraccess, (IBlockData) this.a.set(blockcrops.d(), MathHelper.nextInt(random, k, j)), 2, 1, i, structureboundingbox); + blockcrops = (BlockCrops) this.b.getBlock(); + int l = blockcrops.e(); + int i1 = l / 3; + + this.a(generatoraccess, (IBlockData) this.b.set(blockcrops.d(), MathHelper.nextInt(random, i1, l)), 4, 1, i, structureboundingbox); + this.a(generatoraccess, (IBlockData) this.b.set(blockcrops.d(), MathHelper.nextInt(random, i1, l)), 5, 1, i, structureboundingbox); + } + + for (i = 0; i < 9; ++i) { + for (int j1 = 0; j1 < 7; ++j1) { + this.a(generatoraccess, j1, 4, i, structureboundingbox); + this.b(generatoraccess, Blocks.DIRT.getBlockData(), j1, -1, i, structureboundingbox); + } + } + + return true; + } + } + + public static class WorldGenVillageBlacksmith extends WorldGenVillagePieces.WorldGenVillagePiece { + + private boolean a; + + public WorldGenVillageBlacksmith() {} + + public WorldGenVillageBlacksmith(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, int i, Random random, StructureBoundingBox structureboundingbox, EnumDirection enumdirection) { + super(worldgenvillagepieces_worldgenvillagestartpiece, i); + this.a(enumdirection); + this.n = structureboundingbox; + } + + public static WorldGenVillagePieces.WorldGenVillageBlacksmith a(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, List list, Random random, int i, int j, int k, EnumDirection enumdirection, int l) { + StructureBoundingBox structureboundingbox = StructureBoundingBox.a(i, j, k, 0, 0, 0, 10, 6, 7, enumdirection); + + return a(structureboundingbox) && StructurePiece.a(list, structureboundingbox) == null ? new WorldGenVillagePieces.WorldGenVillageBlacksmith(worldgenvillagepieces_worldgenvillagestartpiece, l, random, structureboundingbox, enumdirection) : null; + } + + protected void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + nbttagcompound.setBoolean("Chest", this.a); + } + + protected void a(NBTTagCompound nbttagcompound, DefinedStructureManager definedstructuremanager) { + super.a(nbttagcompound, definedstructuremanager); + this.a = nbttagcompound.getBoolean("Chest"); + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (this.f < 0) { + this.f = this.a(generatoraccess, structureboundingbox); + if (this.f < 0) { + return true; + } + + this.n.a(0, this.f - this.n.e + 6 - 1, 0); + } + + IBlockData iblockdata = Blocks.COBBLESTONE.getBlockData(); + IBlockData iblockdata1 = this.a((IBlockData) Blocks.OAK_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.NORTH)); + IBlockData iblockdata2 = this.a((IBlockData) Blocks.OAK_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.WEST)); + IBlockData iblockdata3 = this.a(Blocks.OAK_PLANKS.getBlockData()); + IBlockData iblockdata4 = this.a((IBlockData) Blocks.COBBLESTONE_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.NORTH)); + IBlockData iblockdata5 = this.a(Blocks.OAK_LOG.getBlockData()); + IBlockData iblockdata6 = this.a(Blocks.OAK_FENCE.getBlockData()); + + this.a(generatoraccess, structureboundingbox, 0, 1, 0, 9, 4, 6, Blocks.AIR.getBlockData(), Blocks.AIR.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 0, 0, 0, 9, 0, 6, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 0, 4, 0, 9, 4, 6, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 0, 5, 0, 9, 5, 6, Blocks.STONE_SLAB.getBlockData(), Blocks.STONE_SLAB.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 1, 5, 1, 8, 5, 5, Blocks.AIR.getBlockData(), Blocks.AIR.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 1, 1, 0, 2, 3, 0, iblockdata3, iblockdata3, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 0, 0, 4, 0, iblockdata5, iblockdata5, false); + this.a(generatoraccess, structureboundingbox, 3, 1, 0, 3, 4, 0, iblockdata5, iblockdata5, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 6, 0, 4, 6, iblockdata5, iblockdata5, false); + this.a(generatoraccess, iblockdata3, 3, 3, 1, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 3, 1, 2, 3, 3, 2, iblockdata3, iblockdata3, false); + this.a(generatoraccess, structureboundingbox, 4, 1, 3, 5, 3, 3, iblockdata3, iblockdata3, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 1, 0, 3, 5, iblockdata3, iblockdata3, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 6, 5, 3, 6, iblockdata3, iblockdata3, false); + this.a(generatoraccess, structureboundingbox, 5, 1, 0, 5, 3, 0, iblockdata6, iblockdata6, false); + this.a(generatoraccess, structureboundingbox, 9, 1, 0, 9, 3, 0, iblockdata6, iblockdata6, false); + this.a(generatoraccess, structureboundingbox, 6, 1, 4, 9, 4, 6, iblockdata, iblockdata, false); + this.a(generatoraccess, Blocks.LAVA.getBlockData(), 7, 1, 5, structureboundingbox); + this.a(generatoraccess, Blocks.LAVA.getBlockData(), 8, 1, 5, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.IRON_BARS.getBlockData().set(BlockIronBars.NORTH, true)).set(BlockIronBars.SOUTH, true), 9, 2, 5, structureboundingbox); + this.a(generatoraccess, (IBlockData) Blocks.IRON_BARS.getBlockData().set(BlockIronBars.NORTH, true), 9, 2, 4, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 7, 2, 4, 8, 2, 5, Blocks.AIR.getBlockData(), Blocks.AIR.getBlockData(), false); + this.a(generatoraccess, iblockdata, 6, 1, 3, structureboundingbox); + this.a(generatoraccess, (IBlockData) Blocks.FURNACE.getBlockData().set(BlockFurnace.FACING, EnumDirection.SOUTH), 6, 2, 3, structureboundingbox); + this.a(generatoraccess, (IBlockData) Blocks.FURNACE.getBlockData().set(BlockFurnace.FACING, EnumDirection.SOUTH), 6, 3, 3, structureboundingbox); + this.a(generatoraccess, (IBlockData) Blocks.STONE_SLAB.getBlockData().set(BlockStepAbstract.a, BlockPropertySlabType.DOUBLE), 8, 1, 1, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 2, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 2, 4, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 2, 2, 6, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 4, 2, 6, structureboundingbox); + this.a(generatoraccess, iblockdata6, 2, 1, 4, structureboundingbox); + this.a(generatoraccess, Blocks.OAK_PRESSURE_PLATE.getBlockData(), 2, 2, 4, structureboundingbox); + this.a(generatoraccess, iblockdata3, 1, 1, 5, structureboundingbox); + this.a(generatoraccess, iblockdata1, 2, 1, 5, structureboundingbox); + this.a(generatoraccess, iblockdata2, 1, 1, 4, structureboundingbox); + if (!this.a && structureboundingbox.b((BaseBlockPosition) (new BlockPosition(this.a(5, 5), this.d(1), this.b(5, 5))))) { + this.a = true; + this.a(generatoraccess, structureboundingbox, random, 5, 1, 5, LootTables.e); + } + + int i; + + for (i = 6; i <= 8; ++i) { + if (this.a((IBlockAccess) generatoraccess, i, 0, -1, structureboundingbox).isAir() && !this.a((IBlockAccess) generatoraccess, i, -1, -1, structureboundingbox).isAir()) { + this.a(generatoraccess, iblockdata4, i, 0, -1, structureboundingbox); + if (this.a((IBlockAccess) generatoraccess, i, -1, -1, structureboundingbox).getBlock() == Blocks.GRASS_PATH) { + this.a(generatoraccess, Blocks.GRASS_BLOCK.getBlockData(), i, -1, -1, structureboundingbox); + } + } + } + + for (i = 0; i < 7; ++i) { + for (int j = 0; j < 10; ++j) { + this.a(generatoraccess, j, 6, i, structureboundingbox); + this.b(generatoraccess, iblockdata, j, -1, i, structureboundingbox); + } + } + + this.a(generatoraccess, structureboundingbox, 7, 1, 1, 1); + return true; + } + + protected int c(int i, int j) { + return 3; + } + } + + public static class WorldGenVillageHouse2 extends WorldGenVillagePieces.WorldGenVillagePiece { + + public WorldGenVillageHouse2() {} + + public WorldGenVillageHouse2(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, int i, Random random, StructureBoundingBox structureboundingbox, EnumDirection enumdirection) { + super(worldgenvillagepieces_worldgenvillagestartpiece, i); + this.a(enumdirection); + this.n = structureboundingbox; + } + + public static WorldGenVillagePieces.WorldGenVillageHouse2 a(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, List list, Random random, int i, int j, int k, EnumDirection enumdirection, int l) { + StructureBoundingBox structureboundingbox = StructureBoundingBox.a(i, j, k, 0, 0, 0, 9, 7, 12, enumdirection); + + return a(structureboundingbox) && StructurePiece.a(list, structureboundingbox) == null ? new WorldGenVillagePieces.WorldGenVillageHouse2(worldgenvillagepieces_worldgenvillagestartpiece, l, random, structureboundingbox, enumdirection) : null; + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (this.f < 0) { + this.f = this.a(generatoraccess, structureboundingbox); + if (this.f < 0) { + return true; + } + + this.n.a(0, this.f - this.n.e + 7 - 1, 0); + } + + IBlockData iblockdata = this.a(Blocks.COBBLESTONE.getBlockData()); + IBlockData iblockdata1 = this.a((IBlockData) Blocks.OAK_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.NORTH)); + IBlockData iblockdata2 = this.a((IBlockData) Blocks.OAK_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.SOUTH)); + IBlockData iblockdata3 = this.a((IBlockData) Blocks.OAK_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.EAST)); + IBlockData iblockdata4 = this.a((IBlockData) Blocks.OAK_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.WEST)); + IBlockData iblockdata5 = this.a(Blocks.OAK_PLANKS.getBlockData()); + IBlockData iblockdata6 = this.a(Blocks.OAK_LOG.getBlockData()); + + this.a(generatoraccess, structureboundingbox, 1, 1, 1, 7, 4, 4, Blocks.AIR.getBlockData(), Blocks.AIR.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 2, 1, 6, 8, 4, 10, Blocks.AIR.getBlockData(), Blocks.AIR.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 2, 0, 5, 8, 0, 10, iblockdata5, iblockdata5, false); + this.a(generatoraccess, structureboundingbox, 1, 0, 1, 7, 0, 4, iblockdata5, iblockdata5, false); + this.a(generatoraccess, structureboundingbox, 0, 0, 0, 0, 3, 5, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 8, 0, 0, 8, 3, 10, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 0, 0, 7, 2, 0, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 0, 5, 2, 1, 5, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 2, 0, 6, 2, 3, 10, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 3, 0, 10, 7, 3, 10, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 0, 7, 3, 0, iblockdata5, iblockdata5, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 5, 2, 3, 5, iblockdata5, iblockdata5, false); + this.a(generatoraccess, structureboundingbox, 0, 4, 1, 8, 4, 1, iblockdata5, iblockdata5, false); + this.a(generatoraccess, structureboundingbox, 0, 4, 4, 3, 4, 4, iblockdata5, iblockdata5, false); + this.a(generatoraccess, structureboundingbox, 0, 5, 2, 8, 5, 3, iblockdata5, iblockdata5, false); + this.a(generatoraccess, iblockdata5, 0, 4, 2, structureboundingbox); + this.a(generatoraccess, iblockdata5, 0, 4, 3, structureboundingbox); + this.a(generatoraccess, iblockdata5, 8, 4, 2, structureboundingbox); + this.a(generatoraccess, iblockdata5, 8, 4, 3, structureboundingbox); + this.a(generatoraccess, iblockdata5, 8, 4, 4, structureboundingbox); + IBlockData iblockdata7 = iblockdata1; + IBlockData iblockdata8 = iblockdata2; + IBlockData iblockdata9 = iblockdata4; + IBlockData iblockdata10 = iblockdata3; + + int i; + int j; + + for (j = -1; j <= 2; ++j) { + for (i = 0; i <= 8; ++i) { + this.a(generatoraccess, iblockdata7, i, 4 + j, j, structureboundingbox); + if ((j > -1 || i <= 1) && (j > 0 || i <= 3) && (j > 1 || i <= 4 || i >= 6)) { + this.a(generatoraccess, iblockdata8, i, 4 + j, 5 - j, structureboundingbox); + } + } + } + + this.a(generatoraccess, structureboundingbox, 3, 4, 5, 3, 4, 10, iblockdata5, iblockdata5, false); + this.a(generatoraccess, structureboundingbox, 7, 4, 2, 7, 4, 10, iblockdata5, iblockdata5, false); + this.a(generatoraccess, structureboundingbox, 4, 5, 4, 4, 5, 10, iblockdata5, iblockdata5, false); + this.a(generatoraccess, structureboundingbox, 6, 5, 4, 6, 5, 10, iblockdata5, iblockdata5, false); + this.a(generatoraccess, structureboundingbox, 5, 6, 3, 5, 6, 10, iblockdata5, iblockdata5, false); + + for (j = 4; j >= 1; --j) { + this.a(generatoraccess, iblockdata5, j, 2 + j, 7 - j, structureboundingbox); + + for (i = 8 - j; i <= 10; ++i) { + this.a(generatoraccess, iblockdata10, j, 2 + j, i, structureboundingbox); + } + } + + this.a(generatoraccess, iblockdata5, 6, 6, 3, structureboundingbox); + this.a(generatoraccess, iblockdata5, 7, 5, 4, structureboundingbox); + this.a(generatoraccess, iblockdata4, 6, 6, 4, structureboundingbox); + + for (j = 6; j <= 8; ++j) { + for (i = 5; i <= 10; ++i) { + this.a(generatoraccess, iblockdata9, j, 12 - j, i, structureboundingbox); + } + } + + this.a(generatoraccess, iblockdata6, 0, 2, 1, structureboundingbox); + this.a(generatoraccess, iblockdata6, 0, 2, 4, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 2, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 2, 3, structureboundingbox); + this.a(generatoraccess, iblockdata6, 4, 2, 0, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 5, 2, 0, structureboundingbox); + this.a(generatoraccess, iblockdata6, 6, 2, 0, structureboundingbox); + this.a(generatoraccess, iblockdata6, 8, 2, 1, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 8, 2, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 8, 2, 3, structureboundingbox); + this.a(generatoraccess, iblockdata6, 8, 2, 4, structureboundingbox); + this.a(generatoraccess, iblockdata5, 8, 2, 5, structureboundingbox); + this.a(generatoraccess, iblockdata6, 8, 2, 6, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 8, 2, 7, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 8, 2, 8, structureboundingbox); + this.a(generatoraccess, iblockdata6, 8, 2, 9, structureboundingbox); + this.a(generatoraccess, iblockdata6, 2, 2, 6, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 2, 2, 7, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 2, 2, 8, structureboundingbox); + this.a(generatoraccess, iblockdata6, 2, 2, 9, structureboundingbox); + this.a(generatoraccess, iblockdata6, 4, 4, 10, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 5, 4, 10, structureboundingbox); + this.a(generatoraccess, iblockdata6, 6, 4, 10, structureboundingbox); + this.a(generatoraccess, iblockdata5, 5, 5, 10, structureboundingbox); + this.a(generatoraccess, Blocks.AIR.getBlockData(), 2, 1, 0, structureboundingbox); + this.a(generatoraccess, Blocks.AIR.getBlockData(), 2, 2, 0, structureboundingbox); + this.a(generatoraccess, EnumDirection.NORTH, 2, 3, 1, structureboundingbox); + this.a(generatoraccess, structureboundingbox, random, 2, 1, 0, EnumDirection.NORTH); + this.a(generatoraccess, structureboundingbox, 1, 0, -1, 3, 2, -1, Blocks.AIR.getBlockData(), Blocks.AIR.getBlockData(), false); + if (this.a((IBlockAccess) generatoraccess, 2, 0, -1, structureboundingbox).isAir() && !this.a((IBlockAccess) generatoraccess, 2, -1, -1, structureboundingbox).isAir()) { + this.a(generatoraccess, iblockdata7, 2, 0, -1, structureboundingbox); + if (this.a((IBlockAccess) generatoraccess, 2, -1, -1, structureboundingbox).getBlock() == Blocks.GRASS_PATH) { + this.a(generatoraccess, Blocks.GRASS_BLOCK.getBlockData(), 2, -1, -1, structureboundingbox); + } + } + + for (j = 0; j < 5; ++j) { + for (i = 0; i < 9; ++i) { + this.a(generatoraccess, i, 7, j, structureboundingbox); + this.b(generatoraccess, iblockdata, i, -1, j, structureboundingbox); + } + } + + for (j = 5; j < 11; ++j) { + for (i = 2; i < 9; ++i) { + this.a(generatoraccess, i, 7, j, structureboundingbox); + this.b(generatoraccess, iblockdata, i, -1, j, structureboundingbox); + } + } + + this.a(generatoraccess, structureboundingbox, 4, 1, 2, 2); + return true; + } + } + + public static class WorldGenVillageButcher extends WorldGenVillagePieces.WorldGenVillagePiece { + + public WorldGenVillageButcher() {} + + public WorldGenVillageButcher(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, int i, Random random, StructureBoundingBox structureboundingbox, EnumDirection enumdirection) { + super(worldgenvillagepieces_worldgenvillagestartpiece, i); + this.a(enumdirection); + this.n = structureboundingbox; + } + + public static WorldGenVillagePieces.WorldGenVillageButcher a(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, List list, Random random, int i, int j, int k, EnumDirection enumdirection, int l) { + StructureBoundingBox structureboundingbox = StructureBoundingBox.a(i, j, k, 0, 0, 0, 9, 7, 11, enumdirection); + + return a(structureboundingbox) && StructurePiece.a(list, structureboundingbox) == null ? new WorldGenVillagePieces.WorldGenVillageButcher(worldgenvillagepieces_worldgenvillagestartpiece, l, random, structureboundingbox, enumdirection) : null; + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (this.f < 0) { + this.f = this.a(generatoraccess, structureboundingbox); + if (this.f < 0) { + return true; + } + + this.n.a(0, this.f - this.n.e + 7 - 1, 0); + } + + IBlockData iblockdata = this.a(Blocks.COBBLESTONE.getBlockData()); + IBlockData iblockdata1 = this.a((IBlockData) Blocks.OAK_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.NORTH)); + IBlockData iblockdata2 = this.a((IBlockData) Blocks.OAK_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.SOUTH)); + IBlockData iblockdata3 = this.a((IBlockData) Blocks.OAK_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.WEST)); + IBlockData iblockdata4 = this.a(Blocks.OAK_PLANKS.getBlockData()); + IBlockData iblockdata5 = this.a(Blocks.OAK_LOG.getBlockData()); + IBlockData iblockdata6 = this.a(Blocks.OAK_FENCE.getBlockData()); + + this.a(generatoraccess, structureboundingbox, 1, 1, 1, 7, 4, 4, Blocks.AIR.getBlockData(), Blocks.AIR.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 2, 1, 6, 8, 4, 10, Blocks.AIR.getBlockData(), Blocks.AIR.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 2, 0, 6, 8, 0, 10, Blocks.DIRT.getBlockData(), Blocks.DIRT.getBlockData(), false); + this.a(generatoraccess, iblockdata, 6, 0, 6, structureboundingbox); + IBlockData iblockdata7 = (IBlockData) ((IBlockData) iblockdata6.set(BlockFence.NORTH, true)).set(BlockFence.SOUTH, true); + IBlockData iblockdata8 = (IBlockData) ((IBlockData) iblockdata6.set(BlockFence.WEST, true)).set(BlockFence.EAST, true); + + this.a(generatoraccess, structureboundingbox, 2, 1, 6, 2, 1, 9, iblockdata7, iblockdata7, false); + this.a(generatoraccess, (IBlockData) ((IBlockData) iblockdata6.set(BlockFence.SOUTH, true)).set(BlockFence.EAST, true), 2, 1, 10, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 8, 1, 6, 8, 1, 9, iblockdata7, iblockdata7, false); + this.a(generatoraccess, (IBlockData) ((IBlockData) iblockdata6.set(BlockFence.SOUTH, true)).set(BlockFence.WEST, true), 8, 1, 10, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 3, 1, 10, 7, 1, 10, iblockdata8, iblockdata8, false); + this.a(generatoraccess, structureboundingbox, 1, 0, 1, 7, 0, 4, iblockdata4, iblockdata4, false); + this.a(generatoraccess, structureboundingbox, 0, 0, 0, 0, 3, 5, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 8, 0, 0, 8, 3, 5, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 0, 0, 7, 1, 0, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 0, 5, 7, 1, 5, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 0, 7, 3, 0, iblockdata4, iblockdata4, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 5, 7, 3, 5, iblockdata4, iblockdata4, false); + this.a(generatoraccess, structureboundingbox, 0, 4, 1, 8, 4, 1, iblockdata4, iblockdata4, false); + this.a(generatoraccess, structureboundingbox, 0, 4, 4, 8, 4, 4, iblockdata4, iblockdata4, false); + this.a(generatoraccess, structureboundingbox, 0, 5, 2, 8, 5, 3, iblockdata4, iblockdata4, false); + this.a(generatoraccess, iblockdata4, 0, 4, 2, structureboundingbox); + this.a(generatoraccess, iblockdata4, 0, 4, 3, structureboundingbox); + this.a(generatoraccess, iblockdata4, 8, 4, 2, structureboundingbox); + this.a(generatoraccess, iblockdata4, 8, 4, 3, structureboundingbox); + IBlockData iblockdata9 = iblockdata1; + IBlockData iblockdata10 = iblockdata2; + + int i; + + for (int j = -1; j <= 2; ++j) { + for (i = 0; i <= 8; ++i) { + this.a(generatoraccess, iblockdata9, i, 4 + j, j, structureboundingbox); + this.a(generatoraccess, iblockdata10, i, 4 + j, 5 - j, structureboundingbox); + } + } + + this.a(generatoraccess, iblockdata5, 0, 2, 1, structureboundingbox); + this.a(generatoraccess, iblockdata5, 0, 2, 4, structureboundingbox); + this.a(generatoraccess, iblockdata5, 8, 2, 1, structureboundingbox); + this.a(generatoraccess, iblockdata5, 8, 2, 4, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 2, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 2, 3, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 8, 2, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 8, 2, 3, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 2, 2, 5, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 3, 2, 5, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 5, 2, 0, structureboundingbox); + this.a(generatoraccess, iblockdata6, 2, 1, 3, structureboundingbox); + this.a(generatoraccess, Blocks.OAK_PRESSURE_PLATE.getBlockData(), 2, 2, 3, structureboundingbox); + this.a(generatoraccess, iblockdata4, 1, 1, 4, structureboundingbox); + this.a(generatoraccess, iblockdata9, 2, 1, 4, structureboundingbox); + this.a(generatoraccess, iblockdata3, 1, 1, 3, structureboundingbox); + IBlockData iblockdata11 = (IBlockData) Blocks.STONE_SLAB.getBlockData().set(BlockStepAbstract.a, BlockPropertySlabType.DOUBLE); + + this.a(generatoraccess, structureboundingbox, 5, 0, 1, 7, 0, 3, iblockdata11, iblockdata11, false); + this.a(generatoraccess, iblockdata11, 6, 1, 1, structureboundingbox); + this.a(generatoraccess, iblockdata11, 6, 1, 2, structureboundingbox); + this.a(generatoraccess, Blocks.AIR.getBlockData(), 2, 1, 0, structureboundingbox); + this.a(generatoraccess, Blocks.AIR.getBlockData(), 2, 2, 0, structureboundingbox); + this.a(generatoraccess, EnumDirection.NORTH, 2, 3, 1, structureboundingbox); + this.a(generatoraccess, structureboundingbox, random, 2, 1, 0, EnumDirection.NORTH); + if (this.a((IBlockAccess) generatoraccess, 2, 0, -1, structureboundingbox).isAir() && !this.a((IBlockAccess) generatoraccess, 2, -1, -1, structureboundingbox).isAir()) { + this.a(generatoraccess, iblockdata9, 2, 0, -1, structureboundingbox); + if (this.a((IBlockAccess) generatoraccess, 2, -1, -1, structureboundingbox).getBlock() == Blocks.GRASS_PATH) { + this.a(generatoraccess, Blocks.GRASS_BLOCK.getBlockData(), 2, -1, -1, structureboundingbox); + } + } + + this.a(generatoraccess, Blocks.AIR.getBlockData(), 6, 1, 5, structureboundingbox); + this.a(generatoraccess, Blocks.AIR.getBlockData(), 6, 2, 5, structureboundingbox); + this.a(generatoraccess, EnumDirection.SOUTH, 6, 3, 4, structureboundingbox); + this.a(generatoraccess, structureboundingbox, random, 6, 1, 5, EnumDirection.SOUTH); + + for (i = 0; i < 5; ++i) { + for (int k = 0; k < 9; ++k) { + this.a(generatoraccess, k, 7, i, structureboundingbox); + this.b(generatoraccess, iblockdata, k, -1, i, structureboundingbox); + } + } + + this.a(generatoraccess, structureboundingbox, 4, 1, 2, 2); + return true; + } + + protected int c(int i, int j) { + return i == 0 ? 4 : super.c(i, j); + } + } + + public static class WorldGenVillageHut extends WorldGenVillagePieces.WorldGenVillagePiece { + + private boolean a; + private int b; + + public WorldGenVillageHut() {} + + public WorldGenVillageHut(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, int i, Random random, StructureBoundingBox structureboundingbox, EnumDirection enumdirection) { + super(worldgenvillagepieces_worldgenvillagestartpiece, i); + this.a(enumdirection); + this.n = structureboundingbox; + this.a = random.nextBoolean(); + this.b = random.nextInt(3); + } + + protected void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + nbttagcompound.setInt("T", this.b); + nbttagcompound.setBoolean("C", this.a); + } + + protected void a(NBTTagCompound nbttagcompound, DefinedStructureManager definedstructuremanager) { + super.a(nbttagcompound, definedstructuremanager); + this.b = nbttagcompound.getInt("T"); + this.a = nbttagcompound.getBoolean("C"); + } + + public static WorldGenVillagePieces.WorldGenVillageHut a(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, List list, Random random, int i, int j, int k, EnumDirection enumdirection, int l) { + StructureBoundingBox structureboundingbox = StructureBoundingBox.a(i, j, k, 0, 0, 0, 4, 6, 5, enumdirection); + + return a(structureboundingbox) && StructurePiece.a(list, structureboundingbox) == null ? new WorldGenVillagePieces.WorldGenVillageHut(worldgenvillagepieces_worldgenvillagestartpiece, l, random, structureboundingbox, enumdirection) : null; + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (this.f < 0) { + this.f = this.a(generatoraccess, structureboundingbox); + if (this.f < 0) { + return true; + } + + this.n.a(0, this.f - this.n.e + 6 - 1, 0); + } + + IBlockData iblockdata = this.a(Blocks.COBBLESTONE.getBlockData()); + IBlockData iblockdata1 = this.a(Blocks.OAK_PLANKS.getBlockData()); + IBlockData iblockdata2 = this.a((IBlockData) Blocks.COBBLESTONE_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.NORTH)); + IBlockData iblockdata3 = this.a(Blocks.OAK_LOG.getBlockData()); + IBlockData iblockdata4 = this.a(Blocks.OAK_FENCE.getBlockData()); + + this.a(generatoraccess, structureboundingbox, 1, 1, 1, 3, 5, 4, Blocks.AIR.getBlockData(), Blocks.AIR.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 0, 0, 0, 3, 0, 4, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 0, 1, 2, 0, 3, Blocks.DIRT.getBlockData(), Blocks.DIRT.getBlockData(), false); + if (this.a) { + this.a(generatoraccess, structureboundingbox, 1, 4, 1, 2, 4, 3, iblockdata3, iblockdata3, false); + } else { + this.a(generatoraccess, structureboundingbox, 1, 5, 1, 2, 5, 3, iblockdata3, iblockdata3, false); + } + + this.a(generatoraccess, iblockdata3, 1, 4, 0, structureboundingbox); + this.a(generatoraccess, iblockdata3, 2, 4, 0, structureboundingbox); + this.a(generatoraccess, iblockdata3, 1, 4, 4, structureboundingbox); + this.a(generatoraccess, iblockdata3, 2, 4, 4, structureboundingbox); + this.a(generatoraccess, iblockdata3, 0, 4, 1, structureboundingbox); + this.a(generatoraccess, iblockdata3, 0, 4, 2, structureboundingbox); + this.a(generatoraccess, iblockdata3, 0, 4, 3, structureboundingbox); + this.a(generatoraccess, iblockdata3, 3, 4, 1, structureboundingbox); + this.a(generatoraccess, iblockdata3, 3, 4, 2, structureboundingbox); + this.a(generatoraccess, iblockdata3, 3, 4, 3, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 0, 1, 0, 0, 3, 0, iblockdata3, iblockdata3, false); + this.a(generatoraccess, structureboundingbox, 3, 1, 0, 3, 3, 0, iblockdata3, iblockdata3, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 4, 0, 3, 4, iblockdata3, iblockdata3, false); + this.a(generatoraccess, structureboundingbox, 3, 1, 4, 3, 3, 4, iblockdata3, iblockdata3, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 1, 0, 3, 3, iblockdata1, iblockdata1, false); + this.a(generatoraccess, structureboundingbox, 3, 1, 1, 3, 3, 3, iblockdata1, iblockdata1, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 0, 2, 3, 0, iblockdata1, iblockdata1, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 4, 2, 3, 4, iblockdata1, iblockdata1, false); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 2, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 3, 2, 2, structureboundingbox); + if (this.b > 0) { + this.a(generatoraccess, (IBlockData) ((IBlockData) iblockdata4.set(BlockFence.NORTH, true)).set(this.b == 1 ? BlockFence.WEST : BlockFence.EAST, true), this.b, 1, 3, structureboundingbox); + this.a(generatoraccess, Blocks.OAK_PRESSURE_PLATE.getBlockData(), this.b, 2, 3, structureboundingbox); + } + + this.a(generatoraccess, Blocks.AIR.getBlockData(), 1, 1, 0, structureboundingbox); + this.a(generatoraccess, Blocks.AIR.getBlockData(), 1, 2, 0, structureboundingbox); + this.a(generatoraccess, structureboundingbox, random, 1, 1, 0, EnumDirection.NORTH); + if (this.a((IBlockAccess) generatoraccess, 1, 0, -1, structureboundingbox).isAir() && !this.a((IBlockAccess) generatoraccess, 1, -1, -1, structureboundingbox).isAir()) { + this.a(generatoraccess, iblockdata2, 1, 0, -1, structureboundingbox); + if (this.a((IBlockAccess) generatoraccess, 1, -1, -1, structureboundingbox).getBlock() == Blocks.GRASS_PATH) { + this.a(generatoraccess, Blocks.GRASS_BLOCK.getBlockData(), 1, -1, -1, structureboundingbox); + } + } + + for (int i = 0; i < 5; ++i) { + for (int j = 0; j < 4; ++j) { + this.a(generatoraccess, j, 6, i, structureboundingbox); + this.b(generatoraccess, iblockdata, j, -1, i, structureboundingbox); + } + } + + this.a(generatoraccess, structureboundingbox, 1, 1, 2, 1); + return true; + } + } + + public static class WorldGenVillageLibrary extends WorldGenVillagePieces.WorldGenVillagePiece { + + public WorldGenVillageLibrary() {} + + public WorldGenVillageLibrary(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, int i, Random random, StructureBoundingBox structureboundingbox, EnumDirection enumdirection) { + super(worldgenvillagepieces_worldgenvillagestartpiece, i); + this.a(enumdirection); + this.n = structureboundingbox; + } + + public static WorldGenVillagePieces.WorldGenVillageLibrary a(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, List list, Random random, int i, int j, int k, EnumDirection enumdirection, int l) { + StructureBoundingBox structureboundingbox = StructureBoundingBox.a(i, j, k, 0, 0, 0, 9, 9, 6, enumdirection); + + return a(structureboundingbox) && StructurePiece.a(list, structureboundingbox) == null ? new WorldGenVillagePieces.WorldGenVillageLibrary(worldgenvillagepieces_worldgenvillagestartpiece, l, random, structureboundingbox, enumdirection) : null; + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (this.f < 0) { + this.f = this.a(generatoraccess, structureboundingbox); + if (this.f < 0) { + return true; + } + + this.n.a(0, this.f - this.n.e + 9 - 1, 0); + } + + IBlockData iblockdata = this.a(Blocks.COBBLESTONE.getBlockData()); + IBlockData iblockdata1 = this.a((IBlockData) Blocks.OAK_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.NORTH)); + IBlockData iblockdata2 = this.a((IBlockData) Blocks.OAK_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.SOUTH)); + IBlockData iblockdata3 = this.a((IBlockData) Blocks.OAK_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.EAST)); + IBlockData iblockdata4 = this.a(Blocks.OAK_PLANKS.getBlockData()); + IBlockData iblockdata5 = this.a((IBlockData) Blocks.COBBLESTONE_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.NORTH)); + IBlockData iblockdata6 = this.a(Blocks.OAK_FENCE.getBlockData()); + + this.a(generatoraccess, structureboundingbox, 1, 1, 1, 7, 5, 4, Blocks.AIR.getBlockData(), Blocks.AIR.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 0, 0, 0, 8, 0, 5, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 0, 5, 0, 8, 5, 5, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 0, 6, 1, 8, 6, 4, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 0, 7, 2, 8, 7, 3, iblockdata, iblockdata, false); + + int i; + + for (int j = -1; j <= 2; ++j) { + for (i = 0; i <= 8; ++i) { + this.a(generatoraccess, iblockdata1, i, 6 + j, j, structureboundingbox); + this.a(generatoraccess, iblockdata2, i, 6 + j, 5 - j, structureboundingbox); + } + } + + this.a(generatoraccess, structureboundingbox, 0, 1, 0, 0, 1, 5, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 5, 8, 1, 5, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 8, 1, 0, 8, 1, 4, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 2, 1, 0, 7, 1, 0, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 0, 2, 0, 0, 4, 0, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 0, 2, 5, 0, 4, 5, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 8, 2, 5, 8, 4, 5, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 8, 2, 0, 8, 4, 0, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 0, 2, 1, 0, 4, 4, iblockdata4, iblockdata4, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 5, 7, 4, 5, iblockdata4, iblockdata4, false); + this.a(generatoraccess, structureboundingbox, 8, 2, 1, 8, 4, 4, iblockdata4, iblockdata4, false); + this.a(generatoraccess, structureboundingbox, 1, 2, 0, 7, 4, 0, iblockdata4, iblockdata4, false); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 4, 2, 0, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 5, 2, 0, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 6, 2, 0, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 4, 3, 0, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 5, 3, 0, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 6, 3, 0, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 2, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 2, 3, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 3, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 3, 3, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 8, 2, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 8, 2, 3, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 8, 3, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 8, 3, 3, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 2, 2, 5, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 3, 2, 5, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 5, 2, 5, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 6, 2, 5, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 1, 4, 1, 7, 4, 1, iblockdata4, iblockdata4, false); + this.a(generatoraccess, structureboundingbox, 1, 4, 4, 7, 4, 4, iblockdata4, iblockdata4, false); + this.a(generatoraccess, structureboundingbox, 1, 3, 4, 7, 3, 4, Blocks.BOOKSHELF.getBlockData(), Blocks.BOOKSHELF.getBlockData(), false); + this.a(generatoraccess, iblockdata4, 7, 1, 4, structureboundingbox); + this.a(generatoraccess, iblockdata3, 7, 1, 3, structureboundingbox); + this.a(generatoraccess, iblockdata1, 6, 1, 4, structureboundingbox); + this.a(generatoraccess, iblockdata1, 5, 1, 4, structureboundingbox); + this.a(generatoraccess, iblockdata1, 4, 1, 4, structureboundingbox); + this.a(generatoraccess, iblockdata1, 3, 1, 4, structureboundingbox); + this.a(generatoraccess, iblockdata6, 6, 1, 3, structureboundingbox); + this.a(generatoraccess, Blocks.OAK_PRESSURE_PLATE.getBlockData(), 6, 2, 3, structureboundingbox); + this.a(generatoraccess, iblockdata6, 4, 1, 3, structureboundingbox); + this.a(generatoraccess, Blocks.OAK_PRESSURE_PLATE.getBlockData(), 4, 2, 3, structureboundingbox); + this.a(generatoraccess, Blocks.CRAFTING_TABLE.getBlockData(), 7, 1, 1, structureboundingbox); + this.a(generatoraccess, Blocks.AIR.getBlockData(), 1, 1, 0, structureboundingbox); + this.a(generatoraccess, Blocks.AIR.getBlockData(), 1, 2, 0, structureboundingbox); + this.a(generatoraccess, structureboundingbox, random, 1, 1, 0, EnumDirection.NORTH); + if (this.a((IBlockAccess) generatoraccess, 1, 0, -1, structureboundingbox).isAir() && !this.a((IBlockAccess) generatoraccess, 1, -1, -1, structureboundingbox).isAir()) { + this.a(generatoraccess, iblockdata5, 1, 0, -1, structureboundingbox); + if (this.a((IBlockAccess) generatoraccess, 1, -1, -1, structureboundingbox).getBlock() == Blocks.GRASS_PATH) { + this.a(generatoraccess, Blocks.GRASS_BLOCK.getBlockData(), 1, -1, -1, structureboundingbox); + } + } + + for (i = 0; i < 6; ++i) { + for (int k = 0; k < 9; ++k) { + this.a(generatoraccess, k, 9, i, structureboundingbox); + this.b(generatoraccess, iblockdata, k, -1, i, structureboundingbox); + } + } + + this.a(generatoraccess, structureboundingbox, 2, 1, 2, 1); + return true; + } + + protected int c(int i, int j) { + return 1; + } + } + + public static class WorldGenVillageTemple extends WorldGenVillagePieces.WorldGenVillagePiece { + + public WorldGenVillageTemple() {} + + public WorldGenVillageTemple(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, int i, Random random, StructureBoundingBox structureboundingbox, EnumDirection enumdirection) { + super(worldgenvillagepieces_worldgenvillagestartpiece, i); + this.a(enumdirection); + this.n = structureboundingbox; + } + + public static WorldGenVillagePieces.WorldGenVillageTemple a(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, List list, Random random, int i, int j, int k, EnumDirection enumdirection, int l) { + StructureBoundingBox structureboundingbox = StructureBoundingBox.a(i, j, k, 0, 0, 0, 5, 12, 9, enumdirection); + + return a(structureboundingbox) && StructurePiece.a(list, structureboundingbox) == null ? new WorldGenVillagePieces.WorldGenVillageTemple(worldgenvillagepieces_worldgenvillagestartpiece, l, random, structureboundingbox, enumdirection) : null; + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (this.f < 0) { + this.f = this.a(generatoraccess, structureboundingbox); + if (this.f < 0) { + return true; + } + + this.n.a(0, this.f - this.n.e + 12 - 1, 0); + } + + IBlockData iblockdata = Blocks.COBBLESTONE.getBlockData(); + IBlockData iblockdata1 = this.a((IBlockData) Blocks.COBBLESTONE_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.NORTH)); + IBlockData iblockdata2 = this.a((IBlockData) Blocks.COBBLESTONE_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.WEST)); + IBlockData iblockdata3 = this.a((IBlockData) Blocks.COBBLESTONE_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.EAST)); + + this.a(generatoraccess, structureboundingbox, 1, 1, 1, 3, 3, 7, Blocks.AIR.getBlockData(), Blocks.AIR.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 1, 5, 1, 3, 9, 3, Blocks.AIR.getBlockData(), Blocks.AIR.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 1, 0, 0, 3, 0, 8, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 0, 3, 10, 0, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 0, 1, 1, 0, 10, 3, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 4, 1, 1, 4, 10, 3, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 0, 0, 4, 0, 4, 7, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 4, 0, 4, 4, 4, 7, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 8, 3, 4, 8, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 5, 4, 3, 10, 4, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 1, 5, 5, 3, 5, 7, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 0, 9, 0, 4, 9, 4, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 0, 4, 0, 4, 4, 4, iblockdata, iblockdata, false); + this.a(generatoraccess, iblockdata, 0, 11, 2, structureboundingbox); + this.a(generatoraccess, iblockdata, 4, 11, 2, structureboundingbox); + this.a(generatoraccess, iblockdata, 2, 11, 0, structureboundingbox); + this.a(generatoraccess, iblockdata, 2, 11, 4, structureboundingbox); + this.a(generatoraccess, iblockdata, 1, 1, 6, structureboundingbox); + this.a(generatoraccess, iblockdata, 1, 1, 7, structureboundingbox); + this.a(generatoraccess, iblockdata, 2, 1, 7, structureboundingbox); + this.a(generatoraccess, iblockdata, 3, 1, 6, structureboundingbox); + this.a(generatoraccess, iblockdata, 3, 1, 7, structureboundingbox); + this.a(generatoraccess, iblockdata1, 1, 1, 5, structureboundingbox); + this.a(generatoraccess, iblockdata1, 2, 1, 6, structureboundingbox); + this.a(generatoraccess, iblockdata1, 3, 1, 5, structureboundingbox); + this.a(generatoraccess, iblockdata2, 1, 2, 7, structureboundingbox); + this.a(generatoraccess, iblockdata3, 3, 2, 7, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 2, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 3, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 4, 2, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 4, 3, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 6, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 7, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 4, 6, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 4, 7, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 2, 6, 0, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 2, 7, 0, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 2, 6, 4, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 2, 7, 4, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 3, 6, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 4, 3, 6, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 2, 3, 8, structureboundingbox); + this.a(generatoraccess, EnumDirection.SOUTH, 2, 4, 7, structureboundingbox); + this.a(generatoraccess, EnumDirection.EAST, 1, 4, 6, structureboundingbox); + this.a(generatoraccess, EnumDirection.WEST, 3, 4, 6, structureboundingbox); + this.a(generatoraccess, EnumDirection.NORTH, 2, 4, 5, structureboundingbox); + IBlockData iblockdata4 = (IBlockData) Blocks.LADDER.getBlockData().set(BlockLadder.FACING, EnumDirection.WEST); + + int i; + + for (i = 1; i <= 9; ++i) { + this.a(generatoraccess, iblockdata4, 3, i, 3, structureboundingbox); + } + + this.a(generatoraccess, Blocks.AIR.getBlockData(), 2, 1, 0, structureboundingbox); + this.a(generatoraccess, Blocks.AIR.getBlockData(), 2, 2, 0, structureboundingbox); + this.a(generatoraccess, structureboundingbox, random, 2, 1, 0, EnumDirection.NORTH); + if (this.a((IBlockAccess) generatoraccess, 2, 0, -1, structureboundingbox).isAir() && !this.a((IBlockAccess) generatoraccess, 2, -1, -1, structureboundingbox).isAir()) { + this.a(generatoraccess, iblockdata1, 2, 0, -1, structureboundingbox); + if (this.a((IBlockAccess) generatoraccess, 2, -1, -1, structureboundingbox).getBlock() == Blocks.GRASS_PATH) { + this.a(generatoraccess, Blocks.GRASS_BLOCK.getBlockData(), 2, -1, -1, structureboundingbox); + } + } + + for (i = 0; i < 9; ++i) { + for (int j = 0; j < 5; ++j) { + this.a(generatoraccess, j, 12, i, structureboundingbox); + this.b(generatoraccess, iblockdata, j, -1, i, structureboundingbox); + } + } + + this.a(generatoraccess, structureboundingbox, 2, 1, 2, 1); + return true; + } + + protected int c(int i, int j) { + return 2; + } + } + + public static class WorldGenVillageHouse extends WorldGenVillagePieces.WorldGenVillagePiece { + + private boolean a; + + public WorldGenVillageHouse() {} + + public WorldGenVillageHouse(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, int i, Random random, StructureBoundingBox structureboundingbox, EnumDirection enumdirection) { + super(worldgenvillagepieces_worldgenvillagestartpiece, i); + this.a(enumdirection); + this.n = structureboundingbox; + this.a = random.nextBoolean(); + } + + protected void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + nbttagcompound.setBoolean("Terrace", this.a); + } + + protected void a(NBTTagCompound nbttagcompound, DefinedStructureManager definedstructuremanager) { + super.a(nbttagcompound, definedstructuremanager); + this.a = nbttagcompound.getBoolean("Terrace"); + } + + public static WorldGenVillagePieces.WorldGenVillageHouse a(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, List list, Random random, int i, int j, int k, EnumDirection enumdirection, int l) { + StructureBoundingBox structureboundingbox = StructureBoundingBox.a(i, j, k, 0, 0, 0, 5, 6, 5, enumdirection); + + return StructurePiece.a(list, structureboundingbox) != null ? null : new WorldGenVillagePieces.WorldGenVillageHouse(worldgenvillagepieces_worldgenvillagestartpiece, l, random, structureboundingbox, enumdirection); + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (this.f < 0) { + this.f = this.a(generatoraccess, structureboundingbox); + if (this.f < 0) { + return true; + } + + this.n.a(0, this.f - this.n.e + 6 - 1, 0); + } + + IBlockData iblockdata = this.a(Blocks.COBBLESTONE.getBlockData()); + IBlockData iblockdata1 = this.a(Blocks.OAK_PLANKS.getBlockData()); + IBlockData iblockdata2 = this.a((IBlockData) Blocks.COBBLESTONE_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.NORTH)); + IBlockData iblockdata3 = this.a(Blocks.OAK_LOG.getBlockData()); + IBlockData iblockdata4 = this.a(Blocks.OAK_FENCE.getBlockData()); + + this.a(generatoraccess, structureboundingbox, 0, 0, 0, 4, 0, 4, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 0, 4, 0, 4, 4, 4, iblockdata3, iblockdata3, false); + this.a(generatoraccess, structureboundingbox, 1, 4, 1, 3, 4, 3, iblockdata1, iblockdata1, false); + this.a(generatoraccess, iblockdata, 0, 1, 0, structureboundingbox); + this.a(generatoraccess, iblockdata, 0, 2, 0, structureboundingbox); + this.a(generatoraccess, iblockdata, 0, 3, 0, structureboundingbox); + this.a(generatoraccess, iblockdata, 4, 1, 0, structureboundingbox); + this.a(generatoraccess, iblockdata, 4, 2, 0, structureboundingbox); + this.a(generatoraccess, iblockdata, 4, 3, 0, structureboundingbox); + this.a(generatoraccess, iblockdata, 0, 1, 4, structureboundingbox); + this.a(generatoraccess, iblockdata, 0, 2, 4, structureboundingbox); + this.a(generatoraccess, iblockdata, 0, 3, 4, structureboundingbox); + this.a(generatoraccess, iblockdata, 4, 1, 4, structureboundingbox); + this.a(generatoraccess, iblockdata, 4, 2, 4, structureboundingbox); + this.a(generatoraccess, iblockdata, 4, 3, 4, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 0, 1, 1, 0, 3, 3, iblockdata1, iblockdata1, false); + this.a(generatoraccess, structureboundingbox, 4, 1, 1, 4, 3, 3, iblockdata1, iblockdata1, false); + this.a(generatoraccess, structureboundingbox, 1, 1, 4, 3, 3, 4, iblockdata1, iblockdata1, false); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 0, 2, 2, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.EAST, true)).set(BlockGlassPane.WEST, true), 2, 2, 4, structureboundingbox); + this.a(generatoraccess, (IBlockData) ((IBlockData) Blocks.GLASS_PANE.getBlockData().set(BlockGlassPane.SOUTH, true)).set(BlockGlassPane.NORTH, true), 4, 2, 2, structureboundingbox); + this.a(generatoraccess, iblockdata1, 1, 1, 0, structureboundingbox); + this.a(generatoraccess, iblockdata1, 1, 2, 0, structureboundingbox); + this.a(generatoraccess, iblockdata1, 1, 3, 0, structureboundingbox); + this.a(generatoraccess, iblockdata1, 2, 3, 0, structureboundingbox); + this.a(generatoraccess, iblockdata1, 3, 3, 0, structureboundingbox); + this.a(generatoraccess, iblockdata1, 3, 2, 0, structureboundingbox); + this.a(generatoraccess, iblockdata1, 3, 1, 0, structureboundingbox); + if (this.a((IBlockAccess) generatoraccess, 2, 0, -1, structureboundingbox).isAir() && !this.a((IBlockAccess) generatoraccess, 2, -1, -1, structureboundingbox).isAir()) { + this.a(generatoraccess, iblockdata2, 2, 0, -1, structureboundingbox); + if (this.a((IBlockAccess) generatoraccess, 2, -1, -1, structureboundingbox).getBlock() == Blocks.GRASS_PATH) { + this.a(generatoraccess, Blocks.GRASS_BLOCK.getBlockData(), 2, -1, -1, structureboundingbox); + } + } + + this.a(generatoraccess, structureboundingbox, 1, 1, 1, 3, 3, 3, Blocks.AIR.getBlockData(), Blocks.AIR.getBlockData(), false); + if (this.a) { + boolean flag = false; + boolean flag1 = true; + + for (int i = 0; i <= 4; ++i) { + for (int j = 0; j <= 4; ++j) { + boolean flag2 = i == 0 || i == 4; + boolean flag3 = j == 0 || j == 4; + + if (flag2 || flag3) { + boolean flag4 = i == 0 || i == 4; + boolean flag5 = j == 0 || j == 4; + IBlockData iblockdata5 = (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) iblockdata4.set(BlockFence.SOUTH, flag4 && j != 0)).set(BlockFence.NORTH, flag4 && j != 4)).set(BlockFence.WEST, flag5 && i != 0)).set(BlockFence.EAST, flag5 && i != 4); + + this.a(generatoraccess, iblockdata5, i, 5, j, structureboundingbox); + } + } + } + } + + if (this.a) { + IBlockData iblockdata6 = (IBlockData) Blocks.LADDER.getBlockData().set(BlockLadder.FACING, EnumDirection.SOUTH); + + this.a(generatoraccess, iblockdata6, 3, 1, 3, structureboundingbox); + this.a(generatoraccess, iblockdata6, 3, 2, 3, structureboundingbox); + this.a(generatoraccess, iblockdata6, 3, 3, 3, structureboundingbox); + this.a(generatoraccess, iblockdata6, 3, 4, 3, structureboundingbox); + } + + this.a(generatoraccess, EnumDirection.NORTH, 2, 3, 1, structureboundingbox); + + for (int k = 0; k < 5; ++k) { + for (int l = 0; l < 5; ++l) { + this.a(generatoraccess, l, 6, k, structureboundingbox); + this.b(generatoraccess, iblockdata, l, -1, k, structureboundingbox); + } + } + + this.a(generatoraccess, structureboundingbox, 1, 1, 2, 1); + return true; + } + } + + public static class WorldGenVillageRoad extends WorldGenVillagePieces.WorldGenVillageRoadPiece { + + private int a; + + public WorldGenVillageRoad() {} + + public WorldGenVillageRoad(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, int i, Random random, StructureBoundingBox structureboundingbox, EnumDirection enumdirection) { + super(worldgenvillagepieces_worldgenvillagestartpiece, i); + this.a(enumdirection); + this.n = structureboundingbox; + this.a = Math.max(structureboundingbox.c(), structureboundingbox.e()); + } + + protected void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + nbttagcompound.setInt("Length", this.a); + } + + protected void a(NBTTagCompound nbttagcompound, DefinedStructureManager definedstructuremanager) { + super.a(nbttagcompound, definedstructuremanager); + this.a = nbttagcompound.getInt("Length"); + } + + public void a(StructurePiece structurepiece, List list, Random random) { + boolean flag = false; + + StructurePiece structurepiece1; + int i; + + for (i = random.nextInt(5); i < this.a - 8; i += 2 + random.nextInt(5)) { + structurepiece1 = this.a((WorldGenVillagePieces.WorldGenVillageStartPiece) structurepiece, list, random, 0, i); + if (structurepiece1 != null) { + i += Math.max(structurepiece1.n.c(), structurepiece1.n.e()); + flag = true; + } + } + + for (i = random.nextInt(5); i < this.a - 8; i += 2 + random.nextInt(5)) { + structurepiece1 = this.b((WorldGenVillagePieces.WorldGenVillageStartPiece) structurepiece, list, random, 0, i); + if (structurepiece1 != null) { + i += Math.max(structurepiece1.n.c(), structurepiece1.n.e()); + flag = true; + } + } + + EnumDirection enumdirection = this.f(); + + if (flag && random.nextInt(3) > 0 && enumdirection != null) { + switch (enumdirection) { + case NORTH: + default: + WorldGenVillagePieces.e((WorldGenVillagePieces.WorldGenVillageStartPiece) structurepiece, list, random, this.n.a - 1, this.n.b, this.n.c, EnumDirection.WEST, this.e()); + break; + case SOUTH: + WorldGenVillagePieces.e((WorldGenVillagePieces.WorldGenVillageStartPiece) structurepiece, list, random, this.n.a - 1, this.n.b, this.n.f - 2, EnumDirection.WEST, this.e()); + break; + case WEST: + WorldGenVillagePieces.e((WorldGenVillagePieces.WorldGenVillageStartPiece) structurepiece, list, random, this.n.a, this.n.b, this.n.c - 1, EnumDirection.NORTH, this.e()); + break; + case EAST: + WorldGenVillagePieces.e((WorldGenVillagePieces.WorldGenVillageStartPiece) structurepiece, list, random, this.n.d - 2, this.n.b, this.n.c - 1, EnumDirection.NORTH, this.e()); + } + } + + if (flag && random.nextInt(3) > 0 && enumdirection != null) { + switch (enumdirection) { + case NORTH: + default: + WorldGenVillagePieces.e((WorldGenVillagePieces.WorldGenVillageStartPiece) structurepiece, list, random, this.n.d + 1, this.n.b, this.n.c, EnumDirection.EAST, this.e()); + break; + case SOUTH: + WorldGenVillagePieces.e((WorldGenVillagePieces.WorldGenVillageStartPiece) structurepiece, list, random, this.n.d + 1, this.n.b, this.n.f - 2, EnumDirection.EAST, this.e()); + break; + case WEST: + WorldGenVillagePieces.e((WorldGenVillagePieces.WorldGenVillageStartPiece) structurepiece, list, random, this.n.a, this.n.b, this.n.f + 1, EnumDirection.SOUTH, this.e()); + break; + case EAST: + WorldGenVillagePieces.e((WorldGenVillagePieces.WorldGenVillageStartPiece) structurepiece, list, random, this.n.d - 2, this.n.b, this.n.f + 1, EnumDirection.SOUTH, this.e()); + } + } + + } + + public static StructureBoundingBox a(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, List list, Random random, int i, int j, int k, EnumDirection enumdirection) { + for (int l = 7 * MathHelper.nextInt(random, 3, 5); l >= 7; l -= 7) { + StructureBoundingBox structureboundingbox = StructureBoundingBox.a(i, j, k, 0, 0, 0, 3, 3, l, enumdirection); + + if (StructurePiece.a(list, structureboundingbox) == null) { + return structureboundingbox; + } + } + + return null; + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + IBlockData iblockdata = this.a(Blocks.GRASS_PATH.getBlockData()); + IBlockData iblockdata1 = this.a(Blocks.OAK_PLANKS.getBlockData()); + IBlockData iblockdata2 = this.a(Blocks.GRAVEL.getBlockData()); + IBlockData iblockdata3 = this.a(Blocks.COBBLESTONE.getBlockData()); + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + this.n.b = 1000; + this.n.e = 0; + + for (int i = this.n.a; i <= this.n.d; ++i) { + for (int j = this.n.c; j <= this.n.f; ++j) { + blockposition_mutableblockposition.c(i, 64, j); + if (structureboundingbox.b((BaseBlockPosition) blockposition_mutableblockposition)) { + int k = generatoraccess.a(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, blockposition_mutableblockposition.getX(), blockposition_mutableblockposition.getZ()); + + blockposition_mutableblockposition.c(blockposition_mutableblockposition.getX(), k, blockposition_mutableblockposition.getZ()).c(EnumDirection.DOWN); + if (blockposition_mutableblockposition.getY() < generatoraccess.getSeaLevel()) { + blockposition_mutableblockposition.p(generatoraccess.getSeaLevel() - 1); + } + + while (blockposition_mutableblockposition.getY() >= generatoraccess.getSeaLevel() - 1) { + IBlockData iblockdata4 = generatoraccess.getType(blockposition_mutableblockposition); + Block block = iblockdata4.getBlock(); + + if (block == Blocks.GRASS_BLOCK && generatoraccess.isEmpty(blockposition_mutableblockposition.up())) { + generatoraccess.setTypeAndData(blockposition_mutableblockposition, iblockdata, 2); + break; + } + + if (iblockdata4.getMaterial().isLiquid()) { + generatoraccess.setTypeAndData(new BlockPosition(blockposition_mutableblockposition), iblockdata1, 2); + break; + } + + if (block == Blocks.SAND || block == Blocks.RED_SAND || block == Blocks.SANDSTONE || block == Blocks.CHISELED_SANDSTONE || block == Blocks.CUT_SANDSTONE || block == Blocks.RED_SANDSTONE || block == Blocks.CHISELED_SANDSTONE || block == Blocks.CUT_SANDSTONE) { + generatoraccess.setTypeAndData(blockposition_mutableblockposition, iblockdata2, 2); + generatoraccess.setTypeAndData(blockposition_mutableblockposition.down(), iblockdata3, 2); + break; + } + + blockposition_mutableblockposition.c(EnumDirection.DOWN); + } + + this.n.b = Math.min(this.n.b, blockposition_mutableblockposition.getY()); + this.n.e = Math.max(this.n.e, blockposition_mutableblockposition.getY()); + } + } + } + + return true; + } + } + + public abstract static class WorldGenVillageRoadPiece extends WorldGenVillagePieces.WorldGenVillagePiece { + + public WorldGenVillageRoadPiece() {} + + protected WorldGenVillageRoadPiece(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, int i) { + super(worldgenvillagepieces_worldgenvillagestartpiece, i); + } + } + + public static class WorldGenVillageStartPiece extends WorldGenVillagePieces.WorldGenVillageWell { + + public int a; + public WorldGenVillagePieces.WorldGenVillagePieceWeight b; + public List c; + public List d = Lists.newArrayList(); + public List e = Lists.newArrayList(); + + public WorldGenVillageStartPiece() {} + + public WorldGenVillageStartPiece(int i, Random random, int j, int k, List list, WorldGenFeatureVillageConfiguration worldgenfeaturevillageconfiguration) { + super((WorldGenVillagePieces.WorldGenVillageStartPiece) null, 0, random, j, k); + this.c = list; + this.a = worldgenfeaturevillageconfiguration.a; + this.g = worldgenfeaturevillageconfiguration.b; + this.a(this.g); + this.h = random.nextInt(50) == 0; + } + } + + public static class WorldGenVillageWell extends WorldGenVillagePieces.WorldGenVillagePiece { + + public WorldGenVillageWell() {} + + public WorldGenVillageWell(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, int i, Random random, int j, int k) { + super(worldgenvillagepieces_worldgenvillagestartpiece, i); + this.a(EnumDirection.EnumDirectionLimit.HORIZONTAL.a(random)); + if (this.f().k() == EnumDirection.EnumAxis.Z) { + this.n = new StructureBoundingBox(j, 64, k, j + 6 - 1, 78, k + 6 - 1); + } else { + this.n = new StructureBoundingBox(j, 64, k, j + 6 - 1, 78, k + 6 - 1); + } + + } + + public void a(StructurePiece structurepiece, List list, Random random) { + WorldGenVillagePieces.e((WorldGenVillagePieces.WorldGenVillageStartPiece) structurepiece, list, random, this.n.a - 1, this.n.e - 4, this.n.c + 1, EnumDirection.WEST, this.e()); + WorldGenVillagePieces.e((WorldGenVillagePieces.WorldGenVillageStartPiece) structurepiece, list, random, this.n.d + 1, this.n.e - 4, this.n.c + 1, EnumDirection.EAST, this.e()); + WorldGenVillagePieces.e((WorldGenVillagePieces.WorldGenVillageStartPiece) structurepiece, list, random, this.n.a + 1, this.n.e - 4, this.n.c - 1, EnumDirection.NORTH, this.e()); + WorldGenVillagePieces.e((WorldGenVillagePieces.WorldGenVillageStartPiece) structurepiece, list, random, this.n.a + 1, this.n.e - 4, this.n.f + 1, EnumDirection.SOUTH, this.e()); + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (this.f < 0) { + this.f = this.a(generatoraccess, structureboundingbox); + if (this.f < 0) { + return true; + } + + this.n.a(0, this.f - this.n.e + 3, 0); + } + + IBlockData iblockdata = this.a(Blocks.COBBLESTONE.getBlockData()); + IBlockData iblockdata1 = this.a(Blocks.OAK_FENCE.getBlockData()); + + this.a(generatoraccess, structureboundingbox, 1, 0, 1, 4, 12, 4, iblockdata, Blocks.WATER.getBlockData(), false); + this.a(generatoraccess, Blocks.AIR.getBlockData(), 2, 12, 2, structureboundingbox); + this.a(generatoraccess, Blocks.AIR.getBlockData(), 3, 12, 2, structureboundingbox); + this.a(generatoraccess, Blocks.AIR.getBlockData(), 2, 12, 3, structureboundingbox); + this.a(generatoraccess, Blocks.AIR.getBlockData(), 3, 12, 3, structureboundingbox); + this.a(generatoraccess, iblockdata1, 1, 13, 1, structureboundingbox); + this.a(generatoraccess, iblockdata1, 1, 14, 1, structureboundingbox); + this.a(generatoraccess, iblockdata1, 4, 13, 1, structureboundingbox); + this.a(generatoraccess, iblockdata1, 4, 14, 1, structureboundingbox); + this.a(generatoraccess, iblockdata1, 1, 13, 4, structureboundingbox); + this.a(generatoraccess, iblockdata1, 1, 14, 4, structureboundingbox); + this.a(generatoraccess, iblockdata1, 4, 13, 4, structureboundingbox); + this.a(generatoraccess, iblockdata1, 4, 14, 4, structureboundingbox); + this.a(generatoraccess, structureboundingbox, 1, 15, 1, 4, 15, 4, iblockdata, iblockdata, false); + + for (int i = 0; i <= 5; ++i) { + for (int j = 0; j <= 5; ++j) { + if (j == 0 || j == 5 || i == 0 || i == 5) { + this.a(generatoraccess, iblockdata, j, 11, i, structureboundingbox); + this.a(generatoraccess, j, 12, i, structureboundingbox); + } + } + } + + return true; + } + } + + abstract static class WorldGenVillagePiece extends StructurePiece { + + protected int f = -1; + private int a; + protected WorldGenVillagePieces.Material g; + protected boolean h; + + public WorldGenVillagePiece() {} + + protected WorldGenVillagePiece(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, int i) { + super(i); + if (worldgenvillagepieces_worldgenvillagestartpiece != null) { + this.g = worldgenvillagepieces_worldgenvillagestartpiece.g; + this.h = worldgenvillagepieces_worldgenvillagestartpiece.h; + } + + } + + protected void a(NBTTagCompound nbttagcompound) { + nbttagcompound.setInt("HPos", this.f); + nbttagcompound.setInt("VCount", this.a); + nbttagcompound.setByte("Type", (byte) this.g.a()); + nbttagcompound.setBoolean("Zombie", this.h); + } + + protected void a(NBTTagCompound nbttagcompound, DefinedStructureManager definedstructuremanager) { + this.f = nbttagcompound.getInt("HPos"); + this.a = nbttagcompound.getInt("VCount"); + this.g = WorldGenVillagePieces.Material.a(nbttagcompound.getByte("Type")); + if (nbttagcompound.getBoolean("Desert")) { + this.g = WorldGenVillagePieces.Material.SANDSTONE; + } + + this.h = nbttagcompound.getBoolean("Zombie"); + } + + @Nullable + protected StructurePiece a(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, List list, Random random, int i, int j) { + EnumDirection enumdirection = this.f(); + + if (enumdirection != null) { + switch (enumdirection) { + case NORTH: + default: + return WorldGenVillagePieces.d(worldgenvillagepieces_worldgenvillagestartpiece, list, random, this.n.a - 1, this.n.b + i, this.n.c + j, EnumDirection.WEST, this.e()); + case SOUTH: + return WorldGenVillagePieces.d(worldgenvillagepieces_worldgenvillagestartpiece, list, random, this.n.a - 1, this.n.b + i, this.n.c + j, EnumDirection.WEST, this.e()); + case WEST: + return WorldGenVillagePieces.d(worldgenvillagepieces_worldgenvillagestartpiece, list, random, this.n.a + j, this.n.b + i, this.n.c - 1, EnumDirection.NORTH, this.e()); + case EAST: + return WorldGenVillagePieces.d(worldgenvillagepieces_worldgenvillagestartpiece, list, random, this.n.a + j, this.n.b + i, this.n.c - 1, EnumDirection.NORTH, this.e()); + } + } else { + return null; + } + } + + @Nullable + protected StructurePiece b(WorldGenVillagePieces.WorldGenVillageStartPiece worldgenvillagepieces_worldgenvillagestartpiece, List list, Random random, int i, int j) { + EnumDirection enumdirection = this.f(); + + if (enumdirection != null) { + switch (enumdirection) { + case NORTH: + default: + return WorldGenVillagePieces.d(worldgenvillagepieces_worldgenvillagestartpiece, list, random, this.n.d + 1, this.n.b + i, this.n.c + j, EnumDirection.EAST, this.e()); + case SOUTH: + return WorldGenVillagePieces.d(worldgenvillagepieces_worldgenvillagestartpiece, list, random, this.n.d + 1, this.n.b + i, this.n.c + j, EnumDirection.EAST, this.e()); + case WEST: + return WorldGenVillagePieces.d(worldgenvillagepieces_worldgenvillagestartpiece, list, random, this.n.a + j, this.n.b + i, this.n.f + 1, EnumDirection.SOUTH, this.e()); + case EAST: + return WorldGenVillagePieces.d(worldgenvillagepieces_worldgenvillagestartpiece, list, random, this.n.a + j, this.n.b + i, this.n.f + 1, EnumDirection.SOUTH, this.e()); + } + } else { + return null; + } + } + + protected int a(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox) { + int i = 0; + int j = 0; + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + for (int k = this.n.c; k <= this.n.f; ++k) { + for (int l = this.n.a; l <= this.n.d; ++l) { + blockposition_mutableblockposition.c(l, 64, k); + if (structureboundingbox.b((BaseBlockPosition) blockposition_mutableblockposition)) { + i += generatoraccess.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, blockposition_mutableblockposition).getY(); + ++j; + } + } + } + + if (j == 0) { + return -1; + } else { + return i / j; + } + } + + protected static boolean a(StructureBoundingBox structureboundingbox) { + return structureboundingbox != null && structureboundingbox.b > 10; + } + + protected void a(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox, int i, int j, int k, int l) { + if (this.a < l) { + for (int i1 = this.a; i1 < l; ++i1) { + int j1 = this.a(i + i1, k); + int k1 = this.d(j); + int l1 = this.b(i + i1, k); + + if (!structureboundingbox.b((BaseBlockPosition) (new BlockPosition(j1, k1, l1)))) { + break; + } + + ++this.a; + if (this.h) { + EntityZombieVillager entityzombievillager = EntityTypes.ZOMBIE_VILLAGER.create(generatoraccess.getMinecraftWorld()); // Paper + + entityzombievillager.setPositionRotation((double) j1 + 0.5D, (double) k1, (double) l1 + 0.5D, 0.0F, 0.0F); + entityzombievillager.prepare(generatoraccess.getDamageScaler(new BlockPosition(entityzombievillager)), (GroupDataEntity) null, (NBTTagCompound) null); + entityzombievillager.setProfession(this.c(i1, 0)); + entityzombievillager.di(); + generatoraccess.addEntity(entityzombievillager, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN); // CraftBukkit - add SpawnReason + } else { + EntityVillager entityvillager = EntityTypes.VILLAGER.create(generatoraccess.getMinecraftWorld()); // Paper + + entityvillager.setPositionRotation((double) j1 + 0.5D, (double) k1, (double) l1 + 0.5D, 0.0F, 0.0F); + entityvillager.setProfession(this.c(i1, generatoraccess.m().nextInt(6))); + entityvillager.a(generatoraccess.getDamageScaler(new BlockPosition(entityvillager)), (GroupDataEntity) null, (NBTTagCompound) null, false); + generatoraccess.addEntity(entityvillager, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN); // CraftBukkit - add SpawnReason + } + } + + } + } + + protected int c(int i, int j) { + return j; + } + + protected IBlockData a(IBlockData iblockdata) { + Block block = iblockdata.getBlock(); + + if (this.g == WorldGenVillagePieces.Material.SANDSTONE) { + if (block.a(TagsBlock.LOGS) || block == Blocks.COBBLESTONE) { + return Blocks.SANDSTONE.getBlockData(); + } + + if (block.a(TagsBlock.PLANKS)) { + return Blocks.CUT_SANDSTONE.getBlockData(); + } + + if (block == Blocks.OAK_STAIRS) { + return (IBlockData) Blocks.SANDSTONE_STAIRS.getBlockData().set(BlockStairs.FACING, iblockdata.get(BlockStairs.FACING)); + } + + if (block == Blocks.COBBLESTONE_STAIRS) { + return (IBlockData) Blocks.SANDSTONE_STAIRS.getBlockData().set(BlockStairs.FACING, iblockdata.get(BlockStairs.FACING)); + } + + if (block == Blocks.GRAVEL) { + return Blocks.SANDSTONE.getBlockData(); + } + + if (block == Blocks.OAK_PRESSURE_PLATE) { + return Blocks.BIRCH_PRESSURE_PLATE.getBlockData(); + } + } else if (this.g == WorldGenVillagePieces.Material.SPRUCE) { + if (block.a(TagsBlock.LOGS)) { + return (IBlockData) Blocks.SPRUCE_LOG.getBlockData().set(BlockLogAbstract.AXIS, iblockdata.get(BlockLogAbstract.AXIS)); + } + + if (block.a(TagsBlock.PLANKS)) { + return Blocks.SPRUCE_PLANKS.getBlockData(); + } + + if (block == Blocks.OAK_STAIRS) { + return (IBlockData) Blocks.SPRUCE_STAIRS.getBlockData().set(BlockStairs.FACING, iblockdata.get(BlockStairs.FACING)); + } + + if (block == Blocks.OAK_FENCE) { + return Blocks.SPRUCE_FENCE.getBlockData(); + } + + if (block == Blocks.OAK_PRESSURE_PLATE) { + return Blocks.SPRUCE_PRESSURE_PLATE.getBlockData(); + } + } else if (this.g == WorldGenVillagePieces.Material.ACACIA) { + if (block.a(TagsBlock.LOGS)) { + return (IBlockData) Blocks.ACACIA_LOG.getBlockData().set(BlockLogAbstract.AXIS, iblockdata.get(BlockLogAbstract.AXIS)); + } + + if (block.a(TagsBlock.PLANKS)) { + return Blocks.ACACIA_PLANKS.getBlockData(); + } + + if (block == Blocks.OAK_STAIRS) { + return (IBlockData) Blocks.ACACIA_STAIRS.getBlockData().set(BlockStairs.FACING, iblockdata.get(BlockStairs.FACING)); + } + + if (block == Blocks.COBBLESTONE) { + return (IBlockData) Blocks.ACACIA_LOG.getBlockData().set(BlockLogAbstract.AXIS, EnumDirection.EnumAxis.Y); + } + + if (block == Blocks.OAK_FENCE) { + return Blocks.ACACIA_FENCE.getBlockData(); + } + + if (block == Blocks.OAK_PRESSURE_PLATE) { + return Blocks.ACACIA_PRESSURE_PLATE.getBlockData(); + } + } + + return iblockdata; + } + + protected BlockDoor b() { + return this.g == WorldGenVillagePieces.Material.ACACIA ? (BlockDoor) Blocks.ACACIA_DOOR : (this.g == WorldGenVillagePieces.Material.SPRUCE ? (BlockDoor) Blocks.SPRUCE_DOOR : (BlockDoor) Blocks.OAK_DOOR); + } + + protected void a(GeneratorAccess generatoraccess, StructureBoundingBox structureboundingbox, Random random, int i, int j, int k, EnumDirection enumdirection) { + if (!this.h) { + this.a(generatoraccess, structureboundingbox, random, i, j, k, EnumDirection.NORTH, this.b()); + } + + } + + protected void a(GeneratorAccess generatoraccess, EnumDirection enumdirection, int i, int j, int k, StructureBoundingBox structureboundingbox) { + if (!this.h) { + this.a(generatoraccess, (IBlockData) Blocks.WALL_TORCH.getBlockData().set(BlockTorchWall.a, enumdirection), i, j, k, structureboundingbox); + } + + } + + protected void b(GeneratorAccess generatoraccess, IBlockData iblockdata, int i, int j, int k, StructureBoundingBox structureboundingbox) { + IBlockData iblockdata1 = this.a(iblockdata); + + super.b(generatoraccess, iblockdata1, i, j, k, structureboundingbox); + } + + protected void a(WorldGenVillagePieces.Material worldgenvillagepieces_material) { + this.g = worldgenvillagepieces_material; + } + } + + public static class WorldGenVillagePieceWeight { + + public Class a; + public final int b; + public int c; + public int d; + + public WorldGenVillagePieceWeight(Class oclass, int i, int j) { + this.a = oclass; + this.b = i; + this.d = j; + } + + public boolean a(int i) { + return this.d == 0 || this.c < this.d; + } + + public boolean a() { + return this.d == 0 || this.c < this.d; + } + } + + public static enum Material { + + OAK(0), SANDSTONE(1), ACACIA(2), SPRUCE(3); + + private final int e; + + private Material(int i) { + this.e = i; + } + + public int a() { + return this.e; + } + + public static WorldGenVillagePieces.Material a(int i) { + WorldGenVillagePieces.Material[] aworldgenvillagepieces_material = values(); + + return i >= 0 && i < aworldgenvillagepieces_material.length ? aworldgenvillagepieces_material[i] : WorldGenVillagePieces.Material.OAK; + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldGenWitchHut.java b/src/main/java/net/minecraft/server/WorldGenWitchHut.java new file mode 100644 index 000000000000..3d8193c477db --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenWitchHut.java @@ -0,0 +1,96 @@ +package net.minecraft.server; + +import java.util.Random; + +public class WorldGenWitchHut extends WorldGenScatteredPiece { + + private boolean e; + + public static void b() { + WorldGenFactory.a(WorldGenWitchHut.class, "TeSH"); + } + + public WorldGenWitchHut() {} + + public WorldGenWitchHut(Random random, int i, int j) { + super(random, i, 64, j, 7, 7, 9); + } + + protected void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + nbttagcompound.setBoolean("Witch", this.e); + } + + protected void a(NBTTagCompound nbttagcompound, DefinedStructureManager definedstructuremanager) { + super.a(nbttagcompound, definedstructuremanager); + this.e = nbttagcompound.getBoolean("Witch"); + } + + public boolean a(GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox, ChunkCoordIntPair chunkcoordintpair) { + if (!this.a(generatoraccess, structureboundingbox, 0)) { + return false; + } else { + this.a(generatoraccess, structureboundingbox, 1, 1, 1, 5, 1, 7, Blocks.SPRUCE_PLANKS.getBlockData(), Blocks.SPRUCE_PLANKS.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 1, 4, 2, 5, 4, 7, Blocks.SPRUCE_PLANKS.getBlockData(), Blocks.SPRUCE_PLANKS.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 2, 1, 0, 4, 1, 0, Blocks.SPRUCE_PLANKS.getBlockData(), Blocks.SPRUCE_PLANKS.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 2, 2, 2, 3, 3, 2, Blocks.SPRUCE_PLANKS.getBlockData(), Blocks.SPRUCE_PLANKS.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 1, 2, 3, 1, 3, 6, Blocks.SPRUCE_PLANKS.getBlockData(), Blocks.SPRUCE_PLANKS.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 5, 2, 3, 5, 3, 6, Blocks.SPRUCE_PLANKS.getBlockData(), Blocks.SPRUCE_PLANKS.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 2, 2, 7, 4, 3, 7, Blocks.SPRUCE_PLANKS.getBlockData(), Blocks.SPRUCE_PLANKS.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 1, 0, 2, 1, 3, 2, Blocks.OAK_LOG.getBlockData(), Blocks.OAK_LOG.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 5, 0, 2, 5, 3, 2, Blocks.OAK_LOG.getBlockData(), Blocks.OAK_LOG.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 1, 0, 7, 1, 3, 7, Blocks.OAK_LOG.getBlockData(), Blocks.OAK_LOG.getBlockData(), false); + this.a(generatoraccess, structureboundingbox, 5, 0, 7, 5, 3, 7, Blocks.OAK_LOG.getBlockData(), Blocks.OAK_LOG.getBlockData(), false); + this.a(generatoraccess, Blocks.OAK_FENCE.getBlockData(), 2, 3, 2, structureboundingbox); + this.a(generatoraccess, Blocks.OAK_FENCE.getBlockData(), 3, 3, 7, structureboundingbox); + this.a(generatoraccess, Blocks.AIR.getBlockData(), 1, 3, 4, structureboundingbox); + this.a(generatoraccess, Blocks.AIR.getBlockData(), 5, 3, 4, structureboundingbox); + this.a(generatoraccess, Blocks.AIR.getBlockData(), 5, 3, 5, structureboundingbox); + this.a(generatoraccess, Blocks.POTTED_RED_MUSHROOM.getBlockData(), 1, 3, 5, structureboundingbox); + this.a(generatoraccess, Blocks.CRAFTING_TABLE.getBlockData(), 3, 2, 6, structureboundingbox); + this.a(generatoraccess, Blocks.CAULDRON.getBlockData(), 4, 2, 6, structureboundingbox); + this.a(generatoraccess, Blocks.OAK_FENCE.getBlockData(), 1, 2, 1, structureboundingbox); + this.a(generatoraccess, Blocks.OAK_FENCE.getBlockData(), 5, 2, 1, structureboundingbox); + IBlockData iblockdata = (IBlockData) Blocks.SPRUCE_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.NORTH); + IBlockData iblockdata1 = (IBlockData) Blocks.SPRUCE_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.EAST); + IBlockData iblockdata2 = (IBlockData) Blocks.SPRUCE_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.WEST); + IBlockData iblockdata3 = (IBlockData) Blocks.SPRUCE_STAIRS.getBlockData().set(BlockStairs.FACING, EnumDirection.SOUTH); + + this.a(generatoraccess, structureboundingbox, 0, 4, 1, 6, 4, 1, iblockdata, iblockdata, false); + this.a(generatoraccess, structureboundingbox, 0, 4, 2, 0, 4, 7, iblockdata1, iblockdata1, false); + this.a(generatoraccess, structureboundingbox, 6, 4, 2, 6, 4, 7, iblockdata2, iblockdata2, false); + this.a(generatoraccess, structureboundingbox, 0, 4, 8, 6, 4, 8, iblockdata3, iblockdata3, false); + this.a(generatoraccess, (IBlockData) iblockdata.set(BlockStairs.SHAPE, BlockPropertyStairsShape.OUTER_RIGHT), 0, 4, 1, structureboundingbox); + this.a(generatoraccess, (IBlockData) iblockdata.set(BlockStairs.SHAPE, BlockPropertyStairsShape.OUTER_LEFT), 6, 4, 1, structureboundingbox); + this.a(generatoraccess, (IBlockData) iblockdata3.set(BlockStairs.SHAPE, BlockPropertyStairsShape.OUTER_LEFT), 0, 4, 8, structureboundingbox); + this.a(generatoraccess, (IBlockData) iblockdata3.set(BlockStairs.SHAPE, BlockPropertyStairsShape.OUTER_RIGHT), 6, 4, 8, structureboundingbox); + + int i; + int j; + + for (j = 2; j <= 7; j += 5) { + for (i = 1; i <= 5; i += 4) { + this.b(generatoraccess, Blocks.OAK_LOG.getBlockData(), i, -1, j, structureboundingbox); + } + } + + if (!this.e) { + j = this.a(2, 5); + i = this.d(2); + int k = this.b(2, 5); + + if (structureboundingbox.b((BaseBlockPosition) (new BlockPosition(j, i, k)))) { + this.e = true; + EntityWitch entitywitch = EntityTypes.WITCH.create(generatoraccess.getMinecraftWorld()); // Paper + + entitywitch.di(); + entitywitch.setPositionRotation((double) j + 0.5D, (double) i, (double) k + 0.5D, 0.0F, 0.0F); + entitywitch.prepare(generatoraccess.getDamageScaler(new BlockPosition(j, i, k)), (GroupDataEntity) null, (NBTTagCompound) null); + generatoraccess.addEntity(entitywitch, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN); // CraftBukkit - add SpawnReason + } + } + + return true; + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldGenWoodlandMansionPieces.java b/src/main/java/net/minecraft/server/WorldGenWoodlandMansionPieces.java new file mode 100644 index 000000000000..4eb746ebb0f8 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldGenWoodlandMansionPieces.java @@ -0,0 +1,1084 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import javax.annotation.Nullable; + +public class WorldGenWoodlandMansionPieces { + + public static void a() { + WorldGenFactory.a(WorldGenWoodlandMansionPieces.i.class, "WMP"); + } + + public static void a(DefinedStructureManager definedstructuremanager, BlockPosition blockposition, EnumBlockRotation enumblockrotation, List list, Random random) { + WorldGenWoodlandMansionPieces.c worldgenwoodlandmansionpieces_c = new WorldGenWoodlandMansionPieces.c(random); + WorldGenWoodlandMansionPieces.d worldgenwoodlandmansionpieces_d = new WorldGenWoodlandMansionPieces.d(definedstructuremanager, random); + + worldgenwoodlandmansionpieces_d.a(blockposition, enumblockrotation, list, worldgenwoodlandmansionpieces_c); + } + + static class h extends WorldGenWoodlandMansionPieces.f { + + private h() { + super(); // Paper - decompile fix + } + } + + static class f extends WorldGenWoodlandMansionPieces.b { + + private f() { + super(); // Paper - decompile fix + } + + public String a(Random random) { + return "1x1_b" + (random.nextInt(4) + 1); + } + + public String b(Random random) { + return "1x1_as" + (random.nextInt(4) + 1); + } + + public String a(Random random, boolean flag) { + return flag ? "1x2_c_stairs" : "1x2_c" + (random.nextInt(4) + 1); + } + + public String b(Random random, boolean flag) { + return flag ? "1x2_d_stairs" : "1x2_d" + (random.nextInt(5) + 1); + } + + public String c(Random random) { + return "1x2_se" + (random.nextInt(1) + 1); + } + + public String d(Random random) { + return "2x2_b" + (random.nextInt(5) + 1); + } + + public String e(Random random) { + return "2x2_s1"; + } + } + + static class a extends WorldGenWoodlandMansionPieces.b { + + private a() { + super(); // Paper - decompile fix + } + + public String a(Random random) { + return "1x1_a" + (random.nextInt(5) + 1); + } + + public String b(Random random) { + return "1x1_as" + (random.nextInt(4) + 1); + } + + public String a(Random random, boolean flag) { + return "1x2_a" + (random.nextInt(9) + 1); + } + + public String b(Random random, boolean flag) { + return "1x2_b" + (random.nextInt(5) + 1); + } + + public String c(Random random) { + return "1x2_s" + (random.nextInt(2) + 1); + } + + public String d(Random random) { + return "2x2_a" + (random.nextInt(4) + 1); + } + + public String e(Random random) { + return "2x2_s1"; + } + } + + abstract static class b { + + private b() {} + + public abstract String a(Random random); + + public abstract String b(Random random); + + public abstract String a(Random random, boolean flag); + + public abstract String b(Random random, boolean flag); + + public abstract String c(Random random); + + public abstract String d(Random random); + + public abstract String e(Random random); + } + + static class g { + + private final int[][] a; + private final int b; + private final int c; + private final int d; + + public g(int i, int j, int k) { + this.b = i; + this.c = j; + this.d = k; + this.a = new int[i][j]; + } + + public void a(int i, int j, int k) { + if (i >= 0 && i < this.b && j >= 0 && j < this.c) { + this.a[i][j] = k; + } + + } + + public void a(int i, int j, int k, int l, int i1) { + for (int j1 = j; j1 <= l; ++j1) { + for (int k1 = i; k1 <= k; ++k1) { + this.a(k1, j1, i1); + } + } + + } + + public int a(int i, int j) { + return i >= 0 && i < this.b && j >= 0 && j < this.c ? this.a[i][j] : this.d; + } + + public void a(int i, int j, int k, int l) { + if (this.a(i, j) == k) { + this.a(i, j, l); + } + + } + + public boolean b(int i, int j, int k) { + return this.a(i - 1, j) == k || this.a(i + 1, j) == k || this.a(i, j + 1) == k || this.a(i, j - 1) == k; + } + } + + static class c { + + private final Random a; + private final WorldGenWoodlandMansionPieces.g b; + private final WorldGenWoodlandMansionPieces.g c; + private final WorldGenWoodlandMansionPieces.g[] d; + private final int e; + private final int f; + + public c(Random random) { + this.a = random; + boolean flag = true; + + this.e = 7; + this.f = 4; + this.b = new WorldGenWoodlandMansionPieces.g(11, 11, 5); + this.b.a(this.e, this.f, this.e + 1, this.f + 1, 3); + this.b.a(this.e - 1, this.f, this.e - 1, this.f + 1, 2); + this.b.a(this.e + 2, this.f - 2, this.e + 3, this.f + 3, 5); + this.b.a(this.e + 1, this.f - 2, this.e + 1, this.f - 1, 1); + this.b.a(this.e + 1, this.f + 2, this.e + 1, this.f + 3, 1); + this.b.a(this.e - 1, this.f - 1, 1); + this.b.a(this.e - 1, this.f + 2, 1); + this.b.a(0, 0, 11, 1, 5); + this.b.a(0, 9, 11, 11, 5); + this.a(this.b, this.e, this.f - 2, EnumDirection.WEST, 6); + this.a(this.b, this.e, this.f + 3, EnumDirection.WEST, 6); + this.a(this.b, this.e - 2, this.f - 1, EnumDirection.WEST, 3); + this.a(this.b, this.e - 2, this.f + 2, EnumDirection.WEST, 3); + + while (this.a(this.b)) { + ; + } + + this.d = new WorldGenWoodlandMansionPieces.g[3]; + this.d[0] = new WorldGenWoodlandMansionPieces.g(11, 11, 5); + this.d[1] = new WorldGenWoodlandMansionPieces.g(11, 11, 5); + this.d[2] = new WorldGenWoodlandMansionPieces.g(11, 11, 5); + this.a(this.b, this.d[0]); + this.a(this.b, this.d[1]); + this.d[0].a(this.e + 1, this.f, this.e + 1, this.f + 1, 8388608); + this.d[1].a(this.e + 1, this.f, this.e + 1, this.f + 1, 8388608); + this.c = new WorldGenWoodlandMansionPieces.g(this.b.b, this.b.c, 5); + this.b(); + this.a(this.c, this.d[2]); + } + + public static boolean a(WorldGenWoodlandMansionPieces.g worldgenwoodlandmansionpieces_g, int i, int j) { + int k = worldgenwoodlandmansionpieces_g.a(i, j); + + return k == 1 || k == 2 || k == 3 || k == 4; + } + + public boolean a(WorldGenWoodlandMansionPieces.g worldgenwoodlandmansionpieces_g, int i, int j, int k, int l) { + return (this.d[k].a(i, j) & '\uffff') == l; + } + + @Nullable + public EnumDirection b(WorldGenWoodlandMansionPieces.g worldgenwoodlandmansionpieces_g, int i, int j, int k, int l) { + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + EnumDirection enumdirection; + + do { + if (!iterator.hasNext()) { + return null; + } + + enumdirection = (EnumDirection) iterator.next(); + } while (!this.a(worldgenwoodlandmansionpieces_g, i + enumdirection.getAdjacentX(), j + enumdirection.getAdjacentZ(), k, l)); + + return enumdirection; + } + + private void a(WorldGenWoodlandMansionPieces.g worldgenwoodlandmansionpieces_g, int i, int j, EnumDirection enumdirection, int k) { + if (k > 0) { + worldgenwoodlandmansionpieces_g.a(i, j, 1); + worldgenwoodlandmansionpieces_g.a(i + enumdirection.getAdjacentX(), j + enumdirection.getAdjacentZ(), 0, 1); + + EnumDirection enumdirection1; + + for (int l = 0; l < 8; ++l) { + enumdirection1 = EnumDirection.fromType2(this.a.nextInt(4)); + if (enumdirection1 != enumdirection.opposite() && (enumdirection1 != EnumDirection.EAST || !this.a.nextBoolean())) { + int i1 = i + enumdirection.getAdjacentX(); + int j1 = j + enumdirection.getAdjacentZ(); + + if (worldgenwoodlandmansionpieces_g.a(i1 + enumdirection1.getAdjacentX(), j1 + enumdirection1.getAdjacentZ()) == 0 && worldgenwoodlandmansionpieces_g.a(i1 + enumdirection1.getAdjacentX() * 2, j1 + enumdirection1.getAdjacentZ() * 2) == 0) { + this.a(worldgenwoodlandmansionpieces_g, i + enumdirection.getAdjacentX() + enumdirection1.getAdjacentX(), j + enumdirection.getAdjacentZ() + enumdirection1.getAdjacentZ(), enumdirection1, k - 1); + break; + } + } + } + + EnumDirection enumdirection2 = enumdirection.e(); + + enumdirection1 = enumdirection.f(); + worldgenwoodlandmansionpieces_g.a(i + enumdirection2.getAdjacentX(), j + enumdirection2.getAdjacentZ(), 0, 2); + worldgenwoodlandmansionpieces_g.a(i + enumdirection1.getAdjacentX(), j + enumdirection1.getAdjacentZ(), 0, 2); + worldgenwoodlandmansionpieces_g.a(i + enumdirection.getAdjacentX() + enumdirection2.getAdjacentX(), j + enumdirection.getAdjacentZ() + enumdirection2.getAdjacentZ(), 0, 2); + worldgenwoodlandmansionpieces_g.a(i + enumdirection.getAdjacentX() + enumdirection1.getAdjacentX(), j + enumdirection.getAdjacentZ() + enumdirection1.getAdjacentZ(), 0, 2); + worldgenwoodlandmansionpieces_g.a(i + enumdirection.getAdjacentX() * 2, j + enumdirection.getAdjacentZ() * 2, 0, 2); + worldgenwoodlandmansionpieces_g.a(i + enumdirection2.getAdjacentX() * 2, j + enumdirection2.getAdjacentZ() * 2, 0, 2); + worldgenwoodlandmansionpieces_g.a(i + enumdirection1.getAdjacentX() * 2, j + enumdirection1.getAdjacentZ() * 2, 0, 2); + } + } + + private boolean a(WorldGenWoodlandMansionPieces.g worldgenwoodlandmansionpieces_g) { + boolean flag = false; + + for (int i = 0; i < worldgenwoodlandmansionpieces_g.c; ++i) { + for (int j = 0; j < worldgenwoodlandmansionpieces_g.b; ++j) { + if (worldgenwoodlandmansionpieces_g.a(j, i) == 0) { + byte b0 = 0; + int k = b0 + (a(worldgenwoodlandmansionpieces_g, j + 1, i) ? 1 : 0); + + k += a(worldgenwoodlandmansionpieces_g, j - 1, i) ? 1 : 0; + k += a(worldgenwoodlandmansionpieces_g, j, i + 1) ? 1 : 0; + k += a(worldgenwoodlandmansionpieces_g, j, i - 1) ? 1 : 0; + if (k >= 3) { + worldgenwoodlandmansionpieces_g.a(j, i, 2); + flag = true; + } else if (k == 2) { + byte b1 = 0; + int l = b1 + (a(worldgenwoodlandmansionpieces_g, j + 1, i + 1) ? 1 : 0); + + l += a(worldgenwoodlandmansionpieces_g, j - 1, i + 1) ? 1 : 0; + l += a(worldgenwoodlandmansionpieces_g, j + 1, i - 1) ? 1 : 0; + l += a(worldgenwoodlandmansionpieces_g, j - 1, i - 1) ? 1 : 0; + if (l <= 1) { + worldgenwoodlandmansionpieces_g.a(j, i, 2); + flag = true; + } + } + } + } + } + + return flag; + } + + private void b() { + List> list = Lists.newArrayList(); + WorldGenWoodlandMansionPieces.g worldgenwoodlandmansionpieces_g = this.d[1]; + + int i; + int j; + + for (int k = 0; k < this.c.c; ++k) { + for (i = 0; i < this.c.b; ++i) { + int l = worldgenwoodlandmansionpieces_g.a(i, k); + + j = l & 983040; + if (j == 131072 && (l & 2097152) == 2097152) { + list.add(new Tuple<>(i, k)); + } + } + } + + if (list.isEmpty()) { + this.c.a(0, 0, this.c.b, this.c.c, 5); + } else { + Tuple tuple = (Tuple) list.get(this.a.nextInt(list.size())); + + i = worldgenwoodlandmansionpieces_g.a((Integer) tuple.a(), (Integer) tuple.b()); + worldgenwoodlandmansionpieces_g.a((Integer) tuple.a(), (Integer) tuple.b(), i | 4194304); + EnumDirection enumdirection = this.b(this.b, (Integer) tuple.a(), (Integer) tuple.b(), 1, i & '\uffff'); + + j = (Integer) tuple.a() + enumdirection.getAdjacentX(); + int i1 = (Integer) tuple.b() + enumdirection.getAdjacentZ(); + + for (int j1 = 0; j1 < this.c.c; ++j1) { + for (int k1 = 0; k1 < this.c.b; ++k1) { + if (!a(this.b, k1, j1)) { + this.c.a(k1, j1, 5); + } else if (k1 == (Integer) tuple.a() && j1 == (Integer) tuple.b()) { + this.c.a(k1, j1, 3); + } else if (k1 == j && j1 == i1) { + this.c.a(k1, j1, 3); + this.d[2].a(k1, j1, 8388608); + } + } + } + + List list1 = Lists.newArrayList(); + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection1 = (EnumDirection) iterator.next(); + + if (this.c.a(j + enumdirection1.getAdjacentX(), i1 + enumdirection1.getAdjacentZ()) == 0) { + list1.add(enumdirection1); + } + } + + if (list1.isEmpty()) { + this.c.a(0, 0, this.c.b, this.c.c, 5); + worldgenwoodlandmansionpieces_g.a((Integer) tuple.a(), (Integer) tuple.b(), i); + } else { + EnumDirection enumdirection2 = (EnumDirection) list1.get(this.a.nextInt(list1.size())); + + this.a(this.c, j + enumdirection2.getAdjacentX(), i1 + enumdirection2.getAdjacentZ(), enumdirection2, 4); + + while (this.a(this.c)) { + ; + } + + } + } + } + + private void a(WorldGenWoodlandMansionPieces.g worldgenwoodlandmansionpieces_g, WorldGenWoodlandMansionPieces.g worldgenwoodlandmansionpieces_g1) { + List> list = Lists.newArrayList(); + + int i; + + for (i = 0; i < worldgenwoodlandmansionpieces_g.c; ++i) { + for (int j = 0; j < worldgenwoodlandmansionpieces_g.b; ++j) { + if (worldgenwoodlandmansionpieces_g.a(j, i) == 2) { + list.add(new Tuple<>(j, i)); + } + } + } + + Collections.shuffle(list, this.a); + i = 10; + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + Tuple tuple = (Tuple) iterator.next(); + int k = (Integer) tuple.a(); + int l = (Integer) tuple.b(); + + if (worldgenwoodlandmansionpieces_g1.a(k, l) == 0) { + int i1 = k; + int j1 = k; + int k1 = l; + int l1 = l; + int i2 = 65536; + + if (worldgenwoodlandmansionpieces_g1.a(k + 1, l) == 0 && worldgenwoodlandmansionpieces_g1.a(k, l + 1) == 0 && worldgenwoodlandmansionpieces_g1.a(k + 1, l + 1) == 0 && worldgenwoodlandmansionpieces_g.a(k + 1, l) == 2 && worldgenwoodlandmansionpieces_g.a(k, l + 1) == 2 && worldgenwoodlandmansionpieces_g.a(k + 1, l + 1) == 2) { + j1 = k + 1; + l1 = l + 1; + i2 = 262144; + } else if (worldgenwoodlandmansionpieces_g1.a(k - 1, l) == 0 && worldgenwoodlandmansionpieces_g1.a(k, l + 1) == 0 && worldgenwoodlandmansionpieces_g1.a(k - 1, l + 1) == 0 && worldgenwoodlandmansionpieces_g.a(k - 1, l) == 2 && worldgenwoodlandmansionpieces_g.a(k, l + 1) == 2 && worldgenwoodlandmansionpieces_g.a(k - 1, l + 1) == 2) { + i1 = k - 1; + l1 = l + 1; + i2 = 262144; + } else if (worldgenwoodlandmansionpieces_g1.a(k - 1, l) == 0 && worldgenwoodlandmansionpieces_g1.a(k, l - 1) == 0 && worldgenwoodlandmansionpieces_g1.a(k - 1, l - 1) == 0 && worldgenwoodlandmansionpieces_g.a(k - 1, l) == 2 && worldgenwoodlandmansionpieces_g.a(k, l - 1) == 2 && worldgenwoodlandmansionpieces_g.a(k - 1, l - 1) == 2) { + i1 = k - 1; + k1 = l - 1; + i2 = 262144; + } else if (worldgenwoodlandmansionpieces_g1.a(k + 1, l) == 0 && worldgenwoodlandmansionpieces_g.a(k + 1, l) == 2) { + j1 = k + 1; + i2 = 131072; + } else if (worldgenwoodlandmansionpieces_g1.a(k, l + 1) == 0 && worldgenwoodlandmansionpieces_g.a(k, l + 1) == 2) { + l1 = l + 1; + i2 = 131072; + } else if (worldgenwoodlandmansionpieces_g1.a(k - 1, l) == 0 && worldgenwoodlandmansionpieces_g.a(k - 1, l) == 2) { + i1 = k - 1; + i2 = 131072; + } else if (worldgenwoodlandmansionpieces_g1.a(k, l - 1) == 0 && worldgenwoodlandmansionpieces_g.a(k, l - 1) == 2) { + k1 = l - 1; + i2 = 131072; + } + + int j2 = this.a.nextBoolean() ? i1 : j1; + int k2 = this.a.nextBoolean() ? k1 : l1; + int l2 = 2097152; + + if (!worldgenwoodlandmansionpieces_g.b(j2, k2, 1)) { + j2 = j2 == i1 ? j1 : i1; + k2 = k2 == k1 ? l1 : k1; + if (!worldgenwoodlandmansionpieces_g.b(j2, k2, 1)) { + k2 = k2 == k1 ? l1 : k1; + if (!worldgenwoodlandmansionpieces_g.b(j2, k2, 1)) { + j2 = j2 == i1 ? j1 : i1; + k2 = k2 == k1 ? l1 : k1; + if (!worldgenwoodlandmansionpieces_g.b(j2, k2, 1)) { + l2 = 0; + j2 = i1; + k2 = k1; + } + } + } + } + + for (int i3 = k1; i3 <= l1; ++i3) { + for (int j3 = i1; j3 <= j1; ++j3) { + if (j3 == j2 && i3 == k2) { + worldgenwoodlandmansionpieces_g1.a(j3, i3, 1048576 | l2 | i2 | i); + } else { + worldgenwoodlandmansionpieces_g1.a(j3, i3, i2 | i); + } + } + } + + ++i; + } + } + + } + } + + static class d { + + private final DefinedStructureManager a; + private final Random b; + private int c; + private int d; + + public d(DefinedStructureManager definedstructuremanager, Random random) { + this.a = definedstructuremanager; + this.b = random; + } + + public void a(BlockPosition blockposition, EnumBlockRotation enumblockrotation, List list, WorldGenWoodlandMansionPieces.c worldgenwoodlandmansionpieces_c) { + WorldGenWoodlandMansionPieces.e worldgenwoodlandmansionpieces_e = new WorldGenWoodlandMansionPieces.e(); + + worldgenwoodlandmansionpieces_e.b = blockposition; + worldgenwoodlandmansionpieces_e.a = enumblockrotation; + worldgenwoodlandmansionpieces_e.c = "wall_flat"; + WorldGenWoodlandMansionPieces.e worldgenwoodlandmansionpieces_e1 = new WorldGenWoodlandMansionPieces.e(); + + this.a(list, worldgenwoodlandmansionpieces_e); + worldgenwoodlandmansionpieces_e1.b = worldgenwoodlandmansionpieces_e.b.up(8); + worldgenwoodlandmansionpieces_e1.a = worldgenwoodlandmansionpieces_e.a; + worldgenwoodlandmansionpieces_e1.c = "wall_window"; + if (!list.isEmpty()) { + ; + } + + WorldGenWoodlandMansionPieces.g worldgenwoodlandmansionpieces_g = worldgenwoodlandmansionpieces_c.b; + WorldGenWoodlandMansionPieces.g worldgenwoodlandmansionpieces_g1 = worldgenwoodlandmansionpieces_c.c; + + this.c = worldgenwoodlandmansionpieces_c.e + 1; + this.d = worldgenwoodlandmansionpieces_c.f + 1; + int i = worldgenwoodlandmansionpieces_c.e + 1; + int j = worldgenwoodlandmansionpieces_c.f; + + this.a(list, worldgenwoodlandmansionpieces_e, worldgenwoodlandmansionpieces_g, EnumDirection.SOUTH, this.c, this.d, i, j); + this.a(list, worldgenwoodlandmansionpieces_e1, worldgenwoodlandmansionpieces_g, EnumDirection.SOUTH, this.c, this.d, i, j); + WorldGenWoodlandMansionPieces.e worldgenwoodlandmansionpieces_e2 = new WorldGenWoodlandMansionPieces.e(); + + worldgenwoodlandmansionpieces_e2.b = worldgenwoodlandmansionpieces_e.b.up(19); + worldgenwoodlandmansionpieces_e2.a = worldgenwoodlandmansionpieces_e.a; + worldgenwoodlandmansionpieces_e2.c = "wall_window"; + boolean flag = false; + + int k; + + for (int l = 0; l < worldgenwoodlandmansionpieces_g1.c && !flag; ++l) { + for (k = worldgenwoodlandmansionpieces_g1.b - 1; k >= 0 && !flag; --k) { + if (WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g1, k, l)) { + worldgenwoodlandmansionpieces_e2.b = worldgenwoodlandmansionpieces_e2.b.shift(enumblockrotation.a(EnumDirection.SOUTH), 8 + (l - this.d) * 8); + worldgenwoodlandmansionpieces_e2.b = worldgenwoodlandmansionpieces_e2.b.shift(enumblockrotation.a(EnumDirection.EAST), (k - this.c) * 8); + this.b(list, worldgenwoodlandmansionpieces_e2); + this.a(list, worldgenwoodlandmansionpieces_e2, worldgenwoodlandmansionpieces_g1, EnumDirection.SOUTH, k, l, k, l); + flag = true; + } + } + } + + this.a(list, blockposition.up(16), enumblockrotation, worldgenwoodlandmansionpieces_g, worldgenwoodlandmansionpieces_g1); + this.a(list, blockposition.up(27), enumblockrotation, worldgenwoodlandmansionpieces_g1, (WorldGenWoodlandMansionPieces.g) null); + if (!list.isEmpty()) { + ; + } + + WorldGenWoodlandMansionPieces.b[] aworldgenwoodlandmansionpieces_b = new WorldGenWoodlandMansionPieces.b[] { new WorldGenWoodlandMansionPieces.a(), new WorldGenWoodlandMansionPieces.f(), new WorldGenWoodlandMansionPieces.h()}; + + for (k = 0; k < 3; ++k) { + BlockPosition blockposition1 = blockposition.up(8 * k + (k == 2 ? 3 : 0)); + WorldGenWoodlandMansionPieces.g worldgenwoodlandmansionpieces_g2 = worldgenwoodlandmansionpieces_c.d[k]; + WorldGenWoodlandMansionPieces.g worldgenwoodlandmansionpieces_g3 = k == 2 ? worldgenwoodlandmansionpieces_g1 : worldgenwoodlandmansionpieces_g; + String s = k == 0 ? "carpet_south_1" : "carpet_south_2"; + String s1 = k == 0 ? "carpet_west_1" : "carpet_west_2"; + + for (int i1 = 0; i1 < worldgenwoodlandmansionpieces_g3.c; ++i1) { + for (int j1 = 0; j1 < worldgenwoodlandmansionpieces_g3.b; ++j1) { + if (worldgenwoodlandmansionpieces_g3.a(j1, i1) == 1) { + BlockPosition blockposition2 = blockposition1.shift(enumblockrotation.a(EnumDirection.SOUTH), 8 + (i1 - this.d) * 8); + + blockposition2 = blockposition2.shift(enumblockrotation.a(EnumDirection.EAST), (j1 - this.c) * 8); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "corridor_floor", blockposition2, enumblockrotation)); + if (worldgenwoodlandmansionpieces_g3.a(j1, i1 - 1) == 1 || (worldgenwoodlandmansionpieces_g2.a(j1, i1 - 1) & 8388608) == 8388608) { + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "carpet_north", blockposition2.shift(enumblockrotation.a(EnumDirection.EAST), 1).up(), enumblockrotation)); + } + + if (worldgenwoodlandmansionpieces_g3.a(j1 + 1, i1) == 1 || (worldgenwoodlandmansionpieces_g2.a(j1 + 1, i1) & 8388608) == 8388608) { + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "carpet_east", blockposition2.shift(enumblockrotation.a(EnumDirection.SOUTH), 1).shift(enumblockrotation.a(EnumDirection.EAST), 5).up(), enumblockrotation)); + } + + if (worldgenwoodlandmansionpieces_g3.a(j1, i1 + 1) == 1 || (worldgenwoodlandmansionpieces_g2.a(j1, i1 + 1) & 8388608) == 8388608) { + list.add(new WorldGenWoodlandMansionPieces.i(this.a, s, blockposition2.shift(enumblockrotation.a(EnumDirection.SOUTH), 5).shift(enumblockrotation.a(EnumDirection.WEST), 1), enumblockrotation)); + } + + if (worldgenwoodlandmansionpieces_g3.a(j1 - 1, i1) == 1 || (worldgenwoodlandmansionpieces_g2.a(j1 - 1, i1) & 8388608) == 8388608) { + list.add(new WorldGenWoodlandMansionPieces.i(this.a, s1, blockposition2.shift(enumblockrotation.a(EnumDirection.WEST), 1).shift(enumblockrotation.a(EnumDirection.NORTH), 1), enumblockrotation)); + } + } + } + } + + String s2 = k == 0 ? "indoors_wall_1" : "indoors_wall_2"; + String s3 = k == 0 ? "indoors_door_1" : "indoors_door_2"; + List list1 = Lists.newArrayList(); + + for (int k1 = 0; k1 < worldgenwoodlandmansionpieces_g3.c; ++k1) { + for (int l1 = 0; l1 < worldgenwoodlandmansionpieces_g3.b; ++l1) { + boolean flag1 = k == 2 && worldgenwoodlandmansionpieces_g3.a(l1, k1) == 3; + + if (worldgenwoodlandmansionpieces_g3.a(l1, k1) == 2 || flag1) { + int i2 = worldgenwoodlandmansionpieces_g2.a(l1, k1); + int j2 = i2 & 983040; + int k2 = i2 & '\uffff'; + + flag1 = flag1 && (i2 & 8388608) == 8388608; + list1.clear(); + if ((i2 & 2097152) == 2097152) { + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + + if (worldgenwoodlandmansionpieces_g3.a(l1 + enumdirection.getAdjacentX(), k1 + enumdirection.getAdjacentZ()) == 1) { + list1.add(enumdirection); + } + } + } + + EnumDirection enumdirection1 = null; + + if (!list1.isEmpty()) { + enumdirection1 = (EnumDirection) list1.get(this.b.nextInt(list1.size())); + } else if ((i2 & 1048576) == 1048576) { + enumdirection1 = EnumDirection.UP; + } + + BlockPosition blockposition3 = blockposition1.shift(enumblockrotation.a(EnumDirection.SOUTH), 8 + (k1 - this.d) * 8); + + blockposition3 = blockposition3.shift(enumblockrotation.a(EnumDirection.EAST), -1 + (l1 - this.c) * 8); + if (WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g3, l1 - 1, k1) && !worldgenwoodlandmansionpieces_c.a(worldgenwoodlandmansionpieces_g3, l1 - 1, k1, k, k2)) { + list.add(new WorldGenWoodlandMansionPieces.i(this.a, enumdirection1 == EnumDirection.WEST ? s3 : s2, blockposition3, enumblockrotation)); + } + + BlockPosition blockposition4; + + if (worldgenwoodlandmansionpieces_g3.a(l1 + 1, k1) == 1 && !flag1) { + blockposition4 = blockposition3.shift(enumblockrotation.a(EnumDirection.EAST), 8); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, enumdirection1 == EnumDirection.EAST ? s3 : s2, blockposition4, enumblockrotation)); + } + + if (WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g3, l1, k1 + 1) && !worldgenwoodlandmansionpieces_c.a(worldgenwoodlandmansionpieces_g3, l1, k1 + 1, k, k2)) { + blockposition4 = blockposition3.shift(enumblockrotation.a(EnumDirection.SOUTH), 7); + blockposition4 = blockposition4.shift(enumblockrotation.a(EnumDirection.EAST), 7); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, enumdirection1 == EnumDirection.SOUTH ? s3 : s2, blockposition4, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_90))); + } + + if (worldgenwoodlandmansionpieces_g3.a(l1, k1 - 1) == 1 && !flag1) { + blockposition4 = blockposition3.shift(enumblockrotation.a(EnumDirection.NORTH), 1); + blockposition4 = blockposition4.shift(enumblockrotation.a(EnumDirection.EAST), 7); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, enumdirection1 == EnumDirection.NORTH ? s3 : s2, blockposition4, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_90))); + } + + if (j2 == 65536) { + this.a(list, blockposition3, enumblockrotation, enumdirection1, aworldgenwoodlandmansionpieces_b[k]); + } else { + EnumDirection enumdirection2; + + if (j2 == 131072 && enumdirection1 != null) { + enumdirection2 = worldgenwoodlandmansionpieces_c.b(worldgenwoodlandmansionpieces_g3, l1, k1, k, k2); + boolean flag2 = (i2 & 4194304) == 4194304; + + this.a(list, blockposition3, enumblockrotation, enumdirection2, enumdirection1, aworldgenwoodlandmansionpieces_b[k], flag2); + } else if (j2 == 262144 && enumdirection1 != null && enumdirection1 != EnumDirection.UP) { + enumdirection2 = enumdirection1.e(); + if (!worldgenwoodlandmansionpieces_c.a(worldgenwoodlandmansionpieces_g3, l1 + enumdirection2.getAdjacentX(), k1 + enumdirection2.getAdjacentZ(), k, k2)) { + enumdirection2 = enumdirection2.opposite(); + } + + this.a(list, blockposition3, enumblockrotation, enumdirection2, enumdirection1, aworldgenwoodlandmansionpieces_b[k]); + } else if (j2 == 262144 && enumdirection1 == EnumDirection.UP) { + this.a(list, blockposition3, enumblockrotation, aworldgenwoodlandmansionpieces_b[k]); + } + } + } + } + } + } + + } + + private void a(List list, WorldGenWoodlandMansionPieces.e worldgenwoodlandmansionpieces_e, WorldGenWoodlandMansionPieces.g worldgenwoodlandmansionpieces_g, EnumDirection enumdirection, int i, int j, int k, int l) { + int i1 = i; + int j1 = j; + EnumDirection enumdirection1 = enumdirection; + + do { + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, i1 + enumdirection.getAdjacentX(), j1 + enumdirection.getAdjacentZ())) { + this.c(list, worldgenwoodlandmansionpieces_e); + enumdirection = enumdirection.e(); + if (i1 != k || j1 != l || enumdirection1 != enumdirection) { + this.b(list, worldgenwoodlandmansionpieces_e); + } + } else if (WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, i1 + enumdirection.getAdjacentX(), j1 + enumdirection.getAdjacentZ()) && WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, i1 + enumdirection.getAdjacentX() + enumdirection.f().getAdjacentX(), j1 + enumdirection.getAdjacentZ() + enumdirection.f().getAdjacentZ())) { + this.d(list, worldgenwoodlandmansionpieces_e); + i1 += enumdirection.getAdjacentX(); + j1 += enumdirection.getAdjacentZ(); + enumdirection = enumdirection.f(); + } else { + i1 += enumdirection.getAdjacentX(); + j1 += enumdirection.getAdjacentZ(); + if (i1 != k || j1 != l || enumdirection1 != enumdirection) { + this.b(list, worldgenwoodlandmansionpieces_e); + } + } + } while (i1 != k || j1 != l || enumdirection1 != enumdirection); + + } + + private void a(List list, BlockPosition blockposition, EnumBlockRotation enumblockrotation, WorldGenWoodlandMansionPieces.g worldgenwoodlandmansionpieces_g, @Nullable WorldGenWoodlandMansionPieces.g worldgenwoodlandmansionpieces_g1) { + BlockPosition blockposition1; + int i; + int j; + boolean flag; + BlockPosition blockposition2; + + for (i = 0; i < worldgenwoodlandmansionpieces_g.c; ++i) { + for (j = 0; j < worldgenwoodlandmansionpieces_g.b; ++j) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.SOUTH), 8 + (i - this.d) * 8); + blockposition1 = blockposition1.shift(enumblockrotation.a(EnumDirection.EAST), (j - this.c) * 8); + flag = worldgenwoodlandmansionpieces_g1 != null && WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g1, j, i); + if (WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j, i) && !flag) { + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "roof", blockposition1.up(3), enumblockrotation)); + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j + 1, i)) { + blockposition2 = blockposition1.shift(enumblockrotation.a(EnumDirection.EAST), 6); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "roof_front", blockposition2, enumblockrotation)); + } + + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j - 1, i)) { + blockposition2 = blockposition1.shift(enumblockrotation.a(EnumDirection.EAST), 0); + blockposition2 = blockposition2.shift(enumblockrotation.a(EnumDirection.SOUTH), 7); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "roof_front", blockposition2, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_180))); + } + + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j, i - 1)) { + blockposition2 = blockposition1.shift(enumblockrotation.a(EnumDirection.WEST), 1); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "roof_front", blockposition2, enumblockrotation.a(EnumBlockRotation.COUNTERCLOCKWISE_90))); + } + + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j, i + 1)) { + blockposition2 = blockposition1.shift(enumblockrotation.a(EnumDirection.EAST), 6); + blockposition2 = blockposition2.shift(enumblockrotation.a(EnumDirection.SOUTH), 6); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "roof_front", blockposition2, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_90))); + } + } + } + } + + if (worldgenwoodlandmansionpieces_g1 != null) { + for (i = 0; i < worldgenwoodlandmansionpieces_g.c; ++i) { + for (j = 0; j < worldgenwoodlandmansionpieces_g.b; ++j) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.SOUTH), 8 + (i - this.d) * 8); + blockposition1 = blockposition1.shift(enumblockrotation.a(EnumDirection.EAST), (j - this.c) * 8); + flag = WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g1, j, i); + if (WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j, i) && flag) { + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j + 1, i)) { + blockposition2 = blockposition1.shift(enumblockrotation.a(EnumDirection.EAST), 7); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "small_wall", blockposition2, enumblockrotation)); + } + + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j - 1, i)) { + blockposition2 = blockposition1.shift(enumblockrotation.a(EnumDirection.WEST), 1); + blockposition2 = blockposition2.shift(enumblockrotation.a(EnumDirection.SOUTH), 6); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "small_wall", blockposition2, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_180))); + } + + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j, i - 1)) { + blockposition2 = blockposition1.shift(enumblockrotation.a(EnumDirection.WEST), 0); + blockposition2 = blockposition2.shift(enumblockrotation.a(EnumDirection.NORTH), 1); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "small_wall", blockposition2, enumblockrotation.a(EnumBlockRotation.COUNTERCLOCKWISE_90))); + } + + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j, i + 1)) { + blockposition2 = blockposition1.shift(enumblockrotation.a(EnumDirection.EAST), 6); + blockposition2 = blockposition2.shift(enumblockrotation.a(EnumDirection.SOUTH), 7); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "small_wall", blockposition2, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_90))); + } + + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j + 1, i)) { + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j, i - 1)) { + blockposition2 = blockposition1.shift(enumblockrotation.a(EnumDirection.EAST), 7); + blockposition2 = blockposition2.shift(enumblockrotation.a(EnumDirection.NORTH), 2); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "small_wall_corner", blockposition2, enumblockrotation)); + } + + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j, i + 1)) { + blockposition2 = blockposition1.shift(enumblockrotation.a(EnumDirection.EAST), 8); + blockposition2 = blockposition2.shift(enumblockrotation.a(EnumDirection.SOUTH), 7); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "small_wall_corner", blockposition2, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_90))); + } + } + + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j - 1, i)) { + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j, i - 1)) { + blockposition2 = blockposition1.shift(enumblockrotation.a(EnumDirection.WEST), 2); + blockposition2 = blockposition2.shift(enumblockrotation.a(EnumDirection.NORTH), 1); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "small_wall_corner", blockposition2, enumblockrotation.a(EnumBlockRotation.COUNTERCLOCKWISE_90))); + } + + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j, i + 1)) { + blockposition2 = blockposition1.shift(enumblockrotation.a(EnumDirection.WEST), 1); + blockposition2 = blockposition2.shift(enumblockrotation.a(EnumDirection.SOUTH), 8); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "small_wall_corner", blockposition2, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_180))); + } + } + } + } + } + } + + for (i = 0; i < worldgenwoodlandmansionpieces_g.c; ++i) { + for (j = 0; j < worldgenwoodlandmansionpieces_g.b; ++j) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.SOUTH), 8 + (i - this.d) * 8); + blockposition1 = blockposition1.shift(enumblockrotation.a(EnumDirection.EAST), (j - this.c) * 8); + flag = worldgenwoodlandmansionpieces_g1 != null && WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g1, j, i); + if (WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j, i) && !flag) { + BlockPosition blockposition3; + + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j + 1, i)) { + blockposition2 = blockposition1.shift(enumblockrotation.a(EnumDirection.EAST), 6); + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j, i + 1)) { + blockposition3 = blockposition2.shift(enumblockrotation.a(EnumDirection.SOUTH), 6); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "roof_corner", blockposition3, enumblockrotation)); + } else if (WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j + 1, i + 1)) { + blockposition3 = blockposition2.shift(enumblockrotation.a(EnumDirection.SOUTH), 5); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "roof_inner_corner", blockposition3, enumblockrotation)); + } + + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j, i - 1)) { + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "roof_corner", blockposition2, enumblockrotation.a(EnumBlockRotation.COUNTERCLOCKWISE_90))); + } else if (WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j + 1, i - 1)) { + blockposition3 = blockposition1.shift(enumblockrotation.a(EnumDirection.EAST), 9); + blockposition3 = blockposition3.shift(enumblockrotation.a(EnumDirection.NORTH), 2); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "roof_inner_corner", blockposition3, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_90))); + } + } + + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j - 1, i)) { + blockposition2 = blockposition1.shift(enumblockrotation.a(EnumDirection.EAST), 0); + blockposition2 = blockposition2.shift(enumblockrotation.a(EnumDirection.SOUTH), 0); + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j, i + 1)) { + blockposition3 = blockposition2.shift(enumblockrotation.a(EnumDirection.SOUTH), 6); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "roof_corner", blockposition3, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_90))); + } else if (WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j - 1, i + 1)) { + blockposition3 = blockposition2.shift(enumblockrotation.a(EnumDirection.SOUTH), 8); + blockposition3 = blockposition3.shift(enumblockrotation.a(EnumDirection.WEST), 3); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "roof_inner_corner", blockposition3, enumblockrotation.a(EnumBlockRotation.COUNTERCLOCKWISE_90))); + } + + if (!WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j, i - 1)) { + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "roof_corner", blockposition2, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_180))); + } else if (WorldGenWoodlandMansionPieces.c.a(worldgenwoodlandmansionpieces_g, j - 1, i - 1)) { + blockposition3 = blockposition2.shift(enumblockrotation.a(EnumDirection.SOUTH), 1); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "roof_inner_corner", blockposition3, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_180))); + } + } + } + } + } + + } + + private void a(List list, WorldGenWoodlandMansionPieces.e worldgenwoodlandmansionpieces_e) { + EnumDirection enumdirection = worldgenwoodlandmansionpieces_e.a.a(EnumDirection.WEST); + + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "entrance", worldgenwoodlandmansionpieces_e.b.shift(enumdirection, 9), worldgenwoodlandmansionpieces_e.a)); + worldgenwoodlandmansionpieces_e.b = worldgenwoodlandmansionpieces_e.b.shift(worldgenwoodlandmansionpieces_e.a.a(EnumDirection.SOUTH), 16); + } + + private void b(List list, WorldGenWoodlandMansionPieces.e worldgenwoodlandmansionpieces_e) { + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_e.c, worldgenwoodlandmansionpieces_e.b.shift(worldgenwoodlandmansionpieces_e.a.a(EnumDirection.EAST), 7), worldgenwoodlandmansionpieces_e.a)); + worldgenwoodlandmansionpieces_e.b = worldgenwoodlandmansionpieces_e.b.shift(worldgenwoodlandmansionpieces_e.a.a(EnumDirection.SOUTH), 8); + } + + private void c(List list, WorldGenWoodlandMansionPieces.e worldgenwoodlandmansionpieces_e) { + worldgenwoodlandmansionpieces_e.b = worldgenwoodlandmansionpieces_e.b.shift(worldgenwoodlandmansionpieces_e.a.a(EnumDirection.SOUTH), -1); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, "wall_corner", worldgenwoodlandmansionpieces_e.b, worldgenwoodlandmansionpieces_e.a)); + worldgenwoodlandmansionpieces_e.b = worldgenwoodlandmansionpieces_e.b.shift(worldgenwoodlandmansionpieces_e.a.a(EnumDirection.SOUTH), -7); + worldgenwoodlandmansionpieces_e.b = worldgenwoodlandmansionpieces_e.b.shift(worldgenwoodlandmansionpieces_e.a.a(EnumDirection.WEST), -6); + worldgenwoodlandmansionpieces_e.a = worldgenwoodlandmansionpieces_e.a.a(EnumBlockRotation.CLOCKWISE_90); + } + + private void d(List list, WorldGenWoodlandMansionPieces.e worldgenwoodlandmansionpieces_e) { + worldgenwoodlandmansionpieces_e.b = worldgenwoodlandmansionpieces_e.b.shift(worldgenwoodlandmansionpieces_e.a.a(EnumDirection.SOUTH), 6); + worldgenwoodlandmansionpieces_e.b = worldgenwoodlandmansionpieces_e.b.shift(worldgenwoodlandmansionpieces_e.a.a(EnumDirection.EAST), 8); + worldgenwoodlandmansionpieces_e.a = worldgenwoodlandmansionpieces_e.a.a(EnumBlockRotation.COUNTERCLOCKWISE_90); + } + + private void a(List list, BlockPosition blockposition, EnumBlockRotation enumblockrotation, EnumDirection enumdirection, WorldGenWoodlandMansionPieces.b worldgenwoodlandmansionpieces_b) { + EnumBlockRotation enumblockrotation1 = EnumBlockRotation.NONE; + String s = worldgenwoodlandmansionpieces_b.a(this.b); + + if (enumdirection != EnumDirection.EAST) { + if (enumdirection == EnumDirection.NORTH) { + enumblockrotation1 = enumblockrotation1.a(EnumBlockRotation.COUNTERCLOCKWISE_90); + } else if (enumdirection == EnumDirection.WEST) { + enumblockrotation1 = enumblockrotation1.a(EnumBlockRotation.CLOCKWISE_180); + } else if (enumdirection == EnumDirection.SOUTH) { + enumblockrotation1 = enumblockrotation1.a(EnumBlockRotation.CLOCKWISE_90); + } else { + s = worldgenwoodlandmansionpieces_b.b(this.b); + } + } + + BlockPosition blockposition1 = DefinedStructure.a(new BlockPosition(1, 0, 0), EnumBlockMirror.NONE, enumblockrotation1, 7, 7); + + enumblockrotation1 = enumblockrotation1.a(enumblockrotation); + blockposition1 = blockposition1.a(enumblockrotation); + BlockPosition blockposition2 = blockposition.a(blockposition1.getX(), 0, blockposition1.getZ()); + + list.add(new WorldGenWoodlandMansionPieces.i(this.a, s, blockposition2, enumblockrotation1)); + } + + private void a(List list, BlockPosition blockposition, EnumBlockRotation enumblockrotation, EnumDirection enumdirection, EnumDirection enumdirection1, WorldGenWoodlandMansionPieces.b worldgenwoodlandmansionpieces_b, boolean flag) { + BlockPosition blockposition1; + + if (enumdirection1 == EnumDirection.EAST && enumdirection == EnumDirection.SOUTH) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.EAST), 1); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_b.a(this.b, flag), blockposition1, enumblockrotation)); + } else if (enumdirection1 == EnumDirection.EAST && enumdirection == EnumDirection.NORTH) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.EAST), 1); + blockposition1 = blockposition1.shift(enumblockrotation.a(EnumDirection.SOUTH), 6); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_b.a(this.b, flag), blockposition1, enumblockrotation, EnumBlockMirror.LEFT_RIGHT)); + } else if (enumdirection1 == EnumDirection.WEST && enumdirection == EnumDirection.NORTH) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.EAST), 7); + blockposition1 = blockposition1.shift(enumblockrotation.a(EnumDirection.SOUTH), 6); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_b.a(this.b, flag), blockposition1, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_180))); + } else if (enumdirection1 == EnumDirection.WEST && enumdirection == EnumDirection.SOUTH) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.EAST), 7); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_b.a(this.b, flag), blockposition1, enumblockrotation, EnumBlockMirror.FRONT_BACK)); + } else if (enumdirection1 == EnumDirection.SOUTH && enumdirection == EnumDirection.EAST) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.EAST), 1); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_b.a(this.b, flag), blockposition1, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_90), EnumBlockMirror.LEFT_RIGHT)); + } else if (enumdirection1 == EnumDirection.SOUTH && enumdirection == EnumDirection.WEST) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.EAST), 7); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_b.a(this.b, flag), blockposition1, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_90))); + } else if (enumdirection1 == EnumDirection.NORTH && enumdirection == EnumDirection.WEST) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.EAST), 7); + blockposition1 = blockposition1.shift(enumblockrotation.a(EnumDirection.SOUTH), 6); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_b.a(this.b, flag), blockposition1, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_90), EnumBlockMirror.FRONT_BACK)); + } else if (enumdirection1 == EnumDirection.NORTH && enumdirection == EnumDirection.EAST) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.EAST), 1); + blockposition1 = blockposition1.shift(enumblockrotation.a(EnumDirection.SOUTH), 6); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_b.a(this.b, flag), blockposition1, enumblockrotation.a(EnumBlockRotation.COUNTERCLOCKWISE_90))); + } else if (enumdirection1 == EnumDirection.SOUTH && enumdirection == EnumDirection.NORTH) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.EAST), 1); + blockposition1 = blockposition1.shift(enumblockrotation.a(EnumDirection.NORTH), 8); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_b.b(this.b, flag), blockposition1, enumblockrotation)); + } else if (enumdirection1 == EnumDirection.NORTH && enumdirection == EnumDirection.SOUTH) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.EAST), 7); + blockposition1 = blockposition1.shift(enumblockrotation.a(EnumDirection.SOUTH), 14); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_b.b(this.b, flag), blockposition1, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_180))); + } else if (enumdirection1 == EnumDirection.WEST && enumdirection == EnumDirection.EAST) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.EAST), 15); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_b.b(this.b, flag), blockposition1, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_90))); + } else if (enumdirection1 == EnumDirection.EAST && enumdirection == EnumDirection.WEST) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.WEST), 7); + blockposition1 = blockposition1.shift(enumblockrotation.a(EnumDirection.SOUTH), 6); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_b.b(this.b, flag), blockposition1, enumblockrotation.a(EnumBlockRotation.COUNTERCLOCKWISE_90))); + } else if (enumdirection1 == EnumDirection.UP && enumdirection == EnumDirection.EAST) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.EAST), 15); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_b.c(this.b), blockposition1, enumblockrotation.a(EnumBlockRotation.CLOCKWISE_90))); + } else if (enumdirection1 == EnumDirection.UP && enumdirection == EnumDirection.SOUTH) { + blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.EAST), 1); + blockposition1 = blockposition1.shift(enumblockrotation.a(EnumDirection.NORTH), 0); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_b.c(this.b), blockposition1, enumblockrotation)); + } + + } + + private void a(List list, BlockPosition blockposition, EnumBlockRotation enumblockrotation, EnumDirection enumdirection, EnumDirection enumdirection1, WorldGenWoodlandMansionPieces.b worldgenwoodlandmansionpieces_b) { + byte b0 = 0; + byte b1 = 0; + EnumBlockRotation enumblockrotation1 = enumblockrotation; + EnumBlockMirror enumblockmirror = EnumBlockMirror.NONE; + + if (enumdirection1 == EnumDirection.EAST && enumdirection == EnumDirection.SOUTH) { + b0 = -7; + } else if (enumdirection1 == EnumDirection.EAST && enumdirection == EnumDirection.NORTH) { + b0 = -7; + b1 = 6; + enumblockmirror = EnumBlockMirror.LEFT_RIGHT; + } else if (enumdirection1 == EnumDirection.NORTH && enumdirection == EnumDirection.EAST) { + b0 = 1; + b1 = 14; + enumblockrotation1 = enumblockrotation.a(EnumBlockRotation.COUNTERCLOCKWISE_90); + } else if (enumdirection1 == EnumDirection.NORTH && enumdirection == EnumDirection.WEST) { + b0 = 7; + b1 = 14; + enumblockrotation1 = enumblockrotation.a(EnumBlockRotation.COUNTERCLOCKWISE_90); + enumblockmirror = EnumBlockMirror.LEFT_RIGHT; + } else if (enumdirection1 == EnumDirection.SOUTH && enumdirection == EnumDirection.WEST) { + b0 = 7; + b1 = -8; + enumblockrotation1 = enumblockrotation.a(EnumBlockRotation.CLOCKWISE_90); + } else if (enumdirection1 == EnumDirection.SOUTH && enumdirection == EnumDirection.EAST) { + b0 = 1; + b1 = -8; + enumblockrotation1 = enumblockrotation.a(EnumBlockRotation.CLOCKWISE_90); + enumblockmirror = EnumBlockMirror.LEFT_RIGHT; + } else if (enumdirection1 == EnumDirection.WEST && enumdirection == EnumDirection.NORTH) { + b0 = 15; + b1 = 6; + enumblockrotation1 = enumblockrotation.a(EnumBlockRotation.CLOCKWISE_180); + } else if (enumdirection1 == EnumDirection.WEST && enumdirection == EnumDirection.SOUTH) { + b0 = 15; + enumblockmirror = EnumBlockMirror.FRONT_BACK; + } + + BlockPosition blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.EAST), b0); + + blockposition1 = blockposition1.shift(enumblockrotation.a(EnumDirection.SOUTH), b1); + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_b.d(this.b), blockposition1, enumblockrotation1, enumblockmirror)); + } + + private void a(List list, BlockPosition blockposition, EnumBlockRotation enumblockrotation, WorldGenWoodlandMansionPieces.b worldgenwoodlandmansionpieces_b) { + BlockPosition blockposition1 = blockposition.shift(enumblockrotation.a(EnumDirection.EAST), 1); + + list.add(new WorldGenWoodlandMansionPieces.i(this.a, worldgenwoodlandmansionpieces_b.e(this.b), blockposition1, enumblockrotation, EnumBlockMirror.NONE)); + } + } + + static class e { + + public EnumBlockRotation a; + public BlockPosition b; + public String c; + + private e() {} + } + + public static class i extends DefinedStructurePiece { + + private String d; + private EnumBlockRotation e; + private EnumBlockMirror f; + + public i() {} + + public i(DefinedStructureManager definedstructuremanager, String s, BlockPosition blockposition, EnumBlockRotation enumblockrotation) { + this(definedstructuremanager, s, blockposition, enumblockrotation, EnumBlockMirror.NONE); + } + + public i(DefinedStructureManager definedstructuremanager, String s, BlockPosition blockposition, EnumBlockRotation enumblockrotation, EnumBlockMirror enumblockmirror) { + super(0); + this.d = s; + this.c = blockposition; + this.e = enumblockrotation; + this.f = enumblockmirror; + this.a(definedstructuremanager); + } + + private void a(DefinedStructureManager definedstructuremanager) { + DefinedStructure definedstructure = definedstructuremanager.a(new MinecraftKey("woodland_mansion/" + this.d)); + DefinedStructureInfo definedstructureinfo = (new DefinedStructureInfo()).a(true).a(this.e).a(this.f); + + this.a(definedstructure, this.c, definedstructureinfo); + } + + protected void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + nbttagcompound.setString("Template", this.d); + nbttagcompound.setString("Rot", this.b.c().name()); + nbttagcompound.setString("Mi", this.b.b().name()); + } + + protected void a(NBTTagCompound nbttagcompound, DefinedStructureManager definedstructuremanager) { + super.a(nbttagcompound, definedstructuremanager); + this.d = nbttagcompound.getString("Template"); + this.e = EnumBlockRotation.valueOf(nbttagcompound.getString("Rot")); + this.f = EnumBlockMirror.valueOf(nbttagcompound.getString("Mi")); + this.a(definedstructuremanager); + } + + protected void a(String s, BlockPosition blockposition, GeneratorAccess generatoraccess, Random random, StructureBoundingBox structureboundingbox) { + if (s.startsWith("Chest")) { + EnumBlockRotation enumblockrotation = this.b.c(); + IBlockData iblockdata = Blocks.CHEST.getBlockData(); + + if ("ChestWest".equals(s)) { + iblockdata = (IBlockData) iblockdata.set(BlockChest.FACING, enumblockrotation.a(EnumDirection.WEST)); + } else if ("ChestEast".equals(s)) { + iblockdata = (IBlockData) iblockdata.set(BlockChest.FACING, enumblockrotation.a(EnumDirection.EAST)); + } else if ("ChestSouth".equals(s)) { + iblockdata = (IBlockData) iblockdata.set(BlockChest.FACING, enumblockrotation.a(EnumDirection.SOUTH)); + } else if ("ChestNorth".equals(s)) { + iblockdata = (IBlockData) iblockdata.set(BlockChest.FACING, enumblockrotation.a(EnumDirection.NORTH)); + } + + this.a(generatoraccess, structureboundingbox, random, blockposition, LootTables.o, iblockdata); + } else if ("Mage".equals(s)) { + EntityEvoker entityevoker = EntityTypes.EVOKER.create(generatoraccess.getMinecraftWorld()); // Paper + entityevoker.di(); + entityevoker.setPositionRotation(blockposition, 0.0F, 0.0F); + generatoraccess.addEntity(entityevoker); + generatoraccess.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 2); + } else if ("Warrior".equals(s)) { + EntityVindicator entityvindicator = EntityTypes.VINDICATOR.create(generatoraccess.getMinecraftWorld()); // Paper + entityvindicator.di(); + entityvindicator.setPositionRotation(blockposition, 0.0F, 0.0F); + entityvindicator.prepare(generatoraccess.getDamageScaler(new BlockPosition(entityvindicator)), (GroupDataEntity) null, (NBTTagCompound) null); + generatoraccess.addEntity(entityvindicator); + generatoraccess.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 2); + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldManager.java b/src/main/java/net/minecraft/server/WorldManager.java new file mode 100644 index 000000000000..0ba0eb661bdc --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldManager.java @@ -0,0 +1,97 @@ +package net.minecraft.server; + +import java.util.Iterator; +import javax.annotation.Nullable; + +public class WorldManager implements IWorldAccess { + + private final MinecraftServer a; + private final WorldServer world; + + public WorldManager(MinecraftServer minecraftserver, WorldServer worldserver) { + this.a = minecraftserver; + this.world = worldserver; + } + + public void a(ParticleParam particleparam, boolean flag, double d0, double d1, double d2, double d3, double d4, double d5) {} + + public void a(ParticleParam particleparam, boolean flag, boolean flag1, double d0, double d1, double d2, double d3, double d4, double d5) {} + + public void a(Entity entity) { + this.world.getTracker().track(entity); + if (entity instanceof EntityPlayer) { + this.world.worldProvider.a((EntityPlayer) entity); + } + + } + + public void b(Entity entity) { + this.world.getTracker().untrackEntity(entity); + this.world.getScoreboard().a(entity); + if (entity instanceof EntityPlayer) { + this.world.worldProvider.b((EntityPlayer) entity); + } + + } + + public void a(@Nullable EntityHuman entityhuman, SoundEffect soundeffect, SoundCategory soundcategory, double d0, double d1, double d2, float f, float f1) { + // CraftBukkit - this.world.dimension, // Paper - this.world.dimension -> this.world + this.a.getPlayerList().sendPacketNearby(entityhuman, d0, d1, d2, f > 1.0F ? (double) (16.0F * f) : 16.0D, this.world, new PacketPlayOutNamedSoundEffect(soundeffect, soundcategory, d0, d1, d2, f, f1)); + } + + public void a(int i, int j, int k, int l, int i1, int j1) {} + + public void a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, IBlockData iblockdata1, int i) { + this.world.getPlayerChunkMap().flagDirty(blockposition); + } + + public void a(BlockPosition blockposition) {} + + public void a(SoundEffect soundeffect, BlockPosition blockposition) {} + + public void a(EntityHuman entityhuman, int i, BlockPosition blockposition, int j) { + // CraftBukkit - this.world.dimension + this.a.getPlayerList().sendPacketNearby(entityhuman, (double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), 64.0D, this.world, new PacketPlayOutWorldEvent(i, blockposition, j, false)); + } + + public void a(int i, BlockPosition blockposition, int j) { + this.a.getPlayerList().sendAll(new PacketPlayOutWorldEvent(i, blockposition, j, true)); + } + + public void b(int i, BlockPosition blockposition, int j) { + // Iterator iterator = this.a.getPlayerList().v().iterator(); // Paper + + // CraftBukkit start + EntityHuman entityhuman = null; + Entity entity = world.getEntity(i); + if (entity instanceof EntityHuman) entityhuman = (EntityHuman) entity; + // CraftBukkit end + + // Paper start + java.util.List list = entity != null ? entity.world.players : this.a.getPlayerList().v(); + Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + EntityHuman human = iterator.next(); + if (!(human instanceof EntityPlayer)) continue; + EntityPlayer entityplayer = (EntityPlayer) human; + // Paper end + + if (entityplayer != null && entityplayer.world == this.world && entityplayer.getId() != i) { + double d0 = (double) blockposition.getX() - entityplayer.locX; + double d1 = (double) blockposition.getY() - entityplayer.locY; + double d2 = (double) blockposition.getZ() - entityplayer.locZ; + + // CraftBukkit start + if (entityhuman != null && entityhuman instanceof EntityPlayer && !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) { + continue; + } + // CraftBukkit end + + if (d0 * d0 + d1 * d1 + d2 * d2 < 1024.0D) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutBlockBreakAnimation(i, blockposition, j)); + } + } + } + + } +} diff --git a/src/main/java/net/minecraft/server/WorldMap.java b/src/main/java/net/minecraft/server/WorldMap.java new file mode 100644 index 000000000000..a819d60375ad --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldMap.java @@ -0,0 +1,481 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; + +// CraftBukkit start +import java.util.UUID; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.map.CraftMapView; +import org.bukkit.craftbukkit.util.CraftChatMessage; +// CraftBukkit end + +public class WorldMap extends PersistentBase { + + public int centerX; + public int centerZ; + public DimensionManager map; + public boolean track; + public boolean unlimitedTracking; + public byte scale; + public byte[] colors = new byte[16384]; + public List h = Lists.newArrayList(); + public final Map humans = Maps.newHashMap(); + private final Map k = Maps.newHashMap(); + public Map decorations = Maps.newLinkedHashMap(); + private final Map l = Maps.newHashMap(); + private org.bukkit.craftbukkit.map.RenderData vanillaRender = new org.bukkit.craftbukkit.map.RenderData(); // Paper + + // CraftBukkit start + public final CraftMapView mapView; + private CraftServer server; + private UUID uniqueId = null; + // CraftBukkit end + + public WorldMap(String s) { + super(s); + // CraftBukkit start + mapView = new CraftMapView(this); + server = (CraftServer) org.bukkit.Bukkit.getServer(); + vanillaRender.buffer = colors; // Paper + // CraftBukkit end + } + + public void a(int i, int j, int k, boolean flag, boolean flag1, DimensionManager dimensionmanager) { + this.scale = (byte) k; + this.a((double) i, (double) j, this.scale); + this.map = dimensionmanager; + this.track = flag; + this.unlimitedTracking = flag1; + this.c(); + } + + public void a(double d0, double d1, int i) { + int j = 128 * (1 << i); + int k = MathHelper.floor((d0 + 64.0D) / (double) j); + int l = MathHelper.floor((d1 + 64.0D) / (double) j); + + this.centerX = k * j + j / 2 - 64; + this.centerZ = l * j + j / 2 - 64; + } + + public void a(NBTTagCompound nbttagcompound) { + // CraftBukkit start + int dimension = nbttagcompound.getInt("dimension"); + + if (dimension >= CraftWorld.CUSTOM_DIMENSION_OFFSET) { + long least = nbttagcompound.getLong("UUIDLeast"); + long most = nbttagcompound.getLong("UUIDMost"); + + if (least != 0L && most != 0L) { + this.uniqueId = new UUID(most, least); + + CraftWorld world = (CraftWorld) server.getWorld(this.uniqueId); + // Check if the stored world details are correct. + if (world == null) { + /* All Maps which do not have their valid world loaded are set to a dimension which hopefully won't be reached. + This is to prevent them being corrupted with the wrong map data. */ + this.map = new DimensionManager(127, null, null, null); + } else { + this.map = world.getHandle().dimension; + } + } + } else { + this.map = DimensionManager.a(dimension); + } + + // CraftBukkit end + this.centerX = nbttagcompound.getInt("xCenter"); + this.centerZ = nbttagcompound.getInt("zCenter"); + this.scale = (byte) MathHelper.clamp(nbttagcompound.getByte("scale"), 0, 4); + this.track = !nbttagcompound.hasKeyOfType("trackingPosition", 1) || nbttagcompound.getBoolean("trackingPosition"); + this.unlimitedTracking = nbttagcompound.getBoolean("unlimitedTracking"); + this.colors = nbttagcompound.getByteArray("colors"); + if (this.colors.length != 16384) { + this.colors = new byte[16384]; + } + + NBTTagList nbttaglist = nbttagcompound.getList("banners", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + MapIconBanner mapiconbanner = MapIconBanner.a(nbttaglist.getCompound(i)); + + this.k.put(mapiconbanner.f(), mapiconbanner); + this.a(mapiconbanner.c(), (GeneratorAccess) null, mapiconbanner.f(), (double) mapiconbanner.a().getX(), (double) mapiconbanner.a().getZ(), 180.0D, mapiconbanner.d()); + } + vanillaRender.buffer = colors; // Paper + + NBTTagList nbttaglist1 = nbttagcompound.getList("frames", 10); + + for (int j = 0; j < nbttaglist1.size(); ++j) { + WorldMapFrame worldmapframe = WorldMapFrame.a(nbttaglist1.getCompound(j)); + + this.l.put(worldmapframe.e(), worldmapframe); + this.a(MapIcon.Type.FRAME, (GeneratorAccess) null, "frame-" + worldmapframe.d(), (double) worldmapframe.b().getX(), (double) worldmapframe.b().getZ(), (double) worldmapframe.c(), (IChatBaseComponent) null); + } + + } + + public NBTTagCompound b(NBTTagCompound nbttagcompound) { + // CraftBukkit start + if (this.map.getDimensionID() >= CraftWorld.CUSTOM_DIMENSION_OFFSET) { + if (this.uniqueId == null) { + for (org.bukkit.World world : server.getWorlds()) { + CraftWorld cWorld = (CraftWorld) world; + if (cWorld.getHandle().dimension == this.map) { + this.uniqueId = cWorld.getUID(); + break; + } + } + } + /* Perform a second check to see if a matching world was found, this is a necessary + change incase Maps are forcefully unlinked from a World and lack a UID.*/ + if (this.uniqueId != null) { + nbttagcompound.setLong("UUIDLeast", this.uniqueId.getLeastSignificantBits()); + nbttagcompound.setLong("UUIDMost", this.uniqueId.getMostSignificantBits()); + } + } + // CraftBukkit end + nbttagcompound.setInt("dimension", this.map.getDimensionID()); + nbttagcompound.setInt("xCenter", this.centerX); + nbttagcompound.setInt("zCenter", this.centerZ); + nbttagcompound.setByte("scale", this.scale); + nbttagcompound.setByteArray("colors", this.colors); + nbttagcompound.setBoolean("trackingPosition", this.track); + nbttagcompound.setBoolean("unlimitedTracking", this.unlimitedTracking); + NBTTagList nbttaglist = new NBTTagList(); + Iterator iterator = this.k.values().iterator(); + + while (iterator.hasNext()) { + MapIconBanner mapiconbanner = (MapIconBanner) iterator.next(); + + nbttaglist.add((NBTBase) mapiconbanner.e()); + } + + nbttagcompound.set("banners", nbttaglist); + NBTTagList nbttaglist1 = new NBTTagList(); + Iterator iterator1 = this.l.values().iterator(); + + while (iterator1.hasNext()) { + WorldMapFrame worldmapframe = (WorldMapFrame) iterator1.next(); + + nbttaglist1.add((NBTBase) worldmapframe.a()); + } + + nbttagcompound.set("frames", nbttaglist1); + return nbttagcompound; + } + + public void updateSeenPlayers(EntityHuman entityhuman, ItemStack itemstack) { a(entityhuman, itemstack); } // Paper - OBFHELPER + public void a(EntityHuman entityhuman, ItemStack itemstack) { + if (!this.humans.containsKey(entityhuman)) { + WorldMap.WorldMapHumanTracker worldmap_worldmaphumantracker = new WorldMap.WorldMapHumanTracker(entityhuman); + + this.humans.put(entityhuman, worldmap_worldmaphumantracker); + this.h.add(worldmap_worldmaphumantracker); + } + + if (!entityhuman.inventory.h(itemstack)) { + this.decorations.remove(entityhuman.getDisplayName().getString()); + } + + for (int i = 0; i < this.h.size(); ++i) { + WorldMap.WorldMapHumanTracker worldmap_worldmaphumantracker1 = (WorldMap.WorldMapHumanTracker) this.h.get(i); + String s = worldmap_worldmaphumantracker1.trackee.getDisplayName().getString(); + + if (!worldmap_worldmaphumantracker1.trackee.dead && (worldmap_worldmaphumantracker1.trackee.inventory.h(itemstack) || itemstack.x())) { + if (!itemstack.x() && worldmap_worldmaphumantracker1.trackee.dimension == this.map && this.track) { + this.a(MapIcon.Type.PLAYER, worldmap_worldmaphumantracker1.trackee.world, s, worldmap_worldmaphumantracker1.trackee.locX, worldmap_worldmaphumantracker1.trackee.locZ, (double) worldmap_worldmaphumantracker1.trackee.yaw, (IChatBaseComponent) null); + } + } else { + this.humans.remove(worldmap_worldmaphumantracker1.trackee); + this.h.remove(worldmap_worldmaphumantracker1); + this.decorations.remove(s); + } + } + + if (itemstack.x() && this.track) { + EntityItemFrame entityitemframe = itemstack.y(); + BlockPosition blockposition = entityitemframe.getBlockPosition(); + WorldMapFrame worldmapframe = (WorldMapFrame) this.l.get(WorldMapFrame.a(blockposition)); + + if (worldmapframe != null && entityitemframe.getId() != worldmapframe.d() && this.l.containsKey(worldmapframe.e())) { + this.decorations.remove("frame-" + worldmapframe.d()); + } + + WorldMapFrame worldmapframe1 = new WorldMapFrame(blockposition, entityitemframe.direction.get2DRotationValue() * 90, entityitemframe.getId()); + + this.a(MapIcon.Type.FRAME, entityhuman.world, "frame-" + entityitemframe.getId(), (double) blockposition.getX(), (double) blockposition.getZ(), (double) (entityitemframe.direction.get2DRotationValue() * 90), (IChatBaseComponent) null); + this.l.put(worldmapframe1.e(), worldmapframe1); + } + + NBTTagCompound nbttagcompound = itemstack.getTag(); + + if (nbttagcompound != null && nbttagcompound.hasKeyOfType("Decorations", 9)) { + NBTTagList nbttaglist = nbttagcompound.getList("Decorations", 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + NBTTagCompound nbttagcompound1 = nbttaglist.getCompound(j); + + if (!this.decorations.containsKey(nbttagcompound1.getString("id"))) { + this.a(MapIcon.Type.a(nbttagcompound1.getByte("type")), entityhuman.world, nbttagcompound1.getString("id"), nbttagcompound1.getDouble("x"), nbttagcompound1.getDouble("z"), nbttagcompound1.getDouble("rot"), (IChatBaseComponent) null); + } + } + } + + } + + public static void decorateMap(ItemStack itemstack, BlockPosition blockposition, String s, MapIcon.Type mapicon_type) { + NBTTagList nbttaglist; + + if (itemstack.hasTag() && itemstack.getTag().hasKeyOfType("Decorations", 9)) { + nbttaglist = itemstack.getTag().getList("Decorations", 10); + } else { + nbttaglist = new NBTTagList(); + itemstack.a("Decorations", (NBTBase) nbttaglist); + } + + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setByte("type", mapicon_type.a()); + nbttagcompound.setString("id", s); + nbttagcompound.setDouble("x", (double) blockposition.getX()); + nbttagcompound.setDouble("z", (double) blockposition.getZ()); + nbttagcompound.setDouble("rot", 180.0D); + nbttaglist.add((NBTBase) nbttagcompound); + if (mapicon_type.c()) { + NBTTagCompound nbttagcompound1 = itemstack.a("display"); + + nbttagcompound1.setInt("MapColor", mapicon_type.d()); + } + + } + + private void a(MapIcon.Type mapicon_type, @Nullable GeneratorAccess generatoraccess, String s, double d0, double d1, double d2, @Nullable IChatBaseComponent ichatbasecomponent) { + int i = 1 << this.scale; + float f = (float) (d0 - (double) this.centerX) / (float) i; + float f1 = (float) (d1 - (double) this.centerZ) / (float) i; + byte b0 = (byte) ((int) ((double) (f * 2.0F) + 0.5D)); + byte b1 = (byte) ((int) ((double) (f1 * 2.0F) + 0.5D)); + boolean flag = true; + byte b2; + + if (f >= -63.0F && f1 >= -63.0F && f <= 63.0F && f1 <= 63.0F) { + d2 += d2 < 0.0D ? -8.0D : 8.0D; + b2 = (byte) ((int) (d2 * 16.0D / 360.0D)); + if (this.map == DimensionManager.NETHER && generatoraccess != null) { + int j = (int) (generatoraccess.getWorldData().getDayTime() / 10L); + + b2 = (byte) (j * j * 34187121 + j * 121 >> 15 & 15); + } + } else { + if (mapicon_type != MapIcon.Type.PLAYER) { + this.decorations.remove(s); + return; + } + + boolean flag1 = true; + + if (Math.abs(f) < 320.0F && Math.abs(f1) < 320.0F) { + mapicon_type = MapIcon.Type.PLAYER_OFF_MAP; + } else { + if (!this.unlimitedTracking) { + this.decorations.remove(s); + return; + } + + mapicon_type = MapIcon.Type.PLAYER_OFF_LIMITS; + } + + b2 = 0; + if (f <= -63.0F) { + b0 = -128; + } + + if (f1 <= -63.0F) { + b1 = -128; + } + + if (f >= 63.0F) { + b0 = 127; + } + + if (f1 >= 63.0F) { + b1 = 127; + } + } + + this.decorations.put(s, new MapIcon(mapicon_type, b0, b1, b2, ichatbasecomponent)); + } + + @Nullable + public Packet a(ItemStack itemstack, IBlockAccess iblockaccess, EntityHuman entityhuman) { + WorldMap.WorldMapHumanTracker worldmap_worldmaphumantracker = (WorldMap.WorldMapHumanTracker) this.humans.get(entityhuman); + + return worldmap_worldmaphumantracker == null ? null : worldmap_worldmaphumantracker.a(itemstack); + } + + public void flagDirty(int i, int j) { + this.c(); + Iterator iterator = this.h.iterator(); + + while (iterator.hasNext()) { + WorldMap.WorldMapHumanTracker worldmap_worldmaphumantracker = (WorldMap.WorldMapHumanTracker) iterator.next(); + + worldmap_worldmaphumantracker.a(i, j); + } + + } + + public WorldMap.WorldMapHumanTracker a(EntityHuman entityhuman) { + WorldMap.WorldMapHumanTracker worldmap_worldmaphumantracker = (WorldMap.WorldMapHumanTracker) this.humans.get(entityhuman); + + if (worldmap_worldmaphumantracker == null) { + worldmap_worldmaphumantracker = new WorldMap.WorldMapHumanTracker(entityhuman); + this.humans.put(entityhuman, worldmap_worldmaphumantracker); + this.h.add(worldmap_worldmaphumantracker); + } + + return worldmap_worldmaphumantracker; + } + + public void a(GeneratorAccess generatoraccess, BlockPosition blockposition) { + float f = (float) blockposition.getX() + 0.5F; + float f1 = (float) blockposition.getZ() + 0.5F; + int i = 1 << this.scale; + float f2 = (f - (float) this.centerX) / (float) i; + float f3 = (f1 - (float) this.centerZ) / (float) i; + boolean flag = true; + boolean flag1 = false; + + if (f2 >= -63.0F && f3 >= -63.0F && f2 <= 63.0F && f3 <= 63.0F) { + MapIconBanner mapiconbanner = MapIconBanner.a(generatoraccess, blockposition); + + if (mapiconbanner == null) { + return; + } + + boolean flag2 = true; + + if (this.k.containsKey(mapiconbanner.f()) && ((MapIconBanner) this.k.get(mapiconbanner.f())).equals(mapiconbanner)) { + this.k.remove(mapiconbanner.f()); + this.decorations.remove(mapiconbanner.f()); + flag2 = false; + flag1 = true; + } + + if (flag2) { + this.k.put(mapiconbanner.f(), mapiconbanner); + this.a(mapiconbanner.c(), generatoraccess, mapiconbanner.f(), (double) f, (double) f1, 180.0D, mapiconbanner.d()); + flag1 = true; + } + + if (flag1) { + this.c(); + } + } + + } + + public void a(IBlockAccess iblockaccess, int i, int j) { + Iterator iterator = this.k.values().iterator(); + + while (iterator.hasNext()) { + MapIconBanner mapiconbanner = (MapIconBanner) iterator.next(); + + if (mapiconbanner.a().getX() == i && mapiconbanner.a().getZ() == j) { + MapIconBanner mapiconbanner1 = MapIconBanner.a(iblockaccess, mapiconbanner.a()); + + if (!mapiconbanner.equals(mapiconbanner1)) { + iterator.remove(); + this.decorations.remove(mapiconbanner.f()); + } + } + } + + } + + public void a(BlockPosition blockposition, int i) { + this.decorations.remove("frame-" + i); + this.l.remove(WorldMapFrame.a(blockposition)); + } + + public class WorldMapHumanTracker { + + // Paper start + private void addSeenPlayers(java.util.Collection icons) { + org.bukkit.entity.Player player = (org.bukkit.entity.Player) trackee.getBukkitEntity(); + WorldMap.this.decorations.forEach((name, mapIcon) -> { + // If this cursor is for a player check visibility with vanish system + org.bukkit.entity.Player other = org.bukkit.Bukkit.getPlayerExact(name); // Spigot + if (other == null || player.canSee(other)) { + icons.add(mapIcon); + } + }); + } + private boolean shouldUseVanillaMap() { + return mapView.getRenderers().size() == 1 && mapView.getRenderers().get(0).getClass() == org.bukkit.craftbukkit.map.CraftMapRenderer.class; + } + // Paper stop + public final EntityHuman trackee; + private boolean d = true; + private int e; + private int f; + private int g = 127; + private int h = 127; + private int i; + public int b; + + public WorldMapHumanTracker(EntityHuman entityhuman) { + this.trackee = entityhuman; + } + + @Nullable + public Packet a(ItemStack itemstack) { + // CraftBukkit start + if (!this.d && this.i % 5 != 0) { this.i++; return null; } // Paper - this won't end up sending, so don't render it! + boolean vanillaMaps = shouldUseVanillaMap(); // Paper + org.bukkit.craftbukkit.map.RenderData render = !vanillaMaps ? WorldMap.this.mapView.render((org.bukkit.craftbukkit.entity.CraftPlayer) this.trackee.getBukkitEntity()) : WorldMap.this.vanillaRender; // CraftBukkit // Paper + + java.util.Collection icons = new java.util.ArrayList(); + if (vanillaMaps) addSeenPlayers(icons); // Paper + + for ( org.bukkit.map.MapCursor cursor : render.cursors) { + + if (cursor.isVisible()) { + icons.add(new MapIcon(MapIcon.Type.a(cursor.getRawType()), cursor.getX(), cursor.getY(), cursor.getDirection(), CraftChatMessage.fromStringOrNull(cursor.getCaption()))); + } + } + + if (this.d) { + this.d = false; + return new PacketPlayOutMap(ItemWorldMap.e(itemstack), WorldMap.this.scale, WorldMap.this.track, icons, render.buffer, this.e, this.f, this.g + 1 - this.e, this.h + 1 - this.f); + } else { + return this.i++ % 5 == 0 ? new PacketPlayOutMap(ItemWorldMap.e(itemstack), WorldMap.this.scale, WorldMap.this.track, icons, render.buffer, 0, 0, 0, 0) : null; + } + // CraftBukkit end + } + + public void a(int i, int j) { + if (this.d) { + this.e = Math.min(this.e, i); + this.f = Math.min(this.f, j); + this.g = Math.max(this.g, i); + this.h = Math.max(this.h, j); + } else { + this.d = true; + this.e = i; + this.f = j; + this.g = i; + this.h = j; + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldNBTStorage.java b/src/main/java/net/minecraft/server/WorldNBTStorage.java new file mode 100644 index 000000000000..9be0e994efdb --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldNBTStorage.java @@ -0,0 +1,363 @@ +package net.minecraft.server; + +import com.mojang.datafixers.DataFixTypes; +import com.mojang.datafixers.DataFixer; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import java.util.UUID; +import org.bukkit.craftbukkit.entity.CraftPlayer; +// CraftBukkit end + +public class WorldNBTStorage implements IDataManager, IPlayerFileData { + + private static final Logger b = LogManager.getLogger(); + private final File baseDir; + private final File playerDir; + private final long sessionId = SystemUtils.getMonotonicMillis(); + private final String f; + private final DefinedStructureManager g; + protected final DataFixer a; + private UUID uuid = null; // CraftBukkit + + public WorldNBTStorage(File file, String s, @Nullable MinecraftServer minecraftserver, DataFixer datafixer) { + this.a = datafixer; + // Paper start + if (com.destroystokyo.paper.PaperConfig.useVersionedWorld) { + File origBaseDir = new File(file, s); + final String currentVersion = MinecraftServer.getServer().getVersion(); + file = new File(file, currentVersion); + File baseDir = new File(file, s); + + if (!baseDir.exists() && origBaseDir.exists() && !baseDir.mkdirs()) { + LogManager.getLogger().error("Could not create world directory for " + file); + System.exit(1); + } + + try { + boolean printedHeader = false; + String[] dirs = {"advancements", "data", "datapacks", "playerdata", "stats"}; + for (String dir : dirs) { + File origPlayerData = new File(origBaseDir, dir); + File targetPlayerData = new File(baseDir, dir); + if (origPlayerData.exists() && !targetPlayerData.exists()) { + if (!printedHeader) { + LogManager.getLogger().info("**** VERSIONED WORLD - Copying files"); + printedHeader = true; + } + LogManager.getLogger().info("- Copying: " + dir); + org.apache.commons.io.FileUtils.copyDirectory(origPlayerData, targetPlayerData); + } + } + + String[] files = {"level.dat", "level.dat_old", "session.lock", "uid.dat"}; + for (String fileName : files) { + File origPlayerData = new File(origBaseDir, fileName); + File targetPlayerData = new File(baseDir, fileName); + if (origPlayerData.exists() && !targetPlayerData.exists()) { + if (!printedHeader) { + LogManager.getLogger().info("- Copying files"); + printedHeader = true; + } + LogManager.getLogger().info("- Copying: " + fileName); + org.apache.commons.io.FileUtils.copyFile(origPlayerData, targetPlayerData); + + } + } + if (printedHeader) { + LogManager.getLogger().info("**** VERSIONED WORLD - Copying DONE"); + } + } catch (IOException e) { + LogManager.getLogger().error("Error copying versioned world data for " + origBaseDir + " to " + baseDir, e); + com.destroystokyo.paper.util.SneakyThrow.sneaky(e); + } + + } + // Paper end + this.baseDir = new File(file, s); + this.baseDir.mkdirs(); + this.playerDir = new File(this.baseDir, "playerdata"); + this.f = s; + if (minecraftserver != null) { + this.playerDir.mkdirs(); + this.g = new DefinedStructureManager(minecraftserver, this.baseDir, datafixer); + } else { + this.g = null; + } + + this.j(); + } + + private void j() { + try { + File file = new File(this.baseDir, "session.lock"); + DataOutputStream dataoutputstream = new DataOutputStream(new FileOutputStream(file)); + + try { + dataoutputstream.writeLong(this.sessionId); + } finally { + dataoutputstream.close(); + } + + } catch (IOException ioexception) { + ioexception.printStackTrace(); + throw new RuntimeException("Failed to check session lock for world located at " + this.baseDir + ", aborting. Stop the server and delete the session.lock in this world to prevent further issues."); // Spigot + } + } + + public File getDirectory() { + return this.baseDir; + } + + public void checkSession() throws ExceptionWorldConflict { + try { + File file = new File(this.baseDir, "session.lock"); + DataInputStream datainputstream = new DataInputStream(new FileInputStream(file)); + + try { + if (datainputstream.readLong() != this.sessionId) { + throw new ExceptionWorldConflict("The save for world located at " + this.baseDir + " is being accessed from another location, aborting"); // Spigot + } + } finally { + datainputstream.close(); + } + + } catch (IOException ioexception) { + throw new ExceptionWorldConflict("Failed to check session lock for world located at " + this.baseDir + ", aborting. Stop the server and delete the session.lock in this world to prevent further issues."); // Spigot + } + } + + public IChunkLoader createChunkLoader(WorldProvider worldprovider) { + throw new RuntimeException("Old Chunk Storage is no longer supported."); + } + + @Nullable + public WorldData getWorldData() { + File file = new File(this.baseDir, "level.dat"); + + if (file.exists()) { + WorldData worlddata = WorldLoader.a(file, this.a); + + if (worlddata != null) { + return worlddata; + } + } + + file = new File(this.baseDir, "level.dat_old"); + return file.exists() ? WorldLoader.a(file, this.a) : null; + } + + public void saveWorldData(WorldData worlddata, @Nullable NBTTagCompound nbttagcompound) { + NBTTagCompound nbttagcompound1 = worlddata.a(nbttagcompound); + NBTTagCompound nbttagcompound2 = new NBTTagCompound(); + + nbttagcompound2.set("Data", nbttagcompound1); + + try { + File file = new File(this.baseDir, "level.dat_new"); + File file1 = new File(this.baseDir, "level.dat_old"); + File file2 = new File(this.baseDir, "level.dat"); + + NBTCompressedStreamTools.a(nbttagcompound2, (OutputStream) (new FileOutputStream(file))); + if (file1.exists()) { + file1.delete(); + } + + file2.renameTo(file1); + if (file2.exists()) { + file2.delete(); + } + + file.renameTo(file2); + if (file.exists()) { + file.delete(); + } + } catch (Exception exception) { + exception.printStackTrace(); + } + + } + + public void saveWorldData(WorldData worlddata) { + this.saveWorldData(worlddata, (NBTTagCompound) null); + } + + public void save(EntityHuman entityhuman) { + if(!com.destroystokyo.paper.PaperConfig.savePlayerData) return; // Paper - Make player data saving configurable + try { + NBTTagCompound nbttagcompound = entityhuman.save(new NBTTagCompound()); + File file = new File(this.playerDir, entityhuman.bu() + ".dat.tmp"); + File file1 = new File(this.playerDir, entityhuman.bu() + ".dat"); + + NBTCompressedStreamTools.a(nbttagcompound, (OutputStream) (new FileOutputStream(file))); + if (file1.exists()) { + file1.delete(); + } + + file.renameTo(file1); + } catch (Exception exception) { + WorldNBTStorage.b.error("Failed to save player data for {}", entityhuman.getName(), exception); // Paper + } + + } + + @Nullable + public NBTTagCompound load(EntityHuman entityhuman) { + NBTTagCompound nbttagcompound = null; + + try { + File file = new File(this.playerDir, entityhuman.bu() + ".dat"); + // Spigot Start + boolean usingWrongFile = false; + if ( org.bukkit.Bukkit.getOnlineMode() && !file.exists() ) // Paper - Check online mode first + { + file = new File( this.playerDir, UUID.nameUUIDFromBytes( ( "OfflinePlayer:" + entityhuman.getName() ).getBytes( "UTF-8" ) ).toString() + ".dat"); + if ( file.exists() ) + { + usingWrongFile = true; + org.bukkit.Bukkit.getServer().getLogger().warning( "Using offline mode UUID file for player " + entityhuman.getName() + " as it is the only copy we can find." ); + } + } + // Spigot End + + if (file.exists() && file.isFile()) { + nbttagcompound = NBTCompressedStreamTools.a((InputStream) (new FileInputStream(file))); + } + // Spigot Start + if ( usingWrongFile ) + { + file.renameTo( new File( file.getPath() + ".offline-read" ) ); + } + // Spigot End + } catch (Exception exception) { + WorldNBTStorage.b.warn("Failed to load player data for {}", entityhuman.getDisplayName().getString()); + } + + if (nbttagcompound != null) { + // CraftBukkit start + if (entityhuman instanceof EntityPlayer) { + CraftPlayer player = (CraftPlayer) entityhuman.getBukkitEntity(); + // Only update first played if it is older than the one we have + long modified = new File(this.playerDir, entityhuman.getUniqueID().toString() + ".dat").lastModified(); + if (modified < player.getFirstPlayed()) { + player.setFirstPlayed(modified); + } + } + // CraftBukkit end + int i = nbttagcompound.hasKeyOfType("DataVersion", 3) ? nbttagcompound.getInt("DataVersion") : -1; + + entityhuman.f(GameProfileSerializer.a(this.a, DataFixTypes.PLAYER, nbttagcompound, i)); + } + + return nbttagcompound; + } + + // CraftBukkit start + public NBTTagCompound getPlayerData(String s) { + try { + File file1 = new File(this.playerDir, s + ".dat"); + + if (file1.exists()) { + return NBTCompressedStreamTools.a((InputStream) (new FileInputStream(file1))); + } + } catch (Exception exception) { + b.warn("Failed to load player data for " + s); + } + + return null; + } + // CraftBukkit end + + public IPlayerFileData getPlayerFileData() { + return this; + } + + public String[] getSeenPlayers() { + String[] astring = this.playerDir.list(); + + if (astring == null) { + astring = new String[0]; + } + + for (int i = 0; i < astring.length; ++i) { + if (astring[i].endsWith(".dat")) { + astring[i] = astring[i].substring(0, astring[i].length() - 4); + } + } + + return astring; + } + + public void a() {} + + public File getDataFile(DimensionManager dimensionmanager, String s) { + File file = new File(dimensionmanager.a(this.baseDir), "data"); + + file.mkdirs(); + return new File(file, s + ".dat"); + } + + public DefinedStructureManager h() { + return this.g; + } + + public DataFixer i() { + return this.a; + } + + // CraftBukkit start + public UUID getUUID() { + if (uuid != null) return uuid; + File file1 = new File(this.baseDir, "uid.dat"); + if (file1.exists()) { + DataInputStream dis = null; + try { + dis = new DataInputStream(new FileInputStream(file1)); + return uuid = new UUID(dis.readLong(), dis.readLong()); + } catch (IOException ex) { + b.warn("Failed to read " + file1 + ", generating new random UUID", ex); + } finally { + if (dis != null) { + try { + dis.close(); + } catch (IOException ex) { + // NOOP + } + } + } + } + uuid = UUID.randomUUID(); + DataOutputStream dos = null; + try { + dos = new DataOutputStream(new FileOutputStream(file1)); + dos.writeLong(uuid.getMostSignificantBits()); + dos.writeLong(uuid.getLeastSignificantBits()); + } catch (IOException ex) { + b.warn("Failed to write " + file1, ex); + } finally { + if (dos != null) { + try { + dos.close(); + } catch (IOException ex) { + // NOOP + } + } + } + return uuid; + } + + public File getPlayerDir() { + return playerDir; + } + // CraftBukkit end +} diff --git a/src/main/java/net/minecraft/server/WorldPersistentData.java b/src/main/java/net/minecraft/server/WorldPersistentData.java new file mode 100644 index 000000000000..8d51af2867b9 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldPersistentData.java @@ -0,0 +1,199 @@ +package net.minecraft.server; + +import com.google.common.collect.Maps; +import com.mojang.datafixers.DataFixTypes; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectIterator; +import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Iterator; +import java.util.Map; +import java.util.function.Function; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class WorldPersistentData { + + private static final Logger a = LogManager.getLogger(); + private final DimensionManager b; + public Map data = Maps.newHashMap(); + private final Object2IntMap d = new Object2IntOpenHashMap(); + @Nullable + private final IDataManager e; + + public WorldPersistentData(DimensionManager dimensionmanager, @Nullable IDataManager idatamanager) { + this.b = dimensionmanager; + this.e = idatamanager; + this.d.defaultReturnValue(-1); + } + + @Nullable + public T a(Function function, String s) { + T persistentbase = (T) this.data.get(s); // Paper - decompile fix + + if (persistentbase == null && this.e != null) { + try { + File file = this.e.getDataFile(this.b, s); + + if (file != null && file.exists()) { + persistentbase = function.apply(s); // Paper - decompile fix + persistentbase.a(a(this.e, this.b, s, 1631).getCompound("data")); + this.data.put(s, persistentbase); + } + } catch (Exception exception) { + WorldPersistentData.a.error("Error loading saved data: {}", s, exception); + } + } + + return persistentbase; + } + + public void a(String s, PersistentBase persistentbase) { + this.data.put(s, persistentbase); + } + + public void a() { + try { + this.d.clear(); + if (this.e == null) { + return; + } + + File file = this.e.getDataFile(this.b, "idcounts"); + + if (file != null && file.exists()) { + DataInputStream datainputstream = new DataInputStream(new FileInputStream(file)); + NBTTagCompound nbttagcompound = NBTCompressedStreamTools.a(datainputstream); + + datainputstream.close(); + Iterator iterator = nbttagcompound.getKeys().iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + if (nbttagcompound.hasKeyOfType(s, 99)) { + this.d.put(s, nbttagcompound.getInt(s)); + } + } + } + } catch (Exception exception) { + WorldPersistentData.a.error("Could not load aux values", exception); + } + + } + + public int a(String s) { + int i = this.d.getInt(s) + 1; + + this.d.put(s, i); + if (this.e == null) { + return i; + } else { + try { + File file = this.e.getDataFile(this.b, "idcounts"); + + if (file != null) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + ObjectIterator objectiterator = this.d.object2IntEntrySet().iterator(); + + while (objectiterator.hasNext()) { + Entry entry = (Entry) objectiterator.next(); + + nbttagcompound.setInt((String) entry.getKey(), entry.getIntValue()); + } + + DataOutputStream dataoutputstream = new DataOutputStream(new FileOutputStream(file)); + + NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) dataoutputstream); + dataoutputstream.close(); + } + } catch (Exception exception) { + WorldPersistentData.a.error("Could not get free aux value {}", s, exception); + } + + return i; + } + } + + public static NBTTagCompound a(IDataManager idatamanager, DimensionManager dimensionmanager, String s, int i) throws IOException { + File file = idatamanager.getDataFile(dimensionmanager, s); + FileInputStream fileinputstream = new FileInputStream(file); + Throwable throwable = null; + + NBTTagCompound nbttagcompound; + + try { + NBTTagCompound nbttagcompound1 = NBTCompressedStreamTools.a((InputStream) fileinputstream); + int j = nbttagcompound1.hasKeyOfType("DataVersion", 99) ? nbttagcompound1.getInt("DataVersion") : 1343; + + nbttagcompound = GameProfileSerializer.a(idatamanager.i(), DataFixTypes.SAVED_DATA, nbttagcompound1, j, i); + } catch (Throwable throwable1) { + com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(throwable1); // Paper + throwable = throwable1; + throw throwable1; + } finally { + if (fileinputstream != null) { + if (throwable != null) { + try { + fileinputstream.close(); + } catch (Throwable throwable2) { + throwable.addSuppressed(throwable2); + } + } else { + fileinputstream.close(); + } + } + + } + + return nbttagcompound; + } + + public void b() { + if (this.e != null) { + Iterator iterator = this.data.values().iterator(); + + while (iterator.hasNext()) { + PersistentBase persistentbase = (PersistentBase) iterator.next(); + + if (persistentbase.d()) { + this.a(persistentbase); + persistentbase.a(false); + } + } + + } + } + + private void a(PersistentBase persistentbase) { + if (this.e != null) { + try { + File file = this.e.getDataFile(this.b, persistentbase.getId()); + + if (file != null) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.set("data", persistentbase.b(new NBTTagCompound())); + nbttagcompound.setInt("DataVersion", 1631); + FileOutputStream fileoutputstream = new FileOutputStream(file); + + NBTCompressedStreamTools.a(nbttagcompound, (OutputStream) fileoutputstream); + fileoutputstream.close(); + } + } catch (Exception exception) { + WorldPersistentData.a.error("Could not save data {}", persistentbase, exception); + } + + } + } +} diff --git a/src/main/java/net/minecraft/server/WorldProvider.java b/src/main/java/net/minecraft/server/WorldProvider.java new file mode 100644 index 000000000000..3911e4947e98 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldProvider.java @@ -0,0 +1,92 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public abstract class WorldProvider { + + public static final float[] a = new float[] { 1.0F, 0.75F, 0.5F, 0.25F, 0.0F, 0.25F, 0.5F, 0.75F}; + protected World b; + protected boolean c; + protected boolean d; + protected boolean e; + protected final float[] f = new float[16]; + private final float[] g = new float[4]; + + public WorldProvider() {} + + public final void a(World world) { + this.b = world; + this.m(); + this.a(); + } + + protected void a() { + float f = 0.0F; + + for (int i = 0; i <= 15; ++i) { + float f1 = 1.0F - (float) i / 15.0F; + + this.f[i] = (1.0F - f1) / (f1 * 3.0F + 1.0F) * 1.0F + 0.0F; + } + + } + + public int a(long i) { + return (int) (i / 24000L % 8L + 8L) % 8; + } + + @Nullable + public BlockPosition d() { + return null; + } + + public boolean isNether() { + return this.c; + } + + public boolean g() { + return this.e; + } + + public boolean h() { + return this.d; + } + + public float[] i() { + return this.f; + } + + public WorldBorder getWorldBorder() { + return new WorldBorder(); + } + + public void a(EntityPlayer entityplayer) {} + + public void b(EntityPlayer entityplayer) {} + + public void k() {} + + public void l() {} + + public boolean a(int i, int j) { + return !this.b.isSpawnChunk(i, j) && !this.b.isForceLoaded(i, j); // Paper - Use spawn chunks check for all worlds + } + + protected abstract void m(); + + public abstract ChunkGenerator getChunkGenerator(); + + @Nullable + public abstract BlockPosition a(ChunkCoordIntPair chunkcoordintpair, boolean flag); + + @Nullable + public abstract BlockPosition a(int i, int j, boolean flag); + + public abstract float a(long i, float f); + + public abstract boolean isOverworld(); + + public abstract boolean canRespawn(); + + public abstract DimensionManager getDimensionManager(); +} diff --git a/src/main/java/net/minecraft/server/WorldProviderHell.java b/src/main/java/net/minecraft/server/WorldProviderHell.java new file mode 100644 index 000000000000..77fe71f243c7 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldProviderHell.java @@ -0,0 +1,71 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; + +public class WorldProviderHell extends WorldProvider { + + public WorldProviderHell() {} + + public void m() { + this.c = true; + this.d = true; + this.e = false; + } + + protected void a() { + float f = 0.1F; + + for (int i = 0; i <= 15; ++i) { + float f1 = 1.0F - (float) i / 15.0F; + + this.f[i] = (1.0F - f1) / (f1 * 3.0F + 1.0F) * 0.9F + 0.1F; + } + + } + + public ChunkGenerator getChunkGenerator() { + GeneratorSettingsNether generatorsettingsnether = (GeneratorSettingsNether) ChunkGeneratorType.b.b(); + + generatorsettingsnether.a(Blocks.NETHERRACK.getBlockData()); + generatorsettingsnether.b(Blocks.LAVA.getBlockData()); + return ChunkGeneratorType.b.create(this.b, BiomeLayout.b.a(((BiomeLayoutFixedConfiguration) BiomeLayout.b.b()).a(Biomes.NETHER)), generatorsettingsnether); + } + + public boolean isOverworld() { + return false; + } + + @Nullable + public BlockPosition a(ChunkCoordIntPair chunkcoordintpair, boolean flag) { + return null; + } + + @Nullable + public BlockPosition a(int i, int j, boolean flag) { + return null; + } + + public float a(long i, float f) { + return 0.5F; + } + + public boolean canRespawn() { + return false; + } + + public WorldBorder getWorldBorder() { + return new WorldBorder() { + public double getCenterX() { + return super.getCenterX(); // CraftBukkit + } + + public double getCenterZ() { + return super.getCenterZ(); // CraftBukkit + } + }; + } + + public DimensionManager getDimensionManager() { + return DimensionManager.NETHER; + } +} diff --git a/src/main/java/net/minecraft/server/WorldProviderTheEnd.java b/src/main/java/net/minecraft/server/WorldProviderTheEnd.java new file mode 100644 index 000000000000..4d692b7e05b2 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldProviderTheEnd.java @@ -0,0 +1,83 @@ +package net.minecraft.server; + +import java.util.Random; +import javax.annotation.Nullable; + +public class WorldProviderTheEnd extends WorldProvider { + + public static final BlockPosition g = new BlockPosition(100, 50, 0); + private EnderDragonBattle h; + + public WorldProviderTheEnd() {} + + public void m() { + NBTTagCompound nbttagcompound = this.b.getWorldData().a(DimensionManager.THE_END); + + this.h = this.b instanceof WorldServer ? new EnderDragonBattle((WorldServer) this.b, nbttagcompound.getCompound("DragonFight")) : null; + this.e = false; + } + + public ChunkGenerator getChunkGenerator() { + GeneratorSettingsEnd generatorsettingsend = (GeneratorSettingsEnd) ChunkGeneratorType.c.b(); + + generatorsettingsend.a(Blocks.END_STONE.getBlockData()); + generatorsettingsend.b(Blocks.AIR.getBlockData()); + generatorsettingsend.a(this.d()); + return ChunkGeneratorType.c.create(this.b, BiomeLayout.d.a(((BiomeLayoutTheEndConfiguration) BiomeLayout.d.b()).a(this.b.getSeed())), generatorsettingsend); + } + + public float a(long i, float f) { + return 0.5F; // Paper - fix MC-93764 + } + + public boolean canRespawn() { + return false; + } + + public boolean isOverworld() { + return false; + } + + @Nullable + public BlockPosition a(ChunkCoordIntPair chunkcoordintpair, boolean flag) { + Random random = new Random(this.b.getSeed()); + BlockPosition blockposition = new BlockPosition(chunkcoordintpair.d() + random.nextInt(15), 0, chunkcoordintpair.g() + random.nextInt(15)); + + return this.b.i(blockposition).getMaterial().isSolid() ? blockposition : null; + } + + public BlockPosition d() { + return WorldProviderTheEnd.g; + } + + @Nullable + public BlockPosition a(int i, int j, boolean flag) { + return this.a(new ChunkCoordIntPair(i >> 4, j >> 4), flag); + } + + public DimensionManager getDimensionManager() { + return DimensionManager.THE_END; + } + + public void k() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + if (this.h != null) { + nbttagcompound.set("DragonFight", this.h.a()); + } + + this.b.getWorldData().a(DimensionManager.THE_END, nbttagcompound); + } + + public void l() { + if (this.h != null) { + this.h.b(); + } + + } + + @Nullable + public EnderDragonBattle r() { + return this.h; + } +} diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java new file mode 100644 index 000000000000..a4fc1e5e2195 --- /dev/null +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -0,0 +1,1289 @@ +package net.minecraft.server; + +import co.aikar.timings.Timings; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.util.concurrent.ListenableFuture; +import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.UUID; +import java.util.function.BooleanSupplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import java.util.logging.Level; + +import org.bukkit.WeatherType; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.util.HashTreeSet; + +import org.bukkit.event.block.BlockFormEvent; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.event.weather.LightningStrikeEvent; +// CraftBukkit end + +public class WorldServer extends World implements IAsyncTaskHandler { + + private static final Logger a = LogManager.getLogger(); + boolean hasPhysicsEvent = true; // Paper + private final MinecraftServer server; + public EntityTracker tracker; + private final PlayerChunkMap manager; + public final Map entitiesByUUID = Maps.newHashMap(); // Paper + public boolean savingDisabled; + private boolean J; + private int emptyTime; + private final PortalTravelAgent portalTravelAgent; + private final SpawnerCreature spawnerCreature = new SpawnerCreature(); + private final TickListServer nextTickListBlock; + private final TickListServer nextTickListFluid; + protected final VillageSiege siegeManager; + ObjectLinkedOpenHashSet d; + private boolean P; + + // CraftBukkit start + public final DimensionManager dimension; + private static Throwable getAddToWorldStackTrace(Entity entity) { + return new Throwable(entity + " Added to world at " + new java.util.Date()); + } + + // Add env and gen to constructor + public WorldServer(MinecraftServer minecraftserver, IDataManager idatamanager, PersistentCollection persistentcollection, WorldData worlddata, DimensionManager dimensionmanager, MethodProfiler methodprofiler, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) { + super(idatamanager, persistentcollection, worlddata, DimensionManager.a(env.getId()).e(), methodprofiler, false, gen, env); + this.dimension = dimensionmanager; + this.pvpMode = minecraftserver.getPVP(); + worlddata.world = this; + // CraftBukkit end + this.nextTickListBlock = new TickListServer<>(this, (block) -> { + return block == null || block.getBlockData().isAir(); + }, IRegistry.BLOCK::getKey, IRegistry.BLOCK::getOrDefault, this::b, "Blocks"); // Paper - timings + + this.nextTickListFluid = new TickListServer<>(this, (fluidtype) -> { + return fluidtype == null || fluidtype == FluidTypes.EMPTY; + }, IRegistry.FLUID::getKey, IRegistry.FLUID::getOrDefault, this::a, "Fluids"); // Paper - timings + this.siegeManager = new VillageSiege(this); + this.d = new ObjectLinkedOpenHashSet(); + this.server = minecraftserver; + this.tracker = new EntityTracker(this); + this.manager = new PlayerChunkMap(this); + this.worldProvider.a((World) this); + this.chunkProvider = this.r(); + this.portalTravelAgent = new org.bukkit.craftbukkit.CraftTravelAgent(this); // CraftBukkit + this.P(); + this.Q(); + this.getWorldBorder().a(minecraftserver.au()); + } + + public WorldServer i_() { + String s = PersistentVillage.a(this.worldProvider); + PersistentVillage persistentvillage = (PersistentVillage) this.a(DimensionManager.OVERWORLD, PersistentVillage::new, s); + + if (persistentvillage == null) { + this.villages = new PersistentVillage(this); + this.a(DimensionManager.OVERWORLD, s, (PersistentBase) this.villages); + } else { + this.villages = persistentvillage; + this.villages.a((World) this); + } + + if (getServer().getScoreboardManager() == null) { // CraftBukkit + PersistentScoreboard persistentscoreboard = (PersistentScoreboard) this.a(DimensionManager.OVERWORLD, PersistentScoreboard::new, "scoreboard"); + + if (persistentscoreboard == null) { + persistentscoreboard = new PersistentScoreboard(); + this.a(DimensionManager.OVERWORLD, "scoreboard", (PersistentBase) persistentscoreboard); + } + + persistentscoreboard.a((Scoreboard) this.server.getScoreboard()); + this.server.getScoreboard().a((Runnable) (new RunnableSaveScoreboard(persistentscoreboard))); + } // CraftBukkit + this.getWorldBorder().setCenter(this.worldData.B(), this.worldData.C()); + this.getWorldBorder().setDamageAmount(this.worldData.H()); + this.getWorldBorder().setDamageBuffer(this.worldData.G()); + this.getWorldBorder().setWarningDistance(this.worldData.I()); + this.getWorldBorder().setWarningTime(this.worldData.J()); + if (this.worldData.E() > 0L) { + this.getWorldBorder().transitionSizeBetween(this.worldData.D(), this.worldData.F(), this.worldData.E()); + } else { + this.getWorldBorder().setSize(this.worldData.D()); + } + + // CraftBukkit start + if (generator != null) { + getWorld().getPopulators().addAll(generator.getDefaultPopulators(getWorld())); + } + // CraftBukkit end + + return this; + } + + // CraftBukkit start + @Override + public TileEntity getTileEntity(BlockPosition pos) { + TileEntity result = super.getTileEntity(pos); + Block type = getType(pos).getBlock(); + + if (type == Blocks.CHEST || type == Blocks.TRAPPED_CHEST) { // Spigot + if (!(result instanceof TileEntityChest)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.FURNACE) { + if (!(result instanceof TileEntityFurnace)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.DROPPER) { + if (!(result instanceof TileEntityDropper)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.DISPENSER) { + if (!(result instanceof TileEntityDispenser)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.JUKEBOX) { + if (!(result instanceof TileEntityJukeBox)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.SPAWNER) { + if (!(result instanceof TileEntityMobSpawner)) { + result = fixTileEntity(pos, type, result); + } + } else if ((type == Blocks.SIGN) || (type == Blocks.WALL_SIGN)) { + if (!(result instanceof TileEntitySign)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.ENDER_CHEST) { + if (!(result instanceof TileEntityEnderChest)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.BREWING_STAND) { + if (!(result instanceof TileEntityBrewingStand)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.BEACON) { + if (!(result instanceof TileEntityBeacon)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.HOPPER) { + if (!(result instanceof TileEntityHopper)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.ENCHANTING_TABLE) { + if (!(result instanceof TileEntityEnchantTable)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.END_PORTAL) { + if (!(result instanceof TileEntityEnderPortal)) { + result = fixTileEntity(pos, type, result); + } + } else if (type instanceof BlockSkullAbstract) { + if (!(result instanceof TileEntitySkull)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.DAYLIGHT_DETECTOR) { + if (!(result instanceof TileEntityLightDetector)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.COMPARATOR) { + if (!(result instanceof TileEntityComparator)) { + result = fixTileEntity(pos, type, result); + } + }else if (type instanceof BlockBannerAbstract) { + if (!(result instanceof TileEntityBanner)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.STRUCTURE_BLOCK) { + if (!(result instanceof TileEntityStructure)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.END_GATEWAY) { + if (!(result instanceof TileEntityEndGateway)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.COMMAND_BLOCK) { + if (!(result instanceof TileEntityCommand)) { + result = fixTileEntity(pos, type, result); + } + } else if (type == Blocks.STRUCTURE_BLOCK) { + if (!(result instanceof TileEntityStructure)) { + result = fixTileEntity(pos, type, result); + } + } else if (type instanceof BlockBed) { + if (!(result instanceof TileEntityBed)) { + result = fixTileEntity(pos, type, result); + } + } + // Paper Start - add TE fix checks for shulkers, see nms.BlockShulkerBox + else if (type instanceof BlockShulkerBox) { + if (!(result instanceof TileEntityShulkerBox)) { + result = fixTileEntity(pos, type, result); + } + } + // Paper end + + return result; + } + + private TileEntity fixTileEntity(BlockPosition pos, Block type, TileEntity found) { + this.getServer().getLogger().log(Level.SEVERE, "Block at {0},{1},{2} is {3} but has {4}" + ". " + + "Bukkit will attempt to fix this, but there may be additional damage that we cannot recover.", new Object[]{pos.getX(), pos.getY(), pos.getZ(), type, found}); + + if (type instanceof ITileEntity) { + TileEntity replacement = ((ITileEntity) type).a(this); + replacement.world = this; + this.setTileEntity(pos, replacement); + return replacement; + } else { + this.getServer().getLogger().severe("Don't know how to fix for this type... Can't do anything! :("); + return found; + } + } + // CraftBukkit end + + public void doTick(BooleanSupplier booleansupplier) { + this.P = true; + super.doTick(booleansupplier); + if (this.getWorldData().isHardcore() && this.getDifficulty() != EnumDifficulty.HARD) { + this.getWorldData().setDifficulty(EnumDifficulty.HARD); + } + + this.chunkProvider.getChunkGenerator().getWorldChunkManager().tick(); + if (this.everyoneDeeplySleeping()) { + if (this.getGameRules().getBoolean("doDaylightCycle")) { + long i = this.worldData.getDayTime() + 24000L; + + this.worldData.setDayTime(i - i % 24000L); + } + + this.i(); + } + + this.methodProfiler.enter("spawner"); + // CraftBukkit start - Only call spawner if we have players online and the world allows for mobs or animals + long time = this.worldData.getTime(); + if (this.getGameRules().getBoolean("doMobSpawning") && this.worldData.getType() != WorldType.DEBUG_ALL_BLOCK_STATES && (this.allowMonsters || this.allowAnimals) && (this instanceof WorldServer && this.players.size() > 0)) { + timings.mobSpawn.startTiming(); // Spigot + this.spawnerCreature.a(this, this.allowMonsters && (this.ticksPerMonsterSpawns != 0 && time % this.ticksPerMonsterSpawns == 0L), this.allowAnimals && (this.ticksPerAnimalSpawns != 0 && time % this.ticksPerAnimalSpawns == 0L), this.worldData.getTime() % 400L == 0L); + this.getChunkProvider().a(this, this.allowMonsters && (this.ticksPerMonsterSpawns != 0 && time % this.ticksPerMonsterSpawns == 0L), this.allowAnimals && (this.ticksPerAnimalSpawns != 0 && time % this.ticksPerAnimalSpawns == 0L)); + timings.mobSpawn.stopTiming(); // Spigot + // CraftBukkit end + } + + timings.doChunkUnload.startTiming(); // Spigot + this.methodProfiler.exitEnter("chunkSource"); + this.chunkProvider.unloadChunks(booleansupplier); + int j = this.a(1.0F); + + if (j != this.c()) { + this.c(j); + } + + this.worldData.setTime(this.worldData.getTime() + 1L); + if (this.getGameRules().getBoolean("doDaylightCycle")) { + this.worldData.setDayTime(this.worldData.getDayTime() + 1L); + } + + timings.doChunkUnload.stopTiming(); // Spigot + this.methodProfiler.exitEnter("tickPending"); + timings.scheduledBlocks.startTiming(); // Paper + this.q(); + timings.scheduledBlocks.stopTiming(); // Paper + this.methodProfiler.exitEnter("tickBlocks"); + timings.chunkTicks.startTiming(); // Paper + this.n_(); + timings.chunkTicks.stopTiming(); // Paper + this.methodProfiler.exitEnter("chunkMap"); + timings.doChunkMap.startTiming(); // Spigot + this.manager.flush(); + timings.doChunkMap.stopTiming(); // Spigot + this.methodProfiler.exitEnter("village"); + timings.doVillages.startTiming(); // Spigot + this.villages.tick(); + this.siegeManager.a(); + timings.doVillages.stopTiming(); // Spigot + this.methodProfiler.exitEnter("portalForcer"); + timings.doPortalForcer.startTiming(); // Spigot + this.portalTravelAgent.a(this.getTime()); + timings.doPortalForcer.stopTiming(); // Spigot + this.methodProfiler.exit(); + timings.doSounds.startTiming(); // Spigot + this.an(); + timings.doSounds.stopTiming(); // Spigot + this.P = false; + + timings.doChunkGC.startTiming();// Spigot + this.getWorld().processChunkGC(); // CraftBukkit + timings.doChunkGC.stopTiming(); // Spigot + } + + public boolean j_() { + return this.P; + } + + @Nullable + public BiomeBase.BiomeMeta a(EnumCreatureType enumcreaturetype, BlockPosition blockposition) { + List list = this.getChunkProvider().a(enumcreaturetype, blockposition); + + return list.isEmpty() ? null : (BiomeBase.BiomeMeta) WeightedRandom.a(this.random, list); + } + + public boolean a(EnumCreatureType enumcreaturetype, BiomeBase.BiomeMeta biomebase_biomemeta, BlockPosition blockposition) { + List list = this.getChunkProvider().a(enumcreaturetype, blockposition); + + return list != null && !list.isEmpty() ? list.contains(biomebase_biomemeta) : false; + } + + public void everyoneSleeping() { + this.J = false; + if (!this.players.isEmpty()) { + int i = 0; + int j = 0; + Iterator iterator = this.players.iterator(); + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + if (entityhuman.isSpectator()) { + ++i; + } else if (entityhuman.isSleeping() || entityhuman.fauxSleeping) { + ++j; + } + } + + this.J = j > 0 && j >= this.players.size() - i; + } + + } + + public ScoreboardServer getScoreboard() { + return this.server.getScoreboard(); + } + + protected void i() { + this.J = false; + List list = (List) this.players.stream().filter(EntityHuman::isSleeping).collect(Collectors.toList()); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + entityhuman.a(false, false, true); + } + + if (this.getGameRules().getBoolean("doWeatherCycle")) { + this.b(); + } + + } + + private void b() { + // CraftBukkit start + this.worldData.setStorm(false); + // If we stop due to everyone sleeping we should reset the weather duration to some other random value. + // Not that everyone ever manages to get the whole server to sleep at the same time.... + if (!this.worldData.hasStorm()) { + this.worldData.setWeatherDuration(0); + } + // CraftBukkit end + this.worldData.setThundering(false); + // CraftBukkit start + // If we stop due to everyone sleeping we should reset the weather duration to some other random value. + // Not that everyone ever manages to get the whole server to sleep at the same time.... + if (!this.worldData.isThundering()) { + this.worldData.setThunderDuration(0); + } + // CraftBukkit end + } + + public boolean everyoneDeeplySleeping() { + if (this.J && !this.isClientSide) { + Iterator iterator = this.players.iterator(); + + // CraftBukkit - This allows us to assume that some people are in bed but not really, allowing time to pass in spite of AFKers + boolean foundActualSleepers = false; + + EntityHuman entityhuman; + + do { + if (!iterator.hasNext()) { + return foundActualSleepers; + } + + entityhuman = (EntityHuman) iterator.next(); + + // CraftBukkit start + if (entityhuman.isDeeplySleeping()) { + foundActualSleepers = true; + } + } while (!entityhuman.isSpectator() || entityhuman.isDeeplySleeping() || entityhuman.fauxSleeping); + // CraftBukkit end + + return false; + } else { + return false; + } + } + + public boolean isChunkLoaded(int i, int j, boolean flag) { + return this.a(i, j); + } + + public boolean a(int i, int j) { + return this.getChunkProvider().isLoaded(i, j); + } + + protected void l() { + this.methodProfiler.enter("playerCheckLight"); + if (spigotConfig.randomLightUpdates && !this.players.isEmpty()) { // Spigot + int i = this.random.nextInt(this.players.size()); + EntityHuman entityhuman = (EntityHuman) this.players.get(i); + int j = MathHelper.floor(entityhuman.locX) + this.random.nextInt(11) - 5; + int k = MathHelper.floor(entityhuman.locY) + this.random.nextInt(11) - 5; + int l = MathHelper.floor(entityhuman.locZ) + this.random.nextInt(11) - 5; + + this.r(new BlockPosition(j, k, l)); + } + + this.methodProfiler.exit(); + } + + protected void n_() { + this.l(); + if (this.worldData.getType() == WorldType.DEBUG_ALL_BLOCK_STATES) { + Iterator iterator = this.manager.b(); + + while (iterator.hasNext()) { + ((Chunk) iterator.next()).d(false); + } + + } else { + int i = this.getGameRules().c("randomTickSpeed"); + boolean flag = this.isRaining(); + boolean flag1 = this.Y(); + + this.methodProfiler.enter("pollingChunks"); + + for (Iterator iterator1 = this.manager.b(); iterator1.hasNext(); this.methodProfiler.exit()) { + this.methodProfiler.enter("getChunk"); + Chunk chunk = (Chunk) iterator1.next(); + int j = chunk.locX * 16; + int k = chunk.locZ * 16; + + this.methodProfiler.exitEnter("checkNextLight"); + chunk.x(); + this.methodProfiler.exitEnter("tickChunk"); + chunk.d(false); + if ( !chunk.areNeighborsLoaded( 1 ) ) continue; // Spigot + this.methodProfiler.exitEnter("thunder"); + int l; + BlockPosition blockposition; + + if (!this.paperConfig.disableThunder && flag && flag1 && this.random.nextInt(100000) == 0) { // Paper - Disable thunder + this.m = this.m * 3 + 1013904223; + l = this.m >> 2; + blockposition = this.a(new BlockPosition(j + (l & 15), 0, k + (l >> 8 & 15))); + if (this.isRainingAt(blockposition)) { + DifficultyDamageScaler difficultydamagescaler = this.getDamageScaler(blockposition); + boolean flag2 = this.getGameRules().getBoolean("doMobSpawning") && this.random.nextDouble() < (double) difficultydamagescaler.b() * paperConfig.skeleHorseSpawnChance; // Paper + + if (flag2) { + EntityHorseSkeleton entityhorseskeleton = EntityTypes.SKELETON_HORSE.create(this); // Paper + + entityhorseskeleton.s(true); + entityhorseskeleton.setAgeRaw(0); + entityhorseskeleton.setPosition((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ()); + this.addEntity(entityhorseskeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); // CraftBukkit + } + + this.strikeLightning(new EntityLightning(this, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, flag2), org.bukkit.event.weather.LightningStrikeEvent.Cause.WEATHER); // CraftBukkit + } + } + + this.methodProfiler.exitEnter("iceandsnow"); + if (!this.paperConfig.disableIceAndSnow && this.random.nextInt(16) == 0) { // Paper - Disable ice and snow + this.m = this.m * 3 + 1013904223; + l = this.m >> 2; + blockposition = this.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING, new BlockPosition(j + (l & 15), 0, k + (l >> 8 & 15))); + BlockPosition blockposition1 = blockposition.down(); + BiomeBase biomebase = this.getBiome(blockposition); + + if (biomebase.a((IWorldReader) this, blockposition1)) { + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition1, Blocks.ICE.getBlockData(), null); // CraftBukkit + } + + if (flag && biomebase.b(this, blockposition)) { + org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(this, blockposition, Blocks.SNOW.getBlockData(), null); // CraftBukkit + } + + if (flag && this.getBiome(blockposition1).c() == BiomeBase.Precipitation.RAIN) { + this.getType(blockposition1).getBlock().c(this, blockposition1); + } + } + + this.methodProfiler.exitEnter("tickBlocks"); + timings.chunkTicksBlocks.startTiming(); // Paper + if (i > 0) { + ChunkSection[] achunksection = chunk.getSections(); + int i1 = achunksection.length; + + for (int j1 = 0; j1 < i1; ++j1) { + ChunkSection chunksection = achunksection[j1]; + + if (chunksection != Chunk.a && chunksection.b()) { + for (int k1 = 0; k1 < i; ++k1) { + this.m = this.m * 3 + 1013904223; + int l1 = this.m >> 2; + int i2 = l1 & 15; + int j2 = l1 >> 8 & 15; + int k2 = l1 >> 16 & 15; + IBlockData iblockdata = chunksection.getType(i2, k2, j2); + Fluid fluid = chunksection.b(i2, k2, j2); + + this.methodProfiler.enter("randomTick"); + if (iblockdata.t()) { + iblockdata.b((World) this, new BlockPosition(i2 + j, k2 + chunksection.getYPosition(), j2 + k), this.random); + } + + if (fluid.h()) { + fluid.b(this, new BlockPosition(i2 + j, k2 + chunksection.getYPosition(), j2 + k), this.random); + } + + this.methodProfiler.exit(); + } + } + } + } + timings.chunkTicksBlocks.stopTiming(); // Paper + } + + this.methodProfiler.exit(); + } + } + + protected BlockPosition a(BlockPosition blockposition) { + BlockPosition blockposition1 = this.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING, blockposition); + AxisAlignedBB axisalignedbb = (new AxisAlignedBB(blockposition1, new BlockPosition(blockposition1.getX(), this.getHeight(), blockposition1.getZ()))).g(3.0D); + List list = this.a(EntityLiving.class, axisalignedbb, (java.util.function.Predicate) (entityliving) -> { // CraftBukkit - decompile error + return entityliving != null && entityliving.isAlive() && this.e(entityliving.getChunkCoordinates()); + }); + + if (!list.isEmpty()) { + return ((EntityLiving) list.get(this.random.nextInt(list.size()))).getChunkCoordinates(); + } else { + if (blockposition1.getY() == -1) { + blockposition1 = blockposition1.up(2); + } + + return blockposition1; + } + } + + public void tickEntities() { + if (false && this.players.isEmpty()) { // CraftBukkit - this prevents entity cleanup, other issues on servers with no players + if (this.emptyTime++ >= 300) { + return; + } + } else { + this.p(); + } + + this.worldProvider.l(); + super.tickEntities(); + spigotConfig.currentPrimedTnt = 0; // Spigot + } + + protected void p_() { + super.p_(); + this.methodProfiler.exitEnter("players"); + + for (int i = 0; i < this.players.size(); ++i) { + Entity entity = (Entity) this.players.get(i); + Entity entity1 = entity.getVehicle(); + + if (entity1 != null) { + if (!entity1.dead && entity1.w(entity)) { + continue; + } + + entity.stopRiding(); + } + + this.methodProfiler.enter("tick"); + if (!entity.dead) { + try { + this.g(entity); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Ticking player"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Player being ticked"); + + entity.appendEntityCrashDetails(crashreportsystemdetails); + throw new ReportedException(crashreport); + } + } + + this.methodProfiler.exit(); + this.methodProfiler.enter("remove"); + if (entity.dead) { + int j = entity.chunkX; + int k = entity.chunkZ; + + if (entity.inChunk && this.isChunkLoaded(j, k, true)) { + this.getChunkAt(j, k).b(entity); + } + + this.entityList.remove(entity); + this.c(entity); + } + + this.methodProfiler.exit(); + } + + } + + public void p() { + this.emptyTime = 0; + } + + public void q() { + if (this.worldData.getType() != WorldType.DEBUG_ALL_BLOCK_STATES) { + this.nextTickListBlock.a(); + this.nextTickListFluid.a(); + } + } + + private void a(NextTickListEntry nextticklistentry) { + Fluid fluid = this.getFluid(nextticklistentry.a); + + if (fluid.c() == nextticklistentry.a()) { + fluid.a((World) this, nextticklistentry.a); + } + + } + + private void b(NextTickListEntry nextticklistentry) { + IBlockData iblockdata = this.getType(nextticklistentry.a); + + if (iblockdata.getBlock() == nextticklistentry.a()) { + iblockdata.a((World) this, nextticklistentry.a, this.random); + } + + } + + /* CraftBukkit start - We prevent spawning in general, so this butchering is not needed + public void entityJoinedWorld(Entity entity, boolean flag) { + if (!this.getSpawnAnimals() && (entity instanceof EntityAnimal || entity instanceof EntityWaterAnimal)) { + entity.die(); + } + + if (!this.getSpawnNPCs() && entity instanceof NPC) { + entity.die(); + } + + super.entityJoinedWorld(entity, flag); + } + // CraftBukkit end */ + + private boolean getSpawnNPCs() { + return this.server.getSpawnNPCs(); + } + + private boolean getSpawnAnimals() { + return this.server.getSpawnAnimals(); + } + + protected IChunkProvider r() { + IChunkLoader ichunkloader = this.dataManager.createChunkLoader(this.worldProvider); + + // CraftBukkit start + org.bukkit.craftbukkit.generator.InternalChunkGenerator gen; + + if (this.generator != null) { + gen = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, this.getSeed(), this.generator); + } else if (this.worldProvider instanceof WorldProviderHell) { + gen = new org.bukkit.craftbukkit.generator.NetherChunkGenerator(this, this.getSeed()); + } else if (this.worldProvider instanceof WorldProviderTheEnd) { + gen = new org.bukkit.craftbukkit.generator.SkyLandsChunkGenerator(this, this.getSeed()); + } else { + gen = new org.bukkit.craftbukkit.generator.NormalChunkGenerator(this, this.getSeed()); + } + + return com.destroystokyo.paper.PaperConfig.asyncChunks ? new PaperAsyncChunkProvider(this, ichunkloader, gen, this.server) : new ChunkProviderServer(this, ichunkloader, gen, this.server); // Paper - async chunks + // CraftBukkit end + } + + public boolean a(EntityHuman entityhuman, BlockPosition blockposition) { + return !this.server.a(this, blockposition, entityhuman) && this.getWorldBorder().a(blockposition); + } + + public void a(WorldSettings worldsettings) { + if (!this.worldData.v()) { + try { + this.b(worldsettings); + if (this.worldData.getType() == WorldType.DEBUG_ALL_BLOCK_STATES) { + this.am(); + } + + super.a(worldsettings); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Exception initializing level"); + + try { + this.a(crashreport); + } catch (Throwable throwable1) { + ; + } + + throw new ReportedException(crashreport); + } + + this.worldData.d(true); + } + + } + + private void am() { + this.worldData.f(false); + this.worldData.c(true); + this.worldData.setStorm(false); + this.worldData.setThundering(false); + this.worldData.g(1000000000); + this.worldData.setDayTime(6000L); + this.worldData.setGameType(EnumGamemode.SPECTATOR); + this.worldData.g(false); + this.worldData.setDifficulty(EnumDifficulty.PEACEFUL); + this.worldData.e(true); + this.getGameRules().set("doDaylightCycle", "false", this.server); + } + + private void b(WorldSettings worldsettings) { + if (!this.worldProvider.canRespawn()) { + this.worldData.setSpawn(BlockPosition.ZERO.up(this.chunkProvider.getChunkGenerator().getSpawnHeight())); + } else if (this.worldData.getType() == WorldType.DEBUG_ALL_BLOCK_STATES) { + this.worldData.setSpawn(BlockPosition.ZERO.up()); + } else { + WorldChunkManager worldchunkmanager = this.chunkProvider.getChunkGenerator().getWorldChunkManager(); + List list = worldchunkmanager.a(); + Random random = new Random(this.getSeed()); + BlockPosition blockposition = worldchunkmanager.a(0, 0, 256, list, random); + ChunkCoordIntPair chunkcoordintpair = blockposition == null ? new ChunkCoordIntPair(0, 0) : new ChunkCoordIntPair(blockposition); + + // CraftBukkit start + if (this.generator != null) { + Random rand = new Random(this.getSeed()); + org.bukkit.Location spawn = this.generator.getFixedSpawnLocation(((WorldServer) this).getWorld(), rand); + + if (spawn != null) { + if (spawn.getWorld() != ((WorldServer) this).getWorld()) { + throw new IllegalStateException("Cannot set spawn point for " + this.worldData.getName() + " to be in another world (" + spawn.getWorld().getName() + ")"); + } else { + this.worldData.setSpawn(new BlockPosition(spawn.getBlockX(), spawn.getBlockY(), spawn.getBlockZ())); + return; + } + } + } + // CraftBukkit end + + if (blockposition == null) { + WorldServer.a.warn("Unable to find spawn biome"); + } + + boolean flag = false; + Iterator iterator = TagsBlock.VALID_SPAWN.a().iterator(); + + while (iterator.hasNext()) { + Block block = (Block) iterator.next(); + + if (worldchunkmanager.b().contains(block.getBlockData())) { + flag = true; + break; + } + } + + this.worldData.setSpawn(chunkcoordintpair.h().a(8, this.chunkProvider.getChunkGenerator().getSpawnHeight(), 8)); + int i = 0; + int j = 0; + int k = 0; + int l = -1; + boolean flag1 = true; + + for (int i1 = 0; i1 < 1024; ++i1) { + if (i > -16 && i <= 16 && j > -16 && j <= 16) { + BlockPosition blockposition1 = this.worldProvider.a(new ChunkCoordIntPair(chunkcoordintpair.x + i, chunkcoordintpair.z + j), flag); + + if (blockposition1 != null) { + this.worldData.setSpawn(blockposition1); + break; + } + } + + if (i == j || i < 0 && i == -j || i > 0 && i == 1 - j) { + int j1 = k; + + k = -l; + l = j1; + } + + i += k; + j += l; + } + + if (worldsettings.c()) { + this.s(); + } + + } + } + + protected void s() { + WorldGenBonusChest worldgenbonuschest = new WorldGenBonusChest(); + + for (int i = 0; i < 10; ++i) { + int j = this.worldData.b() + this.random.nextInt(6) - this.random.nextInt(6); + int k = this.worldData.d() + this.random.nextInt(6) - this.random.nextInt(6); + BlockPosition blockposition = this.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, new BlockPosition(j, 0, k)).up(); + + if (worldgenbonuschest.a(this, this.chunkProvider.getChunkGenerator(), this.random, blockposition, WorldGenFeatureConfiguration.e)) { + break; + } + } + + } + + @Nullable + public BlockPosition getDimensionSpawn() { + return this.worldProvider.d(); + } + + public void save(boolean flag, @Nullable IProgressUpdate iprogressupdate) throws ExceptionWorldConflict { + ChunkProviderServer chunkproviderserver = this.getChunkProvider(); + + if (chunkproviderserver.d()) { + if (flag) org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); // CraftBukkit // Paper - Incremental Auto Saving - Only fire event on full save + timings.worldSave.startTiming(); // Paper + if (flag || server.serverAutoSave) { // Paper + if (iprogressupdate != null) { + iprogressupdate.a(new ChatMessage("menu.savingLevel", new Object[0])); + } + + this.a(); + if (iprogressupdate != null) { + iprogressupdate.c(new ChatMessage("menu.savingChunks", new Object[0])); + } + } // Paper + + timings.worldSaveChunks.startTiming(); // Paper + chunkproviderserver.a(flag); + timings.worldSaveChunks.stopTiming(); // Paper + // CraftBukkit - ArrayList -> Collection + /* //Paper start - disable vanilla chunk GC + java.util.Collection list = chunkproviderserver.a(); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + Chunk chunk = (Chunk) iterator.next(); + + if (chunk != null && !this.manager.a(chunk.locX, chunk.locZ)) { + chunkproviderserver.unload(chunk); + } + }*/ + // Paper end + timings.worldSave.stopTiming(); // Paper + } + } + + public void flushSave() { + ChunkProviderServer chunkproviderserver = this.getChunkProvider(); + + if (chunkproviderserver.d()) { + chunkproviderserver.c(); + } + } + + protected void a() throws ExceptionWorldConflict { + timings.worldSaveLevel.startTiming(); // Paper + this.checkSession(); + Iterator iterator = this.server.getWorlds().iterator(); + + while (iterator.hasNext()) { + WorldServer worldserver = (WorldServer) iterator.next(); + + if (worldserver instanceof SecondaryWorldServer) { + ((SecondaryWorldServer) worldserver).t_(); + } + } + + this.worldData.a(this.getWorldBorder().getSize()); + this.worldData.d(this.getWorldBorder().getCenterX()); + this.worldData.c(this.getWorldBorder().getCenterZ()); + this.worldData.e(this.getWorldBorder().getDamageBuffer()); + this.worldData.f(this.getWorldBorder().getDamageAmount()); + this.worldData.h(this.getWorldBorder().getWarningDistance()); + this.worldData.i(this.getWorldBorder().getWarningTime()); + this.worldData.b(this.getWorldBorder().j()); + this.worldData.c(this.getWorldBorder().i()); + this.worldData.c(this.server.getBossBattleCustomData().c()); + this.dataManager.saveWorldData(this.worldData, this.server.getPlayerList().t()); + this.h().a(); + timings.worldSaveLevel.stopTiming(); // Paper + } + + // CraftBukkit start + public boolean addEntity(Entity entity, SpawnReason spawnReason) { // Changed signature, added SpawnReason + // World.addEntity(Entity) will call this, and we still want to perform + // existing entity checking when it's called with a SpawnReason + return this.j(entity) ? super.addEntity(entity, spawnReason) : false; + } + // CraftBukkit end + + public void a(Stream stream) { + stream.forEach((entity) -> { + if (this.j(entity)) { + this.entityList.add(entity); + this.b(entity); + } + + }); + } + + private boolean j(Entity entity) { + if (entity.dead) { + // Paper start + if (DEBUG_ENTITIES) { + new Throwable("Tried to add entity " + entity + " but it was marked as removed already").printStackTrace(); // CraftBukkit + getAddToWorldStackTrace(entity).printStackTrace(); + } + // Paper end + return false; + } else { + UUID uuid = entity.getUniqueID(); + + if (this.entitiesByUUID.containsKey(uuid)) { + Entity entity1 = (Entity) this.entitiesByUUID.get(uuid); + + if (this.g.contains(entity1) || entity1.dead) { // Paper - if dupe is dead, overwrite + this.g.remove(entity1); + } else { + if (!(entity instanceof EntityHuman)) { + if (DEBUG_ENTITIES && entity.world.paperConfig.duplicateUUIDMode != com.destroystokyo.paper.PaperWorldConfig.DuplicateUUIDMode.NOTHING) { + WorldServer.a.error("Keeping entity {} that already exists with UUID {}", entity1, uuid.toString()); // CraftBukkit // Paper + WorldServer.a.error("Deleting duplicate entity {}", entity); // Paper + + if (entity1.addedToWorldStack != null) { + entity1.addedToWorldStack.printStackTrace(); + } + getAddToWorldStackTrace(entity).printStackTrace(); + } + return false; + } + + WorldServer.a.warn("Force-added player with duplicate UUID {}", uuid.toString()); + } + + this.removeEntity(entity1); + } + + return true; + } + } + + protected void b(Entity entity) { + super.b(entity); + this.entitiesById.a(entity.getId(), entity); + // Paper start + if (DEBUG_ENTITIES) { + entity.addedToWorldStack = getAddToWorldStackTrace(entity); + } + + Entity old = this.entitiesByUUID.put(entity.getUniqueID(), entity); + if (old != null && old.getId() != entity.getId() && old.valid && entity.world.paperConfig.duplicateUUIDMode != com.destroystokyo.paper.PaperWorldConfig.DuplicateUUIDMode.NOTHING) { + Logger logger = LogManager.getLogger(); + logger.error("Overwrote an existing entity " + old + " with " + entity); + if (DEBUG_ENTITIES) { + if (old.addedToWorldStack != null) { + old.addedToWorldStack.printStackTrace(); + } else { + logger.error("Oddly, the old entity was not added to the world in the normal way. Plugins?"); + } + entity.addedToWorldStack.printStackTrace(); + } + } + // Paper end + Entity[] aentity = entity.bi(); + + if (aentity != null) { + Entity[] aentity1 = aentity; + int i = aentity.length; + + for (int j = 0; j < i; ++j) { + Entity entity1 = aentity1[j]; + + this.entitiesById.a(entity1.getId(), entity1); + } + } + + } + + protected void c(Entity entity) { + if (!this.entitiesByUUID.containsKey(entity.getUniqueID()) && !entity.valid) return; // Paper - Already removed, dont fire twice - this looks like it can happen even without our changes + super.c(entity); + this.entitiesById.d(entity.getId()); + this.entitiesByUUID.remove(entity.getUniqueID()); + Entity[] aentity = entity.bi(); + + if (aentity != null) { + Entity[] aentity1 = aentity; + int i = aentity.length; + + for (int j = 0; j < i; ++j) { + Entity entity1 = aentity1[j]; + + this.entitiesById.d(entity1.getId()); + } + } + + } + + // CraftBukkit start + public boolean strikeLightning(Entity entity) { + return this.strikeLightning(entity, LightningStrikeEvent.Cause.UNKNOWN); + } + + public boolean strikeLightning(Entity entity, LightningStrikeEvent.Cause cause) { + LightningStrikeEvent lightning = new LightningStrikeEvent(this.getWorld(), (org.bukkit.entity.LightningStrike) entity.getBukkitEntity(), cause); + this.getServer().getPluginManager().callEvent(lightning); + + if (lightning.isCancelled()) { + return false; + } + // CraftBukkit end + if (super.strikeLightning(entity)) { + this.server.getPlayerList().sendPacketNearby((EntityHuman) null, entity.locX, entity.locY, entity.locZ, this.paperConfig.maxLightningFlashDistance, this, new PacketPlayOutSpawnEntityWeather(entity)); // CraftBukkit - Use dimension, // Paper - use world instead of dimension, limit lightning strike effect distance + return true; + } else { + return false; + } + } + + public void broadcastEntityEffect(Entity entity, byte b0) { + this.getTracker().sendPacketToEntity(entity, new PacketPlayOutEntityStatus(entity, b0)); + } + + public ChunkProviderServer getChunkProvider() { + return (ChunkProviderServer) super.getChunkProvider(); + } + + public Explosion createExplosion(@Nullable Entity entity, DamageSource damagesource, double d0, double d1, double d2, float f, boolean flag, boolean flag1) { + // CraftBukkit start + Explosion explosion = super.createExplosion(entity, damagesource, d0, d1, d2, f, flag, flag1); + + if (explosion.wasCanceled) { + return explosion; + } + + /* Remove + Explosion explosion = new Explosion(this, entity, d0, d1, d2, f, flag, flag1); + + if (damagesource != null) { + explosion.a(damagesource); + } + + explosion.a(); + explosion.a(false); + */ + // CraftBukkit end - TODO: Check if explosions are still properly implemented + if (!flag1) { + explosion.clearBlocks(); + } + + Iterator iterator = this.players.iterator(); + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + if (entityhuman.d(d0, d1, d2) < 4096.0D) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutExplosion(d0, d1, d2, f, explosion.getBlocks(), (Vec3D) explosion.c().get(entityhuman))); + } + } + + return explosion; + } + + public void playBlockAction(BlockPosition blockposition, Block block, int i, int j) { + this.d.add(new BlockActionData(blockposition, block, i, j)); + } + + private void an() { + while (!this.d.isEmpty()) { + BlockActionData blockactiondata = (BlockActionData) this.d.removeFirst(); + + if (this.a(blockactiondata)) { + // CraftBukkit - this.worldProvider.dimension -> this.dimension, // Paper - dimension -> world + this.server.getPlayerList().sendPacketNearby((EntityHuman) null, (double) blockactiondata.a().getX(), (double) blockactiondata.a().getY(), (double) blockactiondata.a().getZ(), 64.0D, this, new PacketPlayOutBlockAction(blockactiondata.a(), blockactiondata.b(), blockactiondata.c(), blockactiondata.d())); + } + } + + } + + private boolean a(BlockActionData blockactiondata) { + IBlockData iblockdata = this.getType(blockactiondata.a()); + + return iblockdata.getBlock() == blockactiondata.b() ? iblockdata.a(this, blockactiondata.a(), blockactiondata.c(), blockactiondata.d()) : false; + } + + public void close() { + this.dataManager.a(); + super.close(); + } + + protected void w() { + boolean flag = this.isRaining(); + + super.w(); + /* CraftBukkit start + if (this.o != this.p) { + this.server.getPlayerList().a((Packet) (new PacketPlayOutGameStateChange(7, this.p)), this.worldProvider.getDimensionManager()); + } + + if (this.q != this.r) { + this.server.getPlayerList().a((Packet) (new PacketPlayOutGameStateChange(8, this.r)), this.worldProvider.getDimensionManager()); + } + + if (flag != this.isRaining()) { + if (flag) { + this.server.getPlayerList().sendAll(new PacketPlayOutGameStateChange(2, 0.0F)); + } else { + this.server.getPlayerList().sendAll(new PacketPlayOutGameStateChange(1, 0.0F)); + } + + this.server.getPlayerList().sendAll(new PacketPlayOutGameStateChange(7, this.p)); + this.server.getPlayerList().sendAll(new PacketPlayOutGameStateChange(8, this.r)); + } + // */ + if (flag != this.isRaining()) { + // Only send weather packets to those affected + for (int i = 0; i < this.players.size(); ++i) { + if (((EntityPlayer) this.players.get(i)).world == this) { + ((EntityPlayer) this.players.get(i)).setPlayerWeather((!flag ? WeatherType.DOWNFALL : WeatherType.CLEAR), false); + } + } + } + for (int i = 0; i < this.players.size(); ++i) { + if (((EntityPlayer) this.players.get(i)).world == this) { + ((EntityPlayer) this.players.get(i)).updateWeather(this.o, this.p, this.q, this.r); + } + } + // CraftBukkit end + + } + + public TickListServer getBlockTickList() { + return this.nextTickListBlock; + } + + public TickListServer getFluidTickList() { + return this.nextTickListFluid; + } + + @Nonnull + public MinecraftServer getMinecraftServer() { + return this.server; + } + + public EntityTracker getTracker() { + return this.tracker; + } + + public PlayerChunkMap getPlayerChunkMap() { + return this.manager; + } + + public PortalTravelAgent getTravelAgent() { + return this.portalTravelAgent; + } + + public DefinedStructureManager D() { + return this.dataManager.h(); + } + + public int a(T t0, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6) { + // CraftBukkit - visibility api support + return sendParticles(null, t0, d0, d1, d2, i, d3, d4, d5, d6, false); + } + + public int sendParticles(EntityPlayer sender, T t0, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6, boolean force) { + // Paper start - Particle API Expansion + return sendParticles(players, sender, t0, d0, d1, d2, i, d3, d4, d5, d6, force); + } + public int sendParticles(List receivers, EntityPlayer sender, T t0, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6, boolean force) { + // Paper end + PacketPlayOutWorldParticles packetplayoutworldparticles = new PacketPlayOutWorldParticles(t0, force, (float) d0, (float) d1, (float) d2, (float) d3, (float) d4, (float) d5, (float) d6, i); + // CraftBukkit end + int j = 0; + + for (EntityHuman entityhuman : receivers) { // Paper - Particle API Expansion + EntityPlayer entityplayer = (EntityPlayer) entityhuman; // Paper - Particle API Expansion + if (sender != null && !entityplayer.getBukkitEntity().canSee(sender.getBukkitEntity())) continue; // CraftBukkit + + if (this.a(entityplayer, force, d0, d1, d2, packetplayoutworldparticles)) { // CraftBukkit + ++j; + } + } + + return j; + } + + public boolean a(EntityPlayer entityplayer, T t0, boolean flag, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6) { + Packet packet = new PacketPlayOutWorldParticles(t0, flag, (float) d0, (float) d1, (float) d2, (float) d3, (float) d4, (float) d5, (float) d6, i); + + return this.a(entityplayer, flag, d0, d1, d2, packet); + } + + private boolean a(EntityPlayer entityplayer, boolean flag, double d0, double d1, double d2, Packet packet) { + if (entityplayer.getWorldServer() != this) { + return false; + } else { + BlockPosition blockposition = entityplayer.getChunkCoordinates(); + double d3 = blockposition.distanceSquared(d0, d1, d2); + + if (d3 > 1024.0D && (!flag || d3 > 262144.0D)) { + return false; + } else { + entityplayer.playerConnection.sendPacket(packet); + return true; + } + } + } + + @Nullable + public Entity getEntity(UUID uuid) { + return (Entity) this.entitiesByUUID.get(uuid); + } + + public ListenableFuture postToMainThread(Runnable runnable) { + return this.server.postToMainThread(runnable); + } + + public boolean isMainThread() { + return this.server.isMainThread(); + } + + @Nullable + public BlockPosition a(String s, BlockPosition blockposition, int i, boolean flag) { + return this.getChunkProvider().a(this, s, blockposition, i, flag); + } + + public CraftingManager getCraftingManager() { + return this.server.getCraftingManager(); + } + + public TagRegistry F() { + return this.server.getTagRegistry(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftArt.java b/src/main/java/org/bukkit/craftbukkit/CraftArt.java new file mode 100644 index 000000000000..378cd360dd90 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftArt.java @@ -0,0 +1,34 @@ +package org.bukkit.craftbukkit; + +import com.google.common.base.Preconditions; +import com.google.common.collect.BiMap; +import com.google.common.collect.ImmutableBiMap; +import net.minecraft.server.IRegistry; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.Paintings; +import org.bukkit.Art; + +public class CraftArt { + private static final BiMap artwork; + + static { + ImmutableBiMap.Builder artworkBuilder = ImmutableBiMap.builder(); + for (MinecraftKey key : IRegistry.MOTIVE.keySet()) { + artworkBuilder.put(IRegistry.MOTIVE.get(key), Art.getByName(key.getKey())); + } + + artwork = artworkBuilder.build(); + } + + public static Art NotchToBukkit(Paintings art) { + Art bukkit = artwork.get(art); + Preconditions.checkArgument(bukkit != null); + return bukkit; + } + + public static Paintings BukkitToNotch(Art art) { + Paintings nms = artwork.inverse().get(art); + Preconditions.checkArgument(nms != null); + return nms; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java new file mode 100644 index 000000000000..9ba2fa5de004 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java @@ -0,0 +1,305 @@ +package org.bukkit.craftbukkit; + +import com.google.common.base.Preconditions; +import java.lang.ref.WeakReference; +import java.util.Arrays; + +import java.util.Random; +import net.minecraft.server.*; + +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.entity.Entity; +import org.bukkit.ChunkSnapshot; + +public class CraftChunk implements Chunk { + private WeakReference weakChunk; + private final WorldServer worldServer; + private final int x; + private final int z; + private static final byte[] emptyData = new byte[2048]; + private static final DataPaletteBlock emptyBlockIDs = new ChunkSection(0, false).getBlocks(); + private static final byte[] emptySkyLight = new byte[2048]; + + public CraftChunk(net.minecraft.server.Chunk chunk) { + this.weakChunk = new WeakReference(chunk); + + worldServer = (WorldServer) getHandle().world; + x = getHandle().locX; + z = getHandle().locZ; + } + + public World getWorld() { + return worldServer.getWorld(); + } + + public CraftWorld getCraftWorld() { + return (CraftWorld) getWorld(); + } + + public net.minecraft.server.Chunk getHandle() { + net.minecraft.server.Chunk c = weakChunk.get(); + + if (c == null) { + c = worldServer.getChunkAt(x, z); + + weakChunk = new WeakReference(c); + } + + return c; + } + + void breakLink() { + weakChunk.clear(); + } + + public int getX() { + return x; + } + + public int getZ() { + return z; + } + + @Override + public String toString() { + return "CraftChunk{" + "x=" + getX() + "z=" + getZ() + '}'; + } + + public Block getBlock(int x, int y, int z) { + validateChunkCoordinates(x, y, z); + + return new CraftBlock(worldServer, new BlockPosition((this.x << 4) | x, y, (this.z << 4) | z)); + } + + public Entity[] getEntities() { + int count = 0, index = 0; + net.minecraft.server.Chunk chunk = getHandle(); + + for (int i = 0; i < 16; i++) { + count += chunk.entitySlices[i].size(); + } + + Entity[] entities = new Entity[count]; + + for (int i = 0; i < 16; i++) { + + for (Object obj : chunk.entitySlices[i].toArray()) { + if (!(obj instanceof net.minecraft.server.Entity)) { + continue; + } + + entities[index++] = ((net.minecraft.server.Entity) obj).getBukkitEntity(); + } + } + + return entities; + } + + // Paper start + public BlockState[] getTileEntities() { + return getTileEntities(true); + } + public BlockState[] getTileEntities(boolean useSnapshot) { + // Paper end + int index = 0; + net.minecraft.server.Chunk chunk = getHandle(); + + BlockState[] entities = new BlockState[chunk.tileEntities.size()]; + + for (Object obj : chunk.tileEntities.keySet().toArray()) { + if (!(obj instanceof BlockPosition)) { + continue; + } + + BlockPosition position = (BlockPosition) obj; + entities[index++] = worldServer.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()).getState(useSnapshot); // Paper + } + + return entities; + } + + public boolean isLoaded() { + return getWorld().isChunkLoaded(this); + } + + public boolean load() { + return getWorld().loadChunk(getX(), getZ(), true); + } + + public boolean load(boolean generate) { + return getWorld().loadChunk(getX(), getZ(), generate); + } + + public boolean unload() { + return getWorld().unloadChunk(getX(), getZ()); + } + + @Override + public boolean isSlimeChunk() { + // 987234911L is deterimined in EntitySlime when seeing if a slime can spawn in a chunk + return SeededRandom.a(getX(), getZ(), getWorld().getSeed(), worldServer.spigotConfig.slimeSeed).nextInt(10) == 0; + } + + public boolean unload(boolean save) { + return getWorld().unloadChunk(getX(), getZ(), save); + } + + public boolean unload(boolean save, boolean safe) { + return getWorld().unloadChunk(getX(), getZ(), save, safe); + } + + @Override + public boolean isForceLoaded() { + return getWorld().isChunkForceLoaded(getX(), getZ()); + } + + @Override + public void setForceLoaded(boolean forced) { + getWorld().setChunkForceLoaded(getX(), getZ(), forced); + } + + public ChunkSnapshot getChunkSnapshot() { + return getChunkSnapshot(true, false, false); + } + + public ChunkSnapshot getChunkSnapshot(boolean includeMaxBlockY, boolean includeBiome, boolean includeBiomeTempRain) { + net.minecraft.server.Chunk chunk = getHandle(); + + ChunkSection[] cs = chunk.getSections(); + DataPaletteBlock[] sectionBlockIDs = new DataPaletteBlock[cs.length]; + byte[][] sectionSkyLights = new byte[cs.length][]; + byte[][] sectionEmitLights = new byte[cs.length][]; + boolean[] sectionEmpty = new boolean[cs.length]; + + for (int i = 0; i < cs.length; i++) { + if (cs[i] == null) { // Section is empty? + sectionBlockIDs[i] = emptyBlockIDs; + sectionSkyLights[i] = emptySkyLight; + sectionEmitLights[i] = emptyData; + sectionEmpty[i] = true; + } else { // Not empty + NBTTagCompound data = new NBTTagCompound(); + cs[i].getBlocks().b(data, "Spigot", "Magic"); + + DataPaletteBlock blockids = new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE, net.minecraft.server.Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData()); // TODO: snapshot whole ChunkSection + blockids.a(data, "Spigot", "Magic"); + + sectionBlockIDs[i] = blockids; + + if (cs[i].getSkyLightArray() == null) { + sectionSkyLights[i] = emptySkyLight; + } else { + sectionSkyLights[i] = new byte[2048]; + System.arraycopy(cs[i].getSkyLightArray().asBytes(), 0, sectionSkyLights[i], 0, 2048); + } + sectionEmitLights[i] = new byte[2048]; + System.arraycopy(cs[i].getEmittedLightArray().asBytes(), 0, sectionEmitLights[i], 0, 2048); + } + } + + HeightMap hmap = null; + + if (includeMaxBlockY) { + hmap = new HeightMap(null, HeightMap.Type.LIGHT_BLOCKING); + hmap.a(chunk.heightMap.get(HeightMap.Type.LIGHT_BLOCKING).b()); + } + + BiomeBase[] biome = null; + double[] biomeTemp = null; + + if (includeBiome || includeBiomeTempRain) { + WorldChunkManager wcm = worldServer.getChunkProvider().getChunkGenerator().getWorldChunkManager(); + + if (includeBiome) { + biome = new BiomeBase[256]; + for (int i = 0; i < 256; i++) { + biome[i] = chunk.getBiome(new BlockPosition(i & 0xF, 0, i >> 4)); + } + } + + if (includeBiomeTempRain) { + biomeTemp = new double[256]; + float[] dat = getTemperatures(wcm, getX() << 4, getZ() << 4); + + for (int i = 0; i < 256; i++) { + biomeTemp[i] = dat[i]; + } + } + } + + World world = getWorld(); + return new CraftChunkSnapshot(getX(), getZ(), world.getName(), world.getFullTime(), sectionBlockIDs, sectionSkyLights, sectionEmitLights, sectionEmpty, hmap, biome, biomeTemp); + } + + public static ChunkSnapshot getEmptyChunkSnapshot(int x, int z, CraftWorld world, boolean includeBiome, boolean includeBiomeTempRain) { + BiomeBase[] biome = null; + double[] biomeTemp = null; + + if (includeBiome || includeBiomeTempRain) { + WorldChunkManager wcm = world.getHandle().getChunkProvider().getChunkGenerator().getWorldChunkManager(); + + if (includeBiome) { + biome = new BiomeBase[256]; + for (int i = 0; i < 256; i++) { + biome[i] = world.getHandle().getBiome(new BlockPosition((x << 4) + (i & 0xF), 0, (z << 4) + (i >> 4))); + } + } + + if (includeBiomeTempRain) { + biomeTemp = new double[256]; + float[] dat = getTemperatures(wcm, x << 4, z << 4); + + for (int i = 0; i < 256; i++) { + biomeTemp[i] = dat[i]; + } + } + } + + /* Fill with empty data */ + int hSection = world.getMaxHeight() >> 4; + DataPaletteBlock[] blockIDs = new DataPaletteBlock[hSection]; + byte[][] skyLight = new byte[hSection][]; + byte[][] emitLight = new byte[hSection][]; + boolean[] empty = new boolean[hSection]; + + for (int i = 0; i < hSection; i++) { + blockIDs[i] = emptyBlockIDs; + skyLight[i] = emptySkyLight; + emitLight[i] = emptyData; + empty[i] = true; + } + + return new CraftChunkSnapshot(x, z, world.getName(), world.getFullTime(), blockIDs, skyLight, emitLight, empty, new HeightMap(null, HeightMap.Type.LIGHT_BLOCKING), biome, biomeTemp); + } + + private static float[] getTemperatures(WorldChunkManager chunkmanager, int chunkX, int chunkZ) { + BiomeBase[] biomes = chunkmanager.getBiomes(chunkX, chunkZ, 16, 16); + float[] temps = new float[biomes.length]; + + for (int i = 0; i < biomes.length; i++) { + float temp = biomes[i].getTemperature(); // Vanilla of olde: ((int) biomes[i].temperature * 65536.0F) / 65536.0F + + if (temp > 1F) { + temp = 1F; + } + + temps[i] = temp; + } + + return temps; + } + + static void validateChunkCoordinates(int x, int y, int z) { + Preconditions.checkArgument(0 <= x && x <= 15, "x out of range (expected 0-15, got %s)", x); + Preconditions.checkArgument(0 <= y && y <= 255, "y out of range (expected 0-255, got %s)", y); + Preconditions.checkArgument(0 <= z && z <= 15, "z out of range (expected 0-15, got %s)", z); + } + + static { + Arrays.fill(emptySkyLight, (byte) 0xFF); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java b/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java new file mode 100644 index 000000000000..f3be8c202ebc --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java @@ -0,0 +1,119 @@ +package org.bukkit.craftbukkit; + +import com.google.common.base.Preconditions; +import org.bukkit.ChunkSnapshot; +import org.bukkit.Material; +import org.bukkit.block.Biome; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; + +import net.minecraft.server.BiomeBase; +import net.minecraft.server.DataPaletteBlock; +import net.minecraft.server.HeightMap; +import net.minecraft.server.IBlockData; + +/** + * Represents a static, thread-safe snapshot of chunk of blocks + * Purpose is to allow clean, efficient copy of a chunk data to be made, and then handed off for processing in another thread (e.g. map rendering) + */ +public class CraftChunkSnapshot implements ChunkSnapshot { + private final int x, z; + private final String worldname; + private final DataPaletteBlock[] blockids; + private final byte[][] skylight; + private final byte[][] emitlight; + private final boolean[] empty; + private final HeightMap hmap; // Height map + private final long captureFulltime; + private final BiomeBase[] biome; + private final double[] biomeTemp; + + CraftChunkSnapshot(int x, int z, String wname, long wtime, DataPaletteBlock[] sectionBlockIDs, byte[][] sectionSkyLights, byte[][] sectionEmitLights, boolean[] sectionEmpty, HeightMap hmap, BiomeBase[] biome, double[] biomeTemp) { + this.x = x; + this.z = z; + this.worldname = wname; + this.captureFulltime = wtime; + this.blockids = sectionBlockIDs; + this.skylight = sectionSkyLights; + this.emitlight = sectionEmitLights; + this.empty = sectionEmpty; + this.hmap = hmap; + this.biome = biome; + this.biomeTemp = biomeTemp; + } + + public int getX() { + return x; + } + + public int getZ() { + return z; + } + + public String getWorldName() { + return worldname; + } + + @Override + public Material getBlockType(int x, int y, int z) { + CraftChunk.validateChunkCoordinates(x, y, z); + + return CraftMagicNumbers.getMaterial(blockids[y >> 4].a(x, y & 0xF, z).getBlock()); + } + + @Override + public final BlockData getBlockData(int x, int y, int z) { + CraftChunk.validateChunkCoordinates(x, y, z); + + return CraftBlockData.fromData(blockids[y >> 4].a(x, y & 0xF, z)); + } + + @Override + public final int getData(int x, int y, int z) { + CraftChunk.validateChunkCoordinates(x, y, z); + + return CraftMagicNumbers.toLegacyData(blockids[y >> 4].a(x, y & 0xF, z)); + } + + public final int getBlockSkyLight(int x, int y, int z) { + CraftChunk.validateChunkCoordinates(x, y, z); + + int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1); + return (skylight[y >> 4][off] >> ((x & 1) << 2)) & 0xF; + } + + public final int getBlockEmittedLight(int x, int y, int z) { + CraftChunk.validateChunkCoordinates(x, y, z); + + int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1); + return (emitlight[y >> 4][off] >> ((x & 1) << 2)) & 0xF; + } + + public final int getHighestBlockYAt(int x, int z) { + CraftChunk.validateChunkCoordinates(x, 0, z); + + return hmap.a(x, z); + } + + public final Biome getBiome(int x, int z) { + CraftChunk.validateChunkCoordinates(x, 0, z); + + return CraftBlock.biomeBaseToBiome(biome[z << 4 | x]); + } + + public final double getRawBiomeTemperature(int x, int z) { + CraftChunk.validateChunkCoordinates(x, 0, z); + + return biomeTemp[z << 4 | x]; + } + + public final long getCaptureFullTime() { + return captureFulltime; + } + + public final boolean isSectionEmpty(int sy) { + return empty[sy]; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftCrashReport.java b/src/main/java/org/bukkit/craftbukkit/CraftCrashReport.java new file mode 100644 index 000000000000..921a57f5a650 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftCrashReport.java @@ -0,0 +1,45 @@ +package org.bukkit.craftbukkit; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.Callable; +import net.minecraft.server.CrashReportCallable; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; + +import net.minecraft.server.MinecraftServer; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; + +public class CraftCrashReport implements CrashReportCallable { + + public Object call() throws Exception { + StringWriter value = new StringWriter(); + try { + value.append("\n Running: ").append(Bukkit.getName()).append(" version ").append(Bukkit.getVersion()).append(" (Implementing API version ").append(Bukkit.getBukkitVersion()).append(") ").append(String.valueOf(MinecraftServer.getServer().getOnlineMode())); + value.append("\n Plugins: {"); + for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) { + PluginDescriptionFile description = plugin.getDescription(); + boolean legacy = CraftMagicNumbers.isLegacy(description); + value.append(' ').append(description.getFullName()).append(legacy ? "*" : "").append(' ').append(description.getMain()).append(' ').append(Arrays.toString(description.getAuthors().toArray())).append(','); + } + value.append("}\n Warnings: ").append(Bukkit.getWarningState().name()); + value.append("\n Reload Count: ").append(String.valueOf(MinecraftServer.getServer().server.reloadCount)); + value.append("\n Threads: {"); + for (Map.Entry entry : Thread.getAllStackTraces().entrySet()) { + value.append(' ').append(entry.getKey().getState().name()).append(' ').append(entry.getKey().getName()).append(": ").append(Arrays.toString(entry.getValue())).append(','); + } + value.append("}\n ").append(Bukkit.getScheduler().toString()); + } catch (Throwable t) { + value.append("\n Failed to handle CraftCrashReport:\n"); + PrintWriter writer = new PrintWriter(value); + t.printStackTrace(writer); + writer.flush(); + } + return value.toString(); + } + +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftEffect.java b/src/main/java/org/bukkit/craftbukkit/CraftEffect.java new file mode 100644 index 000000000000..c6edd7a5077f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftEffect.java @@ -0,0 +1,69 @@ +package org.bukkit.craftbukkit; + +import net.minecraft.server.Block; +import net.minecraft.server.Item; +import org.apache.commons.lang.Validate; +import org.bukkit.Effect; +import org.bukkit.Material; +import org.bukkit.block.BlockFace; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.potion.Potion; + +public class CraftEffect { + public static int getDataValue(Effect effect, T data) { + int datavalue; + switch(effect) { + case VILLAGER_PLANT_GROW: + datavalue = (Integer) data; + break; + case POTION_BREAK: + datavalue = ((Potion) data).toDamageValue() & 0x3F; + break; + case RECORD_PLAY: + Validate.isTrue(((Material) data).isRecord(), "Invalid record type!"); + datavalue = Item.getId(CraftMagicNumbers.getItem((Material) data)); + break; + case SMOKE: + switch((BlockFace) data) { // TODO: Verify (Where did these values come from...?) + case SOUTH_EAST: + datavalue = 0; + break; + case SOUTH: + datavalue = 1; + break; + case SOUTH_WEST: + datavalue = 2; + break; + case EAST: + datavalue = 3; + break; + case UP: + case SELF: + datavalue = 4; + break; + case WEST: + datavalue = 5; + break; + case NORTH_EAST: + datavalue = 6; + break; + case NORTH: + datavalue = 7; + break; + case NORTH_WEST: + datavalue = 8; + break; + default: + throw new IllegalArgumentException("Bad smoke direction!"); + } + break; + case STEP_SOUND: + Validate.isTrue(((Material) data).isBlock(), "Material is not a block!"); + datavalue = Block.getCombinedId(CraftMagicNumbers.getBlock((Material) data).getBlockData()); + break; + default: + datavalue = 0; + } + return datavalue; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftEquipmentSlot.java b/src/main/java/org/bukkit/craftbukkit/CraftEquipmentSlot.java new file mode 100644 index 000000000000..339ae6b7464f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftEquipmentSlot.java @@ -0,0 +1,32 @@ +package org.bukkit.craftbukkit; + +import net.minecraft.server.EnumItemSlot; +import org.bukkit.inventory.EquipmentSlot; + +public class CraftEquipmentSlot { + + private static final EnumItemSlot[] slots = new EnumItemSlot[EquipmentSlot.values().length]; + private static final EquipmentSlot[] enums = new EquipmentSlot[EnumItemSlot.values().length]; + + static { + set(EquipmentSlot.HAND, EnumItemSlot.MAINHAND); + set(EquipmentSlot.OFF_HAND, EnumItemSlot.OFFHAND); + set(EquipmentSlot.FEET, EnumItemSlot.FEET); + set(EquipmentSlot.LEGS, EnumItemSlot.LEGS); + set(EquipmentSlot.CHEST, EnumItemSlot.CHEST); + set(EquipmentSlot.HEAD, EnumItemSlot.HEAD); + } + + private static void set(EquipmentSlot type, EnumItemSlot value) { + slots[type.ordinal()] = value; + enums[value.ordinal()] = type; + } + + public static EquipmentSlot getSlot(EnumItemSlot nms) { + return enums[nms.ordinal()]; + } + + public static EnumItemSlot getNMS(EquipmentSlot slot) { + return slots[slot.ordinal()]; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftFluidCollisionMode.java b/src/main/java/org/bukkit/craftbukkit/CraftFluidCollisionMode.java new file mode 100644 index 000000000000..faf2fd4ecd4e --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftFluidCollisionMode.java @@ -0,0 +1,24 @@ +package org.bukkit.craftbukkit; + +import org.bukkit.FluidCollisionMode; +import net.minecraft.server.FluidCollisionOption; + +public class CraftFluidCollisionMode { + + private CraftFluidCollisionMode() {} + + public static FluidCollisionOption toNMS(FluidCollisionMode fluidCollisionMode) { + if (fluidCollisionMode == null) return null; + + switch (fluidCollisionMode) { + case ALWAYS: + return FluidCollisionOption.ALWAYS; + case SOURCE_ONLY: + return FluidCollisionOption.SOURCE_ONLY; + case NEVER: + return FluidCollisionOption.NEVER; + default: + return null; + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftIpBanEntry.java b/src/main/java/org/bukkit/craftbukkit/CraftIpBanEntry.java new file mode 100644 index 000000000000..7a8ea3bd0256 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftIpBanEntry.java @@ -0,0 +1,88 @@ +package org.bukkit.craftbukkit; + +import net.minecraft.server.IpBanEntry; +import net.minecraft.server.IpBanList; +import net.minecraft.server.MinecraftServer; + +import java.io.IOException; +import java.util.Date; +import java.util.logging.Level; +import org.bukkit.Bukkit; + +public final class CraftIpBanEntry implements org.bukkit.BanEntry { + private final IpBanList list; + private final String target; + private Date created; + private String source; + private Date expiration; + private String reason; + + public CraftIpBanEntry(String target, IpBanEntry entry, IpBanList list) { + this.list = list; + this.target = target; + this.created = entry.getCreated() != null ? new Date(entry.getCreated().getTime()) : null; + this.source = entry.getSource(); + this.expiration = entry.getExpires() != null ? new Date(entry.getExpires().getTime()) : null; + this.reason = entry.getReason(); + } + + @Override + public String getTarget() { + return this.target; + } + + @Override + public Date getCreated() { + return this.created == null ? null : (Date) this.created.clone(); + } + + @Override + public void setCreated(Date created) { + this.created = created; + } + + @Override + public String getSource() { + return this.source; + } + + @Override + public void setSource(String source) { + this.source = source; + } + + @Override + public Date getExpiration() { + return this.expiration == null ? null : (Date) this.expiration.clone(); + } + + @Override + public void setExpiration(Date expiration) { + if (expiration != null && expiration.getTime() == new Date(0, 0, 0, 0, 0, 0).getTime()) { + expiration = null; // Forces "forever" + } + + this.expiration = expiration; + } + + @Override + public String getReason() { + return this.reason; + } + + @Override + public void setReason(String reason) { + this.reason = reason; + } + + @Override + public void save() { + IpBanEntry entry = new IpBanEntry(target, this.created, this.source, this.expiration, this.reason); + this.list.add(entry); + try { + this.list.save(); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Failed to save banned-ips.json, {0}", ex.getMessage()); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftIpBanList.java b/src/main/java/org/bukkit/craftbukkit/CraftIpBanList.java new file mode 100644 index 000000000000..80832f78ae07 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftIpBanList.java @@ -0,0 +1,79 @@ +package org.bukkit.craftbukkit; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Date; +import java.util.Set; + +import net.minecraft.server.IpBanEntry; +import net.minecraft.server.IpBanList; +import net.minecraft.server.MinecraftServer; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; + +import com.google.common.collect.ImmutableSet; +import java.util.logging.Level; +import org.bukkit.Bukkit; + +public class CraftIpBanList implements org.bukkit.BanList { + private final IpBanList list; + + public CraftIpBanList(IpBanList list) { + this.list = list; + } + + @Override + public org.bukkit.BanEntry getBanEntry(String target) { + Validate.notNull(target, "Target cannot be null"); + + IpBanEntry entry = (IpBanEntry) list.get(target); + if (entry == null) { + return null; + } + + return new CraftIpBanEntry(target, entry, list); + } + + @Override + public org.bukkit.BanEntry addBan(String target, String reason, Date expires, String source) { + Validate.notNull(target, "Ban target cannot be null"); + + IpBanEntry entry = new IpBanEntry(target, new Date(), + StringUtils.isBlank(source) ? null : source, expires, + StringUtils.isBlank(reason) ? null : reason); + + list.add(entry); + + try { + list.save(); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Failed to save banned-ips.json, {0}", ex.getMessage()); + } + + return new CraftIpBanEntry(target, entry, list); + } + + @Override + public Set getBanEntries() { + ImmutableSet.Builder builder = ImmutableSet.builder(); + for (String target : list.getEntries()) { + builder.add(new CraftIpBanEntry(target, (IpBanEntry) list.get(target), list)); + } + + return builder.build(); + } + + @Override + public boolean isBanned(String target) { + Validate.notNull(target, "Target cannot be null"); + + return list.isBanned(InetSocketAddress.createUnresolved(target, 0)); + } + + @Override + public void pardon(String target) { + Validate.notNull(target, "Target cannot be null"); + + list.remove(target); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftLootTable.java b/src/main/java/org/bukkit/craftbukkit/CraftLootTable.java new file mode 100644 index 000000000000..d76fe60fbb23 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftLootTable.java @@ -0,0 +1,108 @@ +package org.bukkit.craftbukkit; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Random; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.DamageSource; +import net.minecraft.server.Entity; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.IInventory; +import net.minecraft.server.LootTable; +import net.minecraft.server.LootTableInfo; +import net.minecraft.server.WorldServer; +import org.bukkit.Location; +import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.loot.LootContext; + +public class CraftLootTable implements org.bukkit.loot.LootTable { + + private final LootTable handle; + private final NamespacedKey key; + + public CraftLootTable(NamespacedKey key, LootTable handle) { + this.handle = handle; + this.key = key; + } + + public LootTable getHandle() { + return handle; + } + + @Override + public Collection populateLoot(Random random, LootContext context) { + LootTableInfo nmsContext = convertContext(context); + List nmsItems = handle.populateLoot(random, nmsContext); + Collection bukkit = new ArrayList<>(nmsItems.size()); + + for (net.minecraft.server.ItemStack item : nmsItems) { + if (item.isEmpty()) { + continue; + } + bukkit.add(CraftItemStack.asBukkitCopy(item)); + } + + return bukkit; + } + + @Override + public void fillInventory(Inventory inventory, Random random, LootContext context) { + LootTableInfo nmsContext = convertContext(context); + CraftInventory craftInventory = (CraftInventory) inventory; + IInventory handle = craftInventory.getInventory(); + + // TODO: When events are added, call event here w/ custom reason? + getHandle().fillInventory(handle, random, nmsContext); + } + + @Override + public NamespacedKey getKey() { + return key; + } + + private LootTableInfo convertContext(LootContext context) { + Location loc = context.getLocation(); + WorldServer handle = ((CraftWorld) loc.getWorld()).getHandle(); + + LootTableInfo.Builder builder = new LootTableInfo.Builder(handle); + builder.luck(context.getLuck()); + + if (context.getLootedEntity() != null) { + Entity nmsLootedEntity = ((CraftEntity) context.getLootedEntity()).getHandle(); + builder.entity(nmsLootedEntity); + builder.damageSource(DamageSource.GENERIC); + builder.position(new BlockPosition(nmsLootedEntity)); + } + + if (context.getKiller() != null) { + EntityHuman nmsKiller = ((CraftHumanEntity) context.getKiller()).getHandle(); + builder.killer(nmsKiller); + // If there is a player killer, damage source should reflect that in case loot tables use that information + builder.damageSource(DamageSource.playerAttack(nmsKiller)); + } + + return builder.build(); + } + + @Override + public String toString() { + return getKey().toString(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof org.bukkit.loot.LootTable)) { + return false; + } + + org.bukkit.loot.LootTable table = (org.bukkit.loot.LootTable) obj; + return table.getKey().equals(this.getKey()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java new file mode 100644 index 000000000000..e1973c5d6794 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java @@ -0,0 +1,332 @@ +package org.bukkit.craftbukkit; + +import com.mojang.authlib.GameProfile; +import java.io.File; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import net.minecraft.server.DimensionManager; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.WhiteListEntry; +import net.minecraft.server.WorldNBTStorage; + +import org.bukkit.BanList; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.SerializableAs; +import org.bukkit.entity.Player; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.plugin.Plugin; + +@SerializableAs("Player") +public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializable { + private final GameProfile profile; + private final CraftServer server; + private WorldNBTStorage storage; // Paper - lazy init + + protected CraftOfflinePlayer(CraftServer server, GameProfile profile) { + this.server = server; + this.profile = profile; + //this.storage = (WorldNBTStorage) (server.console.getWorldServer(DimensionManager.OVERWORLD).getDataManager()); // Paper - lazy init + + } + + public GameProfile getProfile() { + return profile; + } + + public boolean isOnline() { + return getPlayer() != null; + } + + public String getName() { + Player player = getPlayer(); + if (player != null) { + return player.getName(); + } + + // This might not match lastKnownName but if not it should be more correct + if (profile.getName() != null) { + return profile.getName(); + } + + NBTTagCompound data = getBukkitData(); + + if (data != null) { + if (data.hasKey("lastKnownName")) { + return data.getString("lastKnownName"); + } + } + + return null; + } + + public UUID getUniqueId() { + return profile.getId(); + } + + public Server getServer() { + return server; + } + + public boolean isOp() { + return server.getHandle().isOp(profile); + } + + public void setOp(boolean value) { + if (value == isOp()) { + return; + } + + if (value) { + server.getHandle().addOp(profile); + } else { + server.getHandle().removeOp(profile); + } + } + + public boolean isBanned() { + if (getName() == null) { + return false; + } + + return server.getBanList(BanList.Type.NAME).isBanned(getName()); + } + + public void setBanned(boolean value) { + if (getName() == null) { + return; + } + + if (value) { + server.getBanList(BanList.Type.NAME).addBan(getName(), null, null, null); + } else { + server.getBanList(BanList.Type.NAME).pardon(getName()); + } + } + + public boolean isWhitelisted() { + return server.getHandle().getWhitelist().isWhitelisted(profile); + } + + public void setWhitelisted(boolean value) { + if (value) { + server.getHandle().getWhitelist().add(new WhiteListEntry(profile)); + } else { + server.getHandle().getWhitelist().remove(profile); + } + } + + public Map serialize() { + Map result = new LinkedHashMap(); + + result.put("UUID", profile.getId().toString()); + + return result; + } + + public static OfflinePlayer deserialize(Map args) { + // Backwards comparability + if (args.get("name") != null) { + return Bukkit.getServer().getOfflinePlayer((String) args.get("name")); + } + + return Bukkit.getServer().getOfflinePlayer(UUID.fromString((String) args.get("UUID"))); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[UUID=" + profile.getId() + "]"; + } + + public Player getPlayer() { + return server.getPlayer(getUniqueId()); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof OfflinePlayer)) { + return false; + } + + OfflinePlayer other = (OfflinePlayer) obj; + if ((this.getUniqueId() == null) || (other.getUniqueId() == null)) { + return false; + } + + return this.getUniqueId().equals(other.getUniqueId()); + } + + @Override + public int hashCode() { + int hash = 5; + hash = 97 * hash + (this.getUniqueId() != null ? this.getUniqueId().hashCode() : 0); + return hash; + } + + // Paper - lazy + private WorldNBTStorage getStorageLazy() { + if (this.storage == null) { + net.minecraft.server.WorldServer worldServer = server.console.getWorldServer(DimensionManager.OVERWORLD); + if (worldServer == null) { + throw new IllegalStateException("Cannot get world storage when there are no worlds loaded!"); + } else { + this.storage = (WorldNBTStorage) worldServer.getDataManager(); + } + } + + return this.storage; + } + // Paper end + + private NBTTagCompound getData() { + return getStorageLazy().getPlayerData(getUniqueId().toString()); + } + + private NBTTagCompound getBukkitData() { + NBTTagCompound result = getData(); + + if (result != null) { + if (!result.hasKey("bukkit")) { + result.set("bukkit", new NBTTagCompound()); + } + result = result.getCompound("bukkit"); + } + + return result; + } + + private File getDataFile() { + return new File(getStorageLazy().getPlayerDir(), getUniqueId() + ".dat"); + } + + public long getFirstPlayed() { + Player player = getPlayer(); + if (player != null) return player.getFirstPlayed(); + + NBTTagCompound data = getBukkitData(); + + if (data != null) { + if (data.hasKey("firstPlayed")) { + return data.getLong("firstPlayed"); + } else { + File file = getDataFile(); + return file.lastModified(); + } + } else { + return 0; + } + } + + public long getLastPlayed() { + Player player = getPlayer(); + if (player != null) return player.getLastPlayed(); + + NBTTagCompound data = getBukkitData(); + + if (data != null) { + if (data.hasKey("lastPlayed")) { + return data.getLong("lastPlayed"); + } else { + File file = getDataFile(); + return file.lastModified(); + } + } else { + return 0; + } + } + + public boolean hasPlayedBefore() { + return getData() != null; + } + + // Paper start + @Override + public long getLastLogin() { + Player player = getPlayer(); + if (player != null) return player.getLastLogin(); + + NBTTagCompound data = getPaperData(); + + if (data != null) { + if (data.hasKey("LastLogin")) { + return data.getLong("LastLogin"); + } else { + // if the player file cannot provide accurate data, this is probably the closest we can approximate + File file = getDataFile(); + return file.lastModified(); + } + } else { + return 0; + } + } + + @Override + public long getLastSeen() { + Player player = getPlayer(); + if (player != null) return player.getLastSeen(); + + NBTTagCompound data = getPaperData(); + + if (data != null) { + if (data.hasKey("LastSeen")) { + return data.getLong("LastSeen"); + } else { + // if the player file cannot provide accurate data, this is probably the closest we can approximate + File file = getDataFile(); + return file.lastModified(); + } + } else { + return 0; + } + } + + private NBTTagCompound getPaperData() { + NBTTagCompound result = getData(); + + if (result != null) { + if (!result.hasKey("Paper")) { + result.set("Paper", new NBTTagCompound()); + } + result = result.getCompound("Paper"); + } + + return result; + } + // Paper end + + public Location getBedSpawnLocation() { + NBTTagCompound data = getData(); + if (data == null) return null; + + if (data.hasKey("SpawnX") && data.hasKey("SpawnY") && data.hasKey("SpawnZ")) { + String spawnWorld = data.getString("SpawnWorld"); + if (spawnWorld.equals("")) { + spawnWorld = server.getWorlds().get(0).getName(); + } + return new Location(server.getWorld(spawnWorld), data.getInt("SpawnX"), data.getInt("SpawnY"), data.getInt("SpawnZ")); + } + return null; + } + + public void setMetadata(String metadataKey, MetadataValue metadataValue) { + server.getPlayerMetadata().setMetadata(this, metadataKey, metadataValue); + } + + public List getMetadata(String metadataKey) { + return server.getPlayerMetadata().getMetadata(this, metadataKey); + } + + public boolean hasMetadata(String metadataKey) { + return server.getPlayerMetadata().hasMetadata(this, metadataKey); + } + + public void removeMetadata(String metadataKey, Plugin plugin) { + server.getPlayerMetadata().removeMetadata(this, metadataKey, plugin); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftParticle.java b/src/main/java/org/bukkit/craftbukkit/CraftParticle.java new file mode 100644 index 000000000000..f4c52bf65652 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftParticle.java @@ -0,0 +1,152 @@ +package org.bukkit.craftbukkit; + +import com.google.common.base.Preconditions; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import java.util.HashMap; +import java.util.Map; +import net.minecraft.server.IRegistry; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.ParticleParam; +import net.minecraft.server.ParticleParamBlock; +import net.minecraft.server.ParticleParamItem; +import net.minecraft.server.ParticleParamRedstone; +import net.minecraft.server.ParticleType; +import org.bukkit.Color; +import org.bukkit.Particle; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.inventory.ItemStack; +import org.bukkit.material.MaterialData; + +public enum CraftParticle { + + EXPLOSION_NORMAL("poof"), + EXPLOSION_LARGE("explosion"), + EXPLOSION_HUGE("explosion_emitter"), + FIREWORKS_SPARK("firework"), + WATER_BUBBLE("bubble"), + WATER_SPLASH("splash"), + WATER_WAKE("fishing"), + SUSPENDED("underwater"), + SUSPENDED_DEPTH("underwater"), + CRIT("crit"), + CRIT_MAGIC("enchanted_hit"), + SMOKE_NORMAL("smoke"), + SMOKE_LARGE("large_smoke"), + SPELL("effect"), + SPELL_INSTANT("instant_effect"), + SPELL_MOB("entity_effect"), + SPELL_MOB_AMBIENT("ambient_entity_effect"), + SPELL_WITCH("witch"), + DRIP_WATER("dripping_water"), + DRIP_LAVA("dripping_lava"), + VILLAGER_ANGRY("angry_villager"), + VILLAGER_HAPPY("happy_villager"), + TOWN_AURA("mycelium"), + NOTE("note"), + PORTAL("portal"), + ENCHANTMENT_TABLE("enchant"), + FLAME("flame"), + LAVA("lava"), + CLOUD("cloud"), + REDSTONE("dust"), + SNOWBALL("item_snowball"), + SNOW_SHOVEL("item_snowball"), + SLIME("item_slime"), + HEART("heart"), + BARRIER("barrier"), + ITEM_CRACK("item"), + BLOCK_CRACK("block"), + BLOCK_DUST("block"), + WATER_DROP("rain"), + MOB_APPEARANCE("elder_guardian"), + DRAGON_BREATH("dragon_breath"), + END_ROD("end_rod"), + DAMAGE_INDICATOR("damage_indicator"), + SWEEP_ATTACK("sweep_attack"), + FALLING_DUST("falling_dust"), + TOTEM("totem_of_undying"), + SPIT("spit"), + SQUID_INK("squid_ink"), + BUBBLE_POP("bubble_pop"), + CURRENT_DOWN("current_down"), + BUBBLE_COLUMN_UP("bubble_column_up"), + NAUTILUS("nautilus"), + DOLPHIN("dolphin"), + // ----- Legacy Separator ----- + LEGACY_BLOCK_CRACK("block"), + LEGACY_BLOCK_DUST("block"), + LEGACY_FALLING_DUST("falling_dust"); + private final MinecraftKey minecraftKey; + private final Particle bukkit; + private static final BiMap particles; + private static final Map aliases; + + static { + particles = HashBiMap.create(); + aliases = new HashMap<>(); + + for (CraftParticle particle : CraftParticle.values()) { + if (particles.containsValue(particle.minecraftKey)) { + aliases.put(particle.bukkit, particles.inverse().get(particle.minecraftKey)); + } else { + particles.put(particle.bukkit, particle.minecraftKey); + } + } + } + + private CraftParticle(String minecraftKey) { + this.minecraftKey = new MinecraftKey(minecraftKey); + + this.bukkit = Particle.valueOf(this.name()); + Preconditions.checkState(bukkit != null, "Bukkit particle %s does not exist", this.name()); + } + + public static ParticleParam toNMS(Particle bukkit) { + return toNMS(bukkit, null); + } + + public static ParticleParam toNMS(Particle particle, T obj) { + Particle canonical = particle; + if (aliases.containsKey(particle)) { + canonical = aliases.get(particle); + } + + net.minecraft.server.Particle nms = IRegistry.PARTICLE_TYPE.get(particles.get(canonical)); + Preconditions.checkArgument(nms != null, "No NMS particle %s", particle); + + if (particle.getDataType().equals(Void.class)) { + return (ParticleType) nms; + } + Preconditions.checkArgument(obj != null, "Particle %s requires data, null provided", particle); + if (particle.getDataType().equals(ItemStack.class)) { + ItemStack itemStack = (ItemStack) obj; + return new ParticleParamItem((net.minecraft.server.Particle) nms, CraftItemStack.asNMSCopy(itemStack)); + } + if (particle.getDataType() == MaterialData.class) { + MaterialData data = (MaterialData) obj; + return new ParticleParamBlock((net.minecraft.server.Particle) nms, CraftMagicNumbers.getBlock(data)); + } + if (particle.getDataType() == BlockData.class) { + BlockData data = (BlockData) obj; + return new ParticleParamBlock((net.minecraft.server.Particle) nms, ((CraftBlockData) data).getState()); + } + if (particle.getDataType() == Particle.DustOptions.class) { + Particle.DustOptions data = (Particle.DustOptions) obj; + Color color = data.getColor(); + return new ParticleParamRedstone(color.getRed() / 255.0f, color.getGreen() / 255.0f, color.getBlue() / 255.0f, data.getSize()); + } + throw new IllegalArgumentException(particle.getDataType().toString()); + } + + public static Particle toBukkit(net.minecraft.server.ParticleParam nms) { + return toBukkit(nms.b()); + } + + public static Particle toBukkit(net.minecraft.server.Particle nms) { + return particles.inverse().get(nms.d()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftProfileBanEntry.java b/src/main/java/org/bukkit/craftbukkit/CraftProfileBanEntry.java new file mode 100644 index 000000000000..3c6f9229f30f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftProfileBanEntry.java @@ -0,0 +1,89 @@ +package org.bukkit.craftbukkit; + +import com.mojang.authlib.GameProfile; +import net.minecraft.server.GameProfileBanEntry; +import net.minecraft.server.GameProfileBanList; +import net.minecraft.server.MinecraftServer; + +import java.io.IOException; +import java.util.Date; +import java.util.logging.Level; +import org.bukkit.Bukkit; + +public final class CraftProfileBanEntry implements org.bukkit.BanEntry { + private final GameProfileBanList list; + private final GameProfile profile; + private Date created; + private String source; + private Date expiration; + private String reason; + + public CraftProfileBanEntry(GameProfile profile, GameProfileBanEntry entry, GameProfileBanList list) { + this.list = list; + this.profile = profile; + this.created = entry.getCreated() != null ? new Date(entry.getCreated().getTime()) : null; + this.source = entry.getSource(); + this.expiration = entry.getExpires() != null ? new Date(entry.getExpires().getTime()) : null; + this.reason = entry.getReason(); + } + + @Override + public String getTarget() { + return this.profile.getName(); + } + + @Override + public Date getCreated() { + return this.created == null ? null : (Date) this.created.clone(); + } + + @Override + public void setCreated(Date created) { + this.created = created; + } + + @Override + public String getSource() { + return this.source; + } + + @Override + public void setSource(String source) { + this.source = source; + } + + @Override + public Date getExpiration() { + return this.expiration == null ? null : (Date) this.expiration.clone(); + } + + @Override + public void setExpiration(Date expiration) { + if (expiration != null && expiration.getTime() == new Date(0, 0, 0, 0, 0, 0).getTime()) { + expiration = null; // Forces "forever" + } + + this.expiration = expiration; + } + + @Override + public String getReason() { + return this.reason; + } + + @Override + public void setReason(String reason) { + this.reason = reason; + } + + @Override + public void save() { + GameProfileBanEntry entry = new GameProfileBanEntry(profile, this.created, this.source, this.expiration, this.reason); + this.list.add(entry); + try { + this.list.save(); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Failed to save banned-players.json, {0}", ex.getMessage()); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftProfileBanList.java b/src/main/java/org/bukkit/craftbukkit/CraftProfileBanList.java new file mode 100644 index 000000000000..84a1f9c86126 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftProfileBanList.java @@ -0,0 +1,99 @@ +package org.bukkit.craftbukkit; + +import java.io.IOException; +import java.util.Date; +import java.util.Set; + +import net.minecraft.server.GameProfileBanEntry; +import net.minecraft.server.GameProfileBanList; +import net.minecraft.server.JsonListEntry; +import net.minecraft.server.MinecraftServer; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; + +import com.google.common.collect.ImmutableSet; +import com.mojang.authlib.GameProfile; +import java.util.logging.Level; +import org.bukkit.Bukkit; + +public class CraftProfileBanList implements org.bukkit.BanList { + private final GameProfileBanList list; + + public CraftProfileBanList(GameProfileBanList list){ + this.list = list; + } + + @Override + public org.bukkit.BanEntry getBanEntry(String target) { + Validate.notNull(target, "Target cannot be null"); + + GameProfile profile = MinecraftServer.getServer().getUserCache().getProfile(target); + if (profile == null) { + return null; + } + + GameProfileBanEntry entry = (GameProfileBanEntry) list.get(profile); + if (entry == null) { + return null; + } + + return new CraftProfileBanEntry(profile, entry, list); + } + + @Override + public org.bukkit.BanEntry addBan(String target, String reason, Date expires, String source) { + Validate.notNull(target, "Ban target cannot be null"); + + GameProfile profile = MinecraftServer.getServer().getUserCache().getProfile(target); + if (profile == null) { + return null; + } + + GameProfileBanEntry entry = new GameProfileBanEntry(profile, new Date(), + StringUtils.isBlank(source) ? null : source, expires, + StringUtils.isBlank(reason) ? null : reason); + + list.add(entry); + + try { + list.save(); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Failed to save banned-players.json, {0}", ex.getMessage()); + } + + return new CraftProfileBanEntry(profile, entry, list); + } + + @Override + public Set getBanEntries() { + ImmutableSet.Builder builder = ImmutableSet.builder(); + + for (JsonListEntry entry : list.getValues()) { + GameProfile profile = (GameProfile) entry.getKey(); + builder.add(new CraftProfileBanEntry(profile, (GameProfileBanEntry) entry, list)); + } + + return builder.build(); + } + + @Override + public boolean isBanned(String target) { + Validate.notNull(target, "Target cannot be null"); + + GameProfile profile = MinecraftServer.getServer().getUserCache().getProfile(target); + if (profile == null) { + return false; + } + + return list.isBanned(profile); + } + + @Override + public void pardon(String target) { + Validate.notNull(target, "Target cannot be null"); + + GameProfile profile = MinecraftServer.getServer().getUserCache().getProfile(target); + list.remove(profile); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java new file mode 100644 index 000000000000..8d26f8d5258b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -0,0 +1,2210 @@ +package org.bukkit.craftbukkit; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +import javax.imageio.ImageIO; + +import net.minecraft.server.*; + +import org.bukkit.BanList; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.UnsafeValues; +import org.bukkit.Warning.WarningState; +import org.bukkit.World; +import org.bukkit.StructureType; +import org.bukkit.World.Environment; +import org.bukkit.WorldCreator; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarFlag; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; +import org.bukkit.boss.KeyedBossBar; +import org.bukkit.command.Command; +import org.bukkit.command.CommandException; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.command.PluginCommand; +import org.bukkit.command.SimpleCommandMap; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.configuration.serialization.ConfigurationSerialization; +import org.bukkit.conversations.Conversable; +import org.bukkit.craftbukkit.boss.CraftBossBar; +import org.bukkit.craftbukkit.boss.CraftKeyedBossbar; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.generator.CraftChunkData; +import org.bukkit.craftbukkit.help.SimpleHelpMap; +import org.bukkit.craftbukkit.inventory.CraftFurnaceRecipe; +import org.bukkit.craftbukkit.inventory.CraftInventoryCustom; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.inventory.CraftItemFactory; +import org.bukkit.craftbukkit.inventory.CraftMerchantCustom; +import org.bukkit.craftbukkit.inventory.CraftRecipe; +import org.bukkit.craftbukkit.inventory.CraftShapedRecipe; +import org.bukkit.craftbukkit.inventory.CraftShapelessRecipe; +import org.bukkit.craftbukkit.inventory.RecipeIterator; +import org.bukkit.craftbukkit.map.CraftMapView; +import org.bukkit.craftbukkit.metadata.EntityMetadataStore; +import org.bukkit.craftbukkit.metadata.PlayerMetadataStore; +import org.bukkit.craftbukkit.metadata.WorldMetadataStore; +import org.bukkit.craftbukkit.potion.CraftPotionBrewer; +import org.bukkit.craftbukkit.scheduler.CraftScheduler; +import org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager; +import org.bukkit.craftbukkit.util.CraftIconCache; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.craftbukkit.util.DatFileFilter; +import org.bukkit.craftbukkit.util.Versioning; +import org.bukkit.craftbukkit.util.permissions.CraftDefaultPermissions; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.command.UnknownCommandEvent; // Paper +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.player.PlayerChatTabCompleteEvent; +import org.bukkit.event.server.BroadcastMessageEvent; +import org.bukkit.event.world.WorldInitEvent; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.event.world.WorldUnloadEvent; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.help.HelpMap; +import org.bukkit.inventory.FurnaceRecipe; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.Merchant; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.Recipe; +import org.bukkit.inventory.ShapedRecipe; +import org.bukkit.inventory.ShapelessRecipe; +import org.bukkit.loot.LootTable; +import org.bukkit.map.MapView; +import org.bukkit.permissions.Permissible; +import org.bukkit.permissions.Permission; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginLoadOrder; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.ServicesManager; +import org.bukkit.plugin.SimplePluginManager; +import org.bukkit.plugin.SimpleServicesManager; +import org.bukkit.plugin.java.JavaPluginLoader; +import org.bukkit.plugin.messaging.Messenger; +import org.bukkit.potion.Potion; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.plugin.messaging.StandardMessenger; +import org.bukkit.scheduler.BukkitWorker; +import org.bukkit.util.StringUtil; +import org.bukkit.util.permissions.DefaultPermissions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.error.MarkedYAMLException; +import org.apache.commons.lang.Validate; + +import com.google.common.base.Charsets; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import com.google.common.collect.MapMaker; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.mojang.authlib.GameProfile; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.tree.LiteralCommandNode; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufOutputStream; +import io.netty.buffer.Unpooled; +import it.unimi.dsi.fastutil.longs.LongIterator; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.HashMap; +import org.bukkit.Keyed; +import org.apache.commons.lang.StringUtils; +import org.bukkit.NamespacedKey; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.command.BukkitCommandWrapper; +import org.bukkit.craftbukkit.command.CraftCommandMap; +import org.bukkit.craftbukkit.command.VanillaCommandWrapper; +import org.bukkit.craftbukkit.inventory.util.CraftInventoryCreator; +import org.bukkit.craftbukkit.tag.CraftBlockTag; +import org.bukkit.craftbukkit.tag.CraftItemTag; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.event.server.ServerLoadEvent; +import org.bukkit.event.server.TabCompleteEvent; +import net.md_5.bungee.api.chat.BaseComponent; + +import javax.annotation.Nullable; // Paper +import javax.annotation.Nonnull; // Paper + + +public final class CraftServer implements Server { + private final String serverName = "Paper"; // Paper + private final String serverVersion; + private final String bukkitVersion = Versioning.getBukkitVersion(); + private final Logger logger = Logger.getLogger("Minecraft"); + private final ServicesManager servicesManager = new SimpleServicesManager(); + private final CraftScheduler scheduler = new CraftScheduler(); + private final CraftCommandMap commandMap = new CraftCommandMap(this); + private final SimpleHelpMap helpMap = new SimpleHelpMap(this); + private final StandardMessenger messenger = new StandardMessenger(); + private final SimplePluginManager pluginManager = new SimplePluginManager(this, commandMap); + protected final MinecraftServer console; + protected final DedicatedPlayerList playerList; + private final Map worlds = new LinkedHashMap(); + private YamlConfiguration configuration; + private YamlConfiguration commandsConfiguration; + private final Yaml yaml = new Yaml(new SafeConstructor()); + private final Map offlinePlayers = new MapMaker().weakValues().makeMap(); + private final EntityMetadataStore entityMetadata = new EntityMetadataStore(); + private final PlayerMetadataStore playerMetadata = new PlayerMetadataStore(); + private final WorldMetadataStore worldMetadata = new WorldMetadataStore(); + private int monsterSpawn = -1; + private int animalSpawn = -1; + private int waterAnimalSpawn = -1; + private int ambientSpawn = -1; + public int chunkGCPeriod = -1; + public int chunkGCLoadThresh = 0; + private File container; + private WarningState warningState = WarningState.DEFAULT; + private final BooleanWrapper online = new BooleanWrapper(); + public CraftScoreboardManager scoreboardManager; + public boolean playerCommandState; + private boolean printSaveWarning; + private CraftIconCache icon; + private boolean overrideAllCommandBlockCommands = false; + public boolean ignoreVanillaPermissions = false; + private final List playerView; + public int reloadCount; + public static Exception excessiveVelEx; // Paper - Velocity warnings + + private final class BooleanWrapper { + private boolean value = true; + } + + static { + ConfigurationSerialization.registerClass(CraftOfflinePlayer.class); + CraftItemFactory.instance(); + } + + public CraftServer(MinecraftServer console, PlayerList playerList) { + this.console = console; + this.playerList = (DedicatedPlayerList) playerList; + this.playerView = Collections.unmodifiableList(Lists.transform(playerList.players, new Function() { + @Override + public CraftPlayer apply(EntityPlayer player) { + return player.getBukkitEntity(); + } + })); + this.serverVersion = CraftServer.class.getPackage().getImplementationVersion(); + online.value = console.getPropertyManager().getBoolean("online-mode", true); + + Bukkit.setServer(this); + + // Register all the Enchantments and PotionTypes now so we can stop new registration immediately after + Enchantments.DAMAGE_ALL.getClass(); + org.bukkit.enchantments.Enchantment.stopAcceptingRegistrations(); + + Potion.setPotionBrewer(new CraftPotionBrewer()); + MobEffects.BLINDNESS.getClass(); + PotionEffectType.stopAcceptingRegistrations(); + // Ugly hack :( + + if (!Main.useConsole) { + getLogger().info("Console input is disabled due to --noconsole command argument"); + } + + configuration = YamlConfiguration.loadConfiguration(getConfigFile()); + configuration.options().copyDefaults(true); + configuration.setDefaults(YamlConfiguration.loadConfiguration(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("configurations/bukkit.yml"), Charsets.UTF_8))); + ConfigurationSection legacyAlias = null; + if (!configuration.isString("aliases")) { + legacyAlias = configuration.getConfigurationSection("aliases"); + configuration.set("aliases", "now-in-commands.yml"); + } + saveConfig(); + if (getCommandsConfigFile().isFile()) { + legacyAlias = null; + } + commandsConfiguration = YamlConfiguration.loadConfiguration(getCommandsConfigFile()); + commandsConfiguration.options().copyDefaults(true); + commandsConfiguration.setDefaults(YamlConfiguration.loadConfiguration(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("configurations/commands.yml"), Charsets.UTF_8))); + saveCommandsConfig(); + + // Migrate aliases from old file and add previously implicit $1- to pass all arguments + if (legacyAlias != null) { + ConfigurationSection aliases = commandsConfiguration.createSection("aliases"); + for (String key : legacyAlias.getKeys(false)) { + ArrayList commands = new ArrayList(); + + if (legacyAlias.isList(key)) { + for (String command : legacyAlias.getStringList(key)) { + commands.add(command + " $1-"); + } + } else { + commands.add(legacyAlias.getString(key) + " $1-"); + } + + aliases.set(key, commands); + } + } + + saveCommandsConfig(); + overrideAllCommandBlockCommands = commandsConfiguration.getStringList("command-block-overrides").contains("*"); + ignoreVanillaPermissions = commandsConfiguration.getBoolean("ignore-vanilla-permissions"); + pluginManager.useTimings(configuration.getBoolean("settings.plugin-profiling")); + monsterSpawn = configuration.getInt("spawn-limits.monsters"); + animalSpawn = configuration.getInt("spawn-limits.animals"); + waterAnimalSpawn = configuration.getInt("spawn-limits.water-animals"); + ambientSpawn = configuration.getInt("spawn-limits.ambient"); + console.autosavePeriod = configuration.getInt("ticks-per.autosave"); + warningState = WarningState.value(configuration.getString("settings.deprecated-verbose")); + chunkGCPeriod = configuration.getInt("chunk-gc.period-in-ticks"); + chunkGCLoadThresh = configuration.getInt("chunk-gc.load-threshold"); + loadIcon(); + } + + public boolean getCommandBlockOverride(String command) { + return overrideAllCommandBlockCommands || commandsConfiguration.getStringList("command-block-overrides").contains(command); + } + + private File getConfigFile() { + return (File) console.options.valueOf("bukkit-settings"); + } + + private File getCommandsConfigFile() { + return (File) console.options.valueOf("commands-settings"); + } + + private void saveConfig() { + try { + configuration.save(getConfigFile()); + } catch (IOException ex) { + Logger.getLogger(CraftServer.class.getName()).log(Level.SEVERE, "Could not save " + getConfigFile(), ex); + } + } + + private void saveCommandsConfig() { + try { + commandsConfiguration.save(getCommandsConfigFile()); + } catch (IOException ex) { + Logger.getLogger(CraftServer.class.getName()).log(Level.SEVERE, "Could not save " + getCommandsConfigFile(), ex); + } + } + + public void loadPlugins() { + pluginManager.registerInterface(JavaPluginLoader.class); + + File pluginFolder = (File) console.options.valueOf("plugins"); + + if (pluginFolder.exists()) { + Plugin[] plugins = pluginManager.loadPlugins(pluginFolder); + for (Plugin plugin : plugins) { + try { + String message = String.format("Loading %s", plugin.getDescription().getFullName()); + plugin.getLogger().info(message); + plugin.onLoad(); + } catch (Throwable ex) { + Logger.getLogger(CraftServer.class.getName()).log(Level.SEVERE, ex.getMessage() + " initializing " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + } + } + } else { + pluginFolder.mkdir(); + } + } + + public void enablePlugins(PluginLoadOrder type) { + if (type == PluginLoadOrder.STARTUP) { + helpMap.clear(); + helpMap.initializeGeneralTopics(); + if (com.destroystokyo.paper.PaperConfig.loadPermsBeforePlugins) loadCustomPermissions(); // Paper + } + + Plugin[] plugins = pluginManager.getPlugins(); + + for (Plugin plugin : plugins) { + if ((!plugin.isEnabled()) && (plugin.getDescription().getLoad() == type)) { + enablePlugin(plugin); + } + } + + if (type == PluginLoadOrder.POSTWORLD) { + // Spigot start - Allow vanilla commands to be forced to be the main command + setVanillaCommands(true); + commandMap.setFallbackCommands(); + setVanillaCommands(false); + // Spigot end + commandMap.registerServerAliases(); + DefaultPermissions.registerCorePermissions(); + CraftDefaultPermissions.registerCorePermissions(); + if (!com.destroystokyo.paper.PaperConfig.loadPermsBeforePlugins) loadCustomPermissions(); // Paper + helpMap.initializeCommands(); + syncCommands(); + } + } + + public void disablePlugins() { + pluginManager.disablePlugins(); + } + + private void setVanillaCommands(boolean first) { // Spigot + CommandDispatcher dispatcher = console.vanillaCommandDispatcher; + + // Build a list of all Vanilla commands and create wrappers + for (CommandNode cmd : dispatcher.a().getRoot().getChildren()) { + // Spigot start + VanillaCommandWrapper wrapper = new VanillaCommandWrapper(dispatcher, cmd); + if (org.spigotmc.SpigotConfig.replaceCommands.contains( wrapper.getName() ) ) { + if (first) { + commandMap.register("minecraft", wrapper); + } + } else if (!first) { + commandMap.register("minecraft", wrapper); + } + // Spigot end + } + } + + private void syncCommands() { + // Clear existing commands + CommandDispatcher dispatcher = console.commandDispatcher = new CommandDispatcher(); + + // Register all commands, vanilla ones will be using the old dispatcher references + for (Map.Entry entry : commandMap.getKnownCommands().entrySet()) { + String label = entry.getKey(); + Command command = entry.getValue(); + + if (command instanceof VanillaCommandWrapper) { + LiteralCommandNode node = (LiteralCommandNode) ((VanillaCommandWrapper) command).vanillaCommand; + if (!node.getLiteral().equals(label)) { + LiteralCommandNode clone = new LiteralCommandNode(label, node.getCommand(), node.getRequirement(), node.getRedirect(), node.getRedirectModifier(), node.isFork()); + + for (CommandNode child : node.getChildren()) { + clone.addChild(child); + } + node = clone; + } + + dispatcher.a().getRoot().addChild(node); + } else { + new BukkitCommandWrapper(this, entry.getValue()).register(dispatcher.a(), label); + } + } + + // Refresh commands + for (EntityPlayer player : getHandle().players) { + dispatcher.a(player); + } + } + + private void enablePlugin(Plugin plugin) { + try { + List perms = plugin.getDescription().getPermissions(); + + for (Permission perm : perms) { + try { + pluginManager.addPermission(perm, false); + } catch (IllegalArgumentException ex) { + getLogger().log(Level.WARNING, "Plugin " + plugin.getDescription().getFullName() + " tried to register permission '" + perm.getName() + "' but it's already registered", ex); + } + } + pluginManager.dirtyPermissibles(); + + pluginManager.enablePlugin(plugin); + } catch (Throwable ex) { + Logger.getLogger(CraftServer.class.getName()).log(Level.SEVERE, ex.getMessage() + " loading " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex); + } + } + + @Override + public String getName() { + return serverName; + } + + @Override + public String getVersion() { + return serverVersion + " (MC: " + console.getVersion() + ")"; + } + + @Override + public String getBukkitVersion() { + return bukkitVersion; + } + + @Override + public List getOnlinePlayers() { + return this.playerView; + } + + @Override + @Deprecated + public Player getPlayer(final String name) { + Validate.notNull(name, "Name cannot be null"); + + Player found = getPlayerExact(name); + // Try for an exact match first. + if (found != null) { + return found; + } + + String lowerName = name.toLowerCase(java.util.Locale.ENGLISH); + int delta = Integer.MAX_VALUE; + for (Player player : getOnlinePlayers()) { + if (player.getName().toLowerCase(java.util.Locale.ENGLISH).startsWith(lowerName)) { + int curDelta = Math.abs(player.getName().length() - lowerName.length()); + if (curDelta < delta) { + found = player; + delta = curDelta; + } + if (curDelta == 0) break; + } + } + return found; + } + + @Override + @Deprecated + public Player getPlayerExact(String name) { + Validate.notNull(name, "Name cannot be null"); + + EntityPlayer player = playerList.getPlayer(name); + return (player != null) ? player.getBukkitEntity() : null; + } + + @Override + public Player getPlayer(UUID id) { + EntityPlayer player = playerList.a(id); + + if (player != null) { + return player.getBukkitEntity(); + } + + return null; + } + + @Override + public int broadcastMessage(String message) { + return broadcast(message, BROADCAST_CHANNEL_USERS); + } + + public Player getPlayer(final EntityPlayer entity) { + return entity.getBukkitEntity(); + } + + @Override + @Deprecated + public List matchPlayer(String partialName) { + Validate.notNull(partialName, "PartialName cannot be null"); + + List matchedPlayers = new ArrayList(); + + for (Player iterPlayer : this.getOnlinePlayers()) { + String iterPlayerName = iterPlayer.getName(); + + if (partialName.equalsIgnoreCase(iterPlayerName)) { + // Exact match + matchedPlayers.clear(); + matchedPlayers.add(iterPlayer); + break; + } + if (iterPlayerName.toLowerCase(java.util.Locale.ENGLISH).contains(partialName.toLowerCase(java.util.Locale.ENGLISH))) { + // Partial match + matchedPlayers.add(iterPlayer); + } + } + + return matchedPlayers; + } + + @Override + public int getMaxPlayers() { + return playerList.getMaxPlayers(); + } + + // NOTE: These are dependent on the corresponding call in MinecraftServer + // so if that changes this will need to as well + @Override + public int getPort() { + return this.getConfigInt("server-port", 25565); + } + + @Override + public int getViewDistance() { + return this.getConfigInt("view-distance", 10); + } + + @Override + public String getIp() { + return this.getConfigString("server-ip", ""); + } + + @Override + public String getServerName() { + return this.getConfigString("server-name", "Unknown Server"); + } + + @Override + public String getServerId() { + return this.getConfigString("server-id", "unnamed"); + } + + @Override + public String getWorldType() { + return this.getConfigString("level-type", "DEFAULT"); + } + + @Override + public boolean getGenerateStructures() { + return this.getConfigBoolean("generate-structures", true); + } + + @Override + public boolean getAllowEnd() { + return this.configuration.getBoolean("settings.allow-end"); + } + + @Override + public boolean getAllowNether() { + return this.getConfigBoolean("allow-nether", true); + } + + public boolean getWarnOnOverload() { + return this.configuration.getBoolean("settings.warn-on-overload"); + } + + public boolean getQueryPlugins() { + return this.configuration.getBoolean("settings.query-plugins"); + } + + @Override + public boolean hasWhitelist() { + return this.getConfigBoolean("white-list", false); + } + + // NOTE: Temporary calls through to server.properies until its replaced + private String getConfigString(String variable, String defaultValue) { + return this.console.getPropertyManager().getString(variable, defaultValue); + } + + private int getConfigInt(String variable, int defaultValue) { + return this.console.getPropertyManager().getInt(variable, defaultValue); + } + + private boolean getConfigBoolean(String variable, boolean defaultValue) { + return this.console.getPropertyManager().getBoolean(variable, defaultValue); + } + + // End Temporary calls + + @Override + public String getUpdateFolder() { + return this.configuration.getString("settings.update-folder", "update"); + } + + @Override + public File getUpdateFolderFile() { + return new File((File) console.options.valueOf("plugins"), this.configuration.getString("settings.update-folder", "update")); + } + + @Override + public long getConnectionThrottle() { + // Spigot Start - Automatically set connection throttle for bungee configurations + if (org.spigotmc.SpigotConfig.bungee) { + return -1; + } else { + return this.configuration.getInt("settings.connection-throttle"); + } + // Spigot End + } + + @Override + public int getTicksPerAnimalSpawns() { + return this.configuration.getInt("ticks-per.animal-spawns"); + } + + @Override + public int getTicksPerMonsterSpawns() { + return this.configuration.getInt("ticks-per.monster-spawns"); + } + + @Override + public PluginManager getPluginManager() { + return pluginManager; + } + + @Override + public CraftScheduler getScheduler() { + return scheduler; + } + + @Override + public ServicesManager getServicesManager() { + return servicesManager; + } + + @Override + public List getWorlds() { + return new ArrayList(worlds.values()); + } + + public DedicatedPlayerList getHandle() { + return playerList; + } + + // NOTE: Should only be called from DedicatedServer.ah() + public boolean dispatchServerCommand(CommandSender sender, ServerCommand serverCommand) { + if (sender instanceof Conversable) { + Conversable conversable = (Conversable)sender; + + if (conversable.isConversing()) { + conversable.acceptConversationInput(serverCommand.command); + return true; + } + } + try { + this.playerCommandState = true; + return dispatchCommand(sender, serverCommand.command); + } catch (Exception ex) { + getLogger().log(Level.WARNING, "Unexpected exception while parsing console command \"" + serverCommand.command + '"', ex); + return false; + } finally { + this.playerCommandState = false; + } + } + + @Override + public boolean dispatchCommand(CommandSender sender, String commandLine) { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(commandLine, "CommandLine cannot be null"); + org.spigotmc.AsyncCatcher.catchOp( "command dispatch" ); // Spigot + + // Paper Start + if (!org.spigotmc.AsyncCatcher.shuttingDown && !Bukkit.isPrimaryThread()) { + final CommandSender fSender = sender; + final String fCommandLine = commandLine; + Bukkit.getLogger().log(Level.SEVERE, "Command Dispatched Async: " + commandLine); + Bukkit.getLogger().log(Level.SEVERE, "Please notify author of plugin causing this execution to fix this bug! see: http://bit.ly/1oSiM6C", new Throwable()); + org.bukkit.craftbukkit.util.Waitable wait = new org.bukkit.craftbukkit.util.Waitable() { + @Override + protected Boolean evaluate() { + return dispatchCommand(fSender, fCommandLine); + } + }; + net.minecraft.server.MinecraftServer.getServer().processQueue.add(wait); + try { + return wait.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // This is proper habit for java. If we aren't handling it, pass it on! + } catch (Exception e) { + throw new RuntimeException("Exception processing dispatch command", e.getCause()); + } + } + // Paper End + + if (commandMap.dispatch(sender, commandLine)) { + return true; + } + + // Spigot start + if (!org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty()) { + // Paper start + UnknownCommandEvent event = new UnknownCommandEvent(sender, commandLine, org.spigotmc.SpigotConfig.unknownCommandMessage); + Bukkit.getServer().getPluginManager().callEvent(event); + if (StringUtils.isNotEmpty(event.getMessage())) { + sender.sendMessage(event.getMessage()); + } + // Paper end + } + // Spigot end + + return false; + } + + @Override + public void reload() { + org.spigotmc.WatchdogThread.hasStarted = false; // Paper - Disable watchdog early timeout on reload + reloadCount++; + configuration = YamlConfiguration.loadConfiguration(getConfigFile()); + commandsConfiguration = YamlConfiguration.loadConfiguration(getCommandsConfigFile()); + PropertyManager config = new PropertyManager(console.options); + + ((DedicatedServer) console).propertyManager = config; + + boolean animals = config.getBoolean("spawn-animals", console.getSpawnAnimals()); + boolean monsters = config.getBoolean("spawn-monsters", console.getWorldServer(DimensionManager.OVERWORLD).getDifficulty() != EnumDifficulty.PEACEFUL); + EnumDifficulty difficulty = EnumDifficulty.getById(config.getInt("difficulty", console.getWorldServer(DimensionManager.OVERWORLD).getDifficulty().ordinal())); + + online.value = config.getBoolean("online-mode", console.getOnlineMode()); + console.setSpawnAnimals(config.getBoolean("spawn-animals", console.getSpawnAnimals())); + console.setPVP(config.getBoolean("pvp", console.getPVP())); + console.setAllowFlight(config.getBoolean("allow-flight", console.getAllowFlight())); + console.setMotd(config.getString("motd", console.getMotd())); + monsterSpawn = configuration.getInt("spawn-limits.monsters"); + animalSpawn = configuration.getInt("spawn-limits.animals"); + waterAnimalSpawn = configuration.getInt("spawn-limits.water-animals"); + ambientSpawn = configuration.getInt("spawn-limits.ambient"); + warningState = WarningState.value(configuration.getString("settings.deprecated-verbose")); + printSaveWarning = false; + console.autosavePeriod = configuration.getInt("ticks-per.autosave"); + chunkGCPeriod = configuration.getInt("chunk-gc.period-in-ticks"); + chunkGCLoadThresh = configuration.getInt("chunk-gc.load-threshold"); + loadIcon(); + + try { + playerList.getIPBans().load(); + } catch (IOException ex) { + logger.log(Level.WARNING, "Failed to load banned-ips.json, " + ex.getMessage()); + } + try { + playerList.getProfileBans().load(); + } catch (IOException ex) { + logger.log(Level.WARNING, "Failed to load banned-players.json, " + ex.getMessage()); + } + + org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot + com.destroystokyo.paper.PaperConfig.init((File) console.options.valueOf("paper-settings")); // Paper + for (WorldServer world : console.getWorlds()) { + world.worldData.setDifficulty(difficulty); + world.setSpawnFlags(monsters, animals); + if (this.getTicksPerAnimalSpawns() < 0) { + world.ticksPerAnimalSpawns = 400; + } else { + world.ticksPerAnimalSpawns = this.getTicksPerAnimalSpawns(); + } + + if (this.getTicksPerMonsterSpawns() < 0) { + world.ticksPerMonsterSpawns = 1; + } else { + world.ticksPerMonsterSpawns = this.getTicksPerMonsterSpawns(); + } + world.spigotConfig.init(); // Spigot + world.paperConfig.init(); // Paper + } + + Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper + pluginManager.clearPlugins(); + commandMap.clearCommands(); + + // Paper start + for (Plugin plugin : pluginClone) { + entityMetadata.removeAll(plugin); + worldMetadata.removeAll(plugin); + playerMetadata.removeAll(plugin); + } + // Paper end + + resetRecipes(); + reloadData(); + org.spigotmc.SpigotConfig.registerCommands(); // Spigot + com.destroystokyo.paper.PaperConfig.registerCommands(); // Paper + overrideAllCommandBlockCommands = commandsConfiguration.getStringList("command-block-overrides").contains("*"); + ignoreVanillaPermissions = commandsConfiguration.getBoolean("ignore-vanilla-permissions"); + + int pollCount = 0; + + // Wait for at most 2.5 seconds for plugins to close their threads + while (pollCount < 50 && getScheduler().getActiveWorkers().size() > 0) { + try { + Thread.sleep(50); + } catch (InterruptedException e) {} + pollCount++; + } + + List overdueWorkers = getScheduler().getActiveWorkers(); + for (BukkitWorker worker : overdueWorkers) { + Plugin plugin = worker.getOwner(); + String author = ""; + if (plugin.getDescription().getAuthors().size() > 0) { + author = plugin.getDescription().getAuthors().get(0); + } + getLogger().log(Level.SEVERE, String.format( + "Nag author: '%s' of '%s' about the following: %s", + author, + plugin.getDescription().getName(), + "This plugin is not properly shutting down its async tasks when it is being reloaded. This may cause conflicts with the newly loaded version of the plugin" + )); + } + loadPlugins(); + enablePlugins(PluginLoadOrder.STARTUP); + enablePlugins(PluginLoadOrder.POSTWORLD); + getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.RELOAD)); + org.spigotmc.WatchdogThread.hasStarted = true; // Paper - Disable watchdog early timeout on reload + } + + @Override + public void reloadData() { + console.reload(); + } + + private void loadIcon() { + icon = new CraftIconCache(null); + try { + final File file = new File(new File("."), "server-icon.png"); + if (file.isFile()) { + icon = loadServerIcon0(file); + } + } catch (Exception ex) { + getLogger().log(Level.WARNING, "Couldn't load server icon", ex); + } + } + + @SuppressWarnings({ "unchecked", "finally" }) + private void loadCustomPermissions() { + File file = new File(configuration.getString("settings.permissions-file")); + FileInputStream stream; + + try { + stream = new FileInputStream(file); + } catch (FileNotFoundException ex) { + try { + file.createNewFile(); + } finally { + return; + } + } + + Map> perms; + + try { + perms = (Map>) yaml.load(stream); + } catch (MarkedYAMLException ex) { + getLogger().log(Level.WARNING, "Server permissions file " + file + " is not valid YAML: " + ex.toString()); + return; + } catch (Throwable ex) { + getLogger().log(Level.WARNING, "Server permissions file " + file + " is not valid YAML.", ex); + return; + } finally { + try { + stream.close(); + } catch (IOException ex) {} + } + + if (perms == null) { + getLogger().log(Level.INFO, "Server permissions file " + file + " is empty, ignoring it"); + return; + } + + List permsList = Permission.loadPermissions(perms, "Permission node '%s' in " + file + " is invalid", Permission.DEFAULT_PERMISSION); + + for (Permission perm : permsList) { + try { + pluginManager.addPermission(perm); + } catch (IllegalArgumentException ex) { + getLogger().log(Level.SEVERE, "Permission in " + file + " was already defined", ex); + } + } + } + + @Override + public String toString() { + return "CraftServer{" + "serverName=" + serverName + ",serverVersion=" + serverVersion + ",minecraftVersion=" + console.getVersion() + '}'; + } + + public World createWorld(String name, World.Environment environment) { + return WorldCreator.name(name).environment(environment).createWorld(); + } + + public World createWorld(String name, World.Environment environment, long seed) { + return WorldCreator.name(name).environment(environment).seed(seed).createWorld(); + } + + public World createWorld(String name, Environment environment, ChunkGenerator generator) { + return WorldCreator.name(name).environment(environment).generator(generator).createWorld(); + } + + public World createWorld(String name, Environment environment, long seed, ChunkGenerator generator) { + return WorldCreator.name(name).environment(environment).seed(seed).generator(generator).createWorld(); + } + + @Override + public World createWorld(WorldCreator creator) { + Validate.notNull(creator, "Creator may not be null"); + + String name = creator.name(); + ChunkGenerator generator = creator.generator(); + File folder = new File(getWorldContainer(), name); + World world = getWorld(name); + WorldType type = WorldType.getType(creator.type().getName()); + boolean generateStructures = creator.generateStructures(); + + if (world != null) { + return world; + } + + if ((folder.exists()) && (!folder.isDirectory())) { + throw new IllegalArgumentException("File exists with the name '" + name + "' and isn't a folder"); + } + + if (generator == null) { + generator = getGenerator(name); + } + + console.convertWorld(name); + + int dimension = CraftWorld.CUSTOM_DIMENSION_OFFSET + console.worldServer.size(); + boolean used = false; + do { + for (WorldServer server : console.getWorlds()) { + used = server.dimension.getDimensionID() + 1 == dimension; // Paper - getDimensionID returns the dimension - 1, so we have to add 1 + if (used) { + dimension++; + break; + } + } + } while(used); + boolean hardcore = false; + + IDataManager sdm = new ServerNBTManager(getWorldContainer(), name, getServer(), getHandle().getServer().dataConverterManager); + PersistentCollection persistentcollection = new PersistentCollection(sdm); + WorldData worlddata = sdm.getWorldData(); + WorldSettings worldSettings = null; + if (worlddata == null) { + worldSettings = new WorldSettings(com.destroystokyo.paper.PaperConfig.seedOverride.getOrDefault(name, creator.seed()), EnumGamemode.getById(getDefaultGameMode().getValue()), generateStructures, hardcore, type); // Paper + JsonElement parsedSettings = new JsonParser().parse(creator.generatorSettings()); + if (parsedSettings.isJsonObject()) { + worldSettings.setGeneratorSettings(parsedSettings.getAsJsonObject()); + } + worlddata = new WorldData(worldSettings, name); + } + worlddata.checkName(name); // CraftBukkit - Migration did not rewrite the level.dat; This forces 1.8 to take the last loaded world as respawn (in this case the end) + + DimensionManager internalDimension = new DimensionManager(dimension, name, name, () -> DimensionManager.a(creator.environment().getId()).e()); + WorldServer internal = (WorldServer) new WorldServer(console, sdm, persistentcollection, worlddata, internalDimension, console.methodProfiler, creator.environment(), generator).i_(); + + if (!(worlds.containsKey(name.toLowerCase(java.util.Locale.ENGLISH)))) { + return null; + } + + if (worldSettings != null) { + internal.a(worldSettings); + } + + internal.tracker = new EntityTracker(internal); + internal.addIWorldAccess(new WorldManager(console, internal)); + internal.worldData.setDifficulty(EnumDifficulty.EASY); + internal.setSpawnFlags(true, true); + console.worldServer.put(internal.dimension, internal); + + pluginManager.callEvent(new WorldInitEvent(internal.getWorld())); + System.out.println("Preparing start region for level " + (console.worldServer.size() - 1) + " (Seed: " + internal.getSeed() + ")"); + + if (internal.getWorld().getKeepSpawnInMemory()) { + short short1 = internal.paperConfig.keepLoadedRange; // Paper + long i = System.currentTimeMillis(); + // Paper start + for (ChunkCoordIntPair coords : internal.getChunkProvider().getSpiralOutChunks(internal.getSpawn(), short1 >> 4)) {{ + int j = coords.x; + int k = coords.z; + // Paper end + + long l = System.currentTimeMillis(); + + if (l < i) { + i = l; + } + + if (l > i + 1000L) { + int i1 = (short1 * 2 + 1) * (short1 * 2 + 1); + int j1 = (j + short1) * (short1 * 2 + 1) + k + 1; + + System.out.println("Preparing spawn area for " + name + ", " + (j1 * 100 / i1) + "%"); + i = l; + } + + BlockPosition chunkcoordinates = internal.getSpawn(); + internal.getChunkProvider().getChunkAt(chunkcoordinates.getX() + j >> 4, chunkcoordinates.getZ() + k >> 4, true, true, c -> {}); // Paper + } + } + } + + DimensionManager dimensionmanager = internalDimension.e().getDimensionManager(); + ForcedChunk forcedchunk = (ForcedChunk) persistentcollection.get(dimensionmanager, ForcedChunk::new, "chunks"); + + if (forcedchunk != null) { + LongIterator longiterator = forcedchunk.a().iterator(); + + while (longiterator.hasNext()) { + System.out.println("Loading forced chunks for dimension " + dimension + ", " + forcedchunk.a().size() * 100 / 625 + "%"); + long k = longiterator.nextLong(); + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(k); + + internal.getChunkProvider().getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z, true, true); + } + } + + pluginManager.callEvent(new WorldLoadEvent(internal.getWorld())); + return internal.getWorld(); + } + + @Override + public boolean unloadWorld(String name, boolean save) { + return unloadWorld(getWorld(name), save); + } + + @Override + public boolean unloadWorld(World world, boolean save) { + if (world == null) { + return false; + } + + WorldServer handle = ((CraftWorld) world).getHandle(); + + if (!(console.worldServer.containsKey(handle.dimension))) { + return false; + } + + if (handle.dimension == DimensionManager.OVERWORLD) { + return false; + } + + if (handle.players.size() > 0) { + return false; + } + + WorldUnloadEvent e = new WorldUnloadEvent(handle.getWorld()); + pluginManager.callEvent(e); + + if (e.isCancelled()) { + return false; + } + + if (save) { + try { + handle.save(true, null); + handle.close(); + } catch (ExceptionWorldConflict ex) { + getLogger().log(Level.SEVERE, null, ex); + } + } + + worlds.remove(world.getName().toLowerCase(java.util.Locale.ENGLISH)); + console.worldServer.remove(handle.dimension); + + File parentFolder = world.getWorldFolder().getAbsoluteFile(); + + // Synchronized because access to RegionFileCache.cache is guarded by this lock. + synchronized (RegionFileCache.class) { + Iterator> i = RegionFileCache.cache.entrySet().iterator(); + while(i.hasNext()) { + Map.Entry entry = i.next(); + File child = entry.getKey().getAbsoluteFile(); + while (child != null) { + if (child.equals(parentFolder)) { + i.remove(); + try { + entry.getValue().close(); + } catch (IOException ex) { + getLogger().log(Level.SEVERE, null, ex); + } + break; + } + child = child.getParentFile(); + } + } + } + + return true; + } + + public MinecraftServer getServer() { + return console; + } + + @Override + public World getWorld(String name) { + Validate.notNull(name, "Name cannot be null"); + + return worlds.get(name.toLowerCase(java.util.Locale.ENGLISH)); + } + + @Override + public World getWorld(UUID uid) { + for (World world : worlds.values()) { + if (world.getUID().equals(uid)) { + return world; + } + } + return null; + } + + public void addWorld(World world) { + // Check if a World already exists with the UID. + if (getWorld(world.getUID()) != null) { + System.out.println("World " + world.getName() + " is a duplicate of another world and has been prevented from loading. Please delete the uid.dat file from " + world.getName() + "'s world directory if you want to be able to load the duplicate world."); + return; + } + worlds.put(world.getName().toLowerCase(java.util.Locale.ENGLISH), world); + } + + @Override + public Logger getLogger() { + return logger; + } + + // Paper start - JLine update + /* + public ConsoleReader getReader() { + return console.reader; + } + */ + // Paper end + + @Override + public PluginCommand getPluginCommand(String name) { + Command command = commandMap.getCommand(name); + + if (command instanceof PluginCommand) { + return (PluginCommand) command; + } else { + return null; + } + } + + @Override + public void savePlayers() { + checkSaveState(); + playerList.savePlayers(); + } + + @Override + public boolean addRecipe(Recipe recipe) { + CraftRecipe toAdd; + if (recipe instanceof CraftRecipe) { + toAdd = (CraftRecipe) recipe; + } else { + if (recipe instanceof ShapedRecipe) { + toAdd = CraftShapedRecipe.fromBukkitRecipe((ShapedRecipe) recipe); + } else if (recipe instanceof ShapelessRecipe) { + toAdd = CraftShapelessRecipe.fromBukkitRecipe((ShapelessRecipe) recipe); + } else if (recipe instanceof FurnaceRecipe) { + toAdd = CraftFurnaceRecipe.fromBukkitRecipe((FurnaceRecipe) recipe); + } else { + return false; + } + } + toAdd.addToCraftingManager(); + return true; + } + + @Override + public List getRecipesFor(ItemStack result) { + Validate.notNull(result, "Result cannot be null"); + + List results = new ArrayList(); + Iterator iter = recipeIterator(); + while (iter.hasNext()) { + Recipe recipe = iter.next(); + ItemStack stack = recipe.getResult(); + if (stack.getType() != result.getType()) { + continue; + } + if (result.getDurability() == -1 || result.getDurability() == stack.getDurability()) { + results.add(recipe); + } + } + return results; + } + + @Override + public Iterator recipeIterator() { + return new RecipeIterator(); + } + + @Override + public void clearRecipes() { + console.getCraftingManager().recipes.clear(); + } + + @Override + public void resetRecipes() { + console.getCraftingManager().a(console.getResourceManager()); + } + + @Override + public Map getCommandAliases() { + ConfigurationSection section = commandsConfiguration.getConfigurationSection("aliases"); + Map result = new LinkedHashMap(); + + if (section != null) { + for (String key : section.getKeys(false)) { + List commands; + + if (section.isList(key)) { + commands = section.getStringList(key); + } else { + commands = ImmutableList.of(section.getString(key)); + } + + result.put(key, commands.toArray(new String[commands.size()])); + } + } + + return result; + } + + public void removeBukkitSpawnRadius() { + configuration.set("settings.spawn-radius", null); + saveConfig(); + } + + public int getBukkitSpawnRadius() { + return configuration.getInt("settings.spawn-radius", -1); + } + + @Override + public String getShutdownMessage() { + return configuration.getString("settings.shutdown-message"); + } + + @Override + public int getSpawnRadius() { + return ((DedicatedServer) console).propertyManager.getInt("spawn-protection", 16); + } + + @Override + public void setSpawnRadius(int value) { + configuration.set("settings.spawn-radius", value); + saveConfig(); + } + + @Override + public boolean getOnlineMode() { + return online.value; + } + + @Override + public boolean getAllowFlight() { + return console.getAllowFlight(); + } + + @Override + public boolean isHardcore() { + return console.isHardcore(); + } + + public ChunkGenerator getGenerator(String world) { + ConfigurationSection section = configuration.getConfigurationSection("worlds"); + ChunkGenerator result = null; + + if (section != null) { + section = section.getConfigurationSection(world); + + if (section != null) { + String name = section.getString("generator"); + + if ((name != null) && (!name.equals(""))) { + String[] split = name.split(":", 2); + String id = (split.length > 1) ? split[1] : null; + Plugin plugin = pluginManager.getPlugin(split[0]); + + if (plugin == null) { + getLogger().severe("Could not set generator for default world '" + world + "': Plugin '" + split[0] + "' does not exist"); + } else if (!plugin.isEnabled()) { + getLogger().severe("Could not set generator for default world '" + world + "': Plugin '" + plugin.getDescription().getFullName() + "' is not enabled yet (is it load:STARTUP?)"); + } else { + try { + result = plugin.getDefaultWorldGenerator(world, id); + if (result == null) { + getLogger().severe("Could not set generator for default world '" + world + "': Plugin '" + plugin.getDescription().getFullName() + "' lacks a default world generator"); + } + } catch (Throwable t) { + plugin.getLogger().log(Level.SEVERE, "Could not set generator for default world '" + world + "': Plugin '" + plugin.getDescription().getFullName(), t); + } + } + } + } + } + + return result; + } + + @Override + @Deprecated + public CraftMapView getMap(int id) { + PersistentCollection collection = console.getWorldServer(DimensionManager.OVERWORLD).worldMaps; + WorldMap worldmap = (WorldMap) collection.get(DimensionManager.OVERWORLD, WorldMap::new, "map_" + id); + if (worldmap == null) { + return null; + } + return worldmap.mapView; + } + + @Override + public CraftMapView createMap(World world) { + Validate.notNull(world, "World cannot be null"); + + net.minecraft.server.ItemStack stack = new net.minecraft.server.ItemStack(Items.MAP, 1); + WorldMap worldmap = ItemWorldMap.getSavedMap(stack, ((CraftWorld) world).getHandle()); + return worldmap.mapView; + } + + @Override + public ItemStack createExplorerMap(World world, Location location, StructureType structureType) { + return this.createExplorerMap(world, location, structureType, 100, true); + } + + @Override + public ItemStack createExplorerMap(World world, Location location, StructureType structureType, int radius, boolean findUnexplored) { + Validate.notNull(world, "World cannot be null"); + Validate.notNull(structureType, "StructureType cannot be null"); + Validate.notNull(structureType.getMapIcon(), "Cannot create explorer maps for StructureType " + structureType.getName()); + + WorldServer worldServer = ((CraftWorld) world).getHandle(); + Location structureLocation = world.locateNearestStructure(location, structureType, radius, findUnexplored); + BlockPosition structurePosition = new BlockPosition(structureLocation.getBlockX(), structureLocation.getBlockY(), structureLocation.getBlockZ()); + + // Create map with trackPlayer = true, unlimitedTracking = true + net.minecraft.server.ItemStack stack = ItemWorldMap.createFilledMapView(worldServer, structurePosition.getX(), structurePosition.getZ(), MapView.Scale.NORMAL.getValue(), true, true); + ItemWorldMap.applySepiaFilter(worldServer, stack); + // "+" map ID taken from EntityVillager + ItemWorldMap.getSavedMap(stack, worldServer).decorateMap(stack, structurePosition, "+", MapIcon.Type.a(structureType.getMapIcon().getValue())); + + return CraftItemStack.asBukkitCopy(stack); + } + + @Override + public void shutdown() { + console.safeShutdown(); + } + + @Override + public int broadcast(String message, String permission) { + Set recipients = new HashSet<>(); + for (Permissible permissible : getPluginManager().getPermissionSubscriptions(permission)) { + if (permissible instanceof CommandSender && permissible.hasPermission(permission)) { + recipients.add((CommandSender) permissible); + } + } + + BroadcastMessageEvent broadcastMessageEvent = new BroadcastMessageEvent(message, recipients); + getPluginManager().callEvent(broadcastMessageEvent); + + if (broadcastMessageEvent.isCancelled()) { + return 0; + } + + message = broadcastMessageEvent.getMessage(); + + for (CommandSender recipient : recipients) { + recipient.sendMessage(message); + } + + return recipients.size(); + } + + // Paper start + @Nullable + public UUID getPlayerUniqueId(String name) { + Player player = Bukkit.getPlayerExact(name); + if (player != null) { + return player.getUniqueId(); + } + GameProfile profile; + // Only fetch an online UUID in online mode + if (MinecraftServer.getServer().getOnlineMode() + || (org.spigotmc.SpigotConfig.bungee && com.destroystokyo.paper.PaperConfig.bungeeOnlineMode)) { + profile = console.getUserCache().getProfile( name ); + } else { + // Make an OfflinePlayer using an offline mode UUID since the name has no profile + profile = new GameProfile(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)), name); + } + return profile != null ? profile.getId() : null; + } + // Paper end + + @Override + @Deprecated + public OfflinePlayer getOfflinePlayer(String name) { + Validate.notNull(name, "Name cannot be null"); + Validate.notEmpty(name, "Name cannot be empty"); + + OfflinePlayer result = getPlayerExact(name); + if (result == null) { + // Spigot Start + GameProfile profile = null; + // Only fetch an online UUID in online mode + if ( MinecraftServer.getServer().getOnlineMode() + || com.destroystokyo.paper.PaperConfig.isProxyOnlineMode()) // Paper - Handle via setting + { + profile = console.getUserCache().getProfile( name ); + } + // Spigot end + if (profile == null) { + // Make an OfflinePlayer using an offline mode UUID since the name has no profile + result = getOfflinePlayer(new GameProfile(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)), name)); + } else { + // Use the GameProfile even when we get a UUID so we ensure we still have a name + result = getOfflinePlayer(profile); + } + } else { + offlinePlayers.remove(result.getUniqueId()); + } + + return result; + } + + @Override + public OfflinePlayer getOfflinePlayer(UUID id) { + Validate.notNull(id, "UUID cannot be null"); + + OfflinePlayer result = getPlayer(id); + if (result == null) { + result = offlinePlayers.get(id); + if (result == null) { + result = new CraftOfflinePlayer(this, new GameProfile(id, null)); + offlinePlayers.put(id, result); + } + } else { + offlinePlayers.remove(id); + } + + return result; + } + + public OfflinePlayer getOfflinePlayer(GameProfile profile) { + OfflinePlayer player = new CraftOfflinePlayer(this, profile); + offlinePlayers.put(profile.getId(), player); + return player; + } + + @Override + @SuppressWarnings("unchecked") + public Set getIPBans() { + return new HashSet(Arrays.asList(playerList.getIPBans().getEntries())); + } + + @Override + public void banIP(String address) { + Validate.notNull(address, "Address cannot be null."); + + this.getBanList(org.bukkit.BanList.Type.IP).addBan(address, null, null, null); + } + + @Override + public void unbanIP(String address) { + Validate.notNull(address, "Address cannot be null."); + + this.getBanList(org.bukkit.BanList.Type.IP).pardon(address); + } + + @Override + public Set getBannedPlayers() { + Set result = new HashSet(); + + for (JsonListEntry entry : playerList.getProfileBans().getValues()) { + result.add(getOfflinePlayer((GameProfile) entry.getKey())); + } + + return result; + } + + @Override + public BanList getBanList(BanList.Type type) { + Validate.notNull(type, "Type cannot be null"); + + switch(type){ + case IP: + return new CraftIpBanList(playerList.getIPBans()); + case NAME: + default: + return new CraftProfileBanList(playerList.getProfileBans()); + } + } + + @Override + public void setWhitelist(boolean value) { + playerList.setHasWhitelist(value); + console.getPropertyManager().setProperty("white-list", value); + } + + @Override + public Set getWhitelistedPlayers() { + Set result = new LinkedHashSet(); + + for (JsonListEntry entry : playerList.getWhitelist().getValues()) { + result.add(getOfflinePlayer((GameProfile) entry.getKey())); + } + + return result; + } + + @Override + public Set getOperators() { + Set result = new HashSet(); + + for (JsonListEntry entry : playerList.getOPs().getValues()) { + result.add(getOfflinePlayer((GameProfile) entry.getKey())); + } + + return result; + } + + @Override + public void reloadWhitelist() { + playerList.reloadWhitelist(); + } + + @Override + public GameMode getDefaultGameMode() { + return GameMode.getByValue(console.getWorldServer(DimensionManager.OVERWORLD).getWorldData().getGameType().getId()); + } + + @Override + public void setDefaultGameMode(GameMode mode) { + Validate.notNull(mode, "Mode cannot be null"); + + for (World world : getWorlds()) { + ((CraftWorld) world).getHandle().worldData.setGameType(EnumGamemode.getById(mode.getValue())); + } + } + + @Override + public ConsoleCommandSender getConsoleSender() { + return console.console; + } + + public EntityMetadataStore getEntityMetadata() { + return entityMetadata; + } + + public PlayerMetadataStore getPlayerMetadata() { + return playerMetadata; + } + + public WorldMetadataStore getWorldMetadata() { + return worldMetadata; + } + + @Override + public File getWorldContainer() { + if (this.getServer().universe != null) { + return this.getServer().universe; + } + + if (container == null) { + container = new File(configuration.getString("settings.world-container", ".")); + } + + return container; + } + + @Override + public OfflinePlayer[] getOfflinePlayers() { + WorldNBTStorage storage = (WorldNBTStorage) console.getWorldServer(DimensionManager.OVERWORLD).getDataManager(); + String[] files = storage.getPlayerDir().list(new DatFileFilter()); + Set players = new HashSet(); + + for (String file : files) { + try { + players.add(getOfflinePlayer(UUID.fromString(file.substring(0, file.length() - 4)))); + } catch (IllegalArgumentException ex) { + // Who knows what is in this directory, just ignore invalid files + } + } + + players.addAll(getOnlinePlayers()); + + return players.toArray(new OfflinePlayer[players.size()]); + } + + @Override + public Messenger getMessenger() { + return messenger; + } + + @Override + public void sendPluginMessage(Plugin source, String channel, byte[] message) { + StandardMessenger.validatePluginMessage(getMessenger(), source, channel, message); + + for (Player player : getOnlinePlayers()) { + player.sendPluginMessage(source, channel, message); + } + } + + @Override + public Set getListeningPluginChannels() { + Set result = new HashSet(); + + for (Player player : getOnlinePlayers()) { + result.addAll(player.getListeningPluginChannels()); + } + + return result; + } + + @Override + public Inventory createInventory(InventoryHolder owner, InventoryType type) { + Validate.isTrue(type.isCreatable(), "Cannot open an inventory of type ", type); + return CraftInventoryCreator.INSTANCE.createInventory(owner, type); + } + + @Override + public Inventory createInventory(InventoryHolder owner, InventoryType type, String title) { + Validate.isTrue(type.isCreatable(), "Cannot open an inventory of type ", type); + return CraftInventoryCreator.INSTANCE.createInventory(owner, type, title); + } + + @Override + public Inventory createInventory(InventoryHolder owner, int size) throws IllegalArgumentException { + Validate.isTrue(size % 9 == 0, "Chests must have a size that is a multiple of 9!"); + return CraftInventoryCreator.INSTANCE.createInventory(owner, size); + } + + @Override + public Inventory createInventory(InventoryHolder owner, int size, String title) throws IllegalArgumentException { + Validate.isTrue(size % 9 == 0, "Chests must have a size that is a multiple of 9!"); + return CraftInventoryCreator.INSTANCE.createInventory(owner, size, title); + } + + @Override + public Merchant createMerchant(String title) { + return new CraftMerchantCustom(title == null ? InventoryType.MERCHANT.getDefaultTitle() : title); + } + + @Override + public HelpMap getHelpMap() { + return helpMap; + } + + @Override // Paper - add override + public SimpleCommandMap getCommandMap() { + return commandMap; + } + + @Override + public int getMonsterSpawnLimit() { + return monsterSpawn; + } + + @Override + public int getAnimalSpawnLimit() { + return animalSpawn; + } + + @Override + public int getWaterAnimalSpawnLimit() { + return waterAnimalSpawn; + } + + @Override + public int getAmbientSpawnLimit() { + return ambientSpawn; + } + + @Override + public boolean isPrimaryThread() { + return Thread.currentThread().equals(console.primaryThread); + } + + @Override + public String getMotd() { + return console.getMotd(); + } + + @Override + public WarningState getWarningState() { + return warningState; + } + + public List tabComplete(CommandSender sender, String message, WorldServer world, Vec3D pos, boolean forceCommand) { + if (!(sender instanceof Player)) { + return ImmutableList.of(); + } + + List offers; + Player player = (Player) sender; + if (message.startsWith("/") || forceCommand) { + offers = tabCompleteCommand(player, message, world, pos); + } else { + offers = tabCompleteChat(player, message); + } + + TabCompleteEvent tabEvent = new TabCompleteEvent(player, message, offers, message.startsWith("/") || forceCommand, pos != null ? MCUtil.toLocation(((CraftWorld) player.getWorld()).getHandle(), new BlockPosition(pos)) : null); // Paper + getPluginManager().callEvent(tabEvent); + + return tabEvent.isCancelled() ? Collections.EMPTY_LIST : tabEvent.getCompletions(); + } + + public List tabCompleteCommand(Player player, String message, WorldServer world, Vec3D pos) { + // Spigot Start + if ( (org.spigotmc.SpigotConfig.tabComplete < 0 || message.length() <= org.spigotmc.SpigotConfig.tabComplete) && !message.contains( " " ) ) + { + return ImmutableList.of(); + } + // Spigot End + + List completions = null; + try { + if (message.startsWith("/")) { + // Trim leading '/' if present (won't always be present in command blocks) + message = message.substring(1); + } + if (pos == null) { + completions = getCommandMap().tabComplete(player, message); + } else { + completions = getCommandMap().tabComplete(player, message, new Location(world.getWorld(), pos.x, pos.y, pos.z)); + } + } catch (CommandException ex) { + player.sendMessage(ChatColor.RED + "An internal error occurred while attempting to tab-complete this command"); + getLogger().log(Level.SEVERE, "Exception when " + player.getName() + " attempted to tab complete " + message, ex); + } + + return completions == null ? ImmutableList.of() : completions; + } + + public List tabCompleteChat(Player player, String message) { + List completions = new ArrayList(); + PlayerChatTabCompleteEvent event = new PlayerChatTabCompleteEvent(player, message, completions); + String token = event.getLastToken(); + for (Player p : getOnlinePlayers()) { + if (player.canSee(p) && StringUtil.startsWithIgnoreCase(p.getName(), token)) { + completions.add(p.getName()); + } + } + pluginManager.callEvent(event); + + Iterator it = completions.iterator(); + while (it.hasNext()) { + Object current = it.next(); + if (!(current instanceof String)) { + // Sanity + it.remove(); + } + } + Collections.sort(completions, String.CASE_INSENSITIVE_ORDER); + return completions; + } + + @Override + public CraftItemFactory getItemFactory() { + return CraftItemFactory.instance(); + } + + @Override + public CraftScoreboardManager getScoreboardManager() { + return scoreboardManager; + } + + public void checkSaveState() { + if (this.playerCommandState || this.printSaveWarning || this.console.autosavePeriod <= 0) { + return; + } + this.printSaveWarning = true; + getLogger().log(Level.WARNING, "A manual (plugin-induced) save has been detected while server is configured to auto-save. This may affect performance.", warningState == WarningState.ON ? new Throwable() : null); + } + + @Override + public CraftIconCache getServerIcon() { + return icon; + } + + @Override + public CraftIconCache loadServerIcon(File file) throws Exception { + Validate.notNull(file, "File cannot be null"); + if (!file.isFile()) { + throw new IllegalArgumentException(file + " is not a file"); + } + return loadServerIcon0(file); + } + + static CraftIconCache loadServerIcon0(File file) throws Exception { + return loadServerIcon0(ImageIO.read(file)); + } + + @Override + public CraftIconCache loadServerIcon(BufferedImage image) throws Exception { + Validate.notNull(image, "Image cannot be null"); + return loadServerIcon0(image); + } + + static CraftIconCache loadServerIcon0(BufferedImage image) throws Exception { + ByteBuf bytebuf = Unpooled.buffer(); + + Validate.isTrue(image.getWidth() == 64, "Must be 64 pixels wide"); + Validate.isTrue(image.getHeight() == 64, "Must be 64 pixels high"); + ImageIO.write(image, "PNG", new ByteBufOutputStream(bytebuf)); + ByteBuffer bytebuffer = Base64.getEncoder().encode(bytebuf.nioBuffer()); + + return new CraftIconCache("data:image/png;base64," + StandardCharsets.UTF_8.decode(bytebuffer)); + } + + @Override + public void setIdleTimeout(int threshold) { + console.setIdleTimeout(threshold); + } + + @Override + public int getIdleTimeout() { + return console.getIdleTimeout(); + } + + @Override + public ChunkGenerator.ChunkData createChunkData(World world) { + Validate.notNull(world, "World cannot be null"); + return new CraftChunkData(world); + } + + @Override + public BossBar createBossBar(String title, BarColor color, BarStyle style, BarFlag... flags) { + return new CraftBossBar(title, color, style, flags); + } + + @Override + public KeyedBossBar createBossBar(NamespacedKey key, String title, BarColor barColor, BarStyle barStyle, BarFlag... barFlags) { + Preconditions.checkArgument(key != null, "key"); + + BossBattleCustom bossBattleCustom = getServer().getBossBattleCustomData().register(CraftNamespacedKey.toMinecraft(key), CraftChatMessage.fromString(title, true)[0]); + CraftKeyedBossbar craftKeyedBossbar = new CraftKeyedBossbar(bossBattleCustom); + craftKeyedBossbar.setColor(barColor); + craftKeyedBossbar.setStyle(barStyle); + for (BarFlag flag : barFlags) { + craftKeyedBossbar.addFlag(flag); + } + + return craftKeyedBossbar; + } + + @Override + public Iterator getBossBars() { + return Iterators.unmodifiableIterator(Iterators.transform(getServer().getBossBattleCustomData().getBattles().iterator(), new Function() { + @Override + public org.bukkit.boss.KeyedBossBar apply(BossBattleCustom bossBattleCustom) { + return bossBattleCustom.getBukkitEntity(); + } + })); + } + + @Override + public KeyedBossBar getBossBar(NamespacedKey key) { + Preconditions.checkArgument(key != null, "key"); + net.minecraft.server.BossBattleCustom bossBattleCustom = getServer().getBossBattleCustomData().a(CraftNamespacedKey.toMinecraft(key)); + + return (bossBattleCustom == null) ? null : bossBattleCustom.getBukkitEntity(); + } + + @Override + public boolean removeBossBar(NamespacedKey key) { + Preconditions.checkArgument(key != null, "key"); + net.minecraft.server.BossBattleCustomData bossBattleCustomData = getServer().getBossBattleCustomData(); + net.minecraft.server.BossBattleCustom bossBattleCustom = bossBattleCustomData.a(CraftNamespacedKey.toMinecraft(key)); + + if (bossBattleCustom != null) { + bossBattleCustomData.remove(bossBattleCustom); + return true; + } + + return false; + } + + @Override + public Entity getEntity(UUID uuid) { + Validate.notNull(uuid, "UUID cannot be null"); + + for (WorldServer world : getServer().getWorlds()) { + net.minecraft.server.Entity entity = world.getEntity(uuid); + if (entity != null) { + return entity.getBukkitEntity(); + } + } + + return null; + } + + @Override + public org.bukkit.advancement.Advancement getAdvancement(NamespacedKey key) { + Preconditions.checkArgument(key != null, "key"); + + Advancement advancement = console.getAdvancementData().a(CraftNamespacedKey.toMinecraft(key)); + return (advancement == null) ? null : advancement.bukkit; + } + + @Override + public Iterator advancementIterator() { + return Iterators.unmodifiableIterator(Iterators.transform(console.getAdvancementData().b().iterator(), new Function() { // PAIL: rename + @Override + public org.bukkit.advancement.Advancement apply(Advancement advancement) { + return advancement.bukkit; + } + })); + } + + @Override + public BlockData createBlockData(org.bukkit.Material material) { + Validate.isTrue(material != null, "Must provide material"); + + return createBlockData(material, (String) null); + } + + @Override + public BlockData createBlockData(org.bukkit.Material material, Consumer consumer) { + BlockData data = createBlockData(material); + + if (consumer != null) { + consumer.accept(data); + } + + return data; + } + + @Override + public BlockData createBlockData(String data) throws IllegalArgumentException { + Validate.isTrue(data != null, "Must provide data"); + + return createBlockData(null, data); + } + + @Override + public BlockData createBlockData(org.bukkit.Material material, String data) { + Validate.isTrue(material != null || data != null, "Must provide one of material or data"); + + return CraftBlockData.newData(material, data); + } + + @Override + @SuppressWarnings("unchecked") + public org.bukkit.Tag getTag(String registry, NamespacedKey tag, Class clazz) { + MinecraftKey key = CraftNamespacedKey.toMinecraft(tag); + + switch (registry) { + case org.bukkit.Tag.REGISTRY_BLOCKS: + Preconditions.checkArgument(clazz == org.bukkit.Material.class, "Block namespace must have material type"); + + return (org.bukkit.Tag) new CraftBlockTag(console.getTagRegistry().a(), key); + case org.bukkit.Tag.REGISTRY_ITEMS: + Preconditions.checkArgument(clazz == org.bukkit.Material.class, "Item namespace must have material type"); + + return (org.bukkit.Tag) new CraftItemTag(console.getTagRegistry().b(), key); + default: + throw new IllegalArgumentException(); + } + } + + @Override + @SuppressWarnings("unchecked") + public Iterable> getTags(String registry, Class clazz) { + switch (registry) { + case org.bukkit.Tag.REGISTRY_BLOCKS: + Preconditions.checkArgument(clazz == org.bukkit.Material.class, "Block namespace must have material type"); + + TagsServer blockTags = console.getTagRegistry().a(); // PAIL: getBlockTags + return blockTags.c().keySet().stream().map(key -> (org.bukkit.Tag) new CraftBlockTag(blockTags, key)).collect(ImmutableList.toImmutableList()); + case org.bukkit.Tag.REGISTRY_ITEMS: + Preconditions.checkArgument(clazz == org.bukkit.Material.class, "Item namespace must have material type"); + + TagsServer itemTags = console.getTagRegistry().b(); // PAIL: getItemTags + return itemTags.c().keySet().stream().map(key -> (org.bukkit.Tag) new CraftItemTag(itemTags, key)).collect(ImmutableList.toImmutableList()); + default: + throw new IllegalArgumentException(); + } + } + + public LootTable getLootTable(NamespacedKey key) { + Validate.notNull(key, "NamespacedKey cannot be null"); + + LootTableRegistry registry = getServer().getLootTableRegistry(); + return new CraftLootTable(key, registry.getLootTable(CraftNamespacedKey.toMinecraft(key))); + } + + @Override + public List selectEntities(CommandSender sender, String selector) { + Preconditions.checkArgument(selector != null, "Selector cannot be null"); + Preconditions.checkArgument(sender != null, "Sender cannot be null"); + + ArgumentEntity arg = ArgumentEntity.b(); + List nms; + + try { + StringReader reader = new StringReader(selector); + nms = arg.parse(reader, true).b(VanillaCommandWrapper.getListener(sender)); + Preconditions.checkArgument(!reader.canRead(), "Spurious trailing data in selector: " + selector); + } catch (CommandSyntaxException ex) { + throw new IllegalArgumentException("Could not parse selector: " + selector, ex); + } + + return new ArrayList<>(Lists.transform(nms, (entity) -> entity.getBukkitEntity())); + } + + @Deprecated + @Override + public UnsafeValues getUnsafe() { + return CraftMagicNumbers.INSTANCE; + } + + // Paper - Add getTPS API - Further improve tick loop + @Override + public double[] getTPS() { + return new double[] { + MinecraftServer.getServer().tps1.getAverage(), + MinecraftServer.getServer().tps5.getAverage(), + MinecraftServer.getServer().tps15.getAverage() + }; + } + // Paper end + + private final Spigot spigot = new Spigot() + { + + @Deprecated + @Override + public YamlConfiguration getConfig() + { + return org.spigotmc.SpigotConfig.config; + } + + @Override + public YamlConfiguration getBukkitConfig() + { + return configuration; + } + + @Override + public YamlConfiguration getSpigotConfig() + { + return org.spigotmc.SpigotConfig.config; + } + + @Override + public YamlConfiguration getPaperConfig() + { + return com.destroystokyo.paper.PaperConfig.config; + } + + @Override + public void restart() { + org.spigotmc.RestartCommand.restart(); + } + + @Override + public void broadcast(BaseComponent component) { + for (Player player : getOnlinePlayers()) { + player.spigot().sendMessage(component); + } + } + + @Override + public void broadcast(BaseComponent... components) { + for (Player player : getOnlinePlayers()) { + player.spigot().sendMessage(components); + } + } + }; + + public Spigot spigot() + { + return spigot; + } + + // Paper start + @SuppressWarnings({"rawtypes", "unchecked"}) + public static boolean dumpHeap(File file) { + try { + if (file.getParentFile() != null) { + file.getParentFile().mkdirs(); + } + + Class clazz = Class.forName("com.sun.management.HotSpotDiagnosticMXBean"); + javax.management.MBeanServer server = java.lang.management.ManagementFactory.getPlatformMBeanServer(); + Object hotspotMBean = java.lang.management.ManagementFactory.newPlatformMXBeanProxy(server, "com.sun.management:type=HotSpotDiagnostic", clazz); + java.lang.reflect.Method m = clazz.getMethod("dumpHeap", String.class, boolean.class); + m.invoke(hotspotMBean, file.getPath(), true); + return true; + } catch (Throwable t) { + Bukkit.getLogger().severe("Could not write heap to " + file); + t.printStackTrace(); + return false; + } + } + + @Override + public void reloadPermissions() { + pluginManager.clearPermissions(); + if (com.destroystokyo.paper.PaperConfig.loadPermsBeforePlugins) loadCustomPermissions(); + for (Plugin plugin : pluginManager.getPlugins()) { + for (Permission perm : plugin.getDescription().getPermissions()) { + try { + pluginManager.addPermission(perm); + } catch (IllegalArgumentException ex) { + getLogger().log(Level.WARNING, "Plugin " + plugin.getDescription().getFullName() + " tried to register permission '" + perm.getName() + "' but it's already registered", ex); + } + } + } + if (!com.destroystokyo.paper.PaperConfig.loadPermsBeforePlugins) loadCustomPermissions(); + DefaultPermissions.registerCorePermissions(); + CraftDefaultPermissions.registerCorePermissions(); + } + + @Override + public boolean reloadCommandAliases() { + Set removals = getCommandAliases().keySet().stream() + .map(key -> key.toLowerCase(java.util.Locale.ENGLISH)) + .collect(java.util.stream.Collectors.toSet()); + getCommandMap().getKnownCommands().keySet().removeIf(removals::contains); + File file = getCommandsConfigFile(); + try { + commandsConfiguration.load(file); + } catch (FileNotFoundException ex) { + return false; + } catch (IOException | org.bukkit.configuration.InvalidConfigurationException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + file, ex); + return false; + } + commandMap.registerServerAliases(); + return true; + } + + @Override + public boolean suggestPlayerNamesWhenNullTabCompletions() { + return com.destroystokyo.paper.PaperConfig.suggestPlayersWhenNullTabCompletions; + } + + public String getPermissionMessage() { + return com.destroystokyo.paper.PaperConfig.noPermissionMessage; + } + + public com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nonnull UUID uuid) { + return createProfile(uuid, null); + } + + public com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nonnull String name) { + return createProfile(null, name); + } + + public com.destroystokyo.paper.profile.PlayerProfile createProfile(@Nullable UUID uuid, @Nullable String name) { + Player player = uuid != null ? Bukkit.getPlayer(uuid) : (name != null ? Bukkit.getPlayerExact(name) : null); + if (player != null) { + return new com.destroystokyo.paper.profile.CraftPlayerProfile((CraftPlayer)player); + } + return new com.destroystokyo.paper.profile.CraftPlayerProfile(uuid, name); + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftSound.java b/src/main/java/org/bukkit/craftbukkit/CraftSound.java new file mode 100644 index 000000000000..ee8219e3bac2 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftSound.java @@ -0,0 +1,709 @@ +package org.bukkit.craftbukkit; + +import com.google.common.base.Preconditions; +import net.minecraft.server.IRegistry; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.SoundEffect; + +import org.apache.commons.lang.Validate; +import org.bukkit.Sound; + +public enum CraftSound { + + AMBIENT_CAVE("ambient.cave"), + AMBIENT_UNDERWATER_ENTER("ambient.underwater.enter"), + AMBIENT_UNDERWATER_EXIT("ambient.underwater.exit"), + AMBIENT_UNDERWATER_LOOP("ambient.underwater.loop"), + AMBIENT_UNDERWATER_LOOP_ADDITIONS("ambient.underwater.loop.additions"), + AMBIENT_UNDERWATER_LOOP_ADDITIONS_RARE("ambient.underwater.loop.additions.rare"), + AMBIENT_UNDERWATER_LOOP_ADDITIONS_ULTRA_RARE("ambient.underwater.loop.additions.ultra_rare"), + BLOCK_ANVIL_BREAK("block.anvil.break"), + BLOCK_ANVIL_DESTROY("block.anvil.destroy"), + BLOCK_ANVIL_FALL("block.anvil.fall"), + BLOCK_ANVIL_HIT("block.anvil.hit"), + BLOCK_ANVIL_LAND("block.anvil.land"), + BLOCK_ANVIL_PLACE("block.anvil.place"), + BLOCK_ANVIL_STEP("block.anvil.step"), + BLOCK_ANVIL_USE("block.anvil.use"), + BLOCK_BEACON_ACTIVATE("block.beacon.activate"), + BLOCK_BEACON_AMBIENT("block.beacon.ambient"), + BLOCK_BEACON_DEACTIVATE("block.beacon.deactivate"), + BLOCK_BEACON_POWER_SELECT("block.beacon.power_select"), + BLOCK_BREWING_STAND_BREW("block.brewing_stand.brew"), + BLOCK_BUBBLE_COLUMN_BUBBLE_POP("block.bubble_column.bubble_pop"), + BLOCK_BUBBLE_COLUMN_UPWARDS_AMBIENT("block.bubble_column.upwards_ambient"), + BLOCK_BUBBLE_COLUMN_UPWARDS_INSIDE("block.bubble_column.upwards_inside"), + BLOCK_BUBBLE_COLUMN_WHIRLPOOL_AMBIENT("block.bubble_column.whirlpool_ambient"), + BLOCK_BUBBLE_COLUMN_WHIRLPOOL_INSIDE("block.bubble_column.whirlpool_inside"), + BLOCK_CHEST_CLOSE("block.chest.close"), + BLOCK_CHEST_LOCKED("block.chest.locked"), + BLOCK_CHEST_OPEN("block.chest.open"), + BLOCK_CHORUS_FLOWER_DEATH("block.chorus_flower.death"), + BLOCK_CHORUS_FLOWER_GROW("block.chorus_flower.grow"), + BLOCK_COMPARATOR_CLICK("block.comparator.click"), + BLOCK_CONDUIT_ACTIVATE("block.conduit.activate"), + BLOCK_CONDUIT_AMBIENT("block.conduit.ambient"), + BLOCK_CONDUIT_AMBIENT_SHORT("block.conduit.ambient.short"), + BLOCK_CONDUIT_ATTACK_TARGET("block.conduit.attack.target"), + BLOCK_CONDUIT_DEACTIVATE("block.conduit.deactivate"), + BLOCK_CORAL_BLOCK_BREAK("block.coral_block.break"), + BLOCK_CORAL_BLOCK_FALL("block.coral_block.fall"), + BLOCK_CORAL_BLOCK_HIT("block.coral_block.hit"), + BLOCK_CORAL_BLOCK_PLACE("block.coral_block.place"), + BLOCK_CORAL_BLOCK_STEP("block.coral_block.step"), + BLOCK_DISPENSER_DISPENSE("block.dispenser.dispense"), + BLOCK_DISPENSER_FAIL("block.dispenser.fail"), + BLOCK_DISPENSER_LAUNCH("block.dispenser.launch"), + BLOCK_ENCHANTMENT_TABLE_USE("block.enchantment_table.use"), + BLOCK_ENDER_CHEST_CLOSE("block.ender_chest.close"), + BLOCK_ENDER_CHEST_OPEN("block.ender_chest.open"), + BLOCK_END_GATEWAY_SPAWN("block.end_gateway.spawn"), + BLOCK_END_PORTAL_FRAME_FILL("block.end_portal_frame.fill"), + BLOCK_END_PORTAL_SPAWN("block.end_portal.spawn"), + BLOCK_FENCE_GATE_CLOSE("block.fence_gate.close"), + BLOCK_FENCE_GATE_OPEN("block.fence_gate.open"), + BLOCK_FIRE_AMBIENT("block.fire.ambient"), + BLOCK_FIRE_EXTINGUISH("block.fire.extinguish"), + BLOCK_FURNACE_FIRE_CRACKLE("block.furnace.fire_crackle"), + BLOCK_GLASS_BREAK("block.glass.break"), + BLOCK_GLASS_FALL("block.glass.fall"), + BLOCK_GLASS_HIT("block.glass.hit"), + BLOCK_GLASS_PLACE("block.glass.place"), + BLOCK_GLASS_STEP("block.glass.step"), + BLOCK_GRASS_BREAK("block.grass.break"), + BLOCK_GRASS_FALL("block.grass.fall"), + BLOCK_GRASS_HIT("block.grass.hit"), + BLOCK_GRASS_PLACE("block.grass.place"), + BLOCK_GRASS_STEP("block.grass.step"), + BLOCK_GRAVEL_BREAK("block.gravel.break"), + BLOCK_GRAVEL_FALL("block.gravel.fall"), + BLOCK_GRAVEL_HIT("block.gravel.hit"), + BLOCK_GRAVEL_PLACE("block.gravel.place"), + BLOCK_GRAVEL_STEP("block.gravel.step"), + BLOCK_IRON_DOOR_CLOSE("block.iron_door.close"), + BLOCK_IRON_DOOR_OPEN("block.iron_door.open"), + BLOCK_IRON_TRAPDOOR_CLOSE("block.iron_trapdoor.close"), + BLOCK_IRON_TRAPDOOR_OPEN("block.iron_trapdoor.open"), + BLOCK_LADDER_BREAK("block.ladder.break"), + BLOCK_LADDER_FALL("block.ladder.fall"), + BLOCK_LADDER_HIT("block.ladder.hit"), + BLOCK_LADDER_PLACE("block.ladder.place"), + BLOCK_LADDER_STEP("block.ladder.step"), + BLOCK_LAVA_AMBIENT("block.lava.ambient"), + BLOCK_LAVA_EXTINGUISH("block.lava.extinguish"), + BLOCK_LAVA_POP("block.lava.pop"), + BLOCK_LEVER_CLICK("block.lever.click"), + BLOCK_LILY_PAD_PLACE("block.lily_pad.place"), + BLOCK_METAL_BREAK("block.metal.break"), + BLOCK_METAL_FALL("block.metal.fall"), + BLOCK_METAL_HIT("block.metal.hit"), + BLOCK_METAL_PLACE("block.metal.place"), + BLOCK_METAL_PRESSURE_PLATE_CLICK_OFF("block.metal_pressure_plate.click_off"), + BLOCK_METAL_PRESSURE_PLATE_CLICK_ON("block.metal_pressure_plate.click_on"), + BLOCK_METAL_STEP("block.metal.step"), + BLOCK_NOTE_BLOCK_BASEDRUM("block.note_block.basedrum"), + BLOCK_NOTE_BLOCK_BASS("block.note_block.bass"), + BLOCK_NOTE_BLOCK_BELL("block.note_block.bell"), + BLOCK_NOTE_BLOCK_CHIME("block.note_block.chime"), + BLOCK_NOTE_BLOCK_FLUTE("block.note_block.flute"), + BLOCK_NOTE_BLOCK_GUITAR("block.note_block.guitar"), + BLOCK_NOTE_BLOCK_HARP("block.note_block.harp"), + BLOCK_NOTE_BLOCK_HAT("block.note_block.hat"), + BLOCK_NOTE_BLOCK_PLING("block.note_block.pling"), + BLOCK_NOTE_BLOCK_SNARE("block.note_block.snare"), + BLOCK_NOTE_BLOCK_XYLOPHONE("block.note_block.xylophone"), + BLOCK_PISTON_CONTRACT("block.piston.contract"), + BLOCK_PISTON_EXTEND("block.piston.extend"), + BLOCK_PORTAL_AMBIENT("block.portal.ambient"), + BLOCK_PORTAL_TRAVEL("block.portal.travel"), + BLOCK_PORTAL_TRIGGER("block.portal.trigger"), + BLOCK_PUMPKIN_CARVE("block.pumpkin.carve"), + BLOCK_REDSTONE_TORCH_BURNOUT("block.redstone_torch.burnout"), + BLOCK_SAND_BREAK("block.sand.break"), + BLOCK_SAND_FALL("block.sand.fall"), + BLOCK_SAND_HIT("block.sand.hit"), + BLOCK_SAND_PLACE("block.sand.place"), + BLOCK_SAND_STEP("block.sand.step"), + BLOCK_SHULKER_BOX_CLOSE("block.shulker_box.close"), + BLOCK_SHULKER_BOX_OPEN("block.shulker_box.open"), + BLOCK_SLIME_BLOCK_BREAK("block.slime_block.break"), + BLOCK_SLIME_BLOCK_FALL("block.slime_block.fall"), + BLOCK_SLIME_BLOCK_HIT("block.slime_block.hit"), + BLOCK_SLIME_BLOCK_PLACE("block.slime_block.place"), + BLOCK_SLIME_BLOCK_STEP("block.slime_block.step"), + BLOCK_SNOW_BREAK("block.snow.break"), + BLOCK_SNOW_FALL("block.snow.fall"), + BLOCK_SNOW_HIT("block.snow.hit"), + BLOCK_SNOW_PLACE("block.snow.place"), + BLOCK_SNOW_STEP("block.snow.step"), + BLOCK_STONE_BREAK("block.stone.break"), + BLOCK_STONE_BUTTON_CLICK_OFF("block.stone_button.click_off"), + BLOCK_STONE_BUTTON_CLICK_ON("block.stone_button.click_on"), + BLOCK_STONE_FALL("block.stone.fall"), + BLOCK_STONE_HIT("block.stone.hit"), + BLOCK_STONE_PLACE("block.stone.place"), + BLOCK_STONE_PRESSURE_PLATE_CLICK_OFF("block.stone_pressure_plate.click_off"), + BLOCK_STONE_PRESSURE_PLATE_CLICK_ON("block.stone_pressure_plate.click_on"), + BLOCK_STONE_STEP("block.stone.step"), + BLOCK_TRIPWIRE_ATTACH("block.tripwire.attach"), + BLOCK_TRIPWIRE_CLICK_OFF("block.tripwire.click_off"), + BLOCK_TRIPWIRE_CLICK_ON("block.tripwire.click_on"), + BLOCK_TRIPWIRE_DETACH("block.tripwire.detach"), + BLOCK_WATER_AMBIENT("block.water.ambient"), + BLOCK_WET_GRASS_BREAK("block.wet_grass.break"), + BLOCK_WET_GRASS_FALL("block.wet_grass.fall"), + BLOCK_WET_GRASS_HIT("block.wet_grass.hit"), + BLOCK_WET_GRASS_PLACE("block.wet_grass.place"), + BLOCK_WET_GRASS_STEP("block.wet_grass.step"), + BLOCK_WOODEN_BUTTON_CLICK_OFF("block.wooden_button.click_off"), + BLOCK_WOODEN_BUTTON_CLICK_ON("block.wooden_button.click_on"), + BLOCK_WOODEN_DOOR_CLOSE("block.wooden_door.close"), + BLOCK_WOODEN_DOOR_OPEN("block.wooden_door.open"), + BLOCK_WOODEN_PRESSURE_PLATE_CLICK_OFF("block.wooden_pressure_plate.click_off"), + BLOCK_WOODEN_PRESSURE_PLATE_CLICK_ON("block.wooden_pressure_plate.click_on"), + BLOCK_WOODEN_TRAPDOOR_CLOSE("block.wooden_trapdoor.close"), + BLOCK_WOODEN_TRAPDOOR_OPEN("block.wooden_trapdoor.open"), + BLOCK_WOOD_BREAK("block.wood.break"), + BLOCK_WOOD_FALL("block.wood.fall"), + BLOCK_WOOD_HIT("block.wood.hit"), + BLOCK_WOOD_PLACE("block.wood.place"), + BLOCK_WOOD_STEP("block.wood.step"), + BLOCK_WOOL_BREAK("block.wool.break"), + BLOCK_WOOL_FALL("block.wool.fall"), + BLOCK_WOOL_HIT("block.wool.hit"), + BLOCK_WOOL_PLACE("block.wool.place"), + BLOCK_WOOL_STEP("block.wool.step"), + ENCHANT_THORNS_HIT("enchant.thorns.hit"), + ENTITY_ARMOR_STAND_BREAK("entity.armor_stand.break"), + ENTITY_ARMOR_STAND_FALL("entity.armor_stand.fall"), + ENTITY_ARMOR_STAND_HIT("entity.armor_stand.hit"), + ENTITY_ARMOR_STAND_PLACE("entity.armor_stand.place"), + ENTITY_ARROW_HIT("entity.arrow.hit"), + ENTITY_ARROW_HIT_PLAYER("entity.arrow.hit_player"), + ENTITY_ARROW_SHOOT("entity.arrow.shoot"), + ENTITY_BAT_AMBIENT("entity.bat.ambient"), + ENTITY_BAT_DEATH("entity.bat.death"), + ENTITY_BAT_HURT("entity.bat.hurt"), + ENTITY_BAT_LOOP("entity.bat.loop"), + ENTITY_BAT_TAKEOFF("entity.bat.takeoff"), + ENTITY_BLAZE_AMBIENT("entity.blaze.ambient"), + ENTITY_BLAZE_BURN("entity.blaze.burn"), + ENTITY_BLAZE_DEATH("entity.blaze.death"), + ENTITY_BLAZE_HURT("entity.blaze.hurt"), + ENTITY_BLAZE_SHOOT("entity.blaze.shoot"), + ENTITY_BOAT_PADDLE_LAND("entity.boat.paddle_land"), + ENTITY_BOAT_PADDLE_WATER("entity.boat.paddle_water"), + ENTITY_CAT_AMBIENT("entity.cat.ambient"), + ENTITY_CAT_DEATH("entity.cat.death"), + ENTITY_CAT_HISS("entity.cat.hiss"), + ENTITY_CAT_HURT("entity.cat.hurt"), + ENTITY_CAT_PURR("entity.cat.purr"), + ENTITY_CAT_PURREOW("entity.cat.purreow"), + ENTITY_CHICKEN_AMBIENT("entity.chicken.ambient"), + ENTITY_CHICKEN_DEATH("entity.chicken.death"), + ENTITY_CHICKEN_EGG("entity.chicken.egg"), + ENTITY_CHICKEN_HURT("entity.chicken.hurt"), + ENTITY_CHICKEN_STEP("entity.chicken.step"), + ENTITY_COD_AMBIENT("entity.cod.ambient"), + ENTITY_COD_DEATH("entity.cod.death"), + ENTITY_COD_FLOP("entity.cod.flop"), + ENTITY_COD_HURT("entity.cod.hurt"), + ENTITY_COW_AMBIENT("entity.cow.ambient"), + ENTITY_COW_DEATH("entity.cow.death"), + ENTITY_COW_HURT("entity.cow.hurt"), + ENTITY_COW_MILK("entity.cow.milk"), + ENTITY_COW_STEP("entity.cow.step"), + ENTITY_CREEPER_DEATH("entity.creeper.death"), + ENTITY_CREEPER_HURT("entity.creeper.hurt"), + ENTITY_CREEPER_PRIMED("entity.creeper.primed"), + ENTITY_DOLPHIN_AMBIENT("entity.dolphin.ambient"), + ENTITY_DOLPHIN_AMBIENT_WATER("entity.dolphin.ambient_water"), + ENTITY_DOLPHIN_ATTACK("entity.dolphin.attack"), + ENTITY_DOLPHIN_DEATH("entity.dolphin.death"), + ENTITY_DOLPHIN_EAT("entity.dolphin.eat"), + ENTITY_DOLPHIN_HURT("entity.dolphin.hurt"), + ENTITY_DOLPHIN_JUMP("entity.dolphin.jump"), + ENTITY_DOLPHIN_PLAY("entity.dolphin.play"), + ENTITY_DOLPHIN_SPLASH("entity.dolphin.splash"), + ENTITY_DOLPHIN_SWIM("entity.dolphin.swim"), + ENTITY_DONKEY_AMBIENT("entity.donkey.ambient"), + ENTITY_DONKEY_ANGRY("entity.donkey.angry"), + ENTITY_DONKEY_CHEST("entity.donkey.chest"), + ENTITY_DONKEY_DEATH("entity.donkey.death"), + ENTITY_DONKEY_HURT("entity.donkey.hurt"), + ENTITY_DRAGON_FIREBALL_EXPLODE("entity.dragon_fireball.explode"), + ENTITY_DROWNED_AMBIENT("entity.drowned.ambient"), + ENTITY_DROWNED_AMBIENT_WATER("entity.drowned.ambient_water"), + ENTITY_DROWNED_DEATH("entity.drowned.death"), + ENTITY_DROWNED_DEATH_WATER("entity.drowned.death_water"), + ENTITY_DROWNED_HURT("entity.drowned.hurt"), + ENTITY_DROWNED_HURT_WATER("entity.drowned.hurt_water"), + ENTITY_DROWNED_SHOOT("entity.drowned.shoot"), + ENTITY_DROWNED_STEP("entity.drowned.step"), + ENTITY_DROWNED_SWIM("entity.drowned.swim"), + ENTITY_EGG_THROW("entity.egg.throw"), + ENTITY_ELDER_GUARDIAN_AMBIENT("entity.elder_guardian.ambient"), + ENTITY_ELDER_GUARDIAN_AMBIENT_LAND("entity.elder_guardian.ambient_land"), + ENTITY_ELDER_GUARDIAN_CURSE("entity.elder_guardian.curse"), + ENTITY_ELDER_GUARDIAN_DEATH("entity.elder_guardian.death"), + ENTITY_ELDER_GUARDIAN_DEATH_LAND("entity.elder_guardian.death_land"), + ENTITY_ELDER_GUARDIAN_FLOP("entity.elder_guardian.flop"), + ENTITY_ELDER_GUARDIAN_HURT("entity.elder_guardian.hurt"), + ENTITY_ELDER_GUARDIAN_HURT_LAND("entity.elder_guardian.hurt_land"), + ENTITY_ENDERMAN_AMBIENT("entity.enderman.ambient"), + ENTITY_ENDERMAN_DEATH("entity.enderman.death"), + ENTITY_ENDERMAN_HURT("entity.enderman.hurt"), + ENTITY_ENDERMAN_SCREAM("entity.enderman.scream"), + ENTITY_ENDERMAN_STARE("entity.enderman.stare"), + ENTITY_ENDERMAN_TELEPORT("entity.enderman.teleport"), + ENTITY_ENDERMITE_AMBIENT("entity.endermite.ambient"), + ENTITY_ENDERMITE_DEATH("entity.endermite.death"), + ENTITY_ENDERMITE_HURT("entity.endermite.hurt"), + ENTITY_ENDERMITE_STEP("entity.endermite.step"), + ENTITY_ENDER_DRAGON_AMBIENT("entity.ender_dragon.ambient"), + ENTITY_ENDER_DRAGON_DEATH("entity.ender_dragon.death"), + ENTITY_ENDER_DRAGON_FLAP("entity.ender_dragon.flap"), + ENTITY_ENDER_DRAGON_GROWL("entity.ender_dragon.growl"), + ENTITY_ENDER_DRAGON_HURT("entity.ender_dragon.hurt"), + ENTITY_ENDER_DRAGON_SHOOT("entity.ender_dragon.shoot"), + ENTITY_ENDER_EYE_DEATH("entity.ender_eye.death"), + ENTITY_ENDER_EYE_LAUNCH("entity.ender_eye.launch"), + ENTITY_ENDER_PEARL_THROW("entity.ender_pearl.throw"), + ENTITY_EVOKER_AMBIENT("entity.evoker.ambient"), + ENTITY_EVOKER_CAST_SPELL("entity.evoker.cast_spell"), + ENTITY_EVOKER_DEATH("entity.evoker.death"), + ENTITY_EVOKER_FANGS_ATTACK("entity.evoker_fangs.attack"), + ENTITY_EVOKER_HURT("entity.evoker.hurt"), + ENTITY_EVOKER_PREPARE_ATTACK("entity.evoker.prepare_attack"), + ENTITY_EVOKER_PREPARE_SUMMON("entity.evoker.prepare_summon"), + ENTITY_EVOKER_PREPARE_WOLOLO("entity.evoker.prepare_wololo"), + ENTITY_EXPERIENCE_BOTTLE_THROW("entity.experience_bottle.throw"), + ENTITY_EXPERIENCE_ORB_PICKUP("entity.experience_orb.pickup"), + ENTITY_FIREWORK_ROCKET_BLAST("entity.firework_rocket.blast"), + ENTITY_FIREWORK_ROCKET_BLAST_FAR("entity.firework_rocket.blast_far"), + ENTITY_FIREWORK_ROCKET_LARGE_BLAST("entity.firework_rocket.large_blast"), + ENTITY_FIREWORK_ROCKET_LARGE_BLAST_FAR("entity.firework_rocket.large_blast_far"), + ENTITY_FIREWORK_ROCKET_LAUNCH("entity.firework_rocket.launch"), + ENTITY_FIREWORK_ROCKET_SHOOT("entity.firework_rocket.shoot"), + ENTITY_FIREWORK_ROCKET_TWINKLE("entity.firework_rocket.twinkle"), + ENTITY_FIREWORK_ROCKET_TWINKLE_FAR("entity.firework_rocket.twinkle_far"), + ENTITY_FISHING_BOBBER_RETRIEVE("entity.fishing_bobber.retrieve"), + ENTITY_FISHING_BOBBER_SPLASH("entity.fishing_bobber.splash"), + ENTITY_FISHING_BOBBER_THROW("entity.fishing_bobber.throw"), + ENTITY_FISH_SWIM("entity.fish.swim"), + ENTITY_GENERIC_BIG_FALL("entity.generic.big_fall"), + ENTITY_GENERIC_BURN("entity.generic.burn"), + ENTITY_GENERIC_DEATH("entity.generic.death"), + ENTITY_GENERIC_DRINK("entity.generic.drink"), + ENTITY_GENERIC_EAT("entity.generic.eat"), + ENTITY_GENERIC_EXPLODE("entity.generic.explode"), + ENTITY_GENERIC_EXTINGUISH_FIRE("entity.generic.extinguish_fire"), + ENTITY_GENERIC_HURT("entity.generic.hurt"), + ENTITY_GENERIC_SMALL_FALL("entity.generic.small_fall"), + ENTITY_GENERIC_SPLASH("entity.generic.splash"), + ENTITY_GENERIC_SWIM("entity.generic.swim"), + ENTITY_GHAST_AMBIENT("entity.ghast.ambient"), + ENTITY_GHAST_DEATH("entity.ghast.death"), + ENTITY_GHAST_HURT("entity.ghast.hurt"), + ENTITY_GHAST_SCREAM("entity.ghast.scream"), + ENTITY_GHAST_SHOOT("entity.ghast.shoot"), + ENTITY_GHAST_WARN("entity.ghast.warn"), + ENTITY_GUARDIAN_AMBIENT("entity.guardian.ambient"), + ENTITY_GUARDIAN_AMBIENT_LAND("entity.guardian.ambient_land"), + ENTITY_GUARDIAN_ATTACK("entity.guardian.attack"), + ENTITY_GUARDIAN_DEATH("entity.guardian.death"), + ENTITY_GUARDIAN_DEATH_LAND("entity.guardian.death_land"), + ENTITY_GUARDIAN_FLOP("entity.guardian.flop"), + ENTITY_GUARDIAN_HURT("entity.guardian.hurt"), + ENTITY_GUARDIAN_HURT_LAND("entity.guardian.hurt_land"), + ENTITY_HORSE_AMBIENT("entity.horse.ambient"), + ENTITY_HORSE_ANGRY("entity.horse.angry"), + ENTITY_HORSE_ARMOR("entity.horse.armor"), + ENTITY_HORSE_BREATHE("entity.horse.breathe"), + ENTITY_HORSE_DEATH("entity.horse.death"), + ENTITY_HORSE_EAT("entity.horse.eat"), + ENTITY_HORSE_GALLOP("entity.horse.gallop"), + ENTITY_HORSE_HURT("entity.horse.hurt"), + ENTITY_HORSE_JUMP("entity.horse.jump"), + ENTITY_HORSE_LAND("entity.horse.land"), + ENTITY_HORSE_SADDLE("entity.horse.saddle"), + ENTITY_HORSE_STEP("entity.horse.step"), + ENTITY_HORSE_STEP_WOOD("entity.horse.step_wood"), + ENTITY_HOSTILE_BIG_FALL("entity.hostile.big_fall"), + ENTITY_HOSTILE_DEATH("entity.hostile.death"), + ENTITY_HOSTILE_HURT("entity.hostile.hurt"), + ENTITY_HOSTILE_SMALL_FALL("entity.hostile.small_fall"), + ENTITY_HOSTILE_SPLASH("entity.hostile.splash"), + ENTITY_HOSTILE_SWIM("entity.hostile.swim"), + ENTITY_HUSK_AMBIENT("entity.husk.ambient"), + ENTITY_HUSK_CONVERTED_TO_ZOMBIE("entity.husk.converted_to_zombie"), + ENTITY_HUSK_DEATH("entity.husk.death"), + ENTITY_HUSK_HURT("entity.husk.hurt"), + ENTITY_HUSK_STEP("entity.husk.step"), + ENTITY_ILLUSIONER_AMBIENT("entity.illusioner.ambient"), + ENTITY_ILLUSIONER_CAST_SPELL("entity.illusioner.cast_spell"), + ENTITY_ILLUSIONER_DEATH("entity.illusioner.death"), + ENTITY_ILLUSIONER_HURT("entity.illusioner.hurt"), + ENTITY_ILLUSIONER_MIRROR_MOVE("entity.illusioner.mirror_move"), + ENTITY_ILLUSIONER_PREPARE_BLINDNESS("entity.illusioner.prepare_blindness"), + ENTITY_ILLUSIONER_PREPARE_MIRROR("entity.illusioner.prepare_mirror"), + ENTITY_IRON_GOLEM_ATTACK("entity.iron_golem.attack"), + ENTITY_IRON_GOLEM_DEATH("entity.iron_golem.death"), + ENTITY_IRON_GOLEM_HURT("entity.iron_golem.hurt"), + ENTITY_IRON_GOLEM_STEP("entity.iron_golem.step"), + ENTITY_ITEM_BREAK("entity.item.break"), + ENTITY_ITEM_FRAME_ADD_ITEM("entity.item_frame.add_item"), + ENTITY_ITEM_FRAME_BREAK("entity.item_frame.break"), + ENTITY_ITEM_FRAME_PLACE("entity.item_frame.place"), + ENTITY_ITEM_FRAME_REMOVE_ITEM("entity.item_frame.remove_item"), + ENTITY_ITEM_FRAME_ROTATE_ITEM("entity.item_frame.rotate_item"), + ENTITY_ITEM_PICKUP("entity.item.pickup"), + ENTITY_LEASH_KNOT_BREAK("entity.leash_knot.break"), + ENTITY_LEASH_KNOT_PLACE("entity.leash_knot.place"), + ENTITY_LIGHTNING_BOLT_IMPACT("entity.lightning_bolt.impact"), + ENTITY_LIGHTNING_BOLT_THUNDER("entity.lightning_bolt.thunder"), + ENTITY_LINGERING_POTION_THROW("entity.lingering_potion.throw"), + ENTITY_LLAMA_AMBIENT("entity.llama.ambient"), + ENTITY_LLAMA_ANGRY("entity.llama.angry"), + ENTITY_LLAMA_CHEST("entity.llama.chest"), + ENTITY_LLAMA_DEATH("entity.llama.death"), + ENTITY_LLAMA_EAT("entity.llama.eat"), + ENTITY_LLAMA_HURT("entity.llama.hurt"), + ENTITY_LLAMA_SPIT("entity.llama.spit"), + ENTITY_LLAMA_STEP("entity.llama.step"), + ENTITY_LLAMA_SWAG("entity.llama.swag"), + ENTITY_MAGMA_CUBE_DEATH("entity.magma_cube.death"), + ENTITY_MAGMA_CUBE_DEATH_SMALL("entity.magma_cube.death_small"), + ENTITY_MAGMA_CUBE_HURT("entity.magma_cube.hurt"), + ENTITY_MAGMA_CUBE_HURT_SMALL("entity.magma_cube.hurt_small"), + ENTITY_MAGMA_CUBE_JUMP("entity.magma_cube.jump"), + ENTITY_MAGMA_CUBE_SQUISH("entity.magma_cube.squish"), + ENTITY_MAGMA_CUBE_SQUISH_SMALL("entity.magma_cube.squish_small"), + ENTITY_MINECART_INSIDE("entity.minecart.inside"), + ENTITY_MINECART_RIDING("entity.minecart.riding"), + ENTITY_MOOSHROOM_SHEAR("entity.mooshroom.shear"), + ENTITY_MULE_AMBIENT("entity.mule.ambient"), + ENTITY_MULE_CHEST("entity.mule.chest"), + ENTITY_MULE_DEATH("entity.mule.death"), + ENTITY_MULE_HURT("entity.mule.hurt"), + ENTITY_PAINTING_BREAK("entity.painting.break"), + ENTITY_PAINTING_PLACE("entity.painting.place"), + ENTITY_PARROT_AMBIENT("entity.parrot.ambient"), + ENTITY_PARROT_DEATH("entity.parrot.death"), + ENTITY_PARROT_EAT("entity.parrot.eat"), + ENTITY_PARROT_FLY("entity.parrot.fly"), + ENTITY_PARROT_HURT("entity.parrot.hurt"), + ENTITY_PARROT_IMITATE_BLAZE("entity.parrot.imitate.blaze"), + ENTITY_PARROT_IMITATE_CREEPER("entity.parrot.imitate.creeper"), + ENTITY_PARROT_IMITATE_DROWNED("entity.parrot.imitate.drowned"), + ENTITY_PARROT_IMITATE_ELDER_GUARDIAN("entity.parrot.imitate.elder_guardian"), + ENTITY_PARROT_IMITATE_ENDERMAN("entity.parrot.imitate.enderman"), + ENTITY_PARROT_IMITATE_ENDERMITE("entity.parrot.imitate.endermite"), + ENTITY_PARROT_IMITATE_ENDER_DRAGON("entity.parrot.imitate.ender_dragon"), + ENTITY_PARROT_IMITATE_EVOKER("entity.parrot.imitate.evoker"), + ENTITY_PARROT_IMITATE_GHAST("entity.parrot.imitate.ghast"), + ENTITY_PARROT_IMITATE_HUSK("entity.parrot.imitate.husk"), + ENTITY_PARROT_IMITATE_ILLUSIONER("entity.parrot.imitate.illusioner"), + ENTITY_PARROT_IMITATE_MAGMA_CUBE("entity.parrot.imitate.magma_cube"), + ENTITY_PARROT_IMITATE_PHANTOM("entity.parrot.imitate.phantom"), + ENTITY_PARROT_IMITATE_POLAR_BEAR("entity.parrot.imitate.polar_bear"), + ENTITY_PARROT_IMITATE_SHULKER("entity.parrot.imitate.shulker"), + ENTITY_PARROT_IMITATE_SILVERFISH("entity.parrot.imitate.silverfish"), + ENTITY_PARROT_IMITATE_SKELETON("entity.parrot.imitate.skeleton"), + ENTITY_PARROT_IMITATE_SLIME("entity.parrot.imitate.slime"), + ENTITY_PARROT_IMITATE_SPIDER("entity.parrot.imitate.spider"), + ENTITY_PARROT_IMITATE_STRAY("entity.parrot.imitate.stray"), + ENTITY_PARROT_IMITATE_VEX("entity.parrot.imitate.vex"), + ENTITY_PARROT_IMITATE_VINDICATOR("entity.parrot.imitate.vindicator"), + ENTITY_PARROT_IMITATE_WITCH("entity.parrot.imitate.witch"), + ENTITY_PARROT_IMITATE_WITHER("entity.parrot.imitate.wither"), + ENTITY_PARROT_IMITATE_WITHER_SKELETON("entity.parrot.imitate.wither_skeleton"), + ENTITY_PARROT_IMITATE_WOLF("entity.parrot.imitate.wolf"), + ENTITY_PARROT_IMITATE_ZOMBIE("entity.parrot.imitate.zombie"), + ENTITY_PARROT_IMITATE_ZOMBIE_PIGMAN("entity.parrot.imitate.zombie_pigman"), + ENTITY_PARROT_IMITATE_ZOMBIE_VILLAGER("entity.parrot.imitate.zombie_villager"), + ENTITY_PARROT_STEP("entity.parrot.step"), + ENTITY_PHANTOM_AMBIENT("entity.phantom.ambient"), + ENTITY_PHANTOM_BITE("entity.phantom.bite"), + ENTITY_PHANTOM_DEATH("entity.phantom.death"), + ENTITY_PHANTOM_FLAP("entity.phantom.flap"), + ENTITY_PHANTOM_HURT("entity.phantom.hurt"), + ENTITY_PHANTOM_SWOOP("entity.phantom.swoop"), + ENTITY_PIG_AMBIENT("entity.pig.ambient"), + ENTITY_PIG_DEATH("entity.pig.death"), + ENTITY_PIG_HURT("entity.pig.hurt"), + ENTITY_PIG_SADDLE("entity.pig.saddle"), + ENTITY_PIG_STEP("entity.pig.step"), + ENTITY_PLAYER_ATTACK_CRIT("entity.player.attack.crit"), + ENTITY_PLAYER_ATTACK_KNOCKBACK("entity.player.attack.knockback"), + ENTITY_PLAYER_ATTACK_NODAMAGE("entity.player.attack.nodamage"), + ENTITY_PLAYER_ATTACK_STRONG("entity.player.attack.strong"), + ENTITY_PLAYER_ATTACK_SWEEP("entity.player.attack.sweep"), + ENTITY_PLAYER_ATTACK_WEAK("entity.player.attack.weak"), + ENTITY_PLAYER_BIG_FALL("entity.player.big_fall"), + ENTITY_PLAYER_BREATH("entity.player.breath"), + ENTITY_PLAYER_BURP("entity.player.burp"), + ENTITY_PLAYER_DEATH("entity.player.death"), + ENTITY_PLAYER_HURT("entity.player.hurt"), + ENTITY_PLAYER_HURT_DROWN("entity.player.hurt_drown"), + ENTITY_PLAYER_HURT_ON_FIRE("entity.player.hurt_on_fire"), + ENTITY_PLAYER_LEVELUP("entity.player.levelup"), + ENTITY_PLAYER_SMALL_FALL("entity.player.small_fall"), + ENTITY_PLAYER_SPLASH("entity.player.splash"), + ENTITY_PLAYER_SPLASH_HIGH_SPEED("entity.player.splash.high_speed"), + ENTITY_PLAYER_SWIM("entity.player.swim"), + ENTITY_POLAR_BEAR_AMBIENT("entity.polar_bear.ambient"), + ENTITY_POLAR_BEAR_AMBIENT_BABY("entity.polar_bear.ambient_baby"), + ENTITY_POLAR_BEAR_DEATH("entity.polar_bear.death"), + ENTITY_POLAR_BEAR_HURT("entity.polar_bear.hurt"), + ENTITY_POLAR_BEAR_STEP("entity.polar_bear.step"), + ENTITY_POLAR_BEAR_WARNING("entity.polar_bear.warning"), + ENTITY_PUFFER_FISH_AMBIENT("entity.puffer_fish.ambient"), + ENTITY_PUFFER_FISH_BLOW_OUT("entity.puffer_fish.blow_out"), + ENTITY_PUFFER_FISH_BLOW_UP("entity.puffer_fish.blow_up"), + ENTITY_PUFFER_FISH_DEATH("entity.puffer_fish.death"), + ENTITY_PUFFER_FISH_FLOP("entity.puffer_fish.flop"), + ENTITY_PUFFER_FISH_HURT("entity.puffer_fish.hurt"), + ENTITY_PUFFER_FISH_STING("entity.puffer_fish.sting"), + ENTITY_RABBIT_AMBIENT("entity.rabbit.ambient"), + ENTITY_RABBIT_ATTACK("entity.rabbit.attack"), + ENTITY_RABBIT_DEATH("entity.rabbit.death"), + ENTITY_RABBIT_HURT("entity.rabbit.hurt"), + ENTITY_RABBIT_JUMP("entity.rabbit.jump"), + ENTITY_SALMON_AMBIENT("entity.salmon.ambient"), + ENTITY_SALMON_DEATH("entity.salmon.death"), + ENTITY_SALMON_FLOP("entity.salmon.flop"), + ENTITY_SALMON_HURT("entity.salmon.hurt"), + ENTITY_SHEEP_AMBIENT("entity.sheep.ambient"), + ENTITY_SHEEP_DEATH("entity.sheep.death"), + ENTITY_SHEEP_HURT("entity.sheep.hurt"), + ENTITY_SHEEP_SHEAR("entity.sheep.shear"), + ENTITY_SHEEP_STEP("entity.sheep.step"), + ENTITY_SHULKER_AMBIENT("entity.shulker.ambient"), + ENTITY_SHULKER_BULLET_HIT("entity.shulker_bullet.hit"), + ENTITY_SHULKER_BULLET_HURT("entity.shulker_bullet.hurt"), + ENTITY_SHULKER_CLOSE("entity.shulker.close"), + ENTITY_SHULKER_DEATH("entity.shulker.death"), + ENTITY_SHULKER_HURT("entity.shulker.hurt"), + ENTITY_SHULKER_HURT_CLOSED("entity.shulker.hurt_closed"), + ENTITY_SHULKER_OPEN("entity.shulker.open"), + ENTITY_SHULKER_SHOOT("entity.shulker.shoot"), + ENTITY_SHULKER_TELEPORT("entity.shulker.teleport"), + ENTITY_SILVERFISH_AMBIENT("entity.silverfish.ambient"), + ENTITY_SILVERFISH_DEATH("entity.silverfish.death"), + ENTITY_SILVERFISH_HURT("entity.silverfish.hurt"), + ENTITY_SILVERFISH_STEP("entity.silverfish.step"), + ENTITY_SKELETON_AMBIENT("entity.skeleton.ambient"), + ENTITY_SKELETON_DEATH("entity.skeleton.death"), + ENTITY_SKELETON_HORSE_AMBIENT("entity.skeleton_horse.ambient"), + ENTITY_SKELETON_HORSE_AMBIENT_WATER("entity.skeleton_horse.ambient_water"), + ENTITY_SKELETON_HORSE_DEATH("entity.skeleton_horse.death"), + ENTITY_SKELETON_HORSE_GALLOP_WATER("entity.skeleton_horse.gallop_water"), + ENTITY_SKELETON_HORSE_HURT("entity.skeleton_horse.hurt"), + ENTITY_SKELETON_HORSE_JUMP_WATER("entity.skeleton_horse.jump_water"), + ENTITY_SKELETON_HORSE_STEP_WATER("entity.skeleton_horse.step_water"), + ENTITY_SKELETON_HORSE_SWIM("entity.skeleton_horse.swim"), + ENTITY_SKELETON_HURT("entity.skeleton.hurt"), + ENTITY_SKELETON_SHOOT("entity.skeleton.shoot"), + ENTITY_SKELETON_STEP("entity.skeleton.step"), + ENTITY_SLIME_ATTACK("entity.slime.attack"), + ENTITY_SLIME_DEATH("entity.slime.death"), + ENTITY_SLIME_DEATH_SMALL("entity.slime.death_small"), + ENTITY_SLIME_HURT("entity.slime.hurt"), + ENTITY_SLIME_HURT_SMALL("entity.slime.hurt_small"), + ENTITY_SLIME_JUMP("entity.slime.jump"), + ENTITY_SLIME_JUMP_SMALL("entity.slime.jump_small"), + ENTITY_SLIME_SQUISH("entity.slime.squish"), + ENTITY_SLIME_SQUISH_SMALL("entity.slime.squish_small"), + ENTITY_SNOWBALL_THROW("entity.snowball.throw"), + ENTITY_SNOW_GOLEM_AMBIENT("entity.snow_golem.ambient"), + ENTITY_SNOW_GOLEM_DEATH("entity.snow_golem.death"), + ENTITY_SNOW_GOLEM_HURT("entity.snow_golem.hurt"), + ENTITY_SNOW_GOLEM_SHOOT("entity.snow_golem.shoot"), + ENTITY_SPIDER_AMBIENT("entity.spider.ambient"), + ENTITY_SPIDER_DEATH("entity.spider.death"), + ENTITY_SPIDER_HURT("entity.spider.hurt"), + ENTITY_SPIDER_STEP("entity.spider.step"), + ENTITY_SPLASH_POTION_BREAK("entity.splash_potion.break"), + ENTITY_SPLASH_POTION_THROW("entity.splash_potion.throw"), + ENTITY_SQUID_AMBIENT("entity.squid.ambient"), + ENTITY_SQUID_DEATH("entity.squid.death"), + ENTITY_SQUID_HURT("entity.squid.hurt"), + ENTITY_SQUID_SQUIRT("entity.squid.squirt"), + ENTITY_STRAY_AMBIENT("entity.stray.ambient"), + ENTITY_STRAY_DEATH("entity.stray.death"), + ENTITY_STRAY_HURT("entity.stray.hurt"), + ENTITY_STRAY_STEP("entity.stray.step"), + ENTITY_TNT_PRIMED("entity.tnt.primed"), + ENTITY_TROPICAL_FISH_AMBIENT("entity.tropical_fish.ambient"), + ENTITY_TROPICAL_FISH_DEATH("entity.tropical_fish.death"), + ENTITY_TROPICAL_FISH_FLOP("entity.tropical_fish.flop"), + ENTITY_TROPICAL_FISH_HURT("entity.tropical_fish.hurt"), + ENTITY_TURTLE_AMBIENT_LAND("entity.turtle.ambient_land"), + ENTITY_TURTLE_DEATH("entity.turtle.death"), + ENTITY_TURTLE_DEATH_BABY("entity.turtle.death_baby"), + ENTITY_TURTLE_EGG_BREAK("entity.turtle.egg_break"), + ENTITY_TURTLE_EGG_CRACK("entity.turtle.egg_crack"), + ENTITY_TURTLE_EGG_HATCH("entity.turtle.egg_hatch"), + ENTITY_TURTLE_HURT("entity.turtle.hurt"), + ENTITY_TURTLE_HURT_BABY("entity.turtle.hurt_baby"), + ENTITY_TURTLE_LAY_EGG("entity.turtle.lay_egg"), + ENTITY_TURTLE_SHAMBLE("entity.turtle.shamble"), + ENTITY_TURTLE_SHAMBLE_BABY("entity.turtle.shamble_baby"), + ENTITY_TURTLE_SWIM("entity.turtle.swim"), + ENTITY_VEX_AMBIENT("entity.vex.ambient"), + ENTITY_VEX_CHARGE("entity.vex.charge"), + ENTITY_VEX_DEATH("entity.vex.death"), + ENTITY_VEX_HURT("entity.vex.hurt"), + ENTITY_VILLAGER_AMBIENT("entity.villager.ambient"), + ENTITY_VILLAGER_DEATH("entity.villager.death"), + ENTITY_VILLAGER_HURT("entity.villager.hurt"), + ENTITY_VILLAGER_NO("entity.villager.no"), + ENTITY_VILLAGER_TRADE("entity.villager.trade"), + ENTITY_VILLAGER_YES("entity.villager.yes"), + ENTITY_VINDICATOR_AMBIENT("entity.vindicator.ambient"), + ENTITY_VINDICATOR_DEATH("entity.vindicator.death"), + ENTITY_VINDICATOR_HURT("entity.vindicator.hurt"), + ENTITY_WITCH_AMBIENT("entity.witch.ambient"), + ENTITY_WITCH_DEATH("entity.witch.death"), + ENTITY_WITCH_DRINK("entity.witch.drink"), + ENTITY_WITCH_HURT("entity.witch.hurt"), + ENTITY_WITCH_THROW("entity.witch.throw"), + ENTITY_WITHER_AMBIENT("entity.wither.ambient"), + ENTITY_WITHER_BREAK_BLOCK("entity.wither.break_block"), + ENTITY_WITHER_DEATH("entity.wither.death"), + ENTITY_WITHER_HURT("entity.wither.hurt"), + ENTITY_WITHER_SHOOT("entity.wither.shoot"), + ENTITY_WITHER_SKELETON_AMBIENT("entity.wither_skeleton.ambient"), + ENTITY_WITHER_SKELETON_DEATH("entity.wither_skeleton.death"), + ENTITY_WITHER_SKELETON_HURT("entity.wither_skeleton.hurt"), + ENTITY_WITHER_SKELETON_STEP("entity.wither_skeleton.step"), + ENTITY_WITHER_SPAWN("entity.wither.spawn"), + ENTITY_WOLF_AMBIENT("entity.wolf.ambient"), + ENTITY_WOLF_DEATH("entity.wolf.death"), + ENTITY_WOLF_GROWL("entity.wolf.growl"), + ENTITY_WOLF_HOWL("entity.wolf.howl"), + ENTITY_WOLF_HURT("entity.wolf.hurt"), + ENTITY_WOLF_PANT("entity.wolf.pant"), + ENTITY_WOLF_SHAKE("entity.wolf.shake"), + ENTITY_WOLF_STEP("entity.wolf.step"), + ENTITY_WOLF_WHINE("entity.wolf.whine"), + ENTITY_ZOMBIE_AMBIENT("entity.zombie.ambient"), + ENTITY_ZOMBIE_ATTACK_IRON_DOOR("entity.zombie.attack_iron_door"), + ENTITY_ZOMBIE_ATTACK_WOODEN_DOOR("entity.zombie.attack_wooden_door"), + ENTITY_ZOMBIE_BREAK_WOODEN_DOOR("entity.zombie.break_wooden_door"), + ENTITY_ZOMBIE_CONVERTED_TO_DROWNED("entity.zombie.converted_to_drowned"), + ENTITY_ZOMBIE_DEATH("entity.zombie.death"), + ENTITY_ZOMBIE_DESTROY_EGG("entity.zombie.destroy_egg"), + ENTITY_ZOMBIE_HORSE_AMBIENT("entity.zombie_horse.ambient"), + ENTITY_ZOMBIE_HORSE_DEATH("entity.zombie_horse.death"), + ENTITY_ZOMBIE_HORSE_HURT("entity.zombie_horse.hurt"), + ENTITY_ZOMBIE_HURT("entity.zombie.hurt"), + ENTITY_ZOMBIE_INFECT("entity.zombie.infect"), + ENTITY_ZOMBIE_PIGMAN_AMBIENT("entity.zombie_pigman.ambient"), + ENTITY_ZOMBIE_PIGMAN_ANGRY("entity.zombie_pigman.angry"), + ENTITY_ZOMBIE_PIGMAN_DEATH("entity.zombie_pigman.death"), + ENTITY_ZOMBIE_PIGMAN_HURT("entity.zombie_pigman.hurt"), + ENTITY_ZOMBIE_STEP("entity.zombie.step"), + ENTITY_ZOMBIE_VILLAGER_AMBIENT("entity.zombie_villager.ambient"), + ENTITY_ZOMBIE_VILLAGER_CONVERTED("entity.zombie_villager.converted"), + ENTITY_ZOMBIE_VILLAGER_CURE("entity.zombie_villager.cure"), + ENTITY_ZOMBIE_VILLAGER_DEATH("entity.zombie_villager.death"), + ENTITY_ZOMBIE_VILLAGER_HURT("entity.zombie_villager.hurt"), + ENTITY_ZOMBIE_VILLAGER_STEP("entity.zombie_villager.step"), + ITEM_ARMOR_EQUIP_CHAIN("item.armor.equip_chain"), + ITEM_ARMOR_EQUIP_DIAMOND("item.armor.equip_diamond"), + ITEM_ARMOR_EQUIP_ELYTRA("item.armor.equip_elytra"), + ITEM_ARMOR_EQUIP_GENERIC("item.armor.equip_generic"), + ITEM_ARMOR_EQUIP_GOLD("item.armor.equip_gold"), + ITEM_ARMOR_EQUIP_IRON("item.armor.equip_iron"), + ITEM_ARMOR_EQUIP_LEATHER("item.armor.equip_leather"), + ITEM_ARMOR_EQUIP_TURTLE("item.armor.equip_turtle"), + ITEM_AXE_STRIP("item.axe.strip"), + ITEM_BOTTLE_EMPTY("item.bottle.empty"), + ITEM_BOTTLE_FILL("item.bottle.fill"), + ITEM_BOTTLE_FILL_DRAGONBREATH("item.bottle.fill_dragonbreath"), + ITEM_BUCKET_EMPTY("item.bucket.empty"), + ITEM_BUCKET_EMPTY_FISH("item.bucket.empty_fish"), + ITEM_BUCKET_EMPTY_LAVA("item.bucket.empty_lava"), + ITEM_BUCKET_FILL("item.bucket.fill"), + ITEM_BUCKET_FILL_FISH("item.bucket.fill_fish"), + ITEM_BUCKET_FILL_LAVA("item.bucket.fill_lava"), + ITEM_CHORUS_FRUIT_TELEPORT("item.chorus_fruit.teleport"), + ITEM_ELYTRA_FLYING("item.elytra.flying"), + ITEM_FIRECHARGE_USE("item.firecharge.use"), + ITEM_FLINTANDSTEEL_USE("item.flintandsteel.use"), + ITEM_HOE_TILL("item.hoe.till"), + ITEM_SHIELD_BLOCK("item.shield.block"), + ITEM_SHIELD_BREAK("item.shield.break"), + ITEM_SHOVEL_FLATTEN("item.shovel.flatten"), + ITEM_TOTEM_USE("item.totem.use"), + ITEM_TRIDENT_HIT("item.trident.hit"), + ITEM_TRIDENT_HIT_GROUND("item.trident.hit_ground"), + ITEM_TRIDENT_RETURN("item.trident.return"), + ITEM_TRIDENT_RIPTIDE_1("item.trident.riptide_1"), + ITEM_TRIDENT_RIPTIDE_2("item.trident.riptide_2"), + ITEM_TRIDENT_RIPTIDE_3("item.trident.riptide_3"), + ITEM_TRIDENT_THROW("item.trident.throw"), + ITEM_TRIDENT_THUNDER("item.trident.thunder"), + MUSIC_CREATIVE("music.creative"), + MUSIC_CREDITS("music.credits"), + MUSIC_DISC_11("music_disc.11"), + MUSIC_DISC_13("music_disc.13"), + MUSIC_DISC_BLOCKS("music_disc.blocks"), + MUSIC_DISC_CAT("music_disc.cat"), + MUSIC_DISC_CHIRP("music_disc.chirp"), + MUSIC_DISC_FAR("music_disc.far"), + MUSIC_DISC_MALL("music_disc.mall"), + MUSIC_DISC_MELLOHI("music_disc.mellohi"), + MUSIC_DISC_STAL("music_disc.stal"), + MUSIC_DISC_STRAD("music_disc.strad"), + MUSIC_DISC_WAIT("music_disc.wait"), + MUSIC_DISC_WARD("music_disc.ward"), + MUSIC_DRAGON("music.dragon"), + MUSIC_END("music.end"), + MUSIC_GAME("music.game"), + MUSIC_MENU("music.menu"), + MUSIC_NETHER("music.nether"), + MUSIC_UNDER_WATER("music.under_water"), + UI_BUTTON_CLICK("ui.button.click"), + UI_TOAST_CHALLENGE_COMPLETE("ui.toast.challenge_complete"), + UI_TOAST_IN("ui.toast.in"), + UI_TOAST_OUT("ui.toast.out"), + WEATHER_RAIN("weather.rain"), + WEATHER_RAIN_ABOVE("weather.rain.above"); + private final String minecraftKey; + + // Paper start - cancellable death event + public static CraftSound getBySoundEffect(final SoundEffect effect) { + MinecraftKey key = IRegistry.SOUND_EVENT.getKey(effect); + Preconditions.checkArgument(key != null, "Key for sound effect %s not found?", effect.toString()); + + return valueOf(key.getKey().replace('.', '_').toUpperCase(java.util.Locale.ENGLISH)); + } + + public static Sound getSoundByEffect(final SoundEffect effect) { + return Sound.valueOf(getBySoundEffect(effect).name()); + } + + public static SoundEffect getSoundEffect(final Sound sound) { + return getSoundEffect(getSound(sound)); + } + // Paper end + CraftSound(String minecraftKey) { + this.minecraftKey = minecraftKey; + } + + public static String getSound(final Sound sound) { + Validate.notNull(sound, "Sound cannot be null"); + + return CraftSound.valueOf(sound.name()).minecraftKey; + } + + public static SoundEffect getSoundEffect(String s) { + SoundEffect effect = IRegistry.SOUND_EVENT.get(new MinecraftKey(s)); + Preconditions.checkArgument(effect != null, "Sound effect %s does not exist", s); + + return effect; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java b/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java new file mode 100644 index 000000000000..31252df5be5d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java @@ -0,0 +1,180 @@ +package org.bukkit.craftbukkit; + +import net.minecraft.server.StatisticList; + +import org.bukkit.Statistic; +import org.bukkit.Material; +import org.bukkit.entity.EntityType; + +import com.google.common.base.Preconditions; +import com.google.common.collect.BiMap; +import com.google.common.collect.ImmutableBiMap; +import net.minecraft.server.Block; +import net.minecraft.server.EntityTypes; +import net.minecraft.server.IRegistry; +import net.minecraft.server.Item; +import net.minecraft.server.MinecraftKey; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; + +public enum CraftStatistic { + DAMAGE_DEALT(StatisticList.DAMAGE_DEALT), + DAMAGE_TAKEN(StatisticList.DAMAGE_TAKEN), + DEATHS(StatisticList.DEATHS), + MOB_KILLS(StatisticList.MOB_KILLS), + PLAYER_KILLS(StatisticList.PLAYER_KILLS), + FISH_CAUGHT(StatisticList.FISH_CAUGHT), + ANIMALS_BRED(StatisticList.ANIMALS_BRED), + LEAVE_GAME(StatisticList.LEAVE_GAME), + JUMP(StatisticList.JUMP), + DROP_COUNT(StatisticList.DROP), + DROP(new MinecraftKey("dropped")), + PICKUP(new MinecraftKey("picked_up")), + PLAY_ONE_MINUTE(StatisticList.PLAY_ONE_MINUTE), + WALK_ONE_CM(StatisticList.WALK_ONE_CM), + WALK_ON_WATER_ONE_CM(StatisticList.WALK_ON_WATER_ONE_CM), + FALL_ONE_CM(StatisticList.FALL_ONE_CM), + SNEAK_TIME(StatisticList.SNEAK_TIME), + CLIMB_ONE_CM(StatisticList.CLIMB_ONE_CM), + FLY_ONE_CM(StatisticList.FLY_ONE_CM), + WALK_UNDER_WATER_ONE_CM(StatisticList.WALK_UNDER_WATER_ONE_CM), + MINECART_ONE_CM(StatisticList.MINECART_ONE_CM), + BOAT_ONE_CM(StatisticList.BOAT_ONE_CM), + PIG_ONE_CM(StatisticList.PIG_ONE_CM), + HORSE_ONE_CM(StatisticList.HORSE_ONE_CM), + SPRINT_ONE_CM(StatisticList.SPRINT_ONE_CM), + CROUCH_ONE_CM(StatisticList.CROUCH_ONE_CM), + AVIATE_ONE_CM(StatisticList.AVIATE_ONE_CM), + MINE_BLOCK(new MinecraftKey("mined")), + USE_ITEM(new MinecraftKey("used")), + BREAK_ITEM(new MinecraftKey("broken")), + CRAFT_ITEM(new MinecraftKey("crafted")), + KILL_ENTITY(new MinecraftKey("killed")), + ENTITY_KILLED_BY(new MinecraftKey("killed_by")), + TIME_SINCE_DEATH(StatisticList.TIME_SINCE_DEATH), + TALKED_TO_VILLAGER(StatisticList.TALKED_TO_VILLAGER), + TRADED_WITH_VILLAGER(StatisticList.TRADED_WITH_VILLAGER), + CAKE_SLICES_EATEN(StatisticList.EAT_CAKE_SLICE), + CAULDRON_FILLED(StatisticList.FILL_CAULDRON), + CAULDRON_USED(StatisticList.USE_CAULDRON), + ARMOR_CLEANED(StatisticList.CLEAN_ARMOR), + BANNER_CLEANED(StatisticList.CLEAN_BANNER), + BREWINGSTAND_INTERACTION(StatisticList.INTERACT_WITH_BREWINGSTAND), + BEACON_INTERACTION(StatisticList.INTERACT_WITH_BEACON), + DROPPER_INSPECTED(StatisticList.INSPECT_DROPPER), + HOPPER_INSPECTED(StatisticList.INSPECT_HOPPER), + DISPENSER_INSPECTED(StatisticList.INSPECT_DISPENSER), + NOTEBLOCK_PLAYED(StatisticList.PLAY_NOTEBLOCK), + NOTEBLOCK_TUNED(StatisticList.TUNE_NOTEBLOCK), + FLOWER_POTTED(StatisticList.POT_FLOWER), + TRAPPED_CHEST_TRIGGERED(StatisticList.TRIGGER_TRAPPED_CHEST), + ENDERCHEST_OPENED(StatisticList.OPEN_ENDERCHEST), + ITEM_ENCHANTED(StatisticList.ENCHANT_ITEM), + RECORD_PLAYED(StatisticList.PLAY_RECORD), + FURNACE_INTERACTION(StatisticList.INTERACT_WITH_FURNACE), + CRAFTING_TABLE_INTERACTION(StatisticList.INTERACT_WITH_CRAFTING_TABLE), + CHEST_OPENED(StatisticList.OPEN_CHEST), + SLEEP_IN_BED(StatisticList.SLEEP_IN_BED), + SHULKER_BOX_OPENED(StatisticList.OPEN_SHULKER_BOX), + TIME_SINCE_REST(StatisticList.TIME_SINCE_REST), + SWIM_ONE_CM(StatisticList.SWIM_ONE_CM), + DAMAGE_DEALT_ABSORBED(StatisticList.DAMAGE_DEALT_ABSORBED), + DAMAGE_DEALT_RESISTED(StatisticList.DAMAGE_DEALT_RESISTED), + DAMAGE_BLOCKED_BY_SHIELD(StatisticList.DAMAGE_BLOCKED_BY_SHIELD), + DAMAGE_ABSORBED(StatisticList.DAMAGE_ABSORBED), + DAMAGE_RESISTED(StatisticList.DAMAGE_RESISTED), + CLEAN_SHULKER_BOX(StatisticList.CLEAN_SHULKER_BOX); + private final MinecraftKey minecraftKey; + private final org.bukkit.Statistic bukkit; + private static final BiMap statistics; + + static { + ImmutableBiMap.Builder statisticBuilder = ImmutableBiMap.builder(); + for (CraftStatistic statistic : CraftStatistic.values()) { + statisticBuilder.put(statistic.minecraftKey, statistic.bukkit); + } + + statistics = statisticBuilder.build(); + } + + private CraftStatistic(MinecraftKey minecraftKey) { + this.minecraftKey = minecraftKey; + + this.bukkit = org.bukkit.Statistic.valueOf(this.name()); + Preconditions.checkState(bukkit != null, "Bukkit statistic %s does not exist", this.name()); + } + + public static org.bukkit.Statistic getBukkitStatistic(net.minecraft.server.Statistic statistic) { + IRegistry statRegistry = statistic.a().a(); + MinecraftKey nmsKey = IRegistry.STATS.getKey(statistic.a()); + + if (statRegistry == IRegistry.CUSTOM_STAT) { + nmsKey = (MinecraftKey) statistic.b(); + } + + return statistics.get(nmsKey); + } + + public static net.minecraft.server.Statistic getNMSStatistic(org.bukkit.Statistic bukkit) { + Preconditions.checkArgument(bukkit.getType() == Statistic.Type.UNTYPED, "This method only accepts untyped statistics"); + + net.minecraft.server.Statistic nms = StatisticList.CUSTOM.b(statistics.inverse().get(bukkit)); + Preconditions.checkArgument(nms != null, "NMS Statistic %s does not exist", bukkit); + + return nms; + } + + public static net.minecraft.server.Statistic getMaterialStatistic(org.bukkit.Statistic stat, Material material) { + try { + if (stat == Statistic.MINE_BLOCK) { + return StatisticList.BLOCK_MINED.b(CraftMagicNumbers.getBlock(material)); + } + if (stat == Statistic.CRAFT_ITEM) { + return StatisticList.ITEM_CRAFTED.b(CraftMagicNumbers.getItem(material)); + } + if (stat == Statistic.USE_ITEM) { + return StatisticList.ITEM_USED.b(CraftMagicNumbers.getItem(material)); + } + if (stat == Statistic.BREAK_ITEM) { + return StatisticList.ITEM_BROKEN.b(CraftMagicNumbers.getItem(material)); + } + if (stat == Statistic.PICKUP) { + return StatisticList.ITEM_PICKED_UP.b(CraftMagicNumbers.getItem(material)); + } + if (stat == Statistic.DROP) { + return StatisticList.ITEM_DROPPED.b(CraftMagicNumbers.getItem(material)); + } + } catch (ArrayIndexOutOfBoundsException e) { + return null; + } + return null; + } + + public static net.minecraft.server.Statistic getEntityStatistic(org.bukkit.Statistic stat, EntityType entity) { + if (entity.getName() != null) { + EntityTypes nmsEntity = IRegistry.ENTITY_TYPE.get(new MinecraftKey(entity.getName())); + + if (stat == org.bukkit.Statistic.KILL_ENTITY) { + return net.minecraft.server.StatisticList.ENTITY_KILLED.b(nmsEntity); + } + if (stat == org.bukkit.Statistic.ENTITY_KILLED_BY) { + return net.minecraft.server.StatisticList.ENTITY_KILLED_BY.b(nmsEntity); + } + } + return null; + } + + public static EntityType getEntityTypeFromStatistic(net.minecraft.server.Statistic> statistic) { + MinecraftKey name = EntityTypes.getName(statistic.b()); + return EntityType.fromName(name.getKey()); + } + + public static Material getMaterialFromStatistic(net.minecraft.server.Statistic statistic) { + if (statistic.b() instanceof Item) { + return CraftMagicNumbers.getMaterial((Item) statistic.b()); + } + if (statistic.b() instanceof Block) { + return CraftMagicNumbers.getMaterial((Block) statistic.b()); + } + return null; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftTravelAgent.java b/src/main/java/org/bukkit/craftbukkit/CraftTravelAgent.java new file mode 100644 index 000000000000..e1eb3aa0f36a --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftTravelAgent.java @@ -0,0 +1,86 @@ +package org.bukkit.craftbukkit; + +import net.minecraft.server.BlockPosition; +import net.minecraft.server.DimensionManager; +import net.minecraft.server.PortalTravelAgent; +import net.minecraft.server.WorldServer; + +import org.bukkit.Location; +import org.bukkit.TravelAgent; + +public class CraftTravelAgent extends PortalTravelAgent implements TravelAgent { + + public static TravelAgent DEFAULT = null; + + private int searchRadius = world.paperConfig.portalSearchRadius; // Paper - Configurable search radius + private int creationRadius = 16; + private boolean canCreatePortal = true; + + public CraftTravelAgent(WorldServer worldserver) { + super(worldserver); + if (DEFAULT == null && worldserver.dimension == DimensionManager.OVERWORLD) { + DEFAULT = this; + } + } + + @Override + public Location findOrCreate(Location target) { + WorldServer worldServer = ((CraftWorld) target.getWorld()).getHandle(); + + Location found = this.findPortal(target); + if (found == null) { + if (this.getCanCreatePortal() && this.createPortal(target)) { + found = this.findPortal(target); + } else { + found = target; // fallback to original if unable to find or create + } + } + + return found; + } + + @Override + public Location findPortal(Location location) { + PortalTravelAgent pta = ((CraftWorld) location.getWorld()).getHandle().getTravelAgent(); + BlockPosition found = pta.findPortal(location.getX(), location.getY(), location.getZ(), this.getSearchRadius()); + return found != null ? new Location(location.getWorld(), found.getX(), found.getY(), found.getZ(), location.getYaw(), location.getPitch()) : null; + } + + @Override + public boolean createPortal(Location location) { + PortalTravelAgent pta = ((CraftWorld) location.getWorld()).getHandle().getTravelAgent(); + return pta.createPortal(location.getX(), location.getY(), location.getZ(), this.getCreationRadius()); + } + + @Override + public TravelAgent setSearchRadius(int radius) { + this.searchRadius = radius; + return this; + } + + @Override + public int getSearchRadius() { + return this.searchRadius; + } + + @Override + public TravelAgent setCreationRadius(int radius) { + this.creationRadius = radius < 2 ? 0 : radius; + return this; + } + + @Override + public int getCreationRadius() { + return this.creationRadius; + } + + @Override + public boolean getCanCreatePortal() { + return this.canCreatePortal; + } + + @Override + public void setCanCreatePortal(boolean create) { + this.canCreatePortal = create; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java new file mode 100644 index 000000000000..6ed7c9355022 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -0,0 +1,1885 @@ +package org.bukkit.craftbukkit; + +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.Futures; +import it.unimi.dsi.fastutil.longs.LongSet; +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Iterator; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import java.util.function.Predicate; + +import net.minecraft.server.*; + +import org.apache.commons.lang.Validate; +import org.bukkit.BlockChangeDelegate; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.ChunkSnapshot; +import org.bukkit.Difficulty; +import org.bukkit.Effect; +import org.bukkit.FluidCollisionMode; +import org.bukkit.GameRule; +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.StructureType; +import org.bukkit.TreeType; +import org.bukkit.World; +import org.bukkit.WorldBorder; +import org.bukkit.block.Biome; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.block.CraftBlockState; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.entity.*; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.metadata.BlockMetadataStore; +import org.bukkit.craftbukkit.potion.CraftPotionUtil; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.craftbukkit.util.CraftRayTraceResult; +import org.bukkit.entity.*; +import org.bukkit.entity.Entity; +import org.bukkit.entity.minecart.CommandMinecart; +import org.bukkit.entity.minecart.ExplosiveMinecart; +import org.bukkit.entity.minecart.HopperMinecart; +import org.bukkit.entity.minecart.PoweredMinecart; +import org.bukkit.entity.minecart.SpawnerMinecart; +import org.bukkit.entity.minecart.StorageMinecart; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.event.world.SpawnChangeEvent; +import org.bukkit.generator.BlockPopulator; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.inventory.ItemStack; +import org.bukkit.material.MaterialData; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.messaging.StandardMessenger; +import org.bukkit.potion.PotionData; +import org.bukkit.potion.PotionType; +import org.bukkit.util.BoundingBox; +import org.bukkit.util.Consumer; +import org.bukkit.util.RayTraceResult; +import org.bukkit.util.Vector; + +public class CraftWorld implements World { + public static final int CUSTOM_DIMENSION_OFFSET = 10; + + private final WorldServer world; + private WorldBorder worldBorder; + private Environment environment; + private final CraftServer server = (CraftServer) Bukkit.getServer(); + private final ChunkGenerator generator; + private final List populators = new ArrayList(); + private final BlockMetadataStore blockMetadata = new BlockMetadataStore(this); + private int monsterSpawn = -1; + private int animalSpawn = -1; + private int waterAnimalSpawn = -1; + private int ambientSpawn = -1; + private int chunkLoadCount = 0; + private int chunkGCTickCount; + + // Paper start - Provide fast information methods + public int getEntityCount() { + return world.entityList.size(); + } + public int getTileEntityCount() { + // We don't use the full world tile entity list, so we must iterate chunks + int size = 0; + for (net.minecraft.server.Chunk chunk : ((ChunkProviderServer) world.getChunkProvider()).chunks.values()) { + size += chunk.tileEntities.size(); + } + return size; + } + public int getTickableTileEntityCount() { + return world.tileEntityListTick.size(); + } + public int getChunkCount() { + return world.getChunkProvider().chunks.size(); + } + public int getPlayerCount() { + return world.players.size(); + } + // Paper end + + private static final Random rand = new Random(); + + public CraftWorld(WorldServer world, ChunkGenerator gen, Environment env) { + this.world = world; + this.generator = gen; + + environment = env; + + if (server.chunkGCPeriod > 0) { + chunkGCTickCount = rand.nextInt(server.chunkGCPeriod); + } + } + + public Block getBlockAt(int x, int y, int z) { + return CraftBlock.at(world, new BlockPosition(x, y, z)); + } + + public int getHighestBlockYAt(int x, int z) { + if (!isChunkLoaded(x >> 4, z >> 4)) { + loadChunk(x >> 4, z >> 4); + } + + return world.getHighestBlockYAt(HeightMap.Type.LIGHT_BLOCKING, new BlockPosition(x, 0, z)).getY(); + } + + public Location getSpawnLocation() { + BlockPosition spawn = world.getSpawn(); + return new Location(this, spawn.getX(), spawn.getY(), spawn.getZ()); + } + + @Override + public boolean setSpawnLocation(Location location) { + Preconditions.checkArgument(location != null, "location"); + + return equals(location.getWorld()) ? setSpawnLocation(location.getBlockX(), location.getBlockY(), location.getBlockZ()) : false; + } + + public boolean setSpawnLocation(int x, int y, int z) { + try { + Location previousLocation = getSpawnLocation(); + world.worldData.setSpawn(new BlockPosition(x, y, z)); + + // Notify anyone who's listening. + SpawnChangeEvent event = new SpawnChangeEvent(this, previousLocation); + server.getPluginManager().callEvent(event); + + return true; + } catch (Exception e) { + return false; + } + } + + // Paper start - Async chunk load API + @Override + public java.util.concurrent.CompletableFuture getChunkAtAsync(final int x, final int z, final boolean gen) { + final ChunkProviderServer cps = this.world.getChunkProvider(); + java.util.concurrent.CompletableFuture future = new java.util.concurrent.CompletableFuture<>(); + cps.getChunkAt(x, z, true, gen, chunk -> future.complete(chunk != null ? chunk.bukkitChunk : null)); + return future; + } + // Paper end + + public Chunk getChunkAt(int x, int z) { + return this.world.getChunkProvider().getChunkAt(x, z, true, true).bukkitChunk; + } + + public Chunk getChunkAt(Block block) { + return getChunkAt(block.getX() >> 4, block.getZ() >> 4); + } + + public boolean isChunkLoaded(int x, int z) { + return world.getChunkProvider().isLoaded(x, z); + } + + @Override + public boolean isChunkGenerated(int x, int z) { + return isChunkLoaded(x, z) || ((ChunkRegionLoader) world.getChunkProvider().chunkLoader).chunkExists(x, z); + } + + public Chunk[] getLoadedChunks() { + Object[] chunks = world.getChunkProvider().chunks.values().toArray(); + org.bukkit.Chunk[] craftChunks = new CraftChunk[chunks.length]; + + for (int i = 0; i < chunks.length; i++) { + net.minecraft.server.Chunk chunk = (net.minecraft.server.Chunk) chunks[i]; + craftChunks[i] = chunk.bukkitChunk; + } + + return craftChunks; + } + + public void loadChunk(int x, int z) { + loadChunk(x, z, true); + } + + public boolean unloadChunk(Chunk chunk) { + return unloadChunk(chunk.getX(), chunk.getZ()); + } + + public boolean unloadChunk(int x, int z) { + return unloadChunk(x, z, true); + } + + public boolean unloadChunk(int x, int z, boolean save) { + return unloadChunk(x, z, save, false); + } + + public boolean unloadChunkRequest(int x, int z) { + return unloadChunkRequest(x, z, true); + } + + public boolean unloadChunkRequest(int x, int z, boolean safe) { + org.spigotmc.AsyncCatcher.catchOp( "chunk unload"); // Spigot + if (safe && isChunkInUse(x, z)) { + return false; + } + + net.minecraft.server.Chunk chunk = world.getChunkIfLoaded(x, z); // Paper - optimize ifLaoded + if (chunk != null) { + world.getChunkProvider().unload(chunk); + } + + return true; + } + + public boolean unloadChunk(int x, int z, boolean save, boolean safe) { + org.spigotmc.AsyncCatcher.catchOp( "chunk unload" ); // Spigot + if (isChunkInUse(x, z)) { + return false; + } + + return unloadChunk0(x, z, save); + } + + private boolean unloadChunk0(int x, int z, boolean save) { + Boolean result = MCUtil.ensureMain("Unload Chunk", () -> { // Paper - Ensure never async + net.minecraft.server.Chunk chunk = world.getChunkIfLoaded(x, z); // Paper - optimize ifLoaded + if (chunk == null) { + return true; + } + + // If chunk had previously been queued to save, must do save to avoid loss of that data + return world.getChunkProvider().unloadChunk(chunk, chunk.mustSave || save); + }); return result != null ? result : false; // Paper - Ensure never async + } + + public boolean regenerateChunk(int x, int z) { + org.spigotmc.AsyncCatcher.catchOp( "chunk regenerate" ); // Spigot + if (!unloadChunk0(x, z, false)) { + return false; + } + + final long chunkKey = ChunkCoordIntPair.a(x, z); + world.getChunkProvider().unloadQueue.remove(chunkKey); + + net.minecraft.server.Chunk chunk = world.getChunkProvider().generateChunk(x, z); + PlayerChunk playerChunk = world.getPlayerChunkMap().getChunk(x, z); + if (playerChunk != null) { + playerChunk.chunk = chunk; + } + + if (chunk != null) { + refreshChunk(x, z); + } + + return chunk != null; + } + + public boolean refreshChunk(int x, int z) { + if (!isChunkLoaded(x, z)) { + return false; + } + + int px = x << 4; + int pz = z << 4; + + // If there are more than 64 updates to a chunk at once, it will update all 'touched' sections within the chunk + // And will include biome data if all sections have been 'touched' + // This flags 65 blocks distributed across all the sections of the chunk, so that everything is sent, including biomes + int height = getMaxHeight() / 16; + for (int idx = 0; idx < 64; idx++) { + world.notify(new BlockPosition(px + (idx / height), ((idx % height) * 16), pz), Blocks.AIR.getBlockData(), Blocks.STONE.getBlockData(), 3); + } + world.notify(new BlockPosition(px + 15, (height * 16) - 1, pz + 15), Blocks.AIR.getBlockData(), Blocks.STONE.getBlockData(), 3); + + return true; + } + + public boolean isChunkInUse(int x, int z) { + return world.getPlayerChunkMap().isChunkInUse(x, z) || world.isForceLoaded(x, z); + } + + public boolean loadChunk(int x, int z, boolean generate) { + org.spigotmc.AsyncCatcher.catchOp( "chunk load"); // Spigot + chunkLoadCount++; + return world.getChunkProvider().getChunkAt(x, z, true, generate || isChunkGenerated(x, z)) != null; // Paper + } + + public boolean isChunkLoaded(Chunk chunk) { + return isChunkLoaded(chunk.getX(), chunk.getZ()); + } + + public void loadChunk(Chunk chunk) { + loadChunk(chunk.getX(), chunk.getZ()); + ((CraftChunk) getChunkAt(chunk.getX(), chunk.getZ())).getHandle().bukkitChunk = chunk; + } + + @Override + public boolean isChunkForceLoaded(int x, int z) { + return getHandle().isForceLoaded(x, z); + } + + @Override + public void setChunkForceLoaded(int x, int z, boolean forced) { + getHandle().setForceLoaded(x, z, forced); + } + + @Override + public Collection getForceLoadedChunks() { + Set chunks = new HashSet<>(); + + for (long coord : getHandle().ag()) { // PAIL + chunks.add(getChunkAt(ChunkCoordIntPair.a(coord), ChunkCoordIntPair.b(coord))); + } + + return Collections.unmodifiableCollection(chunks); + } + + public WorldServer getHandle() { + return world; + } + + public org.bukkit.entity.Item dropItem(Location loc, ItemStack item) { + Validate.notNull(item, "Cannot drop a Null item."); + EntityItem entity = new EntityItem(world, loc.getX(), loc.getY(), loc.getZ(), CraftItemStack.asNMSCopy(item)); + entity.pickupDelay = 10; + world.addEntity(entity, SpawnReason.CUSTOM); + // TODO this is inconsistent with how Entity.getBukkitEntity() works. + // However, this entity is not at the moment backed by a server entity class so it may be left. + return new CraftItem(world.getServer(), entity); + } + + public org.bukkit.entity.Item dropItemNaturally(Location loc, ItemStack item) { + double xs = (world.random.nextFloat() * 0.5F) + 0.25D; + double ys = (world.random.nextFloat() * 0.5F) + 0.25D; + double zs = (world.random.nextFloat() * 0.5F) + 0.25D; + loc = loc.clone(); + loc.setX(loc.getX() + xs); + loc.setY(loc.getY() + ys); + loc.setZ(loc.getZ() + zs); + return dropItem(loc, item); + } + + public Arrow spawnArrow(Location loc, Vector velocity, float speed, float spread) { + return spawnArrow(loc, velocity, speed, spread, Arrow.class); + } + + public T spawnArrow(Location loc, Vector velocity, float speed, float spread, Class clazz) { + Validate.notNull(loc, "Can not spawn arrow with a null location"); + Validate.notNull(velocity, "Can not spawn arrow with a null velocity"); + Validate.notNull(clazz, "Can not spawn an arrow with no class"); + + EntityArrow arrow; + if (TippedArrow.class.isAssignableFrom(clazz)) { + arrow = new EntityTippedArrow(world); + ((EntityTippedArrow) arrow).setType(CraftPotionUtil.fromBukkit(new PotionData(PotionType.WATER, false, false))); + } else if (SpectralArrow.class.isAssignableFrom(clazz)) { + arrow = new EntitySpectralArrow(world); + } else if (Trident.class.isAssignableFrom(clazz)){ + arrow = new EntityThrownTrident(world); + } else { + arrow = new EntityTippedArrow(world); + } + + arrow.setPositionRotation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch()); + arrow.shoot(velocity.getX(), velocity.getY(), velocity.getZ(), speed, spread); + world.addEntity(arrow); + return (T) arrow.getBukkitEntity(); + } + + public Entity spawnEntity(Location loc, EntityType entityType) { + return spawn(loc, entityType.getEntityClass()); + } + + public LightningStrike strikeLightning(Location loc) { + EntityLightning lightning = new EntityLightning(world, loc.getX(), loc.getY(), loc.getZ(), false); + world.strikeLightning(lightning); + return new CraftLightningStrike(server, lightning); + } + + public LightningStrike strikeLightningEffect(Location loc) { + EntityLightning lightning = new EntityLightning(world, loc.getX(), loc.getY(), loc.getZ(), true); + world.strikeLightning(lightning); + return new CraftLightningStrike(server, lightning); + } + + public boolean generateTree(Location loc, TreeType type) { + BlockPosition pos = new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); + + net.minecraft.server.WorldGenerator gen; + switch (type) { + case BIG_TREE: + gen = new WorldGenBigTree(true); + break; + case BIRCH: + gen = new WorldGenForest(true, false); + break; + case REDWOOD: + gen = new WorldGenTaiga2(true); + break; + case TALL_REDWOOD: + gen = new WorldGenTaiga1(); + break; + case JUNGLE: + gen = new WorldGenJungleTree(true, 10, 20, Blocks.JUNGLE_LOG.getBlockData(), Blocks.JUNGLE_LEAVES.getBlockData()); + break; + case SMALL_JUNGLE: + gen = new WorldGenTrees(true, 4 + rand.nextInt(7), Blocks.JUNGLE_LOG.getBlockData(), Blocks.JUNGLE_LEAVES.getBlockData(), false); + break; + case COCOA_TREE: + gen = new WorldGenJungleTree(true, 10, 20, Blocks.JUNGLE_LOG.getBlockData(), Blocks.JUNGLE_LEAVES.getBlockData()); + break; + case JUNGLE_BUSH: + gen = new WorldGenGroundBush(Blocks.JUNGLE_LOG.getBlockData(), Blocks.OAK_LEAVES.getBlockData()); + break; + case RED_MUSHROOM: + gen = new WorldGenHugeMushroomRed(); + break; + case BROWN_MUSHROOM: + gen = new WorldGenHugeMushroomBrown(); + break; + case SWAMP: + gen = new WorldGenSwampTree(); + break; + case ACACIA: + gen = new WorldGenAcaciaTree(true); + break; + case DARK_OAK: + gen = new WorldGenForestTree(true); + break; + case MEGA_REDWOOD: + gen = new WorldGenMegaTree(false, rand.nextBoolean()); + break; + case TALL_BIRCH: + gen = new WorldGenForest(true, true); + break; + case CHORUS_PLANT: + ((BlockChorusFlower) Blocks.CHORUS_FLOWER).a(world, pos, rand, 8); + return true; + case TREE: + default: + gen = new WorldGenTrees(true); + break; + } + + return gen.generate(world, world.worldProvider.getChunkGenerator(), rand, pos, new WorldGenFeatureEmptyConfiguration()); + } + + public boolean generateTree(Location loc, TreeType type, BlockChangeDelegate delegate) { + world.captureTreeGeneration = true; + world.captureBlockStates = true; + boolean grownTree = generateTree(loc, type); + world.captureBlockStates = false; + world.captureTreeGeneration = false; + if (grownTree) { // Copy block data to delegate + for (BlockState blockstate : world.capturedBlockStates) { + BlockPosition position = ((CraftBlockState) blockstate).getPosition(); + net.minecraft.server.IBlockData oldBlock = world.getType(position); + int flag = ((CraftBlockState) blockstate).getFlag(); + delegate.setBlockData(blockstate.getX(), blockstate.getY(), blockstate.getZ(), blockstate.getBlockData()); + net.minecraft.server.IBlockData newBlock = world.getType(position); + world.notifyAndUpdatePhysics(position, null, oldBlock, newBlock, newBlock, flag); + } + world.capturedBlockStates.clear(); + return true; + } else { + world.capturedBlockStates.clear(); + return false; + } + } + + public String getName() { + return world.worldData.getName(); + } + + @Deprecated + public long getId() { + return world.worldData.getSeed(); + } + + public UUID getUID() { + return world.getDataManager().getUUID(); + } + + @Override + public String toString() { + return "CraftWorld{name=" + getName() + '}'; + } + + public long getTime() { + long time = getFullTime() % 24000; + if (time < 0) time += 24000; + return time; + } + + public void setTime(long time) { + long margin = (time - getFullTime()) % 24000; + if (margin < 0) margin += 24000; + setFullTime(getFullTime() + margin); + } + + public long getFullTime() { + return world.getDayTime(); + } + + public void setFullTime(long time) { + world.setDayTime(time); + + // Forces the client to update to the new time immediately + for (Player p : getPlayers()) { + CraftPlayer cp = (CraftPlayer) p; + if (cp.getHandle().playerConnection == null) continue; + + cp.getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateTime(cp.getHandle().world.getTime(), cp.getHandle().getPlayerTime(), cp.getHandle().world.getGameRules().getBoolean("doDaylightCycle"))); + } + } + + // Paper start + public boolean isDayTime() { + return getHandle().isDayTime(); + } + // Paper end + + public boolean createExplosion(double x, double y, double z, float power) { + return createExplosion(x, y, z, power, false, true); + } + + public boolean createExplosion(double x, double y, double z, float power, boolean setFire) { + return createExplosion(x, y, z, power, setFire, true); + } + + public boolean createExplosion(double x, double y, double z, float power, boolean setFire, boolean breakBlocks) { + return !world.createExplosion(null, x, y, z, power, setFire, breakBlocks).wasCanceled; + } + // Paper start + public boolean createExplosion(Entity source, Location loc, float power, boolean setFire, boolean breakBlocks) { + return !world.createExplosion(source != null ? ((CraftEntity) source).getHandle() : null, loc.getX(), loc.getY(), loc.getZ(), power, setFire, breakBlocks).wasCanceled; + } + // Paper end + + public boolean createExplosion(Location loc, float power) { + return createExplosion(loc, power, false); + } + + public boolean createExplosion(Location loc, float power, boolean setFire) { + return createExplosion(loc.getX(), loc.getY(), loc.getZ(), power, setFire); + } + + public Environment getEnvironment() { + return environment; + } + + public void setEnvironment(Environment env) { + if (environment != env) { + environment = env; + switch (env) { + case NORMAL: + world.worldProvider = new WorldProviderNormal(); + break; + case NETHER: + world.worldProvider = new WorldProviderHell(); + break; + case THE_END: + world.worldProvider = new WorldProviderTheEnd(); + break; + } + } + } + + public Block getBlockAt(Location location) { + return getBlockAt(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + + public int getHighestBlockYAt(Location location) { + return getHighestBlockYAt(location.getBlockX(), location.getBlockZ()); + } + + public Chunk getChunkAt(Location location) { + return getChunkAt(location.getBlockX() >> 4, location.getBlockZ() >> 4); + } + + public ChunkGenerator getGenerator() { + return generator; + } + + public List getPopulators() { + return populators; + } + + public Block getHighestBlockAt(int x, int z) { + return getBlockAt(x, getHighestBlockYAt(x, z), z); + } + + public Block getHighestBlockAt(Location location) { + return getHighestBlockAt(location.getBlockX(), location.getBlockZ()); + } + + public Biome getBiome(int x, int z) { + return CraftBlock.biomeBaseToBiome(this.world.getBiome(new BlockPosition(x, 0, z))); + } + + public void setBiome(int x, int z, Biome bio) { + BiomeBase bb = CraftBlock.biomeToBiomeBase(bio); + if (this.world.isLoaded(new BlockPosition(x, 0, z))) { + net.minecraft.server.Chunk chunk = this.world.getChunkAtWorldCoords(new BlockPosition(x, 0, z)); + + if (chunk != null) { + BiomeBase[] biomevals = chunk.getBiomeIndex(); + biomevals[((z & 0xF) << 4) | (x & 0xF)] = bb; + + chunk.markDirty(); // SPIGOT-2890 + } + } + } + + public double getTemperature(int x, int z) { + return this.world.getBiome(new BlockPosition(x, 0, z)).getTemperature(); + } + + public double getHumidity(int x, int z) { + return this.world.getBiome(new BlockPosition(x, 0, z)).getHumidity(); + } + + public List getEntities() { + List list = new ArrayList(); + + for (Object o : world.entityList) { + if (o instanceof net.minecraft.server.Entity) { + net.minecraft.server.Entity mcEnt = (net.minecraft.server.Entity) o; + if (mcEnt.shouldBeRemoved) continue; // Paper + Entity bukkitEntity = mcEnt.getBukkitEntity(); + + // Assuming that bukkitEntity isn't null + if (bukkitEntity != null) { + list.add(bukkitEntity); + } + } + } + + return list; + } + + public List getLivingEntities() { + List list = new ArrayList(); + + for (Object o : world.entityList) { + if (o instanceof net.minecraft.server.Entity) { + net.minecraft.server.Entity mcEnt = (net.minecraft.server.Entity) o; + if (mcEnt.shouldBeRemoved) continue; // Paper + Entity bukkitEntity = mcEnt.getBukkitEntity(); + + // Assuming that bukkitEntity isn't null + if (bukkitEntity != null && bukkitEntity instanceof LivingEntity) { + list.add((LivingEntity) bukkitEntity); + } + } + } + + return list; + } + + @SuppressWarnings("unchecked") + @Deprecated + public Collection getEntitiesByClass(Class... classes) { + return (Collection)getEntitiesByClasses(classes); + } + + @SuppressWarnings("unchecked") + public Collection getEntitiesByClass(Class clazz) { + Collection list = new ArrayList(); + + for (Object entity: world.entityList) { + if (entity instanceof net.minecraft.server.Entity) { + if (((net.minecraft.server.Entity) entity).shouldBeRemoved) continue; // Paper + Entity bukkitEntity = ((net.minecraft.server.Entity) entity).getBukkitEntity(); + + if (bukkitEntity == null) { + continue; + } + + Class bukkitClass = bukkitEntity.getClass(); + + if (clazz.isAssignableFrom(bukkitClass)) { + list.add((T) bukkitEntity); + } + } + } + + return list; + } + + public Collection getEntitiesByClasses(Class... classes) { + Collection list = new ArrayList(); + + for (Object entity: world.entityList) { + if (entity instanceof net.minecraft.server.Entity) { + if (((net.minecraft.server.Entity) entity).shouldBeRemoved) continue; // Paper + Entity bukkitEntity = ((net.minecraft.server.Entity) entity).getBukkitEntity(); + + if (bukkitEntity == null) { + continue; + } + + Class bukkitClass = bukkitEntity.getClass(); + + for (Class clazz : classes) { + if (clazz.isAssignableFrom(bukkitClass)) { + list.add(bukkitEntity); + break; + } + } + } + } + + return list; + } + + @Override + public Collection getNearbyEntities(Location location, double x, double y, double z) { + return this.getNearbyEntities(location, x, y, z, null); + } + + @Override + public Collection getNearbyEntities(Location location, double x, double y, double z, Predicate filter) { + Validate.notNull(location, "Location is null!"); + Validate.isTrue(this.equals(location.getWorld()), "Location is from different world!"); + + BoundingBox aabb = BoundingBox.of(location, x, y, z); + return this.getNearbyEntities(aabb, filter); + } + + @Override + public Collection getNearbyEntities(BoundingBox boundingBox) { + return this.getNearbyEntities(boundingBox, null); + } + + @Override + public Collection getNearbyEntities(BoundingBox boundingBox, Predicate filter) { + Validate.notNull(boundingBox, "Bounding box is null!"); + + AxisAlignedBB bb = new AxisAlignedBB(boundingBox.getMinX(), boundingBox.getMinY(), boundingBox.getMinZ(), boundingBox.getMaxX(), boundingBox.getMaxY(), boundingBox.getMaxZ()); + List entityList = getHandle().getEntities((net.minecraft.server.Entity) null, bb, null); + List bukkitEntityList = new ArrayList(entityList.size()); + + for (net.minecraft.server.Entity entity : entityList) { + Entity bukkitEntity = entity.getBukkitEntity(); + if (filter == null || filter.test(bukkitEntity)) { + bukkitEntityList.add(bukkitEntity); + } + } + + return bukkitEntityList; + } + + @Override + public RayTraceResult rayTraceEntities(Location start, Vector direction, double maxDistance) { + return this.rayTraceEntities(start, direction, maxDistance, null); + } + + @Override + public RayTraceResult rayTraceEntities(Location start, Vector direction, double maxDistance, double raySize) { + return this.rayTraceEntities(start, direction, maxDistance, raySize, null); + } + + @Override + public RayTraceResult rayTraceEntities(Location start, Vector direction, double maxDistance, Predicate filter) { + return this.rayTraceEntities(start, direction, maxDistance, 0.0D, filter); + } + + @Override + public RayTraceResult rayTraceEntities(Location start, Vector direction, double maxDistance, double raySize, Predicate filter) { + Validate.notNull(start, "Start location is null!"); + Validate.isTrue(this.equals(start.getWorld()), "Start location is from different world!"); + start.checkFinite(); + + Validate.notNull(direction, "Direction is null!"); + direction.checkFinite(); + + Validate.isTrue(direction.lengthSquared() > 0, "Direction's magnitude is 0!"); + + if (maxDistance < 0.0D) { + return null; + } + + Vector startPos = start.toVector(); + Vector dir = direction.clone().normalize().multiply(maxDistance); + BoundingBox aabb = BoundingBox.of(startPos, startPos).expandDirectional(dir).expand(raySize); + Collection entities = this.getNearbyEntities(aabb, filter); + + Entity nearestHitEntity = null; + RayTraceResult nearestHitResult = null; + double nearestDistanceSq = Double.MAX_VALUE; + + for (Entity entity : entities) { + BoundingBox boundingBox = entity.getBoundingBox().expand(raySize); + RayTraceResult hitResult = boundingBox.rayTrace(startPos, direction, maxDistance); + + if (hitResult != null) { + double distanceSq = startPos.distanceSquared(hitResult.getHitPosition()); + + if (distanceSq < nearestDistanceSq) { + nearestHitEntity = entity; + nearestHitResult = hitResult; + nearestDistanceSq = distanceSq; + } + } + } + + return (nearestHitEntity == null) ? null : new RayTraceResult(nearestHitResult.getHitPosition(), nearestHitEntity, nearestHitResult.getHitBlockFace()); + } + + @Override + public RayTraceResult rayTraceBlocks(Location start, Vector direction, double maxDistance) { + return this.rayTraceBlocks(start, direction, maxDistance, FluidCollisionMode.NEVER, false); + } + + @Override + public RayTraceResult rayTraceBlocks(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode) { + return this.rayTraceBlocks(start, direction, maxDistance, fluidCollisionMode, false); + } + + @Override + public RayTraceResult rayTraceBlocks(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks) { + Validate.notNull(start, "Start location is null!"); + Validate.isTrue(this.equals(start.getWorld()), "Start location is from different world!"); + start.checkFinite(); + + Validate.notNull(direction, "Direction is null!"); + direction.checkFinite(); + + Validate.isTrue(direction.lengthSquared() > 0, "Direction's magnitude is 0!"); + Validate.notNull(fluidCollisionMode, "Fluid collision mode is null!"); + + if (maxDistance < 0.0D) { + return null; + } + + Vector dir = direction.clone().normalize().multiply(maxDistance); + Vec3D startPos = new Vec3D(start.getX(), start.getY(), start.getZ()); + Vec3D endPos = new Vec3D(start.getX() + dir.getX(), start.getY() + dir.getY(), start.getZ() + dir.getZ()); + MovingObjectPosition nmsHitResult = this.getHandle().rayTrace(startPos, endPos, CraftFluidCollisionMode.toNMS(fluidCollisionMode), ignorePassableBlocks, false); + + return CraftRayTraceResult.fromNMS(this, nmsHitResult); + } + + @Override + public RayTraceResult rayTrace(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode, boolean ignorePassableBlocks, double raySize, Predicate filter) { + RayTraceResult blockHit = this.rayTraceBlocks(start, direction, maxDistance, fluidCollisionMode, ignorePassableBlocks); + Vector startVec = null; + double blockHitDistance = maxDistance; + + // limiting the entity search range if we found a block hit: + if (blockHit != null) { + startVec = start.toVector(); + blockHitDistance = startVec.distance(blockHit.getHitPosition()); + } + + RayTraceResult entityHit = this.rayTraceEntities(start, direction, blockHitDistance, raySize, filter); + if (blockHit == null) { + return entityHit; + } + + if (entityHit == null) { + return blockHit; + } + + // Cannot be null as blockHit == null returns above + double entityHitDistanceSquared = startVec.distanceSquared(entityHit.getHitPosition()); + if (entityHitDistanceSquared < (blockHitDistance * blockHitDistance)) { + return entityHit; + } + + return blockHit; + } + + public List getPlayers() { + List list = new ArrayList(world.players.size()); + + for (EntityHuman human : world.players) { + HumanEntity bukkitEntity = human.getBukkitEntity(); + + if ((bukkitEntity != null) && (bukkitEntity instanceof Player)) { + list.add((Player) bukkitEntity); + } + } + + return list; + } + + // Paper start - getEntity by UUID API + public Entity getEntity(UUID uuid) { + Validate.notNull(uuid, "UUID cannot be null"); + net.minecraft.server.Entity entity = world.getEntity(uuid); + return entity == null ? null : entity.getBukkitEntity(); + } + // Paper end + + public void save() { + // Spigot start + save(true); + } + public void save(boolean forceSave) { + // Spigot end + this.server.checkSaveState(); + try { + boolean oldSave = world.savingDisabled; + + world.savingDisabled = false; + world.save(forceSave, null); // Spigot + + world.savingDisabled = oldSave; + } catch (ExceptionWorldConflict ex) { + ex.printStackTrace(); + } + } + + public boolean isAutoSave() { + return !world.savingDisabled; + } + + public void setAutoSave(boolean value) { + world.savingDisabled = !value; + } + + public void setDifficulty(Difficulty difficulty) { + this.getHandle().worldData.setDifficulty(EnumDifficulty.getById(difficulty.getValue())); + } + + public Difficulty getDifficulty() { + return Difficulty.getByValue(this.getHandle().getDifficulty().ordinal()); + } + + public BlockMetadataStore getBlockMetadata() { + return blockMetadata; + } + + public boolean hasStorm() { + return world.worldData.hasStorm(); + } + + public void setStorm(boolean hasStorm) { + world.worldData.setStorm(hasStorm); + setWeatherDuration(0); // Reset weather duration (legacy behaviour) + } + + public int getWeatherDuration() { + return world.worldData.getWeatherDuration(); + } + + public void setWeatherDuration(int duration) { + world.worldData.setWeatherDuration(duration); + } + + public boolean isThundering() { + return world.worldData.isThundering(); + } + + public void setThundering(boolean thundering) { + world.worldData.setThundering(thundering); + setThunderDuration(0); // Reset weather duration (legacy behaviour) + } + + public int getThunderDuration() { + return world.worldData.getThunderDuration(); + } + + public void setThunderDuration(int duration) { + world.worldData.setThunderDuration(duration); + } + + public long getSeed() { + return world.worldData.getSeed(); + } + + public boolean getPVP() { + return world.pvpMode; + } + + public void setPVP(boolean pvp) { + world.pvpMode = pvp; + } + + public void playEffect(Player player, Effect effect, int data) { + playEffect(player.getLocation(), effect, data, 0); + } + + public void playEffect(Location location, Effect effect, int data) { + playEffect(location, effect, data, 64); + } + + public void playEffect(Location loc, Effect effect, T data) { + playEffect(loc, effect, data, 64); + } + + public void playEffect(Location loc, Effect effect, T data, int radius) { + if (data != null) { + Validate.isTrue(effect.getData() != null && effect.getData().isAssignableFrom(data.getClass()), "Wrong kind of data for this effect!"); + } else { + Validate.isTrue(effect.getData() == null, "Wrong kind of data for this effect!"); + } + + int datavalue = data == null ? 0 : CraftEffect.getDataValue(effect, data); + playEffect(loc, effect, datavalue, radius); + } + + public void playEffect(Location location, Effect effect, int data, int radius) { + Validate.notNull(location, "Location cannot be null"); + Validate.notNull(effect, "Effect cannot be null"); + Validate.notNull(location.getWorld(), "World cannot be null"); + int packetData = effect.getId(); + PacketPlayOutWorldEvent packet = new PacketPlayOutWorldEvent(packetData, new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ()), data, false); + int distance; + radius *= radius; + + for (Player player : getPlayers()) { + if (((CraftPlayer) player).getHandle().playerConnection == null) continue; + if (!location.getWorld().equals(player.getWorld())) continue; + + distance = (int) player.getLocation().distanceSquared(location); + if (distance <= radius) { + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + } + } + } + + public T spawn(Location location, Class clazz) throws IllegalArgumentException { + return spawn(location, clazz, null, SpawnReason.CUSTOM); + } + + @Override + public T spawn(Location location, Class clazz, Consumer function) throws IllegalArgumentException { + return spawn(location, clazz, function, SpawnReason.CUSTOM); + } + + @Override + public FallingBlock spawnFallingBlock(Location location, MaterialData data) throws IllegalArgumentException { + Validate.notNull(data, "MaterialData cannot be null"); + return spawnFallingBlock(location, data.getItemType(), data.getData()); + } + + public FallingBlock spawnFallingBlock(Location location, org.bukkit.Material material, byte data) throws IllegalArgumentException { + Validate.notNull(location, "Location cannot be null"); + Validate.notNull(material, "Material cannot be null"); + Validate.isTrue(material.isBlock(), "Material must be a block"); + + EntityFallingBlock entity = new EntityFallingBlock(world, location.getX(), location.getY(), location.getZ(), CraftMagicNumbers.getBlock(material).getBlockData()); + entity.ticksLived = 1; + + world.addEntity(entity, SpawnReason.CUSTOM); + return (FallingBlock) entity.getBukkitEntity(); + } + + @Override + public FallingBlock spawnFallingBlock(Location location, BlockData data) throws IllegalArgumentException { + Validate.notNull(location, "Location cannot be null"); + Validate.notNull(data, "Material cannot be null"); + + EntityFallingBlock entity = new EntityFallingBlock(world, location.getX(), location.getY(), location.getZ(), ((CraftBlockData) data).getState()); + entity.ticksLived = 1; + + world.addEntity(entity, SpawnReason.CUSTOM); + return (FallingBlock) entity.getBukkitEntity(); + } + + @SuppressWarnings("unchecked") + public net.minecraft.server.Entity createEntity(Location location, Class clazz) throws IllegalArgumentException { + if (location == null || clazz == null) { + throw new IllegalArgumentException("Location or entity class cannot be null"); + } + + net.minecraft.server.Entity entity = null; + + double x = location.getX(); + double y = location.getY(); + double z = location.getZ(); + float pitch = location.getPitch(); + float yaw = location.getYaw(); + + // order is important for some of these + if (Boat.class.isAssignableFrom(clazz)) { + entity = new EntityBoat(world, x, y, z); + entity.setPositionRotation(x, y, z, yaw, pitch); + // Paper start + } else if (org.bukkit.entity.Item.class.isAssignableFrom(clazz)) { + entity = new EntityItem(world, x, y, z, new net.minecraft.server.ItemStack(net.minecraft.server.Item.getItemOf(net.minecraft.server.Blocks.DIRT))); + // Paper end + } else if (FallingBlock.class.isAssignableFrom(clazz)) { + entity = new EntityFallingBlock(world, x, y, z, world.getType(new BlockPosition(x, y, z))); + } else if (Projectile.class.isAssignableFrom(clazz)) { + if (Snowball.class.isAssignableFrom(clazz)) { + entity = new EntitySnowball(world, x, y, z); + } else if (Egg.class.isAssignableFrom(clazz)) { + entity = new EntityEgg(world, x, y, z); + } else if (Arrow.class.isAssignableFrom(clazz)) { + if (TippedArrow.class.isAssignableFrom(clazz)) { + entity = new EntityTippedArrow(world); + ((EntityTippedArrow) entity).setType(CraftPotionUtil.fromBukkit(new PotionData(PotionType.WATER, false, false))); + } else if (SpectralArrow.class.isAssignableFrom(clazz)) { + entity = new EntitySpectralArrow(world); + } else if (Trident.class.isAssignableFrom(clazz)) { + entity = new EntityThrownTrident(world); + } else { + entity = new EntityTippedArrow(world); + } + entity.setPositionRotation(x, y, z, 0, 0); + } else if (ThrownExpBottle.class.isAssignableFrom(clazz)) { + entity = new EntityThrownExpBottle(world); + entity.setPositionRotation(x, y, z, 0, 0); + } else if (EnderPearl.class.isAssignableFrom(clazz)) { + entity = new EntityEnderPearl(world); + entity.setPositionRotation(x, y, z, 0, 0); + } else if (ThrownPotion.class.isAssignableFrom(clazz)) { + if (LingeringPotion.class.isAssignableFrom(clazz)) { + entity = new EntityPotion(world, x, y, z, CraftItemStack.asNMSCopy(new ItemStack(org.bukkit.Material.LINGERING_POTION, 1))); + } else { + entity = new EntityPotion(world, x, y, z, CraftItemStack.asNMSCopy(new ItemStack(org.bukkit.Material.SPLASH_POTION, 1))); + } + } else if (Fireball.class.isAssignableFrom(clazz)) { + if (SmallFireball.class.isAssignableFrom(clazz)) { + entity = new EntitySmallFireball(world); + } else if (WitherSkull.class.isAssignableFrom(clazz)) { + entity = new EntityWitherSkull(world); + } else if (DragonFireball.class.isAssignableFrom(clazz)) { + entity = new EntityDragonFireball(world); + } else { + entity = new EntityLargeFireball(world); + } + entity.setPositionRotation(x, y, z, yaw, pitch); + Vector direction = location.getDirection().multiply(10); + ((EntityFireball) entity).setDirection(direction.getX(), direction.getY(), direction.getZ()); + } else if (ShulkerBullet.class.isAssignableFrom(clazz)) { + entity = new EntityShulkerBullet(world); + entity.setPositionRotation(x, y, z, yaw, pitch); + } else if (LlamaSpit.class.isAssignableFrom(clazz)) { + entity = new EntityLlamaSpit(world); + entity.setPositionRotation(x, y, z, yaw, pitch); + } + } else if (Minecart.class.isAssignableFrom(clazz)) { + if (PoweredMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartFurnace(world, x, y, z); + } else if (StorageMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartChest(world, x, y, z); + } else if (ExplosiveMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartTNT(world, x, y, z); + } else if (HopperMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartHopper(world, x, y, z); + } else if (SpawnerMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartMobSpawner(world, x, y, z); + } else if (CommandMinecart.class.isAssignableFrom(clazz)) { + entity = new EntityMinecartCommandBlock(world, x, y, z); + } else { // Default to rideable minecart for pre-rideable compatibility + entity = new EntityMinecartRideable(world, x, y, z); + } + } else if (EnderSignal.class.isAssignableFrom(clazz)) { + entity = new EntityEnderSignal(world, x, y, z); + } else if (EnderCrystal.class.isAssignableFrom(clazz)) { + entity = new EntityEnderCrystal(world); + entity.setPositionRotation(x, y, z, 0, 0); + } else if (LivingEntity.class.isAssignableFrom(clazz)) { + if (Chicken.class.isAssignableFrom(clazz)) { + entity = EntityTypes.CHICKEN.create(world); // Paper + } else if (Cow.class.isAssignableFrom(clazz)) { + if (MushroomCow.class.isAssignableFrom(clazz)) { + entity = EntityTypes.MOOSHROOM.create(world); // Paper + } else { + entity = EntityTypes.COW.create(world); // Paper + } + } else if (Golem.class.isAssignableFrom(clazz)) { + if (Snowman.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SNOW_GOLEM.create(world); // Paper + } else if (IronGolem.class.isAssignableFrom(clazz)) { + entity = EntityTypes.IRON_GOLEM.create(world); // Paper + } else if (Shulker.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SHULKER.create(world); // Paper + } + } else if (Creeper.class.isAssignableFrom(clazz)) { + entity = EntityTypes.CREEPER.create(world); // Paper + } else if (Ghast.class.isAssignableFrom(clazz)) { + entity = EntityTypes.GHAST.create(world); // Paper + } else if (Pig.class.isAssignableFrom(clazz)) { + entity = EntityTypes.PIG.create(world); // Paper + } else if (Player.class.isAssignableFrom(clazz)) { + // need a net server handler for this one + } else if (Sheep.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SHEEP.create(world); // Paper + } else if (AbstractHorse.class.isAssignableFrom(clazz)) { + if (ChestedHorse.class.isAssignableFrom(clazz)) { + if (Donkey.class.isAssignableFrom(clazz)) { + entity = EntityTypes.DONKEY.create(world); // Paper + } else if (Mule.class.isAssignableFrom(clazz)) { + entity = EntityTypes.MULE.create(world); // Paper + } else if (Llama.class.isAssignableFrom(clazz)) { + entity = EntityTypes.LLAMA.create(world); // Paper + } + } else if (SkeletonHorse.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SKELETON_HORSE.create(world); // Paper + } else if (ZombieHorse.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ZOMBIE_HORSE.create(world); // Paper + } else { + entity = EntityTypes.HORSE.create(world); // Paper + } + } else if (Skeleton.class.isAssignableFrom(clazz)) { + if (Stray.class.isAssignableFrom(clazz)){ + entity = EntityTypes.STRAY.create(world); // Paper + } else if (WitherSkeleton.class.isAssignableFrom(clazz)) { + entity = EntityTypes.WITHER_SKELETON.create(world); // Paper + } else { + entity = EntityTypes.SKELETON.create(world); // Paper + } + } else if (Slime.class.isAssignableFrom(clazz)) { + if (MagmaCube.class.isAssignableFrom(clazz)) { + entity = EntityTypes.MAGMA_CUBE.create(world); // Paper + } else { + entity = EntityTypes.SLIME.create(world); // Paper + } + } else if (Spider.class.isAssignableFrom(clazz)) { + if (CaveSpider.class.isAssignableFrom(clazz)) { + entity = EntityTypes.CAVE_SPIDER.create(world); // Paper + } else { + entity = EntityTypes.SPIDER.create(world); // Paper + } + } else if (Squid.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SQUID.create(world); // Paper + } else if (Tameable.class.isAssignableFrom(clazz)) { + if (Wolf.class.isAssignableFrom(clazz)) { + entity = EntityTypes.WOLF.create(world); // Paper + } else if (Ocelot.class.isAssignableFrom(clazz)) { + entity = EntityTypes.OCELOT.create(world); // Paper + } else if (Parrot.class.isAssignableFrom(clazz)) { + entity = EntityTypes.PARROT.create(world); // Paper + } + } else if (PigZombie.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ZOMBIE_PIGMAN.create(world); // Paper + } else if (Zombie.class.isAssignableFrom(clazz)) { + if (Husk.class.isAssignableFrom(clazz)) { + entity = EntityTypes.HUSK.create(world); // Paper + } else if (ZombieVillager.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ZOMBIE_VILLAGER.create(world); // Paper + } else if (Drowned.class.isAssignableFrom(clazz)) { + entity = EntityTypes.DROWNED.create(world); // Paper + } else { + entity = EntityTypes.ZOMBIE.create(world); // Paper + } + } else if (Giant.class.isAssignableFrom(clazz)) { + entity = EntityTypes.GIANT.create(world); // Paper + } else if (Silverfish.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SILVERFISH.create(world); // Paper + } else if (Enderman.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ENDERMAN.create(world); // Paper + } else if (Blaze.class.isAssignableFrom(clazz)) { + entity = EntityTypes.BLAZE.create(world); // Paper + } else if (Villager.class.isAssignableFrom(clazz)) { + entity = EntityTypes.VILLAGER.create(world); // Paper + } else if (Witch.class.isAssignableFrom(clazz)) { + entity = EntityTypes.WITCH.create(world); // Paper + } else if (Wither.class.isAssignableFrom(clazz)) { + entity = EntityTypes.WITHER.create(world); // Paper + } else if (ComplexLivingEntity.class.isAssignableFrom(clazz)) { + if (EnderDragon.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ENDER_DRAGON.create(world); // Paper + } + } else if (Ambient.class.isAssignableFrom(clazz)) { + if (Bat.class.isAssignableFrom(clazz)) { + entity = EntityTypes.BAT.create(world); // Paper + } + } else if (Rabbit.class.isAssignableFrom(clazz)) { + entity = EntityTypes.RABBIT.create(world); // Paper + } else if (Endermite.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ENDERMITE.create(world); // Paper + } else if (Guardian.class.isAssignableFrom(clazz)) { + if (ElderGuardian.class.isAssignableFrom(clazz)){ + entity = EntityTypes.ELDER_GUARDIAN.create(world); // Paper + } else { + entity = EntityTypes.GUARDIAN.create(world); // Paper + } + } else if (ArmorStand.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ARMOR_STAND.create(world); // Paper + } else if (PolarBear.class.isAssignableFrom(clazz)) { + entity = EntityTypes.POLAR_BEAR.create(world); // Paper + } else if (Vex.class.isAssignableFrom(clazz)) { + entity = EntityTypes.VEX.create(world); // Paper + } else if (Illager.class.isAssignableFrom(clazz)) { + if (Spellcaster.class.isAssignableFrom(clazz)) { + if (Evoker.class.isAssignableFrom(clazz)) { + entity = EntityTypes.EVOKER.create(world); // Paper + } else if (Illusioner.class.isAssignableFrom(clazz)) { + entity = EntityTypes.ILLUSIONER.create(world); // Paper + } + } else if (Vindicator.class.isAssignableFrom(clazz)) { + entity = EntityTypes.VINDICATOR.create(world); // Paper + } + } else if (Turtle.class.isAssignableFrom(clazz)) { + entity = EntityTypes.TURTLE.create(world); // Paper + } else if (Phantom.class.isAssignableFrom(clazz)) { + entity = EntityTypes.PHANTOM.create(world); // Paper + } else if (Fish.class.isAssignableFrom(clazz)) { + if (Cod.class.isAssignableFrom(clazz)) { + entity = EntityTypes.COD.create(world); // Paper + } else if (PufferFish.class.isAssignableFrom(clazz)) { + entity = EntityTypes.PUFFERFISH.create(world); // Paper + } else if (Salmon.class.isAssignableFrom(clazz)) { + entity = EntityTypes.SALMON.create(world); // Paper + } else if (TropicalFish.class.isAssignableFrom(clazz)) { + entity = EntityTypes.TROPICAL_FISH.create(world); // Paper + } + } else if (Dolphin.class.isAssignableFrom(clazz)) { + entity = EntityTypes.DOLPHIN.create(world); // Paper + } + + if (entity != null) { + entity.setLocation(x, y, z, yaw, pitch); + entity.setHeadRotation(yaw); // SPIGOT-3587 + } + } else if (Hanging.class.isAssignableFrom(clazz)) { + BlockFace face = BlockFace.SELF; + + int width = 16; // 1 full block, also painting smallest size. + int height = 16; // 1 full block, also painting smallest size. + + if (ItemFrame.class.isAssignableFrom(clazz)) { + width = 12; + height = 12; + } else if (LeashHitch.class.isAssignableFrom(clazz)) { + width = 9; + height = 9; + } + + BlockFace[] faces = new BlockFace[]{BlockFace.EAST, BlockFace.NORTH, BlockFace.WEST, BlockFace.SOUTH}; + final BlockPosition pos = new BlockPosition((int) x, (int) y, (int) z); + for (BlockFace dir : faces) { + IBlockData nmsBlock = world.getType(pos.shift(CraftBlock.blockFaceToNotch(dir))); + if (nmsBlock.getMaterial().isBuildable() || BlockDiodeAbstract.isDiode(nmsBlock)) { + boolean taken = false; + AxisAlignedBB bb = EntityHanging.calculateBoundingBox(null, pos, CraftBlock.blockFaceToNotch(dir).opposite(), width, height); + List list = (List) world.getEntities(null, bb); + for (Iterator it = list.iterator(); !taken && it.hasNext();) { + net.minecraft.server.Entity e = it.next(); + if (e instanceof EntityHanging) { + taken = true; // Hanging entities do not like hanging entities which intersect them. + } + } + + if (!taken) { + face = dir; + break; + } + } + } + + if (LeashHitch.class.isAssignableFrom(clazz)) { + entity = new EntityLeash(world, new BlockPosition((int) x, (int) y, (int) z)); + entity.attachedToPlayer = true; + } else { + // No valid face found + Preconditions.checkArgument(face != BlockFace.SELF, "Cannot spawn hanging entity for %s at %s (no free face)", clazz.getName(), location); + + EnumDirection dir = CraftBlock.blockFaceToNotch(face).opposite(); + if (Painting.class.isAssignableFrom(clazz)) { + entity = new EntityPainting(world, new BlockPosition((int) x, (int) y, (int) z), dir); + } else if (ItemFrame.class.isAssignableFrom(clazz)) { + entity = new EntityItemFrame(world, new BlockPosition((int) x, (int) y, (int) z), dir); + } + } + + if (entity != null && !((EntityHanging) entity).survives()) { + throw new IllegalArgumentException("Cannot spawn hanging entity for " + clazz.getName() + " at " + location); + } + } else if (TNTPrimed.class.isAssignableFrom(clazz)) { + entity = new EntityTNTPrimed(world, x, y, z, null); + } else if (ExperienceOrb.class.isAssignableFrom(clazz)) { + entity = new EntityExperienceOrb(world, x, y, z, 0, org.bukkit.entity.ExperienceOrb.SpawnReason.CUSTOM, null, null); // Paper + } else if (Weather.class.isAssignableFrom(clazz)) { + // not sure what this can do + if (LightningStrike.class.isAssignableFrom(clazz)) { + entity = new EntityLightning(world, x, y, z, false); + // what is this, I don't even + } + } else if (Firework.class.isAssignableFrom(clazz)) { + entity = new EntityFireworks(world, x, y, z, net.minecraft.server.ItemStack.a); + } else if (AreaEffectCloud.class.isAssignableFrom(clazz)) { + entity = new EntityAreaEffectCloud(world, x, y, z); + } else if (EvokerFangs.class.isAssignableFrom(clazz)) { + entity = new EntityEvokerFangs(world, x, y, z, (float) Math.toRadians(yaw), 0, null); + } + + if (entity != null) { + // Spigot start + if (entity instanceof EntityOcelot) + { + ( (EntityOcelot) entity ).spawnBonus = false; + } + // Spigot end + return entity; + } + + throw new IllegalArgumentException("Cannot spawn an entity for " + clazz.getName()); + } + + @SuppressWarnings("unchecked") + public T addEntity(net.minecraft.server.Entity entity, SpawnReason reason) throws IllegalArgumentException { + return addEntity(entity, reason, null); + } + + @SuppressWarnings("unchecked") + public T addEntity(net.minecraft.server.Entity entity, SpawnReason reason, Consumer function) throws IllegalArgumentException { + Preconditions.checkArgument(entity != null, "Cannot spawn null entity"); + + if (entity instanceof EntityInsentient) { + ((EntityInsentient) entity).prepare(getHandle().getDamageScaler(new BlockPosition(entity)), (GroupDataEntity) null, null); + } + + if (function != null) { + function.accept((T) entity.getBukkitEntity()); + } + + world.addEntity(entity, reason); + return (T) entity.getBukkitEntity(); + } + + public T spawn(Location location, Class clazz, Consumer function, SpawnReason reason) throws IllegalArgumentException { + net.minecraft.server.Entity entity = createEntity(location, clazz); + + return addEntity(entity, reason, function); + } + + public ChunkSnapshot getEmptyChunkSnapshot(int x, int z, boolean includeBiome, boolean includeBiomeTempRain) { + return CraftChunk.getEmptyChunkSnapshot(x, z, this, includeBiome, includeBiomeTempRain); + } + + public void setSpawnFlags(boolean allowMonsters, boolean allowAnimals) { + world.setSpawnFlags(allowMonsters, allowAnimals); + } + + public boolean getAllowAnimals() { + return world.allowAnimals; + } + + public boolean getAllowMonsters() { + return world.allowMonsters; + } + + public int getMaxHeight() { + return world.getHeight(); + } + + public int getSeaLevel() { + return world.getSeaLevel(); + } + + public boolean getKeepSpawnInMemory() { + return world.keepSpawnInMemory; + } + + public void setKeepSpawnInMemory(boolean keepLoaded) { + world.keepSpawnInMemory = keepLoaded; + // Grab the worlds spawn chunk + BlockPosition chunkcoordinates = this.world.getSpawn(); + int chunkCoordX = chunkcoordinates.getX() >> 4; + int chunkCoordZ = chunkcoordinates.getZ() >> 4; + // Cycle through the 25x25 Chunks around it to load/unload the chunks. + int radius = world.paperConfig.keepLoadedRange / 16; // Paper + // Paper start + for (ChunkCoordIntPair coords : world.getChunkProvider().getSpiralOutChunks(world.getSpawn(), radius)) {{ + int x = coords.x; + int z = coords.z; + // Paper end + if (keepLoaded) { + getChunkAtAsync(chunkCoordX + x, chunkCoordZ + z, chunk -> {}); // Paper - Async Chunks + } else { + if (isChunkLoaded(chunkCoordX + x, chunkCoordZ + z)) { + unloadChunk(chunkCoordX + x, chunkCoordZ + z); + } + } + } + } + } + + @Override + public int hashCode() { + return getUID().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + + final CraftWorld other = (CraftWorld) obj; + + return this.getUID() == other.getUID(); + } + + public File getWorldFolder() { + return ((WorldNBTStorage) world.getDataManager()).getDirectory(); + } + + public void sendPluginMessage(Plugin source, String channel, byte[] message) { + StandardMessenger.validatePluginMessage(server.getMessenger(), source, channel, message); + + for (Player player : getPlayers()) { + player.sendPluginMessage(source, channel, message); + } + } + + public Set getListeningPluginChannels() { + Set result = new HashSet(); + + for (Player player : getPlayers()) { + result.addAll(player.getListeningPluginChannels()); + } + + return result; + } + + public org.bukkit.WorldType getWorldType() { + return org.bukkit.WorldType.getByName(world.getWorldData().getType().name()); + } + + public boolean canGenerateStructures() { + return world.getWorldData().shouldGenerateMapFeatures(); + } + + public long getTicksPerAnimalSpawns() { + return world.ticksPerAnimalSpawns; + } + + public void setTicksPerAnimalSpawns(int ticksPerAnimalSpawns) { + world.ticksPerAnimalSpawns = ticksPerAnimalSpawns; + } + + public long getTicksPerMonsterSpawns() { + return world.ticksPerMonsterSpawns; + } + + public void setTicksPerMonsterSpawns(int ticksPerMonsterSpawns) { + world.ticksPerMonsterSpawns = ticksPerMonsterSpawns; + } + + public void setMetadata(String metadataKey, MetadataValue newMetadataValue) { + server.getWorldMetadata().setMetadata(this, metadataKey, newMetadataValue); + } + + public List getMetadata(String metadataKey) { + return server.getWorldMetadata().getMetadata(this, metadataKey); + } + + public boolean hasMetadata(String metadataKey) { + return server.getWorldMetadata().hasMetadata(this, metadataKey); + } + + public void removeMetadata(String metadataKey, Plugin owningPlugin) { + server.getWorldMetadata().removeMetadata(this, metadataKey, owningPlugin); + } + + public int getMonsterSpawnLimit() { + if (monsterSpawn < 0) { + return server.getMonsterSpawnLimit(); + } + + return monsterSpawn; + } + + public void setMonsterSpawnLimit(int limit) { + monsterSpawn = limit; + } + + public int getAnimalSpawnLimit() { + if (animalSpawn < 0) { + return server.getAnimalSpawnLimit(); + } + + return animalSpawn; + } + + public void setAnimalSpawnLimit(int limit) { + animalSpawn = limit; + } + + public int getWaterAnimalSpawnLimit() { + if (waterAnimalSpawn < 0) { + return server.getWaterAnimalSpawnLimit(); + } + + return waterAnimalSpawn; + } + + public void setWaterAnimalSpawnLimit(int limit) { + waterAnimalSpawn = limit; + } + + public int getAmbientSpawnLimit() { + if (ambientSpawn < 0) { + return server.getAmbientSpawnLimit(); + } + + return ambientSpawn; + } + + public void setAmbientSpawnLimit(int limit) { + ambientSpawn = limit; + } + + public void playSound(Location loc, Sound sound, float volume, float pitch) { + playSound(loc, sound, org.bukkit.SoundCategory.MASTER, volume, pitch); + } + + public void playSound(Location loc, String sound, float volume, float pitch) { + playSound(loc, sound, org.bukkit.SoundCategory.MASTER, volume, pitch); + } + + @Override + public void playSound(Location loc, Sound sound, org.bukkit.SoundCategory category, float volume, float pitch) { + if (loc == null || sound == null || category == null) return; + + double x = loc.getX(); + double y = loc.getY(); + double z = loc.getZ(); + + getHandle().a(null, x, y, z, CraftSound.getSoundEffect(CraftSound.getSound(sound)), SoundCategory.valueOf(category.name()), volume, pitch); // PAIL: rename + } + + @Override + public void playSound(Location loc, String sound, org.bukkit.SoundCategory category, float volume, float pitch) { + if (loc == null || sound == null || category == null) return; + + double x = loc.getX(); + double y = loc.getY(); + double z = loc.getZ(); + + PacketPlayOutCustomSoundEffect packet = new PacketPlayOutCustomSoundEffect(new MinecraftKey(sound), SoundCategory.valueOf(category.name()), new Vec3D(x, y, z), volume, pitch); + world.getMinecraftServer().getPlayerList().sendPacketNearby(null, x, y, z, volume > 1.0F ? 16.0F * volume : 16.0D, this.world, packet); // Paper - this.world.dimension -> this.world + } + + public String getGameRuleValue(String rule) { + // In method contract for some reason + if (rule == null) { + return null; + } + + GameRules.GameRuleValue value = getHandle().getGameRules().get(rule); + return value != null ? value.a() : ""; + } + + public boolean setGameRuleValue(String rule, String value) { + // No null values allowed + if (rule == null || value == null) return false; + + if (!isGameRule(rule)) return false; + + getHandle().getGameRules().set(rule, value, getHandle().getMinecraftServer()); + return true; + } + + public String[] getGameRules() { + return GameRules.getGameRules().keySet().toArray(new String[GameRules.getGameRules().size()]); + } + + public boolean isGameRule(String rule) { + Validate.isTrue(rule != null && !rule.isEmpty(), "Rule cannot be null nor empty"); + return GameRules.getGameRules().containsKey(rule); + } + + @Override + public T getGameRuleValue(GameRule rule) { + Validate.notNull(rule, "GameRule cannot be null"); + return convert(rule, getHandle().getGameRules().get(rule.getName())); + } + + @Override + public T getGameRuleDefault(GameRule rule) { + Validate.notNull(rule, "GameRule cannot be null"); + return convert(rule, GameRules.getGameRules().get(rule.getName()).a()); + } + + @Override + public boolean setGameRule(GameRule rule, T newValue) { + Validate.notNull(rule, "GameRule cannot be null"); + Validate.notNull(newValue, "GameRule value cannot be null"); + + if (!isGameRule(rule.getName())) return false; + + getHandle().getGameRules().set(rule.getName(), newValue.toString(), getHandle().getMinecraftServer()); + return true; + } + + private T convert(GameRule rule, GameRules.GameRuleValue value) { + if (value == null) { + return null; + } + + switch (value.getType()) { + case BOOLEAN_VALUE: + return rule.getType().cast(value.b()); + case NUMERICAL_VALUE: + return rule.getType().cast(value.c()); + default: + throw new IllegalArgumentException("Invalid GameRule type (" + value.getType() + ") for GameRule " + rule.getName()); + } + } + + @Override + public WorldBorder getWorldBorder() { + if (this.worldBorder == null) { + this.worldBorder = new CraftWorldBorder(this); + } + + return this.worldBorder; + } + + @Override + public void spawnParticle(Particle particle, Location location, int count) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count); + } + + @Override + public void spawnParticle(Particle particle, double x, double y, double z, int count) { + spawnParticle(particle, x, y, z, count, null); + } + + @Override + public void spawnParticle(Particle particle, Location location, int count, T data) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, data); + } + + @Override + public void spawnParticle(Particle particle, double x, double y, double z, int count, T data) { + spawnParticle(particle, x, y, z, count, 0, 0, 0, data); + } + + @Override + public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ); + } + + @Override + public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ) { + spawnParticle(particle, x, y, z, count, offsetX, offsetY, offsetZ, null); + } + + @Override + public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ, T data) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, data); + } + + @Override + public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, T data) { + spawnParticle(particle, x, y, z, count, offsetX, offsetY, offsetZ, 1, data); + } + + @Override + public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ, double extra) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra); + } + + @Override + public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra) { + spawnParticle(particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, null); + } + + @Override + public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ, double extra, T data) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, data); + } + + @Override + public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, T data) { + spawnParticle(particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data, false); + } + + @Override + public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ, double extra, T data, boolean force) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, data, force); + } + + @Override + public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, T data, boolean force) { + // Paper start - Particle API Expansion + spawnParticle(particle, null, null, x, y, z, count, offsetX, offsetY, offsetZ, extra, data, force); + } + public void spawnParticle(Particle particle, List receivers, Player sender, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, T data, boolean force) { + // Paper end + if (data != null && !particle.getDataType().isInstance(data)) { + throw new IllegalArgumentException("data should be " + particle.getDataType() + " got " + data.getClass()); + } + getHandle().sendParticles( + receivers == null ? getHandle().players : receivers.stream().map(player -> ((CraftPlayer) player).getHandle()).collect(java.util.stream.Collectors.toList()), // Paper - Particle API Expansion + sender != null ? ((CraftPlayer) sender).getHandle() : null, // Sender // Paper - Particle API Expansion + CraftParticle.toNMS(particle, data), // Particle + x, y, z, // Position + count, // Count + offsetX, offsetY, offsetZ, // Random offset + extra, // Speed? + force + ); + + } + + @Override + public Location locateNearestStructure(Location origin, StructureType structureType, int radius, boolean findUnexplored) { + BlockPosition originPos = new BlockPosition(origin.getX(), origin.getY(), origin.getZ()); + BlockPosition nearest = getHandle().getChunkProvider().getChunkGenerator().findNearestMapFeature(getHandle(), structureType.getName(), originPos, radius, findUnexplored); + return (nearest == null) ? null : new Location(this, nearest.getX(), nearest.getY(), nearest.getZ()); + } + + public void processChunkGC() { + chunkGCTickCount++; + + if (chunkLoadCount >= server.chunkGCLoadThresh && server.chunkGCLoadThresh > 0) { + chunkLoadCount = 0; + } else if (chunkGCTickCount >= server.chunkGCPeriod && server.chunkGCPeriod > 0) { + chunkGCTickCount = 0; + } else { + return; + } + + ChunkProviderServer cps = world.getChunkProvider(); + for (net.minecraft.server.Chunk chunk : cps.chunks.values()) { + // If in use, skip it + if (isChunkInUse(chunk.locX, chunk.locZ) || chunk.scheduledForUnload != null) { // Paper - delayed chunk unloads + continue; + } + + // Already unloading? + if (cps.unloadQueue.contains(ChunkCoordIntPair.a(chunk.locX, chunk.locZ))) { + continue; + } + + // Add unload request + cps.unload(chunk); + } + } + // Spigot start + private final Spigot spigot = new Spigot() + { + + @Override + public LightningStrike strikeLightning(Location loc, boolean isSilent) + { + EntityLightning lightning = new EntityLightning( world, loc.getX(), loc.getY(), loc.getZ(), false, isSilent ); + world.strikeLightning( lightning ); + return new CraftLightningStrike( server, lightning ); + } + + @Override + public LightningStrike strikeLightningEffect(Location loc, boolean isSilent) + { + EntityLightning lightning = new EntityLightning( world, loc.getX(), loc.getY(), loc.getZ(), true, isSilent ); + world.strikeLightning( lightning ); + return new CraftLightningStrike( server, lightning ); + } + }; + + public Spigot spigot() + { + return spigot; + } + // Spigot end +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorldBorder.java b/src/main/java/org/bukkit/craftbukkit/CraftWorldBorder.java new file mode 100644 index 000000000000..403c854c4d1e --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorldBorder.java @@ -0,0 +1,120 @@ +package org.bukkit.craftbukkit; + +import com.google.common.base.Preconditions; +import net.minecraft.server.BlockPosition; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.WorldBorder; + +public class CraftWorldBorder implements WorldBorder { + + private final World world; + private final net.minecraft.server.WorldBorder handle; + + public CraftWorldBorder(CraftWorld world) { + this.world = world; + this.handle = world.getHandle().getWorldBorder(); + } + + @Override + public void reset() { + this.setSize(6.0E7D); + this.setDamageAmount(0.2D); + this.setDamageBuffer(5.0D); + this.setWarningDistance(5); + this.setWarningTime(15); + this.setCenter(0, 0); + } + + @Override + public double getSize() { + return this.handle.getSize(); + } + + @Override + public void setSize(double newSize) { + this.setSize(newSize, 0L); + } + + @Override + public void setSize(double newSize, long time) { + // PAIL: TODO: Magic Values + newSize = Math.min(6.0E7D, Math.max(1.0D, newSize)); + time = Math.min(9223372036854775L, Math.max(0L, time)); + + if (time > 0L) { + this.handle.transitionSizeBetween(this.handle.getSize(), newSize, time * 1000L); + } else { + this.handle.setSize(newSize); + } + } + + @Override + public Location getCenter() { + double x = this.handle.getCenterX(); + double z = this.handle.getCenterZ(); + + return new Location(this.world, x, 0, z); + } + + @Override + public void setCenter(double x, double z) { + // PAIL: TODO: Magic Values + x = Math.min(3.0E7D, Math.max(-3.0E7D, x)); + z = Math.min(3.0E7D, Math.max(-3.0E7D, z)); + + this.handle.setCenter(x, z); + } + + @Override + public void setCenter(Location location) { + this.setCenter(location.getX(), location.getZ()); + } + + @Override + public double getDamageBuffer() { + return this.handle.getDamageBuffer(); + } + + @Override + public void setDamageBuffer(double blocks) { + this.handle.setDamageBuffer(blocks); + } + + @Override + public double getDamageAmount() { + return this.handle.getDamageAmount(); + } + + @Override + public void setDamageAmount(double damage) { + this.handle.setDamageAmount(damage); + } + + @Override + public int getWarningTime() { + return this.handle.getWarningTime(); + } + + @Override + public void setWarningTime(int time) { + this.handle.setWarningTime(time); + } + + @Override + public int getWarningDistance() { + return this.handle.getWarningDistance(); + } + + @Override + public void setWarningDistance(int distance) { + this.handle.setWarningDistance(distance); + } + + @Override + public boolean isInside(Location location) { + Preconditions.checkArgument(location != null, "location"); + + return location.getWorld().equals(this.world) && this.handle.a(new BlockPosition(location.getX(), location.getY(), location.getZ())); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/LoggerOutputStream.java b/src/main/java/org/bukkit/craftbukkit/LoggerOutputStream.java new file mode 100644 index 000000000000..93526ab602af --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/LoggerOutputStream.java @@ -0,0 +1,31 @@ +package org.bukkit.craftbukkit; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; + +public class LoggerOutputStream extends ByteArrayOutputStream { + private final String separator = System.getProperty("line.separator"); + private final Logger logger; + private final Level level; + + public LoggerOutputStream(Logger logger, Level level) { + super(); + this.logger = logger; + this.level = level; + } + + @Override + public void flush() throws IOException { + synchronized (this) { + super.flush(); + String record = this.toString(); + super.reset(); + + if ((record.length() > 0) && (!record.equals(separator))) { + logger.log(level, record); + } + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java new file mode 100644 index 000000000000..c9a6b5afb1a3 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/Main.java @@ -0,0 +1,253 @@ +package org.bukkit.craftbukkit; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import net.minecraft.server.MinecraftServer; +import net.minecrell.terminalconsole.TerminalConsoleAppender; // Paper + +public class Main { + public static boolean useJline = true; + public static boolean useConsole = true; + + public static void main(String[] args) { + // Todo: Installation script + OptionParser parser = new OptionParser() { + { + acceptsAll(asList("?", "help"), "Show the help"); + + acceptsAll(asList("c", "config"), "Properties file to use") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File("server.properties")) + .describedAs("Properties file"); + + acceptsAll(asList("P", "plugins"), "Plugin directory to use") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File("plugins")) + .describedAs("Plugin directory"); + + acceptsAll(asList("h", "host", "server-ip"), "Host to listen on") + .withRequiredArg() + .ofType(String.class) + .describedAs("Hostname or IP"); + + acceptsAll(asList("W", "world-dir", "universe", "world-container"), "World container") + .withRequiredArg() + .ofType(File.class) + .describedAs("Directory containing worlds"); + + acceptsAll(asList("w", "world", "level-name"), "World name") + .withRequiredArg() + .ofType(String.class) + .describedAs("World name"); + + acceptsAll(asList("p", "port", "server-port"), "Port to listen on") + .withRequiredArg() + .ofType(Integer.class) + .describedAs("Port"); + + acceptsAll(asList("o", "online-mode"), "Whether to use online authentication") + .withRequiredArg() + .ofType(Boolean.class) + .describedAs("Authentication"); + + acceptsAll(asList("s", "size", "max-players"), "Maximum amount of players") + .withRequiredArg() + .ofType(Integer.class) + .describedAs("Server size"); + + acceptsAll(asList("d", "date-format"), "Format of the date to display in the console (for log entries)") + .withRequiredArg() + .ofType(SimpleDateFormat.class) + .describedAs("Log date format"); + + acceptsAll(asList("log-pattern"), "Specfies the log filename pattern") + .withRequiredArg() + .ofType(String.class) + .defaultsTo("server.log") + .describedAs("Log filename"); + + acceptsAll(asList("log-limit"), "Limits the maximum size of the log file (0 = unlimited)") + .withRequiredArg() + .ofType(Integer.class) + .defaultsTo(0) + .describedAs("Max log size"); + + acceptsAll(asList("log-count"), "Specified how many log files to cycle through") + .withRequiredArg() + .ofType(Integer.class) + .defaultsTo(1) + .describedAs("Log count"); + + acceptsAll(asList("log-append"), "Whether to append to the log file") + .withRequiredArg() + .ofType(Boolean.class) + .defaultsTo(true) + .describedAs("Log append"); + + acceptsAll(asList("log-strip-color"), "Strips color codes from log file"); + + acceptsAll(asList("b", "bukkit-settings"), "File for bukkit settings") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File("bukkit.yml")) + .describedAs("Yml file"); + + acceptsAll(asList("C", "commands-settings"), "File for command settings") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File("commands.yml")) + .describedAs("Yml file"); + + acceptsAll(asList("forceUpgrade"), "Whether to force a world upgrade"); + + acceptsAll(asList("nojline"), "Disables jline and emulates the vanilla console"); + + acceptsAll(asList("noconsole"), "Disables the console"); + + acceptsAll(asList("v", "version"), "Show the CraftBukkit Version"); + + acceptsAll(asList("demo"), "Demo mode"); + + // Spigot Start + acceptsAll(asList("S", "spigot-settings"), "File for spigot settings") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File("spigot.yml")) + .describedAs("Yml file"); + // Spigot End + + // Paper Start + acceptsAll(asList("paper", "paper-settings"), "File for paper settings") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File("paper.yml")) + .describedAs("Yml file"); + // Paper end + + // Paper start + acceptsAll(asList("server-name"), "Name of the server") + .withRequiredArg() + .ofType(String.class) + .defaultsTo("Unknown Server") + .describedAs("Name"); + // Paper end + } + }; + + OptionSet options = null; + + try { + options = parser.parse(args); + } catch (joptsimple.OptionException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, ex.getLocalizedMessage()); + } + + if ((options == null) || (options.has("?"))) { + try { + parser.printHelpOn(System.out); + } catch (IOException ex) { + Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); + } + } else if (options.has("v")) { + System.out.println(CraftServer.class.getPackage().getImplementationVersion()); + } else { + // Do you love Java using + and ! as string based identifiers? I sure do! + String path = new File(".").getAbsolutePath(); + if (path.contains("!") || path.contains("+")) { + System.err.println("Cannot run server in a directory with ! or + in the pathname. Please rename the affected folders and try again."); + return; + } + + float javaVersion = Float.parseFloat(System.getProperty("java.class.version")); + if (javaVersion > 55.0) { + System.err.println("Unsupported Java detected (" + javaVersion + "). Only up to Java 11 is supported."); + return; + } + + try { + // Paper start - Handled by TerminalConsoleAppender + /* + // This trick bypasses Maven Shade's clever rewriting of our getProperty call when using String literals + String jline_UnsupportedTerminal = new String(new char[] {'j','l','i','n','e','.','U','n','s','u','p','p','o','r','t','e','d','T','e','r','m','i','n','a','l'}); + String jline_terminal = new String(new char[] {'j','l','i','n','e','.','t','e','r','m','i','n','a','l'}); + + useJline = !(jline_UnsupportedTerminal).equals(System.getProperty(jline_terminal)); + + if (options.has("nojline")) { + System.setProperty("user.language", "en"); + useJline = false; + } + + if (useJline) { + AnsiConsole.systemInstall(); + } else { + // This ensures the terminal literal will always match the jline implementation + System.setProperty(jline.TerminalFactory.JLINE_TERMINAL, jline.UnsupportedTerminal.class.getName()); + } + */ + + if (options.has("nojline")) { + System.setProperty(TerminalConsoleAppender.JLINE_OVERRIDE_PROPERTY, "false"); + useJline = false; + } + // Paper end + + if (options.has("noconsole")) { + useConsole = false; + useJline = false; // Paper + System.setProperty(TerminalConsoleAppender.JLINE_OVERRIDE_PROPERTY, "false"); // Paper + } + + if (Main.class.getPackage().getImplementationVendor() != null && System.getProperty("IReallyKnowWhatIAmDoingISwear") == null) { + Date buildDate = new SimpleDateFormat("yyyyMMdd-HHmm").parse(Main.class.getPackage().getImplementationVendor()); + + Calendar deadline = Calendar.getInstance(); + deadline.add(Calendar.DAY_OF_YEAR, -21); + if (buildDate.before(deadline.getTime())) { + // Paper start - This is some stupid bullshit + System.err.println("*** Warning, you've not updated in a while! ***"); + System.err.println("*** Please download a new build as per instructions from https://papermc.io/downloads ***"); // Paper + //System.err.println("*** Server will start in 20 seconds ***"); + //Thread.sleep(TimeUnit.SECONDS.toMillis(20)); + // Paper End + } + } + + // Paper start - Log Java and OS versioning to help with debugging plugin issues + java.lang.management.RuntimeMXBean runtimeMX = java.lang.management.ManagementFactory.getRuntimeMXBean(); + java.lang.management.OperatingSystemMXBean osMX = java.lang.management.ManagementFactory.getOperatingSystemMXBean(); + if (runtimeMX != null && osMX != null) { + String javaInfo = "Java " + runtimeMX.getSpecVersion() + " (" + runtimeMX.getVmName() + " " + runtimeMX.getVmVersion() + ")"; + String osInfo = "Host: " + osMX.getName() + " " + osMX.getVersion() + " (" + osMX.getArch() + ")"; + + System.out.println("System Info: " + javaInfo + " " + osInfo); + } else { + System.out.println("Unable to read system info"); + } + // Paper end + + System.out.println("Loading libraries, please wait..."); + MinecraftServer.main(options); + } catch (Throwable t) { + t.printStackTrace(); + } + } + } + + private static List asList(String... params) { + return Arrays.asList(params); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/Overridden.java b/src/main/java/org/bukkit/craftbukkit/Overridden.java new file mode 100644 index 000000000000..1c19c69f840c --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/Overridden.java @@ -0,0 +1,14 @@ +package org.bukkit.craftbukkit; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates a method needs to be overridden in sub classes + */ +@Target({ElementType.CONSTRUCTOR, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Overridden { +} diff --git a/src/main/java/org/bukkit/craftbukkit/TrigMath.java b/src/main/java/org/bukkit/craftbukkit/TrigMath.java new file mode 100644 index 000000000000..6d613c53c6d0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/TrigMath.java @@ -0,0 +1,47 @@ +package org.bukkit.craftbukkit; + +/** + * Credits for this class goes to user aioobe on stackoverflow.com + * Source: http://stackoverflow.com/questions/4454630/j2me-calculate-the-the-distance-between-2-latitude-and-longitude + */ +public class TrigMath { + + static final double sq2p1 = 2.414213562373095048802e0; + static final double sq2m1 = .414213562373095048802e0; + static final double p4 = .161536412982230228262e2; + static final double p3 = .26842548195503973794141e3; + static final double p2 = .11530293515404850115428136e4; + static final double p1 = .178040631643319697105464587e4; + static final double p0 = .89678597403663861959987488e3; + static final double q4 = .5895697050844462222791e2; + static final double q3 = .536265374031215315104235e3; + static final double q2 = .16667838148816337184521798e4; + static final double q1 = .207933497444540981287275926e4; + static final double q0 = .89678597403663861962481162e3; + static final double PIO2 = 1.5707963267948966135E0; + + private static double mxatan(double arg) { + double argsq = arg * arg, value; + + value = ((((p4 * argsq + p3) * argsq + p2) * argsq + p1) * argsq + p0); + value = value / (((((argsq + q4) * argsq + q3) * argsq + q2) * argsq + q1) * argsq + q0); + return value * arg; + } + + private static double msatan(double arg) { + return arg < sq2m1 ? mxatan(arg) + : arg > sq2p1 ? PIO2 - mxatan(1 / arg) + : PIO2 / 2 + mxatan((arg - 1) / (arg + 1)); + } + + public static double atan(double arg) { + return arg > 0 ? msatan(arg) : -msatan(-arg); + } + + public static double atan2(double arg1, double arg2) { + if (arg1 + arg2 == arg1) + return arg1 >= 0 ? PIO2 : -PIO2; + arg1 = atan(arg1 / arg2); + return arg2 < 0 ? arg1 <= 0 ? arg1 + Math.PI : arg1 - Math.PI : arg1; + } +} \ No newline at end of file diff --git a/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancement.java b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancement.java new file mode 100644 index 000000000000..a5aadf2850f2 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancement.java @@ -0,0 +1,30 @@ +package org.bukkit.craftbukkit.advancement; + +import java.util.Collection; +import java.util.Collections; +import net.minecraft.server.Advancement; +import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; + +public class CraftAdvancement implements org.bukkit.advancement.Advancement { + + private final Advancement handle; + + public CraftAdvancement(Advancement handle) { + this.handle = handle; + } + + public Advancement getHandle() { + return handle; + } + + @Override + public NamespacedKey getKey() { + return CraftNamespacedKey.fromMinecraft(handle.getName()); + } + + @Override + public Collection getCriteria() { + return Collections.unmodifiableCollection(handle.getCriteria().keySet()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementProgress.java b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementProgress.java new file mode 100644 index 000000000000..60fc5aff8069 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementProgress.java @@ -0,0 +1,59 @@ +package org.bukkit.craftbukkit.advancement; + +import com.google.common.collect.Lists; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import net.minecraft.server.AdvancementDataPlayer; +import net.minecraft.server.CriterionProgress; +import org.bukkit.advancement.Advancement; +import org.bukkit.advancement.AdvancementProgress; + +public class CraftAdvancementProgress implements AdvancementProgress { + + private final CraftAdvancement advancement; + private final AdvancementDataPlayer playerData; + private final net.minecraft.server.AdvancementProgress handle; + + public CraftAdvancementProgress(CraftAdvancement advancement, AdvancementDataPlayer player, net.minecraft.server.AdvancementProgress handle) { + this.advancement = advancement; + this.playerData = player; + this.handle = handle; + } + + @Override + public Advancement getAdvancement() { + return advancement; + } + + @Override + public boolean isDone() { + return handle.isDone(); + } + + @Override + public boolean awardCriteria(String criteria) { + return playerData.grantCriteria(advancement.getHandle(), criteria); + } + + @Override + public boolean revokeCriteria(String criteria) { + return playerData.revokeCritera(advancement.getHandle(), criteria); + } + + @Override + public Date getDateAwarded(String criteria) { + CriterionProgress criterion = handle.getCriterionProgress(criteria); + return (criterion == null) ? null : criterion.getDate(); + } + + @Override + public Collection getRemainingCriteria() { + return Collections.unmodifiableCollection(Lists.newArrayList(handle.getRemainingCriteria())); + } + + @Override + public Collection getAwardedCriteria() { + return Collections.unmodifiableCollection(Lists.newArrayList(handle.getAwardedCriteria())); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttributeInstance.java b/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttributeInstance.java new file mode 100644 index 000000000000..07b457e8a312 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttributeInstance.java @@ -0,0 +1,75 @@ +package org.bukkit.craftbukkit.attribute; + +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeInstance; +import org.bukkit.attribute.AttributeModifier; + +public class CraftAttributeInstance implements AttributeInstance { + + private final net.minecraft.server.AttributeInstance handle; + private final Attribute attribute; + + public CraftAttributeInstance(net.minecraft.server.AttributeInstance handle, Attribute attribute) { + this.handle = handle; + this.attribute = attribute; + } + + @Override + public Attribute getAttribute() { + return attribute; + } + + @Override + public double getBaseValue() { + return handle.b(); + } + + @Override + public void setBaseValue(double d) { + handle.setValue(d); + } + + @Override + public Collection getModifiers() { + List result = new ArrayList(); + for (net.minecraft.server.AttributeModifier nms : handle.c()) { + result.add(convert(nms)); + } + + return result; + } + + @Override + public void addModifier(AttributeModifier modifier) { + Preconditions.checkArgument(modifier != null, "modifier"); + handle.b(convert(modifier)); + } + + @Override + public void removeModifier(AttributeModifier modifier) { + Preconditions.checkArgument(modifier != null, "modifier"); + handle.c(convert(modifier)); + } + + @Override + public double getValue() { + return handle.getValue(); + } + + @Override + public double getDefaultValue() { + return handle.getAttribute().getDefault(); + } + + public static net.minecraft.server.AttributeModifier convert(AttributeModifier bukkit) { + return new net.minecraft.server.AttributeModifier(bukkit.getUniqueId(), bukkit.getName(), bukkit.getAmount(), bukkit.getOperation().ordinal()); + } + + public static AttributeModifier convert(net.minecraft.server.AttributeModifier nms) { + return new AttributeModifier(nms.a(), nms.b(), nms.d(), AttributeModifier.Operation.values()[nms.c()]); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttributeMap.java b/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttributeMap.java new file mode 100644 index 000000000000..77e584b129ab --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttributeMap.java @@ -0,0 +1,55 @@ +package org.bukkit.craftbukkit.attribute; + +import com.google.common.base.CaseFormat; +import com.google.common.base.Preconditions; +import java.util.Locale; +import net.minecraft.server.AttributeMapBase; +import org.apache.commons.lang3.EnumUtils; +import org.bukkit.attribute.Attributable; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeInstance; + +public class CraftAttributeMap implements Attributable { + + private final AttributeMapBase handle; + + public CraftAttributeMap(AttributeMapBase handle) { + this.handle = handle; + } + + @Override + public AttributeInstance getAttribute(Attribute attribute) { + Preconditions.checkArgument(attribute != null, "attribute"); + net.minecraft.server.AttributeInstance nms = handle.a(toMinecraft(attribute.name())); + + return (nms == null) ? null : new CraftAttributeInstance(nms, attribute); + } + + public static String toMinecraft(String bukkit) { + int first = bukkit.indexOf('_'); + int second = bukkit.indexOf('_', first + 1); + + StringBuilder sb = new StringBuilder(bukkit.toLowerCase(java.util.Locale.ENGLISH)); + + sb.setCharAt(first, '.'); + if (second != -1) { + sb.deleteCharAt(second); + sb.setCharAt(second, bukkit.charAt(second + 1)); + } + + return sb.toString(); + } + + public static String toMinecraft(Attribute attribute) { + return toMinecraft(attribute.name()); + } + + public static Attribute fromMinecraft(String nms) { + String[] split = nms.split("\\.", 2); + + String generic = split[0]; + String descriptor = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, split[1]); // movementSpeed -> MOVEMENT_SPEED + String fin = generic + "_" + descriptor; + return EnumUtils.getEnum(Attribute.class, fin.toUpperCase(Locale.ROOT)); // so we can return null without throwing exceptions + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBanner.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBanner.java new file mode 100644 index 000000000000..1939a881e665 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBanner.java @@ -0,0 +1,109 @@ +package org.bukkit.craftbukkit.block; + +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.List; +import net.minecraft.server.EnumColor; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagList; +import net.minecraft.server.TileEntityBanner; +import org.bukkit.DyeColor; +import org.bukkit.Material; +import org.bukkit.block.Banner; +import org.bukkit.block.Block; +import org.bukkit.block.banner.Pattern; +import org.bukkit.block.banner.PatternType; + +public class CraftBanner extends CraftBlockEntityState implements Banner { + + private DyeColor base; + private List patterns; + + public CraftBanner(final Block block) { + super(block, TileEntityBanner.class); + } + + public CraftBanner(final Material material, final TileEntityBanner te) { + super(material, te); + } + + @Override + public void load(TileEntityBanner banner) { + super.load(banner); + + if (banner.color != null) { + base = DyeColor.getByWoolData((byte) banner.color.getColorIndex()); + } + patterns = new ArrayList(); + + if (banner.patterns != null) { + for (int i = 0; i < banner.patterns.size(); i++) { + NBTTagCompound p = (NBTTagCompound) banner.patterns.get(i); + patterns.add(new Pattern(DyeColor.getByWoolData((byte) p.getInt("Color")), PatternType.getByIdentifier(p.getString("Pattern")))); + } + } + } + + @Override + public DyeColor getBaseColor() { + return this.base; + } + + @Override + public void setBaseColor(DyeColor color) { + Preconditions.checkArgument(color != null, "color"); + this.base = color; + } + + @Override + public List getPatterns() { + return new ArrayList(patterns); + } + + @Override + public void setPatterns(List patterns) { + this.patterns = new ArrayList(patterns); + } + + @Override + public void addPattern(Pattern pattern) { + this.patterns.add(pattern); + } + + @Override + public Pattern getPattern(int i) { + return this.patterns.get(i); + } + + @Override + public Pattern removePattern(int i) { + return this.patterns.remove(i); + } + + @Override + public void setPattern(int i, Pattern pattern) { + this.patterns.set(i, pattern); + } + + @Override + public int numberOfPatterns() { + return patterns.size(); + } + + @Override + public void applyTo(TileEntityBanner banner) { + super.applyTo(banner); + + banner.color = EnumColor.fromColorIndex(base.getWoolData()); + + NBTTagList newPatterns = new NBTTagList(); + + for (Pattern p : patterns) { + NBTTagCompound compound = new NBTTagCompound(); + compound.setInt("Color", p.getColor().getWoolData()); + compound.setString("Pattern", p.getPattern().getIdentifier()); + newPatterns.add(compound); + } + banner.patterns = newPatterns; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBeacon.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBeacon.java new file mode 100644 index 000000000000..5b908d3bf5ac --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBeacon.java @@ -0,0 +1,98 @@ +package org.bukkit.craftbukkit.block; + +import java.util.ArrayList; +import java.util.Collection; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.MobEffectList; +import net.minecraft.server.TileEntity; +import net.minecraft.server.TileEntityBeacon; +import org.bukkit.Material; +import org.bukkit.block.Beacon; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.entity.LivingEntity; +import org.bukkit.inventory.BeaconInventory; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +public class CraftBeacon extends CraftContainer implements Beacon { + + public CraftBeacon(final Block block) { + super(block, TileEntityBeacon.class); + } + + public CraftBeacon(final Material material, final TileEntityBeacon te) { + super(material, te); + } + + @Override + public BeaconInventory getSnapshotInventory() { + return new CraftInventoryBeacon(this.getSnapshot()); + } + + @Override + public BeaconInventory getInventory() { + if (!this.isPlaced()) { + return this.getSnapshotInventory(); + } + + return new CraftInventoryBeacon(this.getTileEntity()); + } + + @Override + public Collection getEntitiesInRange() { + TileEntity tileEntity = this.getTileEntityFromWorld(); + if (tileEntity instanceof TileEntityBeacon) { + TileEntityBeacon beacon = (TileEntityBeacon) tileEntity; + + Collection nms = beacon.getHumansInRange(); + Collection bukkit = new ArrayList(nms.size()); + + for (EntityHuman human : nms) { + bukkit.add(human.getBukkitEntity()); + } + + return bukkit; + } + + // block is no longer a beacon + return new ArrayList(); + } + + @Override + public int getTier() { + return this.getSnapshot().levels; + } + + @Override + public PotionEffect getPrimaryEffect() { + return this.getSnapshot().getPrimaryEffect(); + } + + @Override + public void setPrimaryEffect(PotionEffectType effect) { + this.getSnapshot().primaryEffect = (effect != null) ? MobEffectList.fromId(effect.getId()) : null; + } + + @Override + public PotionEffect getSecondaryEffect() { + return this.getSnapshot().getSecondaryEffect(); + } + + @Override + public void setSecondaryEffect(PotionEffectType effect) { + this.getSnapshot().secondaryEffect = (effect != null) ? MobEffectList.fromId(effect.getId()) : null; + } + + @Override + public String getCustomName() { + TileEntityBeacon beacon = this.getSnapshot(); + return beacon.hasCustomName() ? CraftChatMessage.fromComponent(beacon.getCustomName()) : null; + } + + @Override + public void setCustomName(String name) { + this.getSnapshot().setCustomName(CraftChatMessage.fromStringOrNull(name)); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBed.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBed.java new file mode 100644 index 000000000000..100ed8a8feb0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBed.java @@ -0,0 +1,63 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityBed; +import org.bukkit.DyeColor; +import org.bukkit.Material; +import org.bukkit.block.Bed; +import org.bukkit.block.Block; + +public class CraftBed extends CraftBlockEntityState implements Bed { + + public CraftBed(Block block) { + super(block, TileEntityBed.class); + } + + public CraftBed(Material material, TileEntityBed te) { + super(material, te); + } + + @Override + public DyeColor getColor() { + switch (getType()) { + case BLACK_BED: + return DyeColor.BLACK; + case BLUE_BED: + return DyeColor.BLUE; + case BROWN_BED: + return DyeColor.BROWN; + case CYAN_BED: + return DyeColor.CYAN; + case GRAY_BED: + return DyeColor.GRAY; + case GREEN_BED: + return DyeColor.GREEN; + case LIGHT_BLUE_BED: + return DyeColor.LIGHT_BLUE; + case LIGHT_GRAY_BED: + return DyeColor.LIGHT_GRAY; + case LIME_BED: + return DyeColor.LIME; + case MAGENTA_BED: + return DyeColor.MAGENTA; + case ORANGE_BED: + return DyeColor.ORANGE; + case PINK_BED: + return DyeColor.PINK; + case PURPLE_BED: + return DyeColor.PURPLE; + case RED_BED: + return DyeColor.RED; + case WHITE_BED: + return DyeColor.WHITE; + case YELLOW_BED: + return DyeColor.YELLOW; + default: + throw new IllegalArgumentException("Unknown DyeColor for " + getType()); + } + } + + @Override + public void setColor(DyeColor color) { + throw new UnsupportedOperationException("Must set block type to appropriate bed colour"); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java new file mode 100644 index 000000000000..7ae4b7952f8b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java @@ -0,0 +1,678 @@ +package org.bukkit.craftbukkit.block; + +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import net.minecraft.server.*; + +import org.apache.commons.lang.Validate; +import org.bukkit.Chunk; +import org.bukkit.FluidCollisionMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.PistonMoveReaction; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.CraftFluidCollisionMode; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.craftbukkit.util.CraftRayTraceResult; +import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.plugin.Plugin; +import org.bukkit.util.BlockVector; +import org.bukkit.util.BoundingBox; +import org.bukkit.util.RayTraceResult; +import org.bukkit.util.Vector; + +public class CraftBlock implements Block { + private final net.minecraft.server.GeneratorAccess world; + private final BlockPosition position; + + public CraftBlock(GeneratorAccess world, BlockPosition position) { + this.world = world; + this.position = position.h(); + } + + public static CraftBlock at(GeneratorAccess world, BlockPosition position) { + return new CraftBlock(world, position); + } + + private net.minecraft.server.Block getNMSBlock() { + return getNMS().getBlock(); + } + + public net.minecraft.server.IBlockData getNMS() { + return world.getType(position); + } + + public BlockPosition getPosition() { + return position; + } + + public World getWorld() { + return world.getMinecraftWorld().getWorld(); + } + + public CraftWorld getCraftWorld() { + return (CraftWorld) getWorld(); + } + + public Location getLocation() { + return new Location(getWorld(), position.getX(), position.getY(), position.getZ()); + } + + public Location getLocation(Location loc) { + if (loc != null) { + loc.setWorld(getWorld()); + loc.setX(position.getX()); + loc.setY(position.getY()); + loc.setZ(position.getZ()); + loc.setYaw(0); + loc.setPitch(0); + } + + return loc; + } + + public BlockVector getVector() { + return new BlockVector(getX(), getY(), getZ()); + } + + public int getX() { + return position.getX(); + } + + public int getY() { + return position.getY(); + } + + public int getZ() { + return position.getZ(); + } + + public Chunk getChunk() { + return getWorld().getChunkAt(this); + } + + public void setData(final byte data) { + setData(data, 3); + } + + public void setData(final byte data, boolean applyPhysics) { + if (applyPhysics) { + setData(data, 3); + } else { + setData(data, 2); + } + } + + private void setData(final byte data, int flag) { + world.setTypeAndData(position, CraftMagicNumbers.getBlock(getType(), data), flag); + } + + private IBlockData getData0() { + return world.getType(position); + } + + public byte getData() { + IBlockData blockData = world.getType(position); + return CraftMagicNumbers.toLegacyData(blockData); + } + + @Override + public BlockData getBlockData() { + return CraftBlockData.fromData(getData0()); + } + + public void setType(final Material type) { + setType(type, true); + } + + @Override + public void setType(Material type, boolean applyPhysics) { + Preconditions.checkArgument(type != null, "Material cannot be null"); + setBlockData(type.createBlockData(), applyPhysics); + } + + @Override + public void setBlockData(BlockData data) { + setBlockData(data, true); + } + + @Override + public void setBlockData(BlockData data, boolean applyPhysics) { + Preconditions.checkArgument(data != null, "BlockData cannot be null"); + setTypeAndData(((CraftBlockData) data).getState(), applyPhysics); + } + + public boolean setTypeAndData(final IBlockData blockData, final boolean applyPhysics) { + // SPIGOT-611: need to do this to prevent glitchiness. Easier to handle this here (like /setblock) than to fix weirdness in tile entity cleanup + if (!blockData.isAir() && blockData.getBlock() instanceof BlockTileEntity && blockData.getBlock() != getNMSBlock()) { + world.setTypeAndData(position, Blocks.AIR.getBlockData(), 0); + } + + if (applyPhysics) { + return world.setTypeAndData(position, blockData, 3); + } else { + IBlockData old = world.getType(position); + boolean success = world.setTypeAndData(position, blockData, 2 | 16 | 1024); // NOTIFY | NO_OBSERVER | NO_PLACE (custom) + if (success) { + world.getMinecraftWorld().notify( + position, + old, + blockData, + 3 + ); + } + return success; + } + } + + public Material getType() { + return CraftMagicNumbers.getMaterial(world.getType(position).getBlock()); + } + + public byte getLightLevel() { + return (byte) world.getMinecraftWorld().getLightLevel(position); + } + + public byte getLightFromSky() { + return (byte) world.getBrightness(EnumSkyBlock.SKY, position); + } + + public byte getLightFromBlocks() { + return (byte) world.getBrightness(EnumSkyBlock.BLOCK, position); + } + + + public Block getFace(final BlockFace face) { + return getRelative(face, 1); + } + + public Block getFace(final BlockFace face, final int distance) { + return getRelative(face, distance); + } + + public Block getRelative(final int modX, final int modY, final int modZ) { + return getWorld().getBlockAt(getX() + modX, getY() + modY, getZ() + modZ); + } + + public Block getRelative(BlockFace face) { + return getRelative(face, 1); + } + + public Block getRelative(BlockFace face, int distance) { + return getRelative(face.getModX() * distance, face.getModY() * distance, face.getModZ() * distance); + } + + public BlockFace getFace(final Block block) { + BlockFace[] values = BlockFace.values(); + + for (BlockFace face : values) { + if ((this.getX() + face.getModX() == block.getX()) && + (this.getY() + face.getModY() == block.getY()) && + (this.getZ() + face.getModZ() == block.getZ()) + ) { + return face; + } + } + + return null; + } + + @Override + public String toString() { + return "CraftBlock{pos=" + position + ",type=" + getType() + ",data=" + getNMS() + ",fluid=" + world.getFluid(position) + '}'; + } + + public static BlockFace notchToBlockFace(EnumDirection notch) { + if (notch == null) return BlockFace.SELF; + switch (notch) { + case DOWN: + return BlockFace.DOWN; + case UP: + return BlockFace.UP; + case NORTH: + return BlockFace.NORTH; + case SOUTH: + return BlockFace.SOUTH; + case WEST: + return BlockFace.WEST; + case EAST: + return BlockFace.EAST; + default: + return BlockFace.SELF; + } + } + + public static EnumDirection blockFaceToNotch(BlockFace face) { + switch (face) { + case DOWN: + return EnumDirection.DOWN; + case UP: + return EnumDirection.UP; + case NORTH: + return EnumDirection.NORTH; + case SOUTH: + return EnumDirection.SOUTH; + case WEST: + return EnumDirection.WEST; + case EAST: + return EnumDirection.EAST; + default: + return null; + } + } + + public BlockState getState() { + // Paper start - allow disabling the use of snapshots + return getState(true); + } + public BlockState getState(boolean useSnapshot) { + boolean prev = CraftBlockEntityState.DISABLE_SNAPSHOT; + CraftBlockEntityState.DISABLE_SNAPSHOT = !useSnapshot; + try { + return getState0(); + } finally { + CraftBlockEntityState.DISABLE_SNAPSHOT = prev; + } + } + public BlockState getState0() { + // Paper end + Material material = getType(); + + switch (material) { + case SIGN: + case WALL_SIGN: + return new CraftSign(this); + case CHEST: + case TRAPPED_CHEST: + return new CraftChest(this); + case FURNACE: + return new CraftFurnace(this); + case DISPENSER: + return new CraftDispenser(this); + case DROPPER: + return new CraftDropper(this); + case END_GATEWAY: + return new CraftEndGateway(this); + case HOPPER: + return new CraftHopper(this); + case SPAWNER: + return new CraftCreatureSpawner(this); + case JUKEBOX: + return new CraftJukebox(this); + case BREWING_STAND: + return new CraftBrewingStand(this); + case CREEPER_HEAD: + case CREEPER_WALL_HEAD: + case DRAGON_HEAD: + case DRAGON_WALL_HEAD: + case PLAYER_HEAD: + case PLAYER_WALL_HEAD: + case SKELETON_SKULL: + case SKELETON_WALL_SKULL: + case WITHER_SKELETON_SKULL: + case WITHER_SKELETON_WALL_SKULL: + case ZOMBIE_HEAD: + case ZOMBIE_WALL_HEAD: + return new CraftSkull(this); + case COMMAND_BLOCK: + case CHAIN_COMMAND_BLOCK: + case REPEATING_COMMAND_BLOCK: + return new CraftCommandBlock(this); + case BEACON: + return new CraftBeacon(this); + case BLACK_BANNER: + case BLACK_WALL_BANNER: + case BLUE_BANNER: + case BLUE_WALL_BANNER: + case BROWN_BANNER: + case BROWN_WALL_BANNER: + case CYAN_BANNER: + case CYAN_WALL_BANNER: + case GRAY_BANNER: + case GRAY_WALL_BANNER: + case GREEN_BANNER: + case GREEN_WALL_BANNER: + case LIGHT_BLUE_BANNER: + case LIGHT_BLUE_WALL_BANNER: + case LIGHT_GRAY_BANNER: + case LIGHT_GRAY_WALL_BANNER: + case LIME_BANNER: + case LIME_WALL_BANNER: + case MAGENTA_BANNER: + case MAGENTA_WALL_BANNER: + case ORANGE_BANNER: + case ORANGE_WALL_BANNER: + case PINK_BANNER: + case PINK_WALL_BANNER: + case PURPLE_BANNER: + case PURPLE_WALL_BANNER: + case RED_BANNER: + case RED_WALL_BANNER: + case WHITE_BANNER: + case WHITE_WALL_BANNER: + case YELLOW_BANNER: + case YELLOW_WALL_BANNER: + return new CraftBanner(this); + case STRUCTURE_BLOCK: + return new CraftStructureBlock(this); + case SHULKER_BOX: + case WHITE_SHULKER_BOX: + case ORANGE_SHULKER_BOX: + case MAGENTA_SHULKER_BOX: + case LIGHT_BLUE_SHULKER_BOX: + case YELLOW_SHULKER_BOX: + case LIME_SHULKER_BOX: + case PINK_SHULKER_BOX: + case GRAY_SHULKER_BOX: + case LIGHT_GRAY_SHULKER_BOX: + case CYAN_SHULKER_BOX: + case PURPLE_SHULKER_BOX: + case BLUE_SHULKER_BOX: + case BROWN_SHULKER_BOX: + case GREEN_SHULKER_BOX: + case RED_SHULKER_BOX: + case BLACK_SHULKER_BOX: + return new CraftShulkerBox(this); + case ENCHANTING_TABLE: + return new CraftEnchantingTable(this); + case ENDER_CHEST: + return new CraftEnderChest(this); + case DAYLIGHT_DETECTOR: + return new CraftDaylightDetector(this); + case COMPARATOR: + return new CraftComparator(this); + case BLACK_BED: + case BLUE_BED: + case BROWN_BED: + case CYAN_BED: + case GRAY_BED: + case GREEN_BED: + case LIGHT_BLUE_BED: + case LIGHT_GRAY_BED: + case LIME_BED: + case MAGENTA_BED: + case ORANGE_BED: + case PINK_BED: + case PURPLE_BED: + case RED_BED: + case WHITE_BED: + case YELLOW_BED: + return new CraftBed(this); + case CONDUIT: + return new CraftConduit(this); + default: + TileEntity tileEntity = world.getTileEntity(position); + if (tileEntity != null) { + // block with unhandled TileEntity: + return new CraftBlockEntityState(this, (Class) tileEntity.getClass()); + } else { + // Block without TileEntity: + return new CraftBlockState(this); + } + } + } + + public Biome getBiome() { + return getWorld().getBiome(getX(), getZ()); + } + + public void setBiome(Biome bio) { + getWorld().setBiome(getX(), getZ(), bio); + } + + public static Biome biomeBaseToBiome(BiomeBase base) { + if (base == null) { + return null; + } + + return Biome.valueOf(IRegistry.BIOME.getKey(base).getKey().toUpperCase(java.util.Locale.ENGLISH)); + } + + public static BiomeBase biomeToBiomeBase(Biome bio) { + if (bio == null) { + return null; + } + + return IRegistry.BIOME.get(new MinecraftKey(bio.name().toLowerCase(java.util.Locale.ENGLISH))); + } + + public double getTemperature() { + return world.getBiome(position).getAdjustedTemperature(position); + } + + public double getHumidity() { + return getWorld().getHumidity(getX(), getZ()); + } + + public boolean isBlockPowered() { + return world.getMinecraftWorld().getBlockPower(position) > 0; + } + + public boolean isBlockIndirectlyPowered() { + return world.getMinecraftWorld().isBlockIndirectlyPowered(position); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof CraftBlock)) return false; + CraftBlock other = (CraftBlock) o; + + return this.position.equals(other.position) && this.getWorld().equals(other.getWorld()); + } + + @Override + public int hashCode() { + return this.position.hashCode() ^ this.getWorld().hashCode(); + } + + public boolean isBlockFacePowered(BlockFace face) { + return world.getMinecraftWorld().isBlockFacePowered(position, blockFaceToNotch(face)); + } + + public boolean isBlockFaceIndirectlyPowered(BlockFace face) { + int power = world.getMinecraftWorld().getBlockFacePower(position, blockFaceToNotch(face)); + + Block relative = getRelative(face); + if (relative.getType() == Material.REDSTONE_WIRE) { + return Math.max(power, relative.getData()) > 0; + } + + return power > 0; + } + + public int getBlockPower(BlockFace face) { + int power = 0; + BlockRedstoneWire wire = (BlockRedstoneWire) Blocks.REDSTONE_WIRE; + net.minecraft.server.World world = this.world.getMinecraftWorld(); + int x = getX(); + int y = getY(); + int z = getZ(); + if ((face == BlockFace.DOWN || face == BlockFace.SELF) && world.isBlockFacePowered(new BlockPosition(x, y - 1, z), EnumDirection.DOWN)) power = wire.getPower(power, world.getType(new BlockPosition(x, y - 1, z))); + if ((face == BlockFace.UP || face == BlockFace.SELF) && world.isBlockFacePowered(new BlockPosition(x, y + 1, z), EnumDirection.UP)) power = wire.getPower(power, world.getType(new BlockPosition(x, y + 1, z))); + if ((face == BlockFace.EAST || face == BlockFace.SELF) && world.isBlockFacePowered(new BlockPosition(x + 1, y, z), EnumDirection.EAST)) power = wire.getPower(power, world.getType(new BlockPosition(x + 1, y, z))); + if ((face == BlockFace.WEST || face == BlockFace.SELF) && world.isBlockFacePowered(new BlockPosition(x - 1, y, z), EnumDirection.WEST)) power = wire.getPower(power, world.getType(new BlockPosition(x - 1, y, z))); + if ((face == BlockFace.NORTH || face == BlockFace.SELF) && world.isBlockFacePowered(new BlockPosition(x, y, z - 1), EnumDirection.NORTH)) power = wire.getPower(power, world.getType(new BlockPosition(x, y, z - 1))); + if ((face == BlockFace.SOUTH || face == BlockFace.SELF) && world.isBlockFacePowered(new BlockPosition(x, y, z + 1), EnumDirection.SOUTH)) power = wire.getPower(power, world.getType(new BlockPosition(x, y, z + 1))); + return power > 0 ? power : (face == BlockFace.SELF ? isBlockIndirectlyPowered() : isBlockFaceIndirectlyPowered(face)) ? 15 : 0; + } + + public int getBlockPower() { + return getBlockPower(BlockFace.SELF); + } + + public boolean isEmpty() { + return getNMS().isAir(); + } + + public boolean isLiquid() { + return (getType() == Material.WATER) || (getType() == Material.LAVA); + } + + public PistonMoveReaction getPistonMoveReaction() { + return PistonMoveReaction.getById(getNMS().getPushReaction().ordinal()); + } + + private boolean itemCausesDrops(ItemStack item) { + net.minecraft.server.Block block = this.getNMSBlock(); + net.minecraft.server.Item itemType = CraftMagicNumbers.getItem(item.getType()); + return block != null && (block.getBlockData().getMaterial().isAlwaysDestroyable() || (itemType != null && itemType.canDestroySpecialBlock(block.getBlockData()))); + } + + public boolean breakNaturally() { + // Order matters here, need to drop before setting to air so skulls can get their data + net.minecraft.server.Block block = this.getNMSBlock(); + boolean result = false; + + if (block != null && block != Blocks.AIR) { + block.dropNaturally(getNMS(), world.getMinecraftWorld(), position, 1.0F, 0); + result = true; + } + + setType(Material.AIR); + return result; + } + + public boolean breakNaturally(ItemStack item) { + if (itemCausesDrops(item)) { + return breakNaturally(); + } else { + return setTypeAndData(Blocks.AIR.getBlockData(), true); + } + } + + public Collection getDrops() { + List drops = new ArrayList(); + + net.minecraft.server.Block block = this.getNMSBlock(); + if (block != Blocks.AIR) { + IBlockData data = getData0(); + // based on nms.Block.dropNaturally + int count = block.getDropCount(data, 0, world.getMinecraftWorld(), position, world.getMinecraftWorld().random); + for (int i = 0; i < count; ++i) { + Item item = block.getDropType(data, world.getMinecraftWorld(), position, 0).getItem(); + if (item != Items.AIR) { + // Skulls are special, their data is based on the tile entity + if (block instanceof BlockSkullAbstract) { + net.minecraft.server.ItemStack nmsStack = block.a((IBlockAccess) world, position, data); + TileEntitySkull tileentityskull = (TileEntitySkull) world.getTileEntity(position); + + if ((block == Blocks.PLAYER_HEAD || block == Blocks.PLAYER_WALL_HEAD) && tileentityskull.getGameProfile() != null) { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + GameProfileSerializer.serialize(nbttagcompound, tileentityskull.getGameProfile()); + nmsStack.getOrCreateTag().set("SkullOwner", nbttagcompound); + } + + drops.add(CraftItemStack.asBukkitCopy(nmsStack)); + // We don't want to drop cocoa blocks, we want to drop cocoa beans. + } else if (Blocks.COCOA == block) { + int age = (Integer) data.get(BlockCocoa.AGE); + int dropAmount = (age >= 2 ? 3 : 1); + for (int j = 0; j < dropAmount; ++j) { + drops.add(new ItemStack(Material.COCOA_BEANS, 1)); + } + } else { + drops.add(new ItemStack(org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(item), 1)); + } + } + } + } + return drops; + } + + public Collection getDrops(ItemStack item) { + if (itemCausesDrops(item)) { + return getDrops(); + } else { + return Collections.emptyList(); + } + } + + public void setMetadata(String metadataKey, MetadataValue newMetadataValue) { + getCraftWorld().getBlockMetadata().setMetadata(this, metadataKey, newMetadataValue); + } + + public List getMetadata(String metadataKey) { + return getCraftWorld().getBlockMetadata().getMetadata(this, metadataKey); + } + + public boolean hasMetadata(String metadataKey) { + return getCraftWorld().getBlockMetadata().hasMetadata(this, metadataKey); + } + + public void removeMetadata(String metadataKey, Plugin owningPlugin) { + getCraftWorld().getBlockMetadata().removeMetadata(this, metadataKey, owningPlugin); + } + + @Override + public boolean isPassable() { + return this.getData0().getCollisionShape(world, position).isEmpty(); + } + + @Override + public RayTraceResult rayTrace(Location start, Vector direction, double maxDistance, FluidCollisionMode fluidCollisionMode) { + Validate.notNull(start, "Start location is null!"); + Validate.isTrue(this.getWorld().equals(start.getWorld()), "Start location is from different world!"); + start.checkFinite(); + + Validate.notNull(direction, "Direction is null!"); + direction.checkFinite(); + Validate.isTrue(direction.lengthSquared() > 0, "Direction's magnitude is 0!"); + + Validate.notNull(fluidCollisionMode, "Fluid collision mode is null!"); + if (maxDistance < 0.0D) { + return null; + } + + Vector dir = direction.clone().normalize().multiply(maxDistance); + Vec3D startPos = new Vec3D(start.getX(), start.getY(), start.getZ()); + Vec3D endPos = new Vec3D(start.getX() + dir.getX(), start.getY() + dir.getY(), start.getZ() + dir.getZ()); + + // Similar to to nms.World#rayTrace: + IBlockData blockData = world.getType(position); + Fluid fluid = world.getFluid(position); + boolean collidableBlock = blockData.getBlock().isCollidable(blockData); + boolean collideWithFluid = CraftFluidCollisionMode.toNMS(fluidCollisionMode).predicate.test(fluid); + + if (!collidableBlock && !collideWithFluid) { + return null; + } + + MovingObjectPosition nmsHitResult = null; + if (collidableBlock) { + nmsHitResult = net.minecraft.server.Block.rayTrace(blockData, world.getMinecraftWorld(), position, startPos, endPos); + } + + if (nmsHitResult == null && collideWithFluid) { + nmsHitResult = VoxelShapes.create(0.0D, 0.0D, 0.0D, 1.0D, (double) fluid.getHeight(), 1.0D).rayTrace(startPos, endPos, position); + } + + return CraftRayTraceResult.fromNMS(this.getWorld(), nmsHitResult); + } + + @Override + public BoundingBox getBoundingBox() { + VoxelShape shape = getData0().getShape(world, position); + + if (shape.isEmpty()) { + return new BoundingBox(); // Return an empty bounding box if the block has no dimension + } + + AxisAlignedBB aabb = shape.getBoundingBox(); + return new BoundingBox(getX() + aabb.minX, getY() + aabb.minY, getZ() + aabb.minZ, getX() + aabb.maxX, getY() + aabb.maxY, getZ() + aabb.maxZ); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java new file mode 100644 index 000000000000..2b78fff9f56f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java @@ -0,0 +1,145 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.BlockPosition; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.TileEntity; +import net.minecraft.server.World; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.CraftWorld; + +public class CraftBlockEntityState extends CraftBlockState { + + private final Class tileEntityClass; + private final T tileEntity; + private final T snapshot; + + public CraftBlockEntityState(Block block, Class tileEntityClass) { + super(block); + + this.tileEntityClass = tileEntityClass; + + // get tile entity from block: + CraftWorld world = (CraftWorld) this.getWorld(); + this.tileEntity = tileEntityClass.cast(world.getHandle().getTileEntity(this.getPosition())); + + // Paper start + this.snapshotDisabled = DISABLE_SNAPSHOT; + if (DISABLE_SNAPSHOT) { + this.snapshot = this.tileEntity; + } else { + this.snapshot = this.createSnapshot(this.tileEntity, world.getHandle()); + } + // copy tile entity data: + if(this.snapshot != null) { + this.load(this.snapshot); + } + // Paper end + } + + public final boolean snapshotDisabled; // Paper + public static boolean DISABLE_SNAPSHOT = false; // Paper + + public CraftBlockEntityState(Material material, T tileEntity) { + super(material); + + this.tileEntityClass = (Class) tileEntity.getClass(); + this.tileEntity = tileEntity; + // Paper start + this.snapshotDisabled = DISABLE_SNAPSHOT; + if (DISABLE_SNAPSHOT) { + this.snapshot = this.tileEntity; + } else { + this.snapshot = this.createSnapshot(this.tileEntity, null); + } + // copy tile entity data: + if(this.snapshot != null) { + this.load(this.snapshot); + } + // Paper end + } + + private T createSnapshot(T tileEntity, World world) { + if (tileEntity == null) { + return null; + } + + NBTTagCompound nbtTagCompound = tileEntity.save(new NBTTagCompound()); + T snapshot = (T) TileEntity.create(nbtTagCompound, world); + + return snapshot; + } + + // copies the TileEntity-specific data, retains the position + private void copyData(T from, T to) { + BlockPosition pos = to.getPosition(); + NBTTagCompound nbtTagCompound = from.save(new NBTTagCompound()); + to.load(nbtTagCompound); + + // reset the original position: + to.setPosition(pos); + } + + // gets the wrapped TileEntity + public T getTileEntity() { // Paper - protected -> public + return tileEntity; + } + + // gets the cloned TileEntity which is used to store the captured data + protected T getSnapshot() { + return snapshot; + } + + // gets the current TileEntity from the world at this position + protected TileEntity getTileEntityFromWorld() { + requirePlaced(); + + return ((CraftWorld) this.getWorld()).getHandle().getTileEntity(this.getPosition()); + } + + // gets the NBT data of the TileEntity represented by this block state + public NBTTagCompound getSnapshotNBT() { + // update snapshot + applyTo(snapshot); + + return snapshot.save(new NBTTagCompound()); + } + + // copies the data of the given tile entity to this block state + protected void load(T tileEntity) { + if (tileEntity != null && tileEntity != snapshot) { + copyData(tileEntity, snapshot); + } + } + + // applies the TileEntity data of this block state to the given TileEntity + protected void applyTo(T tileEntity) { + if (tileEntity != null && tileEntity != snapshot) { + copyData(snapshot, tileEntity); + } + } + + protected boolean isApplicable(TileEntity tileEntity) { + return tileEntityClass.isInstance(tileEntity); + } + + @Override + public boolean update(boolean force, boolean applyPhysics) { + boolean result = super.update(force, applyPhysics); + + if (result && this.isPlaced()) { + TileEntity tile = getTileEntityFromWorld(); + + if (isApplicable(tile)) { + // Paper start + if (!snapshotDisabled) { + applyTo(tileEntityClass.cast(tile)); + } + // Paper end + tile.update(); + } + } + + return result; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java new file mode 100644 index 000000000000..97385bd4d74e --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java @@ -0,0 +1,276 @@ +package org.bukkit.craftbukkit.block; + +import com.google.common.base.Preconditions; +import net.minecraft.server.BlockPosition; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.Chunk; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.CraftChunk; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.material.Attachable; +import org.bukkit.material.MaterialData; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.plugin.Plugin; + +import java.util.List; +import net.minecraft.server.GeneratorAccess; +import net.minecraft.server.IBlockData; + +public class CraftBlockState implements BlockState { + private final CraftWorld world; + private final CraftChunk chunk; + private final BlockPosition position; + protected IBlockData data; + protected int flag; + + public CraftBlockState(final Block block) { + this.world = (CraftWorld) block.getWorld(); + this.position = ((CraftBlock) block).getPosition(); + this.data = ((CraftBlock) block).getNMS(); + this.chunk = (CraftChunk) block.getChunk(); + this.flag = 3; + } + + public CraftBlockState(final Block block, int flag) { + this(block); + this.flag = flag; + } + + public CraftBlockState(Material material) { + world = null; + data = CraftMagicNumbers.getBlock(material).getBlockData(); + chunk = null; + position = BlockPosition.ZERO; + } + + public static CraftBlockState getBlockState(GeneratorAccess world, net.minecraft.server.BlockPosition pos) { + return new CraftBlockState(CraftBlock.at(world, pos)); + } + + public static CraftBlockState getBlockState(net.minecraft.server.World world, net.minecraft.server.BlockPosition pos, int flag) { + return new CraftBlockState(world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()), flag); + } + + public World getWorld() { + requirePlaced(); + return world; + } + + public int getX() { + return position.getX(); + } + + public int getY() { + return position.getY(); + } + + public int getZ() { + return position.getZ(); + } + + public Chunk getChunk() { + requirePlaced(); + return chunk; + } + + public void setData(IBlockData data) { + this.data = data; + } + + public BlockPosition getPosition() { + return this.position; + } + + public IBlockData getHandle() { + return this.data; + } + + @Override + public BlockData getBlockData() { + return CraftBlockData.fromData(data); + } + + @Override + public void setBlockData(BlockData data) { + Preconditions.checkArgument(data != null, "BlockData cannot be null"); + this.data = ((CraftBlockData) data).getState(); + } + + public void setData(final MaterialData data) { + Material mat = CraftMagicNumbers.getMaterial(this.data).getItemType(); + + if ((mat == null) || (mat.getData() == null)) { + this.data = CraftMagicNumbers.getBlock(data); + } else { + if ((data.getClass() == mat.getData()) || (data.getClass() == MaterialData.class)) { + this.data = CraftMagicNumbers.getBlock(data); + } else { + throw new IllegalArgumentException("Provided data is not of type " + + mat.getData().getName() + ", found " + data.getClass().getName()); + } + } + } + + public MaterialData getData() { + return CraftMagicNumbers.getMaterial(data); + } + + public void setType(final Material type) { + Preconditions.checkArgument(type != null, "Material cannot be null"); + Preconditions.checkArgument(type.isBlock(), "Material must be a block!"); + + if (this.getType() != type) { + this.data = CraftMagicNumbers.getBlock(type).getBlockData(); + } + } + + public Material getType() { + return CraftMagicNumbers.getMaterial(data.getBlock()); + } + + public void setFlag(int flag) { + this.flag = flag; + } + + public int getFlag() { + return flag; + } + + public byte getLightLevel() { + return getBlock().getLightLevel(); + } + + public CraftBlock getBlock() { + requirePlaced(); + return CraftBlock.at(world.getHandle(), position); + } + + public boolean update() { + return update(false); + } + + public boolean update(boolean force) { + return update(force, true); + } + + public boolean update(boolean force, boolean applyPhysics) { + if (!isPlaced()) { + return true; + } + CraftBlock block = getBlock(); + + if (block.getType() != getType()) { + if (!force) { + return false; + } + } + + IBlockData newBlock = this.data; + block.setTypeAndData(newBlock, applyPhysics); + world.getHandle().notify( + position, + block.getNMS(), + newBlock, + 3 + ); + + // Update levers etc + if (false && applyPhysics && getData() instanceof Attachable) { // Call does not map to new API + world.getHandle().applyPhysics(position.shift(CraftBlock.blockFaceToNotch(((Attachable) getData()).getAttachedFace())), newBlock.getBlock()); + } + + return true; + } + + public byte getRawData() { + return CraftMagicNumbers.toLegacyData(data); + } + + public Location getLocation() { + return new Location(world, getX(), getY(), getZ()); + } + + public Location getLocation(Location loc) { + if (loc != null) { + loc.setWorld(world); + loc.setX(getX()); + loc.setY(getY()); + loc.setZ(getZ()); + loc.setYaw(0); + loc.setPitch(0); + } + + return loc; + } + + public void setRawData(byte data) { + this.data = CraftMagicNumbers.getBlock(getType(), data); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final CraftBlockState other = (CraftBlockState) obj; + if (this.world != other.world && (this.world == null || !this.world.equals(other.world))) { + return false; + } + if (this.position != other.position && (this.position == null || !this.position.equals(other.position))) { + return false; + } + if (this.data != other.data && (this.data == null || !this.data.equals(other.data))) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 73 * hash + (this.world != null ? this.world.hashCode() : 0); + hash = 73 * hash + (this.position != null ? this.position.hashCode() : 0); + hash = 73 * hash + (this.data != null ? this.data.hashCode() : 0); + return hash; + } + + public void setMetadata(String metadataKey, MetadataValue newMetadataValue) { + requirePlaced(); + chunk.getCraftWorld().getBlockMetadata().setMetadata(getBlock(), metadataKey, newMetadataValue); + } + + public List getMetadata(String metadataKey) { + requirePlaced(); + return chunk.getCraftWorld().getBlockMetadata().getMetadata(getBlock(), metadataKey); + } + + public boolean hasMetadata(String metadataKey) { + requirePlaced(); + return chunk.getCraftWorld().getBlockMetadata().hasMetadata(getBlock(), metadataKey); + } + + public void removeMetadata(String metadataKey, Plugin owningPlugin) { + requirePlaced(); + chunk.getCraftWorld().getBlockMetadata().removeMetadata(getBlock(), metadataKey, owningPlugin); + } + + @Override + public boolean isPlaced() { + return world != null; + } + + protected void requirePlaced() { + if (!isPlaced()) { + throw new IllegalStateException("The blockState must be placed to call this method"); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBrewingStand.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBrewingStand.java new file mode 100644 index 000000000000..dafaa6fb3cf8 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBrewingStand.java @@ -0,0 +1,74 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityBrewingStand; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.BrewingStand; +import org.bukkit.craftbukkit.inventory.CraftInventoryBrewer; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.inventory.BrewerInventory; + +public class CraftBrewingStand extends CraftContainer implements BrewingStand { + + public CraftBrewingStand(Block block) { + super(block, TileEntityBrewingStand.class); + } + + public CraftBrewingStand(final Material material, final TileEntityBrewingStand te) { + super(material, te); + } + + @Override + public BrewerInventory getSnapshotInventory() { + return new CraftInventoryBrewer(this.getSnapshot()); + } + + @Override + public BrewerInventory getInventory() { + if (!this.isPlaced()) { + return this.getSnapshotInventory(); + } + + return new CraftInventoryBrewer(this.getTileEntity()); + } + + @Override + public int getBrewingTime() { + return this.getSnapshot().getProperty(0); + } + + @Override + public void setBrewingTime(int brewTime) { + this.getSnapshot().setProperty(0, brewTime); + } + + @Override + public int getFuelLevel() { + return this.getSnapshot().getProperty(1); + } + + @Override + public void setFuelLevel(int level) { + this.getSnapshot().setProperty(1, level); + } + + @Override + public String getCustomName() { + TileEntityBrewingStand brewingStand = this.getSnapshot(); + return brewingStand.hasCustomName() ? CraftChatMessage.fromComponent(brewingStand.getCustomName()) : null; + } + + @Override + public void setCustomName(String name) { + this.getSnapshot().setCustomName(CraftChatMessage.fromStringOrNull(name)); + } + + @Override + public void applyTo(TileEntityBrewingStand brewingStand) { + super.applyTo(brewingStand); + + if (!this.getSnapshot().hasCustomName()) { + brewingStand.setCustomName(null); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java b/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java new file mode 100644 index 000000000000..14b5be75f380 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java @@ -0,0 +1,60 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.BlockChest; +import net.minecraft.server.Blocks; +import net.minecraft.server.ITileInventory; +import net.minecraft.server.InventoryLargeChest; +import net.minecraft.server.TileEntityChest; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Chest; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest; +import org.bukkit.inventory.Inventory; +import com.destroystokyo.paper.loottable.PaperLootableBlockInventory; // Paper + +public class CraftChest extends CraftLootable implements Chest, PaperLootableBlockInventory { // Paper + + public CraftChest(final Block block) { + super(block, TileEntityChest.class); + } + + public CraftChest(final Material material, final TileEntityChest te) { + super(material, te); + } + + @Override + public Inventory getSnapshotInventory() { + return new CraftInventory(this.getSnapshot()); + } + + @Override + public Inventory getBlockInventory() { + if (!this.isPlaced()) { + return this.getSnapshotInventory(); + } + + return new CraftInventory(this.getTileEntity()); + } + + @Override + public Inventory getInventory() { + CraftInventory inventory = (CraftInventory) this.getBlockInventory(); + if (!isPlaced()) { + return inventory; + } + + // The logic here is basically identical to the logic in BlockChest.interact + CraftWorld world = (CraftWorld) this.getWorld(); + + BlockChest blockChest = (BlockChest) (this.getType() == Material.CHEST ? Blocks.CHEST : Blocks.TRAPPED_CHEST); + ITileInventory nms = blockChest.getInventory(data, world.getHandle(), this.getPosition(), true); + + if (nms instanceof InventoryLargeChest) { + inventory = new CraftInventoryDoubleChest((InventoryLargeChest) nms); + } + return inventory; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java new file mode 100644 index 000000000000..5c9bfe951373 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java @@ -0,0 +1,57 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityCommand; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.CommandBlock; +import org.bukkit.craftbukkit.util.CraftChatMessage; + +public class CraftCommandBlock extends CraftBlockEntityState implements CommandBlock { + + private String command; + private String name; + + public CraftCommandBlock(Block block) { + super(block, TileEntityCommand.class); + } + + public CraftCommandBlock(final Material material, final TileEntityCommand te) { + super(material, te); + } + + @Override + public void load(TileEntityCommand commandBlock) { + super.load(commandBlock); + + command = commandBlock.getCommandBlock().getCommand(); + name = CraftChatMessage.fromComponent(commandBlock.getCommandBlock().getName()); + } + + @Override + public String getCommand() { + return command; + } + + @Override + public void setCommand(String command) { + this.command = command != null ? command : ""; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name != null ? name : "@"; + } + + @Override + public void applyTo(TileEntityCommand commandBlock) { + super.applyTo(commandBlock); + + commandBlock.getCommandBlock().setCommand(command); + commandBlock.getCommandBlock().setName(CraftChatMessage.fromStringOrNull(name)); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftComparator.java b/src/main/java/org/bukkit/craftbukkit/block/CraftComparator.java new file mode 100644 index 000000000000..2e6a738ac7f5 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftComparator.java @@ -0,0 +1,17 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityComparator; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Comparator; + +public class CraftComparator extends CraftBlockEntityState implements Comparator { + + public CraftComparator(final Block block) { + super(block, TileEntityComparator.class); + } + + public CraftComparator(final Material material, final TileEntityComparator te) { + super(material, te); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java b/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java new file mode 100644 index 000000000000..5116a3765c60 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftConduit.java @@ -0,0 +1,17 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityConduit; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Conduit; + +public class CraftConduit extends CraftBlockEntityState implements Conduit { + + public CraftConduit(Block block) { + super(block, TileEntityConduit.class); + } + + public CraftConduit(Material material, TileEntityConduit te) { + super(material, te); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftContainer.java b/src/main/java/org/bukkit/craftbukkit/block/CraftContainer.java new file mode 100644 index 000000000000..06a1d4473a56 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftContainer.java @@ -0,0 +1,33 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.ChestLock; +import net.minecraft.server.TileEntityContainer; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Container; + +public abstract class CraftContainer extends CraftBlockEntityState implements Container { + + public CraftContainer(Block block, Class tileEntityClass) { + super(block, tileEntityClass); + } + + public CraftContainer(final Material material, T tileEntity) { + super(material, tileEntity); + } + + @Override + public boolean isLocked() { + return this.getSnapshot().isLocked(); + } + + @Override + public String getLock() { + return this.getSnapshot().getLock().getKey(); + } + + @Override + public void setLock(String key) { + this.getSnapshot().setLock(key == null ? ChestLock.a : new ChestLock(key)); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java b/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java new file mode 100644 index 000000000000..aa63b854d11b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java @@ -0,0 +1,125 @@ +package org.bukkit.craftbukkit.block; + +import com.google.common.base.Preconditions; +import net.minecraft.server.EntityTypes; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.TileEntityMobSpawner; +import org.bukkit.Material; + +import org.bukkit.block.Block; +import org.bukkit.block.CreatureSpawner; +import org.bukkit.entity.EntityType; + +public class CraftCreatureSpawner extends CraftBlockEntityState implements CreatureSpawner { + + public CraftCreatureSpawner(final Block block) { + super(block, TileEntityMobSpawner.class); + } + + public CraftCreatureSpawner(final Material material, TileEntityMobSpawner te) { + super(material, te); + } + + @Override + public EntityType getSpawnedType() { + MinecraftKey key = this.getSnapshot().getSpawner().getMobName(); + return (key == null) ? EntityType.PIG : EntityType.fromName(key.getKey()); + } + + @Override + public void setSpawnedType(EntityType entityType) { + if (entityType == null || entityType.getName() == null) { + throw new IllegalArgumentException("Can't spawn EntityType " + entityType + " from mobspawners!"); + } + + this.getSnapshot().getSpawner().setMobName(EntityTypes.a(entityType.getName())); + } + + @Override + public String getCreatureTypeName() { + return this.getSnapshot().getSpawner().getMobName().getKey(); + } + + @Override + public void setCreatureTypeByName(String creatureType) { + // Verify input + EntityType type = EntityType.fromName(creatureType); + if (type == null) { + return; + } + setSpawnedType(type); + } + + @Override + public int getDelay() { + return this.getSnapshot().getSpawner().spawnDelay; + } + + @Override + public void setDelay(int delay) { + this.getSnapshot().getSpawner().spawnDelay = delay; + } + + @Override + public int getMinSpawnDelay() { + return this.getSnapshot().getSpawner().minSpawnDelay; + } + + @Override + public void setMinSpawnDelay(int spawnDelay) { + Preconditions.checkArgument(spawnDelay <= getMaxSpawnDelay(), "Minimum Spawn Delay must be less than or equal to Maximum Spawn Delay"); + this.getSnapshot().getSpawner().minSpawnDelay = spawnDelay; + } + + @Override + public int getMaxSpawnDelay() { + return this.getSnapshot().getSpawner().maxSpawnDelay; + } + + @Override + public void setMaxSpawnDelay(int spawnDelay) { + Preconditions.checkArgument(spawnDelay > 0, "Maximum Spawn Delay must be greater than 0."); + Preconditions.checkArgument(spawnDelay >= getMinSpawnDelay(), "Maximum Spawn Delay must be greater than or equal to Minimum Spawn Delay"); + this.getSnapshot().getSpawner().maxSpawnDelay = spawnDelay; + } + + @Override + public int getMaxNearbyEntities() { + return this.getSnapshot().getSpawner().maxNearbyEntities; + } + + @Override + public void setMaxNearbyEntities(int maxNearbyEntities) { + this.getSnapshot().getSpawner().maxNearbyEntities = maxNearbyEntities; + } + + @Override + public int getSpawnCount() { + return this.getSnapshot().getSpawner().spawnCount; + } + + @Override + public void setSpawnCount(int count) { + this.getSnapshot().getSpawner().spawnCount = count; + } + + @Override + public int getRequiredPlayerRange() { + return this.getSnapshot().getSpawner().requiredPlayerRange; + } + + @Override + public void setRequiredPlayerRange(int requiredPlayerRange) { + this.getSnapshot().getSpawner().requiredPlayerRange = requiredPlayerRange; + } + + @Override + public int getSpawnRange() { + return this.getSnapshot().getSpawner().spawnRange; + } + + @Override + public void setSpawnRange(int spawnRange) { + this.getSnapshot().getSpawner().spawnRange = spawnRange; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftDaylightDetector.java b/src/main/java/org/bukkit/craftbukkit/block/CraftDaylightDetector.java new file mode 100644 index 000000000000..1c9b84c69c46 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftDaylightDetector.java @@ -0,0 +1,17 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityLightDetector; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.DaylightDetector; + +public class CraftDaylightDetector extends CraftBlockEntityState implements DaylightDetector { + + public CraftDaylightDetector(final Block block) { + super(block, TileEntityLightDetector.class); + } + + public CraftDaylightDetector(final Material material, final TileEntityLightDetector te) { + super(material, te); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftDispenser.java b/src/main/java/org/bukkit/craftbukkit/block/CraftDispenser.java new file mode 100644 index 000000000000..452ea9e4c552 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftDispenser.java @@ -0,0 +1,66 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.BlockDispenser; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.Blocks; +import net.minecraft.server.TileEntityDispenser; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Dispenser; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource; +import org.bukkit.inventory.Inventory; +import org.bukkit.projectiles.BlockProjectileSource; + +public class CraftDispenser extends CraftLootable implements Dispenser { + + public CraftDispenser(final Block block) { + super(block, TileEntityDispenser.class); + } + + public CraftDispenser(final Material material, final TileEntityDispenser te) { + super(material, te); + } + + @Override + public Inventory getSnapshotInventory() { + return new CraftInventory(this.getSnapshot()); + } + + @Override + public Inventory getInventory() { + if (!this.isPlaced()) { + return this.getSnapshotInventory(); + } + + return new CraftInventory(this.getTileEntity()); + } + + @Override + public BlockProjectileSource getBlockProjectileSource() { + Block block = getBlock(); + + if (block.getType() != Material.DISPENSER) { + return null; + } + + return new CraftBlockProjectileSource((TileEntityDispenser) this.getTileEntityFromWorld()); + } + + @Override + public boolean dispense() { + Block block = getBlock(); + + if (block.getType() == Material.DISPENSER) { + CraftWorld world = (CraftWorld) this.getWorld(); + BlockDispenser dispense = (BlockDispenser) Blocks.DISPENSER; + + dispense.dispense(world.getHandle(), this.getPosition()); + return true; + } else { + return false; + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftDropper.java b/src/main/java/org/bukkit/craftbukkit/block/CraftDropper.java new file mode 100644 index 000000000000..6b4bdbc8cd1e --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftDropper.java @@ -0,0 +1,50 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.BlockDropper; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.Blocks; +import net.minecraft.server.TileEntityDropper; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Dropper; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.inventory.Inventory; + +public class CraftDropper extends CraftLootable implements Dropper { + + public CraftDropper(final Block block) { + super(block, TileEntityDropper.class); + } + + public CraftDropper(final Material material, TileEntityDropper te) { + super(material, te); + } + + @Override + public Inventory getSnapshotInventory() { + return new CraftInventory(this.getSnapshot()); + } + + @Override + public Inventory getInventory() { + if (!this.isPlaced()) { + return this.getSnapshotInventory(); + } + + return new CraftInventory(this.getTileEntity()); + } + + @Override + public void drop() { + Block block = getBlock(); + + if (block.getType() == Material.DROPPER) { + CraftWorld world = (CraftWorld) this.getWorld(); + BlockDropper drop = (BlockDropper) Blocks.DROPPER; + + drop.dispense(world.getHandle(), this.getPosition()); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftEnchantingTable.java b/src/main/java/org/bukkit/craftbukkit/block/CraftEnchantingTable.java new file mode 100644 index 000000000000..3a782294c15e --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftEnchantingTable.java @@ -0,0 +1,38 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityEnchantTable; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.EnchantingTable; +import org.bukkit.craftbukkit.util.CraftChatMessage; + +public class CraftEnchantingTable extends CraftBlockEntityState implements EnchantingTable { + + public CraftEnchantingTable(final Block block) { + super(block, TileEntityEnchantTable.class); + } + + public CraftEnchantingTable(final Material material, final TileEntityEnchantTable te) { + super(material, te); + } + + @Override + public String getCustomName() { + TileEntityEnchantTable enchant = this.getSnapshot(); + return enchant.hasCustomName() ? CraftChatMessage.fromComponent(enchant.getCustomName()) : null; + } + + @Override + public void setCustomName(String name) { + this.getSnapshot().setCustomName(CraftChatMessage.fromStringOrNull(name)); + } + + @Override + public void applyTo(TileEntityEnchantTable enchantingTable) { + super.applyTo(enchantingTable); + + if (!this.getSnapshot().hasCustomName()) { + enchantingTable.setCustomName(null); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftEndGateway.java b/src/main/java/org/bukkit/craftbukkit/block/CraftEndGateway.java new file mode 100644 index 000000000000..54287744e138 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftEndGateway.java @@ -0,0 +1,66 @@ +package org.bukkit.craftbukkit.block; + +import java.util.Objects; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.TileEntityEndGateway; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.EndGateway; + +public class CraftEndGateway extends CraftBlockEntityState implements EndGateway { + + public CraftEndGateway(Block block) { + super(block, TileEntityEndGateway.class); + } + + public CraftEndGateway(final Material material, TileEntityEndGateway te) { + super(material, te); + } + + @Override + public Location getExitLocation() { + BlockPosition pos = this.getSnapshot().exitPortal; + return pos == null ? null : new Location(this.isPlaced() ? this.getWorld() : null, pos.getX(), pos.getY(), pos.getZ()); + } + + @Override + public void setExitLocation(Location location) { + if (location == null) { + this.getSnapshot().exitPortal = null; + } else if (!Objects.equals(location.getWorld(), this.isPlaced() ? this.getWorld() : null)) { + throw new IllegalArgumentException("Cannot set exit location to different world"); + } else { + this.getSnapshot().exitPortal = new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + } + + @Override + public boolean isExactTeleport() { + return this.getSnapshot().exactTeleport; + } + + @Override + public void setExactTeleport(boolean exact) { + this.getSnapshot().exactTeleport = exact; + } + + @Override + public long getAge() { + return this.getSnapshot().age; + } + + @Override + public void setAge(long age) { + this.getSnapshot().age = age; + } + + @Override + public void applyTo(TileEntityEndGateway endGateway) { + super.applyTo(endGateway); + + if (this.getSnapshot().exitPortal == null) { + endGateway.exitPortal = null; + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftEnderChest.java b/src/main/java/org/bukkit/craftbukkit/block/CraftEnderChest.java new file mode 100644 index 000000000000..2004ee2f3a41 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftEnderChest.java @@ -0,0 +1,17 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityEnderChest; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.EnderChest; + +public class CraftEnderChest extends CraftBlockEntityState implements EnderChest { + + public CraftEnderChest(final Block block) { + super(block, TileEntityEnderChest.class); + } + + public CraftEnderChest(final Material material, final TileEntityEnderChest te) { + super(material, te); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java b/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java new file mode 100644 index 000000000000..429c780ec7c1 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java @@ -0,0 +1,101 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.BlockFurnace; +import net.minecraft.server.TileEntityFurnace; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Furnace; +import org.bukkit.craftbukkit.inventory.CraftInventoryFurnace; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.inventory.FurnaceInventory; + +public class CraftFurnace extends CraftContainer implements Furnace { + + public CraftFurnace(final Block block) { + super(block, TileEntityFurnace.class); + } + + public CraftFurnace(final Material material, final TileEntityFurnace te) { + super(material, te); + } + + @Override + public FurnaceInventory getSnapshotInventory() { + return new CraftInventoryFurnace(this.getSnapshot()); + } + + @Override + public FurnaceInventory getInventory() { + if (!this.isPlaced()) { + return this.getSnapshotInventory(); + } + + return new CraftInventoryFurnace(this.getTileEntity()); + } + + @Override + public short getBurnTime() { + return (short) this.getSnapshot().getProperty(0); + } + + @Override + public void setBurnTime(short burnTime) { + this.getSnapshot().setProperty(0, burnTime); + // SPIGOT-844: Allow lighting and relighting using this API + this.data = this.data.set(BlockFurnace.LIT, burnTime > 0); + } + + @Override + public short getCookTime() { + return (short) this.getSnapshot().getProperty(2); + } + + @Override + public void setCookTime(short cookTime) { + this.getSnapshot().setProperty(2, cookTime); + } + + @Override + public int getCookTimeTotal() { + return this.getSnapshot().getProperty(3); + } + + @Override + public void setCookTimeTotal(int cookTimeTotal) { + this.getSnapshot().setProperty(3, cookTimeTotal); + } + + @Override + public String getCustomName() { + TileEntityFurnace furnace = this.getSnapshot(); + return furnace.hasCustomName() ? CraftChatMessage.fromComponent(furnace.getCustomName()) : null; + } + + @Override + public void setCustomName(String name) { + this.getSnapshot().setCustomName(CraftChatMessage.fromStringOrNull(name)); + } + + @Override + public void applyTo(TileEntityFurnace furnace) { + super.applyTo(furnace); + + if (!this.getSnapshot().hasCustomName()) { + furnace.setCustomName(null); + } + } + + // Paper start - cook speed multiplier API + @Override + public double getCookSpeedMultiplier() { + return this.getSnapshot().cookSpeedMultiplier; + } + + @Override + public void setCookSpeedMultiplier(double multiplier) { + com.google.common.base.Preconditions.checkArgument(multiplier >= 0, "Furnace speed multiplier cannot be negative"); + com.google.common.base.Preconditions.checkArgument(multiplier <= 200, "Furnace speed multiplier cannot more than 200"); + this.getSnapshot().cookSpeedMultiplier = multiplier; + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftHopper.java b/src/main/java/org/bukkit/craftbukkit/block/CraftHopper.java new file mode 100644 index 000000000000..6566554ab6b5 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftHopper.java @@ -0,0 +1,33 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.TileEntityHopper; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Hopper; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.inventory.Inventory; + +public class CraftHopper extends CraftLootable implements Hopper { + + public CraftHopper(final Block block) { + super(block, TileEntityHopper.class); + } + + public CraftHopper(final Material material, final TileEntityHopper te) { + super(material, te); + } + + @Override + public Inventory getSnapshotInventory() { + return new CraftInventory(this.getSnapshot()); + } + + @Override + public Inventory getInventory() { + if (!this.isPlaced()) { + return this.getSnapshotInventory(); + } + + return new CraftInventory(this.getTileEntity()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftJukebox.java b/src/main/java/org/bukkit/craftbukkit/block/CraftJukebox.java new file mode 100644 index 000000000000..b527cf98ca77 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftJukebox.java @@ -0,0 +1,92 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.BlockJukeBox; +import net.minecraft.server.Blocks; +import net.minecraft.server.ItemStack; +import net.minecraft.server.TileEntity; +import net.minecraft.server.TileEntityJukeBox; +import org.bukkit.Effect; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Jukebox; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; + +public class CraftJukebox extends CraftBlockEntityState implements Jukebox { + + public CraftJukebox(final Block block) { + super(block, TileEntityJukeBox.class); + } + + public CraftJukebox(final Material material, TileEntityJukeBox te) { + super(material, te); + } + + @Override + public boolean update(boolean force, boolean applyPhysics) { + boolean result = super.update(force, applyPhysics); + + if (result && this.isPlaced() && this.getType() == Material.JUKEBOX) { + CraftWorld world = (CraftWorld) this.getWorld(); + Material record = this.getPlaying(); + if (record == Material.AIR) { + world.getHandle().setTypeAndData(this.getPosition(), Blocks.JUKEBOX.getBlockData().set(BlockJukeBox.HAS_RECORD, false), 3); + } else { + world.getHandle().setTypeAndData(this.getPosition(), Blocks.JUKEBOX.getBlockData().set(BlockJukeBox.HAS_RECORD, true), 3); + } + world.playEffect(this.getLocation(), Effect.RECORD_PLAY, record); + } + + return result; + } + + @Override + public Material getPlaying() { + return getRecord().getType(); + } + + @Override + public void setPlaying(Material record) { + if (record == null || CraftMagicNumbers.getItem(record) == null) { + record = Material.AIR; + } + + setRecord(new org.bukkit.inventory.ItemStack(record)); + } + + @Override + public org.bukkit.inventory.ItemStack getRecord() { + ItemStack record = this.getSnapshot().getRecord(); + return CraftItemStack.asBukkitCopy(record); + } + + @Override + public void setRecord(org.bukkit.inventory.ItemStack record) { + ItemStack nms = CraftItemStack.asNMSCopy(record); + this.getSnapshot().setRecord(nms); + if (nms.isEmpty()) { + this.data = this.data.set(BlockJukeBox.HAS_RECORD, false); + } else { + this.data = this.data.set(BlockJukeBox.HAS_RECORD, true); + } + } + + @Override + public boolean isPlaying() { + return getHandle().get(BlockJukeBox.HAS_RECORD); + } + + @Override + public boolean eject() { + requirePlaced(); + TileEntity tileEntity = this.getTileEntityFromWorld(); + if (!(tileEntity instanceof TileEntityJukeBox)) return false; + + TileEntityJukeBox jukebox = (TileEntityJukeBox) tileEntity; + boolean result = !jukebox.getRecord().isEmpty(); + CraftWorld world = (CraftWorld) this.getWorld(); + ((BlockJukeBox) Blocks.JUKEBOX).dropRecord(world.getHandle(), getPosition()); + return result; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftLootable.java b/src/main/java/org/bukkit/craftbukkit/block/CraftLootable.java new file mode 100644 index 000000000000..daf183fb1040 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftLootable.java @@ -0,0 +1,77 @@ +package org.bukkit.craftbukkit.block; + +import com.destroystokyo.paper.loottable.PaperLootableBlockInventory; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.TileEntityLootable; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Nameable; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.loot.LootTable; +import org.bukkit.loot.Lootable; + +public abstract class CraftLootable extends CraftContainer implements Nameable, Lootable, PaperLootableBlockInventory { // Paper + + public CraftLootable(Block block, Class tileEntityClass) { + super(block, tileEntityClass); + } + + public CraftLootable(Material material, T tileEntity) { + super(material, tileEntity); + } + + @Override + public String getCustomName() { + T lootable = this.getSnapshot(); + return lootable.hasCustomName() ? CraftChatMessage.fromComponent(lootable.getCustomName()) : null; + } + + @Override + public void setCustomName(String name) { + this.getSnapshot().setCustomName(CraftChatMessage.fromStringOrNull(name)); + } + + @Override + public void applyTo(T lootable) { + super.applyTo(lootable); + + if (!this.getSnapshot().hasCustomName()) { + lootable.setCustomName(null); + } + if (this.getSnapshot().getLootTable() == null) { + lootable.setLootTable((MinecraftKey) null, 0L); + } + } + + @Override + public LootTable getLootTable() { + if (getSnapshot().getLootTable() == null) { + return null; + } + + MinecraftKey key = getSnapshot().getLootTable(); + return Bukkit.getLootTable(CraftNamespacedKey.fromMinecraft(key)); + } + + @Override + public void setLootTable(LootTable table) { + setLootTable(table, getSeed()); + } + + @Override + public long getSeed() { + return getSnapshotNBT().getLong("LootTableSeed"); // returns OL if an error occurred + } + + @Override + public void setSeed(long seed) { + setLootTable(getLootTable(), seed); + } + + public void setLootTable(LootTable table, long seed) { // Paper - public + MinecraftKey key = (table == null) ? null : CraftNamespacedKey.toMinecraft(table.getKey()); + getSnapshot().setLootTable(key, seed); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftShulkerBox.java b/src/main/java/org/bukkit/craftbukkit/block/CraftShulkerBox.java new file mode 100644 index 000000000000..c029a1244123 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftShulkerBox.java @@ -0,0 +1,43 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.BlockShulkerBox; +import net.minecraft.server.TileEntityShulkerBox; +import org.bukkit.DyeColor; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.ShulkerBox; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.inventory.Inventory; + +public class CraftShulkerBox extends CraftLootable implements ShulkerBox { + + public CraftShulkerBox(final Block block) { + super(block, TileEntityShulkerBox.class); + } + + public CraftShulkerBox(final Material material, final TileEntityShulkerBox te) { + super(material, te); + } + + @Override + public Inventory getSnapshotInventory() { + return new CraftInventory(this.getSnapshot()); + } + + @Override + public Inventory getInventory() { + if (!this.isPlaced()) { + return this.getSnapshotInventory(); + } + + return new CraftInventory(this.getTileEntity()); + } + + @Override + public DyeColor getColor() { + net.minecraft.server.Block block = CraftMagicNumbers.getBlock(this.getType()); + + return DyeColor.getByWoolData((byte) ((BlockShulkerBox) block).color.getColorIndex()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftSign.java b/src/main/java/org/bukkit/craftbukkit/block/CraftSign.java new file mode 100644 index 000000000000..3a8f6436099d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftSign.java @@ -0,0 +1,94 @@ +package org.bukkit.craftbukkit.block; + +import net.minecraft.server.ChatComponentText; +import net.minecraft.server.IChatBaseComponent; +import net.minecraft.server.TileEntitySign; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Sign; +import org.bukkit.craftbukkit.util.CraftChatMessage; + +public class CraftSign extends CraftBlockEntityState implements Sign { + + private String[] lines; + private boolean editable; + + public CraftSign(final Block block) { + super(block, TileEntitySign.class); + if (lines == null) { lines = new String[]{"", "", "", ""}; } // Paper + } + + public CraftSign(final Material material, final TileEntitySign te) { + super(material, te); + if (lines == null) { lines = new String[]{"", "", "", ""}; } // Paper + } + + @Override + public void load(TileEntitySign sign) { + super.load(sign); + + lines = new String[sign.lines.length]; + System.arraycopy(revertComponents(sign.lines), 0, lines, 0, lines.length); + editable = sign.isEditable; + } + + @Override + public String[] getLines() { + return lines; + } + + @Override + public String getLine(int index) throws IndexOutOfBoundsException { + return lines[index]; + } + + @Override + public void setLine(int index, String line) throws IndexOutOfBoundsException { + lines[index] = line; + } + + @Override + public boolean isEditable() { + return this.editable; + } + + @Override + public void setEditable(boolean editable) { + this.editable = editable; + } + + @Override + public void applyTo(TileEntitySign sign) { + super.applyTo(sign); + + IChatBaseComponent[] newLines = sanitizeLines(lines); + System.arraycopy(newLines, 0, sign.lines, 0, 4); + sign.isEditable = editable; + } + + public static IChatBaseComponent[] sanitizeLines(String[] lines) { + IChatBaseComponent[] components = new IChatBaseComponent[4]; + + for (int i = 0; i < 4; i++) { + if (i < lines.length && lines[i] != null) { + components[i] = CraftChatMessage.fromString(lines[i])[0]; + } else { + components[i] = new ChatComponentText(""); + } + } + + return components; + } + + public static String[] revertComponents(IChatBaseComponent[] components) { + String[] lines = new String[components.length]; + for (int i = 0; i < lines.length; i++) { + lines[i] = revertComponent(components[i]); + } + return lines; + } + + private static String revertComponent(IChatBaseComponent component) { + return CraftChatMessage.fromComponent(component); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftSkull.java b/src/main/java/org/bukkit/craftbukkit/block/CraftSkull.java new file mode 100644 index 000000000000..c260af11cd4c --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftSkull.java @@ -0,0 +1,182 @@ +package org.bukkit.craftbukkit.block; + +import com.destroystokyo.paper.profile.CraftPlayerProfile; +import com.destroystokyo.paper.profile.PlayerProfile; +import com.google.common.base.Preconditions; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.TileEntitySkull; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; + +import org.bukkit.SkullType; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.Skull; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Directional; +import org.bukkit.block.data.Rotatable; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import javax.annotation.Nullable; + +public class CraftSkull extends CraftBlockEntityState implements Skull { + + private static final int MAX_OWNER_LENGTH = 16; + private GameProfile profile; + + public CraftSkull(final Block block) { + super(block, TileEntitySkull.class); + } + + public CraftSkull(final Material material, final TileEntitySkull te) { + super(material, te); + } + + @Override + public void load(TileEntitySkull skull) { + super.load(skull); + + profile = skull.getGameProfile(); + } + + static int getSkullType(SkullType type) { + switch(type) { + default: + case SKELETON: + return 0; + case WITHER: + return 1; + case ZOMBIE: + return 2; + case PLAYER: + return 3; + case CREEPER: + return 4; + case DRAGON: + return 5; + } + } + + @Override + public boolean hasOwner() { + return profile != null; + } + + @Override + public String getOwner() { + return hasOwner() ? profile.getName() : null; + } + + @Override + public boolean setOwner(String name) { + if (name == null || name.length() > MAX_OWNER_LENGTH) { + return false; + } + + GameProfile profile = MinecraftServer.getServer().getUserCache().getProfile(name); + if (profile == null) { + return false; + } + + this.profile = profile; + return true; + } + + @Override + public OfflinePlayer getOwningPlayer() { + if (profile != null) { + if (profile.getId() != null) { + return Bukkit.getOfflinePlayer(profile.getId()); + } + + if (profile.getName() != null) { + return Bukkit.getOfflinePlayer(profile.getName()); + } + } + + return null; + } + + @Override + public void setOwningPlayer(OfflinePlayer player) { + Preconditions.checkNotNull(player, "player"); + + if (player instanceof CraftPlayer) { + this.profile = ((CraftPlayer) player).getProfile(); + } else { + this.profile = new GameProfile(player.getUniqueId(), player.getName()); + } + } + + // Paper start + @Override + public void setPlayerProfile(PlayerProfile profile) { + Preconditions.checkNotNull(profile, "profile"); + this.profile = CraftPlayerProfile.asAuthlibCopy(profile); + } + + @Nullable + @Override + public PlayerProfile getPlayerProfile() { + return profile != null ? CraftPlayerProfile.asBukkitCopy(profile) : null; + } + // Paper end + + @Override + public BlockFace getRotation() { + BlockData blockData = getBlockData(); + return (blockData instanceof Rotatable) ? ((Rotatable) blockData).getRotation() : ((Directional) blockData).getFacing(); + } + + @Override + public void setRotation(BlockFace rotation) { + BlockData blockData = getBlockData(); + if (blockData instanceof Rotatable) { + ((Rotatable) blockData).setRotation(rotation); + } else { + ((Directional) blockData).setFacing(rotation); + } + setBlockData(blockData); + } + + @Override + public SkullType getSkullType() { + switch (getType()) { + case SKELETON_SKULL: + case SKELETON_WALL_SKULL: + return SkullType.SKELETON; + case WITHER_SKELETON_SKULL: + case WITHER_SKELETON_WALL_SKULL: + return SkullType.WITHER; + case ZOMBIE_HEAD: + case ZOMBIE_WALL_HEAD: + return SkullType.ZOMBIE; + case PLAYER_HEAD: + case PLAYER_WALL_HEAD: + return SkullType.PLAYER; + case CREEPER_HEAD: + case CREEPER_WALL_HEAD: + return SkullType.CREEPER; + case DRAGON_HEAD: + case DRAGON_WALL_HEAD: + return SkullType.DRAGON; + default: + throw new IllegalArgumentException("Unknown SkullType for " + getType()); + } + } + + @Override + public void setSkullType(SkullType skullType) { + throw new UnsupportedOperationException("Must change block type"); + } + + @Override + public void applyTo(TileEntitySkull skull) { + super.applyTo(skull); + + if (getSkullType() == SkullType.PLAYER) { + skull.setGameProfile(profile); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftStructureBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftStructureBlock.java new file mode 100644 index 000000000000..86095fbe7dde --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftStructureBlock.java @@ -0,0 +1,187 @@ +package org.bukkit.craftbukkit.block; + +import com.google.common.base.Preconditions; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.BlockPropertyStructureMode; +import net.minecraft.server.EnumBlockMirror; +import net.minecraft.server.EnumBlockRotation; +import net.minecraft.server.TileEntityStructure; +import org.apache.commons.lang3.Validate; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Structure; +import org.bukkit.block.structure.Mirror; +import org.bukkit.block.structure.StructureRotation; +import org.bukkit.block.structure.UsageMode; +import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.util.BlockVector; + +public class CraftStructureBlock extends CraftBlockEntityState implements Structure { + + private static final int MAX_SIZE = 32; + + public CraftStructureBlock(Block block) { + super(block, TileEntityStructure.class); + } + + public CraftStructureBlock(Material material, TileEntityStructure structure) { + super(material, structure); + } + + @Override + public String getStructureName() { + return getSnapshot().getStructureName(); + } + + @Override + public void setStructureName(String name) { + Preconditions.checkArgument(name != null, "Structure Name cannot be null"); + getSnapshot().setStructureName(name); + } + + @Override + public String getAuthor() { + return getSnapshot().author; + } + + @Override + public void setAuthor(String author) { + Preconditions.checkArgument(author != null && !author.isEmpty(), "Author name cannot be null nor empty"); + getSnapshot().author = author; + } + + @Override + public void setAuthor(LivingEntity entity) { + Preconditions.checkArgument(entity != null, "Structure Block author entity cannot be null"); + getSnapshot().setAuthor(((CraftLivingEntity) entity).getHandle()); + } + + @Override + public BlockVector getRelativePosition() { + return new BlockVector(getSnapshot().relativePosition.getX(), getSnapshot().relativePosition.getY(), getSnapshot().relativePosition.getZ()); + } + + @Override + public void setRelativePosition(BlockVector vector) { + Validate.isTrue(isBetween(vector.getBlockX(), -MAX_SIZE, MAX_SIZE), "Structure Size (X) must be between -" + MAX_SIZE + " and " + MAX_SIZE); + Validate.isTrue(isBetween(vector.getBlockY(), -MAX_SIZE, MAX_SIZE), "Structure Size (Y) must be between -" + MAX_SIZE + " and " + MAX_SIZE); + Validate.isTrue(isBetween(vector.getBlockZ(), -MAX_SIZE, MAX_SIZE), "Structure Size (Z) must be between -" + MAX_SIZE + " and " + MAX_SIZE); + getSnapshot().relativePosition = new BlockPosition(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ()); + } + + @Override + public BlockVector getStructureSize() { + return new BlockVector(getSnapshot().size.getX(), getSnapshot().size.getY(), getSnapshot().size.getZ()); + } + + @Override + public void setStructureSize(BlockVector vector) { + Validate.isTrue(isBetween(vector.getBlockX(), 0, MAX_SIZE), "Structure Size (X) must be between 0 and " + MAX_SIZE); + Validate.isTrue(isBetween(vector.getBlockY(), 0, MAX_SIZE), "Structure Size (Y) must be between 0 and " + MAX_SIZE); + Validate.isTrue(isBetween(vector.getBlockZ(), 0, MAX_SIZE), "Structure Size (Z) must be between 0 and " + MAX_SIZE); + getSnapshot().size = new BlockPosition(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ()); + } + + @Override + public void setMirror(Mirror mirror) { + getSnapshot().mirror = EnumBlockMirror.valueOf(mirror.name()); + } + + @Override + public Mirror getMirror() { + return Mirror.valueOf(getSnapshot().mirror.name()); + } + + @Override + public void setRotation(StructureRotation rotation) { + getSnapshot().rotation = EnumBlockRotation.valueOf(rotation.name()); + } + + @Override + public StructureRotation getRotation() { + return StructureRotation.valueOf(getSnapshot().rotation.name()); + } + + @Override + public void setUsageMode(UsageMode mode) { + getSnapshot().setUsageMode(BlockPropertyStructureMode.valueOf(mode.name())); + } + + @Override + public UsageMode getUsageMode() { + return UsageMode.valueOf(getSnapshot().getUsageMode().name()); + } + + @Override + public void setIgnoreEntities(boolean flag) { + getSnapshot().ignoreEntities = flag; + } + + @Override + public boolean isIgnoreEntities() { + return getSnapshot().ignoreEntities; + } + + @Override + public void setShowAir(boolean showAir) { + getSnapshot().showAir = showAir; + } + + @Override + public boolean isShowAir() { + return getSnapshot().showAir; + } + + @Override + public void setBoundingBoxVisible(boolean showBoundingBox) { + getSnapshot().showBoundingBox = showBoundingBox; + } + + @Override + public boolean isBoundingBoxVisible() { + return getSnapshot().showBoundingBox; + } + + @Override + public void setIntegrity(float integrity) { + Validate.isTrue(isBetween(integrity, 0.0f, 1.0f), "Integrity must be between 0.0f and 1.0f"); + getSnapshot().integrity = integrity; + } + + @Override + public float getIntegrity() { + return getSnapshot().integrity; + } + + @Override + public void setSeed(long seed) { + getSnapshot().seed = seed; + } + + @Override + public long getSeed() { + return getSnapshot().seed; + } + + @Override + public void setMetadata(String metadata) { + Validate.notNull(metadata, "Structure metadata cannot be null"); + if (getUsageMode() == UsageMode.DATA) { + getSnapshot().metadata = metadata; + } + } + + @Override + public String getMetadata() { + return getSnapshot().metadata; + } + + private static boolean isBetween(int num, int min, int max) { + return num >= min && num <= max; + } + + private static boolean isBetween(float num, float min, float max) { + return num >= min && num <= max; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftAgeable.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftAgeable.java new file mode 100644 index 000000000000..e311e66bd754 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftAgeable.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.block.data; + +import org.bukkit.block.data.Ageable; + +public abstract class CraftAgeable extends CraftBlockData implements Ageable { + + private static final net.minecraft.server.BlockStateInteger AGE = getInteger("age"); + + @Override + public int getAge() { + return get(AGE); + } + + @Override + public void setAge(int age) { + set(AGE, age); + } + + @Override + public int getMaximumAge() { + return getMax(AGE); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftAnaloguePowerable.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftAnaloguePowerable.java new file mode 100644 index 000000000000..60a247cedeb9 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftAnaloguePowerable.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.block.data; + +import org.bukkit.block.data.AnaloguePowerable; + +public abstract class CraftAnaloguePowerable extends CraftBlockData implements AnaloguePowerable { + + private static final net.minecraft.server.BlockStateInteger POWER = getInteger("power"); + + @Override + public int getPower() { + return get(POWER); + } + + @Override + public void setPower(int power) { + set(POWER, power); + } + + @Override + public int getMaximumPower() { + return getMax(POWER); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftAttachable.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftAttachable.java new file mode 100644 index 000000000000..657cf0734018 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftAttachable.java @@ -0,0 +1,18 @@ +package org.bukkit.craftbukkit.block.data; + +import org.bukkit.block.data.Attachable; + +public abstract class CraftAttachable extends CraftBlockData implements Attachable { + + private static final net.minecraft.server.BlockStateBoolean ATTACHED = getBoolean("attached"); + + @Override + public boolean isAttached() { + return get(ATTACHED); + } + + @Override + public void setAttached(boolean attached) { + set(ATTACHED, attached); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftBisected.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftBisected.java new file mode 100644 index 000000000000..969b670ea50e --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftBisected.java @@ -0,0 +1,18 @@ +package org.bukkit.craftbukkit.block.data; + +import org.bukkit.block.data.Bisected; + +public class CraftBisected extends CraftBlockData implements Bisected { + + private static final net.minecraft.server.BlockStateEnum HALF = getEnum("half"); + + @Override + public Half getHalf() { + return get(HALF, Half.class); + } + + @Override + public void setHalf(Half half) { + set(HALF, half); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java new file mode 100644 index 000000000000..4bb43e360df4 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java @@ -0,0 +1,537 @@ +package org.bukkit.craftbukkit.block.data; + +import java.util.stream.Collectors; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.ImmutableSet; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import net.minecraft.server.ArgumentBlock; +import net.minecraft.server.Block; +import net.minecraft.server.BlockDataAbstract; +import net.minecraft.server.BlockStateBoolean; +import net.minecraft.server.BlockStateEnum; +import net.minecraft.server.BlockStateInteger; +import net.minecraft.server.EnumDirection; +import net.minecraft.server.IBlockData; +import net.minecraft.server.IBlockState; +import net.minecraft.server.INamable; +import net.minecraft.server.IRegistry; +import org.bukkit.Material; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; + +public class CraftBlockData implements BlockData { + + private IBlockData state; + private Map, Comparable> parsedStates; + + protected CraftBlockData() { + throw new AssertionError("Template Constructor"); + } + + protected CraftBlockData(IBlockData state) { + this.state = state; + } + + @Override + public Material getMaterial() { + return CraftMagicNumbers.getMaterial(state.getBlock()); + } + + public IBlockData getState() { + return state; + } + + /** + * Get a given BlockStateEnum's value as its Bukkit counterpart. + * + * @param nms the NMS state to convert + * @param bukkit the Bukkit class + * @param the type + * @return the matching Bukkit type + */ + protected > B get(BlockStateEnum nms, Class bukkit) { + return toBukkit(state.get(nms), bukkit); + } + + /** + * Convert all values from the given BlockStateEnum to their appropriate + * Bukkit counterpart. + * + * @param nms the NMS state to get values from + * @param bukkit the bukkit class to convert the values to + * @param the bukkit class type + * @return an immutable Set of values in their appropriate Bukkit type + */ + @SuppressWarnings("unchecked") + protected > Set getValues(BlockStateEnum nms, Class bukkit) { + ImmutableSet.Builder values = ImmutableSet.builder(); + + for (Enum e : nms.d()) { + values.add(toBukkit(e, bukkit)); + } + + return values.build(); + } + + /** + * Set a given {@link BlockStateEnum} with the matching enum from Bukkit. + * + * @param nms the NMS BlockStateEnum to set + * @param bukkit the matching Bukkit Enum + * @param the Bukkit type + * @param the NMS type + */ + protected , N extends Enum & INamable> void set(BlockStateEnum nms, Enum bukkit) { + this.parsedStates = null; + this.state = this.state.set(nms, toNMS(bukkit, nms.b())); + } + + @Override + public BlockData merge(BlockData data) { + CraftBlockData craft = (CraftBlockData) data; + Preconditions.checkArgument(craft.parsedStates != null, "Data not created via string parsing"); + Preconditions.checkArgument(this.state.getBlock() == craft.state.getBlock(), "States have different types (got %s, expected %s)", data, this); + + CraftBlockData clone = (CraftBlockData) this.clone(); + clone.parsedStates = null; + + for (IBlockState parsed : craft.parsedStates.keySet()) { + clone.state = clone.state.set(parsed, craft.state.get(parsed)); + } + + return clone; + } + + @Override + public boolean matches(BlockData data) { + if (data == null) { + return false; + } + if (!(data instanceof CraftBlockData)) { + return false; + } + + CraftBlockData craft = (CraftBlockData) data; + if (this.state.getBlock() != craft.state.getBlock()) { + return false; + } + + // Fastpath an exact match + boolean exactMatch = this.equals(data); + + // If that failed, do a merge and check + if (!exactMatch && craft.parsedStates != null) { + return this.merge(data).equals(this); + } + + return exactMatch; + } + + private static final Map, Enum>> classMappings = new HashMap<>(); + + /** + * Convert an NMS Enum (usually a BlockStateEnum) to its appropriate Bukkit + * enum from the given class. + * + * @throws IllegalStateException if the Enum could not be converted + */ + @SuppressWarnings("unchecked") + private static > B toBukkit(Enum nms, Class bukkit) { + Enum converted; + BiMap, Enum> nmsToBukkit = classMappings.get(nms.getClass()); + + if (nmsToBukkit != null) { + converted = nmsToBukkit.get(nms); + if (converted != null) { + return (B) converted; + } + } + + if (nms instanceof EnumDirection) { + converted = CraftBlock.notchToBlockFace((EnumDirection) nms); + } else { + converted = bukkit.getEnumConstants()[nms.ordinal()]; + } + + Preconditions.checkState(converted != null, "Could not convert enum %s->%s", nms, bukkit); + + if (nmsToBukkit == null) { + nmsToBukkit = HashBiMap.create(); + classMappings.put(nms.getClass(), nmsToBukkit); + } + + nmsToBukkit.put(nms, converted); + + return (B) converted; + } + + /** + * Convert a given Bukkit enum to its matching NMS enum type. + * + * @param bukkit the Bukkit enum to convert + * @param nms the NMS class + * @return the matching NMS type + * @throws IllegalStateException if the Enum could not be converted + */ + @SuppressWarnings("unchecked") + private static & INamable> N toNMS(Enum bukkit, Class nms) { + Enum converted; + BiMap, Enum> nmsToBukkit = classMappings.get(nms); + + if (nmsToBukkit != null) { + converted = nmsToBukkit.inverse().get(bukkit); + if (converted != null) { + return (N) converted; + } + } + + if (bukkit instanceof BlockFace) { + converted = CraftBlock.blockFaceToNotch((BlockFace) bukkit); + } else { + converted = nms.getEnumConstants()[bukkit.ordinal()]; + } + + Preconditions.checkState(converted != null, "Could not convert enum %s->%s", nms, bukkit); + + if (nmsToBukkit == null) { + nmsToBukkit = HashBiMap.create(); + classMappings.put(nms, nmsToBukkit); + } + + nmsToBukkit.put(converted, bukkit); + + return (N) converted; + } + + /** + * Get the current value of a given state. + * + * @param ibs the state to check + * @param the type + * @return the current value of the given state + */ + protected > T get(IBlockState ibs) { + // Straight integer or boolean getter + return this.state.get(ibs); + } + + /** + * Set the specified state's value. + * + * @param ibs the state to set + * @param v the new value + * @param the state's type + * @param the value's type. Must match the state's type. + */ + public , V extends T> void set(IBlockState ibs, V v) { + // Straight integer or boolean setter + this.parsedStates = null; + this.state = this.state.set(ibs, v); + } + + @Override + public String getAsString() { + return toString(((BlockDataAbstract) state).getStateMap()); + } + + @Override + public String getAsString(boolean hideUnspecified) { + return (hideUnspecified && parsedStates != null) ? toString(parsedStates) : getAsString(); + } + + @Override + public BlockData clone() { + try { + return (BlockData) super.clone(); + } catch (CloneNotSupportedException ex) { + throw new AssertionError("Clone not supported", ex); + } + } + + @Override + public String toString() { + return "CraftBlockData{" + state.toString() + "}"; + } + + // Mimicked from BlockDataAbstract#toString() + public String toString(Map, Comparable> states) { + StringBuilder stateString = new StringBuilder(IRegistry.BLOCK.getKey(state.getBlock()).toString()); + + if (!states.isEmpty()) { + stateString.append('['); + stateString.append(states.entrySet().stream().map(BlockDataAbstract.STATE_TO_VALUE).collect(Collectors.joining(","))); + stateString.append(']'); + } + + return stateString.toString(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof CraftBlockData && state.equals(((CraftBlockData) obj).state); + } + + @Override + public int hashCode() { + return state.hashCode(); + } + + protected static BlockStateBoolean getBoolean(String name) { + throw new AssertionError("Template Method"); + } + + protected static BlockStateBoolean getBoolean(String name, boolean optional) { + throw new AssertionError("Template Method"); + } + + protected static BlockStateEnum getEnum(String name) { + throw new AssertionError("Template Method"); + } + + protected static BlockStateInteger getInteger(String name) { + throw new AssertionError("Template Method"); + } + + protected static BlockStateBoolean getBoolean(Class block, String name) { + return (BlockStateBoolean) getState(block, name, false); + } + + protected static BlockStateBoolean getBoolean(Class block, String name, boolean optional) { + return (BlockStateBoolean) getState(block, name, optional); + } + + protected static BlockStateEnum getEnum(Class block, String name) { + return (BlockStateEnum) getState(block, name, false); + } + + protected static BlockStateInteger getInteger(Class block, String name) { + return (BlockStateInteger) getState(block, name, false); + } + + /** + * Get a specified {@link IBlockState} from a given block's class with a + * given name + * + * @param block the class to retrieve the state from + * @param name the name of the state to retrieve + * @param optional if the state can be null + * @return the specified state or null + * @throws IllegalStateException if the state is null and {@code optional} + * is false. + */ + private static IBlockState getState(Class block, String name, boolean optional) { + IBlockState state = null; + + for (Block instance : (Iterable) IRegistry.BLOCK) { // Eclipse fail + if (instance.getClass() == block) { + if (state == null) { + state = instance.getStates().a(name); + } else { + IBlockState newState = instance.getStates().a(name); + + Preconditions.checkState(state == newState, "State mistmatch %s,%s", state, newState); + } + } + } + + Preconditions.checkState(optional || state != null, "Null state for %s,%s", block, name); + + return state; + } + + /** + * Get the minimum value allowed by the BlockStateInteger. + * + * @param state the state to check + * @return the minimum value allowed + */ + protected static int getMin(BlockStateInteger state) { + return state.min; + } + + /** + * Get the maximum value allowed by the BlockStateInteger. + * + * @param state the state to check + * @return the maximum value allowed + */ + protected static int getMax(BlockStateInteger state) { + return state.max; + } + + // + private static final Map, Function> MAP = new HashMap<>(); + + static { + // + register(net.minecraft.server.BlockAnvil.class, org.bukkit.craftbukkit.block.impl.CraftAnvil::new); + register(net.minecraft.server.BlockBanner.class, org.bukkit.craftbukkit.block.impl.CraftBanner::new); + register(net.minecraft.server.BlockBannerWall.class, org.bukkit.craftbukkit.block.impl.CraftBannerWall::new); + register(net.minecraft.server.BlockBed.class, org.bukkit.craftbukkit.block.impl.CraftBed::new); + register(net.minecraft.server.BlockBeetroot.class, org.bukkit.craftbukkit.block.impl.CraftBeetroot::new); + register(net.minecraft.server.BlockBrewingStand.class, org.bukkit.craftbukkit.block.impl.CraftBrewingStand::new); + register(net.minecraft.server.BlockBubbleColumn.class, org.bukkit.craftbukkit.block.impl.CraftBubbleColumn::new); + register(net.minecraft.server.BlockCactus.class, org.bukkit.craftbukkit.block.impl.CraftCactus::new); + register(net.minecraft.server.BlockCake.class, org.bukkit.craftbukkit.block.impl.CraftCake::new); + register(net.minecraft.server.BlockCarrots.class, org.bukkit.craftbukkit.block.impl.CraftCarrots::new); + register(net.minecraft.server.BlockCauldron.class, org.bukkit.craftbukkit.block.impl.CraftCauldron::new); + register(net.minecraft.server.BlockChest.class, org.bukkit.craftbukkit.block.impl.CraftChest::new); + register(net.minecraft.server.BlockChestTrapped.class, org.bukkit.craftbukkit.block.impl.CraftChestTrapped::new); + register(net.minecraft.server.BlockChorusFlower.class, org.bukkit.craftbukkit.block.impl.CraftChorusFlower::new); + register(net.minecraft.server.BlockChorusFruit.class, org.bukkit.craftbukkit.block.impl.CraftChorusFruit::new); + register(net.minecraft.server.BlockCobbleWall.class, org.bukkit.craftbukkit.block.impl.CraftCobbleWall::new); + register(net.minecraft.server.BlockCocoa.class, org.bukkit.craftbukkit.block.impl.CraftCocoa::new); + register(net.minecraft.server.BlockCommand.class, org.bukkit.craftbukkit.block.impl.CraftCommand::new); + register(net.minecraft.server.BlockConduit.class, org.bukkit.craftbukkit.block.impl.CraftConduit::new); + register(net.minecraft.server.BlockCoralDead.class, org.bukkit.craftbukkit.block.impl.CraftCoralDead::new); + register(net.minecraft.server.BlockCoralFan.class, org.bukkit.craftbukkit.block.impl.CraftCoralFan::new); + register(net.minecraft.server.BlockCoralFanAbstract.class, org.bukkit.craftbukkit.block.impl.CraftCoralFanAbstract::new); + register(net.minecraft.server.BlockCoralFanWall.class, org.bukkit.craftbukkit.block.impl.CraftCoralFanWall::new); + register(net.minecraft.server.BlockCoralFanWallAbstract.class, org.bukkit.craftbukkit.block.impl.CraftCoralFanWallAbstract::new); + register(net.minecraft.server.BlockCoralPlant.class, org.bukkit.craftbukkit.block.impl.CraftCoralPlant::new); + register(net.minecraft.server.BlockCrops.class, org.bukkit.craftbukkit.block.impl.CraftCrops::new); + register(net.minecraft.server.BlockDaylightDetector.class, org.bukkit.craftbukkit.block.impl.CraftDaylightDetector::new); + register(net.minecraft.server.BlockDirtSnow.class, org.bukkit.craftbukkit.block.impl.CraftDirtSnow::new); + register(net.minecraft.server.BlockDispenser.class, org.bukkit.craftbukkit.block.impl.CraftDispenser::new); + register(net.minecraft.server.BlockDoor.class, org.bukkit.craftbukkit.block.impl.CraftDoor::new); + register(net.minecraft.server.BlockDropper.class, org.bukkit.craftbukkit.block.impl.CraftDropper::new); + register(net.minecraft.server.BlockEndRod.class, org.bukkit.craftbukkit.block.impl.CraftEndRod::new); + register(net.minecraft.server.BlockEnderChest.class, org.bukkit.craftbukkit.block.impl.CraftEnderChest::new); + register(net.minecraft.server.BlockEnderPortalFrame.class, org.bukkit.craftbukkit.block.impl.CraftEnderPortalFrame::new); + register(net.minecraft.server.BlockFence.class, org.bukkit.craftbukkit.block.impl.CraftFence::new); + register(net.minecraft.server.BlockFenceGate.class, org.bukkit.craftbukkit.block.impl.CraftFenceGate::new); + register(net.minecraft.server.BlockFire.class, org.bukkit.craftbukkit.block.impl.CraftFire::new); + register(net.minecraft.server.BlockFloorSign.class, org.bukkit.craftbukkit.block.impl.CraftFloorSign::new); + register(net.minecraft.server.BlockFluids.class, org.bukkit.craftbukkit.block.impl.CraftFluids::new); + register(net.minecraft.server.BlockFurnace.class, org.bukkit.craftbukkit.block.impl.CraftFurnace::new); + register(net.minecraft.server.BlockGlassPane.class, org.bukkit.craftbukkit.block.impl.CraftGlassPane::new); + register(net.minecraft.server.BlockGlazedTerracotta.class, org.bukkit.craftbukkit.block.impl.CraftGlazedTerracotta::new); + register(net.minecraft.server.BlockGrass.class, org.bukkit.craftbukkit.block.impl.CraftGrass::new); + register(net.minecraft.server.BlockHay.class, org.bukkit.craftbukkit.block.impl.CraftHay::new); + register(net.minecraft.server.BlockHopper.class, org.bukkit.craftbukkit.block.impl.CraftHopper::new); + register(net.minecraft.server.BlockHugeMushroom.class, org.bukkit.craftbukkit.block.impl.CraftHugeMushroom::new); + register(net.minecraft.server.BlockIceFrost.class, org.bukkit.craftbukkit.block.impl.CraftIceFrost::new); + register(net.minecraft.server.BlockIronBars.class, org.bukkit.craftbukkit.block.impl.CraftIronBars::new); + register(net.minecraft.server.BlockJukeBox.class, org.bukkit.craftbukkit.block.impl.CraftJukeBox::new); + register(net.minecraft.server.BlockKelp.class, org.bukkit.craftbukkit.block.impl.CraftKelp::new); + register(net.minecraft.server.BlockLadder.class, org.bukkit.craftbukkit.block.impl.CraftLadder::new); + register(net.minecraft.server.BlockLeaves.class, org.bukkit.craftbukkit.block.impl.CraftLeaves::new); + register(net.minecraft.server.BlockLever.class, org.bukkit.craftbukkit.block.impl.CraftLever::new); + register(net.minecraft.server.BlockLogAbstract.class, org.bukkit.craftbukkit.block.impl.CraftLogAbstract::new); + register(net.minecraft.server.BlockMinecartDetector.class, org.bukkit.craftbukkit.block.impl.CraftMinecartDetector::new); + register(net.minecraft.server.BlockMinecartTrack.class, org.bukkit.craftbukkit.block.impl.CraftMinecartTrack::new); + register(net.minecraft.server.BlockMycel.class, org.bukkit.craftbukkit.block.impl.CraftMycel::new); + register(net.minecraft.server.BlockNetherWart.class, org.bukkit.craftbukkit.block.impl.CraftNetherWart::new); + register(net.minecraft.server.BlockNote.class, org.bukkit.craftbukkit.block.impl.CraftNote::new); + register(net.minecraft.server.BlockObserver.class, org.bukkit.craftbukkit.block.impl.CraftObserver::new); + register(net.minecraft.server.BlockPiston.class, org.bukkit.craftbukkit.block.impl.CraftPiston::new); + register(net.minecraft.server.BlockPistonExtension.class, org.bukkit.craftbukkit.block.impl.CraftPistonExtension::new); + register(net.minecraft.server.BlockPistonMoving.class, org.bukkit.craftbukkit.block.impl.CraftPistonMoving::new); + register(net.minecraft.server.BlockPortal.class, org.bukkit.craftbukkit.block.impl.CraftPortal::new); + register(net.minecraft.server.BlockPotatoes.class, org.bukkit.craftbukkit.block.impl.CraftPotatoes::new); + register(net.minecraft.server.BlockPoweredRail.class, org.bukkit.craftbukkit.block.impl.CraftPoweredRail::new); + register(net.minecraft.server.BlockPressurePlateBinary.class, org.bukkit.craftbukkit.block.impl.CraftPressurePlateBinary::new); + register(net.minecraft.server.BlockPressurePlateWeighted.class, org.bukkit.craftbukkit.block.impl.CraftPressurePlateWeighted::new); + register(net.minecraft.server.BlockPumpkinCarved.class, org.bukkit.craftbukkit.block.impl.CraftPumpkinCarved::new); + register(net.minecraft.server.BlockRedstoneComparator.class, org.bukkit.craftbukkit.block.impl.CraftRedstoneComparator::new); + register(net.minecraft.server.BlockRedstoneLamp.class, org.bukkit.craftbukkit.block.impl.CraftRedstoneLamp::new); + register(net.minecraft.server.BlockRedstoneOre.class, org.bukkit.craftbukkit.block.impl.CraftRedstoneOre::new); + register(net.minecraft.server.BlockRedstoneTorch.class, org.bukkit.craftbukkit.block.impl.CraftRedstoneTorch::new); + register(net.minecraft.server.BlockRedstoneTorchWall.class, org.bukkit.craftbukkit.block.impl.CraftRedstoneTorchWall::new); + register(net.minecraft.server.BlockRedstoneWire.class, org.bukkit.craftbukkit.block.impl.CraftRedstoneWire::new); + register(net.minecraft.server.BlockReed.class, org.bukkit.craftbukkit.block.impl.CraftReed::new); + register(net.minecraft.server.BlockRepeater.class, org.bukkit.craftbukkit.block.impl.CraftRepeater::new); + register(net.minecraft.server.BlockRotatable.class, org.bukkit.craftbukkit.block.impl.CraftRotatable::new); + register(net.minecraft.server.BlockSapling.class, org.bukkit.craftbukkit.block.impl.CraftSapling::new); + register(net.minecraft.server.BlockSeaPickle.class, org.bukkit.craftbukkit.block.impl.CraftSeaPickle::new); + register(net.minecraft.server.BlockShulkerBox.class, org.bukkit.craftbukkit.block.impl.CraftShulkerBox::new); + register(net.minecraft.server.BlockSkull.class, org.bukkit.craftbukkit.block.impl.CraftSkull::new); + register(net.minecraft.server.BlockSkullPlayer.class, org.bukkit.craftbukkit.block.impl.CraftSkullPlayer::new); + register(net.minecraft.server.BlockSkullPlayerWall.class, org.bukkit.craftbukkit.block.impl.CraftSkullPlayerWall::new); + register(net.minecraft.server.BlockSkullWall.class, org.bukkit.craftbukkit.block.impl.CraftSkullWall::new); + register(net.minecraft.server.BlockSnow.class, org.bukkit.craftbukkit.block.impl.CraftSnow::new); + register(net.minecraft.server.BlockSoil.class, org.bukkit.craftbukkit.block.impl.CraftSoil::new); + register(net.minecraft.server.BlockStainedGlassPane.class, org.bukkit.craftbukkit.block.impl.CraftStainedGlassPane::new); + register(net.minecraft.server.BlockStairs.class, org.bukkit.craftbukkit.block.impl.CraftStairs::new); + register(net.minecraft.server.BlockStem.class, org.bukkit.craftbukkit.block.impl.CraftStem::new); + register(net.minecraft.server.BlockStemAttached.class, org.bukkit.craftbukkit.block.impl.CraftStemAttached::new); + register(net.minecraft.server.BlockStepAbstract.class, org.bukkit.craftbukkit.block.impl.CraftStepAbstract::new); + register(net.minecraft.server.BlockStoneButton.class, org.bukkit.craftbukkit.block.impl.CraftStoneButton::new); + register(net.minecraft.server.BlockStructure.class, org.bukkit.craftbukkit.block.impl.CraftStructure::new); + register(net.minecraft.server.BlockTNT.class, org.bukkit.craftbukkit.block.impl.CraftTNT::new); + register(net.minecraft.server.BlockTallPlantFlower.class, org.bukkit.craftbukkit.block.impl.CraftTallPlantFlower::new); + register(net.minecraft.server.BlockTallPlantShearable.class, org.bukkit.craftbukkit.block.impl.CraftTallPlantShearable::new); + register(net.minecraft.server.BlockTallSeaGrass.class, org.bukkit.craftbukkit.block.impl.CraftTallSeaGrass::new); + register(net.minecraft.server.BlockTorchWall.class, org.bukkit.craftbukkit.block.impl.CraftTorchWall::new); + register(net.minecraft.server.BlockTrapdoor.class, org.bukkit.craftbukkit.block.impl.CraftTrapdoor::new); + register(net.minecraft.server.BlockTripwire.class, org.bukkit.craftbukkit.block.impl.CraftTripwire::new); + register(net.minecraft.server.BlockTripwireHook.class, org.bukkit.craftbukkit.block.impl.CraftTripwireHook::new); + register(net.minecraft.server.BlockTurtleEgg.class, org.bukkit.craftbukkit.block.impl.CraftTurtleEgg::new); + register(net.minecraft.server.BlockVine.class, org.bukkit.craftbukkit.block.impl.CraftVine::new); + register(net.minecraft.server.BlockWallSign.class, org.bukkit.craftbukkit.block.impl.CraftWallSign::new); + register(net.minecraft.server.BlockWitherSkull.class, org.bukkit.craftbukkit.block.impl.CraftWitherSkull::new); + register(net.minecraft.server.BlockWitherSkullWall.class, org.bukkit.craftbukkit.block.impl.CraftWitherSkullWall::new); + register(net.minecraft.server.BlockWoodButton.class, org.bukkit.craftbukkit.block.impl.CraftWoodButton::new); + // + } + + private static void register(Class nms, Function bukkit) { + Preconditions.checkState(MAP.put(nms, bukkit) == null, "Duplicate mapping %s->%s", nms, bukkit); + } + + public static CraftBlockData newData(Material material, String data) { + Preconditions.checkArgument(material == null || material.isBlock(), "Cannot get data for not block %s", material); + + IBlockData blockData; + Block block = CraftMagicNumbers.getBlock(material); + Map, Comparable> parsed = null; + + // Data provided, use it + if (data != null) { + try { + // Material provided, force that material in + if (block != null) { + data = IRegistry.BLOCK.getKey(block) + data; + } + + StringReader reader = new StringReader(data); + ArgumentBlock arg = new ArgumentBlock(reader, false).a(false); + Preconditions.checkArgument(!reader.canRead(), "Spurious trailing data: " + data); + + blockData = arg.getBlockData(); + parsed = arg.getStateMap(); + } catch (CommandSyntaxException ex) { + throw new IllegalArgumentException("Could not parse data: " + data, ex); + } + } else { + blockData = block.getBlockData(); + } + + CraftBlockData craft = fromData(blockData); + craft.parsedStates = parsed; + return craft; + } + + // Paper start - optimize creating BlockData to not need a map lookup + static { + // Initialize cached data for all IBlockData instances after registration + Block.REGISTRY_ID.iterator().forEachRemaining(IBlockData::createCraftBlockData); + } + public static CraftBlockData fromData(IBlockData data) { + return data.createCraftBlockData(); + } + + public static CraftBlockData createData(IBlockData data) { + // Paper end + return MAP.getOrDefault(data.getBlock().getClass(), CraftBlockData::new).apply(data); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftDirectional.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftDirectional.java new file mode 100644 index 000000000000..7b3ec8811c27 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftDirectional.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.block.data; + +import org.bukkit.block.data.Directional; + +public abstract class CraftDirectional extends CraftBlockData implements Directional { + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum("facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftLevelled.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftLevelled.java new file mode 100644 index 000000000000..3d4afa748252 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftLevelled.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.block.data; + +import org.bukkit.block.data.Levelled; + +public abstract class CraftLevelled extends CraftBlockData implements Levelled { + + private static final net.minecraft.server.BlockStateInteger LEVEL = getInteger("level"); + + @Override + public int getLevel() { + return get(LEVEL); + } + + @Override + public void setLevel(int level) { + set(LEVEL, level); + } + + @Override + public int getMaximumLevel() { + return getMax(LEVEL); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftLightable.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftLightable.java new file mode 100644 index 000000000000..cfe4b26e41d4 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftLightable.java @@ -0,0 +1,18 @@ +package org.bukkit.craftbukkit.block.data; + +import org.bukkit.block.data.Lightable; + +public abstract class CraftLightable extends CraftBlockData implements Lightable { + + private static final net.minecraft.server.BlockStateBoolean LIT = getBoolean("lit"); + + @Override + public boolean isLit() { + return get(LIT); + } + + @Override + public void setLit(boolean lit) { + set(LIT, lit); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftMultipleFacing.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftMultipleFacing.java new file mode 100644 index 000000000000..5d2cc2a9faf6 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftMultipleFacing.java @@ -0,0 +1,46 @@ +package org.bukkit.craftbukkit.block.data; + +import org.bukkit.block.data.MultipleFacing; + +public abstract class CraftMultipleFacing extends CraftBlockData implements MultipleFacing { + + private static final net.minecraft.server.BlockStateBoolean[] FACES = new net.minecraft.server.BlockStateBoolean[]{ + getBoolean("north", true), getBoolean("east", true), getBoolean("south", true), getBoolean("west", true), getBoolean("up", true), getBoolean("down", true) + }; + + @Override + public boolean hasFace(org.bukkit.block.BlockFace face) { + return get(FACES[face.ordinal()]); + } + + @Override + public void setFace(org.bukkit.block.BlockFace face, boolean has) { + set(FACES[face.ordinal()], has); + } + + @Override + public java.util.Set getFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null && get(FACES[i])) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + @Override + public java.util.Set getAllowedFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftOpenable.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftOpenable.java new file mode 100644 index 000000000000..31abd411ed21 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftOpenable.java @@ -0,0 +1,18 @@ +package org.bukkit.craftbukkit.block.data; + +import org.bukkit.block.data.Openable; + +public abstract class CraftOpenable extends CraftBlockData implements Openable { + + private static final net.minecraft.server.BlockStateBoolean OPEN = getBoolean("open"); + + @Override + public boolean isOpen() { + return get(OPEN); + } + + @Override + public void setOpen(boolean open) { + set(OPEN, open); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftOrientable.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftOrientable.java new file mode 100644 index 000000000000..72cc0aa978bb --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftOrientable.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.block.data; + +import org.bukkit.block.data.Orientable; + +public class CraftOrientable extends CraftBlockData implements Orientable { + + private static final net.minecraft.server.BlockStateEnum AXIS = getEnum("axis"); + + @Override + public org.bukkit.Axis getAxis() { + return get(AXIS, org.bukkit.Axis.class); + } + + @Override + public void setAxis(org.bukkit.Axis axis) { + set(AXIS, axis); + } + + @Override + public java.util.Set getAxes() { + return getValues(AXIS, org.bukkit.Axis.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftPowerable.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftPowerable.java new file mode 100644 index 000000000000..f932cf17fd2a --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftPowerable.java @@ -0,0 +1,18 @@ +package org.bukkit.craftbukkit.block.data; + +import org.bukkit.block.data.Powerable; + +public abstract class CraftPowerable extends CraftBlockData implements Powerable { + + private static final net.minecraft.server.BlockStateBoolean POWERED = getBoolean("powered"); + + @Override + public boolean isPowered() { + return get(POWERED); + } + + @Override + public void setPowered(boolean powered) { + set(POWERED, powered); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftRail.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftRail.java new file mode 100644 index 000000000000..5ee64559b6df --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftRail.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.block.data; + +import org.bukkit.block.data.Rail; + +public abstract class CraftRail extends CraftBlockData implements Rail { + + private static final net.minecraft.server.BlockStateEnum SHAPE = getEnum("shape"); + + @Override + public Shape getShape() { + return get(SHAPE, Shape.class); + } + + @Override + public void setShape(Shape shape) { + set(SHAPE, shape); + } + + @Override + public java.util.Set getShapes() { + return getValues(SHAPE, Shape.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftRotatable.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftRotatable.java new file mode 100644 index 000000000000..78668da5b479 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftRotatable.java @@ -0,0 +1,107 @@ +package org.bukkit.craftbukkit.block.data; + +import org.bukkit.block.data.Rotatable; + +public abstract class CraftRotatable extends CraftBlockData implements Rotatable { + + private static final net.minecraft.server.BlockStateInteger ROTATION = getInteger("rotation"); + + @Override + public org.bukkit.block.BlockFace getRotation() { + int data = get(ROTATION); + switch (data) { + case 0x0: + return org.bukkit.block.BlockFace.SOUTH; + case 0x1: + return org.bukkit.block.BlockFace.SOUTH_SOUTH_WEST; + case 0x2: + return org.bukkit.block.BlockFace.SOUTH_WEST; + case 0x3: + return org.bukkit.block.BlockFace.WEST_SOUTH_WEST; + case 0x4: + return org.bukkit.block.BlockFace.WEST; + case 0x5: + return org.bukkit.block.BlockFace.WEST_NORTH_WEST; + case 0x6: + return org.bukkit.block.BlockFace.NORTH_WEST; + case 0x7: + return org.bukkit.block.BlockFace.NORTH_NORTH_WEST; + case 0x8: + return org.bukkit.block.BlockFace.NORTH; + case 0x9: + return org.bukkit.block.BlockFace.NORTH_NORTH_EAST; + case 0xA: + return org.bukkit.block.BlockFace.NORTH_EAST; + case 0xB: + return org.bukkit.block.BlockFace.EAST_NORTH_EAST; + case 0xC: + return org.bukkit.block.BlockFace.EAST; + case 0xD: + return org.bukkit.block.BlockFace.EAST_SOUTH_EAST; + case 0xE: + return org.bukkit.block.BlockFace.SOUTH_EAST; + case 0xF: + return org.bukkit.block.BlockFace.SOUTH_SOUTH_EAST; + default: + throw new IllegalArgumentException("Unknown rotation " + data); + } + } + + @Override + public void setRotation(org.bukkit.block.BlockFace rotation) { + int val; + switch (rotation) { + case SOUTH: + val = 0x0; + break; + case SOUTH_SOUTH_WEST: + val = 0x1; + break; + case SOUTH_WEST: + val = 0x2; + break; + case WEST_SOUTH_WEST: + val = 0x3; + break; + case WEST: + val = 0x4; + break; + case WEST_NORTH_WEST: + val = 0x5; + break; + case NORTH_WEST: + val = 0x6; + break; + case NORTH_NORTH_WEST: + val = 0x7; + break; + case NORTH: + val = 0x8; + break; + case NORTH_NORTH_EAST: + val = 0x9; + break; + case NORTH_EAST: + val = 0xA; + break; + case EAST_NORTH_EAST: + val = 0xB; + break; + case EAST: + val = 0xC; + break; + case EAST_SOUTH_EAST: + val = 0xD; + break; + case SOUTH_EAST: + val = 0xE; + break; + case SOUTH_SOUTH_EAST: + val = 0xF; + break; + default: + throw new IllegalArgumentException("Illegal rotation " + rotation); + } + set(ROTATION, val); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftSnowable.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftSnowable.java new file mode 100644 index 000000000000..e59c3d230b27 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftSnowable.java @@ -0,0 +1,18 @@ +package org.bukkit.craftbukkit.block.data; + +import org.bukkit.block.data.Snowable; + +public abstract class CraftSnowable extends CraftBlockData implements Snowable { + + private static final net.minecraft.server.BlockStateBoolean SNOWY = getBoolean("snowy"); + + @Override + public boolean isSnowy() { + return get(SNOWY); + } + + @Override + public void setSnowy(boolean snowy) { + set(SNOWY, snowy); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/CraftWaterlogged.java b/src/main/java/org/bukkit/craftbukkit/block/data/CraftWaterlogged.java new file mode 100644 index 000000000000..011c9aea84a1 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/CraftWaterlogged.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data; + +import org.bukkit.block.data.Waterlogged; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftWaterlogged extends CraftBlockData implements Waterlogged { + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean("waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftBed.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftBed.java new file mode 100644 index 000000000000..f86ed2591a8b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftBed.java @@ -0,0 +1,25 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Bed; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftBed extends CraftBlockData implements Bed { + + private static final net.minecraft.server.BlockStateEnum PART = getEnum("part"); + private static final net.minecraft.server.BlockStateBoolean OCCUPIED = getBoolean("occupied"); + + @Override + public Part getPart() { + return get(PART, Part.class); + } + + @Override + public void setPart(Part part) { + set(PART, part); + } + + @Override + public boolean isOccupied() { + return get(OCCUPIED); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftBrewingStand.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftBrewingStand.java new file mode 100644 index 000000000000..9349e02fe9ab --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftBrewingStand.java @@ -0,0 +1,39 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.BrewingStand; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftBrewingStand extends CraftBlockData implements BrewingStand { + + private static final net.minecraft.server.BlockStateBoolean[] HAS_BOTTLE = new net.minecraft.server.BlockStateBoolean[]{ + getBoolean("has_bottle_0"), getBoolean("has_bottle_1"), getBoolean("has_bottle_2") + }; + + @Override + public boolean hasBottle(int bottle) { + return get(HAS_BOTTLE[bottle]); + } + + @Override + public void setBottle(int bottle, boolean has) { + set(HAS_BOTTLE[bottle], has); + } + + @Override + public java.util.Set getBottles() { + com.google.common.collect.ImmutableSet.Builder bottles = com.google.common.collect.ImmutableSet.builder(); + + for (int index = 0; index < getMaximumBottles(); index++) { + if (hasBottle(index)) { + bottles.add(index); + } + } + + return bottles.build(); + } + + @Override + public int getMaximumBottles() { + return HAS_BOTTLE.length; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftBubbleColumn.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftBubbleColumn.java new file mode 100644 index 000000000000..eedb5102220b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftBubbleColumn.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.BubbleColumn; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftBubbleColumn extends CraftBlockData implements BubbleColumn { + + private static final net.minecraft.server.BlockStateBoolean DRAG = getBoolean("drag"); + + @Override + public boolean isDrag() { + return get(DRAG); + } + + @Override + public void setDrag(boolean drag) { + set(DRAG, drag); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftCake.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftCake.java new file mode 100644 index 000000000000..aae60937b1cf --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftCake.java @@ -0,0 +1,24 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Cake; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftCake extends CraftBlockData implements Cake { + + private static final net.minecraft.server.BlockStateInteger BITES = getInteger("bites"); + + @Override + public int getBites() { + return get(BITES); + } + + @Override + public void setBites(int bites) { + set(BITES, bites); + } + + @Override + public int getMaximumBites() { + return getMax(BITES); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftChest.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftChest.java new file mode 100644 index 000000000000..70473ef42024 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftChest.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Chest; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftChest extends CraftBlockData implements Chest { + + private static final net.minecraft.server.BlockStateEnum TYPE = getEnum("type"); + + @Override + public Type getType() { + return get(TYPE, Type.class); + } + + @Override + public void setType(Type type) { + set(TYPE, type); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftCommandBlock.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftCommandBlock.java new file mode 100644 index 000000000000..ddb9ac5e0224 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftCommandBlock.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.CommandBlock; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftCommandBlock extends CraftBlockData implements CommandBlock { + + private static final net.minecraft.server.BlockStateBoolean CONDITIONAL = getBoolean("conditional"); + + @Override + public boolean isConditional() { + return get(CONDITIONAL); + } + + @Override + public void setConditional(boolean conditional) { + set(CONDITIONAL, conditional); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftComparator.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftComparator.java new file mode 100644 index 000000000000..4f3130a5f4e9 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftComparator.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Comparator; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftComparator extends CraftBlockData implements Comparator { + + private static final net.minecraft.server.BlockStateEnum MODE = getEnum("mode"); + + @Override + public Mode getMode() { + return get(MODE, Mode.class); + } + + @Override + public void setMode(Mode mode) { + set(MODE, mode); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftDaylightDetector.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftDaylightDetector.java new file mode 100644 index 000000000000..842fe51f98a7 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftDaylightDetector.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.DaylightDetector; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftDaylightDetector extends CraftBlockData implements DaylightDetector { + + private static final net.minecraft.server.BlockStateBoolean INVERTED = getBoolean("inverted"); + + @Override + public boolean isInverted() { + return get(INVERTED); + } + + @Override + public void setInverted(boolean inverted) { + set(INVERTED, inverted); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftDispenser.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftDispenser.java new file mode 100644 index 000000000000..5d43240e7aee --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftDispenser.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Dispenser; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftDispenser extends CraftBlockData implements Dispenser { + + private static final net.minecraft.server.BlockStateBoolean TRIGGERED = getBoolean("triggered"); + + @Override + public boolean isTriggered() { + return get(TRIGGERED); + } + + @Override + public void setTriggered(boolean triggered) { + set(TRIGGERED, triggered); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftDoor.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftDoor.java new file mode 100644 index 000000000000..3fc75dd2a657 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftDoor.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Door; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftDoor extends CraftBlockData implements Door { + + private static final net.minecraft.server.BlockStateEnum HINGE = getEnum("hinge"); + + @Override + public Hinge getHinge() { + return get(HINGE, Hinge.class); + } + + @Override + public void setHinge(Hinge hinge) { + set(HINGE, hinge); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftEndPortalFrame.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftEndPortalFrame.java new file mode 100644 index 000000000000..84cd1d03912c --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftEndPortalFrame.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.EndPortalFrame; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftEndPortalFrame extends CraftBlockData implements EndPortalFrame { + + private static final net.minecraft.server.BlockStateBoolean EYE = getBoolean("eye"); + + @Override + public boolean hasEye() { + return get(EYE); + } + + @Override + public void setEye(boolean eye) { + set(EYE, eye); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftFarmland.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftFarmland.java new file mode 100644 index 000000000000..515ee588ca13 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftFarmland.java @@ -0,0 +1,24 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Farmland; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftFarmland extends CraftBlockData implements Farmland { + + private static final net.minecraft.server.BlockStateInteger MOISTURE = getInteger("moisture"); + + @Override + public int getMoisture() { + return get(MOISTURE); + } + + @Override + public void setMoisture(int moisture) { + set(MOISTURE, moisture); + } + + @Override + public int getMaximumMoisture() { + return getMax(MOISTURE); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftGate.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftGate.java new file mode 100644 index 000000000000..1f6ad5d98ca5 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftGate.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Gate; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftGate extends CraftBlockData implements Gate { + + private static final net.minecraft.server.BlockStateBoolean IN_WALL = getBoolean("in_wall"); + + @Override + public boolean isInWall() { + return get(IN_WALL); + } + + @Override + public void setInWall(boolean inWall) { + set(IN_WALL, inWall); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftHopper.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftHopper.java new file mode 100644 index 000000000000..055629fde540 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftHopper.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Hopper; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftHopper extends CraftBlockData implements Hopper { + + private static final net.minecraft.server.BlockStateBoolean ENABLED = getBoolean("enabled"); + + @Override + public boolean isEnabled() { + return get(ENABLED); + } + + @Override + public void setEnabled(boolean enabled) { + set(ENABLED, enabled); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftJukebox.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftJukebox.java new file mode 100644 index 000000000000..32eab86d7dd0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftJukebox.java @@ -0,0 +1,14 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.block.data.type.Jukebox; + +public abstract class CraftJukebox extends CraftBlockData implements Jukebox { + + private static final net.minecraft.server.BlockStateBoolean HAS_RECORD = getBoolean("has_record"); + + @Override + public boolean hasRecord() { + return get(HAS_RECORD); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftLeaves.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftLeaves.java new file mode 100644 index 000000000000..a233f5550291 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftLeaves.java @@ -0,0 +1,30 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Leaves; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public class CraftLeaves extends CraftBlockData implements Leaves { + + private static final net.minecraft.server.BlockStateInteger DISTANCE = getInteger("distance"); + private static final net.minecraft.server.BlockStateBoolean PERSISTENT = getBoolean("persistent"); + + @Override + public boolean isPersistent() { + return get(PERSISTENT); + } + + @Override + public void setPersistent(boolean persistent) { + set(PERSISTENT, persistent); + } + + @Override + public int getDistance() { + return get(DISTANCE); + } + + @Override + public void setDistance(int distance) { + set(DISTANCE, distance); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftNoteBlock.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftNoteBlock.java new file mode 100644 index 000000000000..51814a497514 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftNoteBlock.java @@ -0,0 +1,30 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.NoteBlock; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftNoteBlock extends CraftBlockData implements NoteBlock { + + private static final net.minecraft.server.BlockStateEnum INSTRUMENT = getEnum("instrument"); + private static final net.minecraft.server.BlockStateInteger NOTE = getInteger("note"); + + @Override + public org.bukkit.Instrument getInstrument() { + return get(INSTRUMENT, org.bukkit.Instrument.class); + } + + @Override + public void setInstrument(org.bukkit.Instrument instrument) { + set(INSTRUMENT, instrument); + } + + @Override + public org.bukkit.Note getNote() { + return new org.bukkit.Note(get(NOTE)); + } + + @Override + public void setNote(org.bukkit.Note note) { + set(NOTE, (int) note.getId()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftPiston.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftPiston.java new file mode 100644 index 000000000000..8cebb751e184 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftPiston.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Piston; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftPiston extends CraftBlockData implements Piston { + + private static final net.minecraft.server.BlockStateBoolean EXTENDED = getBoolean("extended"); + + @Override + public boolean isExtended() { + return get(EXTENDED); + } + + @Override + public void setExtended(boolean extended) { + set(EXTENDED, extended); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftPistonHead.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftPistonHead.java new file mode 100644 index 000000000000..3fb24d4b85a3 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftPistonHead.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.PistonHead; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftPistonHead extends CraftBlockData implements PistonHead { + + private static final net.minecraft.server.BlockStateBoolean SHORT = getBoolean("short"); + + @Override + public boolean isShort() { + return get(SHORT); + } + + @Override + public void setShort(boolean _short) { + set(SHORT, _short); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftRedstoneWire.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftRedstoneWire.java new file mode 100644 index 000000000000..c5ec1c55dd14 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftRedstoneWire.java @@ -0,0 +1,53 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.RedstoneWire; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftRedstoneWire extends CraftBlockData implements RedstoneWire { + + private static final net.minecraft.server.BlockStateEnum NORTH = getEnum("north"); + private static final net.minecraft.server.BlockStateEnum EAST = getEnum("east"); + private static final net.minecraft.server.BlockStateEnum SOUTH = getEnum("south"); + private static final net.minecraft.server.BlockStateEnum WEST = getEnum("west"); + + @Override + public Connection getFace(org.bukkit.block.BlockFace face) { + switch (face) { + case NORTH: + return get(NORTH, Connection.class); + case EAST: + return get(EAST, Connection.class); + case SOUTH: + return get(SOUTH, Connection.class); + case WEST: + return get(WEST, Connection.class); + default: + throw new IllegalArgumentException("Cannot have face " + face); + } + } + + @Override + public void setFace(org.bukkit.block.BlockFace face, Connection connection) { + switch (face) { + case NORTH: + set(NORTH, connection); + break; + case EAST: + set(EAST, connection); + break; + case SOUTH: + set(SOUTH, connection); + break; + case WEST: + set(WEST, connection); + break; + default: + throw new IllegalArgumentException("Cannot have face " + face); + } + } + + @Override + public java.util.Set getAllowedFaces() { + return com.google.common.collect.ImmutableSet.of(org.bukkit.block.BlockFace.NORTH, org.bukkit.block.BlockFace.EAST, org.bukkit.block.BlockFace.SOUTH, org.bukkit.block.BlockFace.WEST); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftRepeater.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftRepeater.java new file mode 100644 index 000000000000..482a4ed7587f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftRepeater.java @@ -0,0 +1,40 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Repeater; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftRepeater extends CraftBlockData implements Repeater { + + private static final net.minecraft.server.BlockStateInteger DELAY = getInteger("delay"); + private static final net.minecraft.server.BlockStateBoolean LOCKED = getBoolean("locked"); + + @Override + public int getDelay() { + return get(DELAY); + } + + @Override + public void setDelay(int delay) { + set(DELAY, delay); + } + + @Override + public int getMinimumDelay() { + return getMin(DELAY); + } + + @Override + public int getMaximumDelay() { + return getMax(DELAY); + } + + @Override + public boolean isLocked() { + return get(LOCKED); + } + + @Override + public void setLocked(boolean locked) { + set(LOCKED, locked); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSapling.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSapling.java new file mode 100644 index 000000000000..dd96acff1b99 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSapling.java @@ -0,0 +1,24 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Sapling; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftSapling extends CraftBlockData implements Sapling { + + private static final net.minecraft.server.BlockStateInteger STAGE = getInteger("stage"); + + @Override + public int getStage() { + return get(STAGE); + } + + @Override + public void setStage(int stage) { + set(STAGE, stage); + } + + @Override + public int getMaximumStage() { + return getMax(STAGE); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSeaPickle.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSeaPickle.java new file mode 100644 index 000000000000..dc138d9c76e7 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSeaPickle.java @@ -0,0 +1,29 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.SeaPickle; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftSeaPickle extends CraftBlockData implements SeaPickle { + + private static final net.minecraft.server.BlockStateInteger PICKLES = getInteger("pickles"); + + @Override + public int getPickles() { + return get(PICKLES); + } + + @Override + public void setPickles(int pickles) { + set(PICKLES, pickles); + } + + @Override + public int getMinimumPickles() { + return getMin(PICKLES); + } + + @Override + public int getMaximumPickles() { + return getMax(PICKLES); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSlab.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSlab.java new file mode 100644 index 000000000000..1fde489e5579 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSlab.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Slab; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftSlab extends CraftBlockData implements Slab { + + private static final net.minecraft.server.BlockStateEnum TYPE = getEnum("type"); + + @Override + public Type getType() { + return get(TYPE, Type.class); + } + + @Override + public void setType(Type type) { + set(TYPE, type); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSnow.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSnow.java new file mode 100644 index 000000000000..879a5d679a02 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSnow.java @@ -0,0 +1,29 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Snow; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public class CraftSnow extends CraftBlockData implements Snow { + + private static final net.minecraft.server.BlockStateInteger LAYERS = getInteger("layers"); + + @Override + public int getLayers() { + return get(LAYERS); + } + + @Override + public void setLayers(int layers) { + set(LAYERS, layers); + } + + @Override + public int getMinimumLayers() { + return getMin(LAYERS); + } + + @Override + public int getMaximumLayers() { + return getMax(LAYERS); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftStairs.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftStairs.java new file mode 100644 index 000000000000..c6ccd382f096 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftStairs.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Stairs; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftStairs extends CraftBlockData implements Stairs { + + private static final net.minecraft.server.BlockStateEnum SHAPE = getEnum("shape"); + + @Override + public Shape getShape() { + return get(SHAPE, Shape.class); + } + + @Override + public void setShape(Shape shape) { + set(SHAPE, shape); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftStructureBlock.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftStructureBlock.java new file mode 100644 index 000000000000..8b8a82bd36aa --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftStructureBlock.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.StructureBlock; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftStructureBlock extends CraftBlockData implements StructureBlock { + + private static final net.minecraft.server.BlockStateEnum MODE = getEnum("mode"); + + @Override + public Mode getMode() { + return get(MODE, Mode.class); + } + + @Override + public void setMode(Mode mode) { + set(MODE, mode); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSwitch.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSwitch.java new file mode 100644 index 000000000000..2761b3710b00 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftSwitch.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Switch; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftSwitch extends CraftBlockData implements Switch { + + private static final net.minecraft.server.BlockStateEnum FACE = getEnum("face"); + + @Override + public Face getFace() { + return get(FACE, Face.class); + } + + @Override + public void setFace(Face face) { + set(FACE, face); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftTNT.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftTNT.java new file mode 100644 index 000000000000..18662a805eaa --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftTNT.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.TNT; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftTNT extends CraftBlockData implements TNT { + + private static final net.minecraft.server.BlockStateBoolean UNSTABLE = getBoolean("unstable"); + + @Override + public boolean isUnstable() { + return get(UNSTABLE); + } + + @Override + public void setUnstable(boolean unstable) { + set(UNSTABLE, unstable); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftTechnicalPiston.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftTechnicalPiston.java new file mode 100644 index 000000000000..e42fe0d675df --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftTechnicalPiston.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.TechnicalPiston; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftTechnicalPiston extends CraftBlockData implements TechnicalPiston { + + private static final net.minecraft.server.BlockStateEnum TYPE = getEnum("type"); + + @Override + public Type getType() { + return get(TYPE, Type.class); + } + + @Override + public void setType(Type type) { + set(TYPE, type); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftTripwire.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftTripwire.java new file mode 100644 index 000000000000..55bacd2e5bef --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftTripwire.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.Tripwire; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftTripwire extends CraftBlockData implements Tripwire { + + private static final net.minecraft.server.BlockStateBoolean DISARMED = getBoolean("disarmed"); + + @Override + public boolean isDisarmed() { + return get(DISARMED); + } + + @Override + public void setDisarmed(boolean disarmed) { + set(DISARMED, disarmed); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftTurtleEgg.java b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftTurtleEgg.java new file mode 100644 index 000000000000..dca37886e576 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/data/type/CraftTurtleEgg.java @@ -0,0 +1,45 @@ +package org.bukkit.craftbukkit.block.data.type; + +import org.bukkit.block.data.type.TurtleEgg; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +public abstract class CraftTurtleEgg extends CraftBlockData implements TurtleEgg { + + private static final net.minecraft.server.BlockStateInteger EGGS = getInteger("eggs"); + private static final net.minecraft.server.BlockStateInteger HATCH = getInteger("hatch"); + + @Override + public int getEggs() { + return get(EGGS); + } + + @Override + public void setEggs(int eggs) { + set(EGGS, eggs); + } + + @Override + public int getMinimumEggs() { + return getMin(EGGS); + } + + @Override + public int getMaximumEggs() { + return getMax(EGGS); + } + + @Override + public int getHatch() { + return get(HATCH); + } + + @Override + public void setHatch(int hatch) { + set(HATCH, hatch); + } + + @Override + public int getMaximumHatch() { + return getMax(HATCH); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftAnvil.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftAnvil.java new file mode 100644 index 000000000000..3010e8fe120f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftAnvil.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftAnvil extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Directional { + + public CraftAnvil() { + super(); + } + + public CraftAnvil(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockAnvil.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBanner.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBanner.java new file mode 100644 index 000000000000..d95c548bc425 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBanner.java @@ -0,0 +1,118 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftBanner extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Rotatable { + + public CraftBanner() { + super(); + } + + public CraftBanner(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftRotatable + + private static final net.minecraft.server.BlockStateInteger ROTATION = getInteger(net.minecraft.server.BlockBanner.class, "rotation"); + + @Override + public org.bukkit.block.BlockFace getRotation() { + int data = get(ROTATION); + switch (data) { + case 0x0: + return org.bukkit.block.BlockFace.SOUTH; + case 0x1: + return org.bukkit.block.BlockFace.SOUTH_SOUTH_WEST; + case 0x2: + return org.bukkit.block.BlockFace.SOUTH_WEST; + case 0x3: + return org.bukkit.block.BlockFace.WEST_SOUTH_WEST; + case 0x4: + return org.bukkit.block.BlockFace.WEST; + case 0x5: + return org.bukkit.block.BlockFace.WEST_NORTH_WEST; + case 0x6: + return org.bukkit.block.BlockFace.NORTH_WEST; + case 0x7: + return org.bukkit.block.BlockFace.NORTH_NORTH_WEST; + case 0x8: + return org.bukkit.block.BlockFace.NORTH; + case 0x9: + return org.bukkit.block.BlockFace.NORTH_NORTH_EAST; + case 0xA: + return org.bukkit.block.BlockFace.NORTH_EAST; + case 0xB: + return org.bukkit.block.BlockFace.EAST_NORTH_EAST; + case 0xC: + return org.bukkit.block.BlockFace.EAST; + case 0xD: + return org.bukkit.block.BlockFace.EAST_SOUTH_EAST; + case 0xE: + return org.bukkit.block.BlockFace.SOUTH_EAST; + case 0xF: + return org.bukkit.block.BlockFace.SOUTH_SOUTH_EAST; + default: + throw new IllegalArgumentException("Unknown rotation " + data); + } + } + + @Override + public void setRotation(org.bukkit.block.BlockFace rotation) { + int val; + switch (rotation) { + case SOUTH: + val = 0x0; + break; + case SOUTH_SOUTH_WEST: + val = 0x1; + break; + case SOUTH_WEST: + val = 0x2; + break; + case WEST_SOUTH_WEST: + val = 0x3; + break; + case WEST: + val = 0x4; + break; + case WEST_NORTH_WEST: + val = 0x5; + break; + case NORTH_WEST: + val = 0x6; + break; + case NORTH_NORTH_WEST: + val = 0x7; + break; + case NORTH: + val = 0x8; + break; + case NORTH_NORTH_EAST: + val = 0x9; + break; + case NORTH_EAST: + val = 0xA; + break; + case EAST_NORTH_EAST: + val = 0xB; + break; + case EAST: + val = 0xC; + break; + case EAST_SOUTH_EAST: + val = 0xD; + break; + case SOUTH_EAST: + val = 0xE; + break; + case SOUTH_SOUTH_EAST: + val = 0xF; + break; + default: + throw new IllegalArgumentException("Illegal rotation " + rotation); + } + set(ROTATION, val); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBannerWall.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBannerWall.java new file mode 100644 index 000000000000..48215035fcbb --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBannerWall.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftBannerWall extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Directional { + + public CraftBannerWall() { + super(); + } + + public CraftBannerWall(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockBannerWall.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBed.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBed.java new file mode 100644 index 000000000000..c625c964e241 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBed.java @@ -0,0 +1,54 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftBed extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Bed, org.bukkit.block.data.Directional { + + public CraftBed() { + super(); + } + + public CraftBed(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftBed + + private static final net.minecraft.server.BlockStateEnum PART = getEnum(net.minecraft.server.BlockBed.class, "part"); + private static final net.minecraft.server.BlockStateBoolean OCCUPIED = getBoolean(net.minecraft.server.BlockBed.class, "occupied"); + + @Override + public Part getPart() { + return get(PART, Part.class); + } + + @Override + public void setPart(Part part) { + set(PART, part); + } + + @Override + public boolean isOccupied() { + return get(OCCUPIED); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockBed.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBeetroot.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBeetroot.java new file mode 100644 index 000000000000..4e9a27f47c0a --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBeetroot.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftBeetroot extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Ageable { + + public CraftBeetroot() { + super(); + } + + public CraftBeetroot(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftAgeable + + private static final net.minecraft.server.BlockStateInteger AGE = getInteger(net.minecraft.server.BlockBeetroot.class, "age"); + + @Override + public int getAge() { + return get(AGE); + } + + @Override + public void setAge(int age) { + set(AGE, age); + } + + @Override + public int getMaximumAge() { + return getMax(AGE); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBrewingStand.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBrewingStand.java new file mode 100644 index 000000000000..fe0e7dec6edd --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBrewingStand.java @@ -0,0 +1,49 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftBrewingStand extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.BrewingStand { + + public CraftBrewingStand() { + super(); + } + + public CraftBrewingStand(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftBrewingStand + + private static final net.minecraft.server.BlockStateBoolean[] HAS_BOTTLE = new net.minecraft.server.BlockStateBoolean[]{ + getBoolean(net.minecraft.server.BlockBrewingStand.class, "has_bottle_0"), getBoolean(net.minecraft.server.BlockBrewingStand.class, "has_bottle_1"), getBoolean(net.minecraft.server.BlockBrewingStand.class, "has_bottle_2") + }; + + @Override + public boolean hasBottle(int bottle) { + return get(HAS_BOTTLE[bottle]); + } + + @Override + public void setBottle(int bottle, boolean has) { + set(HAS_BOTTLE[bottle], has); + } + + @Override + public java.util.Set getBottles() { + com.google.common.collect.ImmutableSet.Builder bottles = com.google.common.collect.ImmutableSet.builder(); + + for (int index = 0; index < getMaximumBottles(); index++) { + if (hasBottle(index)) { + bottles.add(index); + } + } + + return bottles.build(); + } + + @Override + public int getMaximumBottles() { + return HAS_BOTTLE.length; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBubbleColumn.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBubbleColumn.java new file mode 100644 index 000000000000..574a5b9a712f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBubbleColumn.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftBubbleColumn extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.BubbleColumn { + + public CraftBubbleColumn() { + super(); + } + + public CraftBubbleColumn(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftBubbleColumn + + private static final net.minecraft.server.BlockStateBoolean DRAG = getBoolean(net.minecraft.server.BlockBubbleColumn.class, "drag"); + + @Override + public boolean isDrag() { + return get(DRAG); + } + + @Override + public void setDrag(boolean drag) { + set(DRAG, drag); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCactus.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCactus.java new file mode 100644 index 000000000000..e62f4dd17f70 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCactus.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftCactus extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Ageable { + + public CraftCactus() { + super(); + } + + public CraftCactus(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftAgeable + + private static final net.minecraft.server.BlockStateInteger AGE = getInteger(net.minecraft.server.BlockCactus.class, "age"); + + @Override + public int getAge() { + return get(AGE); + } + + @Override + public void setAge(int age) { + set(AGE, age); + } + + @Override + public int getMaximumAge() { + return getMax(AGE); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCake.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCake.java new file mode 100644 index 000000000000..fee523abe895 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCake.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftCake extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Cake { + + public CraftCake() { + super(); + } + + public CraftCake(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftCake + + private static final net.minecraft.server.BlockStateInteger BITES = getInteger(net.minecraft.server.BlockCake.class, "bites"); + + @Override + public int getBites() { + return get(BITES); + } + + @Override + public void setBites(int bites) { + set(BITES, bites); + } + + @Override + public int getMaximumBites() { + return getMax(BITES); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCarrots.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCarrots.java new file mode 100644 index 000000000000..9ab29636e5ec --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCarrots.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftCarrots extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Ageable { + + public CraftCarrots() { + super(); + } + + public CraftCarrots(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftAgeable + + private static final net.minecraft.server.BlockStateInteger AGE = getInteger(net.minecraft.server.BlockCarrots.class, "age"); + + @Override + public int getAge() { + return get(AGE); + } + + @Override + public void setAge(int age) { + set(AGE, age); + } + + @Override + public int getMaximumAge() { + return getMax(AGE); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCauldron.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCauldron.java new file mode 100644 index 000000000000..5ddeb71582ee --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCauldron.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftCauldron extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Levelled { + + public CraftCauldron() { + super(); + } + + public CraftCauldron(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftLevelled + + private static final net.minecraft.server.BlockStateInteger LEVEL = getInteger(net.minecraft.server.BlockCauldron.class, "level"); + + @Override + public int getLevel() { + return get(LEVEL); + } + + @Override + public void setLevel(int level) { + set(LEVEL, level); + } + + @Override + public int getMaximumLevel() { + return getMax(LEVEL); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftChest.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftChest.java new file mode 100644 index 000000000000..44f3a5328b2d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftChest.java @@ -0,0 +1,62 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftChest extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Chest, org.bukkit.block.data.Directional, org.bukkit.block.data.Waterlogged { + + public CraftChest() { + super(); + } + + public CraftChest(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftChest + + private static final net.minecraft.server.BlockStateEnum TYPE = getEnum(net.minecraft.server.BlockChest.class, "type"); + + @Override + public Type getType() { + return get(TYPE, Type.class); + } + + @Override + public void setType(Type type) { + set(TYPE, type); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockChest.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockChest.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftChestTrapped.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftChestTrapped.java new file mode 100644 index 000000000000..cc8f88baad94 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftChestTrapped.java @@ -0,0 +1,62 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftChestTrapped extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Chest, org.bukkit.block.data.Directional, org.bukkit.block.data.Waterlogged { + + public CraftChestTrapped() { + super(); + } + + public CraftChestTrapped(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftChest + + private static final net.minecraft.server.BlockStateEnum TYPE = getEnum(net.minecraft.server.BlockChestTrapped.class, "type"); + + @Override + public Type getType() { + return get(TYPE, Type.class); + } + + @Override + public void setType(Type type) { + set(TYPE, type); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockChestTrapped.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockChestTrapped.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftChorusFlower.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftChorusFlower.java new file mode 100644 index 000000000000..af1e85bcdb15 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftChorusFlower.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftChorusFlower extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Ageable { + + public CraftChorusFlower() { + super(); + } + + public CraftChorusFlower(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftAgeable + + private static final net.minecraft.server.BlockStateInteger AGE = getInteger(net.minecraft.server.BlockChorusFlower.class, "age"); + + @Override + public int getAge() { + return get(AGE); + } + + @Override + public void setAge(int age) { + set(AGE, age); + } + + @Override + public int getMaximumAge() { + return getMax(AGE); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftChorusFruit.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftChorusFruit.java new file mode 100644 index 000000000000..27ae41a92953 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftChorusFruit.java @@ -0,0 +1,57 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftChorusFruit extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.MultipleFacing { + + public CraftChorusFruit() { + super(); + } + + public CraftChorusFruit(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftMultipleFacing + + private static final net.minecraft.server.BlockStateBoolean[] FACES = new net.minecraft.server.BlockStateBoolean[]{ + getBoolean(net.minecraft.server.BlockChorusFruit.class, "north", true), getBoolean(net.minecraft.server.BlockChorusFruit.class, "east", true), getBoolean(net.minecraft.server.BlockChorusFruit.class, "south", true), getBoolean(net.minecraft.server.BlockChorusFruit.class, "west", true), getBoolean(net.minecraft.server.BlockChorusFruit.class, "up", true), getBoolean(net.minecraft.server.BlockChorusFruit.class, "down", true) + }; + + @Override + public boolean hasFace(org.bukkit.block.BlockFace face) { + return get(FACES[face.ordinal()]); + } + + @Override + public void setFace(org.bukkit.block.BlockFace face, boolean has) { + set(FACES[face.ordinal()], has); + } + + @Override + public java.util.Set getFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null && get(FACES[i])) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + @Override + public java.util.Set getAllowedFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCobbleWall.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCobbleWall.java new file mode 100644 index 000000000000..b1acc7c44188 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCobbleWall.java @@ -0,0 +1,71 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftCobbleWall extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Fence, org.bukkit.block.data.MultipleFacing, org.bukkit.block.data.Waterlogged { + + public CraftCobbleWall() { + super(); + } + + public CraftCobbleWall(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftMultipleFacing + + private static final net.minecraft.server.BlockStateBoolean[] FACES = new net.minecraft.server.BlockStateBoolean[]{ + getBoolean(net.minecraft.server.BlockCobbleWall.class, "north", true), getBoolean(net.minecraft.server.BlockCobbleWall.class, "east", true), getBoolean(net.minecraft.server.BlockCobbleWall.class, "south", true), getBoolean(net.minecraft.server.BlockCobbleWall.class, "west", true), getBoolean(net.minecraft.server.BlockCobbleWall.class, "up", true), getBoolean(net.minecraft.server.BlockCobbleWall.class, "down", true) + }; + + @Override + public boolean hasFace(org.bukkit.block.BlockFace face) { + return get(FACES[face.ordinal()]); + } + + @Override + public void setFace(org.bukkit.block.BlockFace face, boolean has) { + set(FACES[face.ordinal()], has); + } + + @Override + public java.util.Set getFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null && get(FACES[i])) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + @Override + public java.util.Set getAllowedFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockCobbleWall.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCocoa.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCocoa.java new file mode 100644 index 000000000000..79b2b52cacd4 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCocoa.java @@ -0,0 +1,53 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftCocoa extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Cocoa, org.bukkit.block.data.Ageable, org.bukkit.block.data.Directional { + + public CraftCocoa() { + super(); + } + + public CraftCocoa(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftAgeable + + private static final net.minecraft.server.BlockStateInteger AGE = getInteger(net.minecraft.server.BlockCocoa.class, "age"); + + @Override + public int getAge() { + return get(AGE); + } + + @Override + public void setAge(int age) { + set(AGE, age); + } + + @Override + public int getMaximumAge() { + return getMax(AGE); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockCocoa.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCommand.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCommand.java new file mode 100644 index 000000000000..ed1684a63db0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCommand.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftCommand extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.CommandBlock, org.bukkit.block.data.Directional { + + public CraftCommand() { + super(); + } + + public CraftCommand(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftCommandBlock + + private static final net.minecraft.server.BlockStateBoolean CONDITIONAL = getBoolean(net.minecraft.server.BlockCommand.class, "conditional"); + + @Override + public boolean isConditional() { + return get(CONDITIONAL); + } + + @Override + public void setConditional(boolean conditional) { + set(CONDITIONAL, conditional); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockCommand.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftConduit.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftConduit.java new file mode 100644 index 000000000000..ec2c9152666b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftConduit.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftConduit extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Waterlogged { + + public CraftConduit() { + super(); + } + + public CraftConduit(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockConduit.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralDead.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralDead.java new file mode 100644 index 000000000000..450f6658d008 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralDead.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftCoralDead extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Waterlogged { + + public CraftCoralDead() { + super(); + } + + public CraftCoralDead(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockCoralDead.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralFan.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralFan.java new file mode 100644 index 000000000000..dde1a04bd840 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralFan.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftCoralFan extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Waterlogged { + + public CraftCoralFan() { + super(); + } + + public CraftCoralFan(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockCoralFan.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralFanAbstract.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralFanAbstract.java new file mode 100644 index 000000000000..f1eddf70a30d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralFanAbstract.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftCoralFanAbstract extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Waterlogged { + + public CraftCoralFanAbstract() { + super(); + } + + public CraftCoralFanAbstract(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockCoralFanAbstract.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralFanWall.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralFanWall.java new file mode 100644 index 000000000000..8b89b6b412e2 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralFanWall.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftCoralFanWall extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.CoralWallFan, org.bukkit.block.data.Directional, org.bukkit.block.data.Waterlogged { + + public CraftCoralFanWall() { + super(); + } + + public CraftCoralFanWall(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockCoralFanWall.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockCoralFanWall.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralFanWallAbstract.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralFanWallAbstract.java new file mode 100644 index 000000000000..320f2f2164a5 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralFanWallAbstract.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftCoralFanWallAbstract extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.CoralWallFan, org.bukkit.block.data.Directional, org.bukkit.block.data.Waterlogged { + + public CraftCoralFanWallAbstract() { + super(); + } + + public CraftCoralFanWallAbstract(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockCoralFanWallAbstract.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockCoralFanWallAbstract.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralPlant.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralPlant.java new file mode 100644 index 000000000000..4fb0fa98dbd5 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCoralPlant.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftCoralPlant extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Waterlogged { + + public CraftCoralPlant() { + super(); + } + + public CraftCoralPlant(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockCoralPlant.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCrops.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCrops.java new file mode 100644 index 000000000000..61921a2a5e58 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCrops.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftCrops extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Ageable { + + public CraftCrops() { + super(); + } + + public CraftCrops(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftAgeable + + private static final net.minecraft.server.BlockStateInteger AGE = getInteger(net.minecraft.server.BlockCrops.class, "age"); + + @Override + public int getAge() { + return get(AGE); + } + + @Override + public void setAge(int age) { + set(AGE, age); + } + + @Override + public int getMaximumAge() { + return getMax(AGE); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDaylightDetector.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDaylightDetector.java new file mode 100644 index 000000000000..ffed5c9a380b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDaylightDetector.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftDaylightDetector extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.DaylightDetector, org.bukkit.block.data.AnaloguePowerable { + + public CraftDaylightDetector() { + super(); + } + + public CraftDaylightDetector(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftDaylightDetector + + private static final net.minecraft.server.BlockStateBoolean INVERTED = getBoolean(net.minecraft.server.BlockDaylightDetector.class, "inverted"); + + @Override + public boolean isInverted() { + return get(INVERTED); + } + + @Override + public void setInverted(boolean inverted) { + set(INVERTED, inverted); + } + + // org.bukkit.craftbukkit.block.data.CraftAnaloguePowerable + + private static final net.minecraft.server.BlockStateInteger POWER = getInteger(net.minecraft.server.BlockDaylightDetector.class, "power"); + + @Override + public int getPower() { + return get(POWER); + } + + @Override + public void setPower(int power) { + set(POWER, power); + } + + @Override + public int getMaximumPower() { + return getMax(POWER); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDirtSnow.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDirtSnow.java new file mode 100644 index 000000000000..7c3857e925a6 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDirtSnow.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftDirtSnow extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Snowable { + + public CraftDirtSnow() { + super(); + } + + public CraftDirtSnow(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftSnowable + + private static final net.minecraft.server.BlockStateBoolean SNOWY = getBoolean(net.minecraft.server.BlockDirtSnow.class, "snowy"); + + @Override + public boolean isSnowy() { + return get(SNOWY); + } + + @Override + public void setSnowy(boolean snowy) { + set(SNOWY, snowy); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDispenser.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDispenser.java new file mode 100644 index 000000000000..cea8f941e684 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDispenser.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftDispenser extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Dispenser, org.bukkit.block.data.Directional { + + public CraftDispenser() { + super(); + } + + public CraftDispenser(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftDispenser + + private static final net.minecraft.server.BlockStateBoolean TRIGGERED = getBoolean(net.minecraft.server.BlockDispenser.class, "triggered"); + + @Override + public boolean isTriggered() { + return get(TRIGGERED); + } + + @Override + public void setTriggered(boolean triggered) { + set(TRIGGERED, triggered); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockDispenser.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDoor.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDoor.java new file mode 100644 index 000000000000..ec8639bf1285 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDoor.java @@ -0,0 +1,90 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftDoor extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Door, org.bukkit.block.data.Bisected, org.bukkit.block.data.Directional, org.bukkit.block.data.Openable, org.bukkit.block.data.Powerable { + + public CraftDoor() { + super(); + } + + public CraftDoor(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftDoor + + private static final net.minecraft.server.BlockStateEnum HINGE = getEnum(net.minecraft.server.BlockDoor.class, "hinge"); + + @Override + public Hinge getHinge() { + return get(HINGE, Hinge.class); + } + + @Override + public void setHinge(Hinge hinge) { + set(HINGE, hinge); + } + + // org.bukkit.craftbukkit.block.data.CraftBisected + + private static final net.minecraft.server.BlockStateEnum HALF = getEnum(net.minecraft.server.BlockDoor.class, "half"); + + @Override + public Half getHalf() { + return get(HALF, Half.class); + } + + @Override + public void setHalf(Half half) { + set(HALF, half); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockDoor.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftOpenable + + private static final net.minecraft.server.BlockStateBoolean OPEN = getBoolean(net.minecraft.server.BlockDoor.class, "open"); + + @Override + public boolean isOpen() { + return get(OPEN); + } + + @Override + public void setOpen(boolean open) { + set(OPEN, open); + } + + // org.bukkit.craftbukkit.block.data.CraftPowerable + + private static final net.minecraft.server.BlockStateBoolean POWERED = getBoolean(net.minecraft.server.BlockDoor.class, "powered"); + + @Override + public boolean isPowered() { + return get(POWERED); + } + + @Override + public void setPowered(boolean powered) { + set(POWERED, powered); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDropper.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDropper.java new file mode 100644 index 000000000000..82486f1b0ab7 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDropper.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftDropper extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Dispenser, org.bukkit.block.data.Directional { + + public CraftDropper() { + super(); + } + + public CraftDropper(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftDispenser + + private static final net.minecraft.server.BlockStateBoolean TRIGGERED = getBoolean(net.minecraft.server.BlockDropper.class, "triggered"); + + @Override + public boolean isTriggered() { + return get(TRIGGERED); + } + + @Override + public void setTriggered(boolean triggered) { + set(TRIGGERED, triggered); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockDropper.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftEndRod.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftEndRod.java new file mode 100644 index 000000000000..d72c9d0509db --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftEndRod.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftEndRod extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Directional { + + public CraftEndRod() { + super(); + } + + public CraftEndRod(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockEndRod.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftEnderChest.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftEnderChest.java new file mode 100644 index 000000000000..2f8380807322 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftEnderChest.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftEnderChest extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.EnderChest, org.bukkit.block.data.Directional, org.bukkit.block.data.Waterlogged { + + public CraftEnderChest() { + super(); + } + + public CraftEnderChest(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockEnderChest.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockEnderChest.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftEnderPortalFrame.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftEnderPortalFrame.java new file mode 100644 index 000000000000..f57cbbeca1d8 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftEnderPortalFrame.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftEnderPortalFrame extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.EndPortalFrame, org.bukkit.block.data.Directional { + + public CraftEnderPortalFrame() { + super(); + } + + public CraftEnderPortalFrame(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftEndPortalFrame + + private static final net.minecraft.server.BlockStateBoolean EYE = getBoolean(net.minecraft.server.BlockEnderPortalFrame.class, "eye"); + + @Override + public boolean hasEye() { + return get(EYE); + } + + @Override + public void setEye(boolean eye) { + set(EYE, eye); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockEnderPortalFrame.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFence.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFence.java new file mode 100644 index 000000000000..1333b997f0b0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFence.java @@ -0,0 +1,71 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftFence extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Fence, org.bukkit.block.data.MultipleFacing, org.bukkit.block.data.Waterlogged { + + public CraftFence() { + super(); + } + + public CraftFence(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftMultipleFacing + + private static final net.minecraft.server.BlockStateBoolean[] FACES = new net.minecraft.server.BlockStateBoolean[]{ + getBoolean(net.minecraft.server.BlockFence.class, "north", true), getBoolean(net.minecraft.server.BlockFence.class, "east", true), getBoolean(net.minecraft.server.BlockFence.class, "south", true), getBoolean(net.minecraft.server.BlockFence.class, "west", true), getBoolean(net.minecraft.server.BlockFence.class, "up", true), getBoolean(net.minecraft.server.BlockFence.class, "down", true) + }; + + @Override + public boolean hasFace(org.bukkit.block.BlockFace face) { + return get(FACES[face.ordinal()]); + } + + @Override + public void setFace(org.bukkit.block.BlockFace face, boolean has) { + set(FACES[face.ordinal()], has); + } + + @Override + public java.util.Set getFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null && get(FACES[i])) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + @Override + public java.util.Set getAllowedFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockFence.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFenceGate.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFenceGate.java new file mode 100644 index 000000000000..4fc294452735 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFenceGate.java @@ -0,0 +1,76 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftFenceGate extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Gate, org.bukkit.block.data.Directional, org.bukkit.block.data.Openable, org.bukkit.block.data.Powerable { + + public CraftFenceGate() { + super(); + } + + public CraftFenceGate(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftGate + + private static final net.minecraft.server.BlockStateBoolean IN_WALL = getBoolean(net.minecraft.server.BlockFenceGate.class, "in_wall"); + + @Override + public boolean isInWall() { + return get(IN_WALL); + } + + @Override + public void setInWall(boolean inWall) { + set(IN_WALL, inWall); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockFenceGate.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftOpenable + + private static final net.minecraft.server.BlockStateBoolean OPEN = getBoolean(net.minecraft.server.BlockFenceGate.class, "open"); + + @Override + public boolean isOpen() { + return get(OPEN); + } + + @Override + public void setOpen(boolean open) { + set(OPEN, open); + } + + // org.bukkit.craftbukkit.block.data.CraftPowerable + + private static final net.minecraft.server.BlockStateBoolean POWERED = getBoolean(net.minecraft.server.BlockFenceGate.class, "powered"); + + @Override + public boolean isPowered() { + return get(POWERED); + } + + @Override + public void setPowered(boolean powered) { + set(POWERED, powered); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFire.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFire.java new file mode 100644 index 000000000000..a580935527e9 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFire.java @@ -0,0 +1,76 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftFire extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Fire, org.bukkit.block.data.Ageable, org.bukkit.block.data.MultipleFacing { + + public CraftFire() { + super(); + } + + public CraftFire(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftAgeable + + private static final net.minecraft.server.BlockStateInteger AGE = getInteger(net.minecraft.server.BlockFire.class, "age"); + + @Override + public int getAge() { + return get(AGE); + } + + @Override + public void setAge(int age) { + set(AGE, age); + } + + @Override + public int getMaximumAge() { + return getMax(AGE); + } + + // org.bukkit.craftbukkit.block.data.CraftMultipleFacing + + private static final net.minecraft.server.BlockStateBoolean[] FACES = new net.minecraft.server.BlockStateBoolean[]{ + getBoolean(net.minecraft.server.BlockFire.class, "north", true), getBoolean(net.minecraft.server.BlockFire.class, "east", true), getBoolean(net.minecraft.server.BlockFire.class, "south", true), getBoolean(net.minecraft.server.BlockFire.class, "west", true), getBoolean(net.minecraft.server.BlockFire.class, "up", true), getBoolean(net.minecraft.server.BlockFire.class, "down", true) + }; + + @Override + public boolean hasFace(org.bukkit.block.BlockFace face) { + return get(FACES[face.ordinal()]); + } + + @Override + public void setFace(org.bukkit.block.BlockFace face, boolean has) { + set(FACES[face.ordinal()], has); + } + + @Override + public java.util.Set getFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null && get(FACES[i])) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + @Override + public java.util.Set getAllowedFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFloorSign.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFloorSign.java new file mode 100644 index 000000000000..aa52ec777066 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFloorSign.java @@ -0,0 +1,132 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftFloorSign extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Sign, org.bukkit.block.data.Rotatable, org.bukkit.block.data.Waterlogged { + + public CraftFloorSign() { + super(); + } + + public CraftFloorSign(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftRotatable + + private static final net.minecraft.server.BlockStateInteger ROTATION = getInteger(net.minecraft.server.BlockFloorSign.class, "rotation"); + + @Override + public org.bukkit.block.BlockFace getRotation() { + int data = get(ROTATION); + switch (data) { + case 0x0: + return org.bukkit.block.BlockFace.SOUTH; + case 0x1: + return org.bukkit.block.BlockFace.SOUTH_SOUTH_WEST; + case 0x2: + return org.bukkit.block.BlockFace.SOUTH_WEST; + case 0x3: + return org.bukkit.block.BlockFace.WEST_SOUTH_WEST; + case 0x4: + return org.bukkit.block.BlockFace.WEST; + case 0x5: + return org.bukkit.block.BlockFace.WEST_NORTH_WEST; + case 0x6: + return org.bukkit.block.BlockFace.NORTH_WEST; + case 0x7: + return org.bukkit.block.BlockFace.NORTH_NORTH_WEST; + case 0x8: + return org.bukkit.block.BlockFace.NORTH; + case 0x9: + return org.bukkit.block.BlockFace.NORTH_NORTH_EAST; + case 0xA: + return org.bukkit.block.BlockFace.NORTH_EAST; + case 0xB: + return org.bukkit.block.BlockFace.EAST_NORTH_EAST; + case 0xC: + return org.bukkit.block.BlockFace.EAST; + case 0xD: + return org.bukkit.block.BlockFace.EAST_SOUTH_EAST; + case 0xE: + return org.bukkit.block.BlockFace.SOUTH_EAST; + case 0xF: + return org.bukkit.block.BlockFace.SOUTH_SOUTH_EAST; + default: + throw new IllegalArgumentException("Unknown rotation " + data); + } + } + + @Override + public void setRotation(org.bukkit.block.BlockFace rotation) { + int val; + switch (rotation) { + case SOUTH: + val = 0x0; + break; + case SOUTH_SOUTH_WEST: + val = 0x1; + break; + case SOUTH_WEST: + val = 0x2; + break; + case WEST_SOUTH_WEST: + val = 0x3; + break; + case WEST: + val = 0x4; + break; + case WEST_NORTH_WEST: + val = 0x5; + break; + case NORTH_WEST: + val = 0x6; + break; + case NORTH_NORTH_WEST: + val = 0x7; + break; + case NORTH: + val = 0x8; + break; + case NORTH_NORTH_EAST: + val = 0x9; + break; + case NORTH_EAST: + val = 0xA; + break; + case EAST_NORTH_EAST: + val = 0xB; + break; + case EAST: + val = 0xC; + break; + case EAST_SOUTH_EAST: + val = 0xD; + break; + case SOUTH_EAST: + val = 0xE; + break; + case SOUTH_SOUTH_EAST: + val = 0xF; + break; + default: + throw new IllegalArgumentException("Illegal rotation " + rotation); + } + set(ROTATION, val); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockFloorSign.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFluids.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFluids.java new file mode 100644 index 000000000000..21c30c7c75e3 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFluids.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftFluids extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Levelled { + + public CraftFluids() { + super(); + } + + public CraftFluids(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftLevelled + + private static final net.minecraft.server.BlockStateInteger LEVEL = getInteger(net.minecraft.server.BlockFluids.class, "level"); + + @Override + public int getLevel() { + return get(LEVEL); + } + + @Override + public void setLevel(int level) { + set(LEVEL, level); + } + + @Override + public int getMaximumLevel() { + return getMax(LEVEL); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFurnace.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFurnace.java new file mode 100644 index 000000000000..1ca1a9c796fc --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFurnace.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftFurnace extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Furnace, org.bukkit.block.data.Directional, org.bukkit.block.data.Lightable { + + public CraftFurnace() { + super(); + } + + public CraftFurnace(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockFurnace.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftLightable + + private static final net.minecraft.server.BlockStateBoolean LIT = getBoolean(net.minecraft.server.BlockFurnace.class, "lit"); + + @Override + public boolean isLit() { + return get(LIT); + } + + @Override + public void setLit(boolean lit) { + set(LIT, lit); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftGlassPane.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftGlassPane.java new file mode 100644 index 000000000000..4c8bfcb733e4 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftGlassPane.java @@ -0,0 +1,71 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftGlassPane extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.GlassPane, org.bukkit.block.data.MultipleFacing, org.bukkit.block.data.Waterlogged { + + public CraftGlassPane() { + super(); + } + + public CraftGlassPane(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftMultipleFacing + + private static final net.minecraft.server.BlockStateBoolean[] FACES = new net.minecraft.server.BlockStateBoolean[]{ + getBoolean(net.minecraft.server.BlockGlassPane.class, "north", true), getBoolean(net.minecraft.server.BlockGlassPane.class, "east", true), getBoolean(net.minecraft.server.BlockGlassPane.class, "south", true), getBoolean(net.minecraft.server.BlockGlassPane.class, "west", true), getBoolean(net.minecraft.server.BlockGlassPane.class, "up", true), getBoolean(net.minecraft.server.BlockGlassPane.class, "down", true) + }; + + @Override + public boolean hasFace(org.bukkit.block.BlockFace face) { + return get(FACES[face.ordinal()]); + } + + @Override + public void setFace(org.bukkit.block.BlockFace face, boolean has) { + set(FACES[face.ordinal()], has); + } + + @Override + public java.util.Set getFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null && get(FACES[i])) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + @Override + public java.util.Set getAllowedFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockGlassPane.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftGlazedTerracotta.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftGlazedTerracotta.java new file mode 100644 index 000000000000..89cc7f95f21d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftGlazedTerracotta.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftGlazedTerracotta extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Directional { + + public CraftGlazedTerracotta() { + super(); + } + + public CraftGlazedTerracotta(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockGlazedTerracotta.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftGrass.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftGrass.java new file mode 100644 index 000000000000..08fcfb0b44d2 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftGrass.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftGrass extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Snowable { + + public CraftGrass() { + super(); + } + + public CraftGrass(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftSnowable + + private static final net.minecraft.server.BlockStateBoolean SNOWY = getBoolean(net.minecraft.server.BlockGrass.class, "snowy"); + + @Override + public boolean isSnowy() { + return get(SNOWY); + } + + @Override + public void setSnowy(boolean snowy) { + set(SNOWY, snowy); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftHay.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftHay.java new file mode 100644 index 000000000000..db7d61ad6cd5 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftHay.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftHay extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Orientable { + + public CraftHay() { + super(); + } + + public CraftHay(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftOrientable + + private static final net.minecraft.server.BlockStateEnum AXIS = getEnum(net.minecraft.server.BlockHay.class, "axis"); + + @Override + public org.bukkit.Axis getAxis() { + return get(AXIS, org.bukkit.Axis.class); + } + + @Override + public void setAxis(org.bukkit.Axis axis) { + set(AXIS, axis); + } + + @Override + public java.util.Set getAxes() { + return getValues(AXIS, org.bukkit.Axis.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftHopper.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftHopper.java new file mode 100644 index 000000000000..f13115177979 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftHopper.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftHopper extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Hopper, org.bukkit.block.data.Directional { + + public CraftHopper() { + super(); + } + + public CraftHopper(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftHopper + + private static final net.minecraft.server.BlockStateBoolean ENABLED = getBoolean(net.minecraft.server.BlockHopper.class, "enabled"); + + @Override + public boolean isEnabled() { + return get(ENABLED); + } + + @Override + public void setEnabled(boolean enabled) { + set(ENABLED, enabled); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockHopper.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftHugeMushroom.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftHugeMushroom.java new file mode 100644 index 000000000000..fb6e205077a9 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftHugeMushroom.java @@ -0,0 +1,57 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftHugeMushroom extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.MultipleFacing { + + public CraftHugeMushroom() { + super(); + } + + public CraftHugeMushroom(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftMultipleFacing + + private static final net.minecraft.server.BlockStateBoolean[] FACES = new net.minecraft.server.BlockStateBoolean[]{ + getBoolean(net.minecraft.server.BlockHugeMushroom.class, "north", true), getBoolean(net.minecraft.server.BlockHugeMushroom.class, "east", true), getBoolean(net.minecraft.server.BlockHugeMushroom.class, "south", true), getBoolean(net.minecraft.server.BlockHugeMushroom.class, "west", true), getBoolean(net.minecraft.server.BlockHugeMushroom.class, "up", true), getBoolean(net.minecraft.server.BlockHugeMushroom.class, "down", true) + }; + + @Override + public boolean hasFace(org.bukkit.block.BlockFace face) { + return get(FACES[face.ordinal()]); + } + + @Override + public void setFace(org.bukkit.block.BlockFace face, boolean has) { + set(FACES[face.ordinal()], has); + } + + @Override + public java.util.Set getFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null && get(FACES[i])) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + @Override + public java.util.Set getAllowedFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftIceFrost.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftIceFrost.java new file mode 100644 index 000000000000..7b297dc22d03 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftIceFrost.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftIceFrost extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Ageable { + + public CraftIceFrost() { + super(); + } + + public CraftIceFrost(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftAgeable + + private static final net.minecraft.server.BlockStateInteger AGE = getInteger(net.minecraft.server.BlockIceFrost.class, "age"); + + @Override + public int getAge() { + return get(AGE); + } + + @Override + public void setAge(int age) { + set(AGE, age); + } + + @Override + public int getMaximumAge() { + return getMax(AGE); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftIronBars.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftIronBars.java new file mode 100644 index 000000000000..cf6da2dfed98 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftIronBars.java @@ -0,0 +1,71 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftIronBars extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Fence, org.bukkit.block.data.MultipleFacing, org.bukkit.block.data.Waterlogged { + + public CraftIronBars() { + super(); + } + + public CraftIronBars(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftMultipleFacing + + private static final net.minecraft.server.BlockStateBoolean[] FACES = new net.minecraft.server.BlockStateBoolean[]{ + getBoolean(net.minecraft.server.BlockIronBars.class, "north", true), getBoolean(net.minecraft.server.BlockIronBars.class, "east", true), getBoolean(net.minecraft.server.BlockIronBars.class, "south", true), getBoolean(net.minecraft.server.BlockIronBars.class, "west", true), getBoolean(net.minecraft.server.BlockIronBars.class, "up", true), getBoolean(net.minecraft.server.BlockIronBars.class, "down", true) + }; + + @Override + public boolean hasFace(org.bukkit.block.BlockFace face) { + return get(FACES[face.ordinal()]); + } + + @Override + public void setFace(org.bukkit.block.BlockFace face, boolean has) { + set(FACES[face.ordinal()], has); + } + + @Override + public java.util.Set getFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null && get(FACES[i])) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + @Override + public java.util.Set getAllowedFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockIronBars.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftJukeBox.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftJukeBox.java new file mode 100644 index 000000000000..b87801f3a5c8 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftJukeBox.java @@ -0,0 +1,24 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftJukeBox extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Jukebox { + + public CraftJukeBox() { + super(); + } + + public CraftJukeBox(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftJukebox + + private static final net.minecraft.server.BlockStateBoolean HAS_RECORD = getBoolean(net.minecraft.server.BlockJukeBox.class, "has_record"); + + @Override + public boolean hasRecord() { + return get(HAS_RECORD); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftKelp.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftKelp.java new file mode 100644 index 000000000000..e9ba8ade49b9 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftKelp.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftKelp extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Ageable { + + public CraftKelp() { + super(); + } + + public CraftKelp(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftAgeable + + private static final net.minecraft.server.BlockStateInteger AGE = getInteger(net.minecraft.server.BlockKelp.class, "age"); + + @Override + public int getAge() { + return get(AGE); + } + + @Override + public void setAge(int age) { + set(AGE, age); + } + + @Override + public int getMaximumAge() { + return getMax(AGE); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLadder.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLadder.java new file mode 100644 index 000000000000..2c7f396fa31f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLadder.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftLadder extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Ladder, org.bukkit.block.data.Directional, org.bukkit.block.data.Waterlogged { + + public CraftLadder() { + super(); + } + + public CraftLadder(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockLadder.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockLadder.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLeaves.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLeaves.java new file mode 100644 index 000000000000..2e2f091b9ed8 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLeaves.java @@ -0,0 +1,40 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftLeaves extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Leaves { + + public CraftLeaves() { + super(); + } + + public CraftLeaves(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftLeaves + + private static final net.minecraft.server.BlockStateInteger DISTANCE = getInteger(net.minecraft.server.BlockLeaves.class, "distance"); + private static final net.minecraft.server.BlockStateBoolean PERSISTENT = getBoolean(net.minecraft.server.BlockLeaves.class, "persistent"); + + @Override + public boolean isPersistent() { + return get(PERSISTENT); + } + + @Override + public void setPersistent(boolean persistent) { + set(PERSISTENT, persistent); + } + + @Override + public int getDistance() { + return get(DISTANCE); + } + + @Override + public void setDistance(int distance) { + set(DISTANCE, distance); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLever.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLever.java new file mode 100644 index 000000000000..a8b030f2fd77 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLever.java @@ -0,0 +1,62 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftLever extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Switch, org.bukkit.block.data.Directional, org.bukkit.block.data.Powerable { + + public CraftLever() { + super(); + } + + public CraftLever(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftSwitch + + private static final net.minecraft.server.BlockStateEnum FACE = getEnum(net.minecraft.server.BlockLever.class, "face"); + + @Override + public Face getFace() { + return get(FACE, Face.class); + } + + @Override + public void setFace(Face face) { + set(FACE, face); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockLever.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftPowerable + + private static final net.minecraft.server.BlockStateBoolean POWERED = getBoolean(net.minecraft.server.BlockLever.class, "powered"); + + @Override + public boolean isPowered() { + return get(POWERED); + } + + @Override + public void setPowered(boolean powered) { + set(POWERED, powered); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLogAbstract.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLogAbstract.java new file mode 100644 index 000000000000..efa404902385 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLogAbstract.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftLogAbstract extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Orientable { + + public CraftLogAbstract() { + super(); + } + + public CraftLogAbstract(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftOrientable + + private static final net.minecraft.server.BlockStateEnum AXIS = getEnum(net.minecraft.server.BlockLogAbstract.class, "axis"); + + @Override + public org.bukkit.Axis getAxis() { + return get(AXIS, org.bukkit.Axis.class); + } + + @Override + public void setAxis(org.bukkit.Axis axis) { + set(AXIS, axis); + } + + @Override + public java.util.Set getAxes() { + return getValues(AXIS, org.bukkit.Axis.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftMinecartDetector.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftMinecartDetector.java new file mode 100644 index 000000000000..9ad25f9fedf0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftMinecartDetector.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftMinecartDetector extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.RedstoneRail, org.bukkit.block.data.Powerable, org.bukkit.block.data.Rail { + + public CraftMinecartDetector() { + super(); + } + + public CraftMinecartDetector(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftPowerable + + private static final net.minecraft.server.BlockStateBoolean POWERED = getBoolean(net.minecraft.server.BlockMinecartDetector.class, "powered"); + + @Override + public boolean isPowered() { + return get(POWERED); + } + + @Override + public void setPowered(boolean powered) { + set(POWERED, powered); + } + + // org.bukkit.craftbukkit.block.data.CraftRail + + private static final net.minecraft.server.BlockStateEnum SHAPE = getEnum(net.minecraft.server.BlockMinecartDetector.class, "shape"); + + @Override + public Shape getShape() { + return get(SHAPE, Shape.class); + } + + @Override + public void setShape(Shape shape) { + set(SHAPE, shape); + } + + @Override + public java.util.Set getShapes() { + return getValues(SHAPE, Shape.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftMinecartTrack.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftMinecartTrack.java new file mode 100644 index 000000000000..127681a63e56 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftMinecartTrack.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftMinecartTrack extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Rail { + + public CraftMinecartTrack() { + super(); + } + + public CraftMinecartTrack(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftRail + + private static final net.minecraft.server.BlockStateEnum SHAPE = getEnum(net.minecraft.server.BlockMinecartTrack.class, "shape"); + + @Override + public Shape getShape() { + return get(SHAPE, Shape.class); + } + + @Override + public void setShape(Shape shape) { + set(SHAPE, shape); + } + + @Override + public java.util.Set getShapes() { + return getValues(SHAPE, Shape.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftMycel.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftMycel.java new file mode 100644 index 000000000000..ca9d60f3ae31 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftMycel.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftMycel extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Snowable { + + public CraftMycel() { + super(); + } + + public CraftMycel(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftSnowable + + private static final net.minecraft.server.BlockStateBoolean SNOWY = getBoolean(net.minecraft.server.BlockMycel.class, "snowy"); + + @Override + public boolean isSnowy() { + return get(SNOWY); + } + + @Override + public void setSnowy(boolean snowy) { + set(SNOWY, snowy); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftNetherWart.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftNetherWart.java new file mode 100644 index 000000000000..449d0f6f5d25 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftNetherWart.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftNetherWart extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Ageable { + + public CraftNetherWart() { + super(); + } + + public CraftNetherWart(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftAgeable + + private static final net.minecraft.server.BlockStateInteger AGE = getInteger(net.minecraft.server.BlockNetherWart.class, "age"); + + @Override + public int getAge() { + return get(AGE); + } + + @Override + public void setAge(int age) { + set(AGE, age); + } + + @Override + public int getMaximumAge() { + return getMax(AGE); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftNote.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftNote.java new file mode 100644 index 000000000000..03a736bd7067 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftNote.java @@ -0,0 +1,54 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftNote extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.NoteBlock, org.bukkit.block.data.Powerable { + + public CraftNote() { + super(); + } + + public CraftNote(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftNoteBlock + + private static final net.minecraft.server.BlockStateEnum INSTRUMENT = getEnum(net.minecraft.server.BlockNote.class, "instrument"); + private static final net.minecraft.server.BlockStateInteger NOTE = getInteger(net.minecraft.server.BlockNote.class, "note"); + + @Override + public org.bukkit.Instrument getInstrument() { + return get(INSTRUMENT, org.bukkit.Instrument.class); + } + + @Override + public void setInstrument(org.bukkit.Instrument instrument) { + set(INSTRUMENT, instrument); + } + + @Override + public org.bukkit.Note getNote() { + return new org.bukkit.Note(get(NOTE)); + } + + @Override + public void setNote(org.bukkit.Note note) { + set(NOTE, (int) note.getId()); + } + + // org.bukkit.craftbukkit.block.data.CraftPowerable + + private static final net.minecraft.server.BlockStateBoolean POWERED = getBoolean(net.minecraft.server.BlockNote.class, "powered"); + + @Override + public boolean isPowered() { + return get(POWERED); + } + + @Override + public void setPowered(boolean powered) { + set(POWERED, powered); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftObserver.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftObserver.java new file mode 100644 index 000000000000..ba3a4f072f2d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftObserver.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftObserver extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Observer, org.bukkit.block.data.Directional, org.bukkit.block.data.Powerable { + + public CraftObserver() { + super(); + } + + public CraftObserver(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockObserver.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftPowerable + + private static final net.minecraft.server.BlockStateBoolean POWERED = getBoolean(net.minecraft.server.BlockObserver.class, "powered"); + + @Override + public boolean isPowered() { + return get(POWERED); + } + + @Override + public void setPowered(boolean powered) { + set(POWERED, powered); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPiston.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPiston.java new file mode 100644 index 000000000000..f16404f492b4 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPiston.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftPiston extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Piston, org.bukkit.block.data.Directional { + + public CraftPiston() { + super(); + } + + public CraftPiston(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftPiston + + private static final net.minecraft.server.BlockStateBoolean EXTENDED = getBoolean(net.minecraft.server.BlockPiston.class, "extended"); + + @Override + public boolean isExtended() { + return get(EXTENDED); + } + + @Override + public void setExtended(boolean extended) { + set(EXTENDED, extended); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockPiston.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPistonExtension.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPistonExtension.java new file mode 100644 index 000000000000..038e58433822 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPistonExtension.java @@ -0,0 +1,62 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftPistonExtension extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.PistonHead, org.bukkit.block.data.type.TechnicalPiston, org.bukkit.block.data.Directional { + + public CraftPistonExtension() { + super(); + } + + public CraftPistonExtension(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftPistonHead + + private static final net.minecraft.server.BlockStateBoolean SHORT = getBoolean(net.minecraft.server.BlockPistonExtension.class, "short"); + + @Override + public boolean isShort() { + return get(SHORT); + } + + @Override + public void setShort(boolean _short) { + set(SHORT, _short); + } + + // org.bukkit.craftbukkit.block.data.type.CraftTechnicalPiston + + private static final net.minecraft.server.BlockStateEnum TYPE = getEnum(net.minecraft.server.BlockPistonExtension.class, "type"); + + @Override + public Type getType() { + return get(TYPE, Type.class); + } + + @Override + public void setType(Type type) { + set(TYPE, type); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockPistonExtension.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPistonMoving.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPistonMoving.java new file mode 100644 index 000000000000..38b098e0ac6b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPistonMoving.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftPistonMoving extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.TechnicalPiston, org.bukkit.block.data.Directional { + + public CraftPistonMoving() { + super(); + } + + public CraftPistonMoving(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftTechnicalPiston + + private static final net.minecraft.server.BlockStateEnum TYPE = getEnum(net.minecraft.server.BlockPistonMoving.class, "type"); + + @Override + public Type getType() { + return get(TYPE, Type.class); + } + + @Override + public void setType(Type type) { + set(TYPE, type); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockPistonMoving.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPortal.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPortal.java new file mode 100644 index 000000000000..7701ca85d9fa --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPortal.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftPortal extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Orientable { + + public CraftPortal() { + super(); + } + + public CraftPortal(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftOrientable + + private static final net.minecraft.server.BlockStateEnum AXIS = getEnum(net.minecraft.server.BlockPortal.class, "axis"); + + @Override + public org.bukkit.Axis getAxis() { + return get(AXIS, org.bukkit.Axis.class); + } + + @Override + public void setAxis(org.bukkit.Axis axis) { + set(AXIS, axis); + } + + @Override + public java.util.Set getAxes() { + return getValues(AXIS, org.bukkit.Axis.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPotatoes.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPotatoes.java new file mode 100644 index 000000000000..c09a9c78cd13 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPotatoes.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftPotatoes extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Ageable { + + public CraftPotatoes() { + super(); + } + + public CraftPotatoes(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftAgeable + + private static final net.minecraft.server.BlockStateInteger AGE = getInteger(net.minecraft.server.BlockPotatoes.class, "age"); + + @Override + public int getAge() { + return get(AGE); + } + + @Override + public void setAge(int age) { + set(AGE, age); + } + + @Override + public int getMaximumAge() { + return getMax(AGE); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPoweredRail.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPoweredRail.java new file mode 100644 index 000000000000..ea904ef00dd0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPoweredRail.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftPoweredRail extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.RedstoneRail, org.bukkit.block.data.Powerable, org.bukkit.block.data.Rail { + + public CraftPoweredRail() { + super(); + } + + public CraftPoweredRail(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftPowerable + + private static final net.minecraft.server.BlockStateBoolean POWERED = getBoolean(net.minecraft.server.BlockPoweredRail.class, "powered"); + + @Override + public boolean isPowered() { + return get(POWERED); + } + + @Override + public void setPowered(boolean powered) { + set(POWERED, powered); + } + + // org.bukkit.craftbukkit.block.data.CraftRail + + private static final net.minecraft.server.BlockStateEnum SHAPE = getEnum(net.minecraft.server.BlockPoweredRail.class, "shape"); + + @Override + public Shape getShape() { + return get(SHAPE, Shape.class); + } + + @Override + public void setShape(Shape shape) { + set(SHAPE, shape); + } + + @Override + public java.util.Set getShapes() { + return getValues(SHAPE, Shape.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPressurePlateBinary.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPressurePlateBinary.java new file mode 100644 index 000000000000..806ad25654e1 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPressurePlateBinary.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftPressurePlateBinary extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Powerable { + + public CraftPressurePlateBinary() { + super(); + } + + public CraftPressurePlateBinary(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftPowerable + + private static final net.minecraft.server.BlockStateBoolean POWERED = getBoolean(net.minecraft.server.BlockPressurePlateBinary.class, "powered"); + + @Override + public boolean isPowered() { + return get(POWERED); + } + + @Override + public void setPowered(boolean powered) { + set(POWERED, powered); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPressurePlateWeighted.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPressurePlateWeighted.java new file mode 100644 index 000000000000..840506640ea2 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPressurePlateWeighted.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftPressurePlateWeighted extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.AnaloguePowerable { + + public CraftPressurePlateWeighted() { + super(); + } + + public CraftPressurePlateWeighted(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftAnaloguePowerable + + private static final net.minecraft.server.BlockStateInteger POWER = getInteger(net.minecraft.server.BlockPressurePlateWeighted.class, "power"); + + @Override + public int getPower() { + return get(POWER); + } + + @Override + public void setPower(int power) { + set(POWER, power); + } + + @Override + public int getMaximumPower() { + return getMax(POWER); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPumpkinCarved.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPumpkinCarved.java new file mode 100644 index 000000000000..b258e7449fde --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPumpkinCarved.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftPumpkinCarved extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Directional { + + public CraftPumpkinCarved() { + super(); + } + + public CraftPumpkinCarved(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockPumpkinCarved.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneComparator.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneComparator.java new file mode 100644 index 000000000000..2456e30717e3 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneComparator.java @@ -0,0 +1,62 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftRedstoneComparator extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Comparator, org.bukkit.block.data.Directional, org.bukkit.block.data.Powerable { + + public CraftRedstoneComparator() { + super(); + } + + public CraftRedstoneComparator(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftComparator + + private static final net.minecraft.server.BlockStateEnum MODE = getEnum(net.minecraft.server.BlockRedstoneComparator.class, "mode"); + + @Override + public Mode getMode() { + return get(MODE, Mode.class); + } + + @Override + public void setMode(Mode mode) { + set(MODE, mode); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockRedstoneComparator.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftPowerable + + private static final net.minecraft.server.BlockStateBoolean POWERED = getBoolean(net.minecraft.server.BlockRedstoneComparator.class, "powered"); + + @Override + public boolean isPowered() { + return get(POWERED); + } + + @Override + public void setPowered(boolean powered) { + set(POWERED, powered); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneLamp.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneLamp.java new file mode 100644 index 000000000000..7769c93c8f25 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneLamp.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftRedstoneLamp extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Lightable { + + public CraftRedstoneLamp() { + super(); + } + + public CraftRedstoneLamp(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftLightable + + private static final net.minecraft.server.BlockStateBoolean LIT = getBoolean(net.minecraft.server.BlockRedstoneLamp.class, "lit"); + + @Override + public boolean isLit() { + return get(LIT); + } + + @Override + public void setLit(boolean lit) { + set(LIT, lit); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneOre.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneOre.java new file mode 100644 index 000000000000..effe08ead1ab --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneOre.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftRedstoneOre extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Lightable { + + public CraftRedstoneOre() { + super(); + } + + public CraftRedstoneOre(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftLightable + + private static final net.minecraft.server.BlockStateBoolean LIT = getBoolean(net.minecraft.server.BlockRedstoneOre.class, "lit"); + + @Override + public boolean isLit() { + return get(LIT); + } + + @Override + public void setLit(boolean lit) { + set(LIT, lit); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneTorch.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneTorch.java new file mode 100644 index 000000000000..d9d142016a26 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneTorch.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftRedstoneTorch extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Lightable { + + public CraftRedstoneTorch() { + super(); + } + + public CraftRedstoneTorch(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftLightable + + private static final net.minecraft.server.BlockStateBoolean LIT = getBoolean(net.minecraft.server.BlockRedstoneTorch.class, "lit"); + + @Override + public boolean isLit() { + return get(LIT); + } + + @Override + public void setLit(boolean lit) { + set(LIT, lit); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneTorchWall.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneTorchWall.java new file mode 100644 index 000000000000..8125b4004f50 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneTorchWall.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftRedstoneTorchWall extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.RedstoneWallTorch, org.bukkit.block.data.Directional, org.bukkit.block.data.Lightable { + + public CraftRedstoneTorchWall() { + super(); + } + + public CraftRedstoneTorchWall(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockRedstoneTorchWall.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftLightable + + private static final net.minecraft.server.BlockStateBoolean LIT = getBoolean(net.minecraft.server.BlockRedstoneTorchWall.class, "lit"); + + @Override + public boolean isLit() { + return get(LIT); + } + + @Override + public void setLit(boolean lit) { + set(LIT, lit); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneWire.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneWire.java new file mode 100644 index 000000000000..45f66eb408b0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRedstoneWire.java @@ -0,0 +1,82 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftRedstoneWire extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.RedstoneWire, org.bukkit.block.data.AnaloguePowerable { + + public CraftRedstoneWire() { + super(); + } + + public CraftRedstoneWire(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftRedstoneWire + + private static final net.minecraft.server.BlockStateEnum NORTH = getEnum(net.minecraft.server.BlockRedstoneWire.class, "north"); + private static final net.minecraft.server.BlockStateEnum EAST = getEnum(net.minecraft.server.BlockRedstoneWire.class, "east"); + private static final net.minecraft.server.BlockStateEnum SOUTH = getEnum(net.minecraft.server.BlockRedstoneWire.class, "south"); + private static final net.minecraft.server.BlockStateEnum WEST = getEnum(net.minecraft.server.BlockRedstoneWire.class, "west"); + + @Override + public Connection getFace(org.bukkit.block.BlockFace face) { + switch (face) { + case NORTH: + return get(NORTH, Connection.class); + case EAST: + return get(EAST, Connection.class); + case SOUTH: + return get(SOUTH, Connection.class); + case WEST: + return get(WEST, Connection.class); + default: + throw new IllegalArgumentException("Cannot have face " + face); + } + } + + @Override + public void setFace(org.bukkit.block.BlockFace face, Connection connection) { + switch (face) { + case NORTH: + set(NORTH, connection); + break; + case EAST: + set(EAST, connection); + break; + case SOUTH: + set(SOUTH, connection); + break; + case WEST: + set(WEST, connection); + break; + default: + throw new IllegalArgumentException("Cannot have face " + face); + } + } + + @Override + public java.util.Set getAllowedFaces() { + return com.google.common.collect.ImmutableSet.of(org.bukkit.block.BlockFace.NORTH, org.bukkit.block.BlockFace.EAST, org.bukkit.block.BlockFace.SOUTH, org.bukkit.block.BlockFace.WEST); + } + + // org.bukkit.craftbukkit.block.data.CraftAnaloguePowerable + + private static final net.minecraft.server.BlockStateInteger POWER = getInteger(net.minecraft.server.BlockRedstoneWire.class, "power"); + + @Override + public int getPower() { + return get(POWER); + } + + @Override + public void setPower(int power) { + set(POWER, power); + } + + @Override + public int getMaximumPower() { + return getMax(POWER); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftReed.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftReed.java new file mode 100644 index 000000000000..138b8701bb13 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftReed.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftReed extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Ageable { + + public CraftReed() { + super(); + } + + public CraftReed(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftAgeable + + private static final net.minecraft.server.BlockStateInteger AGE = getInteger(net.minecraft.server.BlockReed.class, "age"); + + @Override + public int getAge() { + return get(AGE); + } + + @Override + public void setAge(int age) { + set(AGE, age); + } + + @Override + public int getMaximumAge() { + return getMax(AGE); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRepeater.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRepeater.java new file mode 100644 index 000000000000..97724f682901 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRepeater.java @@ -0,0 +1,83 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftRepeater extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Repeater, org.bukkit.block.data.Directional, org.bukkit.block.data.Powerable { + + public CraftRepeater() { + super(); + } + + public CraftRepeater(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftRepeater + + private static final net.minecraft.server.BlockStateInteger DELAY = getInteger(net.minecraft.server.BlockRepeater.class, "delay"); + private static final net.minecraft.server.BlockStateBoolean LOCKED = getBoolean(net.minecraft.server.BlockRepeater.class, "locked"); + + @Override + public int getDelay() { + return get(DELAY); + } + + @Override + public void setDelay(int delay) { + set(DELAY, delay); + } + + @Override + public int getMinimumDelay() { + return getMin(DELAY); + } + + @Override + public int getMaximumDelay() { + return getMax(DELAY); + } + + @Override + public boolean isLocked() { + return get(LOCKED); + } + + @Override + public void setLocked(boolean locked) { + set(LOCKED, locked); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockRepeater.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftPowerable + + private static final net.minecraft.server.BlockStateBoolean POWERED = getBoolean(net.minecraft.server.BlockRepeater.class, "powered"); + + @Override + public boolean isPowered() { + return get(POWERED); + } + + @Override + public void setPowered(boolean powered) { + set(POWERED, powered); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRotatable.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRotatable.java new file mode 100644 index 000000000000..c44f23e64bdb --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftRotatable.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftRotatable extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Orientable { + + public CraftRotatable() { + super(); + } + + public CraftRotatable(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftOrientable + + private static final net.minecraft.server.BlockStateEnum AXIS = getEnum(net.minecraft.server.BlockRotatable.class, "axis"); + + @Override + public org.bukkit.Axis getAxis() { + return get(AXIS, org.bukkit.Axis.class); + } + + @Override + public void setAxis(org.bukkit.Axis axis) { + set(AXIS, axis); + } + + @Override + public java.util.Set getAxes() { + return getValues(AXIS, org.bukkit.Axis.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSapling.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSapling.java new file mode 100644 index 000000000000..0654c9e7b8ca --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSapling.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftSapling extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Sapling { + + public CraftSapling() { + super(); + } + + public CraftSapling(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftSapling + + private static final net.minecraft.server.BlockStateInteger STAGE = getInteger(net.minecraft.server.BlockSapling.class, "stage"); + + @Override + public int getStage() { + return get(STAGE); + } + + @Override + public void setStage(int stage) { + set(STAGE, stage); + } + + @Override + public int getMaximumStage() { + return getMax(STAGE); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSeaPickle.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSeaPickle.java new file mode 100644 index 000000000000..a0078bd8b634 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSeaPickle.java @@ -0,0 +1,53 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftSeaPickle extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.SeaPickle, org.bukkit.block.data.Waterlogged { + + public CraftSeaPickle() { + super(); + } + + public CraftSeaPickle(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftSeaPickle + + private static final net.minecraft.server.BlockStateInteger PICKLES = getInteger(net.minecraft.server.BlockSeaPickle.class, "pickles"); + + @Override + public int getPickles() { + return get(PICKLES); + } + + @Override + public void setPickles(int pickles) { + set(PICKLES, pickles); + } + + @Override + public int getMinimumPickles() { + return getMin(PICKLES); + } + + @Override + public int getMaximumPickles() { + return getMax(PICKLES); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockSeaPickle.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftShulkerBox.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftShulkerBox.java new file mode 100644 index 000000000000..631f5cffb974 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftShulkerBox.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftShulkerBox extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Directional { + + public CraftShulkerBox() { + super(); + } + + public CraftShulkerBox(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockShulkerBox.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSkull.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSkull.java new file mode 100644 index 000000000000..5677a9f0b8a8 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSkull.java @@ -0,0 +1,118 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftSkull extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Rotatable { + + public CraftSkull() { + super(); + } + + public CraftSkull(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftRotatable + + private static final net.minecraft.server.BlockStateInteger ROTATION = getInteger(net.minecraft.server.BlockSkull.class, "rotation"); + + @Override + public org.bukkit.block.BlockFace getRotation() { + int data = get(ROTATION); + switch (data) { + case 0x0: + return org.bukkit.block.BlockFace.SOUTH; + case 0x1: + return org.bukkit.block.BlockFace.SOUTH_SOUTH_WEST; + case 0x2: + return org.bukkit.block.BlockFace.SOUTH_WEST; + case 0x3: + return org.bukkit.block.BlockFace.WEST_SOUTH_WEST; + case 0x4: + return org.bukkit.block.BlockFace.WEST; + case 0x5: + return org.bukkit.block.BlockFace.WEST_NORTH_WEST; + case 0x6: + return org.bukkit.block.BlockFace.NORTH_WEST; + case 0x7: + return org.bukkit.block.BlockFace.NORTH_NORTH_WEST; + case 0x8: + return org.bukkit.block.BlockFace.NORTH; + case 0x9: + return org.bukkit.block.BlockFace.NORTH_NORTH_EAST; + case 0xA: + return org.bukkit.block.BlockFace.NORTH_EAST; + case 0xB: + return org.bukkit.block.BlockFace.EAST_NORTH_EAST; + case 0xC: + return org.bukkit.block.BlockFace.EAST; + case 0xD: + return org.bukkit.block.BlockFace.EAST_SOUTH_EAST; + case 0xE: + return org.bukkit.block.BlockFace.SOUTH_EAST; + case 0xF: + return org.bukkit.block.BlockFace.SOUTH_SOUTH_EAST; + default: + throw new IllegalArgumentException("Unknown rotation " + data); + } + } + + @Override + public void setRotation(org.bukkit.block.BlockFace rotation) { + int val; + switch (rotation) { + case SOUTH: + val = 0x0; + break; + case SOUTH_SOUTH_WEST: + val = 0x1; + break; + case SOUTH_WEST: + val = 0x2; + break; + case WEST_SOUTH_WEST: + val = 0x3; + break; + case WEST: + val = 0x4; + break; + case WEST_NORTH_WEST: + val = 0x5; + break; + case NORTH_WEST: + val = 0x6; + break; + case NORTH_NORTH_WEST: + val = 0x7; + break; + case NORTH: + val = 0x8; + break; + case NORTH_NORTH_EAST: + val = 0x9; + break; + case NORTH_EAST: + val = 0xA; + break; + case EAST_NORTH_EAST: + val = 0xB; + break; + case EAST: + val = 0xC; + break; + case EAST_SOUTH_EAST: + val = 0xD; + break; + case SOUTH_EAST: + val = 0xE; + break; + case SOUTH_SOUTH_EAST: + val = 0xF; + break; + default: + throw new IllegalArgumentException("Illegal rotation " + rotation); + } + set(ROTATION, val); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSkullPlayer.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSkullPlayer.java new file mode 100644 index 000000000000..6e6e0033fe32 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSkullPlayer.java @@ -0,0 +1,118 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftSkullPlayer extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Rotatable { + + public CraftSkullPlayer() { + super(); + } + + public CraftSkullPlayer(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftRotatable + + private static final net.minecraft.server.BlockStateInteger ROTATION = getInteger(net.minecraft.server.BlockSkullPlayer.class, "rotation"); + + @Override + public org.bukkit.block.BlockFace getRotation() { + int data = get(ROTATION); + switch (data) { + case 0x0: + return org.bukkit.block.BlockFace.SOUTH; + case 0x1: + return org.bukkit.block.BlockFace.SOUTH_SOUTH_WEST; + case 0x2: + return org.bukkit.block.BlockFace.SOUTH_WEST; + case 0x3: + return org.bukkit.block.BlockFace.WEST_SOUTH_WEST; + case 0x4: + return org.bukkit.block.BlockFace.WEST; + case 0x5: + return org.bukkit.block.BlockFace.WEST_NORTH_WEST; + case 0x6: + return org.bukkit.block.BlockFace.NORTH_WEST; + case 0x7: + return org.bukkit.block.BlockFace.NORTH_NORTH_WEST; + case 0x8: + return org.bukkit.block.BlockFace.NORTH; + case 0x9: + return org.bukkit.block.BlockFace.NORTH_NORTH_EAST; + case 0xA: + return org.bukkit.block.BlockFace.NORTH_EAST; + case 0xB: + return org.bukkit.block.BlockFace.EAST_NORTH_EAST; + case 0xC: + return org.bukkit.block.BlockFace.EAST; + case 0xD: + return org.bukkit.block.BlockFace.EAST_SOUTH_EAST; + case 0xE: + return org.bukkit.block.BlockFace.SOUTH_EAST; + case 0xF: + return org.bukkit.block.BlockFace.SOUTH_SOUTH_EAST; + default: + throw new IllegalArgumentException("Unknown rotation " + data); + } + } + + @Override + public void setRotation(org.bukkit.block.BlockFace rotation) { + int val; + switch (rotation) { + case SOUTH: + val = 0x0; + break; + case SOUTH_SOUTH_WEST: + val = 0x1; + break; + case SOUTH_WEST: + val = 0x2; + break; + case WEST_SOUTH_WEST: + val = 0x3; + break; + case WEST: + val = 0x4; + break; + case WEST_NORTH_WEST: + val = 0x5; + break; + case NORTH_WEST: + val = 0x6; + break; + case NORTH_NORTH_WEST: + val = 0x7; + break; + case NORTH: + val = 0x8; + break; + case NORTH_NORTH_EAST: + val = 0x9; + break; + case NORTH_EAST: + val = 0xA; + break; + case EAST_NORTH_EAST: + val = 0xB; + break; + case EAST: + val = 0xC; + break; + case EAST_SOUTH_EAST: + val = 0xD; + break; + case SOUTH_EAST: + val = 0xE; + break; + case SOUTH_SOUTH_EAST: + val = 0xF; + break; + default: + throw new IllegalArgumentException("Illegal rotation " + rotation); + } + set(ROTATION, val); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSkullPlayerWall.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSkullPlayerWall.java new file mode 100644 index 000000000000..d2b3ce935f0e --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSkullPlayerWall.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftSkullPlayerWall extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Directional { + + public CraftSkullPlayerWall() { + super(); + } + + public CraftSkullPlayerWall(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockSkullPlayerWall.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSkullWall.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSkullWall.java new file mode 100644 index 000000000000..020e5b715ed2 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSkullWall.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftSkullWall extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Directional { + + public CraftSkullWall() { + super(); + } + + public CraftSkullWall(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockSkullWall.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSnow.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSnow.java new file mode 100644 index 000000000000..d4f0bfa80ff3 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSnow.java @@ -0,0 +1,39 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftSnow extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Snow { + + public CraftSnow() { + super(); + } + + public CraftSnow(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftSnow + + private static final net.minecraft.server.BlockStateInteger LAYERS = getInteger(net.minecraft.server.BlockSnow.class, "layers"); + + @Override + public int getLayers() { + return get(LAYERS); + } + + @Override + public void setLayers(int layers) { + set(LAYERS, layers); + } + + @Override + public int getMinimumLayers() { + return getMin(LAYERS); + } + + @Override + public int getMaximumLayers() { + return getMax(LAYERS); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSoil.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSoil.java new file mode 100644 index 000000000000..09cfa46719de --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftSoil.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftSoil extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Farmland { + + public CraftSoil() { + super(); + } + + public CraftSoil(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftFarmland + + private static final net.minecraft.server.BlockStateInteger MOISTURE = getInteger(net.minecraft.server.BlockSoil.class, "moisture"); + + @Override + public int getMoisture() { + return get(MOISTURE); + } + + @Override + public void setMoisture(int moisture) { + set(MOISTURE, moisture); + } + + @Override + public int getMaximumMoisture() { + return getMax(MOISTURE); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStainedGlassPane.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStainedGlassPane.java new file mode 100644 index 000000000000..0eb5c66071cc --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStainedGlassPane.java @@ -0,0 +1,71 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftStainedGlassPane extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.GlassPane, org.bukkit.block.data.MultipleFacing, org.bukkit.block.data.Waterlogged { + + public CraftStainedGlassPane() { + super(); + } + + public CraftStainedGlassPane(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftMultipleFacing + + private static final net.minecraft.server.BlockStateBoolean[] FACES = new net.minecraft.server.BlockStateBoolean[]{ + getBoolean(net.minecraft.server.BlockStainedGlassPane.class, "north", true), getBoolean(net.minecraft.server.BlockStainedGlassPane.class, "east", true), getBoolean(net.minecraft.server.BlockStainedGlassPane.class, "south", true), getBoolean(net.minecraft.server.BlockStainedGlassPane.class, "west", true), getBoolean(net.minecraft.server.BlockStainedGlassPane.class, "up", true), getBoolean(net.minecraft.server.BlockStainedGlassPane.class, "down", true) + }; + + @Override + public boolean hasFace(org.bukkit.block.BlockFace face) { + return get(FACES[face.ordinal()]); + } + + @Override + public void setFace(org.bukkit.block.BlockFace face, boolean has) { + set(FACES[face.ordinal()], has); + } + + @Override + public java.util.Set getFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null && get(FACES[i])) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + @Override + public java.util.Set getAllowedFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockStainedGlassPane.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStairs.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStairs.java new file mode 100644 index 000000000000..804a90f2130b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStairs.java @@ -0,0 +1,76 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftStairs extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Stairs, org.bukkit.block.data.Bisected, org.bukkit.block.data.Directional, org.bukkit.block.data.Waterlogged { + + public CraftStairs() { + super(); + } + + public CraftStairs(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftStairs + + private static final net.minecraft.server.BlockStateEnum SHAPE = getEnum(net.minecraft.server.BlockStairs.class, "shape"); + + @Override + public Shape getShape() { + return get(SHAPE, Shape.class); + } + + @Override + public void setShape(Shape shape) { + set(SHAPE, shape); + } + + // org.bukkit.craftbukkit.block.data.CraftBisected + + private static final net.minecraft.server.BlockStateEnum HALF = getEnum(net.minecraft.server.BlockStairs.class, "half"); + + @Override + public Half getHalf() { + return get(HALF, Half.class); + } + + @Override + public void setHalf(Half half) { + set(HALF, half); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockStairs.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockStairs.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStem.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStem.java new file mode 100644 index 000000000000..2a91e958268f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStem.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftStem extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Ageable { + + public CraftStem() { + super(); + } + + public CraftStem(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftAgeable + + private static final net.minecraft.server.BlockStateInteger AGE = getInteger(net.minecraft.server.BlockStem.class, "age"); + + @Override + public int getAge() { + return get(AGE); + } + + @Override + public void setAge(int age) { + set(AGE, age); + } + + @Override + public int getMaximumAge() { + return getMax(AGE); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStemAttached.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStemAttached.java new file mode 100644 index 000000000000..c6d9223d18d5 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStemAttached.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftStemAttached extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Directional { + + public CraftStemAttached() { + super(); + } + + public CraftStemAttached(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockStemAttached.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStepAbstract.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStepAbstract.java new file mode 100644 index 000000000000..922d41b1a5c3 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStepAbstract.java @@ -0,0 +1,43 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftStepAbstract extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Slab, org.bukkit.block.data.Waterlogged { + + public CraftStepAbstract() { + super(); + } + + public CraftStepAbstract(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftSlab + + private static final net.minecraft.server.BlockStateEnum TYPE = getEnum(net.minecraft.server.BlockStepAbstract.class, "type"); + + @Override + public Type getType() { + return get(TYPE, Type.class); + } + + @Override + public void setType(Type type) { + set(TYPE, type); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockStepAbstract.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStoneButton.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStoneButton.java new file mode 100644 index 000000000000..84097d5efe04 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStoneButton.java @@ -0,0 +1,62 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftStoneButton extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Switch, org.bukkit.block.data.Directional, org.bukkit.block.data.Powerable { + + public CraftStoneButton() { + super(); + } + + public CraftStoneButton(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftSwitch + + private static final net.minecraft.server.BlockStateEnum FACE = getEnum(net.minecraft.server.BlockStoneButton.class, "face"); + + @Override + public Face getFace() { + return get(FACE, Face.class); + } + + @Override + public void setFace(Face face) { + set(FACE, face); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockStoneButton.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftPowerable + + private static final net.minecraft.server.BlockStateBoolean POWERED = getBoolean(net.minecraft.server.BlockStoneButton.class, "powered"); + + @Override + public boolean isPowered() { + return get(POWERED); + } + + @Override + public void setPowered(boolean powered) { + set(POWERED, powered); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStructure.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStructure.java new file mode 100644 index 000000000000..f2435d34edfe --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftStructure.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftStructure extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.StructureBlock { + + public CraftStructure() { + super(); + } + + public CraftStructure(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftStructureBlock + + private static final net.minecraft.server.BlockStateEnum MODE = getEnum(net.minecraft.server.BlockStructure.class, "mode"); + + @Override + public Mode getMode() { + return get(MODE, Mode.class); + } + + @Override + public void setMode(Mode mode) { + set(MODE, mode); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTNT.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTNT.java new file mode 100644 index 000000000000..cf9c33b82d5c --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTNT.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftTNT extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.TNT { + + public CraftTNT() { + super(); + } + + public CraftTNT(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftTNT + + private static final net.minecraft.server.BlockStateBoolean UNSTABLE = getBoolean(net.minecraft.server.BlockTNT.class, "unstable"); + + @Override + public boolean isUnstable() { + return get(UNSTABLE); + } + + @Override + public void setUnstable(boolean unstable) { + set(UNSTABLE, unstable); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTallPlantFlower.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTallPlantFlower.java new file mode 100644 index 000000000000..3ae8201c4ed3 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTallPlantFlower.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftTallPlantFlower extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Bisected { + + public CraftTallPlantFlower() { + super(); + } + + public CraftTallPlantFlower(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftBisected + + private static final net.minecraft.server.BlockStateEnum HALF = getEnum(net.minecraft.server.BlockTallPlantFlower.class, "half"); + + @Override + public Half getHalf() { + return get(HALF, Half.class); + } + + @Override + public void setHalf(Half half) { + set(HALF, half); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTallPlantShearable.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTallPlantShearable.java new file mode 100644 index 000000000000..c98414b3bc45 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTallPlantShearable.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftTallPlantShearable extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Bisected { + + public CraftTallPlantShearable() { + super(); + } + + public CraftTallPlantShearable(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftBisected + + private static final net.minecraft.server.BlockStateEnum HALF = getEnum(net.minecraft.server.BlockTallPlantShearable.class, "half"); + + @Override + public Half getHalf() { + return get(HALF, Half.class); + } + + @Override + public void setHalf(Half half) { + set(HALF, half); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTallSeaGrass.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTallSeaGrass.java new file mode 100644 index 000000000000..d51e3ec4d4fd --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTallSeaGrass.java @@ -0,0 +1,29 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftTallSeaGrass extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Bisected { + + public CraftTallSeaGrass() { + super(); + } + + public CraftTallSeaGrass(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftBisected + + private static final net.minecraft.server.BlockStateEnum HALF = getEnum(net.minecraft.server.BlockTallSeaGrass.class, "half"); + + @Override + public Half getHalf() { + return get(HALF, Half.class); + } + + @Override + public void setHalf(Half half) { + set(HALF, half); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTorchWall.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTorchWall.java new file mode 100644 index 000000000000..d98f21aaf3c7 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTorchWall.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftTorchWall extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Directional { + + public CraftTorchWall() { + super(); + } + + public CraftTorchWall(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockTorchWall.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTrapdoor.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTrapdoor.java new file mode 100644 index 000000000000..2ec18b29c100 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTrapdoor.java @@ -0,0 +1,90 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftTrapdoor extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.TrapDoor, org.bukkit.block.data.Bisected, org.bukkit.block.data.Directional, org.bukkit.block.data.Openable, org.bukkit.block.data.Powerable, org.bukkit.block.data.Waterlogged { + + public CraftTrapdoor() { + super(); + } + + public CraftTrapdoor(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftBisected + + private static final net.minecraft.server.BlockStateEnum HALF = getEnum(net.minecraft.server.BlockTrapdoor.class, "half"); + + @Override + public Half getHalf() { + return get(HALF, Half.class); + } + + @Override + public void setHalf(Half half) { + set(HALF, half); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockTrapdoor.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftOpenable + + private static final net.minecraft.server.BlockStateBoolean OPEN = getBoolean(net.minecraft.server.BlockTrapdoor.class, "open"); + + @Override + public boolean isOpen() { + return get(OPEN); + } + + @Override + public void setOpen(boolean open) { + set(OPEN, open); + } + + // org.bukkit.craftbukkit.block.data.CraftPowerable + + private static final net.minecraft.server.BlockStateBoolean POWERED = getBoolean(net.minecraft.server.BlockTrapdoor.class, "powered"); + + @Override + public boolean isPowered() { + return get(POWERED); + } + + @Override + public void setPowered(boolean powered) { + set(POWERED, powered); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockTrapdoor.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTripwire.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTripwire.java new file mode 100644 index 000000000000..e39e79c42f84 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTripwire.java @@ -0,0 +1,99 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftTripwire extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Tripwire, org.bukkit.block.data.Attachable, org.bukkit.block.data.MultipleFacing, org.bukkit.block.data.Powerable { + + public CraftTripwire() { + super(); + } + + public CraftTripwire(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftTripwire + + private static final net.minecraft.server.BlockStateBoolean DISARMED = getBoolean(net.minecraft.server.BlockTripwire.class, "disarmed"); + + @Override + public boolean isDisarmed() { + return get(DISARMED); + } + + @Override + public void setDisarmed(boolean disarmed) { + set(DISARMED, disarmed); + } + + // org.bukkit.craftbukkit.block.data.CraftAttachable + + private static final net.minecraft.server.BlockStateBoolean ATTACHED = getBoolean(net.minecraft.server.BlockTripwire.class, "attached"); + + @Override + public boolean isAttached() { + return get(ATTACHED); + } + + @Override + public void setAttached(boolean attached) { + set(ATTACHED, attached); + } + + // org.bukkit.craftbukkit.block.data.CraftMultipleFacing + + private static final net.minecraft.server.BlockStateBoolean[] FACES = new net.minecraft.server.BlockStateBoolean[]{ + getBoolean(net.minecraft.server.BlockTripwire.class, "north", true), getBoolean(net.minecraft.server.BlockTripwire.class, "east", true), getBoolean(net.minecraft.server.BlockTripwire.class, "south", true), getBoolean(net.minecraft.server.BlockTripwire.class, "west", true), getBoolean(net.minecraft.server.BlockTripwire.class, "up", true), getBoolean(net.minecraft.server.BlockTripwire.class, "down", true) + }; + + @Override + public boolean hasFace(org.bukkit.block.BlockFace face) { + return get(FACES[face.ordinal()]); + } + + @Override + public void setFace(org.bukkit.block.BlockFace face, boolean has) { + set(FACES[face.ordinal()], has); + } + + @Override + public java.util.Set getFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null && get(FACES[i])) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + @Override + public java.util.Set getAllowedFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + // org.bukkit.craftbukkit.block.data.CraftPowerable + + private static final net.minecraft.server.BlockStateBoolean POWERED = getBoolean(net.minecraft.server.BlockTripwire.class, "powered"); + + @Override + public boolean isPowered() { + return get(POWERED); + } + + @Override + public void setPowered(boolean powered) { + set(POWERED, powered); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTripwireHook.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTripwireHook.java new file mode 100644 index 000000000000..bd7b43ade5a4 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTripwireHook.java @@ -0,0 +1,62 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftTripwireHook extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.TripwireHook, org.bukkit.block.data.Attachable, org.bukkit.block.data.Directional, org.bukkit.block.data.Powerable { + + public CraftTripwireHook() { + super(); + } + + public CraftTripwireHook(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftAttachable + + private static final net.minecraft.server.BlockStateBoolean ATTACHED = getBoolean(net.minecraft.server.BlockTripwireHook.class, "attached"); + + @Override + public boolean isAttached() { + return get(ATTACHED); + } + + @Override + public void setAttached(boolean attached) { + set(ATTACHED, attached); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockTripwireHook.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftPowerable + + private static final net.minecraft.server.BlockStateBoolean POWERED = getBoolean(net.minecraft.server.BlockTripwireHook.class, "powered"); + + @Override + public boolean isPowered() { + return get(POWERED); + } + + @Override + public void setPowered(boolean powered) { + set(POWERED, powered); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTurtleEgg.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTurtleEgg.java new file mode 100644 index 000000000000..64812ad12e30 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftTurtleEgg.java @@ -0,0 +1,55 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftTurtleEgg extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.TurtleEgg { + + public CraftTurtleEgg() { + super(); + } + + public CraftTurtleEgg(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftTurtleEgg + + private static final net.minecraft.server.BlockStateInteger EGGS = getInteger(net.minecraft.server.BlockTurtleEgg.class, "eggs"); + private static final net.minecraft.server.BlockStateInteger HATCH = getInteger(net.minecraft.server.BlockTurtleEgg.class, "hatch"); + + @Override + public int getEggs() { + return get(EGGS); + } + + @Override + public void setEggs(int eggs) { + set(EGGS, eggs); + } + + @Override + public int getMinimumEggs() { + return getMin(EGGS); + } + + @Override + public int getMaximumEggs() { + return getMax(EGGS); + } + + @Override + public int getHatch() { + return get(HATCH); + } + + @Override + public void setHatch(int hatch) { + set(HATCH, hatch); + } + + @Override + public int getMaximumHatch() { + return getMax(HATCH); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftVine.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftVine.java new file mode 100644 index 000000000000..90e025cef013 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftVine.java @@ -0,0 +1,57 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftVine extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.MultipleFacing { + + public CraftVine() { + super(); + } + + public CraftVine(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftMultipleFacing + + private static final net.minecraft.server.BlockStateBoolean[] FACES = new net.minecraft.server.BlockStateBoolean[]{ + getBoolean(net.minecraft.server.BlockVine.class, "north", true), getBoolean(net.minecraft.server.BlockVine.class, "east", true), getBoolean(net.minecraft.server.BlockVine.class, "south", true), getBoolean(net.minecraft.server.BlockVine.class, "west", true), getBoolean(net.minecraft.server.BlockVine.class, "up", true), getBoolean(net.minecraft.server.BlockVine.class, "down", true) + }; + + @Override + public boolean hasFace(org.bukkit.block.BlockFace face) { + return get(FACES[face.ordinal()]); + } + + @Override + public void setFace(org.bukkit.block.BlockFace face, boolean has) { + set(FACES[face.ordinal()], has); + } + + @Override + public java.util.Set getFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null && get(FACES[i])) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } + + @Override + public java.util.Set getAllowedFaces() { + com.google.common.collect.ImmutableSet.Builder faces = com.google.common.collect.ImmutableSet.builder(); + + for (int i = 0; i < FACES.length; i++) { + if (FACES[i] != null) { + faces.add(org.bukkit.block.BlockFace.values()[i]); + } + } + + return faces.build(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftWallSign.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftWallSign.java new file mode 100644 index 000000000000..ffa7e3c4b5a2 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftWallSign.java @@ -0,0 +1,48 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftWallSign extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.WallSign, org.bukkit.block.data.Directional, org.bukkit.block.data.Waterlogged { + + public CraftWallSign() { + super(); + } + + public CraftWallSign(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockWallSign.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.server.BlockStateBoolean WATERLOGGED = getBoolean(net.minecraft.server.BlockWallSign.class, "waterlogged"); + + @Override + public boolean isWaterlogged() { + return get(WATERLOGGED); + } + + @Override + public void setWaterlogged(boolean waterlogged) { + set(WATERLOGGED, waterlogged); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftWitherSkull.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftWitherSkull.java new file mode 100644 index 000000000000..0aec1dbed5c0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftWitherSkull.java @@ -0,0 +1,118 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftWitherSkull extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Rotatable { + + public CraftWitherSkull() { + super(); + } + + public CraftWitherSkull(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftRotatable + + private static final net.minecraft.server.BlockStateInteger ROTATION = getInteger(net.minecraft.server.BlockWitherSkull.class, "rotation"); + + @Override + public org.bukkit.block.BlockFace getRotation() { + int data = get(ROTATION); + switch (data) { + case 0x0: + return org.bukkit.block.BlockFace.SOUTH; + case 0x1: + return org.bukkit.block.BlockFace.SOUTH_SOUTH_WEST; + case 0x2: + return org.bukkit.block.BlockFace.SOUTH_WEST; + case 0x3: + return org.bukkit.block.BlockFace.WEST_SOUTH_WEST; + case 0x4: + return org.bukkit.block.BlockFace.WEST; + case 0x5: + return org.bukkit.block.BlockFace.WEST_NORTH_WEST; + case 0x6: + return org.bukkit.block.BlockFace.NORTH_WEST; + case 0x7: + return org.bukkit.block.BlockFace.NORTH_NORTH_WEST; + case 0x8: + return org.bukkit.block.BlockFace.NORTH; + case 0x9: + return org.bukkit.block.BlockFace.NORTH_NORTH_EAST; + case 0xA: + return org.bukkit.block.BlockFace.NORTH_EAST; + case 0xB: + return org.bukkit.block.BlockFace.EAST_NORTH_EAST; + case 0xC: + return org.bukkit.block.BlockFace.EAST; + case 0xD: + return org.bukkit.block.BlockFace.EAST_SOUTH_EAST; + case 0xE: + return org.bukkit.block.BlockFace.SOUTH_EAST; + case 0xF: + return org.bukkit.block.BlockFace.SOUTH_SOUTH_EAST; + default: + throw new IllegalArgumentException("Unknown rotation " + data); + } + } + + @Override + public void setRotation(org.bukkit.block.BlockFace rotation) { + int val; + switch (rotation) { + case SOUTH: + val = 0x0; + break; + case SOUTH_SOUTH_WEST: + val = 0x1; + break; + case SOUTH_WEST: + val = 0x2; + break; + case WEST_SOUTH_WEST: + val = 0x3; + break; + case WEST: + val = 0x4; + break; + case WEST_NORTH_WEST: + val = 0x5; + break; + case NORTH_WEST: + val = 0x6; + break; + case NORTH_NORTH_WEST: + val = 0x7; + break; + case NORTH: + val = 0x8; + break; + case NORTH_NORTH_EAST: + val = 0x9; + break; + case NORTH_EAST: + val = 0xA; + break; + case EAST_NORTH_EAST: + val = 0xB; + break; + case EAST: + val = 0xC; + break; + case EAST_SOUTH_EAST: + val = 0xD; + break; + case SOUTH_EAST: + val = 0xE; + break; + case SOUTH_SOUTH_EAST: + val = 0xF; + break; + default: + throw new IllegalArgumentException("Illegal rotation " + rotation); + } + set(ROTATION, val); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftWitherSkullWall.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftWitherSkullWall.java new file mode 100644 index 000000000000..fa9597da20ee --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftWitherSkullWall.java @@ -0,0 +1,34 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftWitherSkullWall extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.Directional { + + public CraftWitherSkullWall() { + super(); + } + + public CraftWitherSkullWall(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockWitherSkullWall.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftWoodButton.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftWoodButton.java new file mode 100644 index 000000000000..799714178f3f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftWoodButton.java @@ -0,0 +1,62 @@ +/** + * Automatically generated file, changes will be lost. + */ +package org.bukkit.craftbukkit.block.impl; + +public final class CraftWoodButton extends org.bukkit.craftbukkit.block.data.CraftBlockData implements org.bukkit.block.data.type.Switch, org.bukkit.block.data.Directional, org.bukkit.block.data.Powerable { + + public CraftWoodButton() { + super(); + } + + public CraftWoodButton(net.minecraft.server.IBlockData state) { + super(state); + } + + // org.bukkit.craftbukkit.block.data.type.CraftSwitch + + private static final net.minecraft.server.BlockStateEnum FACE = getEnum(net.minecraft.server.BlockWoodButton.class, "face"); + + @Override + public Face getFace() { + return get(FACE, Face.class); + } + + @Override + public void setFace(Face face) { + set(FACE, face); + } + + // org.bukkit.craftbukkit.block.data.CraftDirectional + + private static final net.minecraft.server.BlockStateEnum FACING = getEnum(net.minecraft.server.BlockWoodButton.class, "facing"); + + @Override + public org.bukkit.block.BlockFace getFacing() { + return get(FACING, org.bukkit.block.BlockFace.class); + } + + @Override + public void setFacing(org.bukkit.block.BlockFace facing) { + set(FACING, facing); + } + + @Override + public java.util.Set getFaces() { + return getValues(FACING, org.bukkit.block.BlockFace.class); + } + + // org.bukkit.craftbukkit.block.data.CraftPowerable + + private static final net.minecraft.server.BlockStateBoolean POWERED = getBoolean(net.minecraft.server.BlockWoodButton.class, "powered"); + + @Override + public boolean isPowered() { + return get(POWERED); + } + + @Override + public void setPowered(boolean powered) { + set(POWERED, powered); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/boss/CraftBossBar.java b/src/main/java/org/bukkit/craftbukkit/boss/CraftBossBar.java new file mode 100644 index 000000000000..e9f92eccd2c5 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/boss/CraftBossBar.java @@ -0,0 +1,228 @@ +package org.bukkit.craftbukkit.boss; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import net.minecraft.server.BossBattle; +import net.minecraft.server.BossBattleServer; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.PacketPlayOutBoss; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarFlag; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class CraftBossBar implements BossBar { + + private final BossBattleServer handle; + private Map flags; + + public CraftBossBar(String title, BarColor color, BarStyle style, BarFlag... flags) { + handle = new BossBattleServer( + CraftChatMessage.fromString(title, true)[0], + convertColor(color), + convertStyle(style) + ); + + this.initialize(); + + for (BarFlag flag : flags) { + this.addFlag(flag); + } + + this.setColor(color); + this.setStyle(style); + } + + public CraftBossBar(BossBattleServer bossBattleServer) { + this.handle = bossBattleServer; + this.initialize(); + } + + private void initialize() { + this.flags = new HashMap<>(); + this.flags.put(BarFlag.DARKEN_SKY, new FlagContainer(handle::isDarkenSky, handle::setDarkenSky)); + this.flags.put(BarFlag.PLAY_BOSS_MUSIC, new FlagContainer(handle::isPlayMusic, handle::setPlayMusic)); + this.flags.put(BarFlag.CREATE_FOG, new FlagContainer(handle::isCreateFog, handle::setCreateFog)); + } + + private BarColor convertColor(BossBattle.BarColor color) { + BarColor bukkitColor = BarColor.valueOf(color.name()); + return (bukkitColor == null) ? BarColor.WHITE : bukkitColor; + } + + private BossBattle.BarColor convertColor(BarColor color) { + BossBattle.BarColor nmsColor = BossBattle.BarColor.valueOf(color.name()); + return (nmsColor == null) ? BossBattle.BarColor.WHITE : nmsColor; + } + + private BossBattle.BarStyle convertStyle(BarStyle style) { + switch (style) { + default: + case SOLID: + return BossBattle.BarStyle.PROGRESS; + case SEGMENTED_6: + return BossBattle.BarStyle.NOTCHED_6; + case SEGMENTED_10: + return BossBattle.BarStyle.NOTCHED_10; + case SEGMENTED_12: + return BossBattle.BarStyle.NOTCHED_12; + case SEGMENTED_20: + return BossBattle.BarStyle.NOTCHED_20; + } + } + + private BarStyle convertStyle(BossBattle.BarStyle style) { + switch (style) { + default: + case PROGRESS: + return BarStyle.SOLID; + case NOTCHED_6: + return BarStyle.SEGMENTED_6; + case NOTCHED_10: + return BarStyle.SEGMENTED_10; + case NOTCHED_12: + return BarStyle.SEGMENTED_12; + case NOTCHED_20: + return BarStyle.SEGMENTED_20; + } + } + + @Override + public String getTitle() { + return CraftChatMessage.fromComponent(handle.title); + } + + @Override + public void setTitle(String title) { + handle.title = CraftChatMessage.fromString(title, true)[0]; + handle.sendUpdate(PacketPlayOutBoss.Action.UPDATE_NAME); + } + + @Override + public BarColor getColor() { + return convertColor(handle.color); + } + + @Override + public void setColor(BarColor color) { + handle.color = convertColor(color); + handle.sendUpdate(PacketPlayOutBoss.Action.UPDATE_STYLE); + } + + @Override + public BarStyle getStyle() { + return convertStyle(handle.style); + } + + @Override + public void setStyle(BarStyle style) { + handle.style = convertStyle(style); + handle.sendUpdate(PacketPlayOutBoss.Action.UPDATE_STYLE); + } + + @Override + public void addFlag(BarFlag flag) { + FlagContainer flagContainer = flags.get(flag); + if (flagContainer != null) { + flagContainer.set.accept(true); + } + } + + @Override + public void removeFlag(BarFlag flag) { + FlagContainer flagContainer = flags.get(flag); + if (flagContainer != null) { + flagContainer.set.accept(false); + } + } + + @Override + public boolean hasFlag(BarFlag flag) { + FlagContainer flagContainer = flags.get(flag); + if (flagContainer != null) { + return flagContainer.get.get(); + } + return false; + } + + @Override + public void setProgress(double progress) { + Preconditions.checkArgument(progress >= 0.0 && progress <= 1.0, "Progress must be between 0.0 and 1.0 (%s)", progress); + handle.setProgress((float) progress); + } + + @Override + public double getProgress() { + return handle.getProgress(); + } + + @Override + public void addPlayer(Player player) { + handle.addPlayer(((CraftPlayer) player).getHandle()); + } + + @Override + public void removePlayer(Player player) { + handle.removePlayer(((CraftPlayer) player).getHandle()); + } + + @Override + public List getPlayers() { + ImmutableList.Builder players = ImmutableList.builder(); + for (EntityPlayer p : handle.getPlayers()) { + players.add(p.getBukkitEntity()); + } + return players.build(); + } + + @Override + public void setVisible(boolean visible) { + handle.setVisible(visible); + } + + @Override + public boolean isVisible() { + return handle.visible; + } + + @Override + public void show() { + handle.setVisible(true); + } + + @Override + public void hide() { + handle.setVisible(false); + } + + @Override + public void removeAll() { + for (Player player : getPlayers()) { + removePlayer(player); + } + } + + private class FlagContainer { + + private Supplier get; + private Consumer set; + + private FlagContainer(Supplier get, Consumer set) { + this.get = get; + this.set = set; + } + } + + public BossBattleServer getHandle() { + return handle; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/boss/CraftKeyedBossbar.java b/src/main/java/org/bukkit/craftbukkit/boss/CraftKeyedBossbar.java new file mode 100644 index 000000000000..e6ad94022065 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/boss/CraftKeyedBossbar.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.boss; + +import net.minecraft.server.BossBattleCustom; +import org.bukkit.NamespacedKey; +import org.bukkit.boss.KeyedBossBar; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; + +public class CraftKeyedBossbar extends CraftBossBar implements KeyedBossBar { + + public CraftKeyedBossbar(BossBattleCustom bossBattleCustom) { + super(bossBattleCustom); + } + + @Override + public NamespacedKey getKey() { + return CraftNamespacedKey.fromMinecraft(getHandle().getKey()); + } + + @Override + public BossBattleCustom getHandle() { + return (BossBattleCustom) super.getHandle(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOExecutor.java b/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOExecutor.java new file mode 100644 index 000000000000..33d5fc7d5eaa --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOExecutor.java @@ -0,0 +1,43 @@ +package org.bukkit.craftbukkit.chunkio; + +import net.minecraft.server.Chunk; +import net.minecraft.server.ChunkProviderServer; +import net.minecraft.server.ChunkRegionLoader; +import net.minecraft.server.MCUtil; // Paper +import net.minecraft.server.World; +import org.bukkit.craftbukkit.util.AsynchronousExecutor; + +public class ChunkIOExecutor { + static final int BASE_THREADS = 1; + static final int PLAYERS_PER_THREAD = 50; + + private static final AsynchronousExecutor instance = new AsynchronousExecutor(new ChunkIOProvider(), BASE_THREADS); + + public static Chunk syncChunkLoad(World world, ChunkRegionLoader loader, ChunkProviderServer provider, int x, int z) { + return MCUtil.ensureMain("Async Chunk Load", () -> instance.getSkipQueue(new QueuedChunk(x, z, loader, world, provider))); // Paper + } + + public static void queueChunkLoad(World world, ChunkRegionLoader loader, ChunkProviderServer provider, int x, int z, Runnable runnable) { + instance.add(new QueuedChunk(x, z, loader, world, provider), runnable); + } + + // Abuses the fact that hashCode and equals for QueuedChunk only use world and coords + public static void dropQueuedChunkLoad(World world, int x, int z, Runnable runnable) { + instance.drop(new QueuedChunk(x, z, null, world, null), runnable); + } + + public static void adjustPoolSize(int players) { + int size = Math.max(BASE_THREADS, (int) Math.ceil(players / PLAYERS_PER_THREAD)); + instance.setActiveThreads(size); + } + + public static void tick() { + instance.finishActive(); + } + + // Paper start + public static boolean hasQueuedChunkLoad(World world, int x, int z) { + return instance.hasTask(new QueuedChunk(x, z, null, world, null)); + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java b/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java new file mode 100644 index 000000000000..4cfe24df154f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java @@ -0,0 +1,61 @@ +package org.bukkit.craftbukkit.chunkio; + +import java.io.IOException; + +import co.aikar.timings.Timing; +import net.minecraft.server.Chunk; +import net.minecraft.server.ChunkCoordIntPair; +import net.minecraft.server.ChunkRegionLoader; +import net.minecraft.server.NBTTagCompound; + +import org.bukkit.Server; +import org.bukkit.craftbukkit.util.AsynchronousExecutor; + +import java.util.concurrent.atomic.AtomicInteger; + +class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider { + private final AtomicInteger threadNumber = new AtomicInteger(1); + + // async stuff + public Chunk callStage1(QueuedChunk queuedChunk) throws RuntimeException { + try (Timing ignored = queuedChunk.provider.world.timings.chunkIOStage1.startTimingIfSync()) { // Paper + ChunkRegionLoader loader = queuedChunk.loader; + Object[] data = loader.loadChunk(queuedChunk.world, queuedChunk.x, queuedChunk.z, (chunk) -> {}); + + if (data != null) { + queuedChunk.compound = (NBTTagCompound) data[1]; + return (Chunk) data[0]; + } + + return null; + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + // sync stuff + public void callStage2(QueuedChunk queuedChunk, Chunk chunk) throws RuntimeException { + if (chunk == null || queuedChunk.provider.chunks.containsKey(ChunkCoordIntPair.a(queuedChunk.x, queuedChunk.z))) { // Paper - also call original if it was already loaded + // If the chunk loading failed just do it synchronously (may generate) + queuedChunk.provider.getChunkAt(queuedChunk.x, queuedChunk.z, true, true); // Paper - actually call original if it was already loaded + return; + } + try (Timing ignored = queuedChunk.provider.world.timings.chunkIOStage2.startTimingIfSync()) { // Paper + + queuedChunk.loader.loadEntities(queuedChunk.compound.getCompound("Level"), chunk); + chunk.setLastSaved(queuedChunk.provider.world.getTime()); + queuedChunk.provider.chunks.put(ChunkCoordIntPair.a(queuedChunk.x, queuedChunk.z), chunk); + chunk.addEntities(); + } // Paper + } + + public void callStage3(QueuedChunk queuedChunk, Chunk chunk, Runnable runnable) throws RuntimeException { + runnable.run(); + } + + public Thread newThread(Runnable runnable) { + Thread thread = new Thread(runnable, "Chunk I/O Executor Thread-" + threadNumber.getAndIncrement()); + thread.setDaemon(true); + return thread; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/chunkio/QueuedChunk.java b/src/main/java/org/bukkit/craftbukkit/chunkio/QueuedChunk.java new file mode 100644 index 000000000000..842d424f6a0e --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/chunkio/QueuedChunk.java @@ -0,0 +1,38 @@ +package org.bukkit.craftbukkit.chunkio; + +import net.minecraft.server.ChunkProviderServer; +import net.minecraft.server.ChunkRegionLoader; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.World; + +class QueuedChunk { + final int x; + final int z; + final ChunkRegionLoader loader; + final World world; + final ChunkProviderServer provider; + NBTTagCompound compound; + + public QueuedChunk(int x, int z, ChunkRegionLoader loader, World world, ChunkProviderServer provider) { + this.x = x; + this.z = z; + this.loader = loader; + this.world = world; + this.provider = provider; + } + + @Override + public int hashCode() { + return (x * 31 + z * 29) ^ world.hashCode(); + } + + @Override + public boolean equals(Object object) { + if (object instanceof QueuedChunk) { + QueuedChunk other = (QueuedChunk) object; + return x == other.x && z == other.z && world == other.world; + } + + return false; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java new file mode 100644 index 000000000000..5f33c9e52ac5 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java @@ -0,0 +1,60 @@ +package org.bukkit.craftbukkit.command; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import com.mojang.brigadier.tree.LiteralCommandNode; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; +import net.minecraft.server.CommandListenerWrapper; +import org.bukkit.command.Command; +import org.bukkit.craftbukkit.CraftServer; + +public class BukkitCommandWrapper implements com.mojang.brigadier.Command, Predicate, SuggestionProvider { + + private final CraftServer server; + private final Command command; + + public BukkitCommandWrapper(CraftServer server, Command command) { + this.server = server; + this.command = command; + } + + public LiteralCommandNode register(CommandDispatcher dispatcher, String label) { + return dispatcher.register( + LiteralArgumentBuilder.literal(label).requires(this).executes(this) + .then(RequiredArgumentBuilder.argument("args", StringArgumentType.greedyString()).suggests(this).executes(this)) + ); + } + + @Override + public boolean test(CommandListenerWrapper wrapper) { + return command.testPermissionSilent(wrapper.getBukkitSender()); + } + + @Override + public int run(CommandContext context) throws CommandSyntaxException { + return server.dispatchCommand(context.getSource().getBukkitSender(), context.getInput()) ? 1 : 0; + } + + @Override + public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) throws CommandSyntaxException { + List results = server.tabComplete(context.getSource().getBukkitSender(), builder.getInput(), context.getSource().getWorld(), context.getSource().getPosition(), true); + + // Defaults to sub nodes, but we have just one giant args node, so offset accordingly + builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + 1); + + for (String s : results) { + builder.suggest(s); + } + + return builder.buildFuture(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java new file mode 100644 index 000000000000..95d13c146bba --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java @@ -0,0 +1,109 @@ +package org.bukkit.craftbukkit.command; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.util.Waitable; + +// Paper start - JLine update +import net.minecraft.server.DedicatedServer; // Paper +import org.jline.reader.Candidate; +import org.jline.reader.Completer; +import org.jline.reader.LineReader; +import org.jline.reader.ParsedLine; +// Paper end +import org.bukkit.event.server.TabCompleteEvent; + +public class ConsoleCommandCompleter implements Completer { + private final DedicatedServer server; // Paper - CraftServer -> DedicatedServer + + public ConsoleCommandCompleter(DedicatedServer server) { // Paper - CraftServer -> DedicatedServer + this.server = server; + } + + // Paper start - Change method signature for JLine update + public void complete(LineReader reader, ParsedLine line, List candidates) { + final CraftServer server = this.server.server; + final String buffer = line.line(); + // Async Tab Complete + com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event; + java.util.List completions = new java.util.ArrayList<>(); + event = new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(server.getConsoleSender(), completions, + buffer, true, null); + event.callEvent(); + completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.getCompletions(); + + if (event.isCancelled() || event.isHandled()) { + // Still fire sync event with the provided completions, if someone is listening + if (!event.isCancelled() && TabCompleteEvent.getHandlerList().getRegisteredListeners().length > 0) { + List finalCompletions = completions; + Waitable> syncCompletions = new Waitable>() { + @Override + protected List evaluate() { + org.bukkit.event.server.TabCompleteEvent syncEvent = new org.bukkit.event.server.TabCompleteEvent(server.getConsoleSender(), buffer, finalCompletions); + return syncEvent.callEvent() ? syncEvent.getCompletions() : com.google.common.collect.ImmutableList.of(); + } + }; + server.getServer().processQueue.add(syncCompletions); + try { + completions = syncCompletions.get(); + } catch (InterruptedException | ExecutionException e1) { + e1.printStackTrace(); + } + } + + if (!completions.isEmpty()) { + candidates.addAll(completions.stream().map(Candidate::new).collect(java.util.stream.Collectors.toList())); + } + return; + } + + // Paper end + Waitable> waitable = new Waitable>() { + @Override + protected List evaluate() { + List offers = server.getCommandMap().tabComplete(server.getConsoleSender(), buffer); + + TabCompleteEvent tabEvent = new TabCompleteEvent(server.getConsoleSender(), buffer, (offers == null) ? Collections.EMPTY_LIST : offers); + server.getPluginManager().callEvent(tabEvent); + + return tabEvent.isCancelled() ? Collections.EMPTY_LIST : tabEvent.getCompletions(); + } + }; + server.getServer().processQueue.add(waitable); // Paper - Remove "this." + try { + List offers = waitable.get(); + if (offers == null) { + return; // Paper - Method returns void + } + + // Paper start - JLine update + for (String completion : offers) { + if (completion.isEmpty()) { + continue; + } + + candidates.add(new Candidate(completion)); + } + // Paper end + + // Paper start - JLine handles cursor now + /* + final int lastSpace = buffer.lastIndexOf(' '); + if (lastSpace == -1) { + return cursor - buffer.length(); + } else { + return cursor - (buffer.length() - lastSpace - 1); + } + */ + // Paper end + } catch (ExecutionException e) { + server.getLogger().log(Level.WARNING, "Unhandled exception when tab completing", e); // Paper - Remove "this." + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/command/CraftBlockCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/CraftBlockCommandSender.java new file mode 100644 index 000000000000..701a57e02098 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/command/CraftBlockCommandSender.java @@ -0,0 +1,56 @@ +package org.bukkit.craftbukkit.command; + +import net.minecraft.server.CommandListenerWrapper; +import net.minecraft.server.IChatBaseComponent; +import net.minecraft.server.TileEntity; + +import org.bukkit.block.Block; +import org.bukkit.command.BlockCommandSender; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.util.CraftChatMessage; + +/** + * Represents input from a command block + */ +public class CraftBlockCommandSender extends ServerCommandSender implements BlockCommandSender { + private final CommandListenerWrapper block; + private final TileEntity tile; + + public CraftBlockCommandSender(CommandListenerWrapper commandBlockListenerAbstract, TileEntity tile) { + super(); + this.block = commandBlockListenerAbstract; + this.tile = tile; + } + + public Block getBlock() { + return CraftBlock.at(tile.getWorld(), tile.getPosition()); + } + + public void sendMessage(String message) { + for (IChatBaseComponent component : CraftChatMessage.fromString(message)) { + block.base.sendMessage(component); + } + } + + public void sendMessage(String[] messages) { + for (String message : messages) { + sendMessage(message); + } + } + + public String getName() { + return block.getName(); + } + + public boolean isOp() { + return true; + } + + public void setOp(boolean value) { + throw new UnsupportedOperationException("Cannot change operator status of a block"); + } + + public CommandListenerWrapper getWrapper() { + return block; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/command/CraftCommandMap.java b/src/main/java/org/bukkit/craftbukkit/command/CraftCommandMap.java new file mode 100644 index 000000000000..2fd69c0f915f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/command/CraftCommandMap.java @@ -0,0 +1,17 @@ +package org.bukkit.craftbukkit.command; + +import java.util.Map; +import org.bukkit.Server; +import org.bukkit.command.Command; +import org.bukkit.command.SimpleCommandMap; + +public class CraftCommandMap extends SimpleCommandMap { + + public CraftCommandMap(Server server) { + super(server); + } + + public Map getKnownCommands() { + return knownCommands; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java new file mode 100644 index 000000000000..9abcf92db3b5 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java @@ -0,0 +1,66 @@ +package org.bukkit.craftbukkit.command; + +import org.bukkit.ChatColor; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.conversations.Conversation; +import org.bukkit.conversations.ConversationAbandonedEvent; +import org.bukkit.conversations.ManuallyAbandonedConversationCanceller; +import org.bukkit.craftbukkit.conversations.ConversationTracker; + +/** + * Represents CLI input from a console + */ +public class CraftConsoleCommandSender extends ServerCommandSender implements ConsoleCommandSender { + + protected final ConversationTracker conversationTracker = new ConversationTracker(); + + protected CraftConsoleCommandSender() { + super(); + } + + public void sendMessage(String message) { + sendRawMessage(message); + } + + public void sendRawMessage(String message) { + System.out.println(ChatColor.stripColor(message)); + } + + public void sendMessage(String[] messages) { + for (String message : messages) { + sendMessage(message); + } + } + + public String getName() { + return "CONSOLE"; + } + + public boolean isOp() { + return true; + } + + public void setOp(boolean value) { + throw new UnsupportedOperationException("Cannot change operator status of server console"); + } + + public boolean beginConversation(Conversation conversation) { + return conversationTracker.beginConversation(conversation); + } + + public void abandonConversation(Conversation conversation) { + conversationTracker.abandonConversation(conversation, new ConversationAbandonedEvent(conversation, new ManuallyAbandonedConversationCanceller())); + } + + public void abandonConversation(Conversation conversation, ConversationAbandonedEvent details) { + conversationTracker.abandonConversation(conversation, details); + } + + public void acceptConversationInput(String input) { + conversationTracker.acceptConversationInput(input); + } + + public boolean isConversing() { + return conversationTracker.isConversing(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/command/CraftRemoteConsoleCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/CraftRemoteConsoleCommandSender.java new file mode 100644 index 000000000000..228e88a6e2b9 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/command/CraftRemoteConsoleCommandSender.java @@ -0,0 +1,41 @@ +package org.bukkit.craftbukkit.command; + +import net.minecraft.server.ChatComponentText; +import net.minecraft.server.RemoteControlCommandListener; +import org.bukkit.command.RemoteConsoleCommandSender; + +public class CraftRemoteConsoleCommandSender extends ServerCommandSender implements RemoteConsoleCommandSender { + + private final RemoteControlCommandListener listener; + + public CraftRemoteConsoleCommandSender(RemoteControlCommandListener listener) { + this.listener = listener; + } + + @Override + public void sendMessage(String message) { + listener.sendMessage(new ChatComponentText(message + "\n")); // Send a newline after each message, to preserve formatting. + } + + @Override + public void sendMessage(String[] messages) { + for (String message : messages) { + sendMessage(message); + } + } + + @Override + public String getName() { + return "Rcon"; + } + + @Override + public boolean isOp() { + return true; + } + + @Override + public void setOp(boolean value) { + throw new UnsupportedOperationException("Cannot change operator status of remote controller."); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/command/ProxiedNativeCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/ProxiedNativeCommandSender.java new file mode 100644 index 000000000000..7609e861cb4b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/command/ProxiedNativeCommandSender.java @@ -0,0 +1,133 @@ + +package org.bukkit.craftbukkit.command; + +import java.util.Set; +import net.minecraft.server.CommandListenerWrapper; + +import org.bukkit.Server; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ProxiedCommandSender; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.plugin.Plugin; + +public class ProxiedNativeCommandSender implements ProxiedCommandSender { + + private final CommandListenerWrapper orig; + private final CommandSender caller; + private final CommandSender callee; + + public ProxiedNativeCommandSender(CommandListenerWrapper orig, CommandSender caller, CommandSender callee) { + this.orig = orig; + this.caller = caller; + this.callee = callee; + } + + public CommandListenerWrapper getHandle() { + return orig; + } + + @Override + public CommandSender getCaller() { + return caller; + } + + @Override + public CommandSender getCallee() { + return callee; + } + + @Override + public void sendMessage(String message) { + getCaller().sendMessage(message); + } + + @Override + public void sendMessage(String[] messages) { + getCaller().sendMessage(messages); + } + + @Override + public Server getServer() { + return getCallee().getServer(); + } + + @Override + public String getName() { + return getCallee().getName(); + } + + @Override + public boolean isPermissionSet(String name) { + return getCaller().isPermissionSet(name); + } + + @Override + public boolean isPermissionSet(Permission perm) { + return getCaller().isPermissionSet(perm); + } + + @Override + public boolean hasPermission(String name) { + return getCaller().hasPermission(name); + } + + @Override + public boolean hasPermission(Permission perm) { + return getCaller().hasPermission(perm); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { + return getCaller().addAttachment(plugin, name, value); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin) { + return getCaller().addAttachment(plugin); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { + return getCaller().addAttachment(plugin, name, value, ticks); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, int ticks) { + return getCaller().addAttachment(plugin, ticks); + } + + @Override + public void removeAttachment(PermissionAttachment attachment) { + getCaller().removeAttachment(attachment); + } + + @Override + public void recalculatePermissions() { + getCaller().recalculatePermissions(); + } + + @Override + public Set getEffectivePermissions() { + return getCaller().getEffectivePermissions(); + } + + @Override + public boolean isOp() { + return getCaller().isOp(); + } + + @Override + public void setOp(boolean value) { + getCaller().setOp(value); + } + + // Spigot start + @Override + public Spigot spigot() + { + return getCaller().spigot(); + } + // Spigot end +} diff --git a/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java new file mode 100644 index 000000000000..5c3421dd360f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java @@ -0,0 +1,103 @@ +package org.bukkit.craftbukkit.command; + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.command.CommandSender; +import org.bukkit.permissions.PermissibleBase; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.plugin.Plugin; + +import java.util.Set; + +public abstract class ServerCommandSender implements CommandSender { + private static PermissibleBase blockPermInst; + private final PermissibleBase perm; + + public ServerCommandSender() { + if (this instanceof CraftBlockCommandSender) { + if (blockPermInst == null) { + blockPermInst = new PermissibleBase(this); + } + this.perm = blockPermInst; + } else { + this.perm = new PermissibleBase(this); + } + } + + public boolean isPermissionSet(String name) { + return perm.isPermissionSet(name); + } + + public boolean isPermissionSet(Permission perm) { + return this.perm.isPermissionSet(perm); + } + + public boolean hasPermission(String name) { + return perm.hasPermission(name); + } + + public boolean hasPermission(Permission perm) { + return this.perm.hasPermission(perm); + } + + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { + return perm.addAttachment(plugin, name, value); + } + + public PermissionAttachment addAttachment(Plugin plugin) { + return perm.addAttachment(plugin); + } + + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { + return perm.addAttachment(plugin, name, value, ticks); + } + + public PermissionAttachment addAttachment(Plugin plugin, int ticks) { + return perm.addAttachment(plugin, ticks); + } + + public void removeAttachment(PermissionAttachment attachment) { + perm.removeAttachment(attachment); + } + + public void recalculatePermissions() { + perm.recalculatePermissions(); + } + + public Set getEffectivePermissions() { + return perm.getEffectivePermissions(); + } + + public boolean isPlayer() { + return false; + } + + public Server getServer() { + return Bukkit.getServer(); + } + + // Spigot start + private final Spigot spigot = new Spigot() + { + @Override + public void sendMessage(net.md_5.bungee.api.chat.BaseComponent component) + { + ServerCommandSender.this.sendMessage(net.md_5.bungee.api.chat.TextComponent.toLegacyText(component)); + } + + @Override + public void sendMessage(net.md_5.bungee.api.chat.BaseComponent... components) + { + ServerCommandSender.this.sendMessage(net.md_5.bungee.api.chat.TextComponent.toLegacyText(components)); + } + }; + + @Override + public Spigot spigot() + { + return spigot; + } + // Spigot end +} diff --git a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java new file mode 100644 index 000000000000..59d82cf64a11 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java @@ -0,0 +1,96 @@ +package org.bukkit.craftbukkit.command; + +import com.google.common.base.Joiner; +import com.mojang.brigadier.ParseResults; +import com.mojang.brigadier.tree.CommandNode; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import net.minecraft.server.CommandDispatcher; +import net.minecraft.server.CommandListenerWrapper; +import net.minecraft.server.DedicatedServer; +import net.minecraft.server.EntityMinecartCommandBlock; +import net.minecraft.server.MinecraftServer; +import org.apache.commons.lang.Validate; +import org.bukkit.Location; +import org.bukkit.command.BlockCommandSender; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.command.ProxiedCommandSender; +import org.bukkit.command.RemoteConsoleCommandSender; +import org.bukkit.command.defaults.BukkitCommand; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.entity.CraftMinecartCommand; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.entity.minecart.CommandMinecart; + +public final class VanillaCommandWrapper extends BukkitCommand { + + private final CommandDispatcher dispatcher; + public final CommandNode vanillaCommand; + + public VanillaCommandWrapper(CommandDispatcher dispatcher, CommandNode vanillaCommand) { + super(vanillaCommand.getName(), "A Mojang provided command.", vanillaCommand.getUsageText(), Collections.EMPTY_LIST); + this.dispatcher = dispatcher; + this.vanillaCommand = vanillaCommand; + this.setPermission(getPermission(vanillaCommand)); + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + if (!testPermission(sender)) return true; + + CommandListenerWrapper icommandlistener = getListener(sender); + dispatcher.a(icommandlistener, toDispatcher(args, getName()), toDispatcher(args, commandLabel)); + return true; + } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { + Validate.notNull(sender, "Sender cannot be null"); + Validate.notNull(args, "Arguments cannot be null"); + Validate.notNull(alias, "Alias cannot be null"); + + CommandListenerWrapper icommandlistener = getListener(sender); + ParseResults parsed = dispatcher.a().parse(toDispatcher(args, getName()), icommandlistener); + + List results = new ArrayList<>(); + dispatcher.a().getCompletionSuggestions(parsed).thenAccept((suggestions) -> { + suggestions.getList().forEach((s) -> results.add(s.getText())); + }); + + return results; + } + + public static CommandListenerWrapper getListener(CommandSender sender) { + if (sender instanceof Player) { + return ((CraftPlayer) sender).getHandle().getCommandListener(); + } + if (sender instanceof BlockCommandSender) { + return ((CraftBlockCommandSender) sender).getWrapper(); + } + if (sender instanceof CommandMinecart) { + return ((EntityMinecartCommandBlock) ((CraftMinecartCommand) sender).getHandle()).getCommandBlock().getWrapper(); + } + if (sender instanceof RemoteConsoleCommandSender) { + return ((DedicatedServer) MinecraftServer.getServer()).remoteControlCommandListener.f(); + } + if (sender instanceof ConsoleCommandSender) { + return ((CraftServer) sender.getServer()).getServer().getServerCommandListener(); + } + if (sender instanceof ProxiedCommandSender) { + return ((ProxiedNativeCommandSender) sender).getHandle(); + } + + throw new IllegalArgumentException("Cannot make " + sender + " a vanilla command listener"); + } + + public static String getPermission(CommandNode vanillaCommand) { + return "minecraft.command." + ((vanillaCommand.getRedirect() == null) ? vanillaCommand.getName() : vanillaCommand.getRedirect().getName()); + } + + private String toDispatcher(String[] args, String name) { + return "/" + name + ((args.length > 0) ? " " + Joiner.on(' ').join(args) : ""); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/conversations/ConversationTracker.java b/src/main/java/org/bukkit/craftbukkit/conversations/ConversationTracker.java new file mode 100644 index 000000000000..eefa68a69560 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/conversations/ConversationTracker.java @@ -0,0 +1,77 @@ +package org.bukkit.craftbukkit.conversations; + +import java.util.LinkedList; +import java.util.logging.Level; + +import org.bukkit.Bukkit; +import org.bukkit.conversations.Conversation; +import org.bukkit.conversations.ConversationAbandonedEvent; +import org.bukkit.conversations.ManuallyAbandonedConversationCanceller; + +/** + */ +public class ConversationTracker { + + private LinkedList conversationQueue = new LinkedList(); + + public synchronized boolean beginConversation(Conversation conversation) { + if (!conversationQueue.contains(conversation)) { + conversationQueue.addLast(conversation); + if (conversationQueue.getFirst() == conversation) { + conversation.begin(); + conversation.outputNextPrompt(); + return true; + } + } + return true; + } + + public synchronized void abandonConversation(Conversation conversation, ConversationAbandonedEvent details) { + if (!conversationQueue.isEmpty()) { + if (conversationQueue.getFirst() == conversation) { + conversation.abandon(details); + } + if (conversationQueue.contains(conversation)) { + conversationQueue.remove(conversation); + } + if (!conversationQueue.isEmpty()) { + conversationQueue.getFirst().outputNextPrompt(); + } + } + } + + public synchronized void abandonAllConversations() { + + LinkedList oldQueue = conversationQueue; + conversationQueue = new LinkedList(); + for (Conversation conversation : oldQueue) { + try { + conversation.abandon(new ConversationAbandonedEvent(conversation, new ManuallyAbandonedConversationCanceller())); + } catch (Throwable t) { + Bukkit.getLogger().log(Level.SEVERE, "Unexpected exception while abandoning a conversation", t); + } + } + } + + public synchronized void acceptConversationInput(String input) { + if (isConversing()) { + Conversation conversation = conversationQueue.getFirst(); + try { + conversation.acceptInput(input); + } catch (Throwable t) { + conversation.getContext().getPlugin().getLogger().log(Level.WARNING, + String.format("Plugin %s generated an exception whilst handling conversation input", + conversation.getContext().getPlugin().getDescription().getFullName() + ), t); + } + } + } + + public synchronized boolean isConversing() { + return !conversationQueue.isEmpty(); + } + + public synchronized boolean isConversingModaly() { + return isConversing() && conversationQueue.getFirst().isModal(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java b/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java new file mode 100644 index 000000000000..2897713adde2 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java @@ -0,0 +1,184 @@ +package org.bukkit.craftbukkit.enchantments; + +import net.minecraft.server.EnchantmentBinding; +import net.minecraft.server.EnchantmentVanishing; +import net.minecraft.server.IRegistry; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.enchantments.EnchantmentWrapper; +import org.bukkit.inventory.ItemStack; + +public class CraftEnchantment extends Enchantment { + private final net.minecraft.server.Enchantment target; + + public CraftEnchantment(net.minecraft.server.Enchantment target) { + super(CraftNamespacedKey.fromMinecraft(IRegistry.ENCHANTMENT.getKey(target))); + this.target = target; + } + + @Override + public int getMaxLevel() { + return target.getMaxLevel(); + } + + @Override + public int getStartLevel() { + return target.getStartLevel(); + } + + @Override + public EnchantmentTarget getItemTarget() { + switch (target.itemTarget) { + case ALL: + return EnchantmentTarget.ALL; + case ARMOR: + return EnchantmentTarget.ARMOR; + case ARMOR_FEET: + return EnchantmentTarget.ARMOR_FEET; + case ARMOR_HEAD: + return EnchantmentTarget.ARMOR_HEAD; + case ARMOR_LEGS: + return EnchantmentTarget.ARMOR_LEGS; + case ARMOR_CHEST: + return EnchantmentTarget.ARMOR_TORSO; + case DIGGER: + return EnchantmentTarget.TOOL; + case WEAPON: + return EnchantmentTarget.WEAPON; + case BOW: + return EnchantmentTarget.BOW; + case FISHING_ROD: + return EnchantmentTarget.FISHING_ROD; + case BREAKABLE: + return EnchantmentTarget.BREAKABLE; + case WEARABLE: + return EnchantmentTarget.WEARABLE; + case TRIDENT: + return EnchantmentTarget.TRIDENT; + default: + return null; + } + } + + @Override + public boolean isTreasure() { + return target.isTreasure(); + } + + @Override + public boolean isCursed() { + return target instanceof EnchantmentBinding || target instanceof EnchantmentVanishing; + } + + @Override + public boolean canEnchantItem(ItemStack item) { + return target.canEnchant(CraftItemStack.asNMSCopy(item)); + } + + @Override + public String getName() { + // PAIL: migration paths + switch (IRegistry.ENCHANTMENT.a(target)) { + case 0: + return "PROTECTION_ENVIRONMENTAL"; + case 1: + return "PROTECTION_FIRE"; + case 2: + return "PROTECTION_FALL"; + case 3: + return "PROTECTION_EXPLOSIONS"; + case 4: + return "PROTECTION_PROJECTILE"; + case 5: + return "OXYGEN"; + case 6: + return "WATER_WORKER"; + case 7: + return "THORNS"; + case 8: + return "DEPTH_STRIDER"; + case 9: + return "FROST_WALKER"; + case 10: + return "BINDING_CURSE"; + case 11: + return "DAMAGE_ALL"; + case 12: + return "DAMAGE_UNDEAD"; + case 13: + return "DAMAGE_ARTHROPODS"; + case 14: + return "KNOCKBACK"; + case 15: + return "FIRE_ASPECT"; + case 16: + return "LOOT_BONUS_MOBS"; + case 17: + return "SWEEPING_EDGE"; + case 18: + return "DIG_SPEED"; + case 19: + return "SILK_TOUCH"; + case 20: + return "DURABILITY"; + case 21: + return "LOOT_BONUS_BLOCKS"; + case 22: + return "ARROW_DAMAGE"; + case 23: + return "ARROW_KNOCKBACK"; + case 24: + return "ARROW_FIRE"; + case 25: + return "ARROW_INFINITE"; + case 26: + return "LUCK"; + case 27: + return "LURE"; + case 28: + return "LOYALTY"; + case 29: + return "IMPALING"; + case 30: + return "RIPTIDE"; + case 31: + return "CHANNELING"; + case 32: + return "MENDING"; + case 33: + return "VANISHING_CURSE"; + default: + return "UNKNOWN_ENCHANT_" + getName(); + } + } + + public static net.minecraft.server.Enchantment getRaw(Enchantment enchantment) { + if (enchantment instanceof EnchantmentWrapper) { + enchantment = ((EnchantmentWrapper) enchantment).getEnchantment(); + } + + if (enchantment instanceof CraftEnchantment) { + return ((CraftEnchantment) enchantment).target; + } + + return null; + } + + @Override + public boolean conflictsWith(Enchantment other) { + if (other instanceof EnchantmentWrapper) { + other = ((EnchantmentWrapper) other).getEnchantment(); + } + if (!(other instanceof CraftEnchantment)) { + return false; + } + CraftEnchantment ench = (CraftEnchantment) other; + return !target.b(ench.target); + } + + public net.minecraft.server.Enchantment getHandle() { + return target; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java b/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java new file mode 100644 index 000000000000..7c1625519b4f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.entity; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Projectile; + +public abstract class AbstractProjectile extends CraftEntity implements Projectile { + + private boolean doesBounce; + + public AbstractProjectile(CraftServer server, net.minecraft.server.Entity entity) { + super(server, entity); + doesBounce = false; + } + + public boolean doesBounce() { + return doesBounce; + } + + public void setBounce(boolean doesBounce) { + this.doesBounce = doesBounce; + } + +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractHorse.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractHorse.java new file mode 100644 index 000000000000..cc9d432e7f91 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractHorse.java @@ -0,0 +1,101 @@ +package org.bukkit.craftbukkit.entity; + +import java.util.UUID; +import net.minecraft.server.EntityHorse; +import net.minecraft.server.EntityHorseAbstract; +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.inventory.CraftInventoryAbstractHorse; +import org.bukkit.craftbukkit.inventory.CraftSaddledInventory; +import org.bukkit.entity.AbstractHorse; +import org.bukkit.entity.AnimalTamer; +import org.bukkit.entity.Horse; +import org.bukkit.inventory.AbstractHorseInventory; + +public abstract class CraftAbstractHorse extends CraftAnimals implements AbstractHorse { + + public CraftAbstractHorse(CraftServer server, EntityHorseAbstract entity) { + super(server, entity); + } + + @Override + public EntityHorseAbstract getHandle() { + return (EntityHorseAbstract) entity; + } + + public void setVariant(Horse.Variant variant) { + throw new UnsupportedOperationException("Not supported."); + } + + public int getDomestication() { + return getHandle().getTemper(); + } + + public void setDomestication(int value) { + Validate.isTrue(value >= 0, "Domestication cannot be less than zero"); + Validate.isTrue(value <= getMaxDomestication(), "Domestication cannot be greater than the max domestication"); + getHandle().setTemper(value); + } + + public int getMaxDomestication() { + return getHandle().getMaxDomestication(); + } + + public void setMaxDomestication(int value) { + Validate.isTrue(value > 0, "Max domestication cannot be zero or less"); + getHandle().maxDomestication = value; + } + + public double getJumpStrength() { + return getHandle().getJumpStrength(); + } + + public void setJumpStrength(double strength) { + Validate.isTrue(strength >= 0, "Jump strength cannot be less than zero"); + getHandle().getAttributeInstance(EntityHorse.attributeJumpStrength).setValue(strength); + } + + @Override + public boolean isTamed() { + return getHandle().isTamed(); + } + + @Override + public void setTamed(boolean tamed) { + getHandle().setTamed(tamed); + } + + @Override + public AnimalTamer getOwner() { + if (getOwnerUUID() == null) return null; + return getServer().getOfflinePlayer(getOwnerUUID()); + } + + @Override + public void setOwner(AnimalTamer owner) { + if (owner != null) { + setTamed(true); + getHandle().setGoalTarget(null, null, false); + setOwnerUUID(owner.getUniqueId()); + } else { + setTamed(false); + setOwnerUUID(null); + } + } + + public UUID getOwnerUniqueId() { + return getOwnerUUID(); + } + public UUID getOwnerUUID() { + return getHandle().getOwnerUUID(); + } + + public void setOwnerUUID(UUID uuid) { + getHandle().setOwnerUUID(uuid); + } + + @Override + public AbstractHorseInventory getInventory() { + return new CraftSaddledInventory(getHandle().inventoryChest); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftAgeable.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftAgeable.java new file mode 100644 index 000000000000..edb07964ac36 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftAgeable.java @@ -0,0 +1,67 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityAgeable; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Ageable; + +public class CraftAgeable extends CraftCreature implements Ageable { + public CraftAgeable(CraftServer server, EntityAgeable entity) { + super(server, entity); + } + + public int getAge() { + return getHandle().getAge(); + } + + public void setAge(int age) { + getHandle().setAgeRaw(age); + } + + public void setAgeLock(boolean lock) { + getHandle().ageLocked = lock; + } + + public boolean getAgeLock() { + return getHandle().ageLocked; + } + + public void setBaby() { + if (isAdult()) { + setAge(-24000); + } + } + + public void setAdult() { + if (!isAdult()) { + setAge(0); + } + } + + public boolean isAdult() { + return getAge() >= 0; + } + + + public boolean canBreed() { + return getAge() == 0; + } + + public void setBreed(boolean breed) { + if (breed) { + setAge(0); + } else if (isAdult()) { + setAge(6000); + } + } + + @Override + public EntityAgeable getHandle() { + return (EntityAgeable) entity; + } + + @Override + public String toString() { + return "CraftAgeable"; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftAmbient.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftAmbient.java new file mode 100644 index 000000000000..734f54891afc --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftAmbient.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityAmbient; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Ambient; +import org.bukkit.entity.EntityType; + +public class CraftAmbient extends CraftMob implements Ambient { + public CraftAmbient(CraftServer server, EntityAmbient entity) { + super(server, entity); + } + + @Override + public EntityAmbient getHandle() { + return (EntityAmbient) entity; + } + + @Override + public String toString() { + return "CraftAmbient"; + } + + public EntityType getType() { + return EntityType.UNKNOWN; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftAnimals.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftAnimals.java new file mode 100644 index 000000000000..b06909c3e908 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftAnimals.java @@ -0,0 +1,50 @@ +package org.bukkit.craftbukkit.entity; + +import java.util.UUID; +import com.google.common.base.Preconditions; +import net.minecraft.server.EntityAnimal; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Animals; + +public class CraftAnimals extends CraftAgeable implements Animals { + + public CraftAnimals(CraftServer server, EntityAnimal entity) { + super(server, entity); + } + + @Override + public EntityAnimal getHandle() { + return (EntityAnimal) entity; + } + + @Override + public String toString() { + return "CraftAnimals"; + } + + @Override + public UUID getBreedCause() { + return getHandle().breedCause; + } + + @Override + public void setBreedCause(UUID uuid) { + getHandle().breedCause = uuid; + } + + @Override + public boolean isLoveMode() { + return getHandle().isInLove(); + } + + @Override + public void setLoveModeTicks(int ticks) { + Preconditions.checkArgument(ticks >= 0, "Love mode ticks must be positive or 0"); + getHandle().d(ticks); // PAIL rename setLoveModeTicks + } + + @Override + public int getLoveModeTicks() { + return getHandle().bC; // PAIL rename loveTicks + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java new file mode 100644 index 000000000000..7e9239cf120e --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java @@ -0,0 +1,231 @@ +package org.bukkit.craftbukkit.entity; + +import java.util.List; +import net.minecraft.server.EntityAreaEffectCloud; +import net.minecraft.server.EntityLiving; +import net.minecraft.server.MobEffect; +import net.minecraft.server.MobEffectList; + +import org.apache.commons.lang.Validate; +import org.bukkit.Color; +import org.bukkit.Particle; +import org.bukkit.craftbukkit.CraftParticle; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.potion.CraftPotionUtil; +import org.bukkit.entity.AreaEffectCloud; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.projectiles.ProjectileSource; +import org.bukkit.potion.PotionData; + +import com.google.common.collect.ImmutableList; + +public class CraftAreaEffectCloud extends CraftEntity implements AreaEffectCloud { + + public CraftAreaEffectCloud(CraftServer server, EntityAreaEffectCloud entity) { + super(server, entity); + } + + @Override + public EntityAreaEffectCloud getHandle() { + return (EntityAreaEffectCloud) super.getHandle(); + } + + @Override + public String toString() { + return "CraftAreaEffectCloud"; + } + + @Override + public EntityType getType() { + return EntityType.AREA_EFFECT_CLOUD; + } + + @Override + public int getDuration() { + return getHandle().getDuration(); + } + + @Override + public void setDuration(int duration) { + getHandle().setDuration(duration); + } + + @Override + public int getWaitTime() { + return getHandle().waitTime; + } + + @Override + public void setWaitTime(int waitTime) { + getHandle().setWaitTime(waitTime); + } + + @Override + public int getReapplicationDelay() { + return getHandle().reapplicationDelay; + } + + @Override + public void setReapplicationDelay(int delay) { + getHandle().reapplicationDelay = delay; + } + + @Override + public int getDurationOnUse() { + return getHandle().durationOnUse; + } + + @Override + public void setDurationOnUse(int duration) { + getHandle().durationOnUse = duration; + } + + @Override + public float getRadius() { + return getHandle().getRadius(); + } + + @Override + public void setRadius(float radius) { + getHandle().setRadius(radius); + } + + @Override + public float getRadiusOnUse() { + return getHandle().radiusOnUse; + } + + @Override + public void setRadiusOnUse(float radius) { + getHandle().setRadiusOnUse(radius); + } + + @Override + public float getRadiusPerTick() { + return getHandle().radiusPerTick; + } + + @Override + public void setRadiusPerTick(float radius) { + getHandle().setRadiusPerTick(radius); + } + + @Override + public Particle getParticle() { + return CraftParticle.toBukkit(getHandle().getParticle()); + } + + @Override + public void setParticle(Particle particle) { + setParticle(particle, null); + } + + @Override + public void setParticle(Particle particle, T data) { + getHandle().setParticle(CraftParticle.toNMS(particle, data)); + } + + @Override + public Color getColor() { + return Color.fromRGB(getHandle().getColor()); + } + + @Override + public void setColor(Color color) { + getHandle().setColor(color.asRGB()); + } + + @Override + public boolean addCustomEffect(PotionEffect effect, boolean override) { + int effectId = effect.getType().getId(); + MobEffect existing = null; + for (MobEffect mobEffect : getHandle().effects) { + if (MobEffectList.getId(mobEffect.getMobEffect()) == effectId) { + existing = mobEffect; + } + } + if (existing != null) { + if (!override) { + return false; + } + getHandle().effects.remove(existing); + } + getHandle().a(CraftPotionUtil.fromBukkit(effect)); + getHandle().refreshEffects(); + return true; + } + + @Override + public void clearCustomEffects() { + getHandle().effects.clear(); + getHandle().refreshEffects(); + } + + @Override + public List getCustomEffects() { + ImmutableList.Builder builder = ImmutableList.builder(); + for (MobEffect effect : getHandle().effects) { + builder.add(CraftPotionUtil.toBukkit(effect)); + } + return builder.build(); + } + + @Override + public boolean hasCustomEffect(PotionEffectType type) { + for (MobEffect effect : getHandle().effects) { + if (CraftPotionUtil.equals(effect.getMobEffect(), type)) { + return true; + } + } + return false; + } + + @Override + public boolean hasCustomEffects() { + return !getHandle().effects.isEmpty(); + } + + @Override + public boolean removeCustomEffect(PotionEffectType effect) { + int effectId = effect.getId(); + MobEffect existing = null; + for (MobEffect mobEffect : getHandle().effects) { + if (MobEffectList.getId(mobEffect.getMobEffect()) == effectId) { + existing = mobEffect; + } + } + if (existing == null) { + return false; + } + getHandle().effects.remove(existing); + getHandle().refreshEffects(); + return true; + } + + @Override + public void setBasePotionData(PotionData data) { + Validate.notNull(data, "PotionData cannot be null"); + getHandle().setType(CraftPotionUtil.fromBukkit(data)); + } + + @Override + public PotionData getBasePotionData() { + return CraftPotionUtil.toBukkit(getHandle().getType()); + } + + public ProjectileSource getSource() { + EntityLiving source = getHandle().getSource(); + return (source == null) ? null : (LivingEntity) source.getBukkitEntity(); + } + + public void setSource(ProjectileSource shooter) { + if (shooter instanceof CraftLivingEntity) { + getHandle().setSource((EntityLiving) ((CraftLivingEntity) shooter).getHandle()); + } else { + getHandle().setSource((EntityLiving) null); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftArmorStand.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftArmorStand.java new file mode 100644 index 000000000000..07ce93f17ce0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftArmorStand.java @@ -0,0 +1,311 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityArmorStand; +import net.minecraft.server.Vector3f; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.EulerAngle; + +public class CraftArmorStand extends CraftLivingEntity implements ArmorStand { + + public CraftArmorStand(CraftServer server, EntityArmorStand entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftArmorStand"; + } + + @Override + public EntityType getType() { + return EntityType.ARMOR_STAND; + } + + @Override + public EntityArmorStand getHandle() { + return (EntityArmorStand) super.getHandle(); + } + + @Override + @Deprecated // Paper + public ItemStack getItemInHand() { + return getEquipment().getItemInHand(); + } + + @Override + @Deprecated // Paper + public void setItemInHand(ItemStack item) { + getEquipment().setItemInHand(item); + } + + @Override + public ItemStack getBoots() { + return getEquipment().getBoots(); + } + + @Override + public void setBoots(ItemStack item) { + getEquipment().setBoots(item); + } + + @Override + public ItemStack getLeggings() { + return getEquipment().getLeggings(); + } + + @Override + public void setLeggings(ItemStack item) { + getEquipment().setLeggings(item); + } + + @Override + public ItemStack getChestplate() { + return getEquipment().getChestplate(); + } + + @Override + public void setChestplate(ItemStack item) { + getEquipment().setChestplate(item); + } + + @Override + public ItemStack getHelmet() { + return getEquipment().getHelmet(); + } + + @Override + public void setHelmet(ItemStack item) { + getEquipment().setHelmet(item); + } + + @Override + public EulerAngle getBodyPose() { + return fromNMS(getHandle().bodyPose); + } + + @Override + public void setBodyPose(EulerAngle pose) { + getHandle().setBodyPose(toNMS(pose)); + } + + @Override + public EulerAngle getLeftArmPose() { + return fromNMS(getHandle().leftArmPose); + } + + @Override + public void setLeftArmPose(EulerAngle pose) { + getHandle().setLeftArmPose(toNMS(pose)); + } + + @Override + public EulerAngle getRightArmPose() { + return fromNMS(getHandle().rightArmPose); + } + + @Override + public void setRightArmPose(EulerAngle pose) { + getHandle().setRightArmPose(toNMS(pose)); + } + + @Override + public EulerAngle getLeftLegPose() { + return fromNMS(getHandle().leftLegPose); + } + + @Override + public void setLeftLegPose(EulerAngle pose) { + getHandle().setLeftLegPose(toNMS(pose)); + } + + @Override + public EulerAngle getRightLegPose() { + return fromNMS(getHandle().rightLegPose); + } + + @Override + public void setRightLegPose(EulerAngle pose) { + getHandle().setRightLegPose(toNMS(pose)); + } + + @Override + public EulerAngle getHeadPose() { + return fromNMS(getHandle().headPose); + } + + @Override + public void setHeadPose(EulerAngle pose) { + getHandle().setHeadPose(toNMS(pose)); + } + + @Override + public boolean hasBasePlate() { + return !getHandle().hasBasePlate(); + } + + @Override + public void setBasePlate(boolean basePlate) { + getHandle().setBasePlate(!basePlate); + } + + @Override + public void setGravity(boolean gravity) { + super.setGravity(gravity); + // Armor stands are special + getHandle().noclip = !gravity; + } + + @Override + public boolean isVisible() { + return !getHandle().isInvisible(); + } + + @Override + public void setVisible(boolean visible) { + getHandle().setInvisible(!visible); + } + + @Override + public boolean hasArms() { + return getHandle().hasArms(); + } + + @Override + public void setArms(boolean arms) { + getHandle().setArms(arms); + } + + @Override + public boolean isSmall() { + return getHandle().isSmall(); + } + + @Override + public void setSmall(boolean small) { + getHandle().setSmall(small); + } + + private static EulerAngle fromNMS(Vector3f old) { + return new EulerAngle( + Math.toRadians(old.getX()), + Math.toRadians(old.getY()), + Math.toRadians(old.getZ()) + ); + } + + private static Vector3f toNMS(EulerAngle old) { + return new Vector3f( + (float) Math.toDegrees(old.getX()), + (float) Math.toDegrees(old.getY()), + (float) Math.toDegrees(old.getZ()) + ); + } + + @Override + public boolean isMarker() { + return getHandle().isMarker(); + } + + @Override + public void setMarker(boolean marker) { + getHandle().setMarker(marker); + } + + // Paper start + @Override + public boolean canMove() { + return getHandle().canMove; + } + + @Override + public void setCanMove(boolean move) { + getHandle().canMove = move; + } + + @Override + public ItemStack getItem(org.bukkit.inventory.EquipmentSlot slot) { + com.google.common.base.Preconditions.checkNotNull(slot, "slot"); + return getHandle().getEquipment(org.bukkit.craftbukkit.CraftEquipmentSlot.getNMS(slot)).asBukkitMirror(); + } + + @Override + public void setItem(org.bukkit.inventory.EquipmentSlot slot, ItemStack item) { + com.google.common.base.Preconditions.checkNotNull(slot, "slot"); + switch (slot) { + case HAND: + getEquipment().setItemInMainHand(item); + return; + case OFF_HAND: + getEquipment().setItemInOffHand(item); + return; + case FEET: + setBoots(item); + return; + case LEGS: + setLeggings(item); + break; + case CHEST: + setChestplate(item); + return; + case HEAD: + setHelmet(item); + return; + } + throw new UnsupportedOperationException(slot.name()); + } + + @Override + public java.util.Set getDisabledSlots() { + java.util.Set disabled = new java.util.HashSet<>(); + for (org.bukkit.inventory.EquipmentSlot slot : org.bukkit.inventory.EquipmentSlot.values()) { + if (this.isSlotDisabled(slot)) { + disabled.add(slot); + } + } + return disabled; + } + + @Override + public void setDisabledSlots(org.bukkit.inventory.EquipmentSlot... slots) { + int disabled = 0; + for (org.bukkit.inventory.EquipmentSlot slot : slots) { + if (slot == org.bukkit.inventory.EquipmentSlot.OFF_HAND) continue; + net.minecraft.server.EnumItemSlot nmsSlot = org.bukkit.craftbukkit.CraftEquipmentSlot.getNMS(slot); + disabled += (1 << nmsSlot.c()) + (1 << (nmsSlot.c() + 8)) + (1 << (nmsSlot.c() + 16)); + } + getHandle().setDisabledSlots(disabled); + } + + @Override + public void addDisabledSlots(org.bukkit.inventory.EquipmentSlot... slots) { + java.util.Set disabled = getDisabledSlots(); + java.util.Collections.addAll(disabled, slots); + setDisabledSlots(disabled.toArray(new org.bukkit.inventory.EquipmentSlot[0])); + } + + @Override + public void removeDisabledSlots(org.bukkit.inventory.EquipmentSlot... slots) { + java.util.Set disabled = getDisabledSlots(); + for (final org.bukkit.inventory.EquipmentSlot slot : slots) disabled.remove(slot); + setDisabledSlots(disabled.toArray(new org.bukkit.inventory.EquipmentSlot[0])); + } + + @Override + public boolean isSlotDisabled(org.bukkit.inventory.EquipmentSlot slot) { + return getHandle().isSlotDisabled(org.bukkit.craftbukkit.CraftEquipmentSlot.getNMS(slot)); + } + + @Override + public boolean canTick() { + return this.getHandle().canTick; + } + + @Override + public void setCanTick(final boolean tick) { + this.getHandle().canTick = tick; + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java new file mode 100644 index 000000000000..ff92f8584d41 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java @@ -0,0 +1,130 @@ +package org.bukkit.craftbukkit.entity; + +import com.google.common.base.Preconditions; +import net.minecraft.server.EntityArrow; + +import org.apache.commons.lang.Validate; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.projectiles.ProjectileSource; + +public class CraftArrow extends AbstractProjectile implements Arrow { + + public CraftArrow(CraftServer server, EntityArrow entity) { + super(server, entity); + } + + public void setKnockbackStrength(int knockbackStrength) { + Validate.isTrue(knockbackStrength >= 0, "Knockback cannot be negative"); + getHandle().setKnockbackStrength(knockbackStrength); + } + + public int getKnockbackStrength() { + return getHandle().knockbackStrength; + } + + @Override + public double getDamage() { + return getHandle().getDamage(); + } + + @Override + public void setDamage(double damage) { + Preconditions.checkArgument(damage >= 0, "Damage must be positive"); + getHandle().setDamage(damage); + } + + public boolean isCritical() { + return getHandle().isCritical(); + } + + public void setCritical(boolean critical) { + getHandle().setCritical(critical); + } + + public ProjectileSource getShooter() { + return getHandle().projectileSource; + } + + public void setShooter(ProjectileSource shooter) { + if (shooter instanceof Entity) { + getHandle().setShooter(((CraftEntity) shooter).getHandle()); + } else { + getHandle().setShooter(null); + } + getHandle().projectileSource = shooter; + } + + @Override + public boolean isInBlock() { + return getHandle().inGround; + } + + @Override + public Block getAttachedBlock() { + if (!isInBlock()) { + return null; + } + + EntityArrow handle = getHandle(); + return getWorld().getBlockAt(handle.tileX, handle.tileY, handle.tileZ); + } + + @Override + public PickupStatus getPickupStatus() { + return PickupStatus.values()[getHandle().fromPlayer.ordinal()]; + } + + @Override + public void setPickupStatus(PickupStatus status) { + Preconditions.checkNotNull(status, "status"); + getHandle().fromPlayer = EntityArrow.PickupStatus.a(status.ordinal()); + } + + @Override + public void setTicksLived(int value) { + super.setTicksLived(value); + + // Second field for EntityArrow + getHandle().despawnCounter = value; + } + + @Override + public EntityArrow getHandle() { + return (EntityArrow) entity; + } + + @Override + public String toString() { + return "CraftArrow"; + } + + public EntityType getType() { + return EntityType.ARROW; + } + + // Spigot start + private final Arrow.Spigot spigot = new Arrow.Spigot() + { + @Override + public double getDamage() + { + return getHandle().getDamage(); + } + + @Override + public void setDamage(double damage) + { + getHandle().setDamage( damage ); + } + }; + + public Arrow.Spigot spigot() + { + return spigot; + } + // Spigot end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java new file mode 100644 index 000000000000..76ada1c379cd --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java @@ -0,0 +1,36 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityBat; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Bat; +import org.bukkit.entity.EntityType; + +public class CraftBat extends CraftAmbient implements Bat { + public CraftBat(CraftServer server, EntityBat entity) { + super(server, entity); + } + + @Override + public EntityBat getHandle() { + return (EntityBat) entity; + } + + @Override + public String toString() { + return "CraftBat"; + } + + public EntityType getType() { + return EntityType.BAT; + } + + @Override + public boolean isAwake() { + return !getHandle().isAsleep(); + } + + @Override + public void setAwake(boolean state) { + getHandle().setAsleep(!state); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftBlaze.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftBlaze.java new file mode 100644 index 000000000000..830d7a8406bc --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftBlaze.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityBlaze; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Blaze; +import org.bukkit.entity.EntityType; + +public class CraftBlaze extends CraftMonster implements Blaze { + public CraftBlaze(CraftServer server, EntityBlaze entity) { + super(server, entity); + } + + @Override + public EntityBlaze getHandle() { + return (EntityBlaze) entity; + } + + @Override + public String toString() { + return "CraftBlaze"; + } + + public EntityType getType() { + return EntityType.BLAZE; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftBoat.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftBoat.java new file mode 100644 index 000000000000..3613f5b3e069 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftBoat.java @@ -0,0 +1,110 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityBoat; +import org.bukkit.TreeSpecies; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Boat; +import org.bukkit.entity.EntityType; + +public class CraftBoat extends CraftVehicle implements Boat { + + public CraftBoat(CraftServer server, EntityBoat entity) { + super(server, entity); + } + + @Override + public TreeSpecies getWoodType() { + return getTreeSpecies(getHandle().getType()); + } + + @Override + public void setWoodType(TreeSpecies species) { + getHandle().setType(getBoatType(species)); + } + + public double getMaxSpeed() { + return getHandle().maxSpeed; + } + + public void setMaxSpeed(double speed) { + if (speed >= 0D) { + getHandle().maxSpeed = speed; + } + } + + public double getOccupiedDeceleration() { + return getHandle().occupiedDeceleration; + } + + public void setOccupiedDeceleration(double speed) { + if (speed >= 0D) { + getHandle().occupiedDeceleration = speed; + } + } + + public double getUnoccupiedDeceleration() { + return getHandle().unoccupiedDeceleration; + } + + public void setUnoccupiedDeceleration(double speed) { + getHandle().unoccupiedDeceleration = speed; + } + + public boolean getWorkOnLand() { + return getHandle().landBoats; + } + + public void setWorkOnLand(boolean workOnLand) { + getHandle().landBoats = workOnLand; + } + + @Override + public EntityBoat getHandle() { + return (EntityBoat) entity; + } + + @Override + public String toString() { + return "CraftBoat"; + } + + public EntityType getType() { + return EntityType.BOAT; + } + + public static TreeSpecies getTreeSpecies(EntityBoat.EnumBoatType boatType) { + switch (boatType) { + case SPRUCE: + return TreeSpecies.REDWOOD; + case BIRCH: + return TreeSpecies.BIRCH; + case JUNGLE: + return TreeSpecies.JUNGLE; + case ACACIA: + return TreeSpecies.ACACIA; + case DARK_OAK: + return TreeSpecies.DARK_OAK; + case OAK: + default: + return TreeSpecies.GENERIC; + } + } + + public static EntityBoat.EnumBoatType getBoatType(TreeSpecies species) { + switch (species) { + case REDWOOD: + return EntityBoat.EnumBoatType.SPRUCE; + case BIRCH: + return EntityBoat.EnumBoatType.BIRCH; + case JUNGLE: + return EntityBoat.EnumBoatType.JUNGLE; + case ACACIA: + return EntityBoat.EnumBoatType.ACACIA; + case DARK_OAK: + return EntityBoat.EnumBoatType.DARK_OAK; + case GENERIC: + default: + return EntityBoat.EnumBoatType.OAK; + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftCaveSpider.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftCaveSpider.java new file mode 100644 index 000000000000..0648a851d2cd --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftCaveSpider.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityCaveSpider; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.CaveSpider; +import org.bukkit.entity.EntityType; + +public class CraftCaveSpider extends CraftSpider implements CaveSpider { + public CraftCaveSpider(CraftServer server, EntityCaveSpider entity) { + super(server, entity); + } + + @Override + public EntityCaveSpider getHandle() { + return (EntityCaveSpider) entity; + } + + @Override + public String toString() { + return "CraftCaveSpider"; + } + + public EntityType getType() { + return EntityType.CAVE_SPIDER; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftChestedHorse.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftChestedHorse.java new file mode 100644 index 000000000000..39620e06e341 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftChestedHorse.java @@ -0,0 +1,29 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityHorseChestedAbstract; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.ChestedHorse; + +public abstract class CraftChestedHorse extends CraftAbstractHorse implements ChestedHorse { + + public CraftChestedHorse(CraftServer server, EntityHorseChestedAbstract entity) { + super(server, entity); + } + + @Override + public EntityHorseChestedAbstract getHandle() { + return (EntityHorseChestedAbstract) super.getHandle(); + } + + @Override + public boolean isCarryingChest() { + return getHandle().isCarryingChest(); + } + + @Override + public void setCarryingChest(boolean chest) { + if (chest == isCarryingChest()) return; + getHandle().setCarryingChest(chest); + getHandle().loadChest(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftChicken.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftChicken.java new file mode 100644 index 000000000000..d20c2196cee4 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftChicken.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityChicken; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Chicken; +import org.bukkit.entity.EntityType; + +public class CraftChicken extends CraftAnimals implements Chicken { + + public CraftChicken(CraftServer server, EntityChicken entity) { + super(server, entity); + } + + @Override + public EntityChicken getHandle() { + return (EntityChicken) entity; + } + + @Override + public String toString() { + return "CraftChicken"; + } + + public EntityType getType() { + return EntityType.CHICKEN; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftCod.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftCod.java new file mode 100644 index 000000000000..9cda680c0b58 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftCod.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityCod; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Cod; +import org.bukkit.entity.EntityType; + +public class CraftCod extends CraftFish implements Cod { + + public CraftCod(CraftServer server, EntityCod entity) { + super(server, entity); + } + + @Override + public EntityCod getHandle() { + return (EntityCod) super.getHandle(); + } + + @Override + public String toString() { + return "CraftCod"; + } + + @Override + public EntityType getType() { + return EntityType.COD; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftComplexLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftComplexLivingEntity.java new file mode 100644 index 000000000000..4947249da2d5 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftComplexLivingEntity.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityInsentient; +import net.minecraft.server.EntityLiving; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.ComplexLivingEntity; + +public abstract class CraftComplexLivingEntity extends CraftMob implements ComplexLivingEntity { // Paper + public CraftComplexLivingEntity(CraftServer server, EntityInsentient entity) { // Paper + super(server, entity); + } + + @Override + public EntityInsentient getHandle() { // Paper + return (EntityInsentient) entity; // Paper + } + + @Override + public String toString() { + return "CraftComplexLivingEntity"; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftComplexPart.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftComplexPart.java new file mode 100644 index 000000000000..02fd230a34c4 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftComplexPart.java @@ -0,0 +1,48 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityComplexPart; +import net.minecraft.server.EntityEnderDragon; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.ComplexEntityPart; +import org.bukkit.entity.ComplexLivingEntity; +import org.bukkit.entity.EntityType; +import org.bukkit.event.entity.EntityDamageEvent; + +public class CraftComplexPart extends CraftEntity implements ComplexEntityPart { + public CraftComplexPart(CraftServer server, EntityComplexPart entity) { + super(server, entity); + } + + public ComplexLivingEntity getParent() { + return (ComplexLivingEntity) ((EntityEnderDragon) getHandle().owner).getBukkitEntity(); + } + + @Override + public void setLastDamageCause(EntityDamageEvent cause) { + getParent().setLastDamageCause(cause); + } + + @Override + public EntityDamageEvent getLastDamageCause() { + return getParent().getLastDamageCause(); + } + + @Override + public boolean isValid() { + return getParent().isValid(); + } + + @Override + public EntityComplexPart getHandle() { + return (EntityComplexPart) entity; + } + + @Override + public String toString() { + return "CraftComplexPart"; + } + + public EntityType getType() { + return EntityType.COMPLEX_PART; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftCow.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftCow.java new file mode 100644 index 000000000000..fc48ebd9b26a --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftCow.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityCow; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Cow; +import org.bukkit.entity.EntityType; + +public class CraftCow extends CraftAnimals implements Cow { + + public CraftCow(CraftServer server, EntityCow entity) { + super(server, entity); + } + + @Override + public EntityCow getHandle() { + return (EntityCow) entity; + } + + @Override + public String toString() { + return "CraftCow"; + } + + public EntityType getType() { + return EntityType.COW; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftCreature.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftCreature.java new file mode 100644 index 000000000000..73db6101bc68 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftCreature.java @@ -0,0 +1,21 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityCreature; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Creature; + +public class CraftCreature extends CraftMob implements Creature { + public CraftCreature(CraftServer server, EntityCreature entity) { + super(server, entity); + } + + @Override + public EntityCreature getHandle() { + return (EntityCreature) entity; + } + + @Override + public String toString() { + return "CraftCreature"; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftCreeper.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftCreeper.java new file mode 100644 index 000000000000..ab2b20a0d446 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftCreeper.java @@ -0,0 +1,97 @@ +package org.bukkit.craftbukkit.entity; + +import com.google.common.base.Preconditions; +import net.minecraft.server.EntityCreeper; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.EntityType; +import org.bukkit.event.entity.CreeperPowerEvent; + +public class CraftCreeper extends CraftMonster implements Creeper { + + public CraftCreeper(CraftServer server, EntityCreeper entity) { + super(server, entity); + } + + public boolean isPowered() { + return getHandle().isPowered(); + } + + public void setPowered(boolean powered) { + CraftServer server = this.server; + Creeper entity = (Creeper) this.getHandle().getBukkitEntity(); + + if (powered) { + CreeperPowerEvent event = new CreeperPowerEvent(entity, CreeperPowerEvent.PowerCause.SET_ON); + server.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + getHandle().setPowered(true); + } + } else { + CreeperPowerEvent event = new CreeperPowerEvent(entity, CreeperPowerEvent.PowerCause.SET_OFF); + server.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + getHandle().setPowered(false); + } + } + } + + @Override + public void setMaxFuseTicks(int ticks) { + Preconditions.checkArgument(ticks >= 0, "ticks < 0"); + + getHandle().maxFuseTicks = ticks; + } + + @Override + public int getMaxFuseTicks() { + return getHandle().maxFuseTicks; + } + + @Override + public void setExplosionRadius(int radius) { + Preconditions.checkArgument(radius >= 0, "radius < 0"); + + getHandle().explosionRadius = radius; + } + + @Override + public int getExplosionRadius() { + return getHandle().explosionRadius; + } + + @Override + public EntityCreeper getHandle() { + return (EntityCreeper) entity; + } + + @Override + public String toString() { + return "CraftCreeper"; + } + + public EntityType getType() { + return EntityType.CREEPER; + } + + // Paper start + public void setIgnited(boolean ignited) { + getHandle().setIgnited(ignited); + } + + public boolean isIgnited() { + return getHandle().isIgnited(); + } + + public int getFuseTicks() { + return getHandle().fuseTicks; + } + + public void explode() { + getHandle().explode(); + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java new file mode 100644 index 000000000000..0399e6f51a54 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityDolphin; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Dolphin; +import org.bukkit.entity.EntityType; + +public class CraftDolphin extends CraftWaterMob implements Dolphin { + + public CraftDolphin(CraftServer server, EntityDolphin entity) { + super(server, entity); + } + + @Override + public EntityDolphin getHandle() { + return (EntityDolphin) super.getHandle(); + } + + @Override + public String toString() { + return "CraftDolphin"; + } + + @Override + public EntityType getType() { + return EntityType.DOLPHIN; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftDonkey.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftDonkey.java new file mode 100644 index 000000000000..b4d74414ad09 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftDonkey.java @@ -0,0 +1,29 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityHorseDonkey; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Donkey; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Horse.Variant; + +public class CraftDonkey extends CraftChestedHorse implements Donkey { + + public CraftDonkey(CraftServer server, EntityHorseDonkey entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftDonkey"; + } + + @Override + public EntityType getType() { + return EntityType.DONKEY; + } + + @Override + public Variant getVariant() { + return Variant.DONKEY; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftDragonFireball.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftDragonFireball.java new file mode 100644 index 000000000000..4a48b16d5c95 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftDragonFireball.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityDragonFireball; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.DragonFireball; +import org.bukkit.entity.EntityType; + +public class CraftDragonFireball extends CraftFireball implements DragonFireball { + public CraftDragonFireball(CraftServer server, EntityDragonFireball entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftDragonFireball"; + } + + @Override + public EntityType getType() { + return EntityType.DRAGON_FIREBALL; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftDrowned.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftDrowned.java new file mode 100644 index 000000000000..6e41dfd92344 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftDrowned.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityDrowned; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Drowned; +import org.bukkit.entity.EntityType; + +public class CraftDrowned extends CraftZombie implements Drowned { + + public CraftDrowned(CraftServer server, EntityDrowned entity) { + super(server, entity); + } + + @Override + public EntityDrowned getHandle() { + return (EntityDrowned) entity; + } + + @Override + public String toString() { + return "CraftDrowned"; + } + + @Override + public EntityType getType() { + return EntityType.DROWNED; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEgg.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEgg.java new file mode 100644 index 000000000000..60c5188ec497 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEgg.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityEgg; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Egg; +import org.bukkit.entity.EntityType; + +public class CraftEgg extends CraftProjectile implements Egg { + public CraftEgg(CraftServer server, EntityEgg entity) { + super(server, entity); + } + + @Override + public EntityEgg getHandle() { + return (EntityEgg) entity; + } + + @Override + public String toString() { + return "CraftEgg"; + } + + public EntityType getType() { + return EntityType.EGG; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftElderGuardian.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftElderGuardian.java new file mode 100644 index 000000000000..34df49bbd8d0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftElderGuardian.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityGuardianElder; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.ElderGuardian; +import org.bukkit.entity.EntityType; + +public class CraftElderGuardian extends CraftGuardian implements ElderGuardian { + + public CraftElderGuardian(CraftServer server, EntityGuardianElder entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftElderGuardian"; + } + + @Override + public EntityType getType() { + return EntityType.ELDER_GUARDIAN; + } + + @Override + public boolean isElder() { + return true; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderCrystal.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderCrystal.java new file mode 100644 index 000000000000..ffb863e601dd --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderCrystal.java @@ -0,0 +1,55 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.BlockPosition; +import net.minecraft.server.EntityEnderCrystal; +import org.bukkit.Location; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EnderCrystal; +import org.bukkit.entity.EntityType; + +public class CraftEnderCrystal extends CraftEntity implements EnderCrystal { + public CraftEnderCrystal(CraftServer server, EntityEnderCrystal entity) { + super(server, entity); + } + + @Override + public boolean isShowingBottom() { + return getHandle().isShowingBottom(); + } + + @Override + public void setShowingBottom(boolean showing) { + getHandle().setShowingBottom(showing); + } + + @Override + public Location getBeamTarget() { + BlockPosition pos = getHandle().getBeamTarget(); + return pos == null ? null : new Location(getWorld(), pos.getX(), pos.getY(), pos.getZ()); + } + + @Override + public void setBeamTarget(Location location) { + if (location == null) { + getHandle().setBeamTarget((BlockPosition) null); + } else if (location.getWorld() != getWorld()) { + throw new IllegalArgumentException("Cannot set beam target location to different world"); + } else { + getHandle().setBeamTarget(new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ())); + } + } + + @Override + public EntityEnderCrystal getHandle() { + return (EntityEnderCrystal) entity; + } + + @Override + public String toString() { + return "CraftEnderCrystal"; + } + + public EntityType getType() { + return EntityType.ENDER_CRYSTAL; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragon.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragon.java new file mode 100644 index 000000000000..a976238300c7 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragon.java @@ -0,0 +1,77 @@ +package org.bukkit.craftbukkit.entity; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; + +import java.util.Set; + +import net.minecraft.server.DragonControllerPhase; +import net.minecraft.server.EntityComplexPart; +import net.minecraft.server.EntityEnderDragon; + +import org.bukkit.boss.BossBar; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.boss.CraftBossBar; +import org.bukkit.entity.ComplexEntityPart; +import org.bukkit.entity.EnderDragon; +import org.bukkit.entity.EntityType; + +public class CraftEnderDragon extends CraftComplexLivingEntity implements EnderDragon { + + private BossBar bossBar; + + public CraftEnderDragon(CraftServer server, EntityEnderDragon entity) { + super(server, entity); + + if (entity.getEnderDragonBattle() != null) { + this.bossBar = new CraftBossBar(entity.getEnderDragonBattle().bossBattle); + } + } + + public Set getParts() { + Builder builder = ImmutableSet.builder(); + + for (EntityComplexPart part : getHandle().children) { + builder.add((ComplexEntityPart) part.getBukkitEntity()); + } + + return builder.build(); + } + + @Override + public EntityEnderDragon getHandle() { + return (EntityEnderDragon) entity; + } + + @Override + public String toString() { + return "CraftEnderDragon"; + } + + public EntityType getType() { + return EntityType.ENDER_DRAGON; + } + + @Override + public Phase getPhase() { + return Phase.values()[getHandle().getDataWatcher().get(EntityEnderDragon.PHASE)]; + } + + @Override + public void setPhase(Phase phase) { + getHandle().getDragonControllerManager().setControllerPhase(getMinecraftPhase(phase)); + } + + public static Phase getBukkitPhase(DragonControllerPhase phase) { + return Phase.values()[phase.b()]; + } + + public static DragonControllerPhase getMinecraftPhase(Phase phase) { + return DragonControllerPhase.getById(phase.ordinal()); + } + + @Override + public BossBar getBossBar() { + return bossBar; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragonPart.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragonPart.java new file mode 100644 index 000000000000..c3338710bc4d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragonPart.java @@ -0,0 +1,56 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityComplexPart; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EnderDragon; +import org.bukkit.entity.EnderDragonPart; +import org.bukkit.entity.Entity; + +public class CraftEnderDragonPart extends CraftComplexPart implements EnderDragonPart { + public CraftEnderDragonPart(CraftServer server, EntityComplexPart entity) { + super(server, entity); + } + + @Override + public EnderDragon getParent() { + return (EnderDragon) super.getParent(); + } + + @Override + public EntityComplexPart getHandle() { + return (EntityComplexPart) entity; + } + + @Override + public String toString() { + return "CraftEnderDragonPart"; + } + + public void damage(double amount) { + getParent().damage(amount); + } + + public void damage(double amount, Entity source) { + getParent().damage(amount, source); + } + + public double getHealth() { + return getParent().getHealth(); + } + + public void setHealth(double health) { + getParent().setHealth(health); + } + + public double getMaxHealth() { + return getParent().getMaxHealth(); + } + + public void setMaxHealth(double health) { + getParent().setMaxHealth(health); + } + + public void resetMaxHealth() { + getParent().resetMaxHealth(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderPearl.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderPearl.java new file mode 100644 index 000000000000..f42f9ab7efc1 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderPearl.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityEnderPearl; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EnderPearl; +import org.bukkit.entity.EntityType; + +public class CraftEnderPearl extends CraftProjectile implements EnderPearl { + public CraftEnderPearl(CraftServer server, EntityEnderPearl entity) { + super(server, entity); + } + + @Override + public EntityEnderPearl getHandle() { + return (EntityEnderPearl) entity; + } + + @Override + public String toString() { + return "CraftEnderPearl"; + } + + public EntityType getType() { + return EntityType.ENDER_PEARL; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderSignal.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderSignal.java new file mode 100644 index 000000000000..d771fdc6ed35 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderSignal.java @@ -0,0 +1,61 @@ +package org.bukkit.craftbukkit.entity; + +import com.google.common.base.Preconditions; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.EntityEnderSignal; +import org.bukkit.Location; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EnderSignal; +import org.bukkit.entity.EntityType; + +public class CraftEnderSignal extends CraftEntity implements EnderSignal { + public CraftEnderSignal(CraftServer server, EntityEnderSignal entity) { + super(server, entity); + } + + @Override + public EntityEnderSignal getHandle() { + return (EntityEnderSignal) entity; + } + + @Override + public String toString() { + return "CraftEnderSignal"; + } + + @Override + public EntityType getType() { + return EntityType.ENDER_SIGNAL; + } + + @Override + public Location getTargetLocation() { + return new Location(getWorld(), getHandle().targetX, getHandle().targetY, getHandle().targetZ, getHandle().yaw, getHandle().pitch); + } + + @Override + public void setTargetLocation(Location location) { + Preconditions.checkArgument(getWorld().equals(location.getWorld()), "Cannot target EnderSignal across worlds"); + getHandle().a(new BlockPosition(location.getX(), location.getY(), location.getZ())); + } + + @Override + public boolean getDropItem() { + return getHandle().shouldDropItem; + } + + @Override + public void setDropItem(boolean shouldDropItem) { + getHandle().shouldDropItem = shouldDropItem; + } + + @Override + public int getDespawnTimer() { + return getHandle().despawnTimer; + } + + @Override + public void setDespawnTimer(int time) { + getHandle().despawnTimer = time; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderman.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderman.java new file mode 100644 index 000000000000..9bc6a6c0c14b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderman.java @@ -0,0 +1,54 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityEnderman; + +import net.minecraft.server.IBlockData; +import org.bukkit.Material; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.Enderman; +import org.bukkit.entity.EntityType; +import org.bukkit.material.MaterialData; + +public class CraftEnderman extends CraftMonster implements Enderman { + public CraftEnderman(CraftServer server, EntityEnderman entity) { + super(server, entity); + } + + @Override public boolean teleportRandomly() { return getHandle().teleportRandomly(); } // Paper + public MaterialData getCarriedMaterial() { + IBlockData blockData = getHandle().getCarried(); + return (blockData == null) ? Material.AIR.getNewData((byte) 0) : CraftMagicNumbers.getMaterial(blockData); + } + + @Override + public BlockData getCarriedBlock() { + IBlockData blockData = getHandle().getCarried(); + return (blockData == null) ? null : CraftBlockData.fromData(blockData); + } + + public void setCarriedMaterial(MaterialData data) { + getHandle().setCarried(CraftMagicNumbers.getBlock(data)); + } + + @Override + public void setCarriedBlock(BlockData blockData) { + getHandle().setCarried(blockData == null ? null : ((CraftBlockData) blockData).getState()); + } + + @Override + public EntityEnderman getHandle() { + return (EntityEnderman) entity; + } + + @Override + public String toString() { + return "CraftEnderman"; + } + + public EntityType getType() { + return EntityType.ENDERMAN; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java new file mode 100644 index 000000000000..31c5d97eefd0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java @@ -0,0 +1,38 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityEndermite; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Endermite; +import org.bukkit.entity.EntityType; + +public class CraftEndermite extends CraftMonster implements Endermite { + + public CraftEndermite(CraftServer server, EntityEndermite entity) { + super(server, entity); + } + + @Override + public EntityEndermite getHandle() { + return (EntityEndermite) super.getHandle(); + } + + @Override + public String toString() { + return "CraftEndermite"; + } + + @Override + public EntityType getType() { + return EntityType.ENDERMITE; + } + + @Override + public boolean isPlayerSpawned() { + return getHandle().isPlayerSpawned(); + } + + @Override + public void setPlayerSpawned(boolean playerSpawned) { + getHandle().setPlayerSpawned(playerSpawned); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java new file mode 100644 index 000000000000..d4a8fb16b879 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -0,0 +1,849 @@ +package org.bukkit.craftbukkit.entity; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import net.minecraft.server.*; + +import org.bukkit.Chunk; +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.block.BlockFace; +import org.bukkit.block.PistonMoveReaction; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.permissions.PermissibleBase; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.permissions.ServerOperator; +import org.bukkit.plugin.Plugin; +import org.bukkit.util.BoundingBox; +import org.bukkit.util.Vector; + +public abstract class CraftEntity implements org.bukkit.entity.Entity { + private static PermissibleBase perm; + + protected final CraftServer server; + protected Entity entity; + private EntityDamageEvent lastDamageEvent; + + public CraftEntity(final CraftServer server, final Entity entity) { + this.server = server; + this.entity = entity; + } + + @Override + public Chunk getChunk() { + net.minecraft.server.Chunk currentChunk = entity.getCurrentChunk(); + return currentChunk != null ? currentChunk.bukkitChunk : getLocation().getChunk(); + } + + public static CraftEntity getEntity(CraftServer server, Entity entity) { + /** + * Order is *EXTREMELY* important -- keep it right! =D + */ + if (entity instanceof EntityLiving) { + // Players + if (entity instanceof EntityHuman) { + if (entity instanceof EntityPlayer) { return new CraftPlayer(server, (EntityPlayer) entity); } + else { return new CraftHumanEntity(server, (EntityHuman) entity); } + } + // Water Animals + else if (entity instanceof EntityWaterAnimal) { + if (entity instanceof EntitySquid) { return new CraftSquid(server, (EntitySquid) entity); } + else if (entity instanceof EntityFish) { + if (entity instanceof EntityCod) { return new CraftCod(server, (EntityCod) entity); } + else if (entity instanceof EntityPufferFish) { return new CraftPufferFish(server, (EntityPufferFish) entity); } + else if (entity instanceof EntitySalmon) { return new CraftSalmon(server, (EntitySalmon) entity); } + else if (entity instanceof EntityTropicalFish) { return new CraftTropicalFish(server, (EntityTropicalFish) entity); } + else { return new CraftFish(server, (EntityFish) entity); } + } + else if (entity instanceof EntityDolphin) { return new CraftDolphin(server, (EntityDolphin) entity); } + else { return new CraftWaterMob(server, (EntityWaterAnimal) entity); } + } + else if (entity instanceof EntityCreature) { + // Animals + if (entity instanceof EntityAnimal) { + if (entity instanceof EntityChicken) { return new CraftChicken(server, (EntityChicken) entity); } + else if (entity instanceof EntityCow) { + if (entity instanceof EntityMushroomCow) { return new CraftMushroomCow(server, (EntityMushroomCow) entity); } + else { return new CraftCow(server, (EntityCow) entity); } + } + else if (entity instanceof EntityPig) { return new CraftPig(server, (EntityPig) entity); } + else if (entity instanceof EntityTameableAnimal) { + if (entity instanceof EntityWolf) { return new CraftWolf(server, (EntityWolf) entity); } + else if (entity instanceof EntityOcelot) { return new CraftOcelot(server, (EntityOcelot) entity); } + else if (entity instanceof EntityParrot) { return new CraftParrot(server, (EntityParrot) entity); } + } + else if (entity instanceof EntitySheep) { return new CraftSheep(server, (EntitySheep) entity); } + else if (entity instanceof EntityHorseAbstract) { + if (entity instanceof EntityHorseChestedAbstract){ + if (entity instanceof EntityHorseDonkey) { return new CraftDonkey(server, (EntityHorseDonkey) entity); } + else if (entity instanceof EntityHorseMule) { return new CraftMule(server, (EntityHorseMule) entity); } + else if (entity instanceof EntityLlama) { return new CraftLlama(server, (EntityLlama) entity); } + } else if (entity instanceof EntityHorse) { return new CraftHorse(server, (EntityHorse) entity); } + else if (entity instanceof EntityHorseSkeleton) { return new CraftSkeletonHorse(server, (EntityHorseSkeleton) entity); } + else if (entity instanceof EntityHorseZombie) { return new CraftZombieHorse(server, (EntityHorseZombie) entity); } + } + else if (entity instanceof EntityRabbit) { return new CraftRabbit(server, (EntityRabbit) entity); } + else if (entity instanceof EntityPolarBear) { return new CraftPolarBear(server, (EntityPolarBear) entity); } + else if (entity instanceof EntityTurtle) { return new CraftTurtle(server, (EntityTurtle) entity); } + else { return new CraftAnimals(server, (EntityAnimal) entity); } + } + // Monsters + else if (entity instanceof EntityMonster) { + if (entity instanceof EntityZombie) { + if (entity instanceof EntityPigZombie) { return new CraftPigZombie(server, (EntityPigZombie) entity); } + else if (entity instanceof EntityZombieHusk) { return new CraftHusk(server, (EntityZombieHusk) entity); } + else if (entity instanceof EntityZombieVillager) { return new CraftVillagerZombie(server, (EntityZombieVillager) entity); } + else if (entity instanceof EntityDrowned) { return new CraftDrowned(server, (EntityDrowned) entity); } + else { return new CraftZombie(server, (EntityZombie) entity); } + } + else if (entity instanceof EntityCreeper) { return new CraftCreeper(server, (EntityCreeper) entity); } + else if (entity instanceof EntityEnderman) { return new CraftEnderman(server, (EntityEnderman) entity); } + else if (entity instanceof EntitySilverfish) { return new CraftSilverfish(server, (EntitySilverfish) entity); } + else if (entity instanceof EntityGiantZombie) { return new CraftGiant(server, (EntityGiantZombie) entity); } + else if (entity instanceof EntitySkeletonAbstract) { + if (entity instanceof EntitySkeletonStray) { return new CraftStray(server, (EntitySkeletonStray) entity); } + else if (entity instanceof EntitySkeletonWither) { return new CraftWitherSkeleton(server, (EntitySkeletonWither) entity); } + else { return new CraftSkeleton(server, (EntitySkeletonAbstract) entity); } + } + else if (entity instanceof EntityBlaze) { return new CraftBlaze(server, (EntityBlaze) entity); } + else if (entity instanceof EntityWitch) { return new CraftWitch(server, (EntityWitch) entity); } + else if (entity instanceof EntityWither) { return new CraftWither(server, (EntityWither) entity); } + else if (entity instanceof EntitySpider) { + if (entity instanceof EntityCaveSpider) { return new CraftCaveSpider(server, (EntityCaveSpider) entity); } + else { return new CraftSpider(server, (EntitySpider) entity); } + } + else if (entity instanceof EntityEndermite) { return new CraftEndermite(server, (EntityEndermite) entity); } + else if (entity instanceof EntityGuardian) { + if (entity instanceof EntityGuardianElder) { return new CraftElderGuardian(server, (EntityGuardianElder) entity); } + else { return new CraftGuardian(server, (EntityGuardian) entity); } + } + else if (entity instanceof EntityVex) { return new CraftVex(server, (EntityVex) entity); } + else if (entity instanceof EntityIllagerAbstract) { + if (entity instanceof EntityIllagerWizard) { + if (entity instanceof EntityEvoker) { return new CraftEvoker(server, (EntityEvoker) entity); } + else if (entity instanceof EntityIllagerIllusioner) { return new CraftIllusioner(server, (EntityIllagerIllusioner) entity); } + else { return new CraftSpellcaster(server, (EntityIllagerWizard) entity); } + } + else if (entity instanceof EntityVindicator) { return new CraftVindicator(server, (EntityVindicator) entity); } + else { return new CraftIllager(server, (EntityIllagerAbstract) entity); } + } + + else { return new CraftMonster(server, (EntityMonster) entity); } + } + else if (entity instanceof EntityGolem) { + if (entity instanceof EntitySnowman) { return new CraftSnowman(server, (EntitySnowman) entity); } + else if (entity instanceof EntityIronGolem) { return new CraftIronGolem(server, (EntityIronGolem) entity); } + else if (entity instanceof EntityShulker) { return new CraftShulker(server, (EntityShulker) entity); } + } + else if (entity instanceof EntityVillager) { return new CraftVillager(server, (EntityVillager) entity); } + else { return new CraftCreature(server, (EntityCreature) entity); } + } + // Slimes are a special (and broken) case + else if (entity instanceof EntitySlime) { + if (entity instanceof EntityMagmaCube) { return new CraftMagmaCube(server, (EntityMagmaCube) entity); } + else { return new CraftSlime(server, (EntitySlime) entity); } + } + // Flying + else if (entity instanceof EntityFlying) { + if (entity instanceof EntityGhast) { return new CraftGhast(server, (EntityGhast) entity); } + else if (entity instanceof EntityPhantom) { return new CraftPhantom(server, (EntityPhantom) entity); } + else { return new CraftFlying(server, (EntityFlying) entity); } + } + else if (entity instanceof EntityEnderDragon) { + return new CraftEnderDragon(server, (EntityEnderDragon) entity); + } + // Ambient + else if (entity instanceof EntityAmbient) { + if (entity instanceof EntityBat) { return new CraftBat(server, (EntityBat) entity); } + else { return new CraftAmbient(server, (EntityAmbient) entity); } + } + else if (entity instanceof EntityArmorStand) { return new CraftArmorStand(server, (EntityArmorStand) entity); } + else { return new CraftLivingEntity(server, (EntityLiving) entity); } + } + else if (entity instanceof EntityComplexPart) { + EntityComplexPart part = (EntityComplexPart) entity; + if (part.owner instanceof EntityEnderDragon) { return new CraftEnderDragonPart(server, (EntityComplexPart) entity); } + else { return new CraftComplexPart(server, (EntityComplexPart) entity); } + } + else if (entity instanceof EntityExperienceOrb) { return new CraftExperienceOrb(server, (EntityExperienceOrb) entity); } + else if (entity instanceof EntityTippedArrow) { + if (((EntityTippedArrow) entity).isTipped()) { return new CraftTippedArrow(server, (EntityTippedArrow) entity); } + else { return new CraftArrow(server, (EntityArrow) entity); } + } + else if (entity instanceof EntitySpectralArrow) { return new CraftSpectralArrow(server, (EntitySpectralArrow) entity); } + else if (entity instanceof EntityArrow) { + if (entity instanceof EntityThrownTrident) { return new CraftTrident(server, (EntityThrownTrident) entity); } + else { return new CraftArrow(server, (EntityArrow) entity); } + } + else if (entity instanceof EntityBoat) { return new CraftBoat(server, (EntityBoat) entity); } + else if (entity instanceof EntityProjectile) { + if (entity instanceof EntityEgg) { return new CraftEgg(server, (EntityEgg) entity); } + else if (entity instanceof EntitySnowball) { return new CraftSnowball(server, (EntitySnowball) entity); } + else if (entity instanceof EntityPotion) { + if (!((EntityPotion) entity).isLingering()) { return new CraftSplashPotion(server, (EntityPotion) entity); } + else { return new CraftLingeringPotion(server, (EntityPotion) entity); } + } + else if (entity instanceof EntityEnderPearl) { return new CraftEnderPearl(server, (EntityEnderPearl) entity); } + else if (entity instanceof EntityThrownExpBottle) { return new CraftThrownExpBottle(server, (EntityThrownExpBottle) entity); } + } + else if (entity instanceof EntityFallingBlock) { return new CraftFallingBlock(server, (EntityFallingBlock) entity); } + else if (entity instanceof EntityFireball) { + if (entity instanceof EntitySmallFireball) { return new CraftSmallFireball(server, (EntitySmallFireball) entity); } + else if (entity instanceof EntityLargeFireball) { return new CraftLargeFireball(server, (EntityLargeFireball) entity); } + else if (entity instanceof EntityWitherSkull) { return new CraftWitherSkull(server, (EntityWitherSkull) entity); } + else if (entity instanceof EntityDragonFireball) { return new CraftDragonFireball(server, (EntityDragonFireball) entity); } + else { return new CraftFireball(server, (EntityFireball) entity); } + } + else if (entity instanceof EntityEnderSignal) { return new CraftEnderSignal(server, (EntityEnderSignal) entity); } + else if (entity instanceof EntityEnderCrystal) { return new CraftEnderCrystal(server, (EntityEnderCrystal) entity); } + else if (entity instanceof EntityFishingHook) { return new CraftFishHook(server, (EntityFishingHook) entity); } + else if (entity instanceof EntityItem) { return new CraftItem(server, (EntityItem) entity); } + else if (entity instanceof EntityWeather) { + if (entity instanceof EntityLightning) { return new CraftLightningStrike(server, (EntityLightning) entity); } + else { return new CraftWeather(server, (EntityWeather) entity); } + } + else if (entity instanceof EntityMinecartAbstract) { + if (entity instanceof EntityMinecartFurnace) { return new CraftMinecartFurnace(server, (EntityMinecartFurnace) entity); } + else if (entity instanceof EntityMinecartChest) { return new CraftMinecartChest(server, (EntityMinecartChest) entity); } + else if (entity instanceof EntityMinecartTNT) { return new CraftMinecartTNT(server, (EntityMinecartTNT) entity); } + else if (entity instanceof EntityMinecartHopper) { return new CraftMinecartHopper(server, (EntityMinecartHopper) entity); } + else if (entity instanceof EntityMinecartMobSpawner) { return new CraftMinecartMobSpawner(server, (EntityMinecartMobSpawner) entity); } + else if (entity instanceof EntityMinecartRideable) { return new CraftMinecartRideable(server, (EntityMinecartRideable) entity); } + else if (entity instanceof EntityMinecartCommandBlock) { return new CraftMinecartCommand(server, (EntityMinecartCommandBlock) entity); } + } else if (entity instanceof EntityHanging) { + if (entity instanceof EntityPainting) { return new CraftPainting(server, (EntityPainting) entity); } + else if (entity instanceof EntityItemFrame) { return new CraftItemFrame(server, (EntityItemFrame) entity); } + else if (entity instanceof EntityLeash) { return new CraftLeash(server, (EntityLeash) entity); } + else { return new CraftHanging(server, (EntityHanging) entity); } + } + else if (entity instanceof EntityTNTPrimed) { return new CraftTNTPrimed(server, (EntityTNTPrimed) entity); } + else if (entity instanceof EntityFireworks) { return new CraftFirework(server, (EntityFireworks) entity); } + else if (entity instanceof EntityShulkerBullet) { return new CraftShulkerBullet(server, (EntityShulkerBullet) entity); } + else if (entity instanceof EntityAreaEffectCloud) { return new CraftAreaEffectCloud(server, (EntityAreaEffectCloud) entity); } + else if (entity instanceof EntityEvokerFangs) { return new CraftEvokerFangs(server, (EntityEvokerFangs) entity); } + else if (entity instanceof EntityLlamaSpit) { return new CraftLlamaSpit(server, (EntityLlamaSpit) entity); } + + throw new AssertionError("Unknown entity " + (entity == null ? null : entity.getClass())); + } + + public Location getLocation() { + return new Location(getWorld(), entity.locX, entity.locY, entity.locZ, entity.getBukkitYaw(), entity.pitch); + } + + public Location getLocation(Location loc) { + if (loc != null) { + loc.setWorld(getWorld()); + loc.setX(entity.locX); + loc.setY(entity.locY); + loc.setZ(entity.locZ); + loc.setYaw(entity.getBukkitYaw()); + loc.setPitch(entity.pitch); + } + + return loc; + } + + public Vector getVelocity() { + return new Vector(entity.motX, entity.motY, entity.motZ); + } + + public void setVelocity(Vector velocity) { + Preconditions.checkArgument(velocity != null, "velocity"); + velocity.checkFinite(); + + // Paper start - Warn server owners when plugins try to set super high velocities + if (!(this instanceof org.bukkit.entity.Projectile) && isUnsafeVelocity(velocity)) { + CraftServer.excessiveVelEx = new Exception("Excessive velocity set detected: tried to set velocity of entity " + entity.getName() + " id #" + getEntityId() + " to (" + velocity.getX() + "," + velocity.getY() + "," + velocity.getZ() + ")."); + } + // Paper end + + entity.motX = velocity.getX(); + entity.motY = velocity.getY(); + entity.motZ = velocity.getZ(); + entity.velocityChanged = true; + } + + // Paper start + /** + * Checks if the given velocity is not necessarily safe in all situations. + * This function returning true does not mean the velocity is dangerous or to be avoided, only that it may be + * a detriment to performance on the server. + * + * It is not to be used as a hard rule of any sort. + * Paper only uses it to warn server owners in watchdog crashes. + * + * @param vel incoming velocity to check + * @return if the velocity has the potential to be a performance detriment + */ + private static boolean isUnsafeVelocity(Vector vel) { + final double x = vel.getX(); + final double y = vel.getY(); + final double z = vel.getZ(); + + if (x > 4 || x < -4 || y > 4 || y < -4 || z > 4 || z < -4) { + return true; + } + + return false; + } + // Paper end + + @Override + public double getHeight() { + return getHandle().length; + } + + @Override + public double getWidth() { + return getHandle().width; + } + + @Override + public BoundingBox getBoundingBox() { + AxisAlignedBB bb = getHandle().getBoundingBox(); + return new BoundingBox(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); + } + + public boolean isOnGround() { + if (entity instanceof EntityArrow) { + return ((EntityArrow) entity).inGround; + } + return entity.onGround; + } + + public World getWorld() { + return entity.world.getWorld(); + } + + public boolean teleport(Location location) { + return teleport(location, TeleportCause.PLUGIN); + } + + public boolean teleport(Location location, TeleportCause cause) { + Preconditions.checkArgument(location != null, "location"); + location.checkFinite(); + + if (entity.isVehicle() || entity.dead) { + return false; + } + + // If this entity is riding another entity, we must dismount before teleporting. + entity.stopRiding(); + + // Let the server handle cross world teleports + if (!location.getWorld().equals(getWorld())) { + entity.teleportTo(location, false); + return true; + } + + // entity.setLocation() throws no event, and so cannot be cancelled + entity.setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + // SPIGOT-619: Force sync head rotation also + entity.setHeadRotation(location.getYaw()); + entity.world.entityJoinedWorld(entity, false); // Spigot - register to new chunk + + return true; + } + + public boolean teleport(org.bukkit.entity.Entity destination) { + return teleport(destination.getLocation()); + } + + public boolean teleport(org.bukkit.entity.Entity destination, TeleportCause cause) { + return teleport(destination.getLocation(), cause); + } + + public List getNearbyEntities(double x, double y, double z) { + List notchEntityList = entity.world.getEntities(entity, entity.getBoundingBox().grow(x, y, z), null); + List bukkitEntityList = new java.util.ArrayList(notchEntityList.size()); + + for (Entity e : notchEntityList) { + bukkitEntityList.add(e.getBukkitEntity()); + } + return bukkitEntityList; + } + + public int getEntityId() { + return entity.getId(); + } + + public int getFireTicks() { + return entity.fireTicks; + } + + public int getMaxFireTicks() { + return entity.getMaxFireTicks(); + } + + public void setFireTicks(int ticks) { + entity.fireTicks = ticks; + } + + public void remove() { + entity.die(); + } + + public boolean isDead() { + return !entity.isAlive(); + } + + public boolean isValid() { + return entity.isAlive() && entity.valid; + } + + public Server getServer() { + return server; + } + + @Override + public boolean isPersistent() { + return entity.persist; + } + + @Override + public void setPersistent(boolean persistent) { + entity.persist = persistent; + } + + public Vector getMomentum() { + return getVelocity(); + } + + public void setMomentum(Vector value) { + setVelocity(value); + } + + public org.bukkit.entity.Entity getPassenger() { + return isEmpty() ? null : getHandle().passengers.get(0).getBukkitEntity(); + } + + public boolean setPassenger(org.bukkit.entity.Entity passenger) { + Preconditions.checkArgument(!this.equals(passenger), "Entity cannot ride itself."); + if (passenger instanceof CraftEntity) { + eject(); + return ((CraftEntity) passenger).getHandle().startRiding(getHandle()); + } else { + return false; + } + } + + @Override + public List getPassengers() { + return Lists.newArrayList(Lists.transform(getHandle().passengers, new Function() { + @Override + public org.bukkit.entity.Entity apply(Entity input) { + return input.getBukkitEntity(); + } + })); + } + + @Override + public boolean addPassenger(org.bukkit.entity.Entity passenger) { + Preconditions.checkArgument(passenger != null, "passenger == null"); + + return ((CraftEntity) passenger).getHandle().a(getHandle(), true); + } + + @Override + public boolean removePassenger(org.bukkit.entity.Entity passenger) { + Preconditions.checkArgument(passenger != null, "passenger == null"); + + ((CraftEntity) passenger).getHandle().stopRiding(); + return true; + } + + public boolean isEmpty() { + return !getHandle().isVehicle(); + } + + public boolean eject() { + if (isEmpty()) { + return false; + } + + getHandle().ejectPassengers(); + return true; + } + + public float getFallDistance() { + return getHandle().fallDistance; + } + + public void setFallDistance(float distance) { + getHandle().fallDistance = distance; + } + + public void setLastDamageCause(EntityDamageEvent event) { + lastDamageEvent = event; + } + + public EntityDamageEvent getLastDamageCause() { + return lastDamageEvent; + } + + public UUID getUniqueId() { + return getHandle().getUniqueID(); + } + + public int getTicksLived() { + return getHandle().ticksLived; + } + + public void setTicksLived(int value) { + if (value <= 0) { + throw new IllegalArgumentException("Age must be at least 1 tick"); + } + getHandle().ticksLived = value; + } + + public Entity getHandle() { + return entity; + } + + @Override + public void playEffect(EntityEffect type) { + Preconditions.checkArgument(type != null, "type"); + + if (type.getApplicable().isInstance(this)) { + this.getHandle().world.broadcastEntityEffect(getHandle(), type.getData()); + } + } + + public void setHandle(final Entity entity) { + this.entity = entity; + } + + @Override + public String toString() { + return "CraftEntity{" + "id=" + getEntityId() + '}'; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final CraftEntity other = (CraftEntity) obj; + return (this.getHandle() == other.getHandle()); // Paper - while logically the same, this is clearer + } + + // Paper - Fix hashCode. entity ID's are not static. + // A CraftEntity can change reference to a new entity with a new ID, and hash codes should never change + @Override + public int hashCode() { + return getUniqueId().hashCode(); + // Paper end + } + + public void setMetadata(String metadataKey, MetadataValue newMetadataValue) { + server.getEntityMetadata().setMetadata(this, metadataKey, newMetadataValue); + } + + public List getMetadata(String metadataKey) { + return server.getEntityMetadata().getMetadata(this, metadataKey); + } + + public boolean hasMetadata(String metadataKey) { + return server.getEntityMetadata().hasMetadata(this, metadataKey); + } + + public void removeMetadata(String metadataKey, Plugin owningPlugin) { + server.getEntityMetadata().removeMetadata(this, metadataKey, owningPlugin); + } + + public boolean isInsideVehicle() { + return getHandle().isPassenger(); + } + + public boolean leaveVehicle() { + if (!isInsideVehicle()) { + return false; + } + + getHandle().stopRiding(); + return true; + } + + public org.bukkit.entity.Entity getVehicle() { + if (!isInsideVehicle()) { + return null; + } + + return getHandle().getVehicle().getBukkitEntity(); + } + + @Override + public void setCustomName(String name) { + // sane limit for name length + if (name != null && name.length() > 256) { + name = name.substring(0, 256); + } + + getHandle().setCustomName(CraftChatMessage.fromStringOrNull(name)); + } + + @Override + public String getCustomName() { + IChatBaseComponent name = getHandle().getCustomName(); + + if (name == null) { + return null; + } + + return CraftChatMessage.fromComponent(name); + } + + @Override + public void setCustomNameVisible(boolean flag) { + getHandle().setCustomNameVisible(flag); + } + + @Override + public boolean isCustomNameVisible() { + return getHandle().getCustomNameVisible(); + } + + @Override + public void sendMessage(String message) { + + } + + @Override + public void sendMessage(String[] messages) { + + } + + @Override + public String getName() { + return CraftChatMessage.fromComponent(getHandle().getDisplayName(), EnumChatFormat.WHITE); + } + + @Override + public boolean isPermissionSet(String name) { + return getPermissibleBase().isPermissionSet(name); + } + + @Override + public boolean isPermissionSet(Permission perm) { + return CraftEntity.getPermissibleBase().isPermissionSet(perm); + } + + @Override + public boolean hasPermission(String name) { + return getPermissibleBase().hasPermission(name); + } + + @Override + public boolean hasPermission(Permission perm) { + return getPermissibleBase().hasPermission(perm); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { + return getPermissibleBase().addAttachment(plugin, name, value); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin) { + return getPermissibleBase().addAttachment(plugin); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { + return getPermissibleBase().addAttachment(plugin, name, value, ticks); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, int ticks) { + return getPermissibleBase().addAttachment(plugin, ticks); + } + + @Override + public void removeAttachment(PermissionAttachment attachment) { + getPermissibleBase().removeAttachment(attachment); + } + + @Override + public void recalculatePermissions() { + getPermissibleBase().recalculatePermissions(); + } + + @Override + public Set getEffectivePermissions() { + return getPermissibleBase().getEffectivePermissions(); + } + + @Override + public boolean isOp() { + return getPermissibleBase().isOp(); + } + + @Override + public void setOp(boolean value) { + getPermissibleBase().setOp(value); + } + + @Override + public void setGlowing(boolean flag) { + getHandle().glowing = flag; + Entity e = getHandle(); + if (e.getFlag(6) != flag) { + e.setFlag(6, flag); + } + } + + @Override + public boolean isGlowing() { + return getHandle().glowing; + } + + @Override + public void setInvulnerable(boolean flag) { + getHandle().setInvulnerable(flag); + } + + @Override + public boolean isInvulnerable() { + return getHandle().isInvulnerable(DamageSource.GENERIC); + } + + @Override + public boolean isSilent() { + return getHandle().isSilent(); + } + + @Override + public void setSilent(boolean flag) { + getHandle().setSilent(flag); + } + + @Override + public boolean hasGravity() { + return !getHandle().isNoGravity(); + } + + @Override + public void setGravity(boolean gravity) { + getHandle().setNoGravity(!gravity); + } + + @Override + public int getPortalCooldown() { + return getHandle().portalCooldown; + } + + @Override + public void setPortalCooldown(int cooldown) { + getHandle().portalCooldown = cooldown; + } + + @Override + public Set getScoreboardTags() { + return getHandle().getScoreboardTags(); + } + + @Override + public boolean addScoreboardTag(String tag) { + return getHandle().addScoreboardTag(tag); + } + + @Override + public boolean removeScoreboardTag(String tag) { + return getHandle().removeScoreboardTag(tag); + } + + @Override + public PistonMoveReaction getPistonMoveReaction() { + return PistonMoveReaction.getById(getHandle().getPushReaction().ordinal()); + } + + @Override + public BlockFace getFacing() { + // Use this method over getDirection because it handles boats and minecarts. + return CraftBlock.notchToBlockFace(getHandle().getAdjustedDirection()); + } + + protected NBTTagCompound save() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + + nbttagcompound.setString("id", getHandle().getSaveID()); + getHandle().save(nbttagcompound); + + return nbttagcompound; + } + + private static PermissibleBase getPermissibleBase() { + if (perm == null) { + perm = new PermissibleBase(new ServerOperator() { + + @Override + public boolean isOp() { + return false; + } + + @Override + public void setOp(boolean value) { + + } + }); + } + return perm; + } + + // Spigot start + private final Spigot spigot = new Spigot() + { + @Override + public boolean isInvulnerable() + { + return getHandle().isInvulnerable(net.minecraft.server.DamageSource.GENERIC); + } + + @Override + public void sendMessage(net.md_5.bungee.api.chat.BaseComponent component) + { + } + + @Override + public void sendMessage(net.md_5.bungee.api.chat.BaseComponent... components) + { + } + }; + + public Spigot spigot() + { + return spigot; + } + // Spigot end + + // Paper start + @Override + public Location getOrigin() { + Location origin = getHandle().origin; + return origin == null ? null : origin.clone(); + } + + @Override + public boolean fromMobSpawner() { + return getHandle().spawnedViaMobSpawner; + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEvoker.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEvoker.java new file mode 100644 index 000000000000..0636d264731f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEvoker.java @@ -0,0 +1,39 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityEvoker; +import net.minecraft.server.EntityIllagerWizard; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Evoker; + +public class CraftEvoker extends CraftSpellcaster implements Evoker { + + public CraftEvoker(CraftServer server, EntityEvoker entity) { + super(server, entity); + } + + @Override + public EntityEvoker getHandle() { + return (EntityEvoker) super.getHandle(); + } + + @Override + public String toString() { + return "CraftEvoker"; + } + + @Override + public EntityType getType() { + return EntityType.EVOKER; + } + + @Override + public Evoker.Spell getCurrentSpell() { + return Evoker.Spell.values()[getHandle().getSpell().ordinal()]; + } + + @Override + public void setCurrentSpell(Evoker.Spell spell) { + getHandle().setSpell(spell == null ? EntityIllagerWizard.Spell.NONE : EntityIllagerWizard.Spell.a(spell.ordinal())); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEvokerFangs.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEvokerFangs.java new file mode 100644 index 000000000000..14e0accf84d3 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEvokerFangs.java @@ -0,0 +1,42 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityEvokerFangs; +import net.minecraft.server.EntityLiving; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.EvokerFangs; +import org.bukkit.entity.LivingEntity; + +public class CraftEvokerFangs extends CraftEntity implements EvokerFangs { + + public CraftEvokerFangs(CraftServer server, EntityEvokerFangs entity) { + super(server, entity); + } + + @Override + public EntityEvokerFangs getHandle() { + return (EntityEvokerFangs) super.getHandle(); + } + + @Override + public String toString() { + return "CraftEvokerFangs"; + } + + @Override + public EntityType getType() { + return EntityType.EVOKER_FANGS; + } + + @Override + public LivingEntity getOwner() { + EntityLiving owner = getHandle().getOwner(); + + return (owner == null) ? null : (LivingEntity) owner.getBukkitEntity(); + } + + @Override + public void setOwner(LivingEntity owner) { + getHandle().a(owner == null ? null : ((CraftLivingEntity) owner).getHandle()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java new file mode 100644 index 000000000000..3302af0e4575 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftExperienceOrb.java @@ -0,0 +1,46 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityExperienceOrb; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.ExperienceOrb; + +public class CraftExperienceOrb extends CraftEntity implements ExperienceOrb { + public CraftExperienceOrb(CraftServer server, EntityExperienceOrb entity) { + super(server, entity); + } + + public int getExperience() { + return getHandle().value; + } + + public void setExperience(int value) { + getHandle().value = value; + } + + // Paper start + public java.util.UUID getTriggerEntityId() { + return getHandle().triggerEntityId; + } + public java.util.UUID getSourceEntityId() { + return getHandle().sourceEntityId; + } + public SpawnReason getSpawnReason() { + return getHandle().spawnReason; + } + // Paper end + + @Override + public EntityExperienceOrb getHandle() { + return (EntityExperienceOrb) entity; + } + + @Override + public String toString() { + return "CraftExperienceOrb"; + } + + public EntityType getType() { + return EntityType.EXPERIENCE_ORB; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java new file mode 100644 index 000000000000..b4322dff668f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java @@ -0,0 +1,66 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityFallingBlock; + +import org.bukkit.Material; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.FallingBlock; + +public class CraftFallingBlock extends CraftEntity implements FallingBlock { + + public CraftFallingBlock(CraftServer server, EntityFallingBlock entity) { + super(server, entity); + } + + @Override + public EntityFallingBlock getHandle() { + return (EntityFallingBlock) entity; + } + + @Override + public String toString() { + return "CraftFallingBlock"; + } + + public EntityType getType() { + return EntityType.FALLING_BLOCK; + } + + public Material getMaterial() { + return CraftMagicNumbers.getMaterial(getHandle().getBlock()).getItemType(); + } + + public BlockData getBlockData() { + return CraftBlockData.fromData(getHandle().getBlock()); + } + + public boolean getDropItem() { + return getHandle().dropItem; + } + + public void setDropItem(boolean drop) { + getHandle().dropItem = drop; + } + + @Override + public boolean canHurtEntities() { + return getHandle().hurtEntities; + } + + @Override + public void setHurtEntities(boolean hurtEntities) { + getHandle().hurtEntities = hurtEntities; + } + + @Override + public void setTicksLived(int value) { + super.setTicksLived(value); + + // Second field for EntityFallingBlock + getHandle().ticksLived = value; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java new file mode 100644 index 000000000000..7703cc726bff --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java @@ -0,0 +1,75 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityFireball; +import net.minecraft.server.MathHelper; + +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Fireball; +import org.bukkit.projectiles.ProjectileSource; +import org.bukkit.util.Vector; + +public class CraftFireball extends AbstractProjectile implements Fireball { + public CraftFireball(CraftServer server, EntityFireball entity) { + super(server, entity); + } + + public float getYield() { + return getHandle().bukkitYield; + } + + public boolean isIncendiary() { + return getHandle().isIncendiary; + } + + public void setIsIncendiary(boolean isIncendiary) { + getHandle().isIncendiary = isIncendiary; + } + + public void setYield(float yield) { + getHandle().bukkitYield = yield; + } + + public ProjectileSource getShooter() { + return getHandle().projectileSource; + } + + public void setShooter(ProjectileSource shooter) { + if (shooter instanceof CraftLivingEntity) { + getHandle().shooter = ((CraftLivingEntity) shooter).getHandle(); + } else { + getHandle().shooter = null; + } + getHandle().projectileSource = shooter; + } + + public Vector getDirection() { + return new Vector(getHandle().dirX, getHandle().dirY, getHandle().dirZ); + } + + public void setDirection(Vector direction) { + Validate.notNull(direction, "Direction can not be null"); + double x = direction.getX(); + double y = direction.getY(); + double z = direction.getZ(); + double magnitude = (double) MathHelper.sqrt(x * x + y * y + z * z); + getHandle().dirX = x / magnitude; + getHandle().dirY = y / magnitude; + getHandle().dirZ = z / magnitude; + } + + @Override + public EntityFireball getHandle() { + return (EntityFireball) entity; + } + + @Override + public String toString() { + return "CraftFireball"; + } + + public EntityType getType() { + return EntityType.UNKNOWN; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java new file mode 100644 index 000000000000..b39e33f4f04d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java @@ -0,0 +1,90 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityFireworks; +import net.minecraft.server.EntityLiving; +import net.minecraft.server.ItemStack; +import net.minecraft.server.Items; + +import org.bukkit.Material; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Firework; +import org.bukkit.entity.LivingEntity; +import org.bukkit.inventory.meta.FireworkMeta; + +import java.util.Random; +import java.util.UUID; + +public class CraftFirework extends CraftEntity implements Firework { + + private final Random random = new Random(); + private final CraftItemStack item; + + public CraftFirework(CraftServer server, EntityFireworks entity) { + super(server, entity); + + ItemStack item = getHandle().getDataWatcher().get(EntityFireworks.FIREWORK_ITEM); + + if (item.isEmpty()) { + item = new ItemStack(Items.FIREWORK_ROCKET); + getHandle().getDataWatcher().set(EntityFireworks.FIREWORK_ITEM, item); + } + + this.item = CraftItemStack.asCraftMirror(item); + + // Ensure the item is a firework... + if (this.item.getType() != Material.FIREWORK_ROCKET) { + this.item.setType(Material.FIREWORK_ROCKET); + } + } + + @Override + public EntityFireworks getHandle() { + return (EntityFireworks) entity; + } + + @Override + public String toString() { + return "CraftFirework"; + } + + @Override + public EntityType getType() { + return EntityType.FIREWORK; + } + + @Override + public FireworkMeta getFireworkMeta() { + return (FireworkMeta) item.getItemMeta(); + } + + @Override + public void setFireworkMeta(FireworkMeta meta) { + item.setItemMeta(meta); + + // Copied from EntityFireworks constructor, update firework lifetime/power + getHandle().expectedLifespan = 10 * (1 + meta.getPower()) + random.nextInt(6) + random.nextInt(7); + + getHandle().getDataWatcher().markDirty(EntityFireworks.FIREWORK_ITEM); + } + + @Override + public void detonate() { + getHandle().expectedLifespan = 0; + } + + // Paper start + + @Override + public UUID getSpawningEntity() { + return getHandle().spawningEntity; + } + + @Override + public LivingEntity getBoostedEntity() { + EntityLiving boostedEntity = getHandle().getBoostedEntity(); + return boostedEntity != null ? (LivingEntity) boostedEntity.getBukkitEntity() : null; + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFish.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFish.java new file mode 100644 index 000000000000..6169119cf82f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFish.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityFish; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Fish; + +public class CraftFish extends CraftWaterMob implements Fish { + + public CraftFish(CraftServer server, EntityFish entity) { + super(server, entity); + } + + @Override + public EntityFish getHandle() { + return (EntityFish) entity; + } + + @Override + public String toString() { + return "CraftFish"; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java new file mode 100644 index 000000000000..752b56435dcf --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java @@ -0,0 +1,76 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.BlockPosition; +import net.minecraft.server.EntityFishingHook; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.MathHelper; + +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.FishHook; +import org.bukkit.projectiles.ProjectileSource; + +public class CraftFishHook extends AbstractProjectile implements FishHook { + private double biteChance = -1; + + public CraftFishHook(CraftServer server, EntityFishingHook entity) { + super(server, entity); + } + + public ProjectileSource getShooter() { + if (getHandle().owner != null) { + return getHandle().owner.getBukkitEntity(); + } + + return null; + } + + public void setShooter(ProjectileSource shooter) { + if (shooter instanceof CraftHumanEntity) { + getHandle().owner = (EntityHuman) ((CraftHumanEntity) shooter).entity; + } + } + + @Override + public EntityFishingHook getHandle() { + return (EntityFishingHook) entity; + } + + @Override + public String toString() { + return "CraftFishingHook"; + } + + public EntityType getType() { + return EntityType.FISHING_HOOK; + } + + public double getBiteChance() { + EntityFishingHook hook = getHandle(); + + if (this.biteChance == -1) { + if (hook.world.isRainingAt(new BlockPosition(MathHelper.floor(hook.locX), MathHelper.floor(hook.locY) + 1, MathHelper.floor(hook.locZ)))) { + return 1/300.0; + } + return 1/500.0; + } + return this.biteChance; + } + + public void setBiteChance(double chance) { + Validate.isTrue(chance >= 0 && chance <= 1, "The bite chance must be between 0 and 1."); + this.biteChance = chance; + } + + // Paper start + @Override + public void remove() { + super.remove(); + if (getHandle().owner != null) { + getHandle().owner.hookedFish = null; + } + } + // Paper end + +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFlying.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFlying.java new file mode 100644 index 000000000000..6b0c33b39177 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFlying.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityFlying; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Flying; + +public class CraftFlying extends CraftMob implements Flying { + + public CraftFlying(CraftServer server, EntityFlying entity) { + super(server, entity); + } + + @Override + public EntityFlying getHandle() { + return (EntityFlying) entity; + } + + @Override + public String toString() { + return "CraftFlying"; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftGhast.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftGhast.java new file mode 100644 index 000000000000..ee9516fc41b0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftGhast.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityGhast; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Ghast; + +public class CraftGhast extends CraftFlying implements Ghast { + + public CraftGhast(CraftServer server, EntityGhast entity) { + super(server, entity); + } + + @Override + public EntityGhast getHandle() { + return (EntityGhast) entity; + } + + @Override + public String toString() { + return "CraftGhast"; + } + + public EntityType getType() { + return EntityType.GHAST; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftGiant.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftGiant.java new file mode 100644 index 000000000000..e5609130d219 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftGiant.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityGiantZombie; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Giant; + +public class CraftGiant extends CraftMonster implements Giant { + + public CraftGiant(CraftServer server, EntityGiantZombie entity) { + super(server, entity); + } + + @Override + public EntityGiantZombie getHandle() { + return (EntityGiantZombie) entity; + } + + @Override + public String toString() { + return "CraftGiant"; + } + + public EntityType getType() { + return EntityType.GIANT; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftGolem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftGolem.java new file mode 100644 index 000000000000..1fef5e029188 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftGolem.java @@ -0,0 +1,21 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityGolem; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Golem; + +public class CraftGolem extends CraftCreature implements Golem { + public CraftGolem(CraftServer server, EntityGolem entity) { + super(server, entity); + } + + @Override + public EntityGolem getHandle() { + return (EntityGolem) entity; + } + + @Override + public String toString() { + return "CraftGolem"; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftGuardian.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftGuardian.java new file mode 100644 index 000000000000..fd4f774f088d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftGuardian.java @@ -0,0 +1,33 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityGuardian; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Guardian; + +public class CraftGuardian extends CraftMonster implements Guardian { + + public CraftGuardian(CraftServer server, EntityGuardian entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftGuardian"; + } + + @Override + public EntityType getType() { + return EntityType.GUARDIAN; + } + + @Override + public boolean isElder() { + return false; + } + + @Override + public void setElder(boolean shouldBeElder) { + throw new UnsupportedOperationException("Not supported."); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHanging.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHanging.java new file mode 100644 index 000000000000..d9a871a8cc1d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHanging.java @@ -0,0 +1,69 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityHanging; +import net.minecraft.server.EnumDirection; +import org.bukkit.block.BlockFace; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Hanging; + +public class CraftHanging extends CraftEntity implements Hanging { + public CraftHanging(CraftServer server, EntityHanging entity) { + super(server, entity); + } + + public BlockFace getAttachedFace() { + return getFacing().getOppositeFace(); + } + + public void setFacingDirection(BlockFace face) { + setFacingDirection(face, false); + } + + public boolean setFacingDirection(BlockFace face, boolean force) { + EntityHanging hanging = getHandle(); + EnumDirection dir = hanging.direction; + switch (face) { + case SOUTH: + default: + getHandle().setDirection(EnumDirection.SOUTH); + break; + case WEST: + getHandle().setDirection(EnumDirection.WEST); + break; + case NORTH: + getHandle().setDirection(EnumDirection.NORTH); + break; + case EAST: + getHandle().setDirection(EnumDirection.EAST); + break; + } + if (!force && !hanging.survives()) { + // Revert since it doesn't fit + hanging.setDirection(dir); + return false; + } + return true; + } + + public BlockFace getFacing() { + EnumDirection direction = this.getHandle().direction; + if (direction == null) return BlockFace.SELF; + return CraftBlock.notchToBlockFace(direction); + } + + @Override + public EntityHanging getHandle() { + return (EntityHanging) entity; + } + + @Override + public String toString() { + return "CraftHanging"; + } + + public EntityType getType() { + return EntityType.UNKNOWN; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHorse.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHorse.java new file mode 100644 index 000000000000..98ef44c302bc --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHorse.java @@ -0,0 +1,73 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityHorse; +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.inventory.CraftInventoryHorse; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Horse; +import org.bukkit.inventory.HorseInventory; + +public class CraftHorse extends CraftAbstractHorse implements Horse { + + public CraftHorse(CraftServer server, EntityHorse entity) { + super(server, entity); + } + + @Override + public EntityHorse getHandle() { + return (EntityHorse) super.getHandle(); + } + + @Override + public Variant getVariant() { + return Variant.HORSE; + } + + @Override + public Color getColor() { + return Color.values()[getHandle().getVariant() & 0xFF]; + } + + @Override + public void setColor(Color color) { + Validate.notNull(color, "Color cannot be null"); + getHandle().setVariant(color.ordinal() & 0xFF | getStyle().ordinal() << 8); + } + + @Override + public Style getStyle() { + return Style.values()[getHandle().getVariant() >>> 8]; + } + + @Override + public void setStyle(Style style) { + Validate.notNull(style, "Style cannot be null"); + getHandle().setVariant(getColor().ordinal() & 0xFF | style.ordinal() << 8); + } + + @Override + public boolean isCarryingChest() { + return false; + } + + @Override + public void setCarryingChest(boolean chest) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public HorseInventory getInventory() { + return new CraftInventoryHorse(getHandle().inventoryChest); + } + + @Override + public String toString() { + return "CraftHorse{variant=" + getVariant() + ", owner=" + getOwner() + '}'; + } + + @Override + public EntityType getType() { + return EntityType.HORSE; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java new file mode 100644 index 000000000000..a2bf6b04609c --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java @@ -0,0 +1,676 @@ +package org.bukkit.craftbukkit.entity; + +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Set; +import net.minecraft.server.BlockAnvil; +import net.minecraft.server.BlockBed; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.BlockWorkbench; +import net.minecraft.server.ChatComponentText; +import net.minecraft.server.Container; +import net.minecraft.server.CraftingManager; +import net.minecraft.server.Entity; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.EntityMinecartHopper; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.EntityTypes; +import net.minecraft.server.EnumMainHand; +import net.minecraft.server.IBlockData; +import net.minecraft.server.IInventory; +import net.minecraft.server.IMerchant; +import net.minecraft.server.IRecipe; +import net.minecraft.server.ITileEntityContainer; +import net.minecraft.server.ITileInventory; +import net.minecraft.server.ItemCooldown; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.PacketPlayInCloseWindow; +import net.minecraft.server.PacketPlayOutOpenWindow; +import net.minecraft.server.TileEntity; +import net.minecraft.server.TileEntityBeacon; +import net.minecraft.server.TileEntityBrewingStand; +import net.minecraft.server.TileEntityDispenser; +import net.minecraft.server.TileEntityDropper; +import net.minecraft.server.TileEntityEnchantTable; +import net.minecraft.server.TileEntityFurnace; +import net.minecraft.server.TileEntityHopper; +import net.minecraft.server.TileEntityShulkerBox; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.craftbukkit.inventory.CraftContainer; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftInventoryPlayer; +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.inventory.CraftMerchant; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Villager; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MainHand; +import org.bukkit.inventory.Merchant; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.permissions.PermissibleBase; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.plugin.Plugin; + +public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity { + private CraftInventoryPlayer inventory; + private final CraftInventory enderChest; + protected final PermissibleBase perm = new PermissibleBase(this); + private boolean op; + private GameMode mode; + + public CraftHumanEntity(final CraftServer server, final EntityHuman entity) { + super(server, entity); + mode = server.getDefaultGameMode(); + this.inventory = new CraftInventoryPlayer(entity.inventory); + enderChest = new CraftInventory(entity.getEnderChest()); + } + + public PlayerInventory getInventory() { + return inventory; + } + + public EntityEquipment getEquipment() { + return inventory; + } + + public Inventory getEnderChest() { + return enderChest; + } + + public MainHand getMainHand() { + return getHandle().getMainHand()== EnumMainHand.LEFT ? MainHand.LEFT : MainHand.RIGHT; + } + + public ItemStack getItemInHand() { + return getInventory().getItemInHand(); + } + + public void setItemInHand(ItemStack item) { + getInventory().setItemInHand(item); + } + + public ItemStack getItemOnCursor() { + return CraftItemStack.asCraftMirror(getHandle().inventory.getCarried()); + } + + public void setItemOnCursor(ItemStack item) { + net.minecraft.server.ItemStack stack = CraftItemStack.asNMSCopy(item); + getHandle().inventory.setCarried(stack); + if (this instanceof CraftPlayer) { + ((EntityPlayer) getHandle()).broadcastCarriedItem(); // Send set slot for cursor + } + } + + public boolean isSleeping() { + return getHandle().sleeping; + } + + public int getSleepTicks() { + return getHandle().sleepTicks; + } + + @Override + public Location getBedSpawnLocation() { + World world = getServer().getWorld(getHandle().spawnWorld); + BlockPosition bed = getHandle().getBed(); + + if (world != null && bed != null) { + bed = EntityHuman.getBed(((CraftWorld) world).getHandle(), bed, getHandle().isRespawnForced()); + if (bed != null) { + return new Location(world, bed.getX(), bed.getY(), bed.getZ()); + } + } + return null; + } + + @Override + public void setBedSpawnLocation(Location location) { + setBedSpawnLocation(location, false); + } + + @Override + public void setBedSpawnLocation(Location location, boolean override) { + if (location == null) { + getHandle().setRespawnPosition(null, override); + } else { + getHandle().setRespawnPosition(new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ()), override); + getHandle().spawnWorld = location.getWorld().getName(); + } + } + + @Override + public boolean sleep(Location location, boolean force) { + Preconditions.checkArgument(location != null, "Location == null"); + Preconditions.checkArgument(location.getWorld().equals(getWorld()), "Cannot sleep across worlds"); + + BlockPosition blockposition = new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + IBlockData iblockdata = getHandle().world.getType(blockposition); + if (!(iblockdata.getBlock() instanceof BlockBed)) { + return false; + } + + if (getHandle().a(blockposition) != EntityHuman.EnumBedResult.OK) { + return false; + } + + // From BlockBed + iblockdata = (IBlockData) iblockdata.set(BlockBed.OCCUPIED, true); + getHandle().world.setTypeAndData(blockposition, iblockdata, 4); + + return true; + } + + @Override + public void wakeup(boolean setSpawnLocation) { + Preconditions.checkState(isSleeping(), "Cannot wakeup if not sleeping"); + + getHandle().a(true, true, setSpawnLocation); + } + + @Override + public Location getBedLocation() { + Preconditions.checkState(isSleeping(), "Not sleeping"); + + return new Location(getWorld(), getHandle().bedPosition.getX(), getHandle().bedPosition.getY(), getHandle().bedPosition.getZ()); + } + + @Override + public String getName() { + return getHandle().getName(); + } + + public boolean isOp() { + return op; + } + + public boolean isPermissionSet(String name) { + return perm.isPermissionSet(name); + } + + public boolean isPermissionSet(Permission perm) { + return this.perm.isPermissionSet(perm); + } + + public boolean hasPermission(String name) { + return perm.hasPermission(name); + } + + public boolean hasPermission(Permission perm) { + return this.perm.hasPermission(perm); + } + + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { + return perm.addAttachment(plugin, name, value); + } + + public PermissionAttachment addAttachment(Plugin plugin) { + return perm.addAttachment(plugin); + } + + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { + return perm.addAttachment(plugin, name, value, ticks); + } + + public PermissionAttachment addAttachment(Plugin plugin, int ticks) { + return perm.addAttachment(plugin, ticks); + } + + public void removeAttachment(PermissionAttachment attachment) { + perm.removeAttachment(attachment); + } + + public void recalculatePermissions() { + perm.recalculatePermissions(); + } + + public void setOp(boolean value) { + this.op = value; + perm.recalculatePermissions(); + } + + public Set getEffectivePermissions() { + return perm.getEffectivePermissions(); + } + + public GameMode getGameMode() { + return mode; + } + + public void setGameMode(GameMode mode) { + if (mode == null) { + throw new IllegalArgumentException("Mode cannot be null"); + } + + this.mode = mode; + } + + @Override + public EntityHuman getHandle() { + return (EntityHuman) entity; + } + + public void setHandle(final EntityHuman entity) { + super.setHandle(entity); + this.inventory = new CraftInventoryPlayer(entity.inventory); + } + + @Override + public String toString() { + return "CraftHumanEntity{" + "id=" + getEntityId() + "name=" + getName() + '}'; + } + + public InventoryView getOpenInventory() { + return getHandle().activeContainer.getBukkitView(); + } + + public InventoryView openInventory(Inventory inventory) { + if(!(getHandle() instanceof EntityPlayer)) return null; + EntityPlayer player = (EntityPlayer) getHandle(); + InventoryType type = inventory.getType(); + Container formerContainer = getHandle().activeContainer; + + IInventory iinventory = (inventory instanceof CraftInventory) ? ((CraftInventory) inventory).getInventory() : new org.bukkit.craftbukkit.inventory.InventoryWrapper(inventory); + + if (iinventory instanceof ITileInventory) { + if (iinventory instanceof TileEntity) { + TileEntity te = (TileEntity) iinventory; + if (!te.hasWorld()) { + te.setWorld(getHandle().world); + } + } + } + + switch (type) { + case PLAYER: + case CHEST: + case ENDER_CHEST: + getHandle().openContainer(iinventory); + break; + case DISPENSER: + if (iinventory instanceof TileEntityDispenser) { + getHandle().openContainer((TileEntityDispenser) iinventory); + } else { + openCustomInventory(inventory, player, "minecraft:dispenser"); + } + break; + case DROPPER: + if (iinventory instanceof TileEntityDropper) { + getHandle().openContainer((TileEntityDropper) iinventory); + } else { + openCustomInventory(inventory, player, "minecraft:dropper"); + } + break; + case FURNACE: + if (iinventory instanceof TileEntityFurnace) { + getHandle().openContainer((TileEntityFurnace) iinventory); + } else { + openCustomInventory(inventory, player, "minecraft:furnace"); + } + break; + case WORKBENCH: + openCustomInventory(inventory, player, "minecraft:crafting_table"); + break; + case BREWING: + if (iinventory instanceof TileEntityBrewingStand) { + getHandle().openContainer((TileEntityBrewingStand) iinventory); + } else { + openCustomInventory(inventory, player, "minecraft:brewing_stand"); + } + break; + case ENCHANTING: + openCustomInventory(inventory, player, "minecraft:enchanting_table"); + break; + case HOPPER: + if (iinventory instanceof TileEntityHopper) { + getHandle().openContainer((TileEntityHopper) iinventory); + } else if (iinventory instanceof EntityMinecartHopper) { + getHandle().openContainer((EntityMinecartHopper) iinventory); + } else { + openCustomInventory(inventory, player, "minecraft:hopper"); + } + break; + case BEACON: + if (iinventory instanceof TileEntityBeacon) { + getHandle().openContainer((TileEntityBeacon) iinventory); + } else { + openCustomInventory(inventory, player, "minecraft:beacon"); + } + break; + case ANVIL: + if (iinventory instanceof BlockAnvil.TileEntityContainerAnvil) { + getHandle().openTileEntity((BlockAnvil.TileEntityContainerAnvil) iinventory); + } else { + openCustomInventory(inventory, player, "minecraft:anvil"); + } + break; + case SHULKER_BOX: + if (iinventory instanceof TileEntityShulkerBox) { + getHandle().openContainer((TileEntityShulkerBox) iinventory); + } else { + openCustomInventory(inventory, player, "minecraft:shulker_box"); + } + break; + case CREATIVE: + case CRAFTING: + case MERCHANT: + throw new IllegalArgumentException("Can't open a " + type + " inventory!"); + } + if (getHandle().activeContainer == formerContainer) { + return null; + } + getHandle().activeContainer.checkReachable = false; + return getHandle().activeContainer.getBukkitView(); + } + + private void openCustomInventory(Inventory inventory, EntityPlayer player, String windowType) { + if (player.playerConnection == null) return; + Container container = new CraftContainer(inventory, this.getHandle(), player.nextContainerCounter()); + + container = CraftEventFactory.callInventoryOpenEvent(player, container); + if(container == null) return; + + String title = container.getBukkitView().getTitle(); + int size = container.getBukkitView().getTopInventory().getSize(); + + // Special cases + if (windowType.equals("minecraft:crafting_table") + || windowType.equals("minecraft:anvil") + || windowType.equals("minecraft:enchanting_table") + ) { + size = 0; + } + + player.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, windowType, new ChatComponentText(title), size)); + getHandle().activeContainer = container; + getHandle().activeContainer.addSlotListener(player); + } + + public InventoryView openWorkbench(Location location, boolean force) { + if (!force) { + Block block = location.getBlock(); + if (block.getType() != Material.CRAFTING_TABLE) { + return null; + } + } + if (location == null) { + location = getLocation(); + } + getHandle().openTileEntity(new BlockWorkbench.TileEntityContainerWorkbench(getHandle().world, new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ()))); + if (force) { + getHandle().activeContainer.checkReachable = false; + } + return getHandle().activeContainer.getBukkitView(); + } + + public InventoryView openEnchanting(Location location, boolean force) { + if (!force) { + Block block = location.getBlock(); + if (block.getType() != Material.ENCHANTING_TABLE) { + return null; + } + } + if (location == null) { + location = getLocation(); + } + + // If there isn't an enchant table we can force create one, won't be very useful though. + BlockPosition pos = new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + TileEntity container = getHandle().world.getTileEntity(pos); + if (container == null && force) { + container = new TileEntityEnchantTable(); + container.setWorld(getHandle().world); + container.setPosition(pos); + } + getHandle().openTileEntity((ITileEntityContainer) container); + + if (force) { + getHandle().activeContainer.checkReachable = false; + } + return getHandle().activeContainer.getBukkitView(); + } + + public void openInventory(InventoryView inventory) { + if (!(getHandle() instanceof EntityPlayer)) return; // TODO: NPC support? + if (((EntityPlayer) getHandle()).playerConnection == null) return; + if (getHandle().activeContainer != getHandle().defaultContainer) { + // fire INVENTORY_CLOSE if one already open + ((EntityPlayer)getHandle()).playerConnection.a(new PacketPlayInCloseWindow(getHandle().activeContainer.windowId)); + } + EntityPlayer player = (EntityPlayer) getHandle(); + Container container; + if (inventory instanceof CraftInventoryView) { + container = ((CraftInventoryView) inventory).getHandle(); + } else { + container = new CraftContainer(inventory, this.getHandle(), player.nextContainerCounter()); + } + + // Trigger an INVENTORY_OPEN event + container = CraftEventFactory.callInventoryOpenEvent(player, container); + if (container == null) { + return; + } + + // Now open the window + InventoryType type = inventory.getType(); + String windowType = CraftContainer.getNotchInventoryType(type); + String title = inventory.getTitle(); + int size = inventory.getTopInventory().getSize(); + player.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, windowType, new ChatComponentText(title), size)); + player.activeContainer = container; + player.activeContainer.addSlotListener(player); + } + + @Override + public InventoryView openMerchant(Villager villager, boolean force) { + Preconditions.checkNotNull(villager, "villager cannot be null"); + + return this.openMerchant((Merchant) villager, force); + } + + @Override + public InventoryView openMerchant(Merchant merchant, boolean force) { + Preconditions.checkNotNull(merchant, "merchant cannot be null"); + + if (!force && merchant.isTrading()) { + return null; + } else if (merchant.isTrading()) { + // we're not supposed to have multiple people using the same merchant, so we have to close it. + merchant.getTrader().closeInventory(); + } + + IMerchant mcMerchant; + if (merchant instanceof CraftVillager) { + mcMerchant = ((CraftVillager) merchant).getHandle(); + } else if (merchant instanceof CraftMerchant) { + mcMerchant = ((CraftMerchant) merchant).getMerchant(); + } else { + throw new IllegalArgumentException("Can't open merchant " + merchant.toString()); + } + + mcMerchant.setTradingPlayer(this.getHandle()); + this.getHandle().openTrade(mcMerchant); + + return this.getHandle().activeContainer.getBukkitView(); + } + + public void closeInventory() { + // Paper start + getHandle().closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.PLUGIN); + } + public void closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) { + getHandle().closeInventory(reason); + } + // Paper end + + public boolean isBlocking() { + return getHandle().isBlocking(); + } + + @Override + public boolean isHandRaised() { + return getHandle().isHandRaised(); + } + + public boolean setWindowProperty(InventoryView.Property prop, int value) { + return false; + } + + public int getExpToLevel() { + return getHandle().getExpToLevel(); + } + + @Override + public boolean hasCooldown(Material material) { + Preconditions.checkArgument(material != null, "material"); + + return getHandle().getCooldownTracker().a(CraftMagicNumbers.getItem(material)); + } + + @Override + public int getCooldown(Material material) { + Preconditions.checkArgument(material != null, "material"); + + ItemCooldown.Info cooldown = getHandle().getCooldownTracker().cooldowns.get(CraftMagicNumbers.getItem(material)); + return (cooldown == null) ? 0 : Math.max(0, cooldown.endTick - getHandle().getCooldownTracker().currentTick); + } + + @Override + public void setCooldown(Material material, int ticks) { + Preconditions.checkArgument(material != null, "material"); + Preconditions.checkArgument(ticks >= 0, "Cannot have negative cooldown"); + + getHandle().getCooldownTracker().a(CraftMagicNumbers.getItem(material), ticks); + } + + // Paper start + @Override + public org.bukkit.entity.Entity releaseLeftShoulderEntity() { + if (!getHandle().getShoulderEntityLeft().isEmpty()) { + Entity entity = getHandle().releaseLeftShoulderEntity(); + if (entity != null) { + return entity.getBukkitEntity(); + } + } + + return null; + } + + @Override + public org.bukkit.entity.Entity releaseRightShoulderEntity() { + if (!getHandle().getShoulderEntityRight().isEmpty()) { + Entity entity = getHandle().releaseRightShoulderEntity(); + if (entity != null) { + return entity.getBukkitEntity(); + } + } + + return null; + } + // Paper end + + @Override + public boolean discoverRecipe(NamespacedKey recipe) { + return discoverRecipes(Arrays.asList(recipe)) != 0; + } + + @Override + public int discoverRecipes(Collection recipes) { + return getHandle().discoverRecipes(bukkitKeysToMinecraftRecipes(recipes)); + } + + @Override + public boolean undiscoverRecipe(NamespacedKey recipe) { + return undiscoverRecipes(Arrays.asList(recipe)) != 0; + } + + @Override + public int undiscoverRecipes(Collection recipes) { + return getHandle().undiscoverRecipes(bukkitKeysToMinecraftRecipes(recipes)); + } + + private Collection bukkitKeysToMinecraftRecipes(Collection recipeKeys) { + Collection recipes = new ArrayList<>(); + CraftingManager manager = getHandle().world.getMinecraftServer().getCraftingManager(); + + for (NamespacedKey recipeKey : recipeKeys) { + IRecipe recipe = manager.a(CraftNamespacedKey.toMinecraft(recipeKey)); + if (recipe == null) { + continue; + } + + recipes.add(recipe); + } + + return recipes; + } + + @Override + public org.bukkit.entity.Entity getShoulderEntityLeft() { + if (!getHandle().getShoulderEntityLeft().isEmpty()) { + Entity shoulder = EntityTypes.a(getHandle().getShoulderEntityLeft(), getHandle().world); + + return (shoulder == null) ? null : shoulder.getBukkitEntity(); + } + + return null; + } + + @Override + public void setShoulderEntityLeft(org.bukkit.entity.Entity entity) { + getHandle().setShoulderEntityLeft(entity == null ? new NBTTagCompound() : ((CraftEntity) entity).save()); + if (entity != null) { + entity.remove(); + } + } + + @Override + public org.bukkit.entity.Entity getShoulderEntityRight() { + if (!getHandle().getShoulderEntityRight().isEmpty()) { + Entity shoulder = EntityTypes.a(getHandle().getShoulderEntityRight(), getHandle().world); + + return (shoulder == null) ? null : shoulder.getBukkitEntity(); + } + + return null; + } + + @Override + public void setShoulderEntityRight(org.bukkit.entity.Entity entity) { + getHandle().setShoulderEntityRight(entity == null ? new NBTTagCompound() : ((CraftEntity) entity).save()); + if (entity != null) { + entity.remove(); + } + } + + // Paper start - Add method to open already placed sign + @Override + public void openSign(org.bukkit.block.Sign sign) { + org.apache.commons.lang.Validate.isTrue(sign.getWorld().equals(this.getWorld()), "Sign must be in the same world as player is in"); + org.bukkit.craftbukkit.block.CraftSign craftSign = (org.bukkit.craftbukkit.block.CraftSign) sign; + net.minecraft.server.TileEntitySign teSign = craftSign.getTileEntity(); + // Make sign editable temporarily, will be set back to false in PlayerConnection later + teSign.isEditable = true; + + getHandle().openSign(teSign); + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHusk.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHusk.java new file mode 100644 index 000000000000..46425820b235 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHusk.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityZombieHusk; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Husk; + +public class CraftHusk extends CraftZombie implements Husk { + + public CraftHusk(CraftServer server, EntityZombieHusk entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftHusk"; + } + + @Override + public EntityType getType() { + return EntityType.HUSK; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftIllager.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftIllager.java new file mode 100644 index 000000000000..824af57808e6 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftIllager.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityIllagerAbstract; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Illager; + +public class CraftIllager extends CraftMonster implements Illager { + + public CraftIllager(CraftServer server, EntityIllagerAbstract entity) { + super(server, entity); + } + + @Override + public EntityIllagerAbstract getHandle() { + return (EntityIllagerAbstract) super.getHandle(); + } + + @Override + public String toString() { + return "CraftIllager"; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftIllusioner.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftIllusioner.java new file mode 100644 index 000000000000..f31d3eed3a53 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftIllusioner.java @@ -0,0 +1,29 @@ +package org.bukkit.craftbukkit.entity; + +import com.destroystokyo.paper.entity.CraftRangedEntity; +import net.minecraft.server.EntityIllagerIllusioner; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Illusioner; + +public class CraftIllusioner extends CraftSpellcaster implements Illusioner, CraftRangedEntity { // Paper + + public CraftIllusioner(CraftServer server, EntityIllagerIllusioner entity) { + super(server, entity); + } + + @Override + public EntityIllagerIllusioner getHandle() { + return (EntityIllagerIllusioner) super.getHandle(); + } + + @Override + public String toString() { + return "CraftIllusioner"; + } + + @Override + public EntityType getType() { + return EntityType.ILLUSIONER; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java new file mode 100644 index 000000000000..ddee207eb7cf --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftIronGolem.java @@ -0,0 +1,35 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityIronGolem; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.IronGolem; + +public class CraftIronGolem extends CraftGolem implements IronGolem { + public CraftIronGolem(CraftServer server, EntityIronGolem entity) { + super(server, entity); + } + + @Override + public EntityIronGolem getHandle() { + return (EntityIronGolem) entity; + } + + @Override + public String toString() { + return "CraftIronGolem"; + } + + public boolean isPlayerCreated() { + return getHandle().isPlayerCreated(); + } + + public void setPlayerCreated(boolean playerCreated) { + getHandle().setPlayerCreated(playerCreated); + } + + @Override + public EntityType getType() { + return EntityType.IRON_GOLEM; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java new file mode 100644 index 000000000000..4128ba4c0675 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java @@ -0,0 +1,89 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.Entity; +import net.minecraft.server.EntityItem; + +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Item; +import org.bukkit.inventory.ItemStack; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.CraftServer; + +// Paper start +import javax.annotation.Nullable; +import java.util.UUID; +// Paper end + +public class CraftItem extends CraftEntity implements Item { + private final EntityItem item; + + public CraftItem(CraftServer server, Entity entity, EntityItem item) { + super(server, entity); + this.item = item; + } + + public CraftItem(CraftServer server, EntityItem entity) { + this(server, entity, entity); + } + + public ItemStack getItemStack() { + return CraftItemStack.asCraftMirror(item.getItemStack()); + } + + public void setItemStack(ItemStack stack) { + item.setItemStack(CraftItemStack.asNMSCopy(stack)); + } + + public int getPickupDelay() { + return item.pickupDelay; + } + + public void setPickupDelay(int delay) { + item.pickupDelay = Math.min(delay, Short.MAX_VALUE); + } + + @Override + public void setTicksLived(int value) { + super.setTicksLived(value); + + // Second field for EntityItem + item.age = value; + } + + // Paper Start + public boolean canMobPickup() { + return item.canMobPickup; + } + + public void setCanMobPickup(boolean canMobPickup) { + item.canMobPickup = canMobPickup; + } + + @Nullable + public UUID getOwner() { + return item.getOwner(); + } + + public void setOwner(@Nullable UUID owner) { + item.setOwner(owner); + } + + @Nullable + public UUID getThrower() { + return item.getThrower(); + } + + public void setThrower(@Nullable UUID thrower) { + item.setThrower(thrower); + } + // Paper End + + @Override + public String toString() { + return "CraftItem"; + } + + public EntityType getType() { + return EntityType.DROPPED_ITEM; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java new file mode 100644 index 000000000000..227a9ffa0351 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java @@ -0,0 +1,141 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.BlockPosition; +import net.minecraft.server.EntityHanging; +import net.minecraft.server.EntityItemFrame; +import net.minecraft.server.EnumDirection; +import net.minecraft.server.ItemStack; +import net.minecraft.server.WorldServer; + +import org.apache.commons.lang.Validate; + +import org.bukkit.Rotation; +import org.bukkit.block.BlockFace; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.ItemFrame; + +public class CraftItemFrame extends CraftHanging implements ItemFrame { + public CraftItemFrame(CraftServer server, EntityItemFrame entity) { + super(server, entity); + } + + @Override + public boolean setFacingDirection(BlockFace face, boolean force) { + EntityHanging hanging = getHandle(); + EnumDirection oldDir = hanging.direction; + EnumDirection newDir = CraftBlock.blockFaceToNotch(face); + + getHandle().setDirection(newDir); + if (!force && !hanging.survives()) { + hanging.setDirection(oldDir); + return false; + } + + update(); + + return true; + } + + private void update() { + EntityItemFrame old = this.getHandle(); + + WorldServer world = ((CraftWorld) getWorld()).getHandle(); + BlockPosition position = old.getBlockPosition(); + EnumDirection direction = old.getDirection(); + ItemStack item = old.getItem() != null ? old.getItem().cloneItemStack() : null; + + old.die(); + + EntityItemFrame frame = new EntityItemFrame(world,position,direction); + frame.setItem(item); + world.addEntity(frame); + this.entity = frame; + } + + public void setItem(org.bukkit.inventory.ItemStack item) { + setItem(item, true); + } + + public void setItem(org.bukkit.inventory.ItemStack item, boolean playSound) { + getHandle().setItem(CraftItemStack.asNMSCopy(item), true, playSound); + } + + public org.bukkit.inventory.ItemStack getItem() { + return CraftItemStack.asBukkitCopy(getHandle().getItem()); + } + + public Rotation getRotation() { + return toBukkitRotation(getHandle().getRotation()); + } + + Rotation toBukkitRotation(int value) { + // Translate NMS rotation integer to Bukkit API + switch (value) { + case 0: + return Rotation.NONE; + case 1: + return Rotation.CLOCKWISE_45; + case 2: + return Rotation.CLOCKWISE; + case 3: + return Rotation.CLOCKWISE_135; + case 4: + return Rotation.FLIPPED; + case 5: + return Rotation.FLIPPED_45; + case 6: + return Rotation.COUNTER_CLOCKWISE; + case 7: + return Rotation.COUNTER_CLOCKWISE_45; + default: + throw new AssertionError("Unknown rotation " + value + " for " + getHandle()); + } + } + + public void setRotation(Rotation rotation) { + Validate.notNull(rotation, "Rotation cannot be null"); + getHandle().setRotation(toInteger(rotation)); + } + + static int toInteger(Rotation rotation) { + // Translate Bukkit API rotation to NMS integer + switch (rotation) { + case NONE: + return 0; + case CLOCKWISE_45: + return 1; + case CLOCKWISE: + return 2; + case CLOCKWISE_135: + return 3; + case FLIPPED: + return 4; + case FLIPPED_45: + return 5; + case COUNTER_CLOCKWISE: + return 6; + case COUNTER_CLOCKWISE_45: + return 7; + default: + throw new IllegalArgumentException(rotation + " is not applicable to an ItemFrame"); + } + } + + @Override + public EntityItemFrame getHandle() { + return (EntityItemFrame) entity; + } + + @Override + public String toString() { + return "CraftItemFrame{item=" + getItem() + ", rotation=" + getRotation() + "}"; + } + + public EntityType getType() { + return EntityType.ITEM_FRAME; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLargeFireball.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLargeFireball.java new file mode 100644 index 000000000000..ca0379403fdf --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLargeFireball.java @@ -0,0 +1,32 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityLargeFireball; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LargeFireball; + +public class CraftLargeFireball extends CraftFireball implements LargeFireball { + public CraftLargeFireball(CraftServer server, EntityLargeFireball entity) { + super(server, entity); + } + + @Override + public void setYield(float yield) { + super.setYield(yield); + getHandle().yield = (int) yield; + } + + @Override + public EntityLargeFireball getHandle() { + return (EntityLargeFireball) entity; + } + + @Override + public String toString() { + return "CraftLargeFireball"; + } + + public EntityType getType() { + return EntityType.FIREBALL; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLeash.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLeash.java new file mode 100644 index 000000000000..710ed7a646ef --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLeash.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityLeash; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LeashHitch; + +public class CraftLeash extends CraftHanging implements LeashHitch { + public CraftLeash(CraftServer server, EntityLeash entity) { + super(server, entity); + } + + @Override + public EntityLeash getHandle() { + return (EntityLeash) entity; + } + + @Override + public String toString() { + return "CraftLeash"; + } + + public EntityType getType() { + return EntityType.LEASH_HITCH; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLightningStrike.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLightningStrike.java new file mode 100644 index 000000000000..ddd5ccd58065 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLightningStrike.java @@ -0,0 +1,46 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityLightning; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LightningStrike; + +public class CraftLightningStrike extends CraftEntity implements LightningStrike { + public CraftLightningStrike(final CraftServer server, final EntityLightning entity) { + super(server, entity); + } + + public boolean isEffect() { + return ((EntityLightning) super.getHandle()).isEffect; + } + + @Override + public EntityLightning getHandle() { + return (EntityLightning) entity; + } + + @Override + public String toString() { + return "CraftLightningStrike"; + } + + public EntityType getType() { + return EntityType.LIGHTNING; + } + + // Spigot start + private final LightningStrike.Spigot spigot = new LightningStrike.Spigot() { + + @Override + public boolean isSilent() + { + return getHandle().isSilent; + } + }; + + @Override + public LightningStrike.Spigot spigot() { + return spigot; + } + // Spigot end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLingeringPotion.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLingeringPotion.java new file mode 100644 index 000000000000..2a5482ff8960 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLingeringPotion.java @@ -0,0 +1,42 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityPotion; +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LingeringPotion; +import org.bukkit.inventory.ItemStack; + +public class CraftLingeringPotion extends CraftThrownPotion implements LingeringPotion { + + public CraftLingeringPotion(CraftServer server, EntityPotion entity) { + super(server, entity); + } + + public void setItem(ItemStack item) { + // The ItemStack must not be null. + Validate.notNull(item, "ItemStack cannot be null."); + + // The ItemStack must be a potion. + Validate.isTrue(item.getType() == Material.LINGERING_POTION, "ItemStack must be a lingering potion. This item stack was " + item.getType() + "."); + + getHandle().setItem(CraftItemStack.asNMSCopy(item)); + } + + @Override + public EntityPotion getHandle() { + return (EntityPotion) entity; + } + + @Override + public String toString() { + return "CraftLingeringPotion"; + } + + @Override + public EntityType getType() { + return EntityType.LINGERING_POTION; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java new file mode 100644 index 000000000000..d6a4bc64ae21 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java @@ -0,0 +1,636 @@ +package org.bukkit.craftbukkit.entity; + +import com.google.common.collect.Sets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import net.minecraft.server.DamageSource; +import net.minecraft.server.EntityArmorStand; +import net.minecraft.server.EntityArrow; +import net.minecraft.server.EntityDragonFireball; +import net.minecraft.server.EntityEgg; +import net.minecraft.server.EntityEnderPearl; +import net.minecraft.server.EntityFishingHook; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.EntityFireball; +import net.minecraft.server.EntityInsentient; +import net.minecraft.server.EntityLargeFireball; +import net.minecraft.server.EntityLiving; +import net.minecraft.server.EntityLlamaSpit; +import net.minecraft.server.EntityPotion; +import net.minecraft.server.EntityProjectile; +import net.minecraft.server.EntityShulkerBullet; +import net.minecraft.server.EntitySmallFireball; +import net.minecraft.server.EntitySnowball; +import net.minecraft.server.EntityThrownExpBottle; +import net.minecraft.server.EntityTippedArrow; +import net.minecraft.server.EntitySpectralArrow; +import net.minecraft.server.EntityThrownTrident; +import net.minecraft.server.EntityWither; +import net.minecraft.server.EntityWitherSkull; +import net.minecraft.server.GenericAttributes; +import net.minecraft.server.MobEffect; +import net.minecraft.server.MobEffectList; + +import org.apache.commons.lang.Validate; +import org.bukkit.FluidCollisionMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeInstance; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.inventory.CraftEntityEquipment; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.potion.CraftPotionUtil; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.DragonFireball; +import org.bukkit.entity.Egg; +import org.bukkit.entity.EnderPearl; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Fireball; +import org.bukkit.entity.FishHook; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.LingeringPotion; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.LlamaSpit; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.ShulkerBullet; +import org.bukkit.entity.SmallFireball; +import org.bukkit.entity.Snowball; +import org.bukkit.entity.SpectralArrow; +import org.bukkit.entity.ThrownExpBottle; +import org.bukkit.entity.ThrownPotion; +import org.bukkit.entity.TippedArrow; +import org.bukkit.entity.Trident; +import org.bukkit.entity.WitherSkull; +import org.bukkit.event.entity.EntityPotionEffectEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionData; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.potion.PotionType; +import org.bukkit.util.BlockIterator; +import org.bukkit.util.RayTraceResult; +import org.bukkit.util.Vector; + +public class CraftLivingEntity extends CraftEntity implements LivingEntity { + private CraftEntityEquipment equipment; + + public CraftLivingEntity(final CraftServer server, final EntityLiving entity) { + super(server, entity); + + if (entity instanceof EntityInsentient || entity instanceof EntityArmorStand) { + equipment = new CraftEntityEquipment(this); + } + } + + public double getHealth() { + return Math.min(Math.max(0, getHandle().getHealth()), getMaxHealth()); + } + + public void setHealth(double health) { + health = (float) health; + if ((health < 0) || (health > getMaxHealth())) { + // Paper - Be more informative + throw new IllegalArgumentException("Health must be between 0 and " + getMaxHealth() + ", but was " + health + + ". (attribute base value: " + this.getHandle().getAttributeInstance(GenericAttributes.maxHealth).b() + + (this instanceof CraftPlayer ? ", player: " + this.getName() + ')' : ')')); + } + + getHandle().setHealth((float) health); + + if (health == 0) { + getHandle().die(DamageSource.GENERIC); + } + } + + public double getMaxHealth() { + return getHandle().getMaxHealth(); + } + + public void setMaxHealth(double amount) { + Validate.isTrue(amount > 0, "Max health must be greater than 0"); + + getHandle().getAttributeInstance(GenericAttributes.maxHealth).setValue(amount); + + if (getHealth() > amount) { + setHealth(amount); + } + } + + public void resetMaxHealth() { + setMaxHealth(getHandle().getAttributeInstance(GenericAttributes.maxHealth).getAttribute().getDefault()); + } + + public double getEyeHeight() { + return getHandle().getHeadHeight(); + } + + public double getEyeHeight(boolean ignorePose) { + return getEyeHeight(); + } + + private List getLineOfSight(Set transparent, int maxDistance, int maxLength) { + if (transparent == null) { + transparent = Sets.newHashSet(Material.AIR, Material.CAVE_AIR, Material.VOID_AIR); + } + if (maxDistance > 120) { + maxDistance = 120; + } + ArrayList blocks = new ArrayList(); + Iterator itr = new BlockIterator(this, maxDistance); + while (itr.hasNext()) { + Block block = itr.next(); + blocks.add(block); + if (maxLength != 0 && blocks.size() > maxLength) { + blocks.remove(0); + } + Material material = block.getType(); + if (!transparent.contains(material)) { + break; + } + } + return blocks; + } + + public List getLineOfSight(Set transparent, int maxDistance) { + return getLineOfSight(transparent, maxDistance, 0); + } + + public Block getTargetBlock(Set transparent, int maxDistance) { + List blocks = getLineOfSight(transparent, maxDistance, 1); + return blocks.get(0); + } + + // Paper start + public Block getTargetBlock(int maxDistance, com.destroystokyo.paper.block.TargetBlockInfo.FluidMode fluidMode) { + net.minecraft.server.MovingObjectPosition rayTrace = getHandle().getRayTrace(maxDistance, net.minecraft.server.MCUtil.getNMSFluidCollisionOption(fluidMode)); + return rayTrace == null ? null : org.bukkit.craftbukkit.block.CraftBlock.at(getHandle().world, rayTrace.getBlockPosition()); + } + + public org.bukkit.block.BlockFace getTargetBlockFace(int maxDistance, com.destroystokyo.paper.block.TargetBlockInfo.FluidMode fluidMode) { + net.minecraft.server.MovingObjectPosition rayTrace = getHandle().getRayTrace(maxDistance, net.minecraft.server.MCUtil.getNMSFluidCollisionOption(fluidMode)); + return rayTrace == null ? null : net.minecraft.server.MCUtil.toBukkitBlockFace(rayTrace.direction); + } + + public com.destroystokyo.paper.block.TargetBlockInfo getTargetBlockInfo(int maxDistance, com.destroystokyo.paper.block.TargetBlockInfo.FluidMode fluidMode) { + net.minecraft.server.MovingObjectPosition rayTrace = getHandle().getRayTrace(maxDistance, net.minecraft.server.MCUtil.getNMSFluidCollisionOption(fluidMode)); + return rayTrace == null ? null : new com.destroystokyo.paper.block.TargetBlockInfo(org.bukkit.craftbukkit.block.CraftBlock.at(getHandle().world, rayTrace.getBlockPosition()), net.minecraft.server.MCUtil.toBukkitBlockFace(rayTrace.direction)); + } + + public Entity getTargetEntity(int maxDistance, boolean ignoreBlocks) { + net.minecraft.server.MovingObjectPosition rayTrace = rayTraceEntity(maxDistance, ignoreBlocks); + return rayTrace == null ? null : rayTrace.entity.getBukkitEntity(); + } + + public com.destroystokyo.paper.entity.TargetEntityInfo getTargetEntityInfo(int maxDistance, boolean ignoreBlocks) { + net.minecraft.server.MovingObjectPosition rayTrace = rayTraceEntity(maxDistance, ignoreBlocks); + return rayTrace == null ? null : new com.destroystokyo.paper.entity.TargetEntityInfo(rayTrace.entity.getBukkitEntity(), new org.bukkit.util.Vector(rayTrace.pos.x, rayTrace.pos.y, rayTrace.pos.z)); + } + + public net.minecraft.server.MovingObjectPosition rayTraceEntity(int maxDistance, boolean ignoreBlocks) { + net.minecraft.server.MovingObjectPosition rayTrace = getHandle().getTargetEntity(maxDistance); + if (rayTrace == null) { + return null; + } + if (!ignoreBlocks) { + net.minecraft.server.MovingObjectPosition rayTraceBlocks = getHandle().getRayTrace(maxDistance, net.minecraft.server.FluidCollisionOption.NEVER); + if (rayTraceBlocks != null) { + net.minecraft.server.Vec3D eye = getHandle().getEyePosition(1.0F); + if (eye.distanceSquared(rayTraceBlocks.pos) <= eye.distanceSquared(rayTrace.pos)) { + return null; + } + } + } + return rayTrace; + } + // Paper end + + public List getLastTwoTargetBlocks(Set transparent, int maxDistance) { + return getLineOfSight(transparent, maxDistance, 2); + } + + @Override + public Block getTargetBlockExact(int maxDistance) { + return this.getTargetBlockExact(maxDistance, FluidCollisionMode.NEVER); + } + + @Override + public Block getTargetBlockExact(int maxDistance, FluidCollisionMode fluidCollisionMode) { + RayTraceResult hitResult = this.rayTraceBlocks(maxDistance, fluidCollisionMode); + return (hitResult != null ? hitResult.getHitBlock() : null); + } + + @Override + public RayTraceResult rayTraceBlocks(double maxDistance) { + return this.rayTraceBlocks(maxDistance, FluidCollisionMode.NEVER); + } + + @Override + public RayTraceResult rayTraceBlocks(double maxDistance, FluidCollisionMode fluidCollisionMode) { + Location eyeLocation = this.getEyeLocation(); + Vector direction = eyeLocation.getDirection(); + return this.getWorld().rayTraceBlocks(eyeLocation, direction, maxDistance, fluidCollisionMode, false); + } + + public int getRemainingAir() { + return getHandle().getAirTicks(); + } + + public void setRemainingAir(int ticks) { + getHandle().setAirTicks(ticks); + } + + public int getMaximumAir() { + return getHandle().maxAirTicks; + } + + public void setMaximumAir(int ticks) { + getHandle().maxAirTicks = ticks; + } + + public void damage(double amount) { + damage(amount, null); + } + + public void damage(double amount, org.bukkit.entity.Entity source) { + DamageSource reason = DamageSource.GENERIC; + + if (source instanceof HumanEntity) { + reason = DamageSource.playerAttack(((CraftHumanEntity) source).getHandle()); + } else if (source instanceof LivingEntity) { + reason = DamageSource.mobAttack(((CraftLivingEntity) source).getHandle()); + } + + entity.damageEntity(reason, (float) amount); + } + + public Location getEyeLocation() { + Location loc = getLocation(); + loc.setY(loc.getY() + getEyeHeight()); + return loc; + } + + public int getMaximumNoDamageTicks() { + return getHandle().maxNoDamageTicks; + } + + public void setMaximumNoDamageTicks(int ticks) { + getHandle().maxNoDamageTicks = ticks; + } + + public double getLastDamage() { + return getHandle().lastDamage; + } + + public void setLastDamage(double damage) { + getHandle().lastDamage = (float) damage; + } + + public int getNoDamageTicks() { + return getHandle().noDamageTicks; + } + + public void setNoDamageTicks(int ticks) { + getHandle().noDamageTicks = ticks; + } + + @Override + public EntityLiving getHandle() { + return (EntityLiving) entity; + } + + public void setHandle(final EntityLiving entity) { + super.setHandle(entity); + } + + @Override + public String toString() { + return "CraftLivingEntity{" + "id=" + getEntityId() + '}'; + } + + public Player getKiller() { + return getHandle().killer == null ? null : (Player) getHandle().killer.getBukkitEntity(); + } + + // Paper start + @Override + public void setKiller(Player killer) { + net.minecraft.server.EntityPlayer entityPlayer = killer == null ? null : ((CraftPlayer) killer).getHandle(); + getHandle().killer = entityPlayer; + getHandle().lastDamager = entityPlayer; + getHandle().lastDamageByPlayerTime = entityPlayer == null ? 0 : 100; // 100 value taken from EntityLiving#damageEntity + } + // Paper end + + public boolean addPotionEffect(PotionEffect effect) { + return addPotionEffect(effect, false); + } + + public boolean addPotionEffect(PotionEffect effect, boolean force) { + if (hasPotionEffect(effect.getType())) { + if (!force) { + return false; + } + removePotionEffect(effect.getType()); + } + getHandle().addEffect(new MobEffect(MobEffectList.fromId(effect.getType().getId()), effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles()), EntityPotionEffectEvent.Cause.PLUGIN); + return true; + } + + public boolean addPotionEffects(Collection effects) { + boolean success = true; + for (PotionEffect effect : effects) { + success &= addPotionEffect(effect); + } + return success; + } + + public boolean hasPotionEffect(PotionEffectType type) { + return getHandle().hasEffect(MobEffectList.fromId(type.getId())); + } + + @Override + public PotionEffect getPotionEffect(PotionEffectType type) { + MobEffect handle = getHandle().getEffect(MobEffectList.fromId(type.getId())); + return (handle == null) ? null : new PotionEffect(PotionEffectType.getById(MobEffectList.getId(handle.getMobEffect())), handle.getDuration(), handle.getAmplifier(), handle.isAmbient(), handle.isShowParticles()); + } + + public void removePotionEffect(PotionEffectType type) { + getHandle().removeEffect(MobEffectList.fromId(type.getId()), EntityPotionEffectEvent.Cause.PLUGIN); + } + + public Collection getActivePotionEffects() { + List effects = new ArrayList(); + for (MobEffect handle : getHandle().effects.values()) { + effects.add(new PotionEffect(PotionEffectType.getById(MobEffectList.getId(handle.getMobEffect())), handle.getDuration(), handle.getAmplifier(), handle.isAmbient(), handle.isShowParticles())); + } + return effects; + } + + public T launchProjectile(Class projectile) { + return launchProjectile(projectile, null); + } + + @SuppressWarnings("unchecked") + public T launchProjectile(Class projectile, Vector velocity) { + net.minecraft.server.World world = ((CraftWorld) getWorld()).getHandle(); + net.minecraft.server.Entity launch = null; + + if (Snowball.class.isAssignableFrom(projectile)) { + launch = new EntitySnowball(world, getHandle()); + ((EntityProjectile) launch).a(getHandle(), getHandle().pitch, getHandle().yaw, 0.0F, 1.5F, 1.0F); // ItemSnowball + } else if (Egg.class.isAssignableFrom(projectile)) { + launch = new EntityEgg(world, getHandle()); + ((EntityProjectile) launch).a(getHandle(), getHandle().pitch, getHandle().yaw, 0.0F, 1.5F, 1.0F); // ItemEgg + } else if (EnderPearl.class.isAssignableFrom(projectile)) { + launch = new EntityEnderPearl(world, getHandle()); + ((EntityProjectile) launch).a(getHandle(), getHandle().pitch, getHandle().yaw, 0.0F, 1.5F, 1.0F); // ItemEnderPearl + } else if (Arrow.class.isAssignableFrom(projectile)) { + if (TippedArrow.class.isAssignableFrom(projectile)) { + launch = new EntityTippedArrow(world, getHandle()); + ((EntityTippedArrow) launch).setType(CraftPotionUtil.fromBukkit(new PotionData(PotionType.WATER, false, false))); + } else if (SpectralArrow.class.isAssignableFrom(projectile)) { + launch = new EntitySpectralArrow(world, getHandle()); + } else if (Trident.class.isAssignableFrom(projectile)) { + launch = new EntityThrownTrident(world, getHandle(), new net.minecraft.server.ItemStack(net.minecraft.server.Items.TRIDENT)); + } else { + launch = new EntityTippedArrow(world, getHandle()); + } + ((EntityArrow) launch).a(getHandle(), getHandle().pitch, getHandle().yaw, 0.0F, 3.0F, 1.0F); // ItemBow + } else if (ThrownPotion.class.isAssignableFrom(projectile)) { + if (LingeringPotion.class.isAssignableFrom(projectile)) { + launch = new EntityPotion(world, getHandle(), CraftItemStack.asNMSCopy(new ItemStack(org.bukkit.Material.LINGERING_POTION, 1))); + } else { + launch = new EntityPotion(world, getHandle(), CraftItemStack.asNMSCopy(new ItemStack(org.bukkit.Material.SPLASH_POTION, 1))); + } + ((EntityProjectile) launch).a(getHandle(), getHandle().pitch, getHandle().yaw, -20.0F, 0.5F, 1.0F); // ItemSplashPotion + } else if (ThrownExpBottle.class.isAssignableFrom(projectile)) { + launch = new EntityThrownExpBottle(world, getHandle()); + ((EntityProjectile) launch).a(getHandle(), getHandle().pitch, getHandle().yaw, -20.0F, 0.7F, 1.0F); // ItemExpBottle + } else if (FishHook.class.isAssignableFrom(projectile) && getHandle() instanceof EntityHuman) { + launch = new EntityFishingHook(world, (EntityHuman) getHandle()); + } else if (Fireball.class.isAssignableFrom(projectile)) { + Location location = getEyeLocation(); + Vector direction = location.getDirection().multiply(10); + + if (SmallFireball.class.isAssignableFrom(projectile)) { + launch = new EntitySmallFireball(world, getHandle(), direction.getX(), direction.getY(), direction.getZ()); + } else if (WitherSkull.class.isAssignableFrom(projectile)) { + launch = new EntityWitherSkull(world, getHandle(), direction.getX(), direction.getY(), direction.getZ()); + } else if (DragonFireball.class.isAssignableFrom(projectile)) { + launch = new EntityDragonFireball(world, getHandle(), direction.getX(), direction.getY(), direction.getZ()); + } else { + launch = new EntityLargeFireball(world, getHandle(), direction.getX(), direction.getY(), direction.getZ()); + } + + ((EntityFireball) launch).projectileSource = this; + launch.setPositionRotation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + } else if (LlamaSpit.class.isAssignableFrom(projectile)) { + Location location = getEyeLocation(); + Vector direction = location.getDirection(); + + launch = new EntityLlamaSpit(world); + + ((EntityLlamaSpit) launch).shooter = getHandle(); + ((EntityLlamaSpit) launch).shoot(direction.getX(), direction.getY(), direction.getZ(), 1.5F, 10.0F); // EntityLlama + launch.setPositionRotation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + } else if (ShulkerBullet.class.isAssignableFrom(projectile)) { + Location location = getEyeLocation(); + + launch = new EntityShulkerBullet(world, getHandle(), null, null); + launch.setPositionRotation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + } + + Validate.notNull(launch, "Projectile not supported"); + + if (velocity != null) { + ((T) launch.getBukkitEntity()).setVelocity(velocity); + } + + world.addEntity(launch); + return (T) launch.getBukkitEntity(); + } + + public EntityType getType() { + return EntityType.UNKNOWN; + } + + public boolean hasLineOfSight(Entity other) { + return getHandle().hasLineOfSight(((CraftEntity) other).getHandle()); + } + + public boolean getRemoveWhenFarAway() { + return getHandle() instanceof EntityInsentient && !((EntityInsentient) getHandle()).persistent; + } + + public void setRemoveWhenFarAway(boolean remove) { + if (getHandle() instanceof EntityInsentient) { + ((EntityInsentient) getHandle()).persistent = !remove; + } + } + + public EntityEquipment getEquipment() { + return equipment; + } + + public void setCanPickupItems(boolean pickup) { + getHandle().canPickUpLoot = pickup; + } + + public boolean getCanPickupItems() { + return getHandle().canPickUpLoot; + } + + @Override + public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) { + if (getHealth() == 0) { + return false; + } + + return super.teleport(location, cause); + } + + public boolean isLeashed() { + if (!(getHandle() instanceof EntityInsentient)) { + return false; + } + return ((EntityInsentient) getHandle()).getLeashHolder() != null; + } + + public Entity getLeashHolder() throws IllegalStateException { + if (!isLeashed()) { + throw new IllegalStateException("Entity not leashed"); + } + return ((EntityInsentient) getHandle()).getLeashHolder().getBukkitEntity(); + } + + private boolean unleash() { + if (!isLeashed()) { + return false; + } + ((EntityInsentient) getHandle()).unleash(true, false); + return true; + } + + public boolean setLeashHolder(Entity holder) { + if ((getHandle() instanceof EntityWither) || !(getHandle() instanceof EntityInsentient)) { + return false; + } + + if (holder == null) { + return unleash(); + } + + if (holder.isDead()) { + return false; + } + + unleash(); + ((EntityInsentient) getHandle()).setLeashHolder(((CraftEntity) holder).getHandle(), true); + return true; + } + + @Override + public boolean isGliding() { + return getHandle().getFlag(7); + } + + @Override + public void setGliding(boolean gliding) { + getHandle().setFlag(7, gliding); + } + + @Override + public boolean isSwimming() { + return getHandle().isSwimming(); + } + + @Override + public void setSwimming(boolean swimming) { + getHandle().setSwimming(swimming); + } + + @Override + public boolean isRiptiding() { + return getHandle().isRiptiding(); + } + + @Override + public AttributeInstance getAttribute(Attribute attribute) { + return getHandle().craftAttributes.getAttribute(attribute); + } + + @Override + public void setAI(boolean ai) { + if (this.getHandle() instanceof EntityInsentient) { + ((EntityInsentient) this.getHandle()).setNoAI(!ai); + } + } + + @Override + public boolean hasAI() { + return (this.getHandle() instanceof EntityInsentient) ? !((EntityInsentient) this.getHandle()).isNoAI(): false; + } + + @Override + public void setCollidable(boolean collidable) { + getHandle().collides = collidable; + } + + @Override + public boolean isCollidable() { + return getHandle().collides; + } + + // Paper start + @Override + public int getArrowsStuck() { + return getHandle().getArrowCount(); + } + + @Override + public void setArrowsStuck(int arrows) { + getHandle().setArrowCount(arrows); + } + + @Override + public int getShieldBlockingDelay() { + return getHandle().getShieldBlockingDelay(); + } + + @Override + public void setShieldBlockingDelay(int delay) { + getHandle().setShieldBlockingDelay(delay); + } + + @Override + public ItemStack getActiveItem() { + return getHandle().activeItem.asBukkitMirror(); + } + + @Override + public int getItemUseRemainingTime() { + return getHandle().getItemUseRemainingTime(); + } + + @Override + public int getHandRaisedTime() { + return getHandle().getHandRaisedTime(); + } + + @Override + public boolean isHandRaised() { + return getHandle().isHandRaised(); + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java new file mode 100644 index 000000000000..3f94c5a9206e --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java @@ -0,0 +1,68 @@ +package org.bukkit.craftbukkit.entity; + +import com.destroystokyo.paper.entity.CraftRangedEntity; +import com.google.common.base.Preconditions; +import net.minecraft.server.EntityLlama; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.inventory.CraftInventoryLlama; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Horse; +import org.bukkit.entity.Llama; +import org.bukkit.inventory.LlamaInventory; + +public class CraftLlama extends CraftChestedHorse implements Llama, CraftRangedEntity { // Paper + + public CraftLlama(CraftServer server, EntityLlama entity) { + super(server, entity); + } + + @Override + public EntityLlama getHandle() { + return (EntityLlama) super.getHandle(); + } + + @Override + public Color getColor() { + return Color.values()[getHandle().getVariant()]; + } + + @Override + public void setColor(Color color) { + Preconditions.checkArgument(color != null, "color"); + + getHandle().setVariant(color.ordinal()); + } + + @Override + public LlamaInventory getInventory() { + return new CraftInventoryLlama(getHandle().inventoryChest); + } + + @Override + public int getStrength() { + return getHandle().getStrength(); + } + + @Override + public void setStrength(int strength) { + Preconditions.checkArgument(1 <= strength && strength <= 5, "strength must be [1,5]"); + if (strength == getStrength()) return; + getHandle().setStrength(strength); + getHandle().loadChest(); + } + + @Override + public Horse.Variant getVariant() { + return Horse.Variant.LLAMA; + } + + @Override + public String toString() { + return "CraftLlama"; + } + + @Override + public EntityType getType() { + return EntityType.LLAMA; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java new file mode 100644 index 000000000000..68d10c23a664 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java @@ -0,0 +1,39 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityLlamaSpit; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LlamaSpit; +import org.bukkit.projectiles.ProjectileSource; + +public class CraftLlamaSpit extends AbstractProjectile implements LlamaSpit { + + public CraftLlamaSpit(CraftServer server, EntityLlamaSpit entity) { + super(server, entity); + } + + @Override + public EntityLlamaSpit getHandle() { + return (EntityLlamaSpit) super.getHandle(); + } + + @Override + public String toString() { + return "CraftLlamaSpit"; + } + + @Override + public EntityType getType() { + return EntityType.LLAMA_SPIT; + } + + @Override + public ProjectileSource getShooter() { + return (getHandle().shooter != null) ? (ProjectileSource) getHandle().shooter.getBukkitEntity() : null; + } + + @Override + public void setShooter(ProjectileSource source) { + getHandle().shooter = (source != null) ? ((CraftLivingEntity) source).getHandle() : null; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMagmaCube.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMagmaCube.java new file mode 100644 index 000000000000..dace70b4f9d0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMagmaCube.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMagmaCube; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.MagmaCube; + +public class CraftMagmaCube extends CraftSlime implements MagmaCube { + + public CraftMagmaCube(CraftServer server, EntityMagmaCube entity) { + super(server, entity); + } + + public EntityMagmaCube getHandle() { + return (EntityMagmaCube) entity; + } + + @Override + public String toString() { + return "CraftMagmaCube"; + } + + public EntityType getType() { + return EntityType.MAGMA_CUBE; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecart.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecart.java new file mode 100644 index 000000000000..7d3198120891 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecart.java @@ -0,0 +1,108 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.Blocks; +import net.minecraft.server.EntityMinecartAbstract; + +import net.minecraft.server.IBlockData; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.Minecart; +import org.bukkit.material.MaterialData; +import org.bukkit.util.Vector; + +public abstract class CraftMinecart extends CraftVehicle implements Minecart { + public CraftMinecart(CraftServer server, EntityMinecartAbstract entity) { + super(server, entity); + } + + public void setDamage(double damage) { + getHandle().setDamage((float) damage); + } + + public double getDamage() { + return getHandle().getDamage(); + } + + public double getMaxSpeed() { + return getHandle().maxSpeed; + } + + public void setMaxSpeed(double speed) { + if (speed >= 0D) { + getHandle().maxSpeed = speed; + } + } + + public boolean isSlowWhenEmpty() { + return getHandle().slowWhenEmpty; + } + + public void setSlowWhenEmpty(boolean slow) { + getHandle().slowWhenEmpty = slow; + } + + public Vector getFlyingVelocityMod() { + return getHandle().getFlyingVelocityMod(); + } + + public void setFlyingVelocityMod(Vector flying) { + getHandle().setFlyingVelocityMod(flying); + } + + public Vector getDerailedVelocityMod() { + return getHandle().getDerailedVelocityMod(); + } + + public void setDerailedVelocityMod(Vector derailed) { + getHandle().setDerailedVelocityMod(derailed); + } + + @Override + public EntityMinecartAbstract getHandle() { + return (EntityMinecartAbstract) entity; + } + + public void setDisplayBlock(MaterialData material) { + if(material != null) { + IBlockData block = CraftMagicNumbers.getBlock(material); + this.getHandle().setDisplayBlock(block); + } else { + // Set block to air (default) and set the flag to not have a display block. + this.getHandle().setDisplayBlock(Blocks.AIR.getBlockData()); + this.getHandle().a(false); + } + } + + @Override + public void setDisplayBlockData(BlockData blockData) { + if (blockData != null) { + IBlockData block = ((CraftBlockData) blockData).getState(); + this.getHandle().setDisplayBlock(block); + } else { + // Set block to air (default) and set the flag to not have a display block. + this.getHandle().setDisplayBlock(Blocks.AIR.getBlockData()); + this.getHandle().a(false); + } + } + + public MaterialData getDisplayBlock() { + IBlockData blockData = getHandle().getDisplayBlock(); + return CraftMagicNumbers.getMaterial(blockData); + } + + @Override + public BlockData getDisplayBlockData() { + IBlockData blockData = getHandle().getDisplayBlock(); + return CraftBlockData.fromData(blockData); + } + + public void setDisplayBlockOffset(int offset) { + getHandle().setDisplayBlockOffset(offset); + } + + public int getDisplayBlockOffset() { + return getHandle().getDisplayBlockOffset(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java new file mode 100644 index 000000000000..e0b99207b279 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartChest.java @@ -0,0 +1,33 @@ +package org.bukkit.craftbukkit.entity; + +import com.destroystokyo.paper.loottable.PaperLootableEntityInventory; // Paper +import net.minecraft.server.EntityMinecartChest; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.minecart.StorageMinecart; +import org.bukkit.inventory.Inventory; + +@SuppressWarnings("deprecation") +public class CraftMinecartChest extends CraftMinecartContainer implements StorageMinecart, PaperLootableEntityInventory { // Paper + private final CraftInventory inventory; + + public CraftMinecartChest(CraftServer server, EntityMinecartChest entity) { + super(server, entity); + inventory = new CraftInventory(entity); + } + + public Inventory getInventory() { + return inventory; + } + + @Override + public String toString() { + return "CraftMinecartChest{" + "inventory=" + inventory + '}'; + } + + public EntityType getType() { + return EntityType.MINECART_CHEST; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartCommand.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartCommand.java new file mode 100644 index 000000000000..b178acd47507 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartCommand.java @@ -0,0 +1,139 @@ +package org.bukkit.craftbukkit.entity; + +import java.util.Set; + +import net.minecraft.server.EntityMinecartCommandBlock; + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.minecart.CommandMinecart; +import org.bukkit.permissions.PermissibleBase; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.plugin.Plugin; + +public class CraftMinecartCommand extends CraftMinecart implements CommandMinecart { + private final PermissibleBase perm = new PermissibleBase(this); + + public CraftMinecartCommand(CraftServer server, EntityMinecartCommandBlock entity) { + super(server, entity); + } + + @Override + public EntityMinecartCommandBlock getHandle() { + return (EntityMinecartCommandBlock) entity; + } + + @Override + public String getCommand() { + return getHandle().getCommandBlock().getCommand(); + } + + @Override + public void setCommand(String command) { + getHandle().getCommandBlock().setCommand(command != null ? command : ""); + getHandle().getDataWatcher().set(EntityMinecartCommandBlock.COMMAND, getHandle().getCommandBlock().getCommand()); + } + + @Override + public void setName(String name) { + getHandle().getCommandBlock().setName(CraftChatMessage.fromStringOrNull(name)); + } + + @Override + public String toString() { + return "CraftMinecartCommand"; + } + + @Override + public EntityType getType() { + return EntityType.MINECART_COMMAND; + } + + @Override + public void sendMessage(String message) { + } + + @Override + public void sendMessage(String[] messages) { + } + + @Override + public String getName() { + return CraftChatMessage.fromComponent(getHandle().getCommandBlock().getName()); + } + + @Override + public boolean isOp() { + return true; + } + + @Override + public void setOp(boolean value) { + throw new UnsupportedOperationException("Cannot change operator status of a minecart"); + } + + @Override + public boolean isPermissionSet(String name) { + return perm.isPermissionSet(name); + } + + @Override + public boolean isPermissionSet(Permission perm) { + return this.perm.isPermissionSet(perm); + } + + @Override + public boolean hasPermission(String name) { + return perm.hasPermission(name); + } + + @Override + public boolean hasPermission(Permission perm) { + return this.perm.hasPermission(perm); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { + return perm.addAttachment(plugin, name, value); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin) { + return perm.addAttachment(plugin); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { + return perm.addAttachment(plugin, name, value, ticks); + } + + @Override + public PermissionAttachment addAttachment(Plugin plugin, int ticks) { + return perm.addAttachment(plugin, ticks); + } + + @Override + public void removeAttachment(PermissionAttachment attachment) { + perm.removeAttachment(attachment); + } + + @Override + public void recalculatePermissions() { + perm.recalculatePermissions(); + } + + @Override + public Set getEffectivePermissions() { + return perm.getEffectivePermissions(); + } + + @Override + public Server getServer() { + return Bukkit.getServer(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartContainer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartContainer.java new file mode 100644 index 000000000000..6790fa2fee74 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartContainer.java @@ -0,0 +1,54 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMinecartAbstract; +import net.minecraft.server.EntityMinecartContainer; +import net.minecraft.server.MinecraftKey; +import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.loot.LootTable; +import org.bukkit.loot.Lootable; + +public abstract class CraftMinecartContainer extends CraftMinecart implements Lootable { + + public CraftMinecartContainer(CraftServer server, EntityMinecartAbstract entity) { + super(server, entity); + } + + @Override + public EntityMinecartContainer getHandle() { + return (EntityMinecartContainer) entity; + } + + @Override + public void setLootTable(LootTable table) { + setLootTable(table, getSeed()); + } + + @Override + public LootTable getLootTable() { + MinecraftKey nmsTable = getHandle().getLootTable(); + if (nmsTable == null) { + return null; // return empty loot table? + } + + NamespacedKey key = CraftNamespacedKey.fromMinecraft(nmsTable); + return Bukkit.getLootTable(key); + } + + @Override + public void setSeed(long seed) { + setLootTable(getLootTable(), seed); + } + + @Override + public long getSeed() { + return getHandle().lootTableSeed; + } + + public void setLootTable(LootTable table, long seed) { // Paper + MinecraftKey newKey = (table == null) ? null : CraftNamespacedKey.toMinecraft(table.getKey()); + getHandle().a(newKey, seed); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartFurnace.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartFurnace.java new file mode 100644 index 000000000000..2d2d9452358c --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartFurnace.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMinecartFurnace; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.minecart.PoweredMinecart; + +@SuppressWarnings("deprecation") +public class CraftMinecartFurnace extends CraftMinecart implements PoweredMinecart { + public CraftMinecartFurnace(CraftServer server, EntityMinecartFurnace entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftMinecartFurnace"; + } + + public EntityType getType() { + return EntityType.MINECART_FURNACE; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java new file mode 100644 index 000000000000..1937f7b9a2eb --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java @@ -0,0 +1,42 @@ +package org.bukkit.craftbukkit.entity; + +import com.destroystokyo.paper.loottable.PaperLootableEntityInventory; // Paper +import net.minecraft.server.EntityMinecartHopper; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.minecart.HopperMinecart; +import org.bukkit.inventory.Inventory; + +public final class CraftMinecartHopper extends CraftMinecartContainer implements HopperMinecart, PaperLootableEntityInventory { // Paper + private final CraftInventory inventory; + + public CraftMinecartHopper(CraftServer server, EntityMinecartHopper entity) { + super(server, entity); + inventory = new CraftInventory(entity); + } + + @Override + public String toString() { + return "CraftMinecartHopper{" + "inventory=" + inventory + '}'; + } + + public EntityType getType() { + return EntityType.MINECART_HOPPER; + } + + public Inventory getInventory() { + return inventory; + } + + @Override + public boolean isEnabled() { + return ((EntityMinecartHopper) getHandle()).isEnabled(); + } + + @Override + public void setEnabled(boolean enabled) { + ((EntityMinecartHopper) getHandle()).setEnabled(enabled); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartMobSpawner.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartMobSpawner.java new file mode 100644 index 000000000000..a78d81691b26 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartMobSpawner.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMinecartMobSpawner; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.minecart.SpawnerMinecart; + +final class CraftMinecartMobSpawner extends CraftMinecart implements SpawnerMinecart { + CraftMinecartMobSpawner(CraftServer server, EntityMinecartMobSpawner entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftMinecartMobSpawner"; + } + + public EntityType getType() { + return EntityType.MINECART_MOB_SPAWNER; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartRideable.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartRideable.java new file mode 100644 index 000000000000..9305d8841f08 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartRideable.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMinecartAbstract; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.minecart.RideableMinecart; + +public class CraftMinecartRideable extends CraftMinecart implements RideableMinecart { + public CraftMinecartRideable(CraftServer server, EntityMinecartAbstract entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftMinecartRideable"; + } + + public EntityType getType() { + return EntityType.MINECART; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartTNT.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartTNT.java new file mode 100644 index 000000000000..0c8109bb16c0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartTNT.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMinecartTNT; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.minecart.ExplosiveMinecart; + +final class CraftMinecartTNT extends CraftMinecart implements ExplosiveMinecart { + CraftMinecartTNT(CraftServer server, EntityMinecartTNT entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftMinecartTNT"; + } + + public EntityType getType() { + return EntityType.MINECART_TNT; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java new file mode 100644 index 000000000000..40a429942edc --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java @@ -0,0 +1,77 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityInsentient; +import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Mob; +import org.bukkit.loot.LootTable; + +public abstract class CraftMob extends CraftLivingEntity implements Mob { + public CraftMob(CraftServer server, EntityInsentient entity) { + super(server, entity); + paperPathfinder = new com.destroystokyo.paper.entity.PaperPathfinder(entity); // Paper + } + + private final com.destroystokyo.paper.entity.PaperPathfinder paperPathfinder; // Paper + @Override public com.destroystokyo.paper.entity.Pathfinder getPathfinder() { return paperPathfinder; } // Paper + @Override + public void setTarget(LivingEntity target) { + EntityInsentient entity = getHandle(); + if (target == null) { + entity.setGoalTarget(null, null, false); + } else if (target instanceof CraftLivingEntity) { + entity.setGoalTarget(((CraftLivingEntity) target).getHandle(), null, false); + } + } + + @Override + public CraftLivingEntity getTarget() { + if (getHandle().getGoalTarget() == null) return null; + + return (CraftLivingEntity) getHandle().getGoalTarget().getBukkitEntity(); + } + + @Override + public EntityInsentient getHandle() { + return (EntityInsentient) entity; + } + + @Override + public String toString() { + return "CraftMob"; + } + + @Override + public void setLootTable(LootTable table) { + getHandle().lootTableKey = (table == null) ? null : CraftNamespacedKey.toMinecraft(table.getKey()); + } + + @Override + public LootTable getLootTable() { + if (getHandle().lootTableKey == null) { + getHandle().lootTableKey = getHandle().getLootTable(); + } + + NamespacedKey key = CraftNamespacedKey.fromMinecraft(getHandle().lootTableKey); + return Bukkit.getLootTable(key); + } + + @Override + public void setSeed(long seed) { + getHandle().lootTableSeed = seed; + } + + @Override + public long getSeed() { + return getHandle().lootTableSeed; + } + + // Paper start + public boolean isInDaylight() { + return getHandle().isInDaylight(); + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMonster.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMonster.java new file mode 100644 index 000000000000..72dedbc8f417 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMonster.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMonster; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Monster; + +public class CraftMonster extends CraftCreature implements Monster { + + public CraftMonster(CraftServer server, EntityMonster entity) { + super(server, entity); + } + + @Override + public EntityMonster getHandle() { + return (EntityMonster) entity; + } + + @Override + public String toString() { + return "CraftMonster"; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMule.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMule.java new file mode 100644 index 000000000000..cf9b3ac18bcd --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMule.java @@ -0,0 +1,29 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityHorseMule; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Horse.Variant; +import org.bukkit.entity.Mule; + +public class CraftMule extends CraftChestedHorse implements Mule { + + public CraftMule(CraftServer server, EntityHorseMule entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftMule"; + } + + @Override + public EntityType getType() { + return EntityType.MULE; + } + + @Override + public Variant getVariant() { + return Variant.MULE; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMushroomCow.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMushroomCow.java new file mode 100644 index 000000000000..3dd7ea3f226c --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMushroomCow.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityMushroomCow; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.MushroomCow; + +public class CraftMushroomCow extends CraftCow implements MushroomCow { + public CraftMushroomCow(CraftServer server, EntityMushroomCow entity) { + super(server, entity); + } + + @Override + public EntityMushroomCow getHandle() { + return (EntityMushroomCow) entity; + } + + @Override + public String toString() { + return "CraftMushroomCow"; + } + + public EntityType getType() { + return EntityType.MUSHROOM_COW; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftOcelot.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftOcelot.java new file mode 100644 index 000000000000..e73c2c9f18fb --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftOcelot.java @@ -0,0 +1,37 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityOcelot; +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Ocelot; + +public class CraftOcelot extends CraftTameableAnimal implements Ocelot { + public CraftOcelot(CraftServer server, EntityOcelot ocelot) { + super(server, ocelot); + } + + @Override + public EntityOcelot getHandle() { + return (EntityOcelot) entity; + } + + public Type getCatType() { + return Type.getType(getHandle().getCatType()); + } + + public void setCatType(Type type) { + Validate.notNull(type, "Cat type cannot be null"); + getHandle().setCatType(type.getId()); + } + + @Override + public String toString() { + return "CraftOcelot"; + } + + @Override + public EntityType getType() { + return EntityType.OCELOT; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPainting.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPainting.java new file mode 100644 index 000000000000..9c29a56da62e --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPainting.java @@ -0,0 +1,79 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityPainting; +import net.minecraft.server.Paintings; +import net.minecraft.server.WorldServer; + +import org.bukkit.Art; +import org.bukkit.block.BlockFace; +import org.bukkit.craftbukkit.CraftArt; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Painting; + +public class CraftPainting extends CraftHanging implements Painting { + + public CraftPainting(CraftServer server, EntityPainting entity) { + super(server, entity); + } + + public Art getArt() { + Paintings art = getHandle().art; + return CraftArt.NotchToBukkit(art); + } + + public boolean setArt(Art art) { + return setArt(art, false); + } + + public boolean setArt(Art art, boolean force) { + EntityPainting painting = this.getHandle(); + Paintings oldArt = painting.art; + painting.art = CraftArt.BukkitToNotch(art); + painting.setDirection(painting.direction); + if (!force && !painting.survives()) { + // Revert painting since it doesn't fit + painting.art = oldArt; + painting.setDirection(painting.direction); + return false; + } + this.update(); + return true; + } + + public boolean setFacingDirection(BlockFace face, boolean force) { + if (super.setFacingDirection(face, force)) { + update(); + return true; + } + + return false; + } + + private void update() { + WorldServer world = ((CraftWorld) getWorld()).getHandle(); + EntityPainting painting = new EntityPainting(world); + painting.blockPosition = getHandle().blockPosition; + painting.art = getHandle().art; + painting.setDirection(getHandle().direction); + getHandle().die(); + getHandle().velocityChanged = true; // because this occurs when the painting is broken, so it might be important + world.addEntity(painting); + this.entity = painting; + } + + @Override + public EntityPainting getHandle() { + return (EntityPainting) entity; + } + + @Override + public String toString() { + return "CraftPainting{art=" + getArt() + "}"; + } + + public EntityType getType() { + return EntityType.PAINTING; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftParrot.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftParrot.java new file mode 100644 index 000000000000..5b2405cf2149 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftParrot.java @@ -0,0 +1,41 @@ +package org.bukkit.craftbukkit.entity; + +import com.google.common.base.Preconditions; +import net.minecraft.server.EntityParrot; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Parrot; + +public class CraftParrot extends CraftTameableAnimal implements Parrot { + + public CraftParrot(CraftServer server, EntityParrot parrot) { + super(server, parrot); + } + + @Override + public EntityParrot getHandle() { + return (EntityParrot) entity; + } + + @Override + public Variant getVariant() { + return Variant.values()[getHandle().getVariant()]; + } + + @Override + public void setVariant(Variant variant) { + Preconditions.checkArgument(variant != null, "variant"); + + getHandle().setVariant(variant.ordinal()); + } + + @Override + public String toString() { + return "CraftParrot"; + } + + @Override + public EntityType getType() { + return EntityType.PARROT; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java new file mode 100644 index 000000000000..2b9731369460 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java @@ -0,0 +1,44 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityPhantom; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Phantom; + +public class CraftPhantom extends CraftFlying implements Phantom { + + public CraftPhantom(CraftServer server, EntityPhantom entity) { + super(server, entity); + } + + @Override + public EntityPhantom getHandle() { + return (EntityPhantom) super.getHandle(); + } + + @Override + public int getSize() { + return getHandle().getSize(); + } + + @Override + public void setSize(int sz) { + getHandle().setSize(sz); + } + + @Override + public String toString() { + return "CraftPhantom"; + } + + @Override + public EntityType getType() { + return EntityType.PHANTOM; + } + + // Paper start + public java.util.UUID getSpawningEntity() { + return getHandle().getSpawningEntity(); + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPig.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPig.java new file mode 100644 index 000000000000..82bc7a6f0366 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPig.java @@ -0,0 +1,34 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityPig; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Pig; + +public class CraftPig extends CraftAnimals implements Pig { + public CraftPig(CraftServer server, EntityPig entity) { + super(server, entity); + } + + public boolean hasSaddle() { + return getHandle().hasSaddle(); + } + + public void setSaddle(boolean saddled) { + getHandle().setSaddle(saddled); + } + + public EntityPig getHandle() { + return (EntityPig) entity; + } + + @Override + public String toString() { + return "CraftPig"; + } + + public EntityType getType() { + return EntityType.PIG; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPigZombie.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPigZombie.java new file mode 100644 index 000000000000..c8d66f311c30 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPigZombie.java @@ -0,0 +1,59 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityPigZombie; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.PigZombie; + +public class CraftPigZombie extends CraftZombie implements PigZombie { + + public CraftPigZombie(CraftServer server, EntityPigZombie entity) { + super(server, entity); + } + + public int getAnger() { + return getHandle().angerLevel; + } + + public void setAnger(int level) { + getHandle().angerLevel = level; + } + + public void setAngry(boolean angry) { + setAnger(angry ? 400 : 0); + } + + public boolean isAngry() { + return getAnger() > 0; + } + + @Override + public EntityPigZombie getHandle() { + return (EntityPigZombie) entity; + } + + @Override + public String toString() { + return "CraftPigZombie"; + } + + public EntityType getType() { + return EntityType.PIG_ZOMBIE; + } + + @Override + public boolean isConverting() { + return false; + } + + @Override + public int getConversionTime() { + throw new UnsupportedOperationException("Not supported by this Entity."); + } + + @Override + public void setConversionTime(int time) { + throw new UnsupportedOperationException("Not supported by this Entity."); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java new file mode 100644 index 000000000000..fbe342902f2f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -0,0 +1,2044 @@ +package org.bukkit.craftbukkit.entity; + +import com.destroystokyo.paper.Title; +import com.destroystokyo.paper.profile.CraftPlayerProfile; +import com.destroystokyo.paper.profile.PlayerProfile; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.common.io.BaseEncoding; +import com.mojang.authlib.GameProfile; +import io.netty.buffer.Unpooled; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import net.md_5.bungee.api.chat.BaseComponent; + +import net.minecraft.server.AdvancementDataPlayer; +import net.minecraft.server.AdvancementProgress; +import net.minecraft.server.AttributeInstance; +import net.minecraft.server.AttributeMapServer; +import net.minecraft.server.AttributeModifiable; +import net.minecraft.server.AttributeRanged; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.ChatComponentText; +import net.minecraft.server.Container; +import net.minecraft.server.Entity; +import net.minecraft.server.EntityLiving; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.EntityTracker; +import net.minecraft.server.EntityTrackerEntry; +import net.minecraft.server.EnumChatFormat; +import net.minecraft.server.EnumGamemode; +import net.minecraft.server.IChatBaseComponent; +import net.minecraft.server.MapIcon; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.PacketDataSerializer; +import net.minecraft.server.PacketPlayOutBlockChange; +import net.minecraft.server.PacketPlayOutChat; +import net.minecraft.server.PacketPlayOutCustomPayload; +import net.minecraft.server.PacketPlayOutCustomSoundEffect; +import net.minecraft.server.PacketPlayOutMap; +import net.minecraft.server.PacketPlayOutNamedSoundEffect; +import net.minecraft.server.PacketPlayOutPlayerInfo; +import net.minecraft.server.PacketPlayOutPlayerListHeaderFooter; +import net.minecraft.server.PacketPlayOutSpawnPosition; +import net.minecraft.server.PacketPlayOutStopSound; +import net.minecraft.server.PacketPlayOutTitle; +import net.minecraft.server.PacketPlayOutUpdateAttributes; +import net.minecraft.server.PacketPlayOutUpdateHealth; +import net.minecraft.server.PacketPlayOutWorldEvent; +import net.minecraft.server.PacketPlayOutWorldParticles; +import net.minecraft.server.PlayerConnection; +import net.minecraft.server.TileEntitySign; +import net.minecraft.server.Vec3D; +import net.minecraft.server.WhiteListEntry; +import net.minecraft.server.WorldServer; + +import org.apache.commons.lang.Validate; +import org.apache.commons.lang.NotImplementedException; +import org.bukkit.*; +import org.bukkit.Achievement; +import org.bukkit.BanList; +import org.bukkit.Statistic; +import org.bukkit.Material; +import org.bukkit.Statistic.Type; +import org.bukkit.block.data.BlockData; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.conversations.Conversation; +import org.bukkit.conversations.ConversationAbandonedEvent; +import org.bukkit.conversations.ManuallyAbandonedConversationCanceller; +import org.bukkit.craftbukkit.CraftParticle; +import org.bukkit.craftbukkit.block.CraftSign; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.conversations.ConversationTracker; +import org.bukkit.craftbukkit.CraftEffect; +import org.bukkit.craftbukkit.CraftOfflinePlayer; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftSound; +import org.bukkit.craftbukkit.CraftStatistic; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.advancement.CraftAdvancement; +import org.bukkit.craftbukkit.advancement.CraftAdvancementProgress; +import org.bukkit.craftbukkit.map.CraftMapView; +import org.bukkit.craftbukkit.map.RenderData; +import org.bukkit.craftbukkit.scoreboard.CraftScoreboard; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerRegisterChannelEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.event.player.PlayerUnregisterChannelEvent; +import org.bukkit.inventory.InventoryView.Property; +import org.bukkit.map.MapCursor; +import org.bukkit.map.MapView; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.messaging.StandardMessenger; +import org.bukkit.scoreboard.Scoreboard; + +import javax.annotation.Nullable; + +@DelegateDeserialization(CraftOfflinePlayer.class) +public class CraftPlayer extends CraftHumanEntity implements Player { + private long firstPlayed = 0; + private long lastPlayed = 0; + private boolean hasPlayedBefore = false; + private final ConversationTracker conversationTracker = new ConversationTracker(); + private final Set channels = new HashSet(); + private final Map>> hiddenPlayers = new HashMap<>(); + private static final WeakHashMap> pluginWeakReferences = new WeakHashMap<>(); + private int hash = 0; + private double health = 20; + private boolean scaledHealth = false; + private double healthScale = 20; + // Paper start + private org.bukkit.event.player.PlayerResourcePackStatusEvent.Status resourcePackStatus; + private String resourcePackHash; + private static final boolean DISABLE_CHANNEL_LIMIT = System.getProperty("paper.disableChannelLimit") != null; // Paper - add a flag to disable the channel limit + private long lastSaveTime; + // Paper end + + public CraftPlayer(CraftServer server, EntityPlayer entity) { + super(server, entity); + + firstPlayed = System.currentTimeMillis(); + } + + public GameProfile getProfile() { + return getHandle().getProfile(); + } + + @Override + public boolean isOp() { + return server.getHandle().isOp(getProfile()); + } + + @Override + public void setOp(boolean value) { + if (value == isOp()) return; + + if (value) { + server.getHandle().addOp(getProfile()); + } else { + server.getHandle().removeOp(getProfile()); + } + + perm.recalculatePermissions(); + } + + public boolean isOnline() { + return server.getPlayer(getUniqueId()) != null; + } + + public InetSocketAddress getAddress() { + if (getHandle().playerConnection == null) return null; + + SocketAddress addr = getHandle().playerConnection.networkManager.getSocketAddress(); + if (addr instanceof InetSocketAddress) { + return (InetSocketAddress) addr; + } else { + return null; + } + } + + // Paper start - Implement NetworkClient + @Override + public int getProtocolVersion() { + if (getHandle().playerConnection == null) return -1; + return getHandle().playerConnection.networkManager.protocolVersion; + } + + @Override + public InetSocketAddress getVirtualHost() { + if (getHandle().playerConnection == null) return null; + return getHandle().playerConnection.networkManager.virtualHost; + } + // Paper end + + @Override + public double getEyeHeight(boolean ignorePose) { + if (ignorePose) { + return 1.62D; + } else { + return getEyeHeight(); + } + } + + @Override + public void sendRawMessage(String message) { + if (getHandle().playerConnection == null) return; + + for (IChatBaseComponent component : CraftChatMessage.fromString(message)) { + getHandle().playerConnection.sendPacket(new PacketPlayOutChat(component)); + } + } + + @Override + public void sendMessage(String message) { + if (!conversationTracker.isConversingModaly()) { + this.sendRawMessage(message); + } + } + + @Override + public void sendMessage(String[] messages) { + for (String message : messages) { + sendMessage(message); + } + } + + // Paper start + @Override + public void sendActionBar(String message) { + if (getHandle().playerConnection == null || message == null || message.isEmpty()) return; + getHandle().playerConnection.sendPacket(new PacketPlayOutChat(new net.minecraft.server.ChatComponentText(message), net.minecraft.server.ChatMessageType.GAME_INFO)); + } + + @Override + public void sendActionBar(char alternateChar, String message) { + if (message == null || message.isEmpty()) return; + sendActionBar(org.bukkit.ChatColor.translateAlternateColorCodes(alternateChar, message)); + } + + @Override + public void setPlayerListHeaderFooter(BaseComponent[] header, BaseComponent[] footer) { + if (header != null) { + String headerJson = net.md_5.bungee.chat.ComponentSerializer.toString(header); + playerListHeader = net.minecraft.server.ChatBaseComponent.ChatSerializer.jsonToComponent(headerJson); + } else { + playerListHeader = null; + } + + if (footer != null) { + String footerJson = net.md_5.bungee.chat.ComponentSerializer.toString(footer); + playerListFooter = net.minecraft.server.ChatBaseComponent.ChatSerializer.jsonToComponent(footerJson); + } else { + playerListFooter = null; + } + + updatePlayerListHeaderFooter(); + } + + @Override + public void setPlayerListHeaderFooter(BaseComponent header, BaseComponent footer) { + this.setPlayerListHeaderFooter(header == null ? null : new BaseComponent[]{header}, + footer == null ? null : new BaseComponent[]{footer}); + } + + + @Override + public void setTitleTimes(int fadeInTicks, int stayTicks, int fadeOutTicks) { + getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TIMES, (BaseComponent[]) null, fadeInTicks, stayTicks, fadeOutTicks)); + } + + @Override + public void setSubtitle(BaseComponent[] subtitle) { + getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.SUBTITLE, subtitle, 0, 0, 0)); + } + + @Override + public void setSubtitle(BaseComponent subtitle) { + setSubtitle(new BaseComponent[]{subtitle}); + } + + @Override + public void showTitle(BaseComponent[] title) { + getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TITLE, title, 0, 0, 0)); + } + + @Override + public void showTitle(BaseComponent title) { + showTitle(new BaseComponent[]{title}); + } + + @Override + public void showTitle(BaseComponent[] title, BaseComponent[] subtitle, int fadeInTicks, int stayTicks, int fadeOutTicks) { + setTitleTimes(fadeInTicks, stayTicks, fadeOutTicks); + setSubtitle(subtitle); + showTitle(title); + } + + @Override + public void showTitle(BaseComponent title, BaseComponent subtitle, int fadeInTicks, int stayTicks, int fadeOutTicks) { + setTitleTimes(fadeInTicks, stayTicks, fadeOutTicks); + setSubtitle(subtitle); + showTitle(title); + } + + @Override + public void sendTitle(Title title) { + Preconditions.checkNotNull(title, "Title is null"); + setTitleTimes(title.getFadeIn(), title.getStay(), title.getFadeOut()); + setSubtitle(title.getSubtitle() == null ? new BaseComponent[0] : title.getSubtitle()); + showTitle(title.getTitle()); + } + + @Override + public void updateTitle(Title title) { + Preconditions.checkNotNull(title, "Title is null"); + setTitleTimes(title.getFadeIn(), title.getStay(), title.getFadeOut()); + if (title.getSubtitle() != null) { + setSubtitle(title.getSubtitle()); + } + showTitle(title.getTitle()); + } + + @Override + public void hideTitle() { + getHandle().playerConnection.sendPacket(new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.CLEAR, (BaseComponent[]) null, 0, 0, 0)); + } + // Paper end + + @Override + public String getDisplayName() { + return getHandle().displayName; + } + + @Override + public void setDisplayName(final String name) { + getHandle().displayName = name == null ? getName() : name; + } + + @Override + public String getPlayerListName() { + return getHandle().listName == null ? getName() : CraftChatMessage.fromComponent(getHandle().listName, EnumChatFormat.WHITE); + } + + @Override + public void setPlayerListName(String name) { + if (name == null) { + name = getName(); + } + getHandle().listName = name.equals(getName()) ? null : CraftChatMessage.fromStringOrNull(name); + for (EntityPlayer player : (List)server.getHandle().players) { + if (player.getBukkitEntity().canSee(this)) { + player.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_DISPLAY_NAME, getHandle())); + } + } + } + + private IChatBaseComponent playerListHeader; + private IChatBaseComponent playerListFooter; + + @Override + public String getPlayerListHeader() { + return (playerListHeader == null) ? null : CraftChatMessage.fromComponent(playerListHeader); + } + + @Override + public String getPlayerListFooter() { + return (playerListFooter == null) ? null : CraftChatMessage.fromComponent(playerListFooter); + } + + @Override + public void setPlayerListHeader(String header) { + this.playerListHeader = CraftChatMessage.fromStringOrNull(header, true); // Paper - fix up spigot tab API + updatePlayerListHeaderFooter(); + } + + @Override + public void setPlayerListFooter(String footer) { + this.playerListFooter = CraftChatMessage.fromStringOrNull(footer, true); // Paper - fix up spigot tab API + updatePlayerListHeaderFooter(); + } + + @Override + public void setPlayerListHeaderFooter(String header, String footer) { + this.playerListHeader = CraftChatMessage.fromStringOrNull(header, true); // Paper - fix up spigot tab API + this.playerListFooter = CraftChatMessage.fromStringOrNull(footer, true); // Paper - fix up spigot tab API + updatePlayerListHeaderFooter(); + } + + private void updatePlayerListHeaderFooter() { + if (getHandle().playerConnection == null) return; + + PacketPlayOutPlayerListHeaderFooter packet = new PacketPlayOutPlayerListHeaderFooter(); + packet.header = (this.playerListHeader == null) ? new ChatComponentText("") : this.playerListHeader; + packet.footer = (this.playerListFooter == null) ? new ChatComponentText("") : this.playerListFooter; + getHandle().playerConnection.sendPacket(packet); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof OfflinePlayer)) { + return false; + } + OfflinePlayer other = (OfflinePlayer) obj; + if ((this.getUniqueId() == null) || (other.getUniqueId() == null)) { + return false; + } + + boolean uuidEquals = this.getUniqueId().equals(other.getUniqueId()); + boolean idEquals = true; + + if (other instanceof CraftPlayer) { + idEquals = this.getEntityId() == ((CraftPlayer) other).getEntityId(); + } + + return uuidEquals && idEquals; + } + + @Override + public void kickPlayer(String message) { + org.spigotmc.AsyncCatcher.catchOp( "player kick"); // Spigot + if (getHandle().playerConnection == null) return; + + getHandle().playerConnection.disconnect(message == null ? "" : message); + } + + @Override + public void setCompassTarget(Location loc) { + if (getHandle().playerConnection == null) return; + + // Do not directly assign here, from the packethandler we'll assign it. + getHandle().playerConnection.sendPacket(new PacketPlayOutSpawnPosition(new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()))); + } + + @Override + public Location getCompassTarget() { + return getHandle().compassTarget; + } + + @Override + public void chat(String msg) { + if (getHandle().playerConnection == null) return; + + getHandle().playerConnection.chat(msg, false); + } + + @Override + public boolean performCommand(String command) { + return server.dispatchCommand(this, command); + } + + @Override + public void playNote(Location loc, byte instrument, byte note) { + if (getHandle().playerConnection == null) return; + + String instrumentName = null; + switch (instrument) { + case 0: + instrumentName = "harp"; + break; + case 1: + instrumentName = "basedrum"; + break; + case 2: + instrumentName = "snare"; + break; + case 3: + instrumentName = "hat"; + break; + case 4: + instrumentName = "bass"; + break; + case 5: + instrumentName = "flute"; + break; + case 6: + instrumentName = "bell"; + break; + case 7: + instrumentName = "guitar"; + break; + case 8: + instrumentName = "chime"; + break; + case 9: + instrumentName = "xylophone"; + break; + } + + float f = (float) Math.pow(2.0D, (note - 12.0D) / 12.0D); + getHandle().playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(CraftSound.getSoundEffect("block.note_block." + instrumentName), net.minecraft.server.SoundCategory.RECORDS, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), 3.0f, f)); + } + + @Override + public void playNote(Location loc, Instrument instrument, Note note) { + if (getHandle().playerConnection == null) return; + + String instrumentName = null; + switch (instrument.ordinal()) { + case 0: + instrumentName = "harp"; + break; + case 1: + instrumentName = "basedrum"; + break; + case 2: + instrumentName = "snare"; + break; + case 3: + instrumentName = "hat"; + break; + case 4: + instrumentName = "bass"; + break; + case 5: + instrumentName = "flute"; + break; + case 6: + instrumentName = "bell"; + break; + case 7: + instrumentName = "guitar"; + break; + case 8: + instrumentName = "chime"; + break; + case 9: + instrumentName = "xylophone"; + break; + } + float f = (float) Math.pow(2.0D, (note.getId() - 12.0D) / 12.0D); + getHandle().playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect(CraftSound.getSoundEffect("block.note_block." + instrumentName), net.minecraft.server.SoundCategory.RECORDS, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), 3.0f, f)); + } + + @Override + public void playSound(Location loc, Sound sound, float volume, float pitch) { + playSound(loc, sound, org.bukkit.SoundCategory.MASTER, volume, pitch); + } + + @Override + public void playSound(Location loc, String sound, float volume, float pitch) { + playSound(loc, sound, org.bukkit.SoundCategory.MASTER, volume, pitch); + } + + @Override + public void playSound(Location loc, Sound sound, org.bukkit.SoundCategory category, float volume, float pitch) { + if (loc == null || sound == null || category == null || getHandle().playerConnection == null) return; + + PacketPlayOutNamedSoundEffect packet = new PacketPlayOutNamedSoundEffect(CraftSound.getSoundEffect(CraftSound.getSound(sound)), net.minecraft.server.SoundCategory.valueOf(category.name()), loc.getX(), loc.getY(), loc.getZ(), volume, pitch); + getHandle().playerConnection.sendPacket(packet); + } + + @Override + public void playSound(Location loc, String sound, org.bukkit.SoundCategory category, float volume, float pitch) { + if (loc == null || sound == null || category == null || getHandle().playerConnection == null) return; + + PacketPlayOutCustomSoundEffect packet = new PacketPlayOutCustomSoundEffect(new MinecraftKey(sound), net.minecraft.server.SoundCategory.valueOf(category.name()), new Vec3D(loc.getX(), loc.getY(), loc.getZ()), volume, pitch); + getHandle().playerConnection.sendPacket(packet); + } + + @Override + public void stopSound(Sound sound) { + stopSound(sound, null); + } + + @Override + public void stopSound(String sound) { + stopSound(sound, null); + } + + @Override + public void stopSound(Sound sound, org.bukkit.SoundCategory category) { + stopSound(CraftSound.getSound(sound), category); + } + + @Override + public void stopSound(String sound, org.bukkit.SoundCategory category) { + if (getHandle().playerConnection == null) return; + + getHandle().playerConnection.sendPacket(new PacketPlayOutStopSound(new MinecraftKey(sound), category == null ? net.minecraft.server.SoundCategory.MASTER : net.minecraft.server.SoundCategory.valueOf(category.name()))); + } + + @Override + public void playEffect(Location loc, Effect effect, int data) { + if (getHandle().playerConnection == null) return; + + int packetData = effect.getId(); + PacketPlayOutWorldEvent packet = new PacketPlayOutWorldEvent(packetData, new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()), data, false); + getHandle().playerConnection.sendPacket(packet); + } + + @Override + public void playEffect(Location loc, Effect effect, T data) { + if (data != null) { + Validate.isTrue(effect.getData() != null && effect.getData().isAssignableFrom(data.getClass()), "Wrong kind of data for this effect!"); + } else { + Validate.isTrue(effect.getData() == null, "Wrong kind of data for this effect!"); + } + + int datavalue = data == null ? 0 : CraftEffect.getDataValue(effect, data); + playEffect(loc, effect, datavalue); + } + + @Override + public void sendBlockChange(Location loc, Material material, byte data) { + if (getHandle().playerConnection == null) return; + + PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(((CraftWorld) loc.getWorld()).getHandle(), new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())); + + packet.block = CraftMagicNumbers.getBlock(material, data); + getHandle().playerConnection.sendPacket(packet); + } + + @Override + public void sendBlockChange(Location loc, BlockData block) { + if (getHandle().playerConnection == null) return; + + PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(((CraftWorld) loc.getWorld()).getHandle(), new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())); + + packet.block = ((CraftBlockData) block).getState(); + getHandle().playerConnection.sendPacket(packet); + } + + @Override + public void sendSignChange(Location loc, String[] lines) { + if (getHandle().playerConnection == null) { + return; + } + + if (lines == null) { + lines = new String[4]; + } + + Validate.notNull(loc, "Location can not be null"); + if (lines.length < 4) { + throw new IllegalArgumentException("Must have at least 4 lines"); + } + + IChatBaseComponent[] components = CraftSign.sanitizeLines(lines); + TileEntitySign sign = new TileEntitySign(); + sign.setPosition(new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())); + System.arraycopy(components, 0, sign.lines, 0, sign.lines.length); + + getHandle().playerConnection.sendPacket(sign.getUpdatePacket()); + } + + @Override + public boolean sendChunkChange(Location loc, int sx, int sy, int sz, byte[] data) { + if (getHandle().playerConnection == null) return false; + + /* + int x = loc.getBlockX(); + int y = loc.getBlockY(); + int z = loc.getBlockZ(); + + int cx = x >> 4; + int cz = z >> 4; + + if (sx <= 0 || sy <= 0 || sz <= 0) { + return false; + } + + if ((x + sx - 1) >> 4 != cx || (z + sz - 1) >> 4 != cz || y < 0 || y + sy > 128) { + return false; + } + + if (data.length != (sx * sy * sz * 5) / 2) { + return false; + } + + Packet51MapChunk packet = new Packet51MapChunk(x, y, z, sx, sy, sz, data); + + getHandle().playerConnection.sendPacket(packet); + + return true; + */ + + throw new NotImplementedException("Chunk changes do not yet work"); // TODO: Chunk changes. + } + + @Override + public void sendMap(MapView map) { + if (getHandle().playerConnection == null) return; + + RenderData data = ((CraftMapView) map).render(this); + Collection icons = new ArrayList(); + for (MapCursor cursor : data.cursors) { + if (cursor.isVisible()) { + icons.add(new MapIcon(MapIcon.Type.a(cursor.getRawType()), cursor.getX(), cursor.getY(), cursor.getDirection(), CraftChatMessage.fromStringOrNull(cursor.getCaption()))); + } + } + + PacketPlayOutMap packet = new PacketPlayOutMap(map.getId(), map.getScale().getValue(), true, icons, data.buffer, 0, 0, 128, 128); + getHandle().playerConnection.sendPacket(packet); + } + + @Override + public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) { + Preconditions.checkArgument(location != null, "location"); + Preconditions.checkArgument(location.getWorld() != null, "location.world"); + location.checkFinite(); + + EntityPlayer entity = getHandle(); + + if (getHealth() == 0 || entity.dead) { + return false; + } + + if (entity.playerConnection == null) { + return false; + } + + if (entity.isVehicle()) { + return false; + } + + // From = Players current Location + Location from = this.getLocation(); + // To = Players new Location if Teleport is Successful + Location to = location; + // Create & Call the Teleport Event. + PlayerTeleportEvent event = new PlayerTeleportEvent(this, from, to, cause); + server.getPluginManager().callEvent(event); + + // Return False to inform the Plugin that the Teleport was unsuccessful/cancelled. + if (event.isCancelled()) { + return false; + } + + // If this player is riding another entity, we must dismount before teleporting. + entity.stopRiding(); + + // Update the From Location + from = event.getFrom(); + // Grab the new To Location dependent on whether the event was cancelled. + to = event.getTo(); + // Grab the To and From World Handles. + WorldServer fromWorld = ((CraftWorld) from.getWorld()).getHandle(); + WorldServer toWorld = ((CraftWorld) to.getWorld()).getHandle(); + + // Close any foreign inventory + if (getHandle().activeContainer != getHandle().defaultContainer) { + getHandle().closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.TELEPORT); // Paper + } + + // Check if the fromWorld and toWorld are the same. + if (fromWorld == toWorld) { + entity.playerConnection.teleport(to); + } else { + // Paper - Configurable suffocation check + server.getHandle().moveToWorld(entity, toWorld.dimension, true, to, !toWorld.paperConfig.disableTeleportationSuffocationCheck); + } + return true; + } + + // Paper start - Ugly workaround for SPIGOT-1915 & GH-114 + @Override + public boolean setPassenger(org.bukkit.entity.Entity passenger) { + boolean wasSet = super.setPassenger(passenger); + if (wasSet) { + this.getHandle().playerConnection.sendPacket(new net.minecraft.server.PacketPlayOutMount(this.getHandle())); + } + return wasSet; + } + // Paper end + + @Override + public void setSneaking(boolean sneak) { + getHandle().setSneaking(sneak); + } + + @Override + public boolean isSneaking() { + return getHandle().isSneaking(); + } + + @Override + public boolean isSprinting() { + return getHandle().isSprinting(); + } + + @Override + public void setSprinting(boolean sprinting) { + getHandle().setSprinting(sprinting); + } + + @Override + public void loadData() { + server.getHandle().playerFileData.load(getHandle()); + } + + @Override + public void saveData() { + server.getHandle().playerFileData.save(getHandle()); + } + + @Deprecated + @Override + public void updateInventory() { + getHandle().updateInventory(getHandle().activeContainer); + } + + @Override + public void setSleepingIgnored(boolean isSleeping) { + getHandle().fauxSleeping = isSleeping; + ((CraftWorld) getWorld()).getHandle().checkSleepStatus(); + } + + @Override + public boolean isSleepingIgnored() { + return getHandle().fauxSleeping; + } + + @Override + public void awardAchievement(Achievement achievement) { + throw new UnsupportedOperationException("Not supported in this Minecraft version."); + } + + @Override + public void removeAchievement(Achievement achievement) { + throw new UnsupportedOperationException("Not supported in this Minecraft version."); + } + + @Override + public boolean hasAchievement(Achievement achievement) { + throw new UnsupportedOperationException("Not supported in this Minecraft version."); + } + + @Override + public void incrementStatistic(Statistic statistic) { + incrementStatistic(statistic, 1); + } + + @Override + public void decrementStatistic(Statistic statistic) { + decrementStatistic(statistic, 1); + } + + @Override + public int getStatistic(Statistic statistic) { + Validate.notNull(statistic, "Statistic cannot be null"); + Validate.isTrue(statistic.getType() == Type.UNTYPED, "Must supply additional paramater for this statistic"); + return getHandle().getStatisticManager().getStatisticValue(CraftStatistic.getNMSStatistic(statistic)); + } + + @Override + public void incrementStatistic(Statistic statistic, int amount) { + Validate.isTrue(amount > 0, "Amount must be greater than 0"); + setStatistic(statistic, getStatistic(statistic) + amount); + } + + @Override + public void decrementStatistic(Statistic statistic, int amount) { + Validate.isTrue(amount > 0, "Amount must be greater than 0"); + setStatistic(statistic, getStatistic(statistic) - amount); + } + + @Override + public void setStatistic(Statistic statistic, int newValue) { + Validate.notNull(statistic, "Statistic cannot be null"); + Validate.isTrue(statistic.getType() == Type.UNTYPED, "Must supply additional paramater for this statistic"); + Validate.isTrue(newValue >= 0, "Value must be greater than or equal to 0"); + net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getNMSStatistic(statistic); + getHandle().getStatisticManager().setStatistic(getHandle(), nmsStatistic, newValue); + } + + @Override + public void incrementStatistic(Statistic statistic, Material material) { + incrementStatistic(statistic, material, 1); + } + + @Override + public void decrementStatistic(Statistic statistic, Material material) { + decrementStatistic(statistic, material, 1); + } + + @Override + public int getStatistic(Statistic statistic, Material material) { + Validate.notNull(statistic, "Statistic cannot be null"); + Validate.notNull(material, "Material cannot be null"); + Validate.isTrue(statistic.getType() == Type.BLOCK || statistic.getType() == Type.ITEM, "This statistic does not take a Material parameter"); + net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getMaterialStatistic(statistic, material); + Validate.notNull(nmsStatistic, "The supplied Material does not have a corresponding statistic"); + return getHandle().getStatisticManager().getStatisticValue(nmsStatistic); + } + + @Override + public void incrementStatistic(Statistic statistic, Material material, int amount) { + Validate.isTrue(amount > 0, "Amount must be greater than 0"); + setStatistic(statistic, material, getStatistic(statistic, material) + amount); + } + + @Override + public void decrementStatistic(Statistic statistic, Material material, int amount) { + Validate.isTrue(amount > 0, "Amount must be greater than 0"); + setStatistic(statistic, material, getStatistic(statistic, material) - amount); + } + + @Override + public void setStatistic(Statistic statistic, Material material, int newValue) { + Validate.notNull(statistic, "Statistic cannot be null"); + Validate.notNull(material, "Material cannot be null"); + Validate.isTrue(newValue >= 0, "Value must be greater than or equal to 0"); + Validate.isTrue(statistic.getType() == Type.BLOCK || statistic.getType() == Type.ITEM, "This statistic does not take a Material parameter"); + net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getMaterialStatistic(statistic, material); + Validate.notNull(nmsStatistic, "The supplied Material does not have a corresponding statistic"); + getHandle().getStatisticManager().setStatistic(getHandle(), nmsStatistic, newValue); + } + + @Override + public void incrementStatistic(Statistic statistic, EntityType entityType) { + incrementStatistic(statistic, entityType, 1); + } + + @Override + public void decrementStatistic(Statistic statistic, EntityType entityType) { + decrementStatistic(statistic, entityType, 1); + } + + @Override + public int getStatistic(Statistic statistic, EntityType entityType) { + Validate.notNull(statistic, "Statistic cannot be null"); + Validate.notNull(entityType, "EntityType cannot be null"); + Validate.isTrue(statistic.getType() == Type.ENTITY, "This statistic does not take an EntityType parameter"); + net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getEntityStatistic(statistic, entityType); + Validate.notNull(nmsStatistic, "The supplied EntityType does not have a corresponding statistic"); + return getHandle().getStatisticManager().getStatisticValue(nmsStatistic); + } + + @Override + public void incrementStatistic(Statistic statistic, EntityType entityType, int amount) { + Validate.isTrue(amount > 0, "Amount must be greater than 0"); + setStatistic(statistic, entityType, getStatistic(statistic, entityType) + amount); + } + + @Override + public void decrementStatistic(Statistic statistic, EntityType entityType, int amount) { + Validate.isTrue(amount > 0, "Amount must be greater than 0"); + setStatistic(statistic, entityType, getStatistic(statistic, entityType) - amount); + } + + @Override + public void setStatistic(Statistic statistic, EntityType entityType, int newValue) { + Validate.notNull(statistic, "Statistic cannot be null"); + Validate.notNull(entityType, "EntityType cannot be null"); + Validate.isTrue(newValue >= 0, "Value must be greater than or equal to 0"); + Validate.isTrue(statistic.getType() == Type.ENTITY, "This statistic does not take an EntityType parameter"); + net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getEntityStatistic(statistic, entityType); + Validate.notNull(nmsStatistic, "The supplied EntityType does not have a corresponding statistic"); + getHandle().getStatisticManager().setStatistic(getHandle(), nmsStatistic, newValue); + } + + @Override + public void setPlayerTime(long time, boolean relative) { + getHandle().timeOffset = time; + getHandle().relativeTime = relative; + } + + @Override + public long getPlayerTimeOffset() { + return getHandle().timeOffset; + } + + @Override + public long getPlayerTime() { + return getHandle().getPlayerTime(); + } + + @Override + public boolean isPlayerTimeRelative() { + return getHandle().relativeTime; + } + + @Override + public void resetPlayerTime() { + setPlayerTime(0, true); + } + + @Override + public void setPlayerWeather(WeatherType type) { + getHandle().setPlayerWeather(type, true); + } + + @Override + public WeatherType getPlayerWeather() { + return getHandle().getPlayerWeather(); + } + + @Override + public void resetPlayerWeather() { + getHandle().resetPlayerWeather(); + } + + @Override + public boolean isBanned() { + return server.getBanList(BanList.Type.NAME).isBanned(getName()); + } + + @Override + public boolean isWhitelisted() { + return server.getHandle().getWhitelist().isWhitelisted(getProfile()); + } + + @Override + public void setWhitelisted(boolean value) { + if (value) { + server.getHandle().getWhitelist().add(new WhiteListEntry(getProfile())); + } else { + server.getHandle().getWhitelist().remove(getProfile()); + } + } + + @Override + public void setGameMode(GameMode mode) { + if (getHandle().playerConnection == null) return; + + if (mode == null) { + throw new IllegalArgumentException("Mode cannot be null"); + } + + getHandle().a(EnumGamemode.getById(mode.getValue())); + } + + @Override + public GameMode getGameMode() { + return GameMode.getByValue(getHandle().playerInteractManager.getGameMode().getId()); + } + + // Paper start + @Override + public int applyMending(int amount) { + EntityPlayer handle = getHandle(); + // Logic copied from EntityExperienceOrb and remapped to unobfuscated methods/properties + net.minecraft.server.ItemStack itemstack = net.minecraft.server.EnchantmentManager.getRandomEquippedItemWithEnchant(net.minecraft.server.Enchantments.MENDING, handle); + if (!itemstack.isEmpty() && itemstack.getItem().usesDurability()) { + + net.minecraft.server.EntityExperienceOrb orb = new net.minecraft.server.EntityExperienceOrb(handle.world); + orb.value = amount; + orb.spawnReason = org.bukkit.entity.ExperienceOrb.SpawnReason.CUSTOM; + orb.locX = handle.locX; + orb.locY = handle.locY; + orb.locZ = handle.locZ; + + int i = Math.min(orb.xpToDur(amount), itemstack.getDamage()); + org.bukkit.event.player.PlayerItemMendEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemMendEvent(handle, orb, itemstack, i); + i = event.getRepairAmount(); + orb.dead = true; + if (!event.isCancelled()) { + amount -= orb.durToXp(i); + itemstack.setDamage(itemstack.getDamage() - i); + } + } + return amount; + } + + @Override + public void giveExp(int exp, boolean applyMending) { + if (applyMending) { + exp = this.applyMending(exp); + } + // Paper end + getHandle().giveExp(exp); + } + + @Override + public void giveExpLevels(int levels) { + getHandle().levelDown(levels); + } + + @Override + public float getExp() { + return getHandle().exp; + } + + @Override + public void setExp(float exp) { + Preconditions.checkArgument(exp >= 0.0 && exp <= 1.0, "Experience progress must be between 0.0 and 1.0 (%s)", exp); + getHandle().exp = exp; + getHandle().lastSentExp = -1; + } + + @Override + public int getLevel() { + return getHandle().expLevel; + } + + @Override + public void setLevel(int level) { + getHandle().expLevel = level; + getHandle().lastSentExp = -1; + } + + @Override + public int getTotalExperience() { + return getHandle().expTotal; + } + + @Override + public void setTotalExperience(int exp) { + getHandle().expTotal = exp; + } + + @Override + public float getExhaustion() { + return getHandle().getFoodData().exhaustionLevel; + } + + @Override + public void setExhaustion(float value) { + getHandle().getFoodData().exhaustionLevel = value; + } + + @Override + public float getSaturation() { + return getHandle().getFoodData().saturationLevel; + } + + @Override + public void setSaturation(float value) { + getHandle().getFoodData().saturationLevel = value; + } + + @Override + public int getFoodLevel() { + return getHandle().getFoodData().foodLevel; + } + + @Override + public void setFoodLevel(int value) { + getHandle().getFoodData().foodLevel = value; + } + + @Nullable + private static WeakReference getPluginWeakReference(@Nullable Plugin plugin) { + return (plugin == null) ? null : pluginWeakReferences.computeIfAbsent(plugin, WeakReference::new); + } + + @Override + @Deprecated + public void hidePlayer(Player player) { + hidePlayer0(null, player); + } + + @Override + public void hidePlayer(Plugin plugin, Player player) { + Validate.notNull(plugin, "Plugin cannot be null"); + Validate.isTrue(plugin.isEnabled(), "Plugin attempted to hide player while disabled"); + + hidePlayer0(plugin, player); + } + + private void hidePlayer0(@Nullable Plugin plugin, Player player) { + Validate.notNull(player, "hidden player cannot be null"); + if (getHandle().playerConnection == null) return; + if (equals(player)) return; + + Set> hidingPlugins = hiddenPlayers.get(player.getUniqueId()); + if (hidingPlugins != null) { + // Some plugins are already hiding the player. Just mark that this + // plugin wants the player hidden too and end. + hidingPlugins.add(getPluginWeakReference(plugin)); + return; + } + hidingPlugins = new HashSet<>(); + hidingPlugins.add(getPluginWeakReference(plugin)); + hiddenPlayers.put(player.getUniqueId(), hidingPlugins); + + // Remove this player from the hidden player's EntityTrackerEntry + EntityPlayer other = ((CraftPlayer) player).getHandle(); + // Paper start + unregisterPlayer(other); + } + private void unregisterPlayer(EntityPlayer other) { + EntityTracker tracker = ((WorldServer) entity.world).tracker; + // Paper end + + EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId()); + if (entry != null) { + entry.clear(getHandle()); + } + + // Remove the hidden player from this player user list, if they're on it + if (other.sentListPacket) { + getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, other)); + } + } + + @Override + @Deprecated + public void showPlayer(Player player) { + showPlayer0(null, player); + } + + @Override + public void showPlayer(Plugin plugin, Player player) { + Validate.notNull(plugin, "Plugin cannot be null"); + // Don't require that plugin be enabled. A plugin must be allowed to call + // showPlayer during its onDisable() method. + showPlayer0(plugin, player); + } + + private void showPlayer0(@Nullable Plugin plugin, Player player) { + Validate.notNull(player, "shown player cannot be null"); + if (getHandle().playerConnection == null) return; + if (equals(player)) return; + + Set> hidingPlugins = hiddenPlayers.get(player.getUniqueId()); + if (hidingPlugins == null) { + return; // Player isn't hidden + } + hidingPlugins.remove(getPluginWeakReference(plugin)); + if (!hidingPlugins.isEmpty()) { + return; // Some other plugins still want the player hidden + } + hiddenPlayers.remove(player.getUniqueId()); + + // Paper start + EntityPlayer other = ((CraftPlayer) player).getHandle(); + registerPlayer(other); + } + private void registerPlayer(EntityPlayer other) { + EntityTracker tracker = ((WorldServer) entity.world).tracker; + // Paper end + + getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other)); + + EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId()); + if (entry != null && !entry.trackedPlayers.contains(getHandle())) { + entry.updatePlayer(getHandle()); + } + } + // Paper start + private void reregisterPlayer(EntityPlayer player) { + if (!hiddenPlayers.containsKey(player.getUniqueID())) { + unregisterPlayer(player); + registerPlayer(player); + } + } + public void setPlayerProfile(PlayerProfile profile) { + EntityPlayer self = getHandle(); + self.setProfile(CraftPlayerProfile.asAuthlibCopy(profile)); + List players = server.getServer().getPlayerList().players; + for (EntityPlayer player : players) { + player.getBukkitEntity().reregisterPlayer(self); + } + refreshPlayer(); + } + public PlayerProfile getPlayerProfile() { + return new CraftPlayerProfile(this).clone(); + } + + private void refreshPlayer() { + EntityPlayer handle = getHandle(); + + Location loc = getLocation(); + + PlayerConnection connection = handle.playerConnection; + reregisterPlayer(handle); + + //Respawn the player then update their position and selected slot + connection.sendPacket(new net.minecraft.server.PacketPlayOutRespawn(handle.dimension, handle.world.getDifficulty(), handle.world.getWorldData().getType(), handle.playerInteractManager.getGameMode())); + handle.updateAbilities(); + connection.sendPacket(new net.minecraft.server.PacketPlayOutPosition(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch(), new HashSet<>(), 0)); + net.minecraft.server.MinecraftServer.getServer().getPlayerList().updateClient(handle); + + if (this.isOp()) { + this.setOp(false); + this.setOp(true); + } + } + // Paper end + + public void removeDisconnectingPlayer(Player player) { + hiddenPlayers.remove(player.getUniqueId()); + } + + @Override + public boolean canSee(Player player) { + return !hiddenPlayers.containsKey(player.getUniqueId()); + } + + @Override + public Map serialize() { + Map result = new LinkedHashMap(); + + result.put("name", getName()); + + return result; + } + + @Override + public Player getPlayer() { + return this; + } + + @Override + public EntityPlayer getHandle() { + return (EntityPlayer) entity; + } + + public void setHandle(final EntityPlayer entity) { + super.setHandle(entity); + } + + @Override + public String toString() { + return "CraftPlayer{" + "name=" + getName() + '}'; + } + + @Override + public int hashCode() { + if (hash == 0 || hash == 485) { + hash = 97 * 5 + (this.getUniqueId() != null ? this.getUniqueId().hashCode() : 0); + } + return hash; + } + + @Override + public long getFirstPlayed() { + return firstPlayed; + } + + @Override + public long getLastPlayed() { + return lastPlayed; + } + + @Override + public boolean hasPlayedBefore() { + return hasPlayedBefore; + } + + public void setFirstPlayed(long firstPlayed) { + this.firstPlayed = firstPlayed; + } + + // Paper start + @Override + public long getLastLogin() { + return getHandle().loginTime; + } + + @Override + public long getLastSeen() { + return isOnline() ? System.currentTimeMillis() : this.lastSaveTime; + } + // Paper end + + public void readExtraData(NBTTagCompound nbttagcompound) { + hasPlayedBefore = true; + if (nbttagcompound.hasKey("bukkit")) { + NBTTagCompound data = nbttagcompound.getCompound("bukkit"); + + if (data.hasKey("firstPlayed")) { + firstPlayed = data.getLong("firstPlayed"); + lastPlayed = data.getLong("lastPlayed"); + } + + if (data.hasKey("newExp")) { + EntityPlayer handle = getHandle(); + handle.newExp = data.getInt("newExp"); + handle.newTotalExp = data.getInt("newTotalExp"); + handle.newLevel = data.getInt("newLevel"); + handle.expToDrop = data.getInt("expToDrop"); + handle.keepLevel = data.getBoolean("keepLevel"); + } + } + } + + public void setExtraData(NBTTagCompound nbttagcompound) { + this.lastSaveTime = System.currentTimeMillis(); // Paper + + if (!nbttagcompound.hasKey("bukkit")) { + nbttagcompound.set("bukkit", new NBTTagCompound()); + } + + NBTTagCompound data = nbttagcompound.getCompound("bukkit"); + EntityPlayer handle = getHandle(); + data.setInt("newExp", handle.newExp); + data.setInt("newTotalExp", handle.newTotalExp); + data.setInt("newLevel", handle.newLevel); + data.setInt("expToDrop", handle.expToDrop); + data.setBoolean("keepLevel", handle.keepLevel); + data.setLong("firstPlayed", getFirstPlayed()); + data.setLong("lastPlayed", System.currentTimeMillis()); + data.setString("lastKnownName", handle.getName()); + + // Paper start - persist for use in offline save data + if (!nbttagcompound.hasKey("Paper")) { + nbttagcompound.set("Paper", new NBTTagCompound()); + } + + NBTTagCompound paper = nbttagcompound.getCompound("Paper"); + paper.setLong("LastLogin", handle.loginTime); + paper.setLong("LastSeen", System.currentTimeMillis()); + // Paper end + } + + @Override + public boolean beginConversation(Conversation conversation) { + return conversationTracker.beginConversation(conversation); + } + + @Override + public void abandonConversation(Conversation conversation) { + conversationTracker.abandonConversation(conversation, new ConversationAbandonedEvent(conversation, new ManuallyAbandonedConversationCanceller())); + } + + @Override + public void abandonConversation(Conversation conversation, ConversationAbandonedEvent details) { + conversationTracker.abandonConversation(conversation, details); + } + + @Override + public void acceptConversationInput(String input) { + conversationTracker.acceptConversationInput(input); + } + + @Override + public boolean isConversing() { + return conversationTracker.isConversing(); + } + + @Override + public void sendPluginMessage(Plugin source, String channel, byte[] message) { + StandardMessenger.validatePluginMessage(server.getMessenger(), source, channel, message); + if (getHandle().playerConnection == null) return; + + if (channels.contains(channel)) { + channel = StandardMessenger.validateAndCorrectChannel(channel); + PacketPlayOutCustomPayload packet = new PacketPlayOutCustomPayload(new MinecraftKey(channel), new PacketDataSerializer(Unpooled.wrappedBuffer(message))); + getHandle().playerConnection.sendPacket(packet); + } + } + + @Override + public void setTexturePack(String url) { + setResourcePack(url); + } + + @Override + public void setResourcePack(String url) { + Validate.notNull(url, "Resource pack URL cannot be null"); + + getHandle().setResourcePack(url, "null"); + } + + @Override + public void setResourcePack(String url, byte[] hash) { + Validate.notNull(url, "Resource pack URL cannot be null"); + Validate.notNull(hash, "Resource pack hash cannot be null"); + Validate.isTrue(hash.length == 20, "Resource pack hash should be 20 bytes long but was " + hash.length); + + getHandle().setResourcePack(url, BaseEncoding.base16().lowerCase().encode(hash)); + } + + public void addChannel(String channel) { + Preconditions.checkState(DISABLE_CHANNEL_LIMIT || channels.size() < 128, "Cannot register channel '%s'. Too many channels registered!", channel); // Paper - flag to disable channel limit + channel = StandardMessenger.validateAndCorrectChannel(channel); + if (channels.add(channel)) { + server.getPluginManager().callEvent(new PlayerRegisterChannelEvent(this, channel)); + } + } + + public void removeChannel(String channel) { + channel = StandardMessenger.validateAndCorrectChannel(channel); + if (channels.remove(channel)) { + server.getPluginManager().callEvent(new PlayerUnregisterChannelEvent(this, channel)); + } + } + + @Override + public Set getListeningPluginChannels() { + return ImmutableSet.copyOf(channels); + } + + public void sendSupportedChannels() { + if (getHandle().playerConnection == null) return; + Set listening = server.getMessenger().getIncomingChannels(); + + if (!listening.isEmpty()) { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + + for (String channel : listening) { + try { + stream.write(channel.getBytes("UTF8")); + stream.write((byte) 0); + } catch (IOException ex) { + Logger.getLogger(CraftPlayer.class.getName()).log(Level.SEVERE, "Could not send Plugin Channel REGISTER to " + getName(), ex); + } + } + + getHandle().playerConnection.sendPacket(new PacketPlayOutCustomPayload(new MinecraftKey("register"), new PacketDataSerializer(Unpooled.wrappedBuffer(stream.toByteArray())))); + } + } + + @Override + public EntityType getType() { + return EntityType.PLAYER; + } + + @Override + public void setMetadata(String metadataKey, MetadataValue newMetadataValue) { + server.getPlayerMetadata().setMetadata(this, metadataKey, newMetadataValue); + } + + @Override + public List getMetadata(String metadataKey) { + return server.getPlayerMetadata().getMetadata(this, metadataKey); + } + + @Override + public boolean hasMetadata(String metadataKey) { + return server.getPlayerMetadata().hasMetadata(this, metadataKey); + } + + @Override + public void removeMetadata(String metadataKey, Plugin owningPlugin) { + server.getPlayerMetadata().removeMetadata(this, metadataKey, owningPlugin); + } + + @Override + public boolean setWindowProperty(Property prop, int value) { + Container container = getHandle().activeContainer; + if (container.getBukkitView().getType() != prop.getType()) { + return false; + } + // Paper start + if (prop == Property.REPAIR_COST && container instanceof net.minecraft.server.ContainerAnvil) { + ((net.minecraft.server.ContainerAnvil) container).levelCost = value; + } + // Paper end + getHandle().setContainerData(container, prop.getId(), value); + return true; + } + + public void disconnect(String reason) { + conversationTracker.abandonAllConversations(); + perm.clearPermissions(); + } + + @Override + public boolean isFlying() { + return getHandle().abilities.isFlying; + } + + @Override + public void setFlying(boolean value) { + boolean needsUpdate = getHandle().abilities.isFlying != value; // Paper - Only refresh abilities if needed + if (!getAllowFlight() && value) { + throw new IllegalArgumentException("Cannot make player fly if getAllowFlight() is false"); + } + + getHandle().abilities.isFlying = value; + if (needsUpdate) getHandle().updateAbilities(); // Paper - Only refresh abilities if needed + } + + @Override + public boolean getAllowFlight() { + return getHandle().abilities.canFly; + } + + @Override + public void setAllowFlight(boolean value) { + if (isFlying() && !value) { + getHandle().abilities.isFlying = false; + } + + getHandle().abilities.canFly = value; + getHandle().updateAbilities(); + } + + @Override + public int getNoDamageTicks() { + if (getHandle().invulnerableTicks > 0) { + return Math.max(getHandle().invulnerableTicks, getHandle().noDamageTicks); + } else { + return getHandle().noDamageTicks; + } + } + + @Override + public void setFlySpeed(float value) { + validateSpeed(value); + EntityPlayer player = getHandle(); + player.abilities.flySpeed = value / 2f; + player.updateAbilities(); + + } + + @Override + public void setWalkSpeed(float value) { + validateSpeed(value); + EntityPlayer player = getHandle(); + player.abilities.walkSpeed = value / 2f; + player.updateAbilities(); + } + + @Override + public float getFlySpeed() { + return (float) getHandle().abilities.flySpeed * 2f; + } + + @Override + public float getWalkSpeed() { + return getHandle().abilities.walkSpeed * 2f; + } + + private void validateSpeed(float value) { + if (value < 0) { + if (value < -1f) { + throw new IllegalArgumentException(value + " is too low"); + } + } else { + if (value > 1f) { + throw new IllegalArgumentException(value + " is too high"); + } + } + } + + @Override + public void setMaxHealth(double amount) { + super.setMaxHealth(amount); + this.health = Math.min(this.health, health); + getHandle().triggerHealthUpdate(); + } + + @Override + public void resetMaxHealth() { + super.resetMaxHealth(); + getHandle().triggerHealthUpdate(); + } + + @Override + public CraftScoreboard getScoreboard() { + return this.server.getScoreboardManager().getPlayerBoard(this); + } + + @Override + public void setScoreboard(Scoreboard scoreboard) { + Validate.notNull(scoreboard, "Scoreboard cannot be null"); + PlayerConnection playerConnection = getHandle().playerConnection; + if (playerConnection == null) { + throw new IllegalStateException("Cannot set scoreboard yet"); + } + if (playerConnection.isDisconnected()) { + // throw new IllegalStateException("Cannot set scoreboard for invalid CraftPlayer"); // Spigot - remove this as Mojang's semi asynchronous Netty implementation can lead to races + } + + this.server.getScoreboardManager().setPlayerBoard(this, scoreboard); + } + + @Override + public void setHealthScale(double value) { + Validate.isTrue((float) value > 0F, "Must be greater than 0"); + healthScale = value; + scaledHealth = true; + updateScaledHealth(); + } + + @Override + public double getHealthScale() { + return healthScale; + } + + @Override + public void setHealthScaled(boolean scale) { + if (scaledHealth != (scaledHealth = scale)) { + updateScaledHealth(); + } + } + + @Override + public boolean isHealthScaled() { + return scaledHealth; + } + + public float getScaledHealth() { + return (float) (isHealthScaled() ? getHealth() * getHealthScale() / getMaxHealth() : getHealth()); + } + + @Override + public double getHealth() { + return health; + } + + public void setRealHealth(double health) { + if (Double.isNaN(health)) {return;} // Paper + this.health = health; + } + + public void updateScaledHealth() { + updateScaledHealth(true); + } + + public void updateScaledHealth(boolean sendHealth) { + AttributeMapServer attributemapserver = (AttributeMapServer) getHandle().getAttributeMap(); + Collection set = attributemapserver.c(); // PAIL: Rename + + injectScaledMaxHealth(set, true); + + // SPIGOT-3813: Attributes before health + if (getHandle().playerConnection != null) { + getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(getHandle().getId(), set)); + if (sendHealth) { + sendHealthUpdate(); + } + } + getHandle().getDataWatcher().set(EntityLiving.HEALTH, (float) getScaledHealth()); + + getHandle().maxHealthCache = getMaxHealth(); + } + + public void sendHealthUpdate() { + // Paper start - cancellable death event + //getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel())); + PacketPlayOutUpdateHealth packet = new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel()); + if (this.getHandle().queueHealthUpdatePacket) { + this.getHandle().queuedHealthUpdatePacket = packet; + } else { + this.getHandle().playerConnection.sendPacket(packet); + } + // Paper end + } + + public void injectScaledMaxHealth(Collection collection, boolean force) { + if (!scaledHealth && !force) { + return; + } + for (AttributeInstance genericInstance : collection) { + if (genericInstance.getAttribute().getName().equals("generic.maxHealth")) { + collection.remove(genericInstance); + break; + } + } + // Spigot start + double healthMod = scaledHealth ? healthScale : getMaxHealth(); + if ( healthMod >= Float.MAX_VALUE || healthMod <= 0 ) + { + healthMod = 20; // Reset health + getServer().getLogger().warning( getName() + " tried to crash the server with a large health attribute" ); + } + collection.add(new AttributeModifiable(getHandle().getAttributeMap(), (new AttributeRanged(null, "generic.maxHealth", healthMod, 0.0D, Float.MAX_VALUE)).a("Max Health").a(true))); + // Spigot end + } + + @Override + public org.bukkit.entity.Entity getSpectatorTarget() { + Entity followed = getHandle().getSpecatorTarget(); + return followed == getHandle() ? null : followed.getBukkitEntity(); + } + + @Override + public void setSpectatorTarget(org.bukkit.entity.Entity entity) { + Preconditions.checkArgument(getGameMode() == GameMode.SPECTATOR, "Player must be in spectator mode"); + getHandle().setSpectatorTarget((entity == null) ? null : ((CraftEntity) entity).getHandle()); + } + + @Override + public void sendTitle(String title, String subtitle) { + sendTitle(title, subtitle, 10, 70, 20); + } + + @Override + public void sendTitle(String title, String subtitle, int fadeIn, int stay, int fadeOut) { + PacketPlayOutTitle times = new PacketPlayOutTitle(fadeIn, stay, fadeOut); + getHandle().playerConnection.sendPacket(times); + + if (title != null) { + PacketPlayOutTitle packetTitle = new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.TITLE, CraftChatMessage.fromStringOrNull(title)); + getHandle().playerConnection.sendPacket(packetTitle); + } + + if (subtitle != null) { + PacketPlayOutTitle packetSubtitle = new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.SUBTITLE, CraftChatMessage.fromStringOrNull(subtitle)); + getHandle().playerConnection.sendPacket(packetSubtitle); + } + } + + @Override + public void resetTitle() { + PacketPlayOutTitle packetReset = new PacketPlayOutTitle(PacketPlayOutTitle.EnumTitleAction.RESET, null); + getHandle().playerConnection.sendPacket(packetReset); + } + + @Override + public void spawnParticle(Particle particle, Location location, int count) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count); + } + + @Override + public void spawnParticle(Particle particle, double x, double y, double z, int count) { + spawnParticle(particle, x, y, z, count, null); + } + + @Override + public void spawnParticle(Particle particle, Location location, int count, T data) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, data); + } + + @Override + public void spawnParticle(Particle particle, double x, double y, double z, int count, T data) { + spawnParticle(particle, x, y, z, count, 0, 0, 0, data); + } + + @Override + public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ); + } + + @Override + public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ) { + spawnParticle(particle, x, y, z, count, offsetX, offsetY, offsetZ, null); + } + + @Override + public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ, T data) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, data); + } + + @Override + public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, T data) { + spawnParticle(particle, x, y, z, count, offsetX, offsetY, offsetZ, 1, data); + } + + @Override + public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ, double extra) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra); + } + + @Override + public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra) { + spawnParticle(particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, null); + } + + @Override + public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY, double offsetZ, double extra, T data) { + spawnParticle(particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, data); + } + + @Override + public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, T data) { + if (data != null && !particle.getDataType().isInstance(data)) { + throw new IllegalArgumentException("data should be " + particle.getDataType() + " got " + data.getClass()); + } + PacketPlayOutWorldParticles packetplayoutworldparticles = new PacketPlayOutWorldParticles(CraftParticle.toNMS(particle, data), true, (float) x, (float) y, (float) z, (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count); + getHandle().playerConnection.sendPacket(packetplayoutworldparticles); + + } + + @Override + public org.bukkit.advancement.AdvancementProgress getAdvancementProgress(org.bukkit.advancement.Advancement advancement) { + Preconditions.checkArgument(advancement != null, "advancement"); + + CraftAdvancement craft = (CraftAdvancement) advancement; + AdvancementDataPlayer data = getHandle().getAdvancementData(); + AdvancementProgress progress = data.getProgress(craft.getHandle()); + + return new CraftAdvancementProgress(craft, data, progress); + } + + @Override + public int getClientViewDistance() { + return (getHandle().clientViewDistance == null) ? Bukkit.getViewDistance() : getHandle().clientViewDistance; + } + + @Override + public String getLocale() { + // Paper start - Locale change event + final String locale = getHandle().locale; + return locale != null ? locale : "en_us"; + // Paper end + } + + // Paper start + public void setAffectsSpawning(boolean affects) { + this.getHandle().affectsSpawning = affects; + } + + @Override + public boolean getAffectsSpawning() { + return this.getHandle().affectsSpawning; + } + + @Override + public int getViewDistance() { + return getHandle().getViewDistance(); + } + + @Override + public void setViewDistance(int viewDistance) { + ((WorldServer) getHandle().world).getPlayerChunkMap().updateViewDistance(getHandle(), viewDistance); + } + // Paper end + + @Override + public void updateCommands() { + if (getHandle().playerConnection == null) return; + + getHandle().server.getCommandDispatcher().a(getHandle()); + } + + @Override + public void setResourcePack(String url, String hash) { + Validate.notNull(url, "Resource pack URL cannot be null"); + Validate.notNull(hash, "Hash cannot be null"); + this.getHandle().setResourcePack(url, hash); + } + + @Override + public org.bukkit.event.player.PlayerResourcePackStatusEvent.Status getResourcePackStatus() { + return this.resourcePackStatus; + } + + @Override + public String getResourcePackHash() { + return this.resourcePackHash; + } + + @Override + public boolean hasResourcePack() { + return this.resourcePackStatus == org.bukkit.event.player.PlayerResourcePackStatusEvent.Status.SUCCESSFULLY_LOADED; + } + + public void setResourcePackStatus(org.bukkit.event.player.PlayerResourcePackStatusEvent.Status status) { + this.resourcePackStatus = status; + } + + //Paper start + public float getCooldownPeriod() { + return getHandle().getCooldownPeriod(); + } + + public float getCooledAttackStrength(float adjustTicks) { + return getHandle().getCooledAttackStrength(adjustTicks); + } + + public void resetCooldown() { + getHandle().resetCooldown(); + } + + @Override + public void remove() { + if (this.getHandle().getClass().equals(EntityPlayer.class)) { // special case for NMS plugins inheriting + throw new UnsupportedOperationException("Calling Entity#remove on players produces undefined (bad) behavior"); + } else { + super.remove(); + } + } + //Paper end + + // Spigot start + private final Player.Spigot spigot = new Player.Spigot() + { + + @Override + public InetSocketAddress getRawAddress() + { + return (InetSocketAddress) getHandle().playerConnection.networkManager.getRawAddress(); + } + + @Override + public boolean getCollidesWithEntities() { + return CraftPlayer.this.isCollidable(); + } + + @Override + public void setCollidesWithEntities(boolean collides) { + CraftPlayer.this.setCollidable(collides); + } + + @Override + public void respawn() + { + if ( getHealth() <= 0 && isOnline() ) + { + server.getServer().getPlayerList().moveToWorld( getHandle(), net.minecraft.server.DimensionManager.OVERWORLD, false ); + } + } + + @Override + public String getLocale() + { + return CraftPlayer.this.getLocale(); // Paper + } + + @Override + public Set getHiddenPlayers() + { + Set ret = new HashSet(); + for ( UUID u : hiddenPlayers.keySet() ) + { + ret.add( getServer().getPlayer( u ) ); + } + + return java.util.Collections.unmodifiableSet( ret ); + } + + @Override + public void sendMessage(BaseComponent component) { + sendMessage( new BaseComponent[] { component } ); + } + + @Override + public void sendMessage(BaseComponent... components) { + if ( getHandle().playerConnection == null ) return; + + PacketPlayOutChat packet = new PacketPlayOutChat(null, net.minecraft.server.ChatMessageType.CHAT); + packet.components = components; + getHandle().playerConnection.sendPacket(packet); + } + + @Override + public void sendMessage(net.md_5.bungee.api.ChatMessageType position, BaseComponent component) { + sendMessage( position, new BaseComponent[] { component } ); + } + + @Override + public void sendMessage(net.md_5.bungee.api.ChatMessageType position, BaseComponent... components) { + if ( getHandle().playerConnection == null ) return; + + PacketPlayOutChat packet = new PacketPlayOutChat(null, net.minecraft.server.ChatMessageType.a((byte) position.ordinal())); + // Action bar doesn't render colours, replace colours with legacy section symbols + if (position == net.md_5.bungee.api.ChatMessageType.ACTION_BAR) { + components = new BaseComponent[]{new net.md_5.bungee.api.chat.TextComponent(BaseComponent.toLegacyText(components))}; + } + packet.components = components; + getHandle().playerConnection.sendPacket(packet); + } + + // Paper start + @Override + public int getPing() + { + return getHandle().ping; + } + // Paper end + }; + + public Player.Spigot spigot() + { + return spigot; + } + // Spigot end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPolarBear.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPolarBear.java new file mode 100644 index 000000000000..21129672cae4 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPolarBear.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityPolarBear; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.PolarBear; + +public class CraftPolarBear extends CraftAnimals implements PolarBear { + + public CraftPolarBear(CraftServer server, EntityPolarBear entity) { + super(server, entity); + } + @Override + public EntityPolarBear getHandle() { + return (EntityPolarBear) entity; + } + + @Override + public String toString() { + return "CraftPolarBear"; + } + + @Override + public EntityType getType() { + return EntityType.POLAR_BEAR; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java new file mode 100644 index 000000000000..335acc24d6a0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java @@ -0,0 +1,39 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityLiving; +import net.minecraft.server.EntityProjectile; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Projectile; +import org.bukkit.projectiles.ProjectileSource; + +public abstract class CraftProjectile extends AbstractProjectile implements Projectile { + public CraftProjectile(CraftServer server, net.minecraft.server.Entity entity) { + super(server, entity); + } + + public ProjectileSource getShooter() { + return getHandle().projectileSource; + } + + public void setShooter(ProjectileSource shooter) { + if (shooter instanceof CraftLivingEntity) { + getHandle().shooter = (EntityLiving) ((CraftLivingEntity) shooter).entity; + getHandle().shooterId = ((CraftLivingEntity) shooter).getUniqueId(); + } else { + getHandle().shooter = null; + getHandle().shooterId = null; + } + getHandle().projectileSource = shooter; + } + + @Override + public EntityProjectile getHandle() { + return (EntityProjectile) entity; + } + + @Override + public String toString() { + return "CraftProjectile"; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPufferFish.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPufferFish.java new file mode 100644 index 000000000000..2701de75ad89 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPufferFish.java @@ -0,0 +1,38 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityPufferFish; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.PufferFish; +import org.bukkit.entity.EntityType; + +public class CraftPufferFish extends CraftFish implements PufferFish { + + public CraftPufferFish(CraftServer server, EntityPufferFish entity) { + super(server, entity); + } + + @Override + public EntityPufferFish getHandle() { + return (EntityPufferFish) super.getHandle(); + } + + @Override + public int getPuffState() { + return getHandle().getPuffState(); + } + + @Override + public void setPuffState(int state) { + getHandle().setPuffState(state); + } + + @Override + public String toString() { + return "CraftPufferFish"; + } + + @Override + public EntityType getType() { + return EntityType.PUFFERFISH; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftRabbit.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftRabbit.java new file mode 100644 index 000000000000..61330d24360d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftRabbit.java @@ -0,0 +1,88 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.World; +import net.minecraft.server.EntityRabbit; +import net.minecraft.server.PathfinderGoalSelector; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Rabbit; +import org.bukkit.craftbukkit.CraftWorld; + +public class CraftRabbit extends CraftAnimals implements Rabbit { + + public CraftRabbit(CraftServer server, EntityRabbit entity) { + super(server, entity); + } + + @Override + public EntityRabbit getHandle() { + return (EntityRabbit) entity; + } + + @Override + public String toString() { + return "CraftRabbit{RabbitType=" + getRabbitType() + "}"; + } + + @Override + public EntityType getType() { + return EntityType.RABBIT; + } + + @Override + public Type getRabbitType() { + int type = getHandle().getRabbitType(); + return CraftMagicMapping.fromMagic(type); + } + + @Override + public void setRabbitType(Type type) { + EntityRabbit entity = getHandle(); + if (getRabbitType() == Type.THE_KILLER_BUNNY) { + // Reset goals and target finders. + World world = ((CraftWorld) this.getWorld()).getHandle(); + entity.goalSelector = new PathfinderGoalSelector(world != null && world.methodProfiler != null ? world.methodProfiler : null); + entity.targetSelector = new PathfinderGoalSelector(world != null && world.methodProfiler != null ? world.methodProfiler : null); + entity.initializePathFinderGoals(); + } + + entity.setRabbitType(CraftMagicMapping.toMagic(type)); + } + + private static class CraftMagicMapping { + + private static final int[] types = new int[Type.values().length]; + private static final Type[] reverse = new Type[Type.values().length]; + + static { + set(Type.BROWN, 0); + set(Type.WHITE, 1); + set(Type.BLACK, 2); + set(Type.BLACK_AND_WHITE, 3); + set(Type.GOLD, 4); + set(Type.SALT_AND_PEPPER, 5); + set(Type.THE_KILLER_BUNNY, 99); + } + + private static void set(Type type, int value) { + types[type.ordinal()] = value; + if (value < reverse.length) { + reverse[value] = type; + } + } + + public static Type fromMagic(int magic) { + if (magic >= 0 && magic < reverse.length) { + return reverse[magic]; + } else if (magic == 99) { + return Type.THE_KILLER_BUNNY; + } else { + return null; + } + } + + public static int toMagic(Type type) { + return types[type.ordinal()]; + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSalmon.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSalmon.java new file mode 100644 index 000000000000..4d91f576051a --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSalmon.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySalmon; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Salmon; +import org.bukkit.entity.EntityType; + +public class CraftSalmon extends CraftFish implements Salmon { + + public CraftSalmon(CraftServer server, EntitySalmon entity) { + super(server, entity); + } + + @Override + public EntitySalmon getHandle() { + return (EntitySalmon) super.getHandle(); + } + + @Override + public String toString() { + return "CraftSalmon"; + } + + @Override + public EntityType getType() { + return EntityType.SALMON; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSheep.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSheep.java new file mode 100644 index 000000000000..f7253554cbd3 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSheep.java @@ -0,0 +1,45 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySheep; + +import net.minecraft.server.EnumColor; +import org.bukkit.DyeColor; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Sheep; + +public class CraftSheep extends CraftAnimals implements Sheep { + public CraftSheep(CraftServer server, EntitySheep entity) { + super(server, entity); + } + + public DyeColor getColor() { + return DyeColor.getByWoolData((byte) getHandle().getColor().getColorIndex()); + } + + public void setColor(DyeColor color) { + getHandle().setColor(EnumColor.fromColorIndex(color.getWoolData())); + } + + public boolean isSheared() { + return getHandle().isSheared(); + } + + public void setSheared(boolean flag) { + getHandle().setSheared(flag); + } + + @Override + public EntitySheep getHandle() { + return (EntitySheep) entity; + } + + @Override + public String toString() { + return "CraftSheep"; + } + + public EntityType getType() { + return EntityType.SHEEP; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftShulker.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftShulker.java new file mode 100644 index 000000000000..d25b742e758f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftShulker.java @@ -0,0 +1,40 @@ +package org.bukkit.craftbukkit.entity; + +import com.google.common.base.Preconditions; +import net.minecraft.server.EntityShulker; +import org.bukkit.DyeColor; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Shulker; + +public class CraftShulker extends CraftGolem implements Shulker { + + public CraftShulker(CraftServer server, EntityShulker entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftShulker"; + } + + @Override + public EntityType getType() { + return EntityType.SHULKER; + } + + @Override + public EntityShulker getHandle() { + return (EntityShulker) entity; + } + + @Override + public DyeColor getColor() { + return DyeColor.getByWoolData(getHandle().getDataWatcher().get(EntityShulker.COLOR)); + } + + @Override + public void setColor(DyeColor color) { + getHandle().getDataWatcher().set(EntityShulker.COLOR, (color == null) ? 16 : color.getWoolData()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java new file mode 100644 index 000000000000..8fbdedc86aee --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java @@ -0,0 +1,55 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityShulkerBullet; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.ShulkerBullet; +import org.bukkit.projectiles.ProjectileSource; + +public class CraftShulkerBullet extends AbstractProjectile implements ShulkerBullet { + + public CraftShulkerBullet(CraftServer server, EntityShulkerBullet entity) { + super(server, entity); + } + + @Override + public ProjectileSource getShooter() { + return getHandle().projectileSource; + } + + @Override + public void setShooter(ProjectileSource shooter) { + if (shooter instanceof LivingEntity) { + getHandle().setShooter(((CraftLivingEntity) shooter).getHandle()); + } else { + getHandle().setShooter(null); + } + getHandle().projectileSource = shooter; + } + + @Override + public org.bukkit.entity.Entity getTarget() { + return getHandle().getTarget() != null ? getHandle().getTarget().getBukkitEntity() : null; + } + + @Override + public void setTarget(org.bukkit.entity.Entity target) { + getHandle().setTarget(target == null ? null : ((CraftEntity) target).getHandle()); + } + + @Override + public String toString() { + return "CraftShulkerBullet"; + } + + @Override + public EntityType getType() { + return EntityType.SHULKER_BULLET; + } + + @Override + public EntityShulkerBullet getHandle() { + return (EntityShulkerBullet) entity; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSilverfish.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSilverfish.java new file mode 100644 index 000000000000..ef9477519fff --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSilverfish.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySilverfish; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Silverfish; + +public class CraftSilverfish extends CraftMonster implements Silverfish { + public CraftSilverfish(CraftServer server, EntitySilverfish entity) { + super(server, entity); + } + + @Override + public EntitySilverfish getHandle() { + return (EntitySilverfish) entity; + } + + @Override + public String toString() { + return "CraftSilverfish"; + } + + public EntityType getType() { + return EntityType.SILVERFISH; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeleton.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeleton.java new file mode 100644 index 000000000000..4fa5e84ea4c3 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeleton.java @@ -0,0 +1,39 @@ +package org.bukkit.craftbukkit.entity; + +import com.destroystokyo.paper.entity.CraftRangedEntity; +import net.minecraft.server.EntitySkeletonAbstract; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Skeleton; + +public class CraftSkeleton extends CraftMonster implements Skeleton, CraftRangedEntity { // Paper + + public CraftSkeleton(CraftServer server, EntitySkeletonAbstract entity) { + super(server, entity); + } + + @Override + public EntitySkeletonAbstract getHandle() { + return (EntitySkeletonAbstract) entity; + } + + @Override + public String toString() { + return "CraftSkeleton"; + } + + public EntityType getType() { + return EntityType.SKELETON; + } + + @Override + public SkeletonType getSkeletonType() { + return SkeletonType.NORMAL; + } + + @Override + public void setSkeletonType(SkeletonType type) { + throw new UnsupportedOperationException("Not supported."); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeletonHorse.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeletonHorse.java new file mode 100644 index 000000000000..496d0c0cac3a --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeletonHorse.java @@ -0,0 +1,48 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityHorseSkeleton; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Horse.Variant; +import org.bukkit.entity.SkeletonHorse; + +public class CraftSkeletonHorse extends CraftAbstractHorse implements SkeletonHorse { + + public CraftSkeletonHorse(CraftServer server, EntityHorseSkeleton entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftSkeletonHorse"; + } + + @Override + public EntityType getType() { + return EntityType.SKELETON_HORSE; + } + + @Override + public Variant getVariant() { + return Variant.SKELETON_HORSE; + } + + // Paper start + @Override + public EntityHorseSkeleton getHandle() { + return (EntityHorseSkeleton) super.getHandle(); + } + + public int getTrapTime() { + return getHandle().getTrapTime(); + } + + public boolean isTrap() { + return getHandle().isTrap(); + } + + public void setTrap(boolean trap) { + getHandle().setTrap(trap); + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSlime.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSlime.java new file mode 100644 index 000000000000..8403c1e01ca6 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSlime.java @@ -0,0 +1,46 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySlime; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Slime; + +public class CraftSlime extends CraftMob implements Slime { + + public CraftSlime(CraftServer server, EntitySlime entity) { + super(server, entity); + } + + public int getSize() { + return getHandle().getSize(); + } + + public void setSize(int size) { + getHandle().setSize(size, true); + } + + @Override + public EntitySlime getHandle() { + return (EntitySlime) entity; + } + + @Override + public String toString() { + return "CraftSlime"; + } + + public EntityType getType() { + return EntityType.SLIME; + } + + // Paper start + public boolean canWander() { + return getHandle().canWander(); + } + + public void setWander(boolean canWander) { + getHandle().setWander(canWander); + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSmallFireball.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSmallFireball.java new file mode 100644 index 000000000000..720097004c6f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSmallFireball.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySmallFireball; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.SmallFireball; + +public class CraftSmallFireball extends CraftFireball implements SmallFireball { + public CraftSmallFireball(CraftServer server, EntitySmallFireball entity) { + super(server, entity); + } + + @Override + public EntitySmallFireball getHandle() { + return (EntitySmallFireball) entity; + } + + @Override + public String toString() { + return "CraftSmallFireball"; + } + + public EntityType getType() { + return EntityType.SMALL_FIREBALL; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowball.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowball.java new file mode 100644 index 000000000000..44de7496c2ba --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowball.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySnowball; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Snowball; + +public class CraftSnowball extends CraftProjectile implements Snowball { + public CraftSnowball(CraftServer server, EntitySnowball entity) { + super(server, entity); + } + + @Override + public EntitySnowball getHandle() { + return (EntitySnowball) entity; + } + + @Override + public String toString() { + return "CraftSnowball"; + } + + public EntityType getType() { + return EntityType.SNOWBALL; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java new file mode 100644 index 000000000000..2e3d8fcdfa10 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSnowman.java @@ -0,0 +1,37 @@ +package org.bukkit.craftbukkit.entity; + +import com.destroystokyo.paper.entity.CraftRangedEntity; +import net.minecraft.server.EntitySnowman; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Snowman; + +public class CraftSnowman extends CraftGolem implements Snowman, CraftRangedEntity { // Paper + public CraftSnowman(CraftServer server, EntitySnowman entity) { + super(server, entity); + } + + @Override + public boolean isDerp() { + return !getHandle().hasPumpkin(); + } + + @Override + public void setDerp(boolean derpMode) { + getHandle().setHasPumpkin(!derpMode); + } + + @Override + public EntitySnowman getHandle() { + return (EntitySnowman) entity; + } + + @Override + public String toString() { + return "CraftSnowman"; + } + + public EntityType getType() { + return EntityType.SNOWMAN; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSpectralArrow.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSpectralArrow.java new file mode 100644 index 000000000000..3a614edd7679 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSpectralArrow.java @@ -0,0 +1,38 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySpectralArrow; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.SpectralArrow; + +public class CraftSpectralArrow extends CraftArrow implements SpectralArrow { + + public CraftSpectralArrow(CraftServer server, EntitySpectralArrow entity) { + super(server, entity); + } + + @Override + public EntitySpectralArrow getHandle() { + return (EntitySpectralArrow) entity; + } + + @Override + public String toString() { + return "CraftSpectralArrow"; + } + + @Override + public EntityType getType() { + return EntityType.SPECTRAL_ARROW; + } + + @Override + public int getGlowingTicks() { + return getHandle().duration; + } + + @Override + public void setGlowingTicks(int duration) { + getHandle().duration = duration; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSpellcaster.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSpellcaster.java new file mode 100644 index 000000000000..db1ba5b2cd99 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSpellcaster.java @@ -0,0 +1,35 @@ +package org.bukkit.craftbukkit.entity; + +import com.google.common.base.Preconditions; +import net.minecraft.server.EntityIllagerWizard; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Spellcaster; + +public class CraftSpellcaster extends CraftIllager implements Spellcaster { + + public CraftSpellcaster(CraftServer server, EntityIllagerWizard entity) { + super(server, entity); + } + + @Override + public EntityIllagerWizard getHandle() { + return (EntityIllagerWizard) super.getHandle(); + } + + @Override + public String toString() { + return "CraftSpellcaster"; + } + + @Override + public Spell getSpell() { + return Spell.valueOf(getHandle().getSpell().name()); + } + + @Override + public void setSpell(Spell spell) { + Preconditions.checkArgument(spell != null, "Use Spell.NONE"); + + getHandle().setSpell(EntityIllagerWizard.Spell.a(spell.ordinal())); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSpider.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSpider.java new file mode 100644 index 000000000000..2a07fe1b2818 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSpider.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySpider; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Spider; + +public class CraftSpider extends CraftMonster implements Spider { + + public CraftSpider(CraftServer server, EntitySpider entity) { + super(server, entity); + } + + @Override + public EntitySpider getHandle() { + return (EntitySpider) entity; + } + + @Override + public String toString() { + return "CraftSpider"; + } + + public EntityType getType() { + return EntityType.SPIDER; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSplashPotion.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSplashPotion.java new file mode 100644 index 000000000000..1ef057b86fe4 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSplashPotion.java @@ -0,0 +1,43 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityPotion; +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.SplashPotion; +import org.bukkit.inventory.ItemStack; + +public class CraftSplashPotion extends CraftThrownPotion implements SplashPotion { + + public CraftSplashPotion(CraftServer server, EntityPotion entity) { + super(server, entity); + } + + @Override + public void setItem(ItemStack item) { + // The ItemStack must not be null. + Validate.notNull(item, "ItemStack cannot be null."); + + // The ItemStack must be a potion. + Validate.isTrue(item.getType() == Material.SPLASH_POTION, "ItemStack must be a splash potion. This item stack was " + item.getType() + "."); + + getHandle().setItem(CraftItemStack.asNMSCopy(item)); + } + + @Override + public EntityPotion getHandle() { + return (EntityPotion) entity; + } + + @Override + public String toString() { + return "CraftSplashPotion"; + } + + @Override + public EntityType getType() { + return EntityType.SPLASH_POTION; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSquid.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSquid.java new file mode 100644 index 000000000000..433a7c6ac534 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSquid.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySquid; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Squid; + +public class CraftSquid extends CraftWaterMob implements Squid { + + public CraftSquid(CraftServer server, EntitySquid entity) { + super(server, entity); + } + + @Override + public EntitySquid getHandle() { + return (EntitySquid) entity; + } + + @Override + public String toString() { + return "CraftSquid"; + } + + public EntityType getType() { + return EntityType.SQUID; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftStray.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftStray.java new file mode 100644 index 000000000000..0fcb05bad5cb --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftStray.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySkeletonStray; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Stray; + +public class CraftStray extends CraftSkeleton implements Stray { + + public CraftStray(CraftServer server, EntitySkeletonStray entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftStray"; + } + + @Override + public EntityType getType() { + return EntityType.STRAY; + } + + @Override + public SkeletonType getSkeletonType() { + return SkeletonType.STRAY; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java new file mode 100644 index 000000000000..dd9c659399a7 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java @@ -0,0 +1,60 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityLiving; +import net.minecraft.server.EntityTNTPrimed; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.TNTPrimed; + +public class CraftTNTPrimed extends CraftEntity implements TNTPrimed { + + public CraftTNTPrimed(CraftServer server, EntityTNTPrimed entity) { + super(server, entity); + } + + public float getYield() { + return getHandle().yield; + } + + public boolean isIncendiary() { + return getHandle().isIncendiary; + } + + public void setIsIncendiary(boolean isIncendiary) { + getHandle().isIncendiary = isIncendiary; + } + + public void setYield(float yield) { + getHandle().yield = yield; + } + + public int getFuseTicks() { + return getHandle().getFuseTicks(); + } + + public void setFuseTicks(int fuseTicks) { + getHandle().setFuseTicks(fuseTicks); + } + + @Override + public EntityTNTPrimed getHandle() { + return (EntityTNTPrimed) entity; + } + + @Override + public String toString() { + return "CraftTNTPrimed"; + } + + public EntityType getType() { + return EntityType.PRIMED_TNT; + } + + public Entity getSource() { + EntityLiving source = getHandle().getSource(); + + return (source != null) ? source.getBukkitEntity() : null; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTameableAnimal.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTameableAnimal.java new file mode 100644 index 000000000000..2e959321b532 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftTameableAnimal.java @@ -0,0 +1,84 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityTameableAnimal; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.AnimalTamer; +import org.bukkit.entity.Creature; +import org.bukkit.entity.Tameable; + +import java.util.UUID; + +public class CraftTameableAnimal extends CraftAnimals implements Tameable, Creature { + public CraftTameableAnimal(CraftServer server, EntityTameableAnimal entity) { + super(server, entity); + } + + @Override + public EntityTameableAnimal getHandle() { + return (EntityTameableAnimal)super.getHandle(); + } + + public UUID getOwnerUniqueId() { + return getOwnerUUID(); + } + public UUID getOwnerUUID() { + try { + return getHandle().getOwnerUUID(); + } catch (IllegalArgumentException ex) { + return null; + } + } + + public void setOwnerUUID(UUID uuid) { + getHandle().setOwnerUUID(uuid); + } + + public AnimalTamer getOwner() { + if (getOwnerUUID() == null) { + return null; + } + + AnimalTamer owner = getServer().getPlayer(getOwnerUUID()); + if (owner == null) { + owner = getServer().getOfflinePlayer(getOwnerUUID()); + } + + return owner; + } + + public boolean isTamed() { + return getHandle().isTamed(); + } + + public void setOwner(AnimalTamer tamer) { + if (tamer != null) { + setTamed(true); + getHandle().setGoalTarget(null, null, false); + setOwnerUUID(tamer.getUniqueId()); + } else { + setTamed(false); + setOwnerUUID(null); + } + } + + public void setTamed(boolean tame) { + getHandle().setTamed(tame); + if (!tame) { + setOwnerUUID(null); + } + } + + public boolean isSitting() { + return getHandle().isSitting(); + } + + public void setSitting(boolean sitting) { + getHandle().setSitting(sitting); + getHandle().getGoalSit().setSitting(sitting); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{owner=" + getOwner() + ",tamed=" + isTamed() + "}"; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownExpBottle.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownExpBottle.java new file mode 100644 index 000000000000..fb3416e65891 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownExpBottle.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityThrownExpBottle; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.ThrownExpBottle; + +public class CraftThrownExpBottle extends CraftProjectile implements ThrownExpBottle { + public CraftThrownExpBottle(CraftServer server, EntityThrownExpBottle entity) { + super(server, entity); + } + + @Override + public EntityThrownExpBottle getHandle() { + return (EntityThrownExpBottle) entity; + } + + @Override + public String toString() { + return "EntityThrownExpBottle"; + } + + public EntityType getType() { + return EntityType.THROWN_EXP_BOTTLE; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java new file mode 100644 index 000000000000..b19539935ddd --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java @@ -0,0 +1,41 @@ +package org.bukkit.craftbukkit.entity; + +import java.util.Collection; +import net.minecraft.server.EntityPotion; +import net.minecraft.server.MobEffect; +import net.minecraft.server.PotionUtil; + +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.potion.CraftPotionUtil; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.ThrownPotion; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; + +import com.google.common.collect.ImmutableList; + +public abstract class CraftThrownPotion extends CraftProjectile implements ThrownPotion { + public CraftThrownPotion(CraftServer server, EntityPotion entity) { + super(server, entity); + } + + public Collection getEffects() { + ImmutableList.Builder builder = ImmutableList.builder(); + for (MobEffect effect : PotionUtil.getEffects(getHandle().getItem())) { + builder.add(CraftPotionUtil.toBukkit(effect)); + } + return builder.build(); + } + + public ItemStack getItem() { + return CraftItemStack.asBukkitCopy(getHandle().getItem()); + } + + @Override + public EntityPotion getHandle() { + return (EntityPotion) entity; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTippedArrow.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTippedArrow.java new file mode 100644 index 000000000000..435203b8819d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftTippedArrow.java @@ -0,0 +1,133 @@ +package org.bukkit.craftbukkit.entity; + +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.bukkit.Color; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.potion.CraftPotionUtil; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.TippedArrow; +import org.bukkit.potion.PotionData; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.potion.PotionType; + +import com.google.common.collect.ImmutableList; + +import net.minecraft.server.EntityTippedArrow; +import net.minecraft.server.MobEffect; +import net.minecraft.server.MobEffectList; + +public class CraftTippedArrow extends CraftArrow implements TippedArrow { + + public CraftTippedArrow(CraftServer server, EntityTippedArrow entity) { + super(server, entity); + } + + @Override + public EntityTippedArrow getHandle() { + return (EntityTippedArrow) entity; + } + + @Override + public String toString() { + return "CraftTippedArrow"; + } + + @Override + public EntityType getType() { + return EntityType.TIPPED_ARROW; + } + + @Override + public boolean addCustomEffect(PotionEffect effect, boolean override) { + int effectId = effect.getType().getId(); + MobEffect existing = null; + for (MobEffect mobEffect : getHandle().effects) { + if (MobEffectList.getId(mobEffect.getMobEffect()) == effectId) { + existing = mobEffect; + } + } + if (existing != null) { + if (!override) { + return false; + } + getHandle().effects.remove(existing); + } + getHandle().a(CraftPotionUtil.fromBukkit(effect)); + getHandle().refreshEffects(); + return true; + } + + @Override + public void clearCustomEffects() { + Validate.isTrue(getBasePotionData().getType() != PotionType.UNCRAFTABLE, "Tipped Arrows must have at least 1 effect"); + getHandle().effects.clear(); + getHandle().refreshEffects(); + } + + @Override + public List getCustomEffects() { + ImmutableList.Builder builder = ImmutableList.builder(); + for (MobEffect effect : getHandle().effects) { + builder.add(CraftPotionUtil.toBukkit(effect)); + } + return builder.build(); + } + + @Override + public boolean hasCustomEffect(PotionEffectType type) { + for (MobEffect effect : getHandle().effects) { + if (CraftPotionUtil.equals(effect.getMobEffect(), type)) { + return true; + } + } + return false; + } + + @Override + public boolean hasCustomEffects() { + return !getHandle().effects.isEmpty(); + } + + @Override + public boolean removeCustomEffect(PotionEffectType effect) { + int effectId = effect.getId(); + MobEffect existing = null; + for (MobEffect mobEffect : getHandle().effects) { + if (MobEffectList.getId(mobEffect.getMobEffect()) == effectId) { + existing = mobEffect; + } + } + if (existing == null) { + return false; + } + Validate.isTrue(getBasePotionData().getType() != PotionType.UNCRAFTABLE || !getHandle().effects.isEmpty(), "Tipped Arrows must have at least 1 effect"); + getHandle().effects.remove(existing); + getHandle().refreshEffects(); + return true; + } + + @Override + public void setBasePotionData(PotionData data) { + Validate.notNull(data, "PotionData cannot be null"); + Validate.isTrue(data.getType() != PotionType.UNCRAFTABLE || !getHandle().effects.isEmpty(), "Tipped Arrows must have at least 1 effect"); + getHandle().setType(CraftPotionUtil.fromBukkit(data)); + } + + @Override + public PotionData getBasePotionData() { + return CraftPotionUtil.toBukkit(getHandle().getType()); + } + + @Override + public void setColor(Color color) { + getHandle().setColor(color.asRGB()); + } + + @Override + public Color getColor() { + return Color.fromRGB(getHandle().getColor()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java new file mode 100644 index 000000000000..0518e6c6833a --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityThrownTrident; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Trident; + +public class CraftTrident extends CraftArrow implements Trident { + + public CraftTrident(CraftServer server, EntityThrownTrident entity) { + super(server, entity); + } + + @Override + public EntityThrownTrident getHandle() { + return (EntityThrownTrident) super.getHandle(); + } + + @Override + public String toString() { + return "CraftTrident"; + } + + @Override + public EntityType getType() { + return EntityType.TRIDENT; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTropicalFish.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTropicalFish.java new file mode 100644 index 000000000000..9243381a2fbd --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftTropicalFish.java @@ -0,0 +1,117 @@ +package org.bukkit.craftbukkit.entity; + +import java.util.HashMap; +import java.util.Map; +import net.minecraft.server.EntityTropicalFish; +import org.bukkit.DyeColor; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.TropicalFish; + +public class CraftTropicalFish extends CraftFish implements TropicalFish { + + public CraftTropicalFish(CraftServer server, EntityTropicalFish entity) { + super(server, entity); + } + + @Override + public EntityTropicalFish getHandle() { + return (EntityTropicalFish) entity; + } + + @Override + public String toString() { + return "CraftTropicalFish"; + } + + @Override + public EntityType getType() { + return EntityType.TROPICAL_FISH; + } + + @Override + public DyeColor getPatternColor() { + return getPatternColor(getHandle().getVariant()); + } + + @Override + public void setPatternColor(DyeColor color) { + getHandle().setVariant(getData(color, getBodyColor(), getPattern())); + } + + @Override + public DyeColor getBodyColor() { + return getBodyColor(getHandle().getVariant()); + } + + @Override + public void setBodyColor(DyeColor color) { + getHandle().setVariant(getData(getPatternColor(), color, getPattern())); + } + + @Override + public Pattern getPattern() { + return getPattern(getHandle().getVariant()); + } + + @Override + public void setPattern(Pattern pattern) { + getHandle().setVariant(getData(getPatternColor(), getBodyColor(), pattern)); + } + + public static enum CraftPattern { + KOB(0, false), + SUNSTREAK(1, false), + SNOOPER(2, false), + DASHER(3, false), + BRINELY(4, false), + SPOTTY(5, false), + FLOPPER(0, true), + STRIPEY(1, true), + GLITTER(2, true), + BLOCKFISH(3, true), + BETTY(4, true), + CLAYFISH(5, true); + + private final int variant; + private final boolean large; + + // + private static final Map BY_DATA = new HashMap<>(); + + static { + for (CraftPattern type : values()) { + BY_DATA.put(type.getDataValue(), Pattern.values()[type.ordinal()]); + } + } + + public static Pattern fromData(int data) { + return BY_DATA.get(data); + } + + private CraftPattern(int variant, boolean large) { + this.variant = variant; + this.large = large; + } + + public int getDataValue() { + return variant << 8 | ((large) ? 1 : 0); + } + } + + public static int getData(DyeColor patternColor, DyeColor bodyColor, Pattern type) { + return patternColor.getWoolData() << 24 | bodyColor.getWoolData() << 16 | CraftPattern.values()[type.ordinal()].getDataValue(); + } + + public static DyeColor getPatternColor(int data) { + return DyeColor.getByWoolData((byte) ((data >> 24) & 0xFF)); + } + + public static DyeColor getBodyColor(int data) { + return DyeColor.getByWoolData((byte) ((data >> 16) & 0xFF)); + } + + public static Pattern getPattern(int data) { + return CraftPattern.fromData(data & 0xFFFF); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java new file mode 100644 index 000000000000..8edcf7af65ec --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java @@ -0,0 +1,62 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityTurtle; +import net.minecraft.server.MCUtil; +import org.bukkit.Location; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Turtle; + +public class CraftTurtle extends CraftAnimals implements Turtle { + + public CraftTurtle(CraftServer server, EntityTurtle entity) { + super(server, entity); + } + + @Override + public EntityTurtle getHandle() { + return (EntityTurtle) super.getHandle(); + } + + @Override + public String toString() { + return "CraftTurtle"; + } + + @Override + public EntityType getType() { + return EntityType.TURTLE; + } + + // Paper start + @Override + public Location getHome() { + return MCUtil.toLocation(getHandle().world, getHandle().getHome()); + } + + @Override + public void setHome(Location location) { + getHandle().setHome(MCUtil.toBlockPosition(location)); + } + + @Override + public boolean isGoingHome() { + return getHandle().isGoingHome(); + } + + @Override + public boolean isDigging() { + return getHandle().isDigging(); + } + + @Override + public boolean hasEgg() { + return getHandle().hasEgg(); + } + + @Override + public void setHasEgg(boolean hasEgg) { + getHandle().setHasEgg(hasEgg); + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVehicle.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVehicle.java new file mode 100644 index 000000000000..8e4af469f97b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVehicle.java @@ -0,0 +1,15 @@ +package org.bukkit.craftbukkit.entity; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Vehicle; + +public abstract class CraftVehicle extends CraftEntity implements Vehicle { + public CraftVehicle(CraftServer server, net.minecraft.server.Entity entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftVehicle{passenger=" + getPassenger() + '}'; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVex.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVex.java new file mode 100644 index 000000000000..c23b1e9601fd --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVex.java @@ -0,0 +1,41 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityInsentient; +import net.minecraft.server.EntityVex; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Mob; +import org.bukkit.entity.Vex; + +public class CraftVex extends CraftMonster implements Vex { + + public CraftVex(CraftServer server, EntityVex entity) { + super(server, entity); + } + + @Override + public EntityVex getHandle() { + return (EntityVex) super.getHandle(); + } + + // Paper start + public Mob getSummoner() { + EntityInsentient owner = getHandle().getOwner(); + return owner != null ? (Mob) owner.getBukkitEntity() : null; + } + + public void setSummoner(Mob summoner) { + getHandle().setOwner(summoner == null ? null : ((CraftMob) summoner).getHandle()); + } + // Paper end + + @Override + public String toString() { + return "CraftVex"; + } + + @Override + public EntityType getType() { + return EntityType.VEX; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java new file mode 100644 index 000000000000..e21fe51a062a --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java @@ -0,0 +1,165 @@ +package org.bukkit.craftbukkit.entity; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; +import net.minecraft.server.EntityVillager; +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftMerchant; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Villager; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.MerchantRecipe; + +public class CraftVillager extends CraftAgeable implements Villager, InventoryHolder { + + private static final Map careerIDMap = new HashMap<>(); + private CraftMerchant merchant; + + public CraftVillager(CraftServer server, EntityVillager entity) { + super(server, entity); + } + + @Override + public EntityVillager getHandle() { + return (EntityVillager) entity; + } + + @Override + public String toString() { + return "CraftVillager"; + } + + public EntityType getType() { + return EntityType.VILLAGER; + } + + public Profession getProfession() { + return Profession.values()[getHandle().getProfession() + 1]; // Offset by 1 from the zombie types + } + + public void setProfession(Profession profession) { + Validate.notNull(profession); + Validate.isTrue(!profession.isZombie(), "Profession is reserved for Zombies: ", profession); + getHandle().setProfession(profession.ordinal() - 1); + } + + @Override + public Career getCareer() { + return getCareer(getProfession(), getHandle().careerId); + } + + @Override + public void setCareer(Career career) { + setCareer(career, true); + } + + @Override + public void setCareer(Career career, boolean resetTrades) { + if (career == null) { + getHandle().careerId = 0; // reset career + } else { + Validate.isTrue(career.getProfession() == getProfession(), "Career assignment mismatch. Found (" + getProfession() + ") Required (" + career.getProfession() + ")"); + getHandle().careerId = getCareerID(career); + } + + if (resetTrades) { + getHandle().trades = null; + getHandle().careerLevel = 0; // SPIGOT-4310 + getHandle().populateTrades(); + } + } + + @Override + public Inventory getInventory() { + return new CraftInventory(getHandle().inventory); + } + + private CraftMerchant getMerchant() { + return (merchant == null) ? merchant = new CraftMerchant(getHandle()) : merchant; + } + + @Override + public List getRecipes() { + return getMerchant().getRecipes(); + } + + @Override + public void setRecipes(List recipes) { + this.getMerchant().setRecipes(recipes); + } + + @Override + public MerchantRecipe getRecipe(int i) { + return getMerchant().getRecipe(i); + } + + @Override + public void setRecipe(int i, MerchantRecipe merchantRecipe) { + getMerchant().setRecipe(i, merchantRecipe); + } + + @Override + public int getRecipeCount() { + return getMerchant().getRecipeCount(); + } + + @Override + public boolean isTrading() { + return getTrader() != null; + } + + @Override + public HumanEntity getTrader() { + return getMerchant().getTrader(); + } + + @Override + public int getRiches() { + return getHandle().riches; + } + + @Override + public void setRiches(int riches) { + getHandle().riches = riches; + } + + @Nullable + private static Career getCareer(Profession profession, int id) { + Validate.isTrue(id > 0, "Career id must be greater than 0"); + + List careers = profession.getCareers(); + for (Career c : careers) { + if (careerIDMap.containsKey(c) && careerIDMap.get(c) == id) { + return c; + } + } + + return null; + } + + private static int getCareerID(Career career) { + return careerIDMap.getOrDefault(career, 0); + } + + static { + // build Career -> ID map + int id = 0; + for (Profession prof : Profession.values()) { + List careers = prof.getCareers(); + if (!careers.isEmpty()) { + for (Career c : careers) { + careerIDMap.put(c, ++id); + } + } + + Validate.isTrue(id == careers.size(), "Career id registration mismatch"); + id = 0; + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillagerZombie.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillagerZombie.java new file mode 100644 index 000000000000..64e4fd402c41 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillagerZombie.java @@ -0,0 +1,63 @@ +package org.bukkit.craftbukkit.entity; + +import com.google.common.base.Preconditions; +import java.util.UUID; +import net.minecraft.server.EntityZombieVillager; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Villager; +import org.bukkit.entity.ZombieVillager; + +public class CraftVillagerZombie extends CraftZombie implements ZombieVillager { + + public CraftVillagerZombie(CraftServer server, EntityZombieVillager entity) { + super(server, entity); + } + + @Override + public EntityZombieVillager getHandle() { + return (EntityZombieVillager) super.getHandle(); + } + + @Override + public String toString() { + return "CraftVillagerZombie"; + } + + @Override + public EntityType getType() { + return EntityType.ZOMBIE_VILLAGER; + } + + @Override + public Villager.Profession getVillagerProfession() { + return Villager.Profession.values()[getHandle().getProfession() + Villager.Profession.FARMER.ordinal()]; + } + + @Override + public void setVillagerProfession(Villager.Profession profession) { + getHandle().setProfession(profession == null ? 0 : profession.ordinal() - Villager.Profession.FARMER.ordinal()); + } + + @Override + public boolean isConverting() { + return getHandle().isConverting(); + } + + @Override + public int getConversionTime() { + Preconditions.checkState(isConverting(), "Entity not converting"); + + return getHandle().conversionTime; + } + + @Override + public void setConversionTime(int time) { + if (time < 0) { + getHandle().conversionTime = -1; + getHandle().getDataWatcher().set(EntityZombieVillager.CONVERTING, false); + } else { + getHandle().startConversion((UUID) null, time); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVindicator.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVindicator.java new file mode 100644 index 000000000000..5ff957ced7a8 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVindicator.java @@ -0,0 +1,38 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityVindicator; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Vindicator; + +public class CraftVindicator extends CraftIllager implements Vindicator { + + public CraftVindicator(CraftServer server, EntityVindicator entity) { + super(server, entity); + } + + @Override + public EntityVindicator getHandle() { + return (EntityVindicator) super.getHandle(); + } + + @Override + public String toString() { + return "CraftVindicator"; + } + + @Override + public EntityType getType() { + return EntityType.VINDICATOR; + } + + // Paper start + public boolean isJohnny() { + return getHandle().isJohnny(); + } + + public void setJohnny(boolean johnny) { + getHandle().setJohnny(johnny); + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWaterMob.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWaterMob.java new file mode 100644 index 000000000000..39e8d89bf4e1 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWaterMob.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityWaterAnimal; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.WaterMob; + +public class CraftWaterMob extends CraftCreature implements WaterMob { + + public CraftWaterMob(CraftServer server, EntityWaterAnimal entity) { + super(server, entity); + } + + @Override + public EntityWaterAnimal getHandle() { + return (EntityWaterAnimal) entity; + } + + @Override + public String toString() { + return "CraftWaterMob"; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWeather.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWeather.java new file mode 100644 index 000000000000..91a749334a04 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWeather.java @@ -0,0 +1,26 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityWeather; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Weather; + +public class CraftWeather extends CraftEntity implements Weather { + public CraftWeather(final CraftServer server, final EntityWeather entity) { + super(server, entity); + } + + @Override + public EntityWeather getHandle() { + return (EntityWeather) entity; + } + + @Override + public String toString() { + return "CraftWeather"; + } + + public EntityType getType() { + return EntityType.WEATHER; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWitch.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWitch.java new file mode 100644 index 000000000000..ac465bda2e9c --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWitch.java @@ -0,0 +1,53 @@ +package org.bukkit.craftbukkit.entity; + +import com.destroystokyo.paper.entity.CraftRangedEntity; +import net.minecraft.server.EntityWitch; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.Witch; +import org.bukkit.entity.EntityType; + +// Paper start +import com.google.common.base.Preconditions; +import org.bukkit.Material; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.inventory.ItemStack; +// Paper end + +public class CraftWitch extends CraftMonster implements Witch, CraftRangedEntity { // Paper + public CraftWitch(CraftServer server, EntityWitch entity) { + super(server, entity); + } + + @Override + public EntityWitch getHandle() { + return (EntityWitch) entity; + } + + @Override + public String toString() { + return "CraftWitch"; + } + + public EntityType getType() { + return EntityType.WITCH; + } + + // Paper start + public boolean isDrinkingPotion() { + return getHandle().isDrinkingPotion(); + } + + public int getPotionUseTimeLeft() { + return getHandle().getPotionUseTimeLeft(); + } + + public ItemStack getDrinkingPotion() { + return CraftItemStack.asCraftMirror(getHandle().getItemInMainHand()); + } + + public void setDrinkingPotion(ItemStack potion) { + Preconditions.checkArgument(potion == null || potion.getType().isEmpty() || potion.getType() == Material.POTION, "must be potion, air, or null"); + getHandle().setDrinkingPotion(CraftItemStack.asNMSCopy(potion)); + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java new file mode 100644 index 000000000000..a1d04ff2846a --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java @@ -0,0 +1,41 @@ +package org.bukkit.craftbukkit.entity; + +import com.destroystokyo.paper.entity.CraftRangedEntity; +import net.minecraft.server.EntityWither; +import org.bukkit.boss.BossBar; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.boss.CraftBossBar; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Wither; + +public class CraftWither extends CraftMonster implements Wither, CraftRangedEntity { // Paper + + private BossBar bossBar; + + public CraftWither(CraftServer server, EntityWither entity) { + super(server, entity); + + if (entity.bossBattle != null) { + this.bossBar = new CraftBossBar(entity.bossBattle); + } + } + + @Override + public EntityWither getHandle() { + return (EntityWither) entity; + } + + @Override + public String toString() { + return "CraftWither"; + } + + public EntityType getType() { + return EntityType.WITHER; + } + + @Override + public BossBar getBossBar() { + return bossBar; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWitherSkeleton.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWitherSkeleton.java new file mode 100644 index 000000000000..6fb1da7da193 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWitherSkeleton.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntitySkeletonWither; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.WitherSkeleton; + +public class CraftWitherSkeleton extends CraftSkeleton implements WitherSkeleton { + + public CraftWitherSkeleton(CraftServer server, EntitySkeletonWither entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftWitherSkeleton"; + } + + @Override + public EntityType getType() { + return EntityType.WITHER_SKELETON; + } + + @Override + public SkeletonType getSkeletonType() { + return SkeletonType.WITHER; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWitherSkull.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWitherSkull.java new file mode 100644 index 000000000000..563177e9a2be --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWitherSkull.java @@ -0,0 +1,36 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityWitherSkull; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.WitherSkull; + +public class CraftWitherSkull extends CraftFireball implements WitherSkull { + public CraftWitherSkull(CraftServer server, EntityWitherSkull entity) { + super(server, entity); + } + + @Override + public void setCharged(boolean charged) { + getHandle().setCharged(charged); + } + + @Override + public boolean isCharged() { + return getHandle().isCharged(); + } + + @Override + public EntityWitherSkull getHandle() { + return (EntityWitherSkull) entity; + } + + @Override + public String toString() { + return "CraftWitherSkull"; + } + + public EntityType getType() { + return EntityType.WITHER_SKULL; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java new file mode 100644 index 000000000000..55ce37c7f642 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWolf.java @@ -0,0 +1,40 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityWolf; +import net.minecraft.server.EnumColor; +import org.bukkit.DyeColor; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Wolf; + +public class CraftWolf extends CraftTameableAnimal implements Wolf { + public CraftWolf(CraftServer server, EntityWolf wolf) { + super(server, wolf); + } + + public boolean isAngry() { + return getHandle().isAngry(); + } + + public void setAngry(boolean angry) { + getHandle().setAngry(angry); + } + + @Override + public EntityWolf getHandle() { + return (EntityWolf) entity; + } + + @Override + public EntityType getType() { + return EntityType.WOLF; + } + + public DyeColor getCollarColor() { + return DyeColor.getByWoolData((byte) getHandle().getCollarColor().getColorIndex()); + } + + public void setCollarColor(DyeColor color) { + getHandle().setCollarColor(EnumColor.fromColorIndex(color.getWoolData())); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java new file mode 100644 index 000000000000..c516d347593e --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java @@ -0,0 +1,110 @@ +package org.bukkit.craftbukkit.entity; + +import com.google.common.base.Preconditions; +import net.minecraft.server.EntityZombie; +import net.minecraft.server.EntityZombieVillager; + +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Villager; +import org.bukkit.entity.Zombie; + +public class CraftZombie extends CraftMonster implements Zombie { + + public CraftZombie(CraftServer server, EntityZombie entity) { + super(server, entity); + } + + @Override + public EntityZombie getHandle() { + return (EntityZombie) entity; + } + + @Override + public String toString() { + return "CraftZombie"; + } + + public EntityType getType() { + return EntityType.ZOMBIE; + } + + public boolean isBaby() { + return getHandle().isBaby(); + } + + public void setBaby(boolean flag) { + getHandle().setBaby(flag); + } + + public boolean isVillager() { + return getHandle() instanceof EntityZombieVillager; + } + + @Override + public void setVillager(boolean flag) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void setVillagerProfession(Villager.Profession profession) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public Villager.Profession getVillagerProfession() { + return null; + } + + @Override + public boolean isConverting() { + return getHandle().isDrownConverting(); + } + + @Override + public int getConversionTime() { + Preconditions.checkState(isConverting(), "Entity not converting"); + + return getHandle().drownedConversionTime; + } + + @Override + public void setConversionTime(int time) { + if (time < 0) { + getHandle().drownedConversionTime = -1; + getHandle().getDataWatcher().set(EntityZombie.DROWN_CONVERTING, false); + } else { + getHandle().startDrownedConversion(time); + } + } + + // Paper start + public boolean isDrowning() { + return getHandle().isDrowning(); + } + + public void startDrowning(int drownedConversionTime) { + getHandle().startDrownedConversion(drownedConversionTime); + } + + public void stopDrowning() { + getHandle().stopDrowning(); + } + + public void setArmsRaised(boolean raised) { + getHandle().setArmsRaised(raised); + } + + public boolean isArmsRaised() { + return getHandle().isArmsRaised(); + } + + public boolean shouldBurnInDay() { + return getHandle().shouldBurnInDay(); + } + + public void setShouldBurnInDay(boolean shouldBurnInDay) { + getHandle().setShouldBurnInDay(shouldBurnInDay); + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftZombieHorse.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftZombieHorse.java new file mode 100644 index 000000000000..5b31b68168e8 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftZombieHorse.java @@ -0,0 +1,29 @@ +package org.bukkit.craftbukkit.entity; + +import net.minecraft.server.EntityHorseZombie; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Horse.Variant; +import org.bukkit.entity.ZombieHorse; + +public class CraftZombieHorse extends CraftAbstractHorse implements ZombieHorse { + + public CraftZombieHorse(CraftServer server, EntityHorseZombie entity) { + super(server, entity); + } + + @Override + public String toString() { + return "CraftZombieHorse"; + } + + @Override + public EntityType getType() { + return EntityType.ZOMBIE_HORSE; + } + + @Override + public Variant getVariant() { + return Variant.UNDEAD_HORSE; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java new file mode 100644 index 000000000000..2e0b4de83b28 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -0,0 +1,1443 @@ +package org.bukkit.craftbukkit.event; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; + +import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.collect.Lists; + +import net.minecraft.server.*; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.Statistic.Type; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftStatistic; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.block.CraftBlockState; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.inventory.CraftInventoryCrafting; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.inventory.CraftMetaBook; +import org.bukkit.craftbukkit.potion.CraftPotionUtil; +import org.bukkit.craftbukkit.util.CraftDamageSource; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.entity.AreaEffectCloud; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.Bat; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.ExperienceOrb; +import org.bukkit.entity.Firework; +import org.bukkit.entity.LightningStrike; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Pig; +import org.bukkit.entity.PigZombie; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.ThrownExpBottle; +import org.bukkit.entity.ThrownPotion; +import org.bukkit.entity.ExperienceOrb; // Paper +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.Event.Result; +import org.bukkit.event.block.*; +import org.bukkit.event.block.BlockIgniteEvent.IgniteCause; +import org.bukkit.event.entity.*; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.entity.EntityDamageEvent.DamageModifier; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; +import org.bukkit.event.inventory.PrepareAnvilEvent; +import org.bukkit.event.inventory.PrepareItemCraftEvent; +import org.bukkit.event.player.*; +import org.bukkit.event.player.PlayerBedEnterEvent.BedEnterResult; +import org.bukkit.event.server.ServerListPingEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.entity.AbstractHorse; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.vehicle.VehicleCreateEvent; +import org.bukkit.potion.PotionEffect; + +public class CraftEventFactory { + public static final DamageSource MELTING = CraftDamageSource.copyOf(DamageSource.BURN); + public static final DamageSource POISON = CraftDamageSource.copyOf(DamageSource.MAGIC); + public static org.bukkit.block.Block blockDamage; // For use in EntityDamageByBlockEvent + public static Entity entityDamage; // For use in EntityDamageByEntityEvent + public static boolean isWorldGen(GeneratorAccess world) { return world instanceof net.minecraft.server.RegionLimitedWorldAccess; } // Paper + + // helper methods + private static boolean canBuild(CraftWorld world, Player player, int x, int z) { + WorldServer worldServer = world.getHandle(); + int spawnSize = Bukkit.getServer().getSpawnRadius(); + + if (world.getHandle().dimension != DimensionManager.OVERWORLD) return true; + if (spawnSize <= 0) return true; + if (((CraftServer) Bukkit.getServer()).getHandle().getOPs().isEmpty()) return true; + if (player.isOp()) return true; + + BlockPosition chunkcoordinates = worldServer.getSpawn(); + + int distanceFromSpawn = Math.max(Math.abs(x - chunkcoordinates.getX()), Math.abs(z - chunkcoordinates.getZ())); + return distanceFromSpawn > spawnSize; + } + + public static T callEvent(T event) { + Bukkit.getServer().getPluginManager().callEvent(event); + return event; + } + + /** + * PlayerBedEnterEvent + */ + public static EntityHuman.EnumBedResult callPlayerBedEnterEvent(EntityHuman player, BlockPosition bed, EntityHuman.EnumBedResult nmsBedResult) { + BedEnterResult bedEnterResult; + switch (nmsBedResult) { + case OK: + bedEnterResult = BedEnterResult.OK; + break; + case NOT_POSSIBLE_HERE: + bedEnterResult = BedEnterResult.NOT_POSSIBLE_HERE; + break; + case NOT_POSSIBLE_NOW: + bedEnterResult = BedEnterResult.NOT_POSSIBLE_NOW; + break; + case TOO_FAR_AWAY: + bedEnterResult = BedEnterResult.TOO_FAR_AWAY; + break; + case NOT_SAFE: + bedEnterResult = BedEnterResult.NOT_SAFE; + break; + default: + bedEnterResult = BedEnterResult.OTHER_PROBLEM; + } + + PlayerBedEnterEvent event = new PlayerBedEnterEvent((Player) player.getBukkitEntity(), CraftBlock.at(player.world, bed), bedEnterResult); + Bukkit.getServer().getPluginManager().callEvent(event); + + Result result = event.useBed(); + if (result == Result.ALLOW) { + return EntityHuman.EnumBedResult.OK; + } else if (result == Result.DENY) { + return EntityHuman.EnumBedResult.OTHER_PROBLEM; + } + + return nmsBedResult; + } + + /** + * Block place methods + */ + public static BlockMultiPlaceEvent callBlockMultiPlaceEvent(World world, EntityHuman who, EnumHand hand, List blockStates, int clickedX, int clickedY, int clickedZ) { + CraftWorld craftWorld = world.getWorld(); + CraftServer craftServer = world.getServer(); + Player player = (Player) who.getBukkitEntity(); + + Block blockClicked = craftWorld.getBlockAt(clickedX, clickedY, clickedZ); + + boolean canBuild = true; + for (int i = 0; i < blockStates.size(); i++) { + if (!canBuild(craftWorld, player, blockStates.get(i).getX(), blockStates.get(i).getZ())) { + canBuild = false; + break; + } + } + + org.bukkit.inventory.ItemStack item; + if (hand == EnumHand.MAIN_HAND) { + item = player.getInventory().getItemInMainHand(); + } else { + item = player.getInventory().getItemInOffHand(); + } + + BlockMultiPlaceEvent event = new BlockMultiPlaceEvent(blockStates, blockClicked, item, player, canBuild); + craftServer.getPluginManager().callEvent(event); + + return event; + } + + public static BlockPlaceEvent callBlockPlaceEvent(World world, EntityHuman who, EnumHand hand, BlockState replacedBlockState, int clickedX, int clickedY, int clickedZ) { + CraftWorld craftWorld = world.getWorld(); + CraftServer craftServer = world.getServer(); + + Player player = (Player) who.getBukkitEntity(); + + Block blockClicked = craftWorld.getBlockAt(clickedX, clickedY, clickedZ); + Block placedBlock = replacedBlockState.getBlock(); + + boolean canBuild = canBuild(craftWorld, player, placedBlock.getX(), placedBlock.getZ()); + + org.bukkit.inventory.ItemStack item; + EquipmentSlot equipmentSlot; + if (hand == EnumHand.MAIN_HAND) { + item = player.getInventory().getItemInMainHand(); + equipmentSlot = EquipmentSlot.HAND; + } else { + item = player.getInventory().getItemInOffHand(); + equipmentSlot = EquipmentSlot.OFF_HAND; + } + + BlockPlaceEvent event = new BlockPlaceEvent(placedBlock, replacedBlockState, blockClicked, item, player, canBuild, equipmentSlot); + craftServer.getPluginManager().callEvent(event); + + return event; + } + + public static void handleBlockDropItemEvent(Block block, BlockState state, EntityPlayer player, List items) { + BlockDropItemEvent event = new BlockDropItemEvent(block, state, player.getBukkitEntity(), Lists.transform(items, (item) -> (org.bukkit.entity.Item) item.getBukkitEntity())); + Bukkit.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + for (EntityItem item : items) { + item.world.addEntity(item); + } + } + } + + public static EntityPlaceEvent callEntityPlaceEvent(ItemActionContext itemactioncontext, Entity entity) { + Player who = (itemactioncontext.getEntity() == null) ? null : (Player) itemactioncontext.getEntity().getBukkitEntity(); + org.bukkit.block.Block blockClicked = CraftBlock.at(itemactioncontext.getWorld(), itemactioncontext.getClickPosition()); + org.bukkit.block.BlockFace blockFace = org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(itemactioncontext.getClickedFace()); + + EntityPlaceEvent event = new EntityPlaceEvent(entity.getBukkitEntity(), who, blockClicked, blockFace); + entity.world.getServer().getPluginManager().callEvent(event); + + return event; + } + + /** + * Bucket methods + */ + public static PlayerBucketEmptyEvent callPlayerBucketEmptyEvent(EntityHuman who, int clickedX, int clickedY, int clickedZ, EnumDirection clickedFace, ItemStack itemInHand) { + return (PlayerBucketEmptyEvent) getPlayerBucketEvent(false, who, clickedX, clickedY, clickedZ, clickedFace, itemInHand, Items.BUCKET); + } + + public static PlayerBucketFillEvent callPlayerBucketFillEvent(EntityHuman who, int clickedX, int clickedY, int clickedZ, EnumDirection clickedFace, ItemStack itemInHand, net.minecraft.server.Item bucket) { + return (PlayerBucketFillEvent) getPlayerBucketEvent(true, who, clickedX, clickedY, clickedZ, clickedFace, itemInHand, bucket); + } + + private static PlayerEvent getPlayerBucketEvent(boolean isFilling, EntityHuman who, int clickedX, int clickedY, int clickedZ, EnumDirection clickedFace, ItemStack itemstack, net.minecraft.server.Item item) { + // Paper start - add EnumHand + return getPlayerBucketEvent(isFilling, who, clickedX, clickedY, clickedZ, clickedFace, itemstack, item, null); + } + + public static PlayerBucketEmptyEvent callPlayerBucketEmptyEvent(EntityHuman who, int clickedX, int clickedY, int clickedZ, EnumDirection clickedFace, ItemStack itemInHand, EnumHand enumHand) { + return (PlayerBucketEmptyEvent) getPlayerBucketEvent(false, who, clickedX, clickedY, clickedZ, clickedFace, itemInHand, Items.BUCKET, enumHand); + } + + public static PlayerBucketFillEvent callPlayerBucketFillEvent(EntityHuman who, int clickedX, int clickedY, int clickedZ, EnumDirection clickedFace, ItemStack itemInHand, net.minecraft.server.Item bucket, EnumHand enumHand) { + return (PlayerBucketFillEvent) getPlayerBucketEvent(true, who, clickedX, clickedY, clickedZ, clickedFace, itemInHand, bucket, enumHand); + } + + private static PlayerEvent getPlayerBucketEvent(boolean isFilling, EntityHuman who, int clickedX, int clickedY, int clickedZ, EnumDirection clickedFace, ItemStack itemstack, net.minecraft.server.Item item, EnumHand enumHand) { + // Paper end + Player player = (who == null) ? null : (Player) who.getBukkitEntity(); + CraftItemStack itemInHand = CraftItemStack.asNewCraftStack(item); + Material bucket = CraftMagicNumbers.getMaterial(itemstack.getItem()); + + CraftWorld craftWorld = (CraftWorld) player.getWorld(); + CraftServer craftServer = (CraftServer) player.getServer(); + + Block blockClicked = craftWorld.getBlockAt(clickedX, clickedY, clickedZ); + BlockFace blockFace = CraftBlock.notchToBlockFace(clickedFace); + + PlayerEvent event = null; + if (isFilling) { + event = new PlayerBucketFillEvent(player, blockClicked, blockFace, bucket, itemInHand, enumHand == null ? null : enumHand == EnumHand.OFF_HAND ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND); // Paper - add enumHand + ((PlayerBucketFillEvent) event).setCancelled(!canBuild(craftWorld, player, clickedX, clickedZ)); + } else { + event = new PlayerBucketEmptyEvent(player, blockClicked, blockFace, bucket, itemInHand, enumHand == null ? null : enumHand == EnumHand.OFF_HAND ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND); // Paper - add enumHand + ((PlayerBucketEmptyEvent) event).setCancelled(!canBuild(craftWorld, player, clickedX, clickedZ)); + } + + craftServer.getPluginManager().callEvent(event); + + return event; + } + + /** + * Player Interact event + */ + public static PlayerInteractEvent callPlayerInteractEvent(EntityHuman who, Action action, ItemStack itemstack, EnumHand hand) { + if (action != Action.LEFT_CLICK_AIR && action != Action.RIGHT_CLICK_AIR) { + throw new AssertionError(String.format("%s performing %s with %s", who, action, itemstack)); + } + return callPlayerInteractEvent(who, action, null, EnumDirection.SOUTH, itemstack, hand); + } + + public static PlayerInteractEvent callPlayerInteractEvent(EntityHuman who, Action action, BlockPosition position, EnumDirection direction, ItemStack itemstack, EnumHand hand) { + return callPlayerInteractEvent(who, action, position, direction, itemstack, false, hand); + } + + public static PlayerInteractEvent callPlayerInteractEvent(EntityHuman who, Action action, BlockPosition position, EnumDirection direction, ItemStack itemstack, boolean cancelledBlock, EnumHand hand) { + Player player = (who == null) ? null : (Player) who.getBukkitEntity(); + CraftItemStack itemInHand = CraftItemStack.asCraftMirror(itemstack); + + CraftWorld craftWorld = (CraftWorld) player.getWorld(); + CraftServer craftServer = (CraftServer) player.getServer(); + + Block blockClicked = null; + if (position != null) { + blockClicked = craftWorld.getBlockAt(position.getX(), position.getY(), position.getZ()); + } else { + switch (action) { + case LEFT_CLICK_BLOCK: + action = Action.LEFT_CLICK_AIR; + break; + case RIGHT_CLICK_BLOCK: + action = Action.RIGHT_CLICK_AIR; + break; + } + } + BlockFace blockFace = CraftBlock.notchToBlockFace(direction); + + if (itemInHand.getType() == Material.AIR || itemInHand.getAmount() == 0) { + itemInHand = null; + } + + PlayerInteractEvent event = new PlayerInteractEvent(player, action, itemInHand, blockClicked, blockFace, (hand == null) ? null : ((hand == EnumHand.OFF_HAND) ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND)); + if (cancelledBlock) { + event.setUseInteractedBlock(Event.Result.DENY); + } + craftServer.getPluginManager().callEvent(event); + + return event; + } + + /** + * EntityTransformEvent + */ + public static EntityTransformEvent callEntityTransformEvent(EntityLiving original, EntityLiving coverted, EntityTransformEvent.TransformReason transformReason) { + return callEntityTransformEvent(original, Collections.singletonList(coverted), transformReason); + } + + /** + * EntityTransformEvent + */ + public static EntityTransformEvent callEntityTransformEvent(EntityLiving original, List convertedList, EntityTransformEvent.TransformReason convertType) { + List list = new ArrayList<>(); + for (EntityLiving entityLiving : convertedList) { + list.add(entityLiving.getBukkitEntity()); + } + + EntityTransformEvent event = new EntityTransformEvent(original.getBukkitEntity(), list, convertType); + Bukkit.getPluginManager().callEvent(event); + + return event; + } + + /** + * EntityShootBowEvent + */ + public static EntityShootBowEvent callEntityShootBowEvent(EntityLiving who, /*bow*/ItemStack itemstack, /*arrow*/ ItemStack arrowItem, EntityArrow entityArrow, float force) { // Paper + LivingEntity shooter = (LivingEntity) who.getBukkitEntity(); + CraftItemStack itemInHand = CraftItemStack.asCraftMirror(itemstack); + Arrow arrow = (Arrow) entityArrow.getBukkitEntity(); + + if (itemInHand != null && (itemInHand.getType() == Material.AIR || itemInHand.getAmount() == 0)) { + itemInHand = null; + } + + EntityShootBowEvent event = new EntityShootBowEvent(shooter, itemInHand, CraftItemStack.asCraftMirror(arrowItem), arrow, force); // Paper + Bukkit.getPluginManager().callEvent(event); + + return event; + } + + /** + * BlockDamageEvent + */ + public static BlockDamageEvent callBlockDamageEvent(EntityHuman who, int x, int y, int z, ItemStack itemstack, boolean instaBreak) { + Player player = (who == null) ? null : (Player) who.getBukkitEntity(); + CraftItemStack itemInHand = CraftItemStack.asCraftMirror(itemstack); + + CraftWorld craftWorld = (CraftWorld) player.getWorld(); + CraftServer craftServer = (CraftServer) player.getServer(); + + Block blockClicked = craftWorld.getBlockAt(x, y, z); + + BlockDamageEvent event = new BlockDamageEvent(player, blockClicked, itemInHand, instaBreak); + craftServer.getPluginManager().callEvent(event); + + return event; + } + + public static boolean doEntityAddEventCalling(World world, Entity entity, SpawnReason spawnReason){ + if (entity == null) return false; + + org.bukkit.event.Cancellable event = null; + if (entity instanceof EntityLiving && !(entity instanceof EntityPlayer)) { + boolean isAnimal = entity instanceof EntityAnimal || entity instanceof EntityWaterAnimal || entity instanceof EntityGolem; + boolean isMonster = entity instanceof EntityMonster || entity instanceof EntityGhast || entity instanceof EntitySlime; + boolean isNpc = entity instanceof NPC; + + if (spawnReason != SpawnReason.CUSTOM) { + if (isAnimal && !world.allowAnimals || isMonster && !world.allowMonsters || isNpc && !world.getServer().getServer().getSpawnNPCs()) { + entity.dead = true; + return false; + } + } + + event = CraftEventFactory.callCreatureSpawnEvent((EntityLiving) entity, spawnReason); + } else if (entity instanceof EntityItem) { + event = CraftEventFactory.callItemSpawnEvent((EntityItem) entity); + } else if (entity.getBukkitEntity() instanceof org.bukkit.entity.Projectile) { + // Not all projectiles extend EntityProjectile, so check for Bukkit interface instead + event = CraftEventFactory.callProjectileLaunchEvent(entity); + } else if (entity.getBukkitEntity() instanceof org.bukkit.entity.Vehicle){ + event = CraftEventFactory.callVehicleCreateEvent(entity); + // Spigot start + } else if (entity instanceof EntityExperienceOrb) { + EntityExperienceOrb xp = (EntityExperienceOrb) entity; + double radius = world.spigotConfig.expMerge; + if (radius > 0) { + // Paper start - Maximum exp value when merging - Whole section has been tweaked, see comments for specifics + final int maxValue = world.paperConfig.expMergeMaxValue; + final boolean mergeUnconditionally = world.paperConfig.expMergeMaxValue <= 0; + if (mergeUnconditionally || xp.value < maxValue) { // Paper - Skip iteration if unnecessary + + List entities = world.getEntities(entity, entity.getBoundingBox().grow(radius, radius, radius)); + for (Entity e : entities) { + if (e instanceof EntityExperienceOrb) { + EntityExperienceOrb loopItem = (EntityExperienceOrb) e; + // Paper start + if (!loopItem.dead && !(maxValue > 0 && loopItem.value >= maxValue) && new com.destroystokyo.paper.event.entity.ExperienceOrbMergeEvent((org.bukkit.entity.ExperienceOrb) entity.getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) loopItem.getBukkitEntity()).callEvent()) { // Paper + long newTotal = (long)xp.value + (long)loopItem.value; + if ((int) newTotal < 0) continue; // Overflow + if (maxValue > 0 && newTotal > (long)maxValue) { + loopItem.value = (int) (newTotal - maxValue); + xp.value = maxValue; + } else { + xp.value += loopItem.value; + loopItem.die(); + } + // Paper end + } + } + } + + } // Paper end - End iteration skip check - All tweaking ends here + } + // Spigot end + } else { + event = CraftEventFactory.callEntitySpawnEvent(entity); + } + + if (event != null && (event.isCancelled() || entity.dead)) { + Entity vehicle = entity.getVehicle(); + if (vehicle != null) { + vehicle.dead = true; + } + for (Entity passenger : entity.getAllPassengers()) { + passenger.dead = true; + } + entity.dead = true; + return false; + } + + return true; + } + + /** + * EntitySpawnEvent + */ + public static EntitySpawnEvent callEntitySpawnEvent(Entity entity) { + org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity(); + + EntitySpawnEvent event = new EntitySpawnEvent(bukkitEntity); + bukkitEntity.getServer().getPluginManager().callEvent(event); + return event; + } + + /** + * CreatureSpawnEvent + */ + public static CreatureSpawnEvent callCreatureSpawnEvent(EntityLiving entityliving, SpawnReason spawnReason) { + LivingEntity entity = (LivingEntity) entityliving.getBukkitEntity(); + CraftServer craftServer = (CraftServer) entity.getServer(); + + CreatureSpawnEvent event = new CreatureSpawnEvent(entity, spawnReason); + if (isWorldGen(entityliving.world)) return event; // Paper - do not call during world gen + craftServer.getPluginManager().callEvent(event); + return event; + } + + /** + * EntityTameEvent + */ + public static EntityTameEvent callEntityTameEvent(EntityInsentient entity, EntityHuman tamer) { + org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity(); + org.bukkit.entity.AnimalTamer bukkitTamer = (tamer != null ? tamer.getBukkitEntity() : null); + CraftServer craftServer = (CraftServer) bukkitEntity.getServer(); + + entity.persistent = true; + + EntityTameEvent event = new EntityTameEvent((LivingEntity) bukkitEntity, bukkitTamer); + craftServer.getPluginManager().callEvent(event); + return event; + } + + /** + * ItemSpawnEvent + */ + public static ItemSpawnEvent callItemSpawnEvent(EntityItem entityitem) { + org.bukkit.entity.Item entity = (org.bukkit.entity.Item) entityitem.getBukkitEntity(); + CraftServer craftServer = (CraftServer) entity.getServer(); + + ItemSpawnEvent event = new ItemSpawnEvent(entity); + + craftServer.getPluginManager().callEvent(event); + return event; + } + + /** + * ItemDespawnEvent + */ + public static ItemDespawnEvent callItemDespawnEvent(EntityItem entityitem) { + org.bukkit.entity.Item entity = (org.bukkit.entity.Item) entityitem.getBukkitEntity(); + + ItemDespawnEvent event = new ItemDespawnEvent(entity, entity.getLocation()); + + entity.getServer().getPluginManager().callEvent(event); + return event; + } + + /** + * ItemMergeEvent + */ + public static ItemMergeEvent callItemMergeEvent(EntityItem merging, EntityItem mergingWith) { + org.bukkit.entity.Item entityMerging = (org.bukkit.entity.Item) merging.getBukkitEntity(); + org.bukkit.entity.Item entityMergingWith = (org.bukkit.entity.Item) mergingWith.getBukkitEntity(); + + ItemMergeEvent event = new ItemMergeEvent(entityMerging, entityMergingWith); + + Bukkit.getPluginManager().callEvent(event); + return event; + } + + /** + * PotionSplashEvent + */ + public static PotionSplashEvent callPotionSplashEvent(EntityPotion potion, Map affectedEntities) { + ThrownPotion thrownPotion = (ThrownPotion) potion.getBukkitEntity(); + + PotionSplashEvent event = new PotionSplashEvent(thrownPotion, affectedEntities); + Bukkit.getPluginManager().callEvent(event); + return event; + } + + public static LingeringPotionSplashEvent callLingeringPotionSplashEvent(EntityPotion potion, EntityAreaEffectCloud cloud) { + ThrownPotion thrownPotion = (ThrownPotion) potion.getBukkitEntity(); + AreaEffectCloud effectCloud = (AreaEffectCloud) cloud.getBukkitEntity(); + + LingeringPotionSplashEvent event = new LingeringPotionSplashEvent(thrownPotion, effectCloud); + Bukkit.getPluginManager().callEvent(event); + return event; + } + + /** + * BlockFadeEvent + */ + public static BlockFadeEvent callBlockFadeEvent(GeneratorAccess world, BlockPosition pos, IBlockData newBlock) { + CraftBlockState state = CraftBlockState.getBlockState(world, pos); + state.setData(newBlock); + + BlockFadeEvent event = new BlockFadeEvent(state.getBlock(), state); + Bukkit.getPluginManager().callEvent(event); + return event; + } + + public static boolean handleMoistureChangeEvent(World world, BlockPosition pos, IBlockData newBlock, int flag) { + CraftBlockState state = CraftBlockState.getBlockState(world, pos, flag); + state.setData(newBlock); + + MoistureChangeEvent event = new MoistureChangeEvent(state.getBlock(), state); + Bukkit.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + state.update(true); + } + return !event.isCancelled(); + } + + public static boolean handleBlockSpreadEvent(World world, BlockPosition source, BlockPosition target, IBlockData block) { + return handleBlockSpreadEvent(world, source, target, block, 2); + } + + public static boolean handleBlockSpreadEvent(World world, BlockPosition source, BlockPosition target, IBlockData block, int flag) { + CraftBlockState state = CraftBlockState.getBlockState(world, target, flag); + state.setData(block); + + BlockSpreadEvent event = new BlockSpreadEvent(world.getWorld().getBlockAt(target.getX(), target.getY(), target.getZ()), world.getWorld().getBlockAt(source.getX(), source.getY(), source.getZ()), state); + Bukkit.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + state.update(true); + } + return !event.isCancelled(); + } + + public static EntityDeathEvent callEntityDeathEvent(EntityLiving victim) { + return callEntityDeathEvent(victim, new ArrayList(0)); + } + + public static EntityDeathEvent callEntityDeathEvent(EntityLiving victim, List drops) { + CraftLivingEntity entity = (CraftLivingEntity) victim.getBukkitEntity(); + EntityDeathEvent event = new EntityDeathEvent(entity, drops, victim.getExpReward()); + populateFields(victim, event); // Paper - make cancellable + CraftWorld world = (CraftWorld) entity.getWorld(); + Bukkit.getServer().getPluginManager().callEvent(event); + + // Paper start - make cancellable + if (event.isCancelled()) { + return event; + } + playDeathSound(victim, event); + // Paper end + victim.expToDrop = event.getDroppedExp(); + + for (org.bukkit.inventory.ItemStack stack : event.getDrops()) { + if (stack == null || stack.getType() == Material.AIR || stack.getAmount() == 0) continue; + + world.dropItem(entity.getLocation(), stack); + } + + return event; + } + + public static PlayerDeathEvent callPlayerDeathEvent(EntityPlayer victim, List drops, String deathMessage, boolean keepInventory) { + CraftPlayer entity = victim.getBukkitEntity(); + PlayerDeathEvent event = new PlayerDeathEvent(entity, drops, victim.getExpReward(), 0, deathMessage); + event.setKeepInventory(keepInventory); + populateFields(victim, event); // Paper - make cancellable + org.bukkit.World world = entity.getWorld(); + Bukkit.getServer().getPluginManager().callEvent(event); + // Paper start - make cancellable + if (event.isCancelled()) { + return event; + } + playDeathSound(victim, event); + // Paper end + + victim.keepLevel = event.getKeepLevel(); + victim.newLevel = event.getNewLevel(); + victim.newTotalExp = event.getNewTotalExp(); + victim.expToDrop = event.getDroppedExp(); + victim.newExp = event.getNewExp(); + + if (event.getKeepInventory()) { + return event; + } + + for (org.bukkit.inventory.ItemStack stack : event.getDrops()) { + if (stack == null || stack.getType() == Material.AIR) continue; + + world.dropItem(entity.getLocation(), stack); + } + + return event; + } + + // Paper start - helper methods for making death event cancellable + // Add information to death event + private static void populateFields(EntityLiving victim, EntityDeathEvent event) { + event.setReviveHealth(event.getEntity().getAttribute(org.bukkit.attribute.Attribute.GENERIC_MAX_HEALTH).getValue()); + event.setShouldPlayDeathSound(!victim.silentDeath && !victim.isSilent()); + SoundEffect soundEffect = victim.getDeathSoundEffect(); + event.setDeathSound(soundEffect != null ? org.bukkit.craftbukkit.CraftSound.getSoundByEffect(soundEffect) : null); + event.setDeathSoundCategory(org.bukkit.SoundCategory.valueOf(victim.getDeathSoundCategory().name())); + event.setDeathSoundVolume(victim.getDeathSoundVolume()); + event.setDeathSoundPitch(victim.getDeathSoundPitch()); + } + + // Play death sound manually + private static void playDeathSound(EntityLiving victim, EntityDeathEvent event) { + if (event.shouldPlayDeathSound() && event.getDeathSound() != null && event.getDeathSoundCategory() != null) { + EntityHuman source = victim instanceof EntityHuman ? (EntityHuman) victim : null; + double x = event.getEntity().getLocation().getX(); + double y = event.getEntity().getLocation().getY(); + double z = event.getEntity().getLocation().getZ(); + SoundEffect soundEffect = org.bukkit.craftbukkit.CraftSound.getSoundEffect(event.getDeathSound()); + SoundCategory soundCategory = SoundCategory.valueOf(event.getDeathSoundCategory().name()); + victim.world.sendSoundEffect(source, x, y, z, soundEffect, soundCategory, event.getDeathSoundVolume(), event.getDeathSoundPitch()); + } + } + // Paper end + /** + * Server methods + */ + public static ServerListPingEvent callServerListPingEvent(Server craftServer, InetAddress address, String motd, int numPlayers, int maxPlayers) { + ServerListPingEvent event = new ServerListPingEvent(address, motd, numPlayers, maxPlayers); + craftServer.getPluginManager().callEvent(event); + return event; + } + + private static EntityDamageEvent handleEntityDamageEvent(Entity entity, DamageSource source, Map modifiers, Map> modifierFunctions) { + if (source.isExplosion()) { + DamageCause damageCause; + Entity damager = entityDamage; + entityDamage = null; + EntityDamageEvent event; + if (damager == null) { + event = new EntityDamageByBlockEvent(null, entity.getBukkitEntity(), DamageCause.BLOCK_EXPLOSION, modifiers, modifierFunctions); + } else if (entity instanceof EntityEnderDragon && /*PAIL FIXME ((EntityEnderDragon) entity).target == damager*/ false) { + event = new EntityDamageEvent(entity.getBukkitEntity(), DamageCause.ENTITY_EXPLOSION, modifiers, modifierFunctions); + } else { + if (damager instanceof org.bukkit.entity.TNTPrimed) { + damageCause = DamageCause.BLOCK_EXPLOSION; + } else { + damageCause = DamageCause.ENTITY_EXPLOSION; + } + event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), entity.getBukkitEntity(), damageCause, modifiers, modifierFunctions); + } + + callEvent(event); + + if (!event.isCancelled()) { + event.getEntity().setLastDamageCause(event); + } + return event; + } else if (source instanceof EntityDamageSource) { + Entity damager = source.getEntity(); + DamageCause cause = (source.isSweep()) ? DamageCause.ENTITY_SWEEP_ATTACK : DamageCause.ENTITY_ATTACK; + + if (source instanceof EntityDamageSourceIndirect) { + damager = ((EntityDamageSourceIndirect) source).getProximateDamageSource(); + if (damager.getBukkitEntity() instanceof ThrownPotion) { + cause = DamageCause.MAGIC; + } else if (damager.getBukkitEntity() instanceof Projectile) { + cause = DamageCause.PROJECTILE; + } + } else if ("thorns".equals(source.translationIndex)) { + cause = DamageCause.THORNS; + } + + return callEntityDamageEvent(damager, entity, cause, modifiers, modifierFunctions); + } else if (source == DamageSource.OUT_OF_WORLD) { + EntityDamageEvent event = callEvent(new EntityDamageByBlockEvent(null, entity.getBukkitEntity(), DamageCause.VOID, modifiers, modifierFunctions)); + if (!event.isCancelled()) { + event.getEntity().setLastDamageCause(event); + } + return event; + } else if (source == DamageSource.LAVA) { + EntityDamageEvent event = callEvent(new EntityDamageByBlockEvent(null, entity.getBukkitEntity(), DamageCause.LAVA, modifiers, modifierFunctions)); + if (!event.isCancelled()) { + event.getEntity().setLastDamageCause(event); + } + return event; + } else if (blockDamage != null) { + DamageCause cause = null; + Block damager = blockDamage; + blockDamage = null; + if (source == DamageSource.CACTUS) { + cause = DamageCause.CONTACT; + } else if (source == DamageSource.HOT_FLOOR) { + cause = DamageCause.HOT_FLOOR; + } else if (source == DamageSource.MAGIC) { + cause = DamageCause.MAGIC; + } else { + throw new IllegalStateException(String.format("Unhandled damage of %s by %s from %s", entity, damager, source.translationIndex)); + } + EntityDamageEvent event = callEvent(new EntityDamageByBlockEvent(damager, entity.getBukkitEntity(), cause, modifiers, modifierFunctions)); + if (!event.isCancelled()) { + event.getEntity().setLastDamageCause(event); + } + return event; + } else if (entityDamage != null) { + DamageCause cause = null; + CraftEntity damager = entityDamage.getBukkitEntity(); + entityDamage = null; + if (source == DamageSource.ANVIL || source == DamageSource.FALLING_BLOCK) { + cause = DamageCause.FALLING_BLOCK; + } else if (damager instanceof LightningStrike) { + cause = DamageCause.LIGHTNING; + } else if (source == DamageSource.FALL) { + cause = DamageCause.FALL; + } else if (source == DamageSource.DRAGON_BREATH) { + cause = DamageCause.DRAGON_BREATH; + } else if (source == DamageSource.MAGIC) { + cause = DamageCause.MAGIC; + } else { + throw new IllegalStateException(String.format("Unhandled damage of %s by %s from %s", entity, damager.getHandle(), source.translationIndex)); + } + EntityDamageEvent event = callEvent(new EntityDamageByEntityEvent(damager, entity.getBukkitEntity(), cause, modifiers, modifierFunctions)); + if (!event.isCancelled()) { + event.getEntity().setLastDamageCause(event); + } + return event; + } + + DamageCause cause = null; + if (source == DamageSource.FIRE) { + cause = DamageCause.FIRE; + } else if (source == DamageSource.STARVE) { + cause = DamageCause.STARVATION; + } else if (source == DamageSource.WITHER) { + cause = DamageCause.WITHER; + } else if (source == DamageSource.STUCK) { + cause = DamageCause.SUFFOCATION; + } else if (source == DamageSource.DROWN) { + cause = DamageCause.DROWNING; + } else if (source == DamageSource.BURN) { + cause = DamageCause.FIRE_TICK; + } else if (source == MELTING) { + cause = DamageCause.MELTING; + } else if (source == POISON) { + cause = DamageCause.POISON; + } else if (source == DamageSource.MAGIC) { + cause = DamageCause.MAGIC; + } else if (source == DamageSource.FALL) { + cause = DamageCause.FALL; + } else if (source == DamageSource.FLY_INTO_WALL) { + cause = DamageCause.FLY_INTO_WALL; + } else if (source == DamageSource.CRAMMING) { + cause = DamageCause.CRAMMING; + } else if (source == DamageSource.DRYOUT) { + cause = DamageCause.DRYOUT; + } else if (source == DamageSource.GENERIC) { + cause = DamageCause.CUSTOM; + } + + if (cause != null) { + return callEntityDamageEvent(null, entity, cause, modifiers, modifierFunctions); + } + + throw new IllegalStateException(String.format("Unhandled damage of %s from %s", entity, source.translationIndex)); + } + + private static EntityDamageEvent callEntityDamageEvent(Entity damager, Entity damagee, DamageCause cause, Map modifiers, Map> modifierFunctions) { + EntityDamageEvent event; + if (damager != null) { + event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), damagee.getBukkitEntity(), cause, modifiers, modifierFunctions); + } else { + event = new EntityDamageEvent(damagee.getBukkitEntity(), cause, modifiers, modifierFunctions); + } + + callEvent(event); + + if (!event.isCancelled()) { + event.getEntity().setLastDamageCause(event); + } + + return event; + } + + private static final Function ZERO = Functions.constant(-0.0); + + public static EntityDamageEvent handleLivingEntityDamageEvent(Entity damagee, DamageSource source, double rawDamage, double hardHatModifier, double blockingModifier, double armorModifier, double resistanceModifier, double magicModifier, double absorptionModifier, Function hardHat, Function blocking, Function armor, Function resistance, Function magic, Function absorption) { + Map modifiers = new EnumMap(DamageModifier.class); + Map> modifierFunctions = new EnumMap>(DamageModifier.class); + modifiers.put(DamageModifier.BASE, rawDamage); + modifierFunctions.put(DamageModifier.BASE, ZERO); + if (source == DamageSource.FALLING_BLOCK || source == DamageSource.ANVIL) { + modifiers.put(DamageModifier.HARD_HAT, hardHatModifier); + modifierFunctions.put(DamageModifier.HARD_HAT, hardHat); + } + if (damagee instanceof EntityHuman) { + modifiers.put(DamageModifier.BLOCKING, blockingModifier); + modifierFunctions.put(DamageModifier.BLOCKING, blocking); + } + modifiers.put(DamageModifier.ARMOR, armorModifier); + modifierFunctions.put(DamageModifier.ARMOR, armor); + modifiers.put(DamageModifier.RESISTANCE, resistanceModifier); + modifierFunctions.put(DamageModifier.RESISTANCE, resistance); + modifiers.put(DamageModifier.MAGIC, magicModifier); + modifierFunctions.put(DamageModifier.MAGIC, magic); + modifiers.put(DamageModifier.ABSORPTION, absorptionModifier); + modifierFunctions.put(DamageModifier.ABSORPTION, absorption); + return handleEntityDamageEvent(damagee, source, modifiers, modifierFunctions); + } + + // Non-Living Entities such as EntityEnderCrystal and EntityFireball need to call this + public static boolean handleNonLivingEntityDamageEvent(Entity entity, DamageSource source, double damage) { + return handleNonLivingEntityDamageEvent(entity, source, damage, true); + } + + public static boolean handleNonLivingEntityDamageEvent(Entity entity, DamageSource source, double damage, boolean cancelOnZeroDamage) { + if (entity instanceof EntityEnderCrystal && !(source instanceof EntityDamageSource)) { + return false; + } + + final EnumMap modifiers = new EnumMap(DamageModifier.class); + final EnumMap> functions = new EnumMap(DamageModifier.class); + + modifiers.put(DamageModifier.BASE, damage); + functions.put(DamageModifier.BASE, ZERO); + + final EntityDamageEvent event = handleEntityDamageEvent(entity, source, modifiers, functions); + if (event == null) { + return false; + } + return event.isCancelled() || (cancelOnZeroDamage && event.getDamage() == 0); + } + + public static PlayerLevelChangeEvent callPlayerLevelChangeEvent(Player player, int oldLevel, int newLevel) { + PlayerLevelChangeEvent event = new PlayerLevelChangeEvent(player, oldLevel, newLevel); + Bukkit.getPluginManager().callEvent(event); + return event; + } + + public static PlayerExpChangeEvent callPlayerExpChangeEvent(EntityHuman entity, int expAmount) { + Player player = (Player) entity.getBukkitEntity(); + PlayerExpChangeEvent event = new PlayerExpChangeEvent(player, expAmount); + Bukkit.getPluginManager().callEvent(event); + return event; + } + + public static PlayerItemMendEvent callPlayerItemMendEvent(EntityHuman entity, EntityExperienceOrb orb, net.minecraft.server.ItemStack nmsMendedItem, int repairAmount) { + Player player = (Player) entity.getBukkitEntity(); + org.bukkit.inventory.ItemStack bukkitStack = CraftItemStack.asCraftMirror(nmsMendedItem); + PlayerItemMendEvent event = new PlayerItemMendEvent(player, bukkitStack, (ExperienceOrb) orb.getBukkitEntity(), repairAmount); + Bukkit.getPluginManager().callEvent(event); + return event; + } + + // Paper start - Add orb + public static PlayerExpChangeEvent callPlayerExpChangeEvent(EntityHuman entity, EntityExperienceOrb entityOrb) { + Player player = (Player) entity.getBukkitEntity(); + ExperienceOrb source = (ExperienceOrb) entityOrb.getBukkitEntity(); + int expAmount = source.getExperience(); + PlayerExpChangeEvent event = new PlayerExpChangeEvent(player, source, expAmount); + Bukkit.getPluginManager().callEvent(event); + return event; + } + // Paper end + + public static boolean handleBlockGrowEvent(World world, BlockPosition pos, IBlockData block) { + return handleBlockGrowEvent(world, pos, block, 3); + } + + public static boolean handleBlockGrowEvent(World world, BlockPosition pos, IBlockData newData, int flag) { + Block block = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()); + CraftBlockState state = (CraftBlockState) block.getState(); + state.setData(newData); + + BlockGrowEvent event = new BlockGrowEvent(block, state); + Bukkit.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + state.update(true); + } + + return !event.isCancelled(); + } + + public static FluidLevelChangeEvent callFluidLevelChangeEvent(World world, BlockPosition block, IBlockData newData) { + FluidLevelChangeEvent event = new FluidLevelChangeEvent(CraftBlock.at(world, block), CraftBlockData.fromData(newData)); + world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static FoodLevelChangeEvent callFoodLevelChangeEvent(EntityHuman entity, int level) { + FoodLevelChangeEvent event = new FoodLevelChangeEvent(entity.getBukkitEntity(), level); + entity.getBukkitEntity().getServer().getPluginManager().callEvent(event); + return event; + } + + public static PigZapEvent callPigZapEvent(Entity pig, Entity lightning, Entity pigzombie) { + PigZapEvent event = new PigZapEvent((Pig) pig.getBukkitEntity(), (LightningStrike) lightning.getBukkitEntity(), (PigZombie) pigzombie.getBukkitEntity()); + pig.getBukkitEntity().getServer().getPluginManager().callEvent(event); + return event; + } + + // Paper start + public static com.destroystokyo.paper.event.entity.EntityZapEvent callEntityZapEvent (Entity entity, Entity lightning, Entity changedEntity) { + com.destroystokyo.paper.event.entity.EntityZapEvent event = new com.destroystokyo.paper.event.entity.EntityZapEvent(entity.getBukkitEntity(), (LightningStrike) lightning.getBukkitEntity(), changedEntity.getBukkitEntity()); + entity.getBukkitEntity().getServer().getPluginManager().callEvent(event); + return event; + } + // Paper end + + public static HorseJumpEvent callHorseJumpEvent(Entity horse, float power) { + HorseJumpEvent event = new HorseJumpEvent((AbstractHorse) horse.getBukkitEntity(), power); + horse.getBukkitEntity().getServer().getPluginManager().callEvent(event); + return event; + } + + public static EntityChangeBlockEvent callEntityChangeBlockEvent(Entity entity, BlockPosition position, IBlockData newBlock) { + return callEntityChangeBlockEvent(entity, position, newBlock, false); + } + + public static EntityChangeBlockEvent callEntityChangeBlockEvent(Entity entity, BlockPosition position, IBlockData newBlock, boolean cancelled) { + Block block = entity.world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()); + + EntityChangeBlockEvent event = new EntityChangeBlockEvent(entity.getBukkitEntity(), block, CraftBlockData.fromData(newBlock)); + event.setCancelled(cancelled); + event.getEntity().getServer().getPluginManager().callEvent(event); + return event; + } + + public static CreeperPowerEvent callCreeperPowerEvent(Entity creeper, Entity lightning, CreeperPowerEvent.PowerCause cause) { + CreeperPowerEvent event = new CreeperPowerEvent((Creeper) creeper.getBukkitEntity(), (LightningStrike) lightning.getBukkitEntity(), cause); + creeper.getBukkitEntity().getServer().getPluginManager().callEvent(event); + return event; + } + + public static EntityTargetEvent callEntityTargetEvent(Entity entity, Entity target, EntityTargetEvent.TargetReason reason) { + EntityTargetEvent event = new EntityTargetEvent(entity.getBukkitEntity(), target == null ? null : target.getBukkitEntity(), reason); + entity.getBukkitEntity().getServer().getPluginManager().callEvent(event); + return event; + } + + public static EntityTargetLivingEntityEvent callEntityTargetLivingEvent(Entity entity, EntityLiving target, EntityTargetEvent.TargetReason reason) { + EntityTargetLivingEntityEvent event = new EntityTargetLivingEntityEvent(entity.getBukkitEntity(), (LivingEntity) target.getBukkitEntity(), reason); + entity.getBukkitEntity().getServer().getPluginManager().callEvent(event); + return event; + } + + public static EntityBreakDoorEvent callEntityBreakDoorEvent(Entity entity, int x, int y, int z) { + org.bukkit.entity.Entity entity1 = entity.getBukkitEntity(); + Block block = entity1.getWorld().getBlockAt(x, y, z); + + EntityBreakDoorEvent event = new EntityBreakDoorEvent((LivingEntity) entity1, block); + entity1.getServer().getPluginManager().callEvent(event); + + return event; + } + + public static Container callInventoryOpenEvent(EntityPlayer player, Container container) { + return callInventoryOpenEvent(player, container, false); + } + + public static Container callInventoryOpenEvent(EntityPlayer player, Container container, boolean cancelled) { + if (player.activeContainer != player.defaultContainer) { // fire INVENTORY_CLOSE if one already open + player.playerConnection.a(new PacketPlayInCloseWindow(player.activeContainer.windowId)); + } + + CraftServer server = player.world.getServer(); + CraftPlayer craftPlayer = player.getBukkitEntity(); + player.activeContainer.transferTo(container, craftPlayer); + + InventoryOpenEvent event = new InventoryOpenEvent(container.getBukkitView()); + event.setCancelled(cancelled); + server.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + container.transferTo(player.activeContainer, craftPlayer); + return null; + } + + return container; + } + + public static ItemStack callPreCraftEvent(IInventory matrix, IInventory resultInventory, ItemStack result, InventoryView lastCraftView, boolean isRepair) { + CraftInventoryCrafting inventory = new CraftInventoryCrafting(matrix, resultInventory); + inventory.setResult(CraftItemStack.asCraftMirror(result)); + + PrepareItemCraftEvent event = new PrepareItemCraftEvent(inventory, lastCraftView, isRepair); + Bukkit.getPluginManager().callEvent(event); + + org.bukkit.inventory.ItemStack bitem = event.getInventory().getResult(); + + return CraftItemStack.asNMSCopy(bitem); + } + + // Paper start + public static com.destroystokyo.paper.event.entity.ProjectileCollideEvent callProjectileCollideEvent(Entity entity, MovingObjectPosition position) { + Projectile projectile = (Projectile) entity.getBukkitEntity(); + org.bukkit.entity.Entity collided = position.entity.getBukkitEntity(); + com.destroystokyo.paper.event.entity.ProjectileCollideEvent event = new com.destroystokyo.paper.event.entity.ProjectileCollideEvent(projectile, collided); + + if (projectile.getShooter() instanceof Player && collided instanceof Player) { + if (!((Player) projectile.getShooter()).canSee((Player) collided)) { + event.setCancelled(true); + return event; + } + } + + Bukkit.getPluginManager().callEvent(event); + return event; + } + // Paper end + + public static ProjectileLaunchEvent callProjectileLaunchEvent(Entity entity) { + Projectile bukkitEntity = (Projectile) entity.getBukkitEntity(); + ProjectileLaunchEvent event = new ProjectileLaunchEvent(bukkitEntity); + Bukkit.getPluginManager().callEvent(event); + return event; + } + + public static ProjectileHitEvent callProjectileHitEvent(Entity entity, MovingObjectPosition position) { + Block hitBlock = null; + BlockFace hitFace = null; + if (position.type == MovingObjectPosition.EnumMovingObjectType.BLOCK) { + hitBlock = CraftBlock.at(entity.world, position.getBlockPosition()); + hitFace = CraftBlock.notchToBlockFace(position.direction); + } + + ProjectileHitEvent event = new ProjectileHitEvent((Projectile) entity.getBukkitEntity(), position.entity == null ? null : position.entity.getBukkitEntity(), hitBlock, hitFace); + entity.world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static ExpBottleEvent callExpBottleEvent(Entity entity, int exp) { + ThrownExpBottle bottle = (ThrownExpBottle) entity.getBukkitEntity(); + ExpBottleEvent event = new ExpBottleEvent(bottle, exp); + Bukkit.getPluginManager().callEvent(event); + return event; + } + + public static BlockRedstoneEvent callRedstoneChange(World world, BlockPosition pos, int oldCurrent, int newCurrent) { + BlockRedstoneEvent event = new BlockRedstoneEvent(world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()), oldCurrent, newCurrent); + world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static NotePlayEvent callNotePlayEvent(World world, BlockPosition pos, BlockPropertyInstrument instrument, int note) { + NotePlayEvent event = new NotePlayEvent(world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()), org.bukkit.Instrument.getByType((byte) instrument.ordinal()), new org.bukkit.Note(note)); + world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static void callPlayerItemBreakEvent(EntityHuman human, ItemStack brokenItem) { + CraftItemStack item = CraftItemStack.asCraftMirror(brokenItem); + PlayerItemBreakEvent event = new PlayerItemBreakEvent((Player) human.getBukkitEntity(), item); + Bukkit.getPluginManager().callEvent(event); + } + + public static BlockIgniteEvent callBlockIgniteEvent(World world, BlockPosition block, BlockPosition source) { + org.bukkit.World bukkitWorld = world.getWorld(); + Block igniter = bukkitWorld.getBlockAt(source.getX(), source.getY(), source.getZ()); + IgniteCause cause; + switch (igniter.getType()) { + case LAVA: + cause = IgniteCause.LAVA; + break; + case DISPENSER: + cause = IgniteCause.FLINT_AND_STEEL; + break; + case FIRE: // Fire or any other unknown block counts as SPREAD. + default: + cause = IgniteCause.SPREAD; + } + + BlockIgniteEvent event = new BlockIgniteEvent(bukkitWorld.getBlockAt(block.getX(), block.getY(), block.getZ()), cause, igniter); + if (isWorldGen(world)) return event; // Paper - do not call during world gen + world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static BlockIgniteEvent callBlockIgniteEvent(World world, BlockPosition pos, Entity igniter) { + org.bukkit.World bukkitWorld = world.getWorld(); + org.bukkit.entity.Entity bukkitIgniter = igniter.getBukkitEntity(); + IgniteCause cause; + switch (bukkitIgniter.getType()) { + case ENDER_CRYSTAL: + cause = IgniteCause.ENDER_CRYSTAL; + break; + case LIGHTNING: + cause = IgniteCause.LIGHTNING; + break; + case SMALL_FIREBALL: + case FIREBALL: + cause = IgniteCause.FIREBALL; + break; + default: + cause = IgniteCause.FLINT_AND_STEEL; + } + + BlockIgniteEvent event = new BlockIgniteEvent(bukkitWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), cause, bukkitIgniter); + if (isWorldGen(world)) return event; // Paper - do not call during world gen + world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static BlockIgniteEvent callBlockIgniteEvent(World world, int x, int y, int z, Explosion explosion) { + org.bukkit.World bukkitWorld = world.getWorld(); + org.bukkit.entity.Entity igniter = explosion.source == null ? null : explosion.source.getBukkitEntity(); + + BlockIgniteEvent event = new BlockIgniteEvent(bukkitWorld.getBlockAt(x, y, z), IgniteCause.EXPLOSION, igniter); + world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static BlockIgniteEvent callBlockIgniteEvent(World world, BlockPosition pos, IgniteCause cause, Entity igniter) { + BlockIgniteEvent event = new BlockIgniteEvent(world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()), cause, igniter.getBukkitEntity()); + world.getServer().getPluginManager().callEvent(event); + return event; + } + + // Paper start + + /** + * Incase plugins hooked into this or Spigot adds a new inventory close event. Prefer to pass a reason + * @param human + */ + @Deprecated + public static void handleInventoryCloseEvent(EntityHuman human) { + handleInventoryCloseEvent(human, org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNKNOWN); + } + public static void handleInventoryCloseEvent(EntityHuman human, org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) { + InventoryCloseEvent event = new InventoryCloseEvent(human.activeContainer.getBukkitView(), reason); + // Paper end + human.world.getServer().getPluginManager().callEvent(event); + human.activeContainer.transferTo(human.defaultContainer, human.getBukkitEntity()); + } + + public static ItemStack handleEditBookEvent(EntityPlayer player, EnumItemSlot slot, ItemStack itemInHand, ItemStack newBookItem) { + int itemInHandIndex = (slot == EnumItemSlot.MAINHAND) ? player.inventory.itemInHandIndex : -1; + + PlayerEditBookEvent editBookEvent = new PlayerEditBookEvent(player.getBukkitEntity(), itemInHandIndex, (BookMeta) CraftItemStack.getItemMeta(itemInHand), (BookMeta) CraftItemStack.getItemMeta(newBookItem), newBookItem.getItem() == Items.WRITTEN_BOOK); + player.world.getServer().getPluginManager().callEvent(editBookEvent); + + // If they've got the same item in their hand, it'll need to be updated. + if (itemInHand != null && itemInHand.getItem() == Items.WRITABLE_BOOK) { + if (!editBookEvent.isCancelled()) { + if (editBookEvent.isSigning()) { + itemInHand.setItem(Items.WRITTEN_BOOK); + } + CraftMetaBook meta = (CraftMetaBook) editBookEvent.getNewBookMeta(); + List pages = meta.pages; + for (int i = 0; i < pages.size(); i++) { + pages.set(i, stripEvents(pages.get(i))); + } + CraftItemStack.setItemMeta(itemInHand, meta); + } + } + + return itemInHand; + } + + private static IChatBaseComponent stripEvents(IChatBaseComponent c) { + ChatModifier modi = c.getChatModifier(); + if (modi != null) { + modi.setChatClickable(null); + modi.setChatHoverable(null); + } + c.setChatModifier(modi); + if (c instanceof ChatMessage) { + ChatMessage cm = (ChatMessage) c; + Object[] oo = cm.l(); + for (int i = 0; i < oo.length; i++) { + Object o = oo[i]; + if (o instanceof IChatBaseComponent) { + oo[i] = stripEvents((IChatBaseComponent) o); + } + } + } + List ls = c.a(); + if (ls != null) { + for (int i = 0; i < ls.size(); i++) { + ls.set(i, stripEvents(ls.get(i))); + } + } + return c; + } + + public static PlayerUnleashEntityEvent callPlayerUnleashEntityEvent(EntityInsentient entity, EntityHuman player) { + PlayerUnleashEntityEvent event = new PlayerUnleashEntityEvent(entity.getBukkitEntity(), (Player) player.getBukkitEntity()); + entity.world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static PlayerLeashEntityEvent callPlayerLeashEntityEvent(EntityInsentient entity, Entity leashHolder, EntityHuman player) { + PlayerLeashEntityEvent event = new PlayerLeashEntityEvent(entity.getBukkitEntity(), leashHolder.getBukkitEntity(), (Player) player.getBukkitEntity()); + entity.world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static Cancellable handleStatisticsIncrease(EntityHuman entityHuman, net.minecraft.server.Statistic statistic, int current, int incrementation) { + Player player = ((EntityPlayer) entityHuman).getBukkitEntity(); + Event event; + if (true) { + org.bukkit.Statistic stat = CraftStatistic.getBukkitStatistic(statistic); + if (stat == null) { + System.err.println("Unhandled statistic: " + statistic); + return null; + } + switch (stat) { + case FALL_ONE_CM: + case BOAT_ONE_CM: + case CLIMB_ONE_CM: + case WALK_ON_WATER_ONE_CM: + case WALK_UNDER_WATER_ONE_CM: + case FLY_ONE_CM: + case HORSE_ONE_CM: + case MINECART_ONE_CM: + case PIG_ONE_CM: + case PLAY_ONE_MINUTE: + case SWIM_ONE_CM: + case WALK_ONE_CM: + case SPRINT_ONE_CM: + case CROUCH_ONE_CM: + case TIME_SINCE_DEATH: + case SNEAK_TIME: + // Do not process event for these - too spammy + return null; + default: + } + if (stat.getType() == Type.UNTYPED) { + event = new PlayerStatisticIncrementEvent(player, stat, current, current + incrementation); + } else if (stat.getType() == Type.ENTITY) { + EntityType entityType = CraftStatistic.getEntityTypeFromStatistic((net.minecraft.server.Statistic>) statistic); + event = new PlayerStatisticIncrementEvent(player, stat, current, current + incrementation, entityType); + } else { + Material material = CraftStatistic.getMaterialFromStatistic(statistic); + event = new PlayerStatisticIncrementEvent(player, stat, current, current + incrementation, material); + } + } + entityHuman.world.getServer().getPluginManager().callEvent(event); + return (Cancellable) event; + } + + public static FireworkExplodeEvent callFireworkExplodeEvent(EntityFireworks firework) { + FireworkExplodeEvent event = new FireworkExplodeEvent((Firework) firework.getBukkitEntity()); + firework.world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static PrepareAnvilEvent callPrepareAnvilEvent(InventoryView view, ItemStack item) { + PrepareAnvilEvent event = new PrepareAnvilEvent(view, CraftItemStack.asCraftMirror(item).clone()); + event.getView().getPlayer().getServer().getPluginManager().callEvent(event); + event.getInventory().setItem(2, event.getResult()); + return event; + } + + /** + * Mob spawner event. + */ + public static SpawnerSpawnEvent callSpawnerSpawnEvent(Entity spawnee, BlockPosition pos) { + org.bukkit.craftbukkit.entity.CraftEntity entity = spawnee.getBukkitEntity(); + BlockState state = entity.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()).getState(); + if (!(state instanceof org.bukkit.block.CreatureSpawner)) { + state = null; + } + + SpawnerSpawnEvent event = new SpawnerSpawnEvent(entity, (org.bukkit.block.CreatureSpawner) state); + entity.getServer().getPluginManager().callEvent(event); + return event; + } + + public static EntityToggleGlideEvent callToggleGlideEvent(EntityLiving entity, boolean gliding) { + EntityToggleGlideEvent event = new EntityToggleGlideEvent((LivingEntity) entity.getBukkitEntity(), gliding); + entity.world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static EntityToggleSwimEvent callToggleSwimEvent(EntityLiving entity, boolean swimming) { + EntityToggleSwimEvent event = new EntityToggleSwimEvent((LivingEntity) entity.getBukkitEntity(), swimming); + entity.world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static AreaEffectCloudApplyEvent callAreaEffectCloudApplyEvent(EntityAreaEffectCloud cloud, List entities) { + AreaEffectCloudApplyEvent event = new AreaEffectCloudApplyEvent((AreaEffectCloud) cloud.getBukkitEntity(), entities); + cloud.world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static VehicleCreateEvent callVehicleCreateEvent(Entity entity) { + Vehicle bukkitEntity = (Vehicle) entity.getBukkitEntity(); + VehicleCreateEvent event = new VehicleCreateEvent(bukkitEntity); + Bukkit.getPluginManager().callEvent(event); + return event; + } + + public static EntityBreedEvent callEntityBreedEvent(EntityLiving child, EntityLiving mother, EntityLiving father, EntityLiving breeder, ItemStack bredWith, int experience) { + org.bukkit.entity.LivingEntity breederEntity = (LivingEntity) (breeder == null ? null : breeder.getBukkitEntity()); + CraftItemStack bredWithStack = bredWith == null ? null : CraftItemStack.asCraftMirror(bredWith).clone(); + + EntityBreedEvent event = new EntityBreedEvent((LivingEntity) child.getBukkitEntity(), (LivingEntity) mother.getBukkitEntity(), (LivingEntity) father.getBukkitEntity(), breederEntity, bredWithStack, experience); + child.world.getServer().getPluginManager().callEvent(event); + return event; + } + + public static BlockPhysicsEvent callBlockPhysicsEvent(GeneratorAccess world, BlockPosition blockposition) { + org.bukkit.block.Block block = CraftBlock.at(world, blockposition); + BlockPhysicsEvent event = new BlockPhysicsEvent(block, block.getBlockData()); + if (isWorldGen(world)) return event; // Paper - do not call during world gen + Bukkit.getPluginManager().callEvent(event); // Paper + return event; + } + + public static boolean handleBlockFormEvent(World world, BlockPosition pos, IBlockData block) { + return handleBlockFormEvent(world, pos, block, 3); + } + + public static EntityPotionEffectEvent callEntityPotionEffectChangeEvent(EntityLiving entity, @Nullable MobEffect oldEffect, @Nullable MobEffect newEffect, EntityPotionEffectEvent.Cause cause) { + return callEntityPotionEffectChangeEvent(entity, oldEffect, newEffect, cause, true); + } + + public static EntityPotionEffectEvent callEntityPotionEffectChangeEvent(EntityLiving entity, @Nullable MobEffect oldEffect, @Nullable MobEffect newEffect, EntityPotionEffectEvent.Cause cause, EntityPotionEffectEvent.Action action) { + return callEntityPotionEffectChangeEvent(entity, oldEffect, newEffect, cause, action, true); + } + + public static EntityPotionEffectEvent callEntityPotionEffectChangeEvent(EntityLiving entity, @Nullable MobEffect oldEffect, @Nullable MobEffect newEffect, EntityPotionEffectEvent.Cause cause, boolean willOverride) { + EntityPotionEffectEvent.Action action = EntityPotionEffectEvent.Action.CHANGED; + if (oldEffect == null) { + action = EntityPotionEffectEvent.Action.ADDED; + } else if (newEffect == null) { + action = EntityPotionEffectEvent.Action.REMOVED; + } + + return callEntityPotionEffectChangeEvent(entity, oldEffect, newEffect, cause, action, willOverride); + } + + public static EntityPotionEffectEvent callEntityPotionEffectChangeEvent(EntityLiving entity, @Nullable MobEffect oldEffect, @Nullable MobEffect newEffect, EntityPotionEffectEvent.Cause cause, EntityPotionEffectEvent.Action action, boolean willOverride) { + PotionEffect bukkitOldEffect = (oldEffect == null) ? null : CraftPotionUtil.toBukkit(oldEffect); + PotionEffect bukkitNewEffect = (newEffect == null) ? null : CraftPotionUtil.toBukkit(newEffect); + + if (bukkitOldEffect == null && bukkitNewEffect == null) { + throw new IllegalStateException("Old and new potion effect are both null"); + } + + EntityPotionEffectEvent event = new EntityPotionEffectEvent((LivingEntity) entity.getBukkitEntity(), bukkitOldEffect, bukkitNewEffect, cause, action, willOverride); + if (isWorldGen(entity.world)) return event; // Paper - do not call during world gen + Bukkit.getPluginManager().callEvent(event); + + return event; + } + + public static boolean handleBlockFormEvent(World world, BlockPosition pos, IBlockData block, @Nullable Entity entity) { + return handleBlockFormEvent(world, pos, block, 3, entity); + } + + public static boolean handleBlockFormEvent(World world, BlockPosition pos, IBlockData block, int flag) { + return handleBlockFormEvent(world, pos, block, flag, null); + } + + public static boolean handleBlockFormEvent(World world, BlockPosition pos, IBlockData block, int flag, @Nullable Entity entity) { + CraftBlockState blockState = CraftBlockState.getBlockState(world, pos, flag); + blockState.setData(block); + + BlockFormEvent event = (entity == null) ? new BlockFormEvent(blockState.getBlock(), blockState) : new EntityBlockFormEvent(entity.getBukkitEntity(), blockState.getBlock(), blockState); + if (isWorldGen(world)) return true; // Paper - do not call during world gen + world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + blockState.update(true); + } + + return !event.isCancelled(); + } + + public static boolean handleBatToggleSleepEvent(Entity bat, boolean awake) { + BatToggleSleepEvent event = new BatToggleSleepEvent((Bat) bat.getBukkitEntity(), awake); + Bukkit.getPluginManager().callEvent(event); + return !event.isCancelled(); + } + + public static boolean handlePlayerRecipeListUpdateEvent(EntityHuman who, MinecraftKey recipe) { + PlayerRecipeDiscoverEvent event = new PlayerRecipeDiscoverEvent((Player) who.getBukkitEntity(), CraftNamespacedKey.fromMinecraft(recipe)); + Bukkit.getPluginManager().callEvent(event); + return !event.isCancelled(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java new file mode 100644 index 000000000000..923d1b28217d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java @@ -0,0 +1,158 @@ +package org.bukkit.craftbukkit.generator; + +import net.minecraft.server.Blocks; +import net.minecraft.server.ChunkSection; +import net.minecraft.server.IBlockData; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.material.MaterialData; + +/** + * Data to be used for the block types and data in a newly generated chunk. + */ +public final class CraftChunkData implements ChunkGenerator.ChunkData { + private final int maxHeight; + private final ChunkSection[] sections; + private World world; // Paper - Anti-Xray + + public CraftChunkData(World world) { + this(world.getMaxHeight()); + this.world = world; // Paper - Anti-Xray + } + + /* pp for tests */ CraftChunkData(int maxHeight) { + if (maxHeight > 256) { + throw new IllegalArgumentException("World height exceeded max chunk height"); + } + this.maxHeight = maxHeight; + sections = new ChunkSection[maxHeight >> 4]; + } + + @Override + public int getMaxHeight() { + return maxHeight; + } + + @Override + public void setBlock(int x, int y, int z, Material material) { + setBlock(x, y, z, material.createBlockData()); + } + + @Override + public void setBlock(int x, int y, int z, MaterialData material) { + setBlock(x, y, z, CraftMagicNumbers.getBlock(material)); + } + + @Override + public void setBlock(int x, int y, int z, BlockData blockData) { + setBlock(x, y, z, ((CraftBlockData) blockData).getState()); + } + + @Override + public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Material material) { + setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material.createBlockData()); + } + + @Override + public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, MaterialData material) { + setRegion(xMin, yMin, zMin, xMax, yMax, zMax, CraftMagicNumbers.getBlock(material)); + } + + @Override + public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockData blockData) { + setRegion(xMin, yMin, zMin, xMax, yMax, zMax, ((CraftBlockData) blockData).getState()); + } + + @Override + public Material getType(int x, int y, int z) { + return CraftMagicNumbers.getMaterial(getTypeId(x, y, z).getBlock()); + } + + @Override + public MaterialData getTypeAndData(int x, int y, int z) { + return CraftMagicNumbers.getMaterial(getTypeId(x, y, z)); + } + + @Override + public BlockData getBlockData(int x, int y, int z) { + return CraftBlockData.fromData(getTypeId(x, y, z)); + } + + public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, IBlockData type) { + // Clamp to sane values. + if (xMin > 0xf || yMin >= maxHeight || zMin > 0xf) { + return; + } + if (xMin < 0) { + xMin = 0; + } + if (yMin < 0) { + yMin = 0; + } + if (zMin < 0) { + zMin = 0; + } + if (xMax > 0x10) { + xMax = 0x10; + } + if (yMax > maxHeight) { + yMax = maxHeight; + } + if (zMax > 0x10) { + zMax = 0x10; + } + if (xMin >= xMax || yMin >= yMax || zMin >= zMax) { + return; + } + for (int y = yMin; y < yMax; y++) { + ChunkSection section = getChunkSection(y, true); + int offsetBase = y & 0xf; + for (int x = xMin; x < xMax; x++) { + for (int z = zMin; z < zMax; z++) { + section.setType(x, offsetBase, z, type); + } + } + } + } + + public IBlockData getTypeId(int x, int y, int z) { + if (x != (x & 0xf) || y < 0 || y >= maxHeight || z != (z & 0xf)) { + return Blocks.AIR.getBlockData(); + } + ChunkSection section = getChunkSection(y, false); + if (section == null) { + return Blocks.AIR.getBlockData(); + } else { + return section.getType(x, y & 0xf, z); + } + } + + @Override + public byte getData(int x, int y, int z) { + return CraftMagicNumbers.toLegacyData(getTypeId(x, y, z)); + } + + private void setBlock(int x, int y, int z, IBlockData type) { + if (x != (x & 0xf) || y < 0 || y >= maxHeight || z != (z & 0xf)) { + return; + } + ChunkSection section = getChunkSection(y, true); + section.setType(x, y & 0xf, z, type); + } + + private ChunkSection getChunkSection(int y, boolean create) { + ChunkSection section = sections[y >> 4]; + if (create && section == null) { + sections[y >> 4] = section = new ChunkSection(y, create, null, world instanceof org.bukkit.craftbukkit.CraftWorld ? ((org.bukkit.craftbukkit.CraftWorld) world).getHandle() : null, true); // Paper - Anti-Xray + } + return section; + } + + ChunkSection[] getRawChunkData() { + return sections; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java new file mode 100644 index 000000000000..a299092a5868 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java @@ -0,0 +1,196 @@ +package org.bukkit.craftbukkit.generator; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; +import it.unimi.dsi.fastutil.longs.LongSet; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import net.minecraft.server.*; + +import org.bukkit.block.Biome; +import org.bukkit.generator.BlockPopulator; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.craftbukkit.block.CraftBlock; + +public class CustomChunkGenerator extends InternalChunkGenerator { + private final ChunkGenerator generator; + private final WorldServer world; + private final long seed; + private final Random random; + public final boolean asyncSupported; // Paper + private final WorldChunkManager chunkManager; + private final WorldGenStronghold strongholdGen = new WorldGenStronghold(); + private final GeneratorSettingsDefault settings = new GeneratorSettingsDefault(); + + private static class CustomBiomeGrid implements BiomeGrid { + BiomeBase[] biome; + + @Override + public Biome getBiome(int x, int z) { + return CraftBlock.biomeBaseToBiome(biome[(z << 4) | x]); + } + + @Override + public void setBiome(int x, int z, Biome bio) { + biome[(z << 4) | x] = CraftBlock.biomeToBiomeBase(bio); + } + } + + public CustomChunkGenerator(World world, long seed, ChunkGenerator generator) { + this.world = (WorldServer) world; + this.generator = generator; + this.seed = seed; + // Paper start + boolean asyncSupported = false; + try { + java.lang.reflect.Field asyncSafe = generator.getClass().getDeclaredField("PAPER_ASYNC_SAFE"); + asyncSafe.setAccessible(true); + asyncSupported = asyncSafe.getBoolean(generator); + } catch (NoSuchFieldException | IllegalAccessException ignored) {} + this.asyncSupported = asyncSupported; + // Paper end + + this.random = new Random(seed); + this.chunkManager = world.worldProvider.getChunkGenerator().getWorldChunkManager(); + } + + @Override + public void createChunk(IChunkAccess ichunkaccess) { + int x = ichunkaccess.getPos().x; + int z = ichunkaccess.getPos().z; + random.setSeed((long) x * 341873128712L + (long) z * 132897987541L); + + // Get default biome data for chunk + CustomBiomeGrid biomegrid = new CustomBiomeGrid(); + biomegrid.biome = chunkManager.getBiomeBlock(x << 4, z << 4, 16, 16); + + ChunkData data = generator.generateChunkData(this.world.getWorld(), random, x, z, biomegrid); + Preconditions.checkArgument(data instanceof CraftChunkData, "Plugins must use createChunkData(World) rather than implementing ChunkData: %s", data); + ChunkSection[] sections = ((CraftChunkData) data).getRawChunkData(); + + ChunkSection[] csect = ichunkaccess.getSections(); + int scnt = Math.min(csect.length, sections.length); + + // Loop through returned sections + for (int sec = 0; sec < scnt; sec++) { + if (sections[sec] == null) { + continue; + } + ChunkSection section = sections[sec]; + + csect[sec] = section; + } + + // Set biome grid + ichunkaccess.a(biomegrid.biome); + } + + @Override + public ChunkData generateChunkData(org.bukkit.World world, Random random, int x, int z, BiomeGrid biome) { + return generator.generateChunkData(world, random, x, z, biome); + } + + @Override + public boolean canSpawn(org.bukkit.World world, int x, int z) { + return generator.canSpawn(world, x, z); + } + + @Override + public List getDefaultPopulators(org.bukkit.World world) { + return generator.getDefaultPopulators(world); + } + + @Override + public List getMobsFor(EnumCreatureType type, BlockPosition position) { + BiomeBase biomebase = world.getBiome(position); + + return biomebase == null ? null : biomebase.getMobs(type); + } + + @Override + public void addFeatures(RegionLimitedWorldAccess regionlimitedworldaccess, WorldGenStage.Features worldgenstage_features) { + } + + @Override + public void addDecorations(RegionLimitedWorldAccess regionlimitedworldaccess) { + } + + @Override + public void addMobs(RegionLimitedWorldAccess regionlimitedworldaccess) { + } + + @Override + public BlockPosition findNearestMapFeature(World world, String type, BlockPosition position, int i, boolean flag) { + return "Stronghold".equals(type) && this.strongholdGen != null ? this.strongholdGen.getNearestGeneratedFeature(world, this, position, i, flag) : null; + } + + @Override + public GeneratorSettingsDefault getSettings() { + return settings; + } + + @Override + public int a(World world, boolean flag, boolean flag1) { + return 0; + } + + @Override + public boolean canSpawnStructure(BiomeBase biomebase, StructureGenerator structuregenerator) { + return biomebase.a(structuregenerator); + } + + @Override + public WorldGenFeatureConfiguration getFeatureConfiguration(BiomeBase biomebase, StructureGenerator structuregenerator) { + return biomebase.b(structuregenerator); + } + + // Taken from ChunkGeneratorAbstract + private final Map, Long2ObjectMap> structureStartCache = Maps.newHashMap(); + + @Override + public Long2ObjectMap getStructureStartCache(StructureGenerator structuregenerator) { + return (Long2ObjectMap) this.structureStartCache.computeIfAbsent(structuregenerator, (s) -> { + return new ExpiringMap(8192, 10000); // Paper - already synchronized + }); + } + + // Taken from ChunkGeneratorAbstract + private final Map, Long2ObjectMap> structureCache = Maps.newHashMap(); + + @Override + public Long2ObjectMap getStructureCache(StructureGenerator structuregenerator) { + return (Long2ObjectMap) this.structureCache.computeIfAbsent(structuregenerator, (s) -> { + return new ExpiringMap(8192, 10000); // Paper - already synchronized + }); + } + + @Override + public WorldChunkManager getWorldChunkManager() { + return chunkManager; + } + + @Override + public long getSeed() { + return seed; + } + + @Override + public int getSpawnHeight() { + return world.getSeaLevel() + 1; + } + + @Override + public int getGenerationDepth() { + return world.getHeight(); + } + + // Spigot start + public WorldServer getWorld() { + return world; + } + // Spigot end +} diff --git a/src/main/java/org/bukkit/craftbukkit/generator/InternalChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/InternalChunkGenerator.java new file mode 100644 index 000000000000..b0710cd825c1 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/generator/InternalChunkGenerator.java @@ -0,0 +1,8 @@ +package org.bukkit.craftbukkit.generator; + +import net.minecraft.server.GeneratorSettings; +import org.bukkit.generator.ChunkGenerator; + +// Do not implement functions to this class, add to NormalChunkGenerator +public abstract class InternalChunkGenerator extends ChunkGenerator implements net.minecraft.server.ChunkGenerator { +} diff --git a/src/main/java/org/bukkit/craftbukkit/generator/NetherChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/NetherChunkGenerator.java new file mode 100644 index 000000000000..59d7bbec557e --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/generator/NetherChunkGenerator.java @@ -0,0 +1,12 @@ +package org.bukkit.craftbukkit.generator; + +import net.minecraft.server.World; + +/** + * This class is useless. Just fyi. + */ +public class NetherChunkGenerator extends NormalChunkGenerator { + public NetherChunkGenerator(World world, long seed) { + super(world, seed); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/generator/NormalChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/NormalChunkGenerator.java new file mode 100644 index 000000000000..97a373771c66 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/generator/NormalChunkGenerator.java @@ -0,0 +1,123 @@ +package org.bukkit.craftbukkit.generator; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.LongSet; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import net.minecraft.server.*; + +import org.bukkit.generator.BlockPopulator; + +public class NormalChunkGenerator extends InternalChunkGenerator { + private final World world; // Spigot + private final ChunkGenerator generator; + + public NormalChunkGenerator(World world, long seed) { + this.world = world; // Spigot + generator = world.worldProvider.getChunkGenerator(); + } + + @Override + public ChunkData generateChunkData(org.bukkit.World world, Random random, int x, int z, BiomeGrid biome) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public boolean canSpawn(org.bukkit.World world, int x, int z) { + return true; // PAIL + } + + @Override + public List getDefaultPopulators(org.bukkit.World world) { + return new ArrayList(); + } + + @Override + public List getMobsFor(EnumCreatureType enumCreatureType, BlockPosition blockPosition) { + return generator.getMobsFor(enumCreatureType, blockPosition); + } + + @Override + public BlockPosition findNearestMapFeature(World world, String s, BlockPosition blockPosition, int i, boolean flag) { + return generator.findNearestMapFeature(world, s, blockPosition, i, flag); + } + + @Override + public void createChunk(IChunkAccess ichunkaccess) { + generator.createChunk(ichunkaccess); + } + + @Override + public void addFeatures(RegionLimitedWorldAccess regionlimitedworldaccess, WorldGenStage.Features worldgenstage_features) { + generator.addFeatures(regionlimitedworldaccess, worldgenstage_features); + } + + @Override + public void addDecorations(RegionLimitedWorldAccess regionlimitedworldaccess) { + generator.addDecorations(regionlimitedworldaccess); + } + + @Override + public void addMobs(RegionLimitedWorldAccess regionlimitedworldaccess) { + generator.addMobs(regionlimitedworldaccess); + } + + @Override + public C getSettings() { + return (C) generator.getSettings(); + } + + @Override + public int a(World world, boolean flag, boolean flag1) { + return generator.a(world, flag, flag1); + } + + @Override + public boolean canSpawnStructure(BiomeBase biomebase, StructureGenerator structuregenerator) { + return generator.canSpawnStructure(biomebase, structuregenerator); + } + + @Override + public WorldGenFeatureConfiguration getFeatureConfiguration(BiomeBase biomebase, StructureGenerator structuregenerator) { + return generator.getFeatureConfiguration(biomebase, structuregenerator); + } + + @Override + public Long2ObjectMap getStructureStartCache(StructureGenerator structuregenerator) { + return generator.getStructureStartCache(structuregenerator); + } + + @Override + public Long2ObjectMap getStructureCache(StructureGenerator structuregenerator) { + return generator.getStructureCache(structuregenerator); + } + + @Override + public WorldChunkManager getWorldChunkManager() { + return generator.getWorldChunkManager(); + } + + @Override + public long getSeed() { + return generator.getSeed(); + } + + @Override + public int getSpawnHeight() { + return generator.getSpawnHeight(); + } + + @Override + public int getGenerationDepth() { + return generator.getGenerationDepth(); + } + + // Spigot start + @Override + public World getWorld() { + return this.world; + } + // Spigot end +} diff --git a/src/main/java/org/bukkit/craftbukkit/generator/SkyLandsChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/SkyLandsChunkGenerator.java new file mode 100644 index 000000000000..e327996943c3 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/generator/SkyLandsChunkGenerator.java @@ -0,0 +1,12 @@ +package org.bukkit.craftbukkit.generator; + +import net.minecraft.server.World; + +/** + * This class is useless. Just fyi. + */ +public class SkyLandsChunkGenerator extends NormalChunkGenerator { + public SkyLandsChunkGenerator(World world, long seed) { + super(world, seed); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/help/CommandAliasHelpTopic.java b/src/main/java/org/bukkit/craftbukkit/help/CommandAliasHelpTopic.java new file mode 100644 index 000000000000..9f2238c9a73b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/help/CommandAliasHelpTopic.java @@ -0,0 +1,46 @@ +package org.bukkit.craftbukkit.help; + +import org.apache.commons.lang.Validate; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.help.HelpMap; +import org.bukkit.help.HelpTopic; + +public class CommandAliasHelpTopic extends HelpTopic { + + private final String aliasFor; + private final HelpMap helpMap; + + public CommandAliasHelpTopic(String alias, String aliasFor, HelpMap helpMap) { + this.aliasFor = aliasFor.startsWith("/") ? aliasFor : "/" + aliasFor; + this.helpMap = helpMap; + this.name = alias.startsWith("/") ? alias : "/" + alias; + Validate.isTrue(!this.name.equals(this.aliasFor), "Command " + this.name + " cannot be alias for itself"); + this.shortText = ChatColor.YELLOW + "Alias for " + ChatColor.WHITE + this.aliasFor; + } + + @Override + public String getFullText(CommandSender forWho) { + StringBuilder sb = new StringBuilder(shortText); + HelpTopic aliasForTopic = helpMap.getHelpTopic(aliasFor); + if (aliasForTopic != null) { + sb.append("\n"); + sb.append(aliasForTopic.getFullText(forWho)); + } + return sb.toString(); + } + + @Override + public boolean canSee(CommandSender commandSender) { + if (amendedPermission == null) { + HelpTopic aliasForTopic = helpMap.getHelpTopic(aliasFor); + if (aliasForTopic != null) { + return aliasForTopic.canSee(commandSender); + } else { + return false; + } + } else { + return commandSender.hasPermission(amendedPermission); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/help/CustomHelpTopic.java b/src/main/java/org/bukkit/craftbukkit/help/CustomHelpTopic.java new file mode 100644 index 000000000000..6dee2296f767 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/help/CustomHelpTopic.java @@ -0,0 +1,31 @@ +package org.bukkit.craftbukkit.help; + +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.help.HelpTopic; + +/** + * This is a help topic implementation for general topics registered in the help.yml file. + */ +public class CustomHelpTopic extends HelpTopic { + private final String permissionNode; + + public CustomHelpTopic(String name, String shortText, String fullText, String permissionNode) { + this.permissionNode = permissionNode; + this.name = name; + this.shortText = shortText; + this.fullText = shortText + "\n" + fullText; + } + + public boolean canSee(CommandSender sender) { + if (sender instanceof ConsoleCommandSender) { + return true; + } + + if (!permissionNode.equals("")) { + return sender.hasPermission(permissionNode); + } else { + return true; + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/help/CustomIndexHelpTopic.java b/src/main/java/org/bukkit/craftbukkit/help/CustomIndexHelpTopic.java new file mode 100644 index 000000000000..2089a5f52422 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/help/CustomIndexHelpTopic.java @@ -0,0 +1,40 @@ +package org.bukkit.craftbukkit.help; + +import org.bukkit.command.CommandSender; +import org.bukkit.help.HelpMap; +import org.bukkit.help.HelpTopic; +import org.bukkit.help.IndexHelpTopic; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +/** + */ +public class CustomIndexHelpTopic extends IndexHelpTopic { + private List futureTopics; + private final HelpMap helpMap; + + public CustomIndexHelpTopic(HelpMap helpMap, String name, String shortText, String permission, List futureTopics, String preamble) { + super(name, shortText, permission, new HashSet(), preamble); + this.helpMap = helpMap; + this.futureTopics = futureTopics; + } + + @Override + public String getFullText(CommandSender sender) { + if (futureTopics != null) { + List topics = new LinkedList(); + for (String futureTopic : futureTopics) { + HelpTopic topic = helpMap.getHelpTopic(futureTopic); + if (topic != null) { + topics.add(topic); + } + } + setTopicsCollection(topics); + futureTopics = null; + } + + return super.getFullText(sender); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/help/HelpTopicAmendment.java b/src/main/java/org/bukkit/craftbukkit/help/HelpTopicAmendment.java new file mode 100644 index 000000000000..4f0e00ec7eb1 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/help/HelpTopicAmendment.java @@ -0,0 +1,50 @@ +package org.bukkit.craftbukkit.help; + +/** + * A HelpTopicAmendment represents the contents of a topic amendment from the help.yml + */ +public class HelpTopicAmendment { + private final String topicName; + private final String shortText; + private final String fullText; + private final String permission; + + public HelpTopicAmendment(String topicName, String shortText, String fullText, String permission) { + this.fullText = fullText; + this.shortText = shortText; + this.topicName = topicName; + this.permission = permission; + } + + /** + * Gets the amended full text + * @return the full text + */ + public String getFullText() { + return fullText; + } + + /** + * Gets the amended short text + * @return the short text + */ + public String getShortText() { + return shortText; + } + + /** + * Gets the name of the topic being amended + * @return the topic name + */ + public String getTopicName() { + return topicName; + } + + /** + * Gets the amended permission + * @return the permission + */ + public String getPermission() { + return permission; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java b/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java new file mode 100644 index 000000000000..60a6221b4f5f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/help/HelpYamlReader.java @@ -0,0 +1,119 @@ +package org.bukkit.craftbukkit.help; + +import org.bukkit.ChatColor; +import org.bukkit.Server; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.help.HelpTopic; + +import com.google.common.base.Charsets; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.LinkedList; +import java.util.List; +import java.util.logging.Level; + +/** + * HelpYamlReader is responsible for processing the contents of the help.yml file. + */ +public class HelpYamlReader { + + private YamlConfiguration helpYaml; + private final char ALT_COLOR_CODE = '&'; + private final Server server; + + public HelpYamlReader(Server server) { + this.server = server; + + File helpYamlFile = new File("help.yml"); + YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("configurations/help.yml"), Charsets.UTF_8)); + + try { + helpYaml = YamlConfiguration.loadConfiguration(helpYamlFile); + helpYaml.options().copyDefaults(true); + helpYaml.setDefaults(defaultConfig); + + try { + if (!helpYamlFile.exists()) { + helpYaml.save(helpYamlFile); + } + } catch (IOException ex) { + server.getLogger().log(Level.SEVERE, "Could not save " + helpYamlFile, ex); + } + } catch (Exception ex) { + server.getLogger().severe("Failed to load help.yml. Verify the yaml indentation is correct. Reverting to default help.yml."); + helpYaml = defaultConfig; + } + } + + /** + * Extracts a list of all general help topics from help.yml + * + * @return A list of general topics. + */ + public List getGeneralTopics() { + List topics = new LinkedList(); + ConfigurationSection generalTopics = helpYaml.getConfigurationSection("general-topics"); + if (generalTopics != null) { + for (String topicName : generalTopics.getKeys(false)) { + ConfigurationSection section = generalTopics.getConfigurationSection(topicName); + String shortText = ChatColor.translateAlternateColorCodes(ALT_COLOR_CODE, section.getString("shortText", "")); + String fullText = ChatColor.translateAlternateColorCodes(ALT_COLOR_CODE, section.getString("fullText", "")); + String permission = section.getString("permission", ""); + topics.add(new CustomHelpTopic(topicName, shortText, fullText, permission)); + } + } + return topics; + } + + /** + * Extracts a list of all index topics from help.yml + * + * @return A list of index topics. + */ + public List getIndexTopics() { + List topics = new LinkedList(); + ConfigurationSection indexTopics = helpYaml.getConfigurationSection("index-topics"); + if (indexTopics != null) { + for (String topicName : indexTopics.getKeys(false)) { + ConfigurationSection section = indexTopics.getConfigurationSection(topicName); + String shortText = ChatColor.translateAlternateColorCodes(ALT_COLOR_CODE, section.getString("shortText", "")); + String preamble = ChatColor.translateAlternateColorCodes(ALT_COLOR_CODE, section.getString("preamble", "")); + String permission = ChatColor.translateAlternateColorCodes(ALT_COLOR_CODE, section.getString("permission", "")); + List commands = section.getStringList("commands"); + topics.add(new CustomIndexHelpTopic(server.getHelpMap(), topicName, shortText, permission, commands, preamble)); + } + } + return topics; + } + + /** + * Extracts a list of topic amendments from help.yml + * + * @return A list of amendments. + */ + public List getTopicAmendments() { + List amendments = new LinkedList(); + ConfigurationSection commandTopics = helpYaml.getConfigurationSection("amended-topics"); + if (commandTopics != null) { + for (String topicName : commandTopics.getKeys(false)) { + ConfigurationSection section = commandTopics.getConfigurationSection(topicName); + String description = ChatColor.translateAlternateColorCodes(ALT_COLOR_CODE, section.getString("shortText", "")); + String usage = ChatColor.translateAlternateColorCodes(ALT_COLOR_CODE, section.getString("fullText", "")); + String permission = section.getString("permission", ""); + amendments.add(new HelpTopicAmendment(topicName, description, usage, permission)); + } + } + return amendments; + } + + public List getIgnoredPlugins() { + return helpYaml.getStringList("ignore-plugins"); + } + + public boolean commandTopicsInMasterIndex() { + return helpYaml.getBoolean("command-topics-in-master-index", true); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/help/MultipleCommandAliasHelpTopic.java b/src/main/java/org/bukkit/craftbukkit/help/MultipleCommandAliasHelpTopic.java new file mode 100644 index 000000000000..6f4b22b99a03 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/help/MultipleCommandAliasHelpTopic.java @@ -0,0 +1,54 @@ +package org.bukkit.craftbukkit.help; + +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.command.MultipleCommandAlias; +import org.bukkit.help.HelpTopic; + +/** + * This is a help topic implementation for {@link MultipleCommandAlias} commands. + */ +public class MultipleCommandAliasHelpTopic extends HelpTopic { + + private final MultipleCommandAlias alias; + + public MultipleCommandAliasHelpTopic(MultipleCommandAlias alias) { + this.alias = alias; + + name = "/" + alias.getLabel(); + + // Build short text + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < alias.getCommands().length; i++) { + if (i != 0) { + sb.append(ChatColor.GOLD + " > " + ChatColor.WHITE); + } + sb.append("/"); + sb.append(alias.getCommands()[i].getLabel()); + } + shortText = sb.toString(); + + // Build full text + fullText = ChatColor.GOLD + "Alias for: " + ChatColor.WHITE + getShortText(); + } + + public boolean canSee(CommandSender sender) { + if (amendedPermission == null) { + if (sender instanceof ConsoleCommandSender) { + return true; + } + + for (Command command : alias.getCommands()) { + if (!command.testPermissionSilent(sender)) { + return false; + } + } + + return true; + } else { + return sender.hasPermission(amendedPermission); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/help/MultipleCommandAliasHelpTopicFactory.java b/src/main/java/org/bukkit/craftbukkit/help/MultipleCommandAliasHelpTopicFactory.java new file mode 100644 index 000000000000..36ddc97683e1 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/help/MultipleCommandAliasHelpTopicFactory.java @@ -0,0 +1,15 @@ +package org.bukkit.craftbukkit.help; + +import org.bukkit.command.MultipleCommandAlias; +import org.bukkit.help.HelpTopic; +import org.bukkit.help.HelpTopicFactory; + +/** + * This class creates {@link MultipleCommandAliasHelpTopic} help topics from {@link MultipleCommandAlias} commands. + */ +public class MultipleCommandAliasHelpTopicFactory implements HelpTopicFactory { + + public HelpTopic createTopic(MultipleCommandAlias multipleCommandAlias) { + return new MultipleCommandAliasHelpTopic(multipleCommandAlias); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java b/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java new file mode 100644 index 000000000000..c7daccd5c0c7 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java @@ -0,0 +1,218 @@ +package org.bukkit.craftbukkit.help; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Collections2; + +import org.bukkit.command.*; +import org.bukkit.command.defaults.BukkitCommand; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.command.VanillaCommandWrapper; +import org.bukkit.help.*; + +import java.util.*; + +/** + * Standard implementation of {@link HelpMap} for CraftBukkit servers. + */ +public class SimpleHelpMap implements HelpMap { + + private HelpTopic defaultTopic; + private final Map helpTopics; + private final Map> topicFactoryMap; + private final CraftServer server; + private HelpYamlReader yaml; + + @SuppressWarnings("unchecked") + public SimpleHelpMap(CraftServer server) { + this.helpTopics = new TreeMap(HelpTopicComparator.topicNameComparatorInstance()); // Using a TreeMap for its explicit sorting on key + this.topicFactoryMap = new HashMap>(); + this.server = server; + this.yaml = new HelpYamlReader(server); + + Predicate indexFilter = Predicates.not(Predicates.instanceOf(CommandAliasHelpTopic.class)); + if (!yaml.commandTopicsInMasterIndex()) { + indexFilter = Predicates.and(indexFilter, Predicates.not(new IsCommandTopicPredicate())); + } + + this.defaultTopic = new IndexHelpTopic("Index", null, null, Collections2.filter(helpTopics.values(), indexFilter), "Use /help [n] to get page n of help."); + + registerHelpTopicFactory(MultipleCommandAlias.class, new MultipleCommandAliasHelpTopicFactory()); + } + + public synchronized HelpTopic getHelpTopic(String topicName) { + if (topicName.equals("")) { + return defaultTopic; + } + + if (helpTopics.containsKey(topicName)) { + return helpTopics.get(topicName); + } + + return null; + } + + public Collection getHelpTopics() { + return helpTopics.values(); + } + + public synchronized void addTopic(HelpTopic topic) { + // Existing topics take priority + if (!helpTopics.containsKey(topic.getName())) { + helpTopics.put(topic.getName(), topic); + } + } + + public synchronized void clear() { + helpTopics.clear(); + } + + public List getIgnoredPlugins() { + return yaml.getIgnoredPlugins(); + } + + /** + * Reads the general topics from help.yml and adds them to the help index. + */ + public synchronized void initializeGeneralTopics() { + yaml = new HelpYamlReader(server); + + // Initialize general help topics from the help.yml file + for (HelpTopic topic : yaml.getGeneralTopics()) { + addTopic(topic); + } + + // Initialize index help topics from the help.yml file + for (HelpTopic topic : yaml.getIndexTopics()) { + if (topic.getName().equals("Default")) { + defaultTopic = topic; + } else { + addTopic(topic); + } + } + } + + /** + * Processes all the commands registered in the server and creates help topics for them. + */ + public synchronized void initializeCommands() { + // ** Load topics from highest to lowest priority order ** + Set ignoredPlugins = new HashSet(yaml.getIgnoredPlugins()); + + // Don't load any automatic help topics if All is ignored + if (ignoredPlugins.contains("All")) { + return; + } + + // Initialize help topics from the server's command map + outer: for (Command command : server.getCommandMap().getCommands()) { + if (commandInIgnoredPlugin(command, ignoredPlugins)) { + continue; + } + + // Register a topic + for (Class c : topicFactoryMap.keySet()) { + if (c.isAssignableFrom(command.getClass())) { + HelpTopic t = topicFactoryMap.get(c).createTopic(command); + if (t != null) addTopic(t); + continue outer; + } + if (command instanceof PluginCommand && c.isAssignableFrom(((PluginCommand)command).getExecutor().getClass())) { + HelpTopic t = topicFactoryMap.get(c).createTopic(command); + if (t != null) addTopic(t); + continue outer; + } + } + addTopic(new GenericCommandHelpTopic(command)); + } + + // Initialize command alias help topics + for (Command command : server.getCommandMap().getCommands()) { + if (commandInIgnoredPlugin(command, ignoredPlugins)) { + continue; + } + for (String alias : command.getAliases()) { + // Only register if this command owns the alias + if (server.getCommandMap().getCommand(alias) == command) { + addTopic(new CommandAliasHelpTopic("/" + alias, "/" + command.getLabel(), this)); + } + } + } + + // Add alias sub-index + Collection filteredTopics = Collections2.filter(helpTopics.values(), Predicates.instanceOf(CommandAliasHelpTopic.class)); + if (!filteredTopics.isEmpty()) { + addTopic(new IndexHelpTopic("Aliases", "Lists command aliases", null, filteredTopics)); + } + + // Initialize plugin-level sub-topics + Map> pluginIndexes = new HashMap>(); + fillPluginIndexes(pluginIndexes, server.getCommandMap().getCommands()); + + for (Map.Entry> entry : pluginIndexes.entrySet()) { + addTopic(new IndexHelpTopic(entry.getKey(), "All commands for " + entry.getKey(), null, entry.getValue(), "Below is a list of all " + entry.getKey() + " commands:")); + } + + // Amend help topics from the help.yml file + for (HelpTopicAmendment amendment : yaml.getTopicAmendments()) { + if (helpTopics.containsKey(amendment.getTopicName())) { + helpTopics.get(amendment.getTopicName()).amendTopic(amendment.getShortText(), amendment.getFullText()); + if (amendment.getPermission() != null) { + helpTopics.get(amendment.getTopicName()).amendCanSee(amendment.getPermission()); + } + } + } + } + + private void fillPluginIndexes(Map> pluginIndexes, Collection commands) { + for (Command command : commands) { + String pluginName = getCommandPluginName(command); + if (pluginName != null) { + HelpTopic topic = getHelpTopic("/" + command.getLabel()); + if (topic != null) { + if (!pluginIndexes.containsKey(pluginName)) { + pluginIndexes.put(pluginName, new TreeSet(HelpTopicComparator.helpTopicComparatorInstance())); //keep things in topic order + } + pluginIndexes.get(pluginName).add(topic); + } + } + } + } + + private String getCommandPluginName(Command command) { + if (command instanceof VanillaCommandWrapper) { + return "Minecraft"; + } + if (command instanceof BukkitCommand) { + return "Bukkit"; + } + if (command instanceof PluginIdentifiableCommand) { + return ((PluginIdentifiableCommand)command).getPlugin().getName(); + } + return null; + } + + private boolean commandInIgnoredPlugin(Command command, Set ignoredPlugins) { + if ((command instanceof BukkitCommand) && ignoredPlugins.contains("Bukkit")) { + return true; + } + if (command instanceof PluginIdentifiableCommand && ignoredPlugins.contains(((PluginIdentifiableCommand)command).getPlugin().getName())) { + return true; + } + return false; + } + + public void registerHelpTopicFactory(Class commandClass, HelpTopicFactory factory) { + if (!Command.class.isAssignableFrom(commandClass) && !CommandExecutor.class.isAssignableFrom(commandClass)) { + throw new IllegalArgumentException("commandClass must implement either Command or CommandExecutor!"); + } + topicFactoryMap.put(commandClass, factory); + } + + private class IsCommandTopicPredicate implements Predicate { + + public boolean apply(HelpTopic topic) { + return topic.getName().charAt(0) == '/'; + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java new file mode 100644 index 000000000000..4c71d9fea165 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java @@ -0,0 +1,220 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.ChatComponentText; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; + +import net.minecraft.server.Container; +import net.minecraft.server.ContainerAnvil; +import net.minecraft.server.ContainerBeacon; +import net.minecraft.server.ContainerBrewingStand; +import net.minecraft.server.ContainerChest; +import net.minecraft.server.ContainerDispenser; +import net.minecraft.server.ContainerEnchantTable; +import net.minecraft.server.ContainerFurnace; +import net.minecraft.server.ContainerHopper; +import net.minecraft.server.ContainerShulkerBox; +import net.minecraft.server.ContainerWorkbench; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.IInventory; +import net.minecraft.server.ItemStack; +import net.minecraft.server.PacketPlayOutOpenWindow; +import net.minecraft.server.PlayerInventory; +import net.minecraft.server.Slot; + +public class CraftContainer extends Container { + + private final InventoryView view; + private InventoryType cachedType; + private String cachedTitle; + private Container delegate; + private final int cachedSize; + + public CraftContainer(InventoryView view, EntityHuman player, int id) { + this.view = view; + this.windowId = id; + // TODO: Do we need to check that it really is a CraftInventory? + IInventory top = ((CraftInventory) view.getTopInventory()).getInventory(); + PlayerInventory bottom = (PlayerInventory) ((CraftInventory) view.getBottomInventory()).getInventory(); + cachedType = view.getType(); + cachedTitle = view.getTitle(); + cachedSize = getSize(); + setupSlots(top, bottom, player); + } + + public CraftContainer(final Inventory inventory, final EntityHuman player, int id) { + this(new InventoryView() { + @Override + public Inventory getTopInventory() { + return inventory; + } + + @Override + public Inventory getBottomInventory() { + return getPlayer().getInventory(); + } + + @Override + public HumanEntity getPlayer() { + return player.getBukkitEntity(); + } + + @Override + public InventoryType getType() { + return inventory.getType(); + } + }, player, id); + } + + @Override + public InventoryView getBukkitView() { + return view; + } + + private int getSize() { + return view.getTopInventory().getSize(); + } + + @Override + public boolean c(EntityHuman entityhuman) { + if (cachedType == view.getType() && cachedSize == getSize() && cachedTitle.equals(view.getTitle())) { + return true; + } + // If the window type has changed for some reason, update the player + // This method will be called every tick or something, so it's + // as good a place as any to put something like this. + boolean typeChanged = (cachedType != view.getType()); + cachedType = view.getType(); + cachedTitle = view.getTitle(); + if (view.getPlayer() instanceof CraftPlayer) { + CraftPlayer player = (CraftPlayer) view.getPlayer(); + String type = getNotchInventoryType(cachedType); + IInventory top = ((CraftInventory) view.getTopInventory()).getInventory(); + PlayerInventory bottom = (PlayerInventory) ((CraftInventory) view.getBottomInventory()).getInventory(); + this.items.clear(); + this.slots.clear(); + if (typeChanged) { + setupSlots(top, bottom, player.getHandle()); + } + int size = getSize(); + player.getHandle().playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.windowId, type, new ChatComponentText(cachedTitle), size)); + player.updateInventory(); + } + return true; + } + + public static String getNotchInventoryType(InventoryType type) { + switch (type) { + case WORKBENCH: + return "minecraft:crafting_table"; + case FURNACE: + return "minecraft:furnace"; + case DISPENSER: + return "minecraft:dispenser"; + case ENCHANTING: + return "minecraft:enchanting_table"; + case BREWING: + return "minecraft:brewing_stand"; + case BEACON: + return "minecraft:beacon"; + case ANVIL: + return "minecraft:anvil"; + case HOPPER: + return "minecraft:hopper"; + case DROPPER: + return "minecraft:dropper"; + case SHULKER_BOX: + return "minecraft:shulker_box"; + default: + return "minecraft:chest"; + } + } + + private void setupSlots(IInventory top, PlayerInventory bottom, EntityHuman entityhuman) { + switch (cachedType) { + case CREATIVE: + break; // TODO: This should be an error? + case PLAYER: + case CHEST: + delegate = new ContainerChest(bottom, top, entityhuman); + break; + case DISPENSER: + case DROPPER: + delegate = new ContainerDispenser(bottom, top); + break; + case FURNACE: + delegate = new ContainerFurnace(bottom, top); + break; + case CRAFTING: // TODO: This should be an error? + case WORKBENCH: + setupWorkbench(top, bottom); // SPIGOT-3812 - manually set up slots so we can use the delegated inventory and not the automatically created one + break; + case ENCHANTING: + delegate = new ContainerEnchantTable(bottom, entityhuman.world, entityhuman.getChunkCoordinates()); + break; + case BREWING: + delegate = new ContainerBrewingStand(bottom, top); + break; + case HOPPER: + delegate = new ContainerHopper(bottom, top, entityhuman); + break; + case ANVIL: + delegate = new ContainerAnvil(bottom, entityhuman.world, entityhuman.getChunkCoordinates(), entityhuman); + break; + case BEACON: + delegate = new ContainerBeacon(bottom, top); + break; + case SHULKER_BOX: + delegate = new ContainerShulkerBox(bottom, top, entityhuman); + break; + } + + if (delegate != null) { + this.items = delegate.items; + this.slots = delegate.slots; + } + + // SPIGOT-4598 - we should still delegate the shift click handler + if (cachedType == InventoryType.WORKBENCH) { + delegate = new ContainerWorkbench(bottom, entityhuman.world, entityhuman.getChunkCoordinates()); + } + } + + private void setupWorkbench(IInventory top, IInventory bottom) { + // This code copied from ContainerWorkbench + this.a(new Slot(top, 0, 124, 35)); + + int row; + int col; + + for (row = 0; row < 3; ++row) { + for (col = 0; col < 3; ++col) { + this.a(new Slot(top, 1 + col + row * 3, 30 + col * 18, 17 + row * 18)); + } + } + + for (row = 0; row < 3; ++row) { + for (col = 0; col < 9; ++col) { + this.a(new Slot(bottom, col + row * 9 + 9, 8 + col * 18, 84 + row * 18)); + } + } + + for (col = 0; col < 9; ++col) { + this.a(new Slot(bottom, col, 8 + col * 18, 142)); + } + // End copy from ContainerWorkbench + } + + @Override + public ItemStack shiftClick(EntityHuman entityhuman, int i) { + return (delegate != null) ? delegate.shiftClick(entityhuman, i) : super.shiftClick(entityhuman, i); + } + + @Override + public boolean canUse(EntityHuman entity) { + return true; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftCustomTagTypeRegistry.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftCustomTagTypeRegistry.java new file mode 100644 index 000000000000..47ff875b9304 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftCustomTagTypeRegistry.java @@ -0,0 +1,217 @@ +package org.bukkit.craftbukkit.inventory; + +import com.google.common.primitives.Primitives; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import net.minecraft.server.NBTBase; +import net.minecraft.server.NBTTagByte; +import net.minecraft.server.NBTTagByteArray; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagDouble; +import net.minecraft.server.NBTTagFloat; +import net.minecraft.server.NBTTagInt; +import net.minecraft.server.NBTTagIntArray; +import net.minecraft.server.NBTTagLong; +import net.minecraft.server.NBTTagLongArray; +import net.minecraft.server.NBTTagShort; +import net.minecraft.server.NBTTagString; +import org.apache.commons.lang3.Validate; +import org.bukkit.craftbukkit.inventory.tags.CraftCustomItemTagContainer; +import org.bukkit.inventory.meta.tags.CustomItemTagContainer; + +/** + * This class represents a registry that contains the used adapters for. + */ +public final class CraftCustomTagTypeRegistry { + + private final Function CREATE_ADAPTER = this::createAdapter; + + private class CustomTagAdapter { + + private final Function builder; + private final Function extractor; + + private final Class primitiveType; + private final Class nbtBaseType; + + public CustomTagAdapter(Class primitiveType, Class nbtBaseType, Function builder, Function extractor) { + this.primitiveType = primitiveType; + this.nbtBaseType = nbtBaseType; + this.builder = builder; + this.extractor = extractor; + } + + /** + * This method will extract the value stored in the tag, according to + * the expected primitive type. + * + * @param base the base to extract from + * @return the value stored inside of the tag + * @throws ClassCastException if the passed base is not an instanced of + * the defined base type and therefore is not applicable to the + * extractor function + */ + T extract(NBTBase base) { + Validate.isInstanceOf(nbtBaseType, base, "The provided NBTBase was of the type %s. Expected type %s", base.getClass().getSimpleName(), nbtBaseType.getSimpleName()); + return this.extractor.apply(nbtBaseType.cast(base)); + } + + /** + * Builds a tag instance wrapping around the provided value object. + * + * @param value the value to store inside the created tag + * @return the new tag instance + * @throws ClassCastException if the passed value object is not of the + * defined primitive type and therefore is not applicable to the builder + * function + */ + Z build(Object value) { + Validate.isInstanceOf(primitiveType, value, "The provided value was of the type %s. Expected type %s", value.getClass().getSimpleName(), primitiveType.getSimpleName()); + return this.builder.apply(primitiveType.cast(value)); + } + + /** + * Returns if the tag instance matches the adapters one. + * + * @param base the base to check + * @return if the tag was an instance of the set type + */ + boolean isInstance(NBTBase base) { + return this.nbtBaseType.isInstance(base); + } + } + + private final Map adapters = new HashMap<>(); + + /** + * Creates a suitable adapter instance for the primitive class type + * + * @param type the type to create an adapter for + * @param the generic type of that class + * @return the created adapter instance + * @throws IllegalArgumentException if no suitable tag type adapter for this + * type was found + */ + private CustomTagAdapter createAdapter(Class type) { + if (!Primitives.isWrapperType(type)) { + type = Primitives.wrap(type); //Make sure we will always "switch" over the wrapper types + } + + /* + Primitives + */ + if (Objects.equals(Byte.class, type)) { + return createAdapter(Byte.class, NBTTagByte.class, NBTTagByte::new, NBTTagByte::asByte); + } + if (Objects.equals(Short.class, type)) { + return createAdapter(Short.class, NBTTagShort.class, NBTTagShort::new, NBTTagShort::asShort); + } + if (Objects.equals(Integer.class, type)) { + return createAdapter(Integer.class, NBTTagInt.class, NBTTagInt::new, NBTTagInt::asInt); + } + if (Objects.equals(Long.class, type)) { + return createAdapter(Long.class, NBTTagLong.class, NBTTagLong::new, NBTTagLong::asLong); + } + if (Objects.equals(Float.class, type)) { + return createAdapter(Float.class, NBTTagFloat.class, NBTTagFloat::new, NBTTagFloat::asFloat); + } + if (Objects.equals(Double.class, type)) { + return createAdapter(Double.class, NBTTagDouble.class, NBTTagDouble::new, NBTTagDouble::asDouble); + } + + /* + String + */ + if (Objects.equals(String.class, type)) { + return createAdapter(String.class, NBTTagString.class, NBTTagString::new, NBTTagString::asString); + } + + /* + Primitive Arrays + */ + if (Objects.equals(byte[].class, type)) { + return createAdapter(byte[].class, NBTTagByteArray.class, array -> new NBTTagByteArray(Arrays.copyOf(array, array.length)), n -> Arrays.copyOf(n.c(), n.size())); + } + if (Objects.equals(int[].class, type)) { + return createAdapter(int[].class, NBTTagIntArray.class, array -> new NBTTagIntArray(Arrays.copyOf(array, array.length)), n -> Arrays.copyOf(n.d(), n.size())); + } + if (Objects.equals(long[].class, type)) { + return createAdapter(long[].class, NBTTagLongArray.class, array -> new NBTTagLongArray(Arrays.copyOf(array, array.length)), n -> Arrays.copyOf(n.d(), n.size())); + } + + /* + Note that this will map the interface CustomItemTagContainer directly to the CraftBukkit implementation + Passing any other instance of this form to the tag type registry will throw a ClassCastException as defined in CustomTagAdapter#build + */ + if (Objects.equals(CustomItemTagContainer.class, type)) { + return createAdapter(CraftCustomItemTagContainer.class, NBTTagCompound.class, CraftCustomItemTagContainer::toTagCompound, tag -> { + CraftCustomItemTagContainer container = new CraftCustomItemTagContainer(this); + for (String key : tag.getKeys()) { + container.put(key, tag.get(key)); + } + return container; + }); + } + + throw new IllegalArgumentException("Could not find a valid CustomTagAdapter implementation for the requested type " + type.getSimpleName()); + } + + private CustomTagAdapter createAdapter(Class primitiveType, Class nbtBaseType, Function builder, Function extractor) { + return new CustomTagAdapter<>(primitiveType, nbtBaseType, builder, extractor); + } + + /** + * Wraps the passed value into a tag instance. + * + * @param type the type of the passed value + * @param value the value to be stored in the tag + * @param the generic type of the value + * @return the created tag instance + * @throws IllegalArgumentException if no suitable tag type adapter for this + * type was found + */ + public NBTBase wrap(Class type, T value) { + return this.adapters.computeIfAbsent(type, CREATE_ADAPTER).build(value); + } + + /** + * Returns if the tag instance matches the provided primitive type. + * + * @param type the type of the primitive value + * @param base the base instance to check + * @param the generic type of the type + * @return if the base stores values of the primitive type passed + * @throws IllegalArgumentException if no suitable tag type adapter for this + * type was found + */ + public boolean isInstanceOf(Class type, NBTBase base) { + return this.adapters.computeIfAbsent(type, CREATE_ADAPTER).isInstance(base); + } + + /** + * Extracts the value out of the provided tag. + * + * @param type the type of the value to extract + * @param tag the tag to extract the value from + * @param the generic type of the value stored inside the tag + * @return the extracted value + * @throws IllegalArgumentException if the passed base is not an instanced + * of the defined base type and therefore is not applicable to the extractor + * function + * @throws IllegalArgumentException if the found object is not of type + * passed + * @throws IllegalArgumentException if no suitable tag type adapter for this + * type was found + */ + public T extract(Class type, NBTBase tag) throws ClassCastException, IllegalArgumentException { + CustomTagAdapter adapter = this.adapters.computeIfAbsent(type, CREATE_ADAPTER); + Validate.isTrue(adapter.isInstance(tag), "`The found tag instance cannot store %s as it is a %s", type.getSimpleName(), tag.getClass().getSimpleName()); + + Object foundValue = adapter.extract(tag); + Validate.isInstanceOf(type, foundValue, "The found object is of the type %s. Expected type %s", foundValue.getClass().getSimpleName(), type.getSimpleName()); + return type.cast(foundValue); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftEntityEquipment.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftEntityEquipment.java new file mode 100644 index 000000000000..3ed9e2bb0956 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftEntityEquipment.java @@ -0,0 +1,193 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.EntityInsentient; +import net.minecraft.server.EnumItemSlot; + +import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.entity.Entity; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; + +public class CraftEntityEquipment implements EntityEquipment { + + private final CraftLivingEntity entity; + + public CraftEntityEquipment(CraftLivingEntity entity) { + this.entity = entity; + } + + @Override + public ItemStack getItemInMainHand() { + return getEquipment(EnumItemSlot.MAINHAND); + } + + @Override + public void setItemInMainHand(ItemStack item) { + setEquipment(EnumItemSlot.MAINHAND, item); + } + + @Override + public ItemStack getItemInOffHand() { + return getEquipment(EnumItemSlot.OFFHAND); + } + + @Override + public void setItemInOffHand(ItemStack item) { + setEquipment(EnumItemSlot.OFFHAND, item); + } + + @Override + public ItemStack getItemInHand() { + return getItemInMainHand(); + } + + @Override + public void setItemInHand(ItemStack stack) { + setItemInMainHand(stack); + } + + public ItemStack getHelmet() { + return getEquipment(EnumItemSlot.HEAD); + } + + public void setHelmet(ItemStack helmet) { + setEquipment(EnumItemSlot.HEAD, helmet); + } + + public ItemStack getChestplate() { + return getEquipment(EnumItemSlot.CHEST); + } + + public void setChestplate(ItemStack chestplate) { + setEquipment(EnumItemSlot.CHEST, chestplate); + } + + public ItemStack getLeggings() { + return getEquipment(EnumItemSlot.LEGS); + } + + public void setLeggings(ItemStack leggings) { + setEquipment(EnumItemSlot.LEGS, leggings); + } + + public ItemStack getBoots() { + return getEquipment(EnumItemSlot.FEET); + } + + public void setBoots(ItemStack boots) { + setEquipment(EnumItemSlot.FEET, boots); + } + + public ItemStack[] getArmorContents() { + ItemStack[] armor = new ItemStack[]{ + getEquipment(EnumItemSlot.FEET), + getEquipment(EnumItemSlot.LEGS), + getEquipment(EnumItemSlot.CHEST), + getEquipment(EnumItemSlot.HEAD), + }; + return armor; + } + + public void setArmorContents(ItemStack[] items) { + setEquipment(EnumItemSlot.FEET, items.length >= 1 ? items[0] : null); + setEquipment(EnumItemSlot.LEGS, items.length >= 2 ? items[1] : null); + setEquipment(EnumItemSlot.CHEST, items.length >= 3 ? items[2] : null); + setEquipment(EnumItemSlot.HEAD, items.length >= 4 ? items[3] : null); + } + + private ItemStack getEquipment(EnumItemSlot slot) { + return CraftItemStack.asBukkitCopy(entity.getHandle().getEquipment(slot)); + } + + private void setEquipment(EnumItemSlot slot, ItemStack stack) { + entity.getHandle().setSlot(slot, CraftItemStack.asNMSCopy(stack)); + } + + public void clear() { + for (EnumItemSlot slot : EnumItemSlot.values()) { + setEquipment(slot, null); + } + } + + public Entity getHolder() { + return entity; + } + + @Override + public float getItemInHandDropChance() { + return getItemInMainHandDropChance(); + } + + @Override + public void setItemInHandDropChance(float chance) { + setItemInMainHandDropChance(chance); + } + + @Override + public float getItemInMainHandDropChance() { + return getDropChance(EnumItemSlot.MAINHAND); + } + + @Override + public void setItemInMainHandDropChance(float chance) { + setDropChance(EnumItemSlot.MAINHAND, chance); + } + + @Override + public float getItemInOffHandDropChance() { + return getDropChance(EnumItemSlot.OFFHAND); + } + + @Override + public void setItemInOffHandDropChance(float chance) { + setDropChance(EnumItemSlot.OFFHAND, chance); + } + + public float getHelmetDropChance() { + return getDropChance(EnumItemSlot.HEAD); + } + + public void setHelmetDropChance(float chance) { + setDropChance(EnumItemSlot.HEAD, chance); + } + + public float getChestplateDropChance() { + return getDropChance(EnumItemSlot.CHEST); + } + + public void setChestplateDropChance(float chance) { + setDropChance(EnumItemSlot.CHEST, chance); + } + + public float getLeggingsDropChance() { + return getDropChance(EnumItemSlot.LEGS); + } + + public void setLeggingsDropChance(float chance) { + setDropChance(EnumItemSlot.LEGS, chance); + } + + public float getBootsDropChance() { + return getDropChance(EnumItemSlot.FEET); + } + + public void setBootsDropChance(float chance) { + setDropChance(EnumItemSlot.FEET, chance); + } + + private void setDropChance(EnumItemSlot slot, float chance) { + if (slot == EnumItemSlot.MAINHAND || slot == EnumItemSlot.OFFHAND) { + ((EntityInsentient) entity.getHandle()).dropChanceHand[slot.b()] = chance - 0.1F; + } else { + ((EntityInsentient) entity.getHandle()).dropChanceArmor[slot.b()] = chance - 0.1F; + } + } + + private float getDropChance(EnumItemSlot slot) { + if (slot == EnumItemSlot.MAINHAND || slot == EnumItemSlot.OFFHAND) { + return ((EntityInsentient) entity.getHandle()).dropChanceHand[slot.b()] + 0.1F; + } else { + return ((EntityInsentient) entity.getHandle()).dropChanceArmor[slot.b()] + 0.1F; + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftFurnaceRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftFurnaceRecipe.java new file mode 100644 index 000000000000..a38d08a15309 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftFurnaceRecipe.java @@ -0,0 +1,30 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.MinecraftServer; +import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.inventory.FurnaceRecipe; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.RecipeChoice; + +public class CraftFurnaceRecipe extends FurnaceRecipe implements CraftRecipe { + public CraftFurnaceRecipe(NamespacedKey key, ItemStack result, RecipeChoice source, float experience, int cookingTime) { + super(key, result, source, experience, cookingTime); + } + + public static CraftFurnaceRecipe fromBukkitRecipe(FurnaceRecipe recipe) { + if (recipe instanceof CraftFurnaceRecipe) { + return (CraftFurnaceRecipe) recipe; + } + CraftFurnaceRecipe ret = new CraftFurnaceRecipe(recipe.getKey(), recipe.getResult(), recipe.getInputChoice(), recipe.getExperience(), recipe.getCookingTime()); + ret.setGroup(recipe.getGroup()); + return ret; + } + + @Override + public void addToCraftingManager() { + ItemStack result = this.getResult(); + + MinecraftServer.getServer().getCraftingManager().a(new net.minecraft.server.FurnaceRecipe(CraftNamespacedKey.toMinecraft(this.getKey()), this.getGroup(), toNMS(this.getInputChoice(), true), CraftItemStack.asNMSCopy(result), getExperience(), getCookingTime())); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java new file mode 100644 index 000000000000..01af98293342 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java @@ -0,0 +1,510 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; + +import net.minecraft.server.IHopper; +import net.minecraft.server.IInventory; +import net.minecraft.server.InventoryCrafting; +import net.minecraft.server.InventoryEnderChest; +import net.minecraft.server.InventoryMerchant; +import net.minecraft.server.PlayerInventory; +import net.minecraft.server.TileEntityBeacon; +import net.minecraft.server.TileEntityBrewingStand; +import net.minecraft.server.TileEntityDispenser; +import net.minecraft.server.TileEntityDropper; +import net.minecraft.server.TileEntityFurnace; +import net.minecraft.server.TileEntityShulkerBox; + +import org.apache.commons.lang.Validate; +import org.bukkit.Location; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.Material; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.craftbukkit.util.CraftLegacy; + +public class CraftInventory implements Inventory { + protected final IInventory inventory; + + public CraftInventory(IInventory inventory) { + this.inventory = inventory; + } + + public IInventory getInventory() { + return inventory; + } + + public int getSize() { + return getInventory().getSize(); + } + + public String getName() { + return CraftChatMessage.fromComponent(getInventory().getDisplayName()); + } + + public ItemStack getItem(int index) { + net.minecraft.server.ItemStack item = getInventory().getItem(index); + return item.isEmpty() ? null : CraftItemStack.asCraftMirror(item); + } + + protected ItemStack[] asCraftMirror(List mcItems) { + int size = mcItems.size(); + ItemStack[] items = new ItemStack[size]; + + for (int i = 0; i < size; i++) { + net.minecraft.server.ItemStack mcItem = mcItems.get(i); + items[i] = (mcItem.isEmpty()) ? null : CraftItemStack.asCraftMirror(mcItem); + } + + return items; + } + + @Override + public ItemStack[] getStorageContents() { + return getContents(); + } + + @Override + public void setStorageContents(ItemStack[] items) throws IllegalArgumentException { + setContents(items); + } + + public ItemStack[] getContents() { + List mcItems = getInventory().getContents(); + + return asCraftMirror(mcItems); + } + + public void setContents(ItemStack[] items) { + if (getSize() < items.length) { + throw new IllegalArgumentException("Invalid inventory size; expected " + getSize() + " or less"); + } + + for (int i = 0; i < getSize(); i++) { + if (i >= items.length) { + setItem(i, null); + } else { + setItem(i, items[i]); + } + } + } + + public void setItem(int index, ItemStack item) { + getInventory().setItem(index, CraftItemStack.asNMSCopy(item)); + } + + public boolean contains(Material material) { + Validate.notNull(material, "Material cannot be null"); + material = CraftLegacy.fromLegacy(material); + for (ItemStack item : getStorageContents()) { + if (item != null && item.getType() == material) { + return true; + } + } + return false; + } + + public boolean contains(ItemStack item) { + if (item == null) { + return false; + } + for (ItemStack i : getStorageContents()) { + if (item.equals(i)) { + return true; + } + } + return false; + } + + public boolean contains(Material material, int amount) { + Validate.notNull(material, "Material cannot be null"); + material = CraftLegacy.fromLegacy(material); + if (amount <= 0) { + return true; + } + for (ItemStack item : getStorageContents()) { + if (item != null && item.getType()== material) { + if ((amount -= item.getAmount()) <= 0) { + return true; + } + } + } + return false; + } + + public boolean contains(ItemStack item, int amount) { + if (item == null) { + return false; + } + if (amount <= 0) { + return true; + } + for (ItemStack i : getStorageContents()) { + if (item.equals(i) && --amount <= 0) { + return true; + } + } + return false; + } + + public boolean containsAtLeast(ItemStack item, int amount) { + if (item == null) { + return false; + } + if (amount <= 0) { + return true; + } + for (ItemStack i : getStorageContents()) { + if (item.isSimilar(i) && (amount -= i.getAmount()) <= 0) { + return true; + } + } + return false; + } + + public HashMap all(Material material) { + Validate.notNull(material, "Material cannot be null"); + material = CraftLegacy.fromLegacy(material); + HashMap slots = new HashMap(); + + ItemStack[] inventory = getStorageContents(); + for (int i = 0; i < inventory.length; i++) { + ItemStack item = inventory[i]; + if (item != null && item.getType()== material) { + slots.put(i, item); + } + } + return slots; + } + + public HashMap all(ItemStack item) { + HashMap slots = new HashMap(); + if (item != null) { + ItemStack[] inventory = getStorageContents(); + for (int i = 0; i < inventory.length; i++) { + if (item.equals(inventory[i])) { + slots.put(i, inventory[i]); + } + } + } + return slots; + } + + public int first(Material material) { + Validate.notNull(material, "Material cannot be null"); + material = CraftLegacy.fromLegacy(material); + ItemStack[] inventory = getStorageContents(); + for (int i = 0; i < inventory.length; i++) { + ItemStack item = inventory[i]; + if (item != null && item.getType()== material) { + return i; + } + } + return -1; + } + + public int first(ItemStack item) { + return first(item, true); + } + + private int first(ItemStack item, boolean withAmount) { + // Paper start + return first(item, withAmount, getStorageContents()); + } + + private int first(ItemStack item, boolean withAmount, ItemStack[] inventory) { + // Paper end + if (item == null) { + return -1; + } + //ItemStack[] inventory = getStorageContents(); // Paper - let param deal + for (int i = 0; i < inventory.length; i++) { + if (inventory[i] == null) continue; + + if (withAmount ? item.equals(inventory[i]) : item.isSimilar(inventory[i])) { + return i; + } + } + return -1; + } + + public int firstEmpty() { + ItemStack[] inventory = getStorageContents(); + for (int i = 0; i < inventory.length; i++) { + if (inventory[i] == null) { + return i; + } + } + return -1; + } + + public int firstPartial(Material material) { + Validate.notNull(material, "Material cannot be null"); + material = CraftLegacy.fromLegacy(material); + ItemStack[] inventory = getStorageContents(); + for (int i = 0; i < inventory.length; i++) { + ItemStack item = inventory[i]; + if (item != null && item.getType()== material && item.getAmount() < item.getMaxStackSize()) { + return i; + } + } + return -1; + } + + private int firstPartial(ItemStack item) { + ItemStack[] inventory = getStorageContents(); + ItemStack filteredItem = CraftItemStack.asCraftCopy(item); + if (item == null) { + return -1; + } + for (int i = 0; i < inventory.length; i++) { + ItemStack cItem = inventory[i]; + if (cItem != null && cItem.getAmount() < cItem.getMaxStackSize() && cItem.isSimilar(filteredItem)) { + return i; + } + } + return -1; + } + + public HashMap addItem(ItemStack... items) { + Validate.noNullElements(items, "Item cannot be null"); + HashMap leftover = new HashMap(); + + /* TODO: some optimization + * - Create a 'firstPartial' with a 'fromIndex' + * - Record the lastPartial per Material + * - Cache firstEmpty result + */ + + for (int i = 0; i < items.length; i++) { + ItemStack item = items[i]; + while (true) { + // Do we already have a stack of it? + int firstPartial = firstPartial(item); + + // Drat! no partial stack + if (firstPartial == -1) { + // Find a free spot! + int firstFree = firstEmpty(); + + if (firstFree == -1) { + // No space at all! + leftover.put(i, item); + break; + } else { + // More than a single stack! + if (item.getAmount() > getMaxItemStack()) { + CraftItemStack stack = CraftItemStack.asCraftCopy(item); + stack.setAmount(getMaxItemStack()); + setItem(firstFree, stack); + item.setAmount(item.getAmount() - getMaxItemStack()); + } else { + // Just store it + setItem(firstFree, item); + break; + } + } + } else { + // So, apparently it might only partially fit, well lets do just that + ItemStack partialItem = getItem(firstPartial); + + int amount = item.getAmount(); + int partialAmount = partialItem.getAmount(); + int maxAmount = partialItem.getMaxStackSize(); + + // Check if it fully fits + if (amount + partialAmount <= maxAmount) { + partialItem.setAmount(amount + partialAmount); + // To make sure the packet is sent to the client + setItem(firstPartial, partialItem); + break; + } + + // It fits partially + partialItem.setAmount(maxAmount); + // To make sure the packet is sent to the client + setItem(firstPartial, partialItem); + item.setAmount(amount + partialAmount - maxAmount); + } + } + } + return leftover; + } + + public HashMap removeItem(ItemStack... items) { + // Paper start + return removeItem(false, items); + } + + @Override + public HashMap removeItemAnySlot(ItemStack... items) { + return removeItem(true, items); + } + + private HashMap removeItem(boolean searchEntire, ItemStack... items) { + // Paper end + Validate.notNull(items, "Items cannot be null"); + HashMap leftover = new HashMap(); + + // TODO: optimization + + for (int i = 0; i < items.length; i++) { + ItemStack item = items[i]; + int toDelete = item.getAmount(); + + while (true) { + // Paper start - Allow searching entire contents + ItemStack[] toSearch = searchEntire ? getContents() : getStorageContents(); + int first = first(item, false, toSearch); + // Paper end + + // Drat! we don't have this type in the inventory + if (first == -1) { + item.setAmount(toDelete); + leftover.put(i, item); + break; + } else { + ItemStack itemStack = getItem(first); + int amount = itemStack.getAmount(); + + if (amount <= toDelete) { + toDelete -= amount; + // clear the slot, all used up + clear(first); + } else { + // split the stack and store + itemStack.setAmount(amount - toDelete); + setItem(first, itemStack); + toDelete = 0; + } + } + + // Bail when done + if (toDelete <= 0) { + break; + } + } + } + return leftover; + } + + private int getMaxItemStack() { + return getInventory().getMaxStackSize(); + } + + public void remove(Material material) { + Validate.notNull(material, "Material cannot be null"); + material = CraftLegacy.fromLegacy(material); + ItemStack[] items = getStorageContents(); + for (int i = 0; i < items.length; i++) { + if (items[i] != null && items[i].getType()== material) { + clear(i); + } + } + } + + public void remove(ItemStack item) { + ItemStack[] items = getStorageContents(); + for (int i = 0; i < items.length; i++) { + if (items[i] != null && items[i].equals(item)) { + clear(i); + } + } + } + + public void clear(int index) { + setItem(index, null); + } + + public void clear() { + for (int i = 0; i < getSize(); i++) { + clear(i); + } + } + + public ListIterator iterator() { + return new InventoryIterator(this); + } + + public ListIterator iterator(int index) { + if (index < 0) { + index += getSize() + 1; // ie, with -1, previous() will return the last element + } + return new InventoryIterator(this, index); + } + + public List getViewers() { + return this.inventory.getViewers(); + } + + public String getTitle() { + return getName(); + } + + public InventoryType getType() { + // Thanks to Droppers extending Dispensers, order is important. + if (inventory instanceof InventoryCrafting) { + return inventory.getSize() >= 9 ? InventoryType.WORKBENCH : InventoryType.CRAFTING; + } else if (inventory instanceof PlayerInventory) { + return InventoryType.PLAYER; + } else if (inventory instanceof TileEntityDropper) { + return InventoryType.DROPPER; + } else if (inventory instanceof TileEntityDispenser) { + return InventoryType.DISPENSER; + } else if (inventory instanceof TileEntityFurnace) { + return InventoryType.FURNACE; + } else if (this instanceof CraftInventoryEnchanting) { + return InventoryType.ENCHANTING; + } else if (inventory instanceof TileEntityBrewingStand) { + return InventoryType.BREWING; + } else if (inventory instanceof CraftInventoryCustom.MinecraftInventory) { + return ((CraftInventoryCustom.MinecraftInventory) inventory).getType(); + } else if (inventory instanceof InventoryEnderChest) { + return InventoryType.ENDER_CHEST; + } else if (inventory instanceof InventoryMerchant) { + return InventoryType.MERCHANT; + } else if (inventory instanceof TileEntityBeacon) { + return InventoryType.BEACON; + } else if (this instanceof CraftInventoryAnvil) { + return InventoryType.ANVIL; + } else if (inventory instanceof IHopper) { + return InventoryType.HOPPER; + } else if (inventory instanceof TileEntityShulkerBox) { + return InventoryType.SHULKER_BOX; + } else { + return InventoryType.CHEST; + } + } + + public InventoryHolder getHolder() { + return inventory.getOwner(); + } + + public int getMaxStackSize() { + return inventory.getMaxStackSize(); + } + + public void setMaxStackSize(int size) { + inventory.setMaxStackSize(size); + } + + @Override + public int hashCode() { + return inventory.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + return obj instanceof CraftInventory && ((CraftInventory) obj).inventory.equals(this.inventory); + } + + @Override + public Location getLocation() { + return inventory.getLocation(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAbstractHorse.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAbstractHorse.java new file mode 100644 index 000000000000..b7793caa2cd0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAbstractHorse.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.IInventory; +import org.bukkit.inventory.AbstractHorseInventory; +import org.bukkit.inventory.ItemStack; + +public class CraftInventoryAbstractHorse extends CraftInventory implements AbstractHorseInventory { + + public CraftInventoryAbstractHorse(IInventory inventory) { + super(inventory); + } + + @Override + public ItemStack getSaddle() { + return getItem(0); + } + + @Override + public void setSaddle(ItemStack stack) { + setItem(0, stack); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java new file mode 100644 index 000000000000..ca19600da57d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryAnvil.java @@ -0,0 +1,86 @@ +package org.bukkit.craftbukkit.inventory; + +import com.google.common.base.Preconditions; +import net.minecraft.server.ContainerAnvil; +import net.minecraft.server.IInventory; +import org.bukkit.Location; +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.ItemStack; + +public class CraftInventoryAnvil extends CraftInventory implements AnvilInventory { + + private final Location location; + private final IInventory resultInventory; + private final ContainerAnvil container; + + public CraftInventoryAnvil(Location location, IInventory inventory, IInventory resultInventory, ContainerAnvil container) { + super(inventory); + this.location = location; + this.resultInventory = resultInventory; + this.container = container; + } + + public IInventory getResultInventory() { + return resultInventory; + } + + public IInventory getIngredientsInventory() { + return inventory; + } + + @Override + public ItemStack getItem(int slot) { + if (slot < getIngredientsInventory().getSize()) { + net.minecraft.server.ItemStack item = getIngredientsInventory().getItem(slot); + return item.isEmpty() ? null : CraftItemStack.asCraftMirror(item); + } else { + net.minecraft.server.ItemStack item = getResultInventory().getItem(slot - getIngredientsInventory().getSize()); + return item.isEmpty() ? null : CraftItemStack.asCraftMirror(item); + } + } + + @Override + public void setItem(int index, ItemStack item) { + if (index < getIngredientsInventory().getSize()) { + getIngredientsInventory().setItem(index, CraftItemStack.asNMSCopy(item)); + } else { + getResultInventory().setItem((index - getIngredientsInventory().getSize()), CraftItemStack.asNMSCopy(item)); + } + } + + @Override + public int getSize() { + return getResultInventory().getSize() + getIngredientsInventory().getSize(); + } + + @Override + public Location getLocation() { + return location; + } + + @Override + public String getRenameText() { + return container.renameText; + } + + @Override + public int getRepairCost() { + return container.levelCost; + } + + @Override + public void setRepairCost(int i) { + container.levelCost = i; + } + + @Override + public int getMaximumRepairCost() { + return container.maximumRepairCost; + } + + @Override + public void setMaximumRepairCost(int levels) { + Preconditions.checkArgument(levels >= 0, "Maximum repair cost must be positive (or 0)"); + container.maximumRepairCost = levels; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryBeacon.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryBeacon.java new file mode 100644 index 000000000000..43c4107c6507 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryBeacon.java @@ -0,0 +1,19 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.TileEntityBeacon; +import org.bukkit.inventory.BeaconInventory; +import org.bukkit.inventory.ItemStack; + +public class CraftInventoryBeacon extends CraftInventory implements BeaconInventory { + public CraftInventoryBeacon(TileEntityBeacon beacon) { + super(beacon); + } + + public void setItem(ItemStack item) { + setItem(0, item); + } + + public ItemStack getItem() { + return getItem(0); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryBrewer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryBrewer.java new file mode 100644 index 000000000000..86c89e869380 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryBrewer.java @@ -0,0 +1,36 @@ +package org.bukkit.craftbukkit.inventory; + +import org.bukkit.block.BrewingStand; +import org.bukkit.inventory.BrewerInventory; +import org.bukkit.inventory.ItemStack; + +import net.minecraft.server.IInventory; + +public class CraftInventoryBrewer extends CraftInventory implements BrewerInventory { + public CraftInventoryBrewer(IInventory inventory) { + super(inventory); + } + + public ItemStack getIngredient() { + return getItem(3); + } + + public void setIngredient(ItemStack ingredient) { + setItem(3, ingredient); + } + + @Override + public BrewingStand getHolder() { + return (BrewingStand) inventory.getOwner(); + } + + @Override + public ItemStack getFuel() { + return getItem(4); + } + + @Override + public void setFuel(ItemStack fuel) { + setItem(4, fuel); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCrafting.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCrafting.java new file mode 100644 index 000000000000..3a375e77da5d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCrafting.java @@ -0,0 +1,121 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Arrays; +import java.util.List; + +import net.minecraft.server.IRecipe; +import net.minecraft.server.IInventory; + +import org.bukkit.inventory.CraftingInventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.Recipe; + +public class CraftInventoryCrafting extends CraftInventory implements CraftingInventory { + private final IInventory resultInventory; + + public CraftInventoryCrafting(IInventory inventory, IInventory resultInventory) { + super(inventory); + this.resultInventory = resultInventory; + } + + public IInventory getResultInventory() { + return resultInventory; + } + + public IInventory getMatrixInventory() { + return inventory; + } + + @Override + public int getSize() { + return getResultInventory().getSize() + getMatrixInventory().getSize(); + } + + @Override + public void setContents(ItemStack[] items) { + if (getSize() > items.length) { + throw new IllegalArgumentException("Invalid inventory size; expected " + getSize() + " or less"); + } + setContents(items[0], Arrays.copyOfRange(items, 1, items.length)); + } + + @Override + public ItemStack[] getContents() { + ItemStack[] items = new ItemStack[getSize()]; + List mcResultItems = getResultInventory().getContents(); + + int i = 0; + for (i = 0; i < mcResultItems.size(); i++ ) { + items[i] = CraftItemStack.asCraftMirror(mcResultItems.get(i)); + } + + List mcItems = getMatrixInventory().getContents(); + + for (int j = 0; j < mcItems.size(); j++) { + items[i + j] = CraftItemStack.asCraftMirror(mcItems.get(j)); + } + + return items; + } + + public void setContents(ItemStack result, ItemStack[] contents) { + setResult(result); + setMatrix(contents); + } + + @Override + public CraftItemStack getItem(int index) { + if (index < getResultInventory().getSize()) { + net.minecraft.server.ItemStack item = getResultInventory().getItem(index); + return item.isEmpty() ? null : CraftItemStack.asCraftMirror(item); + } else { + net.minecraft.server.ItemStack item = getMatrixInventory().getItem(index - getResultInventory().getSize()); + return item.isEmpty() ? null : CraftItemStack.asCraftMirror(item); + } + } + + @Override + public void setItem(int index, ItemStack item) { + if (index < getResultInventory().getSize()) { + getResultInventory().setItem(index, CraftItemStack.asNMSCopy(item)); + } else { + getMatrixInventory().setItem((index - getResultInventory().getSize()), CraftItemStack.asNMSCopy(item)); + } + } + + public ItemStack[] getMatrix() { + List matrix = getMatrixInventory().getContents(); + + return asCraftMirror(matrix); + } + + public ItemStack getResult() { + net.minecraft.server.ItemStack item = getResultInventory().getItem(0); + if (!item.isEmpty()) return CraftItemStack.asCraftMirror(item); + return null; + } + + public void setMatrix(ItemStack[] contents) { + if (getMatrixInventory().getSize() > contents.length) { + throw new IllegalArgumentException("Invalid inventory size; expected " + getMatrixInventory().getSize() + " or less"); + } + + for (int i = 0; i < getMatrixInventory().getSize(); i++) { + if (i < contents.length) { + getMatrixInventory().setItem(i, CraftItemStack.asNMSCopy(contents[i])); + } else { + getMatrixInventory().setItem(i, net.minecraft.server.ItemStack.a); + } + } + } + + public void setResult(ItemStack item) { + List contents = getResultInventory().getContents(); + contents.set(0, CraftItemStack.asNMSCopy(item)); + } + + public Recipe getRecipe() { + IRecipe recipe = getInventory().getCurrentRecipe(); + return recipe == null ? null : recipe.toBukkitRecipe(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java new file mode 100644 index 000000000000..6e17b4fd9033 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryCustom.java @@ -0,0 +1,227 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import net.minecraft.server.ChatComponentText; + +import net.minecraft.server.IChatBaseComponent; +import org.apache.commons.lang.Validate; +import org.bukkit.Location; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.InventoryHolder; + +import net.minecraft.server.EntityHuman; +import net.minecraft.server.IInventory; +import net.minecraft.server.ItemStack; +import net.minecraft.server.NonNullList; +import org.bukkit.craftbukkit.util.CraftChatMessage; + +public class CraftInventoryCustom extends CraftInventory { + public CraftInventoryCustom(InventoryHolder owner, InventoryType type) { + super(new MinecraftInventory(owner, type)); + } + + public CraftInventoryCustom(InventoryHolder owner, InventoryType type, String title) { + super(new MinecraftInventory(owner, type, title)); + } + + public CraftInventoryCustom(InventoryHolder owner, int size) { + super(new MinecraftInventory(owner, size)); + } + + public CraftInventoryCustom(InventoryHolder owner, int size, String title) { + super(new MinecraftInventory(owner, size, title)); + } + + static class MinecraftInventory implements IInventory { + private final NonNullList items; + private int maxStack = MAX_STACK; + private final List viewers; + private final IChatBaseComponent title; + private InventoryType type; + private final InventoryHolder owner; + + public MinecraftInventory(InventoryHolder owner, InventoryType type) { + this(owner, type.getDefaultSize(), type.getDefaultTitle()); + this.type = type; + } + + public MinecraftInventory(InventoryHolder owner, InventoryType type, String title) { + this(owner, type.getDefaultSize(), title); + this.type = type; + } + + public MinecraftInventory(InventoryHolder owner, int size) { + this(owner, size, "Chest"); + } + + public MinecraftInventory(InventoryHolder owner, int size, String title) { + Validate.notNull(title, "Title cannot be null"); + this.items = NonNullList.a(size, ItemStack.a); + this.title = CraftChatMessage.fromString(title)[0]; + this.viewers = new ArrayList(); + this.owner = owner; + this.type = InventoryType.CHEST; + } + + public int getSize() { + return items.size(); + } + + public ItemStack getItem(int i) { + return items.get(i); + } + + public ItemStack splitStack(int i, int j) { + ItemStack stack = this.getItem(i); + ItemStack result; + if (stack == ItemStack.a) return stack; + if (stack.getCount() <= j) { + this.setItem(i, ItemStack.a); + result = stack; + } else { + result = CraftItemStack.copyNMSStack(stack, j); + stack.subtract(j); + } + this.update(); + return result; + } + + public ItemStack splitWithoutUpdate(int i) { + ItemStack stack = this.getItem(i); + ItemStack result; + if (stack == ItemStack.a) return stack; + if (stack.getCount() <= 1) { + this.setItem(i, null); + result = stack; + } else { + result = CraftItemStack.copyNMSStack(stack, 1); + stack.subtract(1); + } + return result; + } + + public void setItem(int i, ItemStack itemstack) { + items.set(i, itemstack); + if (itemstack != ItemStack.a && this.getMaxStackSize() > 0 && itemstack.getCount() > this.getMaxStackSize()) { + itemstack.setCount(this.getMaxStackSize()); + } + } + + public int getMaxStackSize() { + return maxStack; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + + public void update() {} + + public boolean a(EntityHuman entityhuman) { + return true; + } + + public List getContents() { + return items; + } + + public void onOpen(CraftHumanEntity who) { + viewers.add(who); + } + + public void onClose(CraftHumanEntity who) { + viewers.remove(who); + } + + public List getViewers() { + return viewers; + } + + public InventoryType getType() { + return type; + } + + public InventoryHolder getOwner() { + return owner; + } + + public boolean b(int i, ItemStack itemstack) { + return true; + } + + @Override + public void startOpen(EntityHuman entityHuman) { + + } + + @Override + public void closeContainer(EntityHuman entityHuman) { + + } + + @Override + public int getProperty(int i) { + return 0; + } + + @Override + public void setProperty(int i, int j) { + } + + @Override + public int h() { + return 0; + } + + @Override + public void clear() { + items.clear(); + } + + @Override + public IChatBaseComponent getDisplayName() { + return title; + } + + @Override + public IChatBaseComponent getCustomName() { + return title; + } + + @Override + public boolean hasCustomName() { + return title != null; + } + + @Override + public IChatBaseComponent getScoreboardDisplayName() { + return title; + } + + @Override + public Location getLocation() { + return null; + } + + @Override + public boolean P_() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java new file mode 100644 index 000000000000..799f8ea5d271 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java @@ -0,0 +1,68 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.ChatMessage; +import net.minecraft.server.ITileInventory; +import org.bukkit.block.DoubleChest; +import org.bukkit.inventory.DoubleChestInventory; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import net.minecraft.server.InventoryLargeChest; +import org.bukkit.Location; + +public class CraftInventoryDoubleChest extends CraftInventory implements DoubleChestInventory { + private final CraftInventory left; + private final CraftInventory right; + + public CraftInventoryDoubleChest(CraftInventory left, CraftInventory right) { + super(new InventoryLargeChest(new ChatMessage("container.chestDouble"), (ITileInventory) left.getInventory(), (ITileInventory) right.getInventory())); + this.left = left; + this.right = right; + } + + public CraftInventoryDoubleChest(InventoryLargeChest largeChest) { + super(largeChest); + if (largeChest.left instanceof InventoryLargeChest) { + left = new CraftInventoryDoubleChest((InventoryLargeChest) largeChest.left); + } else { + left = new CraftInventory(largeChest.left); + } + if (largeChest.right instanceof InventoryLargeChest) { + right = new CraftInventoryDoubleChest((InventoryLargeChest) largeChest.right); + } else { + right = new CraftInventory(largeChest.right); + } + } + + public Inventory getLeftSide() { + return left; + } + + public Inventory getRightSide() { + return right; + } + + @Override + public void setContents(ItemStack[] items) { + if (getInventory().getSize() < items.length) { + throw new IllegalArgumentException("Invalid inventory size; expected " + getInventory().getSize() + " or less"); + } + ItemStack[] leftItems = new ItemStack[left.getSize()], rightItems = new ItemStack[right.getSize()]; + System.arraycopy(items, 0, leftItems, 0, Math.min(left.getSize(), items.length)); + left.setContents(leftItems); + if (items.length >= left.getSize()) { + System.arraycopy(items, left.getSize(), rightItems, 0, Math.min(right.getSize(), items.length - left.getSize())); + right.setContents(rightItems); + } + } + + @Override + public DoubleChest getHolder() { + return new DoubleChest(this); + } + + @Override + public Location getLocation() { + return getLeftSide().getLocation().add(getRightSide().getLocation()).multiply(0.5); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryEnchanting.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryEnchanting.java new file mode 100644 index 000000000000..a61405fe5349 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryEnchanting.java @@ -0,0 +1,31 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.IInventory; +import org.bukkit.inventory.EnchantingInventory; +import org.bukkit.inventory.ItemStack; + +public class CraftInventoryEnchanting extends CraftInventory implements EnchantingInventory { + public CraftInventoryEnchanting(IInventory inventory) { + super(inventory); + } + + @Override + public void setItem(ItemStack item) { + setItem(0,item); + } + + @Override + public ItemStack getItem() { + return getItem(0); + } + + @Override + public void setSecondary(ItemStack item) { + setItem(1, item); + } + + @Override + public ItemStack getSecondary() { + return getItem(1); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryFurnace.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryFurnace.java new file mode 100644 index 000000000000..37a631ebe4cf --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryFurnace.java @@ -0,0 +1,42 @@ +package org.bukkit.craftbukkit.inventory; + +import org.bukkit.block.Furnace; +import org.bukkit.inventory.FurnaceInventory; +import org.bukkit.inventory.ItemStack; + +import net.minecraft.server.TileEntityFurnace; + +public class CraftInventoryFurnace extends CraftInventory implements FurnaceInventory { + public CraftInventoryFurnace(TileEntityFurnace inventory) { + super(inventory); + } + + public ItemStack getResult() { + return getItem(2); + } + + public ItemStack getFuel() { + return getItem(1); + } + + public ItemStack getSmelting() { + return getItem(0); + } + + public void setFuel(ItemStack stack) { + setItem(1,stack); + } + + public void setResult(ItemStack stack) { + setItem(2,stack); + } + + public void setSmelting(ItemStack stack) { + setItem(0,stack); + } + + @Override + public Furnace getHolder() { + return (Furnace) inventory.getOwner(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryHorse.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryHorse.java new file mode 100644 index 000000000000..2f6852404976 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryHorse.java @@ -0,0 +1,20 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.IInventory; +import org.bukkit.inventory.HorseInventory; +import org.bukkit.inventory.ItemStack; + +public class CraftInventoryHorse extends CraftSaddledInventory implements HorseInventory { + + public CraftInventoryHorse(IInventory inventory) { + super(inventory); + } + + public ItemStack getArmor() { + return getItem(1); + } + + public void setArmor(ItemStack stack) { + setItem(1, stack); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryLlama.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryLlama.java new file mode 100644 index 000000000000..c910f5e9c5e6 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryLlama.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.IInventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.LlamaInventory; + +public class CraftInventoryLlama extends CraftInventoryAbstractHorse implements LlamaInventory { + + public CraftInventoryLlama(IInventory inventory) { + super(inventory); + } + + @Override + public ItemStack getDecor() { + return getItem(1); + } + + @Override + public void setDecor(ItemStack stack) { + setItem(1, stack); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryMerchant.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryMerchant.java new file mode 100644 index 000000000000..10b125300ccf --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryMerchant.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.InventoryMerchant; +import org.bukkit.inventory.MerchantInventory; +import org.bukkit.inventory.MerchantRecipe; + +public class CraftInventoryMerchant extends CraftInventory implements MerchantInventory { + + public CraftInventoryMerchant(InventoryMerchant merchant) { + super(merchant); + } + + @Override + public int getSelectedRecipeIndex() { + return getInventory().selectedIndex; + } + + @Override + public MerchantRecipe getSelectedRecipe() { + net.minecraft.server.MerchantRecipe nmsRecipe = getInventory().getRecipe(); + return (nmsRecipe == null) ? null : nmsRecipe.asBukkit(); + } + + @Override + public InventoryMerchant getInventory() { + return (InventoryMerchant) inventory; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java new file mode 100644 index 000000000000..60446f247809 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java @@ -0,0 +1,303 @@ +package org.bukkit.craftbukkit.inventory; + +import com.google.common.base.Preconditions; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.PacketPlayOutHeldItemSlot; +import net.minecraft.server.PacketPlayOutSetSlot; +import net.minecraft.server.PlayerInventory; + +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; + +public class CraftInventoryPlayer extends CraftInventory implements org.bukkit.inventory.PlayerInventory, EntityEquipment { + public CraftInventoryPlayer(net.minecraft.server.PlayerInventory inventory) { + super(inventory); + } + + @Override + public PlayerInventory getInventory() { + return (PlayerInventory) inventory; + } + + @Override + public ItemStack[] getStorageContents() { + return asCraftMirror(getInventory().items); + } + + @Override + public ItemStack getItemInMainHand() { + return CraftItemStack.asCraftMirror(getInventory().getItemInHand()); + } + + @Override + public void setItemInMainHand(ItemStack item) { + setItem(getHeldItemSlot(), item); + } + + @Override + public ItemStack getItemInOffHand() { + return CraftItemStack.asCraftMirror(getInventory().extraSlots.get(0)); + } + + @Override + public void setItemInOffHand(ItemStack item) { + ItemStack[] extra = getExtraContents(); + extra[0] = item; + setExtraContents(extra); + } + + @Override + public ItemStack getItemInHand() { + return getItemInMainHand(); + } + + @Override + public void setItemInHand(ItemStack stack) { + setItemInMainHand(stack); + } + + @Override + public void setItem(int index, ItemStack item) { + super.setItem(index, item); + if (this.getHolder() == null) return; + EntityPlayer player = ((CraftPlayer) this.getHolder()).getHandle(); + if (player.playerConnection == null) return; + // PacketPlayOutSetSlot places the items differently than setItem() + // + // Between, and including, index 9 (the first index outside of the hotbar) and index 35 (the last index before + // armor slots) both PacketPlayOutSetSlot and setItem() places the items in the player's inventory the same way. + // Index 9 starts at the upper left corner of the inventory and moves to the right as it increases. When it + // reaches the end of the line it goes back to the left side of the new line in the inventory. Basically, it + // follows the path your eyes would follow as you read a book. + // + // The player's hotbar is indexed 0-8 in setItem(). The order goes: 0-8 hotbar, 9-35 normal inventory, 36 boots, + // 37 leggings, 38 chestplate, and 39 helmet. For indexes > 39 an ArrayIndexOutOfBoundsException will be thrown. + // + // PacketPlayOutSetSlot works very differently. Slots 0-8 are as follows: 0 crafting output, 1-4 crafting input, + // 5 helmet, 6 chestplate, 7 leggings, and 8 boots. Then, 9-35 work exactly the same as setItem(). The hotbar + // for PacketPlayOutSetSlot starts at index 36, and continues to index 44. Items placed where index is < 0 or + // > 44 have no action. Basically, the upper part of the player's inventory (crafting area and armor slots) is + // the first "row" of 9 slots for PacketPlayOutSetSlot. From there the rows work as normal, from left to right + // all the way down, including the hotbar. + // + // With this in mind, we have to modify the index we give PacketPlayOutSetSlot to match the index we intended + // with setItem(). First, if the index is 0-8, we need to add 36, or 4 rows worth of slots, to the index. This + // will push the item down to the correct spot in the hotbar. + // + // Now when index is > 35 (if index > 39 an ArrayIndexOutOfBoundsException will be thrown, so we need not worry + // about it) then we need to reset the index, and then count backwards from the "top" of the inventory. That is + // to say, we first find (index - 36), which will give us the index required for the armor slots. Now, we need + // to reverse the order of the index from 8. That means we need 0 to correspond to 8, 1 to correspond to 7, + // 2 to correspond to 6, and 3 to correspond to 5. We do this simply by taking the result of (index - 36) and + // subtracting that value from 8. + if (index < PlayerInventory.getHotbarSize()) { + index += 36; + } else if (index > 39) { + index += 5; // Off hand + } else if (index > 35) { + index = 8 - (index - 36); + } + player.playerConnection.sendPacket(new PacketPlayOutSetSlot(player.defaultContainer.windowId, index, CraftItemStack.asNMSCopy(item))); + } + + public int getHeldItemSlot() { + return getInventory().itemInHandIndex; + } + + public void setHeldItemSlot(int slot) { + Validate.isTrue(slot >= 0 && slot < PlayerInventory.getHotbarSize(), "Slot is not between 0 and 8 inclusive"); + this.getInventory().itemInHandIndex = slot; + ((CraftPlayer) this.getHolder()).getHandle().playerConnection.sendPacket(new PacketPlayOutHeldItemSlot(slot)); + } + + public ItemStack getHelmet() { + return getItem(getSize() - 2); + } + + public ItemStack getChestplate() { + return getItem(getSize() - 3); + } + + public ItemStack getLeggings() { + return getItem(getSize() - 4); + } + + public ItemStack getBoots() { + return getItem(getSize() - 5); + } + + public void setHelmet(ItemStack helmet) { + setItem(getSize() - 2, helmet); + } + + public void setChestplate(ItemStack chestplate) { + setItem(getSize() - 3, chestplate); + } + + public void setLeggings(ItemStack leggings) { + setItem(getSize() - 4, leggings); + } + + public void setBoots(ItemStack boots) { + setItem(getSize() - 5, boots); + } + + public ItemStack[] getArmorContents() { + return asCraftMirror(getInventory().armor); + } + + private void setSlots(ItemStack[] items, int baseSlot, int length) { + if (items == null) { + items = new ItemStack[length]; + } + Preconditions.checkArgument(items.length <= length, "items.length must be < %s", length); + + for (int i = 0; i < length; i++) { + if (i >= items.length) { + setItem(baseSlot + i, null); + } else { + setItem(baseSlot + i, items[i]); + } + } + } + + @Override + public void setStorageContents(ItemStack[] items) throws IllegalArgumentException { + setSlots(items, 0, getInventory().items.size()); + } + + @Override + public void setArmorContents(ItemStack[] items) { + setSlots(items, getInventory().items.size(), getInventory().armor.size()); + } + + @Override + public ItemStack[] getExtraContents() { + return asCraftMirror(getInventory().extraSlots); + } + + @Override + public void setExtraContents(ItemStack[] items) { + setSlots(items, getInventory().items.size() + getInventory().armor.size(), getInventory().extraSlots.size()); + } + + @Override + public HumanEntity getHolder() { + return (HumanEntity) inventory.getOwner(); + } + + @Override + public float getItemInHandDropChance() { + return getItemInMainHandDropChance(); + } + + @Override + public void setItemInHandDropChance(float chance) { + setItemInMainHandDropChance(chance); + } + + @Override + public float getItemInMainHandDropChance() { + return 1; + } + + @Override + public void setItemInMainHandDropChance(float chance) { + throw new UnsupportedOperationException("Cannot set drop chance for PlayerInventory"); + } + + @Override + public float getItemInOffHandDropChance() { + return 1; + } + + @Override + public void setItemInOffHandDropChance(float chance) { + throw new UnsupportedOperationException("Cannot set drop chance for PlayerInventory"); + } + + public float getHelmetDropChance() { + return 1; + } + + public void setHelmetDropChance(float chance) { + throw new UnsupportedOperationException("Cannot set drop chance for PlayerInventory"); + } + + public float getChestplateDropChance() { + return 1; + } + + public void setChestplateDropChance(float chance) { + throw new UnsupportedOperationException("Cannot set drop chance for PlayerInventory"); + } + + public float getLeggingsDropChance() { + return 1; + } + + public void setLeggingsDropChance(float chance) { + throw new UnsupportedOperationException("Cannot set drop chance for PlayerInventory"); + } + + public float getBootsDropChance() { + return 1; + } + + public void setBootsDropChance(float chance) { + throw new UnsupportedOperationException("Cannot set drop chance for PlayerInventory"); + } + + // Paper start + @Override + public ItemStack getItem(org.bukkit.inventory.EquipmentSlot slot) { + Preconditions.checkNotNull(slot, "slot"); + switch (slot) { + case HAND: + return this.getItemInMainHand(); + case OFF_HAND: + return this.getItemInOffHand(); + case HEAD: + return this.getHelmet(); + case CHEST: + return this.getChestplate(); + case LEGS: + return this.getLeggings(); + case FEET: + return this.getBoots(); + } + + throw new UnsupportedOperationException(slot.name()); + } + + @Override + public void setItem(org.bukkit.inventory.EquipmentSlot slot, ItemStack stack) { + Preconditions.checkNotNull(slot, "slot"); + switch (slot) { + case HAND: + this.setItemInMainHand(stack); + return; + case OFF_HAND: + this.setItemInOffHand(stack); + return; + case HEAD: + this.setHelmet(stack); + return; + case CHEST: + this.setChestplate(stack); + return; + case LEGS: + this.setLeggings(stack); + return; + case FEET: + this.setBoots(stack); + return; + } + + throw new UnsupportedOperationException(slot.name()); + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryView.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryView.java new file mode 100644 index 000000000000..ac23350917c1 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryView.java @@ -0,0 +1,75 @@ +package org.bukkit.craftbukkit.inventory; + +import org.bukkit.GameMode; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.inventory.InventoryType.SlotType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; + +import net.minecraft.server.Container; + +public class CraftInventoryView extends InventoryView { + private final Container container; + private final CraftHumanEntity player; + private final CraftInventory viewing; + + public CraftInventoryView(HumanEntity player, Inventory viewing, Container container) { + // TODO: Should we make sure it really IS a CraftHumanEntity first? And a CraftInventory? + this.player = (CraftHumanEntity) player; + this.viewing = (CraftInventory) viewing; + this.container = container; + } + + @Override + public Inventory getTopInventory() { + return viewing; + } + + @Override + public Inventory getBottomInventory() { + return player.getInventory(); + } + + @Override + public HumanEntity getPlayer() { + return player; + } + + @Override + public InventoryType getType() { + InventoryType type = viewing.getType(); + if (type == InventoryType.CRAFTING && player.getGameMode() == GameMode.CREATIVE) { + return InventoryType.CREATIVE; + } + return type; + } + + @Override + public void setItem(int slot, ItemStack item) { + net.minecraft.server.ItemStack stack = CraftItemStack.asNMSCopy(item); + if (slot >= 0) { + container.getSlot(slot).set(stack); + } else { + player.getHandle().drop(stack, false); + } + } + + @Override + public ItemStack getItem(int slot) { + if (slot < 0) { + return null; + } + return CraftItemStack.asCraftMirror(container.getSlot(slot).getItem()); + } + + public boolean isInTop(int rawSlot) { + return rawSlot < viewing.getSize(); + } + + public Container getHandle() { + return container; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java new file mode 100644 index 000000000000..6a86cb7eb45a --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java @@ -0,0 +1,318 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Collection; + +import org.apache.commons.lang.Validate; +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.ConfigurationSerialization; +import org.bukkit.craftbukkit.util.CraftLegacy; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import com.google.common.collect.ImmutableSet; + +public final class CraftItemFactory implements ItemFactory { + static final Color DEFAULT_LEATHER_COLOR = Color.fromRGB(0xA06540); + static final Collection KNOWN_NBT_ATTRIBUTE_NAMES; + private static final CraftItemFactory instance; + + static { + instance = new CraftItemFactory(); + ConfigurationSerialization.registerClass(CraftMetaItem.SerializableMeta.class); + KNOWN_NBT_ATTRIBUTE_NAMES = ImmutableSet.builder() + .add("generic.armor") + .add("generic.armorToughness") + .add("generic.attackDamage") + .add("generic.followRange") + .add("generic.knockbackResistance") + .add("generic.maxHealth") + .add("generic.movementSpeed") + .add("generic.flyingSpeed") + .add("generic.attackSpeed") + .add("generic.luck") + .add("horse.jumpStrength") + .add("zombie.spawnReinforcements") + .build(); + } + + private CraftItemFactory() { + } + + public boolean isApplicable(ItemMeta meta, ItemStack itemstack) { + if (itemstack == null) { + return false; + } + return isApplicable(meta, itemstack.getType()); + } + + public boolean isApplicable(ItemMeta meta, Material type) { + type = CraftLegacy.fromLegacy(type); // This may be called from legacy item stacks, try to get the right material + if (type == null || meta == null) { + return false; + } + if (!(meta instanceof CraftMetaItem)) { + throw new IllegalArgumentException("Meta of " + meta.getClass().toString() + " not created by " + CraftItemFactory.class.getName()); + } + + return ((CraftMetaItem) meta).applicableTo(type); + } + + public ItemMeta getItemMeta(Material material) { + Validate.notNull(material, "Material cannot be null"); + return getItemMeta(material, null); + } + + private ItemMeta getItemMeta(Material material, CraftMetaItem meta) { + material = CraftLegacy.fromLegacy(material); // This may be called from legacy item stacks, try to get the right material + switch (material) { + case AIR: + return null; + case WRITTEN_BOOK: + return meta instanceof CraftMetaBookSigned ? meta : new CraftMetaBookSigned(meta); + case WRITABLE_BOOK: + return meta != null && meta.getClass().equals(CraftMetaBook.class) ? meta : new CraftMetaBook(meta); + case CREEPER_HEAD: + case CREEPER_WALL_HEAD: + case DRAGON_HEAD: + case DRAGON_WALL_HEAD: + case PLAYER_HEAD: + case PLAYER_WALL_HEAD: + case SKELETON_SKULL: + case SKELETON_WALL_SKULL: + case WITHER_SKELETON_SKULL: + case WITHER_SKELETON_WALL_SKULL: + case ZOMBIE_HEAD: + case ZOMBIE_WALL_HEAD: + return meta instanceof CraftMetaSkull ? meta : new CraftMetaSkull(meta); + case LEATHER_HELMET: + case LEATHER_CHESTPLATE: + case LEATHER_LEGGINGS: + case LEATHER_BOOTS: + return meta instanceof CraftMetaLeatherArmor ? meta : new CraftMetaLeatherArmor(meta); + case POTION: + case SPLASH_POTION: + case LINGERING_POTION: + case TIPPED_ARROW: + return meta instanceof CraftMetaPotion ? meta : new CraftMetaPotion(meta); + case FILLED_MAP: + return meta instanceof CraftMetaMap ? meta : new CraftMetaMap(meta); + case FIREWORK_ROCKET: + return meta instanceof CraftMetaFirework ? meta : new CraftMetaFirework(meta); + case FIREWORK_STAR: + return meta instanceof CraftMetaCharge ? meta : new CraftMetaCharge(meta); + case ENCHANTED_BOOK: + return meta instanceof CraftMetaEnchantedBook ? meta : new CraftMetaEnchantedBook(meta); + case BLACK_BANNER: + case BLACK_WALL_BANNER: + case BLUE_BANNER: + case BLUE_WALL_BANNER: + case BROWN_BANNER: + case BROWN_WALL_BANNER: + case CYAN_BANNER: + case CYAN_WALL_BANNER: + case GRAY_BANNER: + case GRAY_WALL_BANNER: + case GREEN_BANNER: + case GREEN_WALL_BANNER: + case LIGHT_BLUE_BANNER: + case LIGHT_BLUE_WALL_BANNER: + case LIGHT_GRAY_BANNER: + case LIGHT_GRAY_WALL_BANNER: + case LIME_BANNER: + case LIME_WALL_BANNER: + case MAGENTA_BANNER: + case MAGENTA_WALL_BANNER: + case ORANGE_BANNER: + case ORANGE_WALL_BANNER: + case PINK_BANNER: + case PINK_WALL_BANNER: + case PURPLE_BANNER: + case PURPLE_WALL_BANNER: + case RED_BANNER: + case RED_WALL_BANNER: + case WHITE_BANNER: + case WHITE_WALL_BANNER: + case YELLOW_BANNER: + case YELLOW_WALL_BANNER: + return meta instanceof CraftMetaBanner ? meta : new CraftMetaBanner(meta); + case BAT_SPAWN_EGG: + case BLAZE_SPAWN_EGG: + case CAVE_SPIDER_SPAWN_EGG: + case CHICKEN_SPAWN_EGG: + case COD_SPAWN_EGG: + case COW_SPAWN_EGG: + case CREEPER_SPAWN_EGG: + case DOLPHIN_SPAWN_EGG: + case DROWNED_SPAWN_EGG: + case DONKEY_SPAWN_EGG: + case ELDER_GUARDIAN_SPAWN_EGG: + case ENDERMAN_SPAWN_EGG: + case ENDERMITE_SPAWN_EGG: + case EVOKER_SPAWN_EGG: + case GHAST_SPAWN_EGG: + case GUARDIAN_SPAWN_EGG: + case HORSE_SPAWN_EGG: + case HUSK_SPAWN_EGG: + case LLAMA_SPAWN_EGG: + case MAGMA_CUBE_SPAWN_EGG: + case MOOSHROOM_SPAWN_EGG: + case MULE_SPAWN_EGG: + case OCELOT_SPAWN_EGG: + case PARROT_SPAWN_EGG: + case PHANTOM_SPAWN_EGG: + case PIG_SPAWN_EGG: + case POLAR_BEAR_SPAWN_EGG: + case PUFFERFISH_SPAWN_EGG: + case RABBIT_SPAWN_EGG: + case SALMON_SPAWN_EGG: + case SHEEP_SPAWN_EGG: + case SHULKER_SPAWN_EGG: + case SILVERFISH_SPAWN_EGG: + case SKELETON_HORSE_SPAWN_EGG: + case SKELETON_SPAWN_EGG: + case SLIME_SPAWN_EGG: + case SPIDER_SPAWN_EGG: + case SQUID_SPAWN_EGG: + case STRAY_SPAWN_EGG: + case TROPICAL_FISH_SPAWN_EGG: + case TURTLE_SPAWN_EGG: + case VEX_SPAWN_EGG: + case VILLAGER_SPAWN_EGG: + case VINDICATOR_SPAWN_EGG: + case WITCH_SPAWN_EGG: + case WITHER_SKELETON_SPAWN_EGG: + case WOLF_SPAWN_EGG: + case ZOMBIE_HORSE_SPAWN_EGG: + case ZOMBIE_PIGMAN_SPAWN_EGG: + case ZOMBIE_SPAWN_EGG: + case ZOMBIE_VILLAGER_SPAWN_EGG: + return meta instanceof CraftMetaSpawnEgg ? meta : new CraftMetaSpawnEgg(meta); + case KNOWLEDGE_BOOK: + return meta instanceof CraftMetaKnowledgeBook ? meta : new CraftMetaKnowledgeBook(meta); + case ARMOR_STAND: + return meta instanceof CraftMetaArmorStand ? meta : new CraftMetaArmorStand(meta); // Paper + case FURNACE: + case CHEST: + case TRAPPED_CHEST: + case JUKEBOX: + case DISPENSER: + case DROPPER: + case SIGN: + case SPAWNER: + case BREWING_STAND: + case ENCHANTING_TABLE: + case COMMAND_BLOCK: + case REPEATING_COMMAND_BLOCK: + case CHAIN_COMMAND_BLOCK: + case BEACON: + case DAYLIGHT_DETECTOR: + case HOPPER: + case COMPARATOR: + case SHIELD: + case STRUCTURE_BLOCK: + case SHULKER_BOX: + case WHITE_SHULKER_BOX: + case ORANGE_SHULKER_BOX: + case MAGENTA_SHULKER_BOX: + case LIGHT_BLUE_SHULKER_BOX: + case YELLOW_SHULKER_BOX: + case LIME_SHULKER_BOX: + case PINK_SHULKER_BOX: + case GRAY_SHULKER_BOX: + case LIGHT_GRAY_SHULKER_BOX: + case CYAN_SHULKER_BOX: + case PURPLE_SHULKER_BOX: + case BLUE_SHULKER_BOX: + case BROWN_SHULKER_BOX: + case GREEN_SHULKER_BOX: + case RED_SHULKER_BOX: + case BLACK_SHULKER_BOX: + case ENDER_CHEST: + return new CraftMetaBlockState(meta, material); + case TROPICAL_FISH_BUCKET: + return meta instanceof CraftMetaTropicalFishBucket ? meta : new CraftMetaTropicalFishBucket(meta); + default: + return new CraftMetaItem(meta); + } + } + + public boolean equals(ItemMeta meta1, ItemMeta meta2) { + if (meta1 == meta2) { + return true; + } + if (meta1 != null && !(meta1 instanceof CraftMetaItem)) { + throw new IllegalArgumentException("First meta of " + meta1.getClass().getName() + " does not belong to " + CraftItemFactory.class.getName()); + } + if (meta2 != null && !(meta2 instanceof CraftMetaItem)) { + throw new IllegalArgumentException("Second meta " + meta2.getClass().getName() + " does not belong to " + CraftItemFactory.class.getName()); + } + if (meta1 == null) { + return ((CraftMetaItem) meta2).isEmpty(); + } + if (meta2 == null) { + return ((CraftMetaItem) meta1).isEmpty(); + } + + return equals((CraftMetaItem) meta1, (CraftMetaItem) meta2); + } + + boolean equals(CraftMetaItem meta1, CraftMetaItem meta2) { + /* + * This couldn't be done inside of the objects themselves, else force recursion. + * This is a fairly clean way of implementing it, by dividing the methods into purposes and letting each method perform its own function. + * + * The common and uncommon were split, as both could have variables not applicable to the other, like a skull and book. + * Each object needs its chance to say "hey wait a minute, we're not equal," but without the redundancy of using the 1.equals(2) && 2.equals(1) checking the 'commons' twice. + * + * Doing it this way fills all conditions of the .equals() method. + */ + return meta1.equalsCommon(meta2) && meta1.notUncommon(meta2) && meta2.notUncommon(meta1); + } + + public static CraftItemFactory instance() { + return instance; + } + + public ItemMeta asMetaFor(ItemMeta meta, ItemStack stack) { + Validate.notNull(stack, "Stack cannot be null"); + return asMetaFor(meta, stack.getType()); + } + + public ItemMeta asMetaFor(ItemMeta meta, Material material) { + Validate.notNull(material, "Material cannot be null"); + if (!(meta instanceof CraftMetaItem)) { + throw new IllegalArgumentException("Meta of " + (meta != null ? meta.getClass().toString() : "null") + " not created by " + CraftItemFactory.class.getName()); + } + return getItemMeta(material, (CraftMetaItem) meta); + } + + public Color getDefaultLeatherColor() { + return DEFAULT_LEATHER_COLOR; + } + + @Override + public Material updateMaterial(ItemMeta meta, Material material) throws IllegalArgumentException { + return ((CraftMetaItem) meta).updateMaterial(material); + } + // Paper start + @Override + public ItemStack ensureServerConversions(ItemStack item) { + return CraftItemStack.asCraftMirror(CraftItemStack.asNMSCopy(item)); + } + + @Override + public String getI18NDisplayName(ItemStack item) { + net.minecraft.server.ItemStack nms = null; + if (item instanceof CraftItemStack) { + nms = ((CraftItemStack) item).handle; + } + if (nms == null) { + nms = CraftItemStack.asNMSCopy(item); + } + + return nms != null ? net.minecraft.server.LocaleLanguage.getInstance().translateKey(nms.getItem().getName()) : null; + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java new file mode 100644 index 000000000000..f41ccba79025 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java @@ -0,0 +1,533 @@ +package org.bukkit.craftbukkit.inventory; + +import static org.bukkit.craftbukkit.inventory.CraftMetaItem.ENCHANTMENTS; +import static org.bukkit.craftbukkit.inventory.CraftMetaItem.ENCHANTMENTS_ID; +import static org.bukkit.craftbukkit.inventory.CraftMetaItem.ENCHANTMENTS_LVL; + +import java.util.Iterator; +import java.util.Map; + +import net.minecraft.server.EnchantmentManager; +import net.minecraft.server.Item; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagList; + +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.material.MaterialData; + +import com.google.common.collect.ImmutableMap; +import org.bukkit.craftbukkit.enchantments.CraftEnchantment; +import org.bukkit.craftbukkit.util.CraftLegacy; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; + +@DelegateDeserialization(ItemStack.class) +public final class CraftItemStack extends ItemStack { + + public static net.minecraft.server.ItemStack asNMSCopy(ItemStack original) { + if (original instanceof CraftItemStack) { + CraftItemStack stack = (CraftItemStack) original; + return stack.handle == null ? net.minecraft.server.ItemStack.a : stack.handle.cloneItemStack(); + } + if (original == null || original.getType() == Material.AIR) { + return net.minecraft.server.ItemStack.a; + } + + Item item = CraftMagicNumbers.getItem(original.getType(), original.getDurability()); + + if (item == null) { + return net.minecraft.server.ItemStack.a; + } + + net.minecraft.server.ItemStack stack = new net.minecraft.server.ItemStack(item, original.getAmount()); + if (original.hasItemMeta()) { + setItemMeta(stack, original.getItemMeta()); + } else { + // Converted after setItemMeta + stack.convertStack(); + } + return stack; + } + + public static net.minecraft.server.ItemStack copyNMSStack(net.minecraft.server.ItemStack original, int amount) { + net.minecraft.server.ItemStack stack = original.cloneItemStack(); + stack.setCount(amount); + return stack; + } + + /** + * Copies the NMS stack to return as a strictly-Bukkit stack + */ + public static ItemStack asBukkitCopy(net.minecraft.server.ItemStack original) { + if (original.isEmpty()) { + return new ItemStack(Material.AIR); + } + ItemStack stack = new ItemStack(CraftMagicNumbers.getMaterial(original.getItem()), original.getCount()); + if (hasItemMeta(original)) { + stack.setItemMeta(getItemMeta(original)); + } + return stack; + } + + public static CraftItemStack asCraftMirror(net.minecraft.server.ItemStack original) { + return new CraftItemStack((original == null || original.isEmpty()) ? null : original); + } + + public static CraftItemStack asCraftCopy(ItemStack original) { + if (original instanceof CraftItemStack) { + CraftItemStack stack = (CraftItemStack) original; + return new CraftItemStack(stack.handle == null ? null : stack.handle.cloneItemStack()); + } + return new CraftItemStack(original); + } + + public static CraftItemStack asNewCraftStack(Item item) { + return asNewCraftStack(item, 1); + } + + public static CraftItemStack asNewCraftStack(Item item, int amount) { + return new CraftItemStack(CraftMagicNumbers.getMaterial(item), amount, (short) 0, null); + } + + net.minecraft.server.ItemStack handle; + + /** + * Mirror + */ + private CraftItemStack(net.minecraft.server.ItemStack item) { + this.handle = item; + } + + private CraftItemStack(ItemStack item) { + this(item.getType(), item.getAmount(), item.getDurability(), item.hasItemMeta() ? item.getItemMeta() : null); + } + + private CraftItemStack(Material type, int amount, short durability, ItemMeta itemMeta) { + setType(type); + setAmount(amount); + setDurability(durability); + setItemMeta(itemMeta); + } + + @Override + public MaterialData getData() { + return handle != null ? CraftMagicNumbers.getMaterialData(handle.getItem()) : super.getData(); + } + + @Override + public Material getType() { + return handle != null ? CraftMagicNumbers.getMaterial(handle.getItem()) : Material.AIR; + } + + @Override + public void setType(Material type) { + if (getType() == type) { + return; + } else if (type == Material.AIR) { + handle = null; + } else if (CraftMagicNumbers.getItem(type) == null) { // :( + handle = null; + } else if (handle == null) { + handle = new net.minecraft.server.ItemStack(CraftMagicNumbers.getItem(type), 1); + } else { + handle.setItem(CraftMagicNumbers.getItem(type)); + if (hasItemMeta()) { + // This will create the appropriate item meta, which will contain all the data we intend to keep + setItemMeta(handle, getItemMeta(handle)); + } + } + setData(null); + } + + @Override + public int getAmount() { + return handle != null ? handle.getCount() : 0; + } + + @Override + public void setAmount(int amount) { + if (handle == null) { + return; + } + + handle.setCount(amount); + if (amount == 0) { + handle = null; + } + } + + @Override + public void setDurability(final short durability) { + // Ignore damage if item is null + if (handle != null) { + handle.setDamage(durability); + } + } + + @Override + public short getDurability() { + if (handle != null) { + return (short) handle.getDamage(); + } else { + return -1; + } + } + + @Override + public int getMaxStackSize() { + return (handle == null) ? Material.AIR.getMaxStackSize() : handle.getItem().getMaxStackSize(); + } + + // Paper start + @Override + public int getMaxItemUseDuration() { + return handle == null ? 0 : handle.getItemUseMaxDuration(); + } + // Paper end + + @Override + public void addUnsafeEnchantment(Enchantment ench, int level) { + Validate.notNull(ench, "Cannot add null enchantment"); + + // Paper start - Replace whole method + final ItemMeta itemMeta = getItemMeta(); + itemMeta.addEnchant(ench, level, true); + setItemMeta(itemMeta); + // Paper end + } + + static boolean makeTag(net.minecraft.server.ItemStack item) { + if (item == null) { + return false; + } + + if (item.getTag() == null) { + item.setTag(new NBTTagCompound()); + } + + return true; + } + + @Override + public boolean containsEnchantment(Enchantment ench) { + return hasItemMeta() && getItemMeta().hasEnchant(ench); // Paper - use meta + } + + @Override + public int getEnchantmentLevel(Enchantment ench) { + return hasItemMeta() ? getItemMeta().getEnchantLevel(ench) : 0; // Paper - replace entire method with meta + } + + @Override + public int removeEnchantment(Enchantment ench) { + Validate.notNull(ench, "Cannot remove null enchantment"); + + // Paper start - replace entire method + final ItemMeta itemMeta = getItemMeta(); + int level = itemMeta.getEnchantLevel(ench); + if (level > 0) { + itemMeta.removeEnchant(ench); + setItemMeta(itemMeta); + } + return level; + // Paper end + } + + @Override + public Map getEnchantments() { + return hasItemMeta() ? getItemMeta().getEnchants() : ImmutableMap.of(); // Paper - use Item Meta + } + + static Map getEnchantments(net.minecraft.server.ItemStack item) { + NBTTagList list = (item != null && item.hasEnchantments()) ? item.getEnchantments() : null; + + if (list == null || list.size() == 0) { + return ImmutableMap.of(); + } + + ImmutableMap.Builder result = ImmutableMap.builder(); + + for (int i = 0; i < list.size(); i++) { + String id = ((NBTTagCompound) list.get(i)).getString(ENCHANTMENTS_ID.NBT); + int level = 0xffff & ((NBTTagCompound) list.get(i)).getShort(ENCHANTMENTS_LVL.NBT); + + Enchantment enchant = Enchantment.getByKey(CraftNamespacedKey.fromStringOrNull(id)); + if (enchant != null) { + result.put(enchant, level); + } + } + + return result.build(); + } + + static NBTTagList getEnchantmentList(net.minecraft.server.ItemStack item) { + return (item != null && item.hasEnchantments()) ? item.getEnchantments() : null; + } + + @Override + public CraftItemStack clone() { + CraftItemStack itemStack = (CraftItemStack) super.clone(); + if (this.handle != null) { + itemStack.handle = this.handle.cloneItemStack(); + } + return itemStack; + } + + @Override + public ItemMeta getItemMeta() { + return getItemMeta(handle); + } + + public static ItemMeta getItemMeta(net.minecraft.server.ItemStack item) { + if (!hasItemMeta(item)) { + return CraftItemFactory.instance().getItemMeta(getType(item)); + } + switch (getType(item)) { + case WRITTEN_BOOK: + return new CraftMetaBookSigned(item.getTag()); + case WRITABLE_BOOK: + return new CraftMetaBook(item.getTag()); + case CREEPER_HEAD: + case CREEPER_WALL_HEAD: + case DRAGON_HEAD: + case DRAGON_WALL_HEAD: + case PLAYER_HEAD: + case PLAYER_WALL_HEAD: + case SKELETON_SKULL: + case SKELETON_WALL_SKULL: + case WITHER_SKELETON_SKULL: + case WITHER_SKELETON_WALL_SKULL: + case ZOMBIE_HEAD: + case ZOMBIE_WALL_HEAD: + return new CraftMetaSkull(item.getTag()); + case LEATHER_HELMET: + case LEATHER_CHESTPLATE: + case LEATHER_LEGGINGS: + case LEATHER_BOOTS: + return new CraftMetaLeatherArmor(item.getTag()); + case POTION: + case SPLASH_POTION: + case LINGERING_POTION: + case TIPPED_ARROW: + return new CraftMetaPotion(item.getTag()); + case FILLED_MAP: + return new CraftMetaMap(item.getTag()); + case FIREWORK_ROCKET: + return new CraftMetaFirework(item.getTag()); + case FIREWORK_STAR: + return new CraftMetaCharge(item.getTag()); + case ENCHANTED_BOOK: + return new CraftMetaEnchantedBook(item.getTag()); + case BLACK_BANNER: + case BLACK_WALL_BANNER: + case BLUE_BANNER: + case BLUE_WALL_BANNER: + case BROWN_BANNER: + case BROWN_WALL_BANNER: + case CYAN_BANNER: + case CYAN_WALL_BANNER: + case GRAY_BANNER: + case GRAY_WALL_BANNER: + case GREEN_BANNER: + case GREEN_WALL_BANNER: + case LIGHT_BLUE_BANNER: + case LIGHT_BLUE_WALL_BANNER: + case LIGHT_GRAY_BANNER: + case LIGHT_GRAY_WALL_BANNER: + case LIME_BANNER: + case LIME_WALL_BANNER: + case MAGENTA_BANNER: + case MAGENTA_WALL_BANNER: + case ORANGE_BANNER: + case ORANGE_WALL_BANNER: + case PINK_BANNER: + case PINK_WALL_BANNER: + case PURPLE_BANNER: + case PURPLE_WALL_BANNER: + case RED_BANNER: + case RED_WALL_BANNER: + case WHITE_BANNER: + case WHITE_WALL_BANNER: + case YELLOW_BANNER: + case YELLOW_WALL_BANNER: + return new CraftMetaBanner(item.getTag()); + case BAT_SPAWN_EGG: + case BLAZE_SPAWN_EGG: + case CAVE_SPIDER_SPAWN_EGG: + case CHICKEN_SPAWN_EGG: + case COD_SPAWN_EGG: + case COW_SPAWN_EGG: + case CREEPER_SPAWN_EGG: + case DOLPHIN_SPAWN_EGG: + case DROWNED_SPAWN_EGG: + case DONKEY_SPAWN_EGG: + case ELDER_GUARDIAN_SPAWN_EGG: + case ENDERMAN_SPAWN_EGG: + case ENDERMITE_SPAWN_EGG: + case EVOKER_SPAWN_EGG: + case GHAST_SPAWN_EGG: + case GUARDIAN_SPAWN_EGG: + case HORSE_SPAWN_EGG: + case HUSK_SPAWN_EGG: + case LLAMA_SPAWN_EGG: + case MAGMA_CUBE_SPAWN_EGG: + case MOOSHROOM_SPAWN_EGG: + case MULE_SPAWN_EGG: + case OCELOT_SPAWN_EGG: + case PARROT_SPAWN_EGG: + case PHANTOM_SPAWN_EGG: + case PIG_SPAWN_EGG: + case POLAR_BEAR_SPAWN_EGG: + case PUFFERFISH_SPAWN_EGG: + case RABBIT_SPAWN_EGG: + case SALMON_SPAWN_EGG: + case SHEEP_SPAWN_EGG: + case SHULKER_SPAWN_EGG: + case SILVERFISH_SPAWN_EGG: + case SKELETON_HORSE_SPAWN_EGG: + case SKELETON_SPAWN_EGG: + case SLIME_SPAWN_EGG: + case SPIDER_SPAWN_EGG: + case SQUID_SPAWN_EGG: + case STRAY_SPAWN_EGG: + case TROPICAL_FISH_SPAWN_EGG: + case TURTLE_SPAWN_EGG: + case VEX_SPAWN_EGG: + case VILLAGER_SPAWN_EGG: + case VINDICATOR_SPAWN_EGG: + case WITCH_SPAWN_EGG: + case WITHER_SKELETON_SPAWN_EGG: + case WOLF_SPAWN_EGG: + case ZOMBIE_HORSE_SPAWN_EGG: + case ZOMBIE_PIGMAN_SPAWN_EGG: + case ZOMBIE_SPAWN_EGG: + case ZOMBIE_VILLAGER_SPAWN_EGG: + return new CraftMetaSpawnEgg(item.getTag()); + case KNOWLEDGE_BOOK: + return new CraftMetaKnowledgeBook(item.getTag()); + case ARMOR_STAND: + return new CraftMetaArmorStand(item.getTag()); // Paper + case FURNACE: + case CHEST: + case TRAPPED_CHEST: + case JUKEBOX: + case DISPENSER: + case DROPPER: + case SIGN: + case SPAWNER: + case BREWING_STAND: + case ENCHANTING_TABLE: + case COMMAND_BLOCK: + case REPEATING_COMMAND_BLOCK: + case CHAIN_COMMAND_BLOCK: + case BEACON: + case DAYLIGHT_DETECTOR: + case HOPPER: + case COMPARATOR: + case SHIELD: + case STRUCTURE_BLOCK: + case SHULKER_BOX: + case WHITE_SHULKER_BOX: + case ORANGE_SHULKER_BOX: + case MAGENTA_SHULKER_BOX: + case LIGHT_BLUE_SHULKER_BOX: + case YELLOW_SHULKER_BOX: + case LIME_SHULKER_BOX: + case PINK_SHULKER_BOX: + case GRAY_SHULKER_BOX: + case LIGHT_GRAY_SHULKER_BOX: + case CYAN_SHULKER_BOX: + case PURPLE_SHULKER_BOX: + case BLUE_SHULKER_BOX: + case BROWN_SHULKER_BOX: + case GREEN_SHULKER_BOX: + case RED_SHULKER_BOX: + case BLACK_SHULKER_BOX: + case ENDER_CHEST: + return new CraftMetaBlockState(item.getTag(), CraftMagicNumbers.getMaterial(item.getItem())); + case TROPICAL_FISH_BUCKET: + return new CraftMetaTropicalFishBucket(item.getTag()); + default: + return new CraftMetaItem(item.getTag()); + } + } + + static Material getType(net.minecraft.server.ItemStack item) { + return item == null ? Material.AIR : CraftMagicNumbers.getMaterial(item.getItem()); + } + + @Override + public boolean setItemMeta(ItemMeta itemMeta) { + return setItemMeta(handle, itemMeta); + } + + public static boolean setItemMeta(net.minecraft.server.ItemStack item, ItemMeta itemMeta) { + if (item == null) { + return false; + } + if (CraftItemFactory.instance().equals(itemMeta, null)) { + item.setTag(null); + return true; + } + if (!CraftItemFactory.instance().isApplicable(itemMeta, getType(item))) { + return false; + } + + itemMeta = CraftItemFactory.instance().asMetaFor(itemMeta, getType(item)); + if (itemMeta == null) return true; + + Item oldItem = item.getItem(); + Item newItem = CraftMagicNumbers.getItem(CraftItemFactory.instance().updateMaterial(itemMeta, CraftMagicNumbers.getMaterial(oldItem))); + if (oldItem != newItem) { + item.setItem(newItem); + } + + NBTTagCompound tag = new NBTTagCompound(); + item.setTag(tag); + + ((CraftMetaItem) itemMeta).applyToItem(tag); + item.convertStack(); + + return true; + } + + @Override + public boolean isSimilar(ItemStack stack) { + if (stack == null) { + return false; + } + if (stack == this) { + return true; + } + if (!(stack instanceof CraftItemStack)) { + return stack.getClass() == ItemStack.class && stack.isSimilar(this); + } + + CraftItemStack that = (CraftItemStack) stack; + if (handle == that.handle) { + return true; + } + if (handle == null || that.handle == null) { + return false; + } + Material comparisonType = CraftLegacy.fromLegacy(that.getType()); // This may be called from legacy item stacks, try to get the right material + if (!(comparisonType == getType() && getDurability() == that.getDurability())) { + return false; + } + return hasItemMeta() ? that.hasItemMeta() && handle.getTag().equals(that.handle.getTag()) : !that.hasItemMeta(); + } + + @Override + public boolean hasItemMeta() { + return hasItemMeta(handle) && (handle.getDamage() != 0 || (handle.getTag() != null && handle.getTag().map.size() >= (handle.getTag().hasKey(CraftMetaItem.DAMAGE.NBT) ? 2 : 1))); // Paper - keep 1.12 CraftBukkit behavior without calling getItemMeta + } + + static boolean hasItemMeta(net.minecraft.server.ItemStack item) { + return !(item == null || item.getTag() == null || item.getTag().isEmpty()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchant.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchant.java new file mode 100644 index 000000000000..7569b29dc7b4 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchant.java @@ -0,0 +1,80 @@ +package org.bukkit.craftbukkit.inventory; + +import com.google.common.base.Function; +import com.google.common.collect.Lists; +import java.util.Collections; +import java.util.List; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.IMerchant; +import net.minecraft.server.MerchantRecipeList; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.Merchant; +import org.bukkit.inventory.MerchantRecipe; + +public class CraftMerchant implements Merchant { + + protected final IMerchant merchant; + + public CraftMerchant(IMerchant merchant) { + this.merchant = merchant; + } + + public IMerchant getMerchant() { + return merchant; + } + + @Override + public List getRecipes() { + return Collections.unmodifiableList(Lists.transform(merchant.getOffers(null), new Function() { + @Override + public MerchantRecipe apply(net.minecraft.server.MerchantRecipe recipe) { + return recipe.asBukkit(); + } + })); + } + + @Override + public void setRecipes(List recipes) { + MerchantRecipeList recipesList = merchant.getOffers(null); + recipesList.clear(); + for (MerchantRecipe recipe : recipes) { + recipesList.add(CraftMerchantRecipe.fromBukkit(recipe).toMinecraft()); + } + } + + @Override + public MerchantRecipe getRecipe(int i) { + return merchant.getOffers(null).get(i).asBukkit(); + } + + @Override + public void setRecipe(int i, MerchantRecipe merchantRecipe) { + merchant.getOffers(null).set(i, CraftMerchantRecipe.fromBukkit(merchantRecipe).toMinecraft()); + } + + @Override + public int getRecipeCount() { + return merchant.getOffers(null).size(); + } + + @Override + public boolean isTrading() { + return getTrader() != null; + } + + @Override + public HumanEntity getTrader() { + EntityHuman eh = merchant.getTrader(); + return eh == null ? null : eh.getBukkitEntity(); + } + + @Override + public int hashCode() { + return merchant.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + return obj instanceof CraftMerchant && ((CraftMerchant) obj).merchant.equals(this.merchant); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantCustom.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantCustom.java new file mode 100644 index 000000000000..4f8edbe3bad8 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantCustom.java @@ -0,0 +1,76 @@ +package org.bukkit.craftbukkit.inventory; + +import org.apache.commons.lang.Validate; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.ChatComponentText; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.IChatBaseComponent; +import net.minecraft.server.IMerchant; +import net.minecraft.server.ItemStack; +import net.minecraft.server.MerchantRecipe; +import net.minecraft.server.MerchantRecipeList; +import net.minecraft.server.World; + +public class CraftMerchantCustom extends CraftMerchant { + + public CraftMerchantCustom(String title) { + super(new MinecraftMerchant(title)); + } + + @Override + public String toString() { + return "CraftMerchantCustom"; + } + + private static class MinecraftMerchant implements IMerchant { + + private final IChatBaseComponent title; + private final MerchantRecipeList trades = new MerchantRecipeList(); + private EntityHuman tradingPlayer; + + public MinecraftMerchant(String title) { + Validate.notNull(title, "Title cannot be null"); + this.title = new ChatComponentText(title); + } + + @Override + public void setTradingPlayer(EntityHuman entityhuman) { + this.tradingPlayer = entityhuman; + } + + @Override + public EntityHuman getTrader() { + return this.tradingPlayer; + } + + @Override + public MerchantRecipeList getOffers(EntityHuman entityhuman) { + return this.trades; + } + + @Override + public void a(MerchantRecipe merchantrecipe) { + // increase recipe's uses + merchantrecipe.increaseUses(); + } + + @Override + public void a(ItemStack itemstack) { + } + + @Override + public IChatBaseComponent getScoreboardDisplayName() { + return title; + } + + @Override + public World getWorld() { + return null; + } + + @Override + public BlockPosition getPosition() { + return null; + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantRecipe.java new file mode 100644 index 000000000000..1730cc93b5c6 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantRecipe.java @@ -0,0 +1,82 @@ +package org.bukkit.craftbukkit.inventory; + +import com.google.common.base.Preconditions; +import java.util.List; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.MerchantRecipe; + +public class CraftMerchantRecipe extends MerchantRecipe { + + private final net.minecraft.server.MerchantRecipe handle; + + public CraftMerchantRecipe(net.minecraft.server.MerchantRecipe merchantRecipe) { + super(CraftItemStack.asBukkitCopy(merchantRecipe.sellingItem), 0); + this.handle = merchantRecipe; + addIngredient(CraftItemStack.asBukkitCopy(merchantRecipe.buyingItem1)); + addIngredient(CraftItemStack.asBukkitCopy(merchantRecipe.buyingItem2)); + } + + public CraftMerchantRecipe(ItemStack result, int uses, int maxUses, boolean experienceReward) { + super(result, uses, maxUses, experienceReward); + this.handle = new net.minecraft.server.MerchantRecipe( + net.minecraft.server.ItemStack.a, + net.minecraft.server.ItemStack.a, + CraftItemStack.asNMSCopy(result), + uses, + maxUses, + this + ); + this.setExperienceReward(experienceReward); + } + + @Override + public int getUses() { + return handle.uses; + } + + @Override + public void setUses(int uses) { + handle.uses = uses; + } + + @Override + public int getMaxUses() { + return handle.maxUses; + } + + @Override + public void setMaxUses(int maxUses) { + handle.maxUses = maxUses; + } + + @Override + public boolean hasExperienceReward() { + return handle.rewardExp; + } + + @Override + public void setExperienceReward(boolean flag) { + handle.rewardExp = flag; + } + + public net.minecraft.server.MerchantRecipe toMinecraft() { + List ingredients = getIngredients(); + Preconditions.checkState(!ingredients.isEmpty(), "No offered ingredients"); + handle.buyingItem1 = CraftItemStack.asNMSCopy(ingredients.get(0)); + if (ingredients.size() > 1) { + handle.buyingItem2 = CraftItemStack.asNMSCopy(ingredients.get(1)); + } + return handle; + } + + public static CraftMerchantRecipe fromBukkit(MerchantRecipe recipe) { + if (recipe instanceof CraftMerchantRecipe) { + return (CraftMerchantRecipe) recipe; + } else { + CraftMerchantRecipe craft = new CraftMerchantRecipe(recipe.getResult(), recipe.getUses(), recipe.getMaxUses(), recipe.hasExperienceReward()); + craft.setIngredients(recipe.getIngredients()); + + return craft; + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java new file mode 100644 index 000000000000..c00b89c8d4b6 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java @@ -0,0 +1,311 @@ +package org.bukkit.craftbukkit.inventory; + +import com.destroystokyo.paper.inventory.meta.ArmorStandMeta; +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.Dynamic; + +import net.minecraft.server.DataConverterTypes; +import net.minecraft.server.DynamicOpsNBT; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.NBTBase; +import net.minecraft.server.NBTTagCompound; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; + +import java.util.Map; + +// Paper - Created entire class +@DelegateDeserialization(CraftMetaItem.SerializableMeta.class) +public class CraftMetaArmorStand extends CraftMetaItem implements ArmorStandMeta { + + static final ItemMetaKey ENTITY_TAG = new ItemMetaKey("EntityTag", "entity-tag"); + static final ItemMetaKey INVISIBLE = new ItemMetaKey("Invisible", "invisible"); + static final ItemMetaKey NO_BASE_PLATE = new ItemMetaKey("NoBasePlate", "no-base-plate"); + static final ItemMetaKey SHOW_ARMS = new ItemMetaKey("ShowArms", "show-arms"); + static final ItemMetaKey SMALL = new ItemMetaKey("Small", "small"); + static final ItemMetaKey MARKER = new ItemMetaKey("Marker", "marker"); + + private NBTTagCompound entityTag; + + private boolean invisible; + private boolean noBasePlate; + private boolean showArms; + private boolean small; + private boolean marker; + + CraftMetaArmorStand(CraftMetaItem meta) { + super(meta); + + if (!(meta instanceof CraftMetaArmorStand)) { + return; + } + + CraftMetaArmorStand standMeta = (CraftMetaArmorStand) meta; + this.invisible = standMeta.invisible; + this.noBasePlate = standMeta.noBasePlate; + this.showArms = standMeta.showArms; + this.small = standMeta.small; + this.marker = standMeta.marker; + } + + CraftMetaArmorStand(NBTTagCompound tag) { + super(tag); + + if (tag.hasKey(ENTITY_TAG.NBT)) { + entityTag = tag.getCompound(ENTITY_TAG.NBT); + + if (entityTag.hasKey(INVISIBLE.NBT)) { + invisible = entityTag.getBoolean(INVISIBLE.NBT); + } + + if (entityTag.hasKey(NO_BASE_PLATE.NBT)) { + noBasePlate = entityTag.getBoolean(NO_BASE_PLATE.NBT); + } + + if (entityTag.hasKey(SHOW_ARMS.NBT)) { + showArms = entityTag.getBoolean(SHOW_ARMS.NBT); + } + + if (entityTag.hasKey(SMALL.NBT)) { + small = entityTag.getBoolean(SMALL.NBT); + } + + if (entityTag.hasKey(MARKER.NBT)) { + marker = entityTag.getBoolean(MARKER.NBT); + } + } + } + + CraftMetaArmorStand(Map map) { + super(map); + + boolean invis = SerializableMeta.getBoolean(map, INVISIBLE.BUKKIT); + boolean noBase = SerializableMeta.getBoolean(map, NO_BASE_PLATE.BUKKIT); + boolean showArms = SerializableMeta.getBoolean(map, SHOW_ARMS.BUKKIT); + boolean small = SerializableMeta.getBoolean(map, SMALL.BUKKIT); + boolean marker = SerializableMeta.getBoolean(map, MARKER.BUKKIT); + + this.invisible = invis; + this.noBasePlate = noBase; + this.showArms = showArms; + this.small = small; + this.marker = marker; + } + + @Override + void applyToItem(NBTTagCompound tag) { + super.applyToItem(tag); + + if (!isArmorStandEmpty() && entityTag == null) { + entityTag = new NBTTagCompound(); + } + + if (isInvisible()) { + entityTag.setBoolean(INVISIBLE.NBT, invisible); + } + + if (hasNoBasePlate()) { + entityTag.setBoolean(NO_BASE_PLATE.NBT, noBasePlate); + } + + if (shouldShowArms()) { + entityTag.setBoolean(SHOW_ARMS.NBT, showArms); + } + + if (isSmall()) { + entityTag.setBoolean(SMALL.NBT, small); + } + + if (isMarker()) { + entityTag.setBoolean(MARKER.NBT, marker); + } + + if (entityTag != null) { + tag.set(ENTITY_TAG.NBT, entityTag); + } + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case ARMOR_STAND: + return true; + default: + return false; + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isArmorStandEmpty(); + } + + boolean isArmorStandEmpty() { + return !(isInvisible() || hasNoBasePlate() || shouldShowArms() || isSmall() || isMarker() || entityTag != null); + } + + @Override + ImmutableMap.Builder serialize(ImmutableMap.Builder builder) { + super.serialize(builder); + + if (isInvisible()) { + builder.put(INVISIBLE.BUKKIT, invisible); + } + + if (hasNoBasePlate()) { + builder.put(NO_BASE_PLATE.BUKKIT, noBasePlate); + } + + if (shouldShowArms()) { + builder.put(SHOW_ARMS.BUKKIT, showArms); + } + + if (isSmall()) { + builder.put(SMALL.BUKKIT, small); + } + + if (isMarker()) { + builder.put(MARKER.BUKKIT, marker); + } + + return builder; + } + + @Override + void deserializeInternal(NBTTagCompound tag, Object context) { + super.deserializeInternal(tag, context); + + if (tag.hasKey(ENTITY_TAG.NBT)) { + entityTag = tag.getCompound(ENTITY_TAG.NBT); + MinecraftServer.getServer().dataConverterManager.update(DataConverterTypes.ENTITY, new Dynamic(DynamicOpsNBT.a, entityTag), -1, Bukkit.getUnsafe().getDataVersion()); + + if (entityTag.hasKey(INVISIBLE.NBT)) { + invisible = entityTag.getBoolean(INVISIBLE.NBT); + } + + if (entityTag.hasKey(NO_BASE_PLATE.NBT)) { + noBasePlate = entityTag.getBoolean(NO_BASE_PLATE.NBT); + } + + if (entityTag.hasKey(SHOW_ARMS.NBT)) { + showArms = entityTag.getBoolean(SHOW_ARMS.NBT); + } + + if (entityTag.hasKey(SMALL.NBT)) { + small = entityTag.getBoolean(SMALL.NBT); + } + + if (entityTag.hasKey(MARKER.NBT)) { + marker = entityTag.getBoolean(MARKER.NBT); + } + } + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaArmorStand) { + CraftMetaArmorStand that = (CraftMetaArmorStand) meta; + + return invisible == that.invisible && + noBasePlate == that.noBasePlate && + showArms == that.showArms && + small == that.small && + marker == that.marker; + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaArmorStand || isArmorStandEmpty()); + } + + @Override + void serializeInternal(Map internalTags) { + if (entityTag != null) { + internalTags.put(ENTITY_TAG.NBT, entityTag); + } + } + + @Override + public boolean isInvisible() { + return invisible; + } + + @Override + public boolean hasNoBasePlate() { + return noBasePlate; + } + + @Override + public boolean shouldShowArms() { + return showArms; + } + + @Override + public boolean isSmall() { + return small; + } + + @Override + public boolean isMarker() { + return marker; + } + + @Override + public void setInvisible(boolean invisible) { + this.invisible = invisible; + } + + @Override + public void setNoBasePlate(boolean noBasePlate) { + this.noBasePlate = noBasePlate; + } + + @Override + public void setShowArms(boolean showArms) { + this.showArms = showArms; + } + + @Override + public void setSmall(boolean small) { + this.small = small; + } + + @Override + public void setMarker(boolean marker) { + this.marker = marker; + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + + hash += entityTag != null ? 73 * hash + entityTag.hashCode() : 0; + hash += isInvisible() ? 61 * hash + 1231 : 0; + hash += hasNoBasePlate() ? 61 * hash + 1231 : 0; + hash += shouldShowArms() ? 61 * hash + 1231 : 0; + hash += isSmall() ? 61 * hash + 1231 : 0; + hash += isMarker() ? 61 * hash + 1231 : 0; + + return original != hash ? CraftMetaArmorStand.class.hashCode() ^ hash : hash; + } + + @Override + public CraftMetaArmorStand clone() { + CraftMetaArmorStand clone = (CraftMetaArmorStand) super.clone(); + + if (entityTag != null) { + clone.entityTag = entityTag.clone(); + } + + return clone; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java new file mode 100644 index 000000000000..2f72f0ce5ce7 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java @@ -0,0 +1,246 @@ +package org.bukkit.craftbukkit.inventory; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagList; +import org.bukkit.DyeColor; +import org.bukkit.Material; +import org.bukkit.block.banner.Pattern; +import org.bukkit.block.banner.PatternType; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.inventory.meta.BannerMeta; + +@DelegateDeserialization(CraftMetaItem.SerializableMeta.class) +public class CraftMetaBanner extends CraftMetaItem implements BannerMeta { + + static final ItemMetaKey BASE = new ItemMetaKey("Base", "base-color"); + static final ItemMetaKey PATTERNS = new ItemMetaKey("Patterns", "patterns"); + static final ItemMetaKey COLOR = new ItemMetaKey("Color", "color"); + static final ItemMetaKey PATTERN = new ItemMetaKey("Pattern", "pattern"); + + private DyeColor base; + private List patterns = new ArrayList(); + + CraftMetaBanner(CraftMetaItem meta) { + super(meta); + + if (!(meta instanceof CraftMetaBanner)) { + return; + } + + CraftMetaBanner banner = (CraftMetaBanner) meta; + base = banner.base; + patterns = new ArrayList(banner.patterns); + } + + CraftMetaBanner(NBTTagCompound tag) { + super(tag); + + if (!tag.hasKey("BlockEntityTag")) { + return; + } + + NBTTagCompound entityTag = tag.getCompound("BlockEntityTag"); + + base = entityTag.hasKey(BASE.NBT) ? DyeColor.getByWoolData((byte) entityTag.getInt(BASE.NBT)) : null; + + if (entityTag.hasKey(PATTERNS.NBT)) { + NBTTagList patterns = entityTag.getList(PATTERNS.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND); + for (int i = 0; i < Math.min(patterns.size(), 20); i++) { + NBTTagCompound p = patterns.getCompound(i); + this.patterns.add(new Pattern(DyeColor.getByWoolData((byte) p.getInt(COLOR.NBT)), PatternType.getByIdentifier(p.getString(PATTERN.NBT)))); + } + } + } + + CraftMetaBanner(Map map) { + super(map); + + String baseStr = SerializableMeta.getString(map, BASE.BUKKIT, true); + if (baseStr != null) { + base = DyeColor.legacyValueOf(baseStr); + } + + Iterable rawPatternList = SerializableMeta.getObject(Iterable.class, map, PATTERNS.BUKKIT, true); + if (rawPatternList == null) { + return; + } + + for (Object obj : rawPatternList) { + if (!(obj instanceof Pattern)) { + throw new IllegalArgumentException("Object in pattern list is not valid. " + obj.getClass()); + } + addPattern((Pattern) obj); + } + } + @Override + void applyToItem(NBTTagCompound tag) { + super.applyToItem(tag); + + NBTTagCompound entityTag = new NBTTagCompound(); + if (base != null) { + entityTag.setInt(BASE.NBT, base.getWoolData()); + } + + NBTTagList newPatterns = new NBTTagList(); + + for (Pattern p : patterns) { + NBTTagCompound compound = new NBTTagCompound(); + compound.setInt(COLOR.NBT, p.getColor().getWoolData()); + compound.setString(PATTERN.NBT, p.getPattern().getIdentifier()); + newPatterns.add(compound); + } + entityTag.set(PATTERNS.NBT, newPatterns); + + tag.set("BlockEntityTag", entityTag); + } + + @Override + public DyeColor getBaseColor() { + return base; + } + + @Override + public void setBaseColor(DyeColor color) { + base = color; + } + + @Override + public List getPatterns() { + return new ArrayList(patterns); + } + + @Override + public void setPatterns(List patterns) { + this.patterns = new ArrayList(patterns); + } + + @Override + public void addPattern(Pattern pattern) { + patterns.add(pattern); + } + + @Override + public Pattern getPattern(int i) { + return patterns.get(i); + } + + @Override + public Pattern removePattern(int i) { + return patterns.remove(i); + } + + @Override + public void setPattern(int i, Pattern pattern) { + patterns.set(i, pattern); + } + + @Override + public int numberOfPatterns() { + return patterns.size(); + } + + @Override + ImmutableMap.Builder serialize(ImmutableMap.Builder builder) { + super.serialize(builder); + + if(base != null){ + builder.put(BASE.BUKKIT, base.toString()); + } + + if(!patterns.isEmpty()){ + builder.put(PATTERNS.BUKKIT, ImmutableList.copyOf(patterns)); + } + + return builder; + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + if (base != null) { + hash = 31 * hash + base.hashCode(); + } + if (!patterns.isEmpty()) { + hash = 31 * hash + patterns.hashCode(); + } + return original != hash ? CraftMetaBanner.class.hashCode() ^ hash : hash; + } + + @Override + public boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaBanner) { + CraftMetaBanner that = (CraftMetaBanner) meta; + + return base == that.base && patterns.equals(that.patterns); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaBanner || (patterns.isEmpty() && base == null)); + } + + @Override + boolean isEmpty() { + return super.isEmpty() && patterns.isEmpty() && base == null; + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case BLACK_BANNER: + case BLACK_WALL_BANNER: + case BLUE_BANNER: + case BLUE_WALL_BANNER: + case BROWN_BANNER: + case BROWN_WALL_BANNER: + case CYAN_BANNER: + case CYAN_WALL_BANNER: + case GRAY_BANNER: + case GRAY_WALL_BANNER: + case GREEN_BANNER: + case GREEN_WALL_BANNER: + case LIGHT_BLUE_BANNER: + case LIGHT_BLUE_WALL_BANNER: + case LIGHT_GRAY_BANNER: + case LIGHT_GRAY_WALL_BANNER: + case LIME_BANNER: + case LIME_WALL_BANNER: + case MAGENTA_BANNER: + case MAGENTA_WALL_BANNER: + case ORANGE_BANNER: + case ORANGE_WALL_BANNER: + case PINK_BANNER: + case PINK_WALL_BANNER: + case PURPLE_BANNER: + case PURPLE_WALL_BANNER: + case RED_BANNER: + case RED_WALL_BANNER: + case WHITE_BANNER: + case WHITE_WALL_BANNER: + case YELLOW_BANNER: + case YELLOW_WALL_BANNER: + return true; + default: + return false; + } + } + + @Override + public CraftMetaBanner clone() { + CraftMetaBanner meta = (CraftMetaBanner) super.clone(); + meta.patterns = new ArrayList<>(patterns); + return meta; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java new file mode 100644 index 000000000000..f96f6af038df --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java @@ -0,0 +1,615 @@ +package org.bukkit.craftbukkit.inventory; + +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableMap; +import java.util.Map; +import net.minecraft.server.EnumColor; +import net.minecraft.server.NBTBase; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.TileEntity; +import net.minecraft.server.TileEntityBanner; +import net.minecraft.server.TileEntityBeacon; +import net.minecraft.server.TileEntityBrewingStand; +import net.minecraft.server.TileEntityChest; +import net.minecraft.server.TileEntityCommand; +import net.minecraft.server.TileEntityComparator; +import net.minecraft.server.TileEntityDispenser; +import net.minecraft.server.TileEntityDropper; +import net.minecraft.server.TileEntityEnchantTable; +import net.minecraft.server.TileEntityEndGateway; +import net.minecraft.server.TileEntityEnderChest; +import net.minecraft.server.TileEntityFurnace; +import net.minecraft.server.TileEntityHopper; +import net.minecraft.server.TileEntityJukeBox; +import net.minecraft.server.TileEntityLightDetector; +import net.minecraft.server.TileEntityMobSpawner; +import net.minecraft.server.TileEntityShulkerBox; +import net.minecraft.server.TileEntitySign; +import net.minecraft.server.TileEntitySkull; +import net.minecraft.server.TileEntityStructure; +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.block.BlockState; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.block.CraftBanner; +import org.bukkit.craftbukkit.block.CraftBeacon; +import org.bukkit.craftbukkit.block.CraftBlockEntityState; +import org.bukkit.craftbukkit.block.CraftBrewingStand; +import org.bukkit.craftbukkit.block.CraftChest; +import org.bukkit.craftbukkit.block.CraftCommandBlock; +import org.bukkit.craftbukkit.block.CraftComparator; +import org.bukkit.craftbukkit.block.CraftCreatureSpawner; +import org.bukkit.craftbukkit.block.CraftDaylightDetector; +import org.bukkit.craftbukkit.block.CraftDispenser; +import org.bukkit.craftbukkit.block.CraftDropper; +import org.bukkit.craftbukkit.block.CraftEnchantingTable; +import org.bukkit.craftbukkit.block.CraftEndGateway; +import org.bukkit.craftbukkit.block.CraftEnderChest; +import org.bukkit.craftbukkit.block.CraftFurnace; +import org.bukkit.craftbukkit.block.CraftHopper; +import org.bukkit.craftbukkit.block.CraftJukebox; +import org.bukkit.craftbukkit.block.CraftShulkerBox; +import org.bukkit.craftbukkit.block.CraftSign; +import org.bukkit.craftbukkit.block.CraftSkull; +import org.bukkit.craftbukkit.block.CraftStructureBlock; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.inventory.meta.BlockStateMeta; + +@DelegateDeserialization(CraftMetaItem.SerializableMeta.class) +public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta { + + @ItemMetaKey.Specific(ItemMetaKey.Specific.To.NBT) + static final ItemMetaKey BLOCK_ENTITY_TAG = new ItemMetaKey("BlockEntityTag"); + + final Material material; + NBTTagCompound blockEntityTag; + + CraftMetaBlockState(CraftMetaItem meta, Material material) { + super(meta); + this.material = material; + + if (!(meta instanceof CraftMetaBlockState) + || ((CraftMetaBlockState) meta).material != material) { + blockEntityTag = null; + return; + } + + CraftMetaBlockState te = (CraftMetaBlockState) meta; + this.blockEntityTag = te.blockEntityTag; + } + + CraftMetaBlockState(NBTTagCompound tag, Material material) { + super(tag); + this.material = material; + + if (tag.hasKeyOfType(BLOCK_ENTITY_TAG.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND)) { + blockEntityTag = tag.getCompound(BLOCK_ENTITY_TAG.NBT); + } else { + blockEntityTag = null; + } + } + + CraftMetaBlockState(Map map) { + super(map); + String matName = SerializableMeta.getString(map, "blockMaterial", true); + Material m = Material.getMaterial(matName); + if (m != null) { + material = m; + } else { + material = Material.AIR; + } + } + + @Override + void applyToItem(NBTTagCompound tag) { + super.applyToItem(tag); + + if (blockEntityTag != null) { + tag.set(BLOCK_ENTITY_TAG.NBT, blockEntityTag); + } + } + + @Override + void deserializeInternal(NBTTagCompound tag, Object context) { + super.deserializeInternal(tag, context); + + if (tag.hasKeyOfType(BLOCK_ENTITY_TAG.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND)) { + blockEntityTag = tag.getCompound(BLOCK_ENTITY_TAG.NBT); + } + } + + @Override + void serializeInternal(final Map internalTags) { + if (blockEntityTag != null) { + internalTags.put(BLOCK_ENTITY_TAG.NBT, blockEntityTag); + } + } + + @Override + ImmutableMap.Builder serialize(ImmutableMap.Builder builder) { + super.serialize(builder); + builder.put("blockMaterial", material.name()); + return builder; + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + if (blockEntityTag != null) { + hash = 61 * hash + this.blockEntityTag.hashCode(); + } + return original != hash ? CraftMetaBlockState.class.hashCode() ^ hash : hash; + } + + @Override + public boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaBlockState) { + CraftMetaBlockState that = (CraftMetaBlockState) meta; + + return Objects.equal(this.blockEntityTag, that.blockEntityTag); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaBlockState || blockEntityTag == null); + } + + @Override + boolean isEmpty() { + return super.isEmpty() && blockEntityTag == null; + } + + @Override + boolean applicableTo(Material type) { + switch(type){ + case FURNACE: + case CHEST: + case TRAPPED_CHEST: + case JUKEBOX: + case DISPENSER: + case DROPPER: + case SIGN: + case SPAWNER: + case BREWING_STAND: + case ENCHANTING_TABLE: + case COMMAND_BLOCK: + case REPEATING_COMMAND_BLOCK: + case CHAIN_COMMAND_BLOCK: + case BEACON: + case DAYLIGHT_DETECTOR: + case HOPPER: + case COMPARATOR: + case SHIELD: + case STRUCTURE_BLOCK: + case SHULKER_BOX: + case WHITE_SHULKER_BOX: + case ORANGE_SHULKER_BOX: + case MAGENTA_SHULKER_BOX: + case LIGHT_BLUE_SHULKER_BOX: + case YELLOW_SHULKER_BOX: + case LIME_SHULKER_BOX: + case PINK_SHULKER_BOX: + case GRAY_SHULKER_BOX: + case LIGHT_GRAY_SHULKER_BOX: + case CYAN_SHULKER_BOX: + case PURPLE_SHULKER_BOX: + case BLUE_SHULKER_BOX: + case BROWN_SHULKER_BOX: + case GREEN_SHULKER_BOX: + case RED_SHULKER_BOX: + case BLACK_SHULKER_BOX: + case ENDER_CHEST: + return true; + } + return false; + } + + @Override + public CraftMetaBlockState clone() { + CraftMetaBlockState meta = (CraftMetaBlockState) super.clone(); + if (blockEntityTag != null) { + meta.blockEntityTag = blockEntityTag.clone(); + } + return meta; + } + + @Override + public boolean hasBlockState() { + return blockEntityTag != null; + } + + @Override + public BlockState getBlockState() { + if (blockEntityTag != null) { + switch (material) { + case SHIELD: + blockEntityTag.setString("id", "banner"); + break; + case SHULKER_BOX: + case WHITE_SHULKER_BOX: + case ORANGE_SHULKER_BOX: + case MAGENTA_SHULKER_BOX: + case LIGHT_BLUE_SHULKER_BOX: + case YELLOW_SHULKER_BOX: + case LIME_SHULKER_BOX: + case PINK_SHULKER_BOX: + case GRAY_SHULKER_BOX: + case LIGHT_GRAY_SHULKER_BOX: + case CYAN_SHULKER_BOX: + case PURPLE_SHULKER_BOX: + case BLUE_SHULKER_BOX: + case BROWN_SHULKER_BOX: + case GREEN_SHULKER_BOX: + case RED_SHULKER_BOX: + case BLACK_SHULKER_BOX: + blockEntityTag.setString("id", "shulker_box"); + break; + } + } + TileEntity te = (blockEntityTag == null) ? null : TileEntity.create(blockEntityTag); + + switch (material) { + case SIGN: + case WALL_SIGN: + if (te == null) { + te = new TileEntitySign(); + } + return new CraftSign(material, (TileEntitySign) te); + case CHEST: + case TRAPPED_CHEST: + if (te == null) { + te = new TileEntityChest(); + } + return new CraftChest(material, (TileEntityChest) te); + case FURNACE: + if (te == null) { + te = new TileEntityFurnace(); + } + return new CraftFurnace(material, (TileEntityFurnace) te); + case DISPENSER: + if (te == null) { + te = new TileEntityDispenser(); + } + return new CraftDispenser(material, (TileEntityDispenser) te); + case DROPPER: + if (te == null) { + te = new TileEntityDropper(); + } + return new CraftDropper(material, (TileEntityDropper) te); + case END_GATEWAY: + if (te == null) { + te = new TileEntityEndGateway(); + } + return new CraftEndGateway(material, (TileEntityEndGateway) te); + case HOPPER: + if (te == null) { + te = new TileEntityHopper(); + } + return new CraftHopper(material, (TileEntityHopper) te); + case SPAWNER: + if (te == null) { + te = new TileEntityMobSpawner(); + } + return new CraftCreatureSpawner(material, (TileEntityMobSpawner) te); + case JUKEBOX: + if (te == null) { + te = new TileEntityJukeBox(); + } + return new CraftJukebox(material, (TileEntityJukeBox) te); + case BREWING_STAND: + if (te == null) { + te = new TileEntityBrewingStand(); + } + return new CraftBrewingStand(material, (TileEntityBrewingStand) te); + case CREEPER_HEAD: + case CREEPER_WALL_HEAD: + case DRAGON_HEAD: + case DRAGON_WALL_HEAD: + case PLAYER_HEAD: + case PLAYER_WALL_HEAD: + case SKELETON_SKULL: + case SKELETON_WALL_SKULL: + case WITHER_SKELETON_SKULL: + case WITHER_SKELETON_WALL_SKULL: + case ZOMBIE_HEAD: + case ZOMBIE_WALL_HEAD: + if (te == null) { + te = new TileEntitySkull(); + } + return new CraftSkull(material, (TileEntitySkull) te); + case COMMAND_BLOCK: + case REPEATING_COMMAND_BLOCK: + case CHAIN_COMMAND_BLOCK: + if (te == null) { + te = new TileEntityCommand(); + } + return new CraftCommandBlock(material, (TileEntityCommand) te); + case BEACON: + if (te == null) { + te = new TileEntityBeacon(); + } + return new CraftBeacon(material, (TileEntityBeacon) te); + case SHIELD: + if (te == null) { + te = new TileEntityBanner(); + } + ((TileEntityBanner) te).color = (blockEntityTag == null) ? EnumColor.WHITE : EnumColor.fromColorIndex(blockEntityTag.getInt(CraftMetaBanner.BASE.NBT)); + case BLACK_BANNER: + case BLACK_WALL_BANNER: + case BLUE_BANNER: + case BLUE_WALL_BANNER: + case BROWN_BANNER: + case BROWN_WALL_BANNER: + case CYAN_BANNER: + case CYAN_WALL_BANNER: + case GRAY_BANNER: + case GRAY_WALL_BANNER: + case GREEN_BANNER: + case GREEN_WALL_BANNER: + case LIGHT_BLUE_BANNER: + case LIGHT_BLUE_WALL_BANNER: + case LIGHT_GRAY_BANNER: + case LIGHT_GRAY_WALL_BANNER: + case LIME_BANNER: + case LIME_WALL_BANNER: + case MAGENTA_BANNER: + case MAGENTA_WALL_BANNER: + case ORANGE_BANNER: + case ORANGE_WALL_BANNER: + case PINK_BANNER: + case PINK_WALL_BANNER: + case PURPLE_BANNER: + case PURPLE_WALL_BANNER: + case RED_BANNER: + case RED_WALL_BANNER: + case WHITE_BANNER: + case WHITE_WALL_BANNER: + case YELLOW_BANNER: + case YELLOW_WALL_BANNER: + if (te == null) { + te = new TileEntityBanner(); + } + return new CraftBanner(material == Material.SHIELD ? shieldToBannerHack(blockEntityTag) : material, (TileEntityBanner) te); + case STRUCTURE_BLOCK: + if (te == null) { + te = new TileEntityStructure(); + } + return new CraftStructureBlock(material, (TileEntityStructure) te); + case SHULKER_BOX: + case WHITE_SHULKER_BOX: + case ORANGE_SHULKER_BOX: + case MAGENTA_SHULKER_BOX: + case LIGHT_BLUE_SHULKER_BOX: + case YELLOW_SHULKER_BOX: + case LIME_SHULKER_BOX: + case PINK_SHULKER_BOX: + case GRAY_SHULKER_BOX: + case LIGHT_GRAY_SHULKER_BOX: + case CYAN_SHULKER_BOX: + case PURPLE_SHULKER_BOX: + case BLUE_SHULKER_BOX: + case BROWN_SHULKER_BOX: + case GREEN_SHULKER_BOX: + case RED_SHULKER_BOX: + case BLACK_SHULKER_BOX: + if (te == null) { + te = new TileEntityShulkerBox(); + } + return new CraftShulkerBox(material, (TileEntityShulkerBox) te); + case ENCHANTING_TABLE: + if (te == null) { + te = new TileEntityEnchantTable(); + } + return new CraftEnchantingTable(material, (TileEntityEnchantTable) te); + case ENDER_CHEST: + if (te == null){ + te = new TileEntityEnderChest(); + } + return new CraftEnderChest(material, (TileEntityEnderChest) te); + case DAYLIGHT_DETECTOR: + if (te == null){ + te = new TileEntityLightDetector(); + } + return new CraftDaylightDetector(material, (TileEntityLightDetector) te); + case COMPARATOR: + if (te == null){ + te = new TileEntityComparator(); + } + return new CraftComparator(material, (TileEntityComparator) te); + default: + throw new IllegalStateException("Missing blockState for " + material); + } + } + + @Override + public void setBlockState(BlockState blockState) { + Validate.notNull(blockState, "blockState must not be null"); + + boolean valid; + switch (material) { + case SIGN: + case WALL_SIGN: + valid = blockState instanceof CraftSign; + break; + case CHEST: + case TRAPPED_CHEST: + valid = blockState instanceof CraftChest; + break; + case FURNACE: + valid = blockState instanceof CraftFurnace; + break; + case DISPENSER: + valid = blockState instanceof CraftDispenser; + break; + case DROPPER: + valid = blockState instanceof CraftDropper; + break; + case END_GATEWAY: + valid = blockState instanceof CraftEndGateway; + break; + case HOPPER: + valid = blockState instanceof CraftHopper; + break; + case SPAWNER: + valid = blockState instanceof CraftCreatureSpawner; + break; + case JUKEBOX: + valid = blockState instanceof CraftJukebox; + break; + case BREWING_STAND: + valid = blockState instanceof CraftBrewingStand; + break; + case CREEPER_HEAD: + case CREEPER_WALL_HEAD: + case DRAGON_HEAD: + case DRAGON_WALL_HEAD: + case PLAYER_HEAD: + case PLAYER_WALL_HEAD: + case SKELETON_SKULL: + case SKELETON_WALL_SKULL: + case WITHER_SKELETON_SKULL: + case WITHER_SKELETON_WALL_SKULL: + case ZOMBIE_HEAD: + case ZOMBIE_WALL_HEAD: + valid = blockState instanceof CraftSkull; + break; + case COMMAND_BLOCK: + case REPEATING_COMMAND_BLOCK: + case CHAIN_COMMAND_BLOCK: + valid = blockState instanceof CraftCommandBlock; + break; + case BEACON: + valid = blockState instanceof CraftBeacon; + break; + case SHIELD: + case BLACK_BANNER: + case BLACK_WALL_BANNER: + case BLUE_BANNER: + case BLUE_WALL_BANNER: + case BROWN_BANNER: + case BROWN_WALL_BANNER: + case CYAN_BANNER: + case CYAN_WALL_BANNER: + case GRAY_BANNER: + case GRAY_WALL_BANNER: + case GREEN_BANNER: + case GREEN_WALL_BANNER: + case LIGHT_BLUE_BANNER: + case LIGHT_BLUE_WALL_BANNER: + case LIGHT_GRAY_BANNER: + case LIGHT_GRAY_WALL_BANNER: + case LIME_BANNER: + case LIME_WALL_BANNER: + case MAGENTA_BANNER: + case MAGENTA_WALL_BANNER: + case ORANGE_BANNER: + case ORANGE_WALL_BANNER: + case PINK_BANNER: + case PINK_WALL_BANNER: + case PURPLE_BANNER: + case PURPLE_WALL_BANNER: + case RED_BANNER: + case RED_WALL_BANNER: + case WHITE_BANNER: + case WHITE_WALL_BANNER: + case YELLOW_BANNER: + case YELLOW_WALL_BANNER: + valid = blockState instanceof CraftBanner; + break; + case STRUCTURE_BLOCK: + valid = blockState instanceof CraftStructureBlock; + break; + case SHULKER_BOX: + case WHITE_SHULKER_BOX: + case ORANGE_SHULKER_BOX: + case MAGENTA_SHULKER_BOX: + case LIGHT_BLUE_SHULKER_BOX: + case YELLOW_SHULKER_BOX: + case LIME_SHULKER_BOX: + case PINK_SHULKER_BOX: + case GRAY_SHULKER_BOX: + case LIGHT_GRAY_SHULKER_BOX: + case CYAN_SHULKER_BOX: + case PURPLE_SHULKER_BOX: + case BLUE_SHULKER_BOX: + case BROWN_SHULKER_BOX: + case GREEN_SHULKER_BOX: + case RED_SHULKER_BOX: + case BLACK_SHULKER_BOX: + valid = blockState instanceof CraftShulkerBox; + break; + case ENCHANTING_TABLE: + valid = blockState instanceof CraftEnchantingTable; + break; + case ENDER_CHEST: + valid = blockState instanceof CraftEnderChest; + break; + case DAYLIGHT_DETECTOR: + valid = blockState instanceof CraftDaylightDetector; + break; + case COMPARATOR: + valid = blockState instanceof CraftComparator; + break; + default: + valid = false; + break; + } + + Validate.isTrue(valid, "Invalid blockState for " + material); + + blockEntityTag = ((CraftBlockEntityState) blockState).getSnapshotNBT(); + // Set shield base + if (material == Material.SHIELD) { + blockEntityTag.setInt(CraftMetaBanner.BASE.NBT, ((CraftBanner) blockState).getBaseColor().getWoolData()); + } + } + + private static Material shieldToBannerHack(NBTTagCompound tag) { + if (tag == null || !tag.hasKeyOfType(CraftMetaBanner.BASE.NBT, CraftMagicNumbers.NBT.TAG_INT)) { + return Material.WHITE_BANNER; + } + + switch (tag.getInt(CraftMetaBanner.BASE.NBT)) { + case 0: + return Material.WHITE_BANNER; + case 1: + return Material.ORANGE_BANNER; + case 2: + return Material.MAGENTA_BANNER; + case 3: + return Material.LIGHT_BLUE_BANNER; + case 4: + return Material.YELLOW_BANNER; + case 5: + return Material.LIME_BANNER; + case 6: + return Material.PINK_BANNER; + case 7: + return Material.GRAY_BANNER; + case 8: + return Material.LIGHT_GRAY_BANNER; + case 9: + return Material.CYAN_BANNER; + case 10: + return Material.PURPLE_BANNER; + case 11: + return Material.BLUE_BANNER; + case 12: + return Material.BROWN_BANNER; + case 13: + return Material.GREEN_BANNER; + case 14: + return Material.RED_BANNER; + case 15: + return Material.BLACK_BANNER; + default: + throw new IllegalArgumentException("Unknown banner colour"); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java new file mode 100644 index 000000000000..20cddd5065d4 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java @@ -0,0 +1,427 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagList; + +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.inventory.meta.BookMeta; + +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap.Builder; +import java.util.AbstractList; +import net.minecraft.server.IChatBaseComponent.ChatSerializer; +import net.minecraft.server.IChatBaseComponent; +import net.minecraft.server.NBTTagString; +import org.bukkit.craftbukkit.util.CraftChatMessage; + +// Spigot start +import static org.spigotmc.ValidateUtils.*; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.chat.ComponentSerializer; +import net.minecraft.server.ChatBaseComponent; +// Spigot end + +@DelegateDeserialization(SerializableMeta.class) +public class CraftMetaBook extends CraftMetaItem implements BookMeta { + static final ItemMetaKey BOOK_TITLE = new ItemMetaKey("title"); + static final ItemMetaKey BOOK_AUTHOR = new ItemMetaKey("author"); + static final ItemMetaKey BOOK_PAGES = new ItemMetaKey("pages"); + static final ItemMetaKey RESOLVED = new ItemMetaKey("resolved"); + static final ItemMetaKey GENERATION = new ItemMetaKey("generation"); + static final int MAX_PAGES = 50; + static final int MAX_PAGE_LENGTH = 320; // 256 limit + 64 characters to allow for psuedo colour codes + static final int MAX_TITLE_LENGTH = 32; + private static final boolean OVERRIDE_CHECKS = Boolean.getBoolean("disable.book-limits"); // Paper - Add override + + protected String title; + protected String author; + public List pages = new ArrayList(); + protected Integer generation; + + CraftMetaBook(CraftMetaItem meta) { + super(meta); + + if (meta instanceof CraftMetaBook) { + CraftMetaBook bookMeta = (CraftMetaBook) meta; + this.title = bookMeta.title; + this.author = bookMeta.author; + pages.addAll(bookMeta.pages); + this.generation = bookMeta.generation; + } + } + + CraftMetaBook(NBTTagCompound tag) { + this(tag, true); + } + + CraftMetaBook(NBTTagCompound tag, boolean handlePages) { + super(tag); + + if (tag.hasKey(BOOK_TITLE.NBT)) { + this.title = limit( tag.getString(BOOK_TITLE.NBT), 1024 ); // Spigot + } + + if (tag.hasKey(BOOK_AUTHOR.NBT)) { + this.author = limit( tag.getString(BOOK_AUTHOR.NBT), 1024 ); // Spigot + } + + boolean resolved = false; + if (tag.hasKey(RESOLVED.NBT)) { + resolved = tag.getBoolean(RESOLVED.NBT); + } + + if (tag.hasKey(GENERATION.NBT)) { + generation = tag.getInt(GENERATION.NBT); + } + + if (tag.hasKey(BOOK_PAGES.NBT) && handlePages) { + NBTTagList pages = tag.getList(BOOK_PAGES.NBT, CraftMagicNumbers.NBT.TAG_STRING); + + for (int i = 0; i < Math.min(pages.size(), MAX_PAGES); i++) { + String page = pages.getString(i); + if (resolved) { + try { + this.pages.add(ChatSerializer.a(page)); + continue; + } catch (Exception e) { + // Ignore and treat as an old book + } + } + addPage( limit( page, 2048 ) ); // Spigot + } + } + } + + CraftMetaBook(Map map) { + super(map); + + setAuthor(SerializableMeta.getString(map, BOOK_AUTHOR.BUKKIT, true)); + + setTitle(SerializableMeta.getString(map, BOOK_TITLE.BUKKIT, true)); + + Iterable pages = SerializableMeta.getObject(Iterable.class, map, BOOK_PAGES.BUKKIT, true); + if(pages != null) { + for (Object page : pages) { + if (page instanceof String) { + addPage((String) page); + } + } + } + + generation = SerializableMeta.getObject(Integer.class, map, GENERATION.BUKKIT, true); + } + + @Override + void applyToItem(NBTTagCompound itemData) { + applyToItem(itemData, true); + } + + void applyToItem(NBTTagCompound itemData, boolean handlePages) { + super.applyToItem(itemData); + + if (hasTitle()) { + itemData.setString(BOOK_TITLE.NBT, this.title); + } + + if (hasAuthor()) { + itemData.setString(BOOK_AUTHOR.NBT, this.author); + } + + if (handlePages) { + if (hasPages()) { + NBTTagList list = new NBTTagList(); + for (IChatBaseComponent page : pages) { + list.add(new NBTTagString(CraftChatMessage.fromComponent(page))); + } + itemData.set(BOOK_PAGES.NBT, list); + } + + itemData.remove(RESOLVED.NBT); + } + + if (generation != null) { + itemData.setInt(GENERATION.NBT, generation); + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isBookEmpty(); + } + + boolean isBookEmpty() { + return !(hasPages() || hasAuthor() || hasTitle()); + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case WRITTEN_BOOK: + case WRITABLE_BOOK: + return true; + default: + return false; + } + } + + public boolean hasAuthor() { + return !Strings.isNullOrEmpty(author); + } + + public boolean hasTitle() { + return !Strings.isNullOrEmpty(title); + } + + public boolean hasPages() { + return !pages.isEmpty(); + } + + public boolean hasGeneration() { + return generation != null; + } + + public String getTitle() { + return this.title; + } + + public boolean setTitle(final String title) { + if (title == null) { + this.title = null; + return true; + } else if (title.length() > MAX_TITLE_LENGTH && !OVERRIDE_CHECKS) { // Paper - Add override + return false; + } + + this.title = title; + return true; + } + + public String getAuthor() { + return this.author; + } + + public void setAuthor(final String author) { + this.author = author; + } + + @Override + public Generation getGeneration() { + return (generation == null) ? null : Generation.values()[generation]; + } + + @Override + public void setGeneration(Generation generation) { + this.generation = (generation == null) ? null : generation.ordinal(); + } + + public String getPage(final int page) { + Validate.isTrue(isValidPage(page), "Invalid page number"); + return CraftChatMessage.fromComponent(pages.get(page - 1)); + } + + public void setPage(final int page, final String text) { + if (!isValidPage(page)) { + throw new IllegalArgumentException("Invalid page number " + page + "/" + pages.size()); + } + + String newText = text == null ? "" : text.length() > MAX_PAGE_LENGTH && !OVERRIDE_CHECKS ? text.substring(0, MAX_PAGE_LENGTH) : text; + pages.set(page - 1, CraftChatMessage.fromString(newText, true)[0]); + } + + public void setPages(final String... pages) { + this.pages.clear(); + + addPage(pages); + } + + public void addPage(final String... pages) { + for (String page : pages) { + if (this.pages.size() >= MAX_PAGES && !OVERRIDE_CHECKS) { + return; + } + + if (page == null) { + page = ""; + } else if (page.length() > MAX_PAGE_LENGTH && !OVERRIDE_CHECKS) { // Paper - Add override + page = page.substring(0, MAX_PAGE_LENGTH); + } + + this.pages.add(CraftChatMessage.fromString(page, true)[0]); + } + } + + public int getPageCount() { + return pages.size(); + } + + public List getPages() { + return pages.stream().map(CraftChatMessage::fromComponent).collect(ImmutableList.toImmutableList()); + } + + public void setPages(List pages) { + this.pages.clear(); + for (String page : pages) { + addPage(page); + } + } + + private boolean isValidPage(int page) { + return page > 0 && page <= pages.size(); + } + + @Override + public CraftMetaBook clone() { + CraftMetaBook meta = (CraftMetaBook) super.clone(); + meta.pages = new ArrayList(pages); + return meta; + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + if (hasTitle()) { + hash = 61 * hash + this.title.hashCode(); + } + if (hasAuthor()) { + hash = 61 * hash + 13 * this.author.hashCode(); + } + if (hasPages()) { + hash = 61 * hash + 17 * this.pages.hashCode(); + } + if (hasGeneration()) { + hash = 61 * hash + 19 * this.generation.hashCode(); + } + return original != hash ? CraftMetaBook.class.hashCode() ^ hash : hash; + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaBook) { + CraftMetaBook that = (CraftMetaBook) meta; + + return (hasTitle() ? that.hasTitle() && this.title.equals(that.title) : !that.hasTitle()) + && (hasAuthor() ? that.hasAuthor() && this.author.equals(that.author) : !that.hasAuthor()) + && (hasPages() ? that.hasPages() && this.pages.equals(that.pages) : !that.hasPages()) + && (hasGeneration() ? that.hasGeneration() && this.generation.equals(that.generation) : !that.hasGeneration()); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaBook || isBookEmpty()); + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + + if (hasTitle()) { + builder.put(BOOK_TITLE.BUKKIT, title); + } + + if (hasAuthor()) { + builder.put(BOOK_AUTHOR.BUKKIT, author); + } + + if (hasPages()) { + List pagesString = new ArrayList(); + for (IChatBaseComponent comp : pages) { + pagesString.add(CraftChatMessage.fromComponent(comp)); + } + builder.put(BOOK_PAGES.BUKKIT, pagesString); + } + + if (generation != null) { + builder.put(GENERATION.BUKKIT, generation); + } + + return builder; + } + + // Spigot start + private final BookMeta.Spigot spigot = new BookMeta.Spigot() { + + @Override + public BaseComponent[] getPage(final int page) { + Validate.isTrue(isValidPage(page), "Invalid page number"); + return ComponentSerializer.parse(IChatBaseComponent.ChatSerializer.a(pages.get(page - 1))); + } + + @Override + public void setPage(final int page, final BaseComponent... text) { + if (!isValidPage(page)) { + throw new IllegalArgumentException("Invalid page number " + page + "/" + pages.size()); + } + + BaseComponent[] newText = text == null ? new BaseComponent[0] : text; + CraftMetaBook.this.pages.set(page - 1, IChatBaseComponent.ChatSerializer.a(ComponentSerializer.toString(newText))); + } + + @Override + public void setPages(final BaseComponent[]... pages) { + CraftMetaBook.this.pages.clear(); + + addPage(pages); + } + + @Override + public void addPage(final BaseComponent[]... pages) { + for (BaseComponent[] page : pages) { + if (CraftMetaBook.this.pages.size() >= MAX_PAGES) { + return; + } + + if (page == null) { + page = new BaseComponent[0]; + } + + CraftMetaBook.this.pages.add(IChatBaseComponent.ChatSerializer.a(ComponentSerializer.toString(page))); + } + } + + @Override + public List getPages() { + final List copy = ImmutableList.copyOf(CraftMetaBook.this.pages); + return new AbstractList() { + + @Override + public BaseComponent[] get(int index) { + return ComponentSerializer.parse(IChatBaseComponent.ChatSerializer.a(copy.get(index))); + } + + @Override + public int size() { + return copy.size(); + } + }; + } + + @Override + public void setPages(List pages) { + CraftMetaBook.this.pages.clear(); + for (BaseComponent[] page : pages) { + addPage(page); + } + } + }; + + @Override + public BookMeta.Spigot spigot() { + return spigot; + } + // Spigot end +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java new file mode 100644 index 000000000000..5050b60991a8 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java @@ -0,0 +1,134 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagList; + +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.inventory.meta.BookMeta; + +import com.google.common.collect.ImmutableMap.Builder; +import net.minecraft.server.IChatBaseComponent.ChatSerializer; +import net.minecraft.server.IChatBaseComponent; +import net.minecraft.server.NBTTagString; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaBookSigned extends CraftMetaBook implements BookMeta { + + CraftMetaBookSigned(CraftMetaItem meta) { + super(meta); + } + + CraftMetaBookSigned(NBTTagCompound tag) { + super(tag, false); + + boolean resolved = true; + if (tag.hasKey(RESOLVED.NBT)) { + resolved = tag.getBoolean(RESOLVED.NBT); + } + + if (tag.hasKey(BOOK_PAGES.NBT)) { + NBTTagList pages = tag.getList(BOOK_PAGES.NBT, CraftMagicNumbers.NBT.TAG_STRING); + + for (int i = 0; i < Math.min(pages.size(), MAX_PAGES); i++) { + String page = pages.getString(i); + if (resolved) { + try { + this.pages.add(ChatSerializer.a(page)); + continue; + } catch (Exception e) { + // Ignore and treat as an old book + } + } + addPage(page); + } + } + } + + CraftMetaBookSigned(Map map) { + super(map); + } + + @Override + void applyToItem(NBTTagCompound itemData) { + super.applyToItem(itemData, false); + + if (hasTitle()) { + itemData.setString(BOOK_TITLE.NBT, this.title); + } else { + itemData.setString(BOOK_TITLE.NBT, " "); + } + + if (hasAuthor()) { + itemData.setString(BOOK_AUTHOR.NBT, this.author); + } else { + itemData.setString(BOOK_AUTHOR.NBT, " "); + } + + if (hasPages()) { + NBTTagList list = new NBTTagList(); + for (IChatBaseComponent page : pages) { + list.add(new NBTTagString( + ChatSerializer.a(page) + )); + } + itemData.set(BOOK_PAGES.NBT, list); + } + itemData.setBoolean(RESOLVED.NBT, true); + + if (generation != null) { + itemData.setInt(GENERATION.NBT, generation); + } else { + itemData.setInt(GENERATION.NBT, 0); + } + } + + @Override + boolean isEmpty() { + return super.isEmpty(); + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case WRITTEN_BOOK: + case WRITABLE_BOOK: + return true; + default: + return false; + } + } + + @Override + public CraftMetaBookSigned clone() { + CraftMetaBookSigned meta = (CraftMetaBookSigned) super.clone(); + return meta; + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + return original != hash ? CraftMetaBookSigned.class.hashCode() ^ hash : hash; + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + return super.equalsCommon(meta); + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaBookSigned || isBookEmpty()); + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + return builder; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java new file mode 100644 index 000000000000..267581ec2e67 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java @@ -0,0 +1,131 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; + +import org.bukkit.FireworkEffect; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.inventory.meta.FireworkEffectMeta; + +import com.google.common.collect.ImmutableMap.Builder; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaCharge extends CraftMetaItem implements FireworkEffectMeta { + static final ItemMetaKey EXPLOSION = new ItemMetaKey("Explosion", "firework-effect"); + + private FireworkEffect effect; + + CraftMetaCharge(CraftMetaItem meta) { + super(meta); + + if (meta instanceof CraftMetaCharge) { + effect = ((CraftMetaCharge) meta).effect; + } + } + + CraftMetaCharge(Map map) { + super(map); + + setEffect(SerializableMeta.getObject(FireworkEffect.class, map, EXPLOSION.BUKKIT, true)); + } + + CraftMetaCharge(NBTTagCompound tag) { + super(tag); + + if (tag.hasKey(EXPLOSION.NBT)) { + effect = CraftMetaFirework.getEffect(tag.getCompound(EXPLOSION.NBT)); + } + } + + @Override + public void setEffect(FireworkEffect effect) { + this.effect = effect; + } + + @Override + public boolean hasEffect() { + return effect != null; + } + + @Override + public FireworkEffect getEffect() { + return effect; + } + + @Override + void applyToItem(NBTTagCompound itemTag) { + super.applyToItem(itemTag); + + if (hasEffect()) { + itemTag.set(EXPLOSION.NBT, CraftMetaFirework.getExplosion(effect)); + } + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case FIREWORK_STAR: + return true; + default: + return false; + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && !hasChargeMeta(); + } + + boolean hasChargeMeta() { + return hasEffect(); + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaCharge) { + CraftMetaCharge that = (CraftMetaCharge) meta; + + return (hasEffect() ? that.hasEffect() && this.effect.equals(that.effect) : !that.hasEffect()); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaCharge || !hasChargeMeta()); + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + + if (hasEffect()) { + hash = 61 * hash + effect.hashCode(); + } + + return hash != original ? CraftMetaCharge.class.hashCode() ^ hash : hash; + } + + @Override + public CraftMetaCharge clone() { + return (CraftMetaCharge) super.clone(); + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + + if (hasEffect()) { + builder.put(EXPLOSION.BUKKIT, effect); + } + + return builder; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java new file mode 100644 index 000000000000..8d44e55e66e8 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java @@ -0,0 +1,168 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.HashMap; +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; + +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaEnchantedBook extends CraftMetaItem implements EnchantmentStorageMeta { + static final ItemMetaKey STORED_ENCHANTMENTS = new ItemMetaKey("StoredEnchantments", "stored-enchants"); + + private Map enchantments; + + CraftMetaEnchantedBook(CraftMetaItem meta) { + super(meta); + + if (!(meta instanceof CraftMetaEnchantedBook)) { + return; + } + + CraftMetaEnchantedBook that = (CraftMetaEnchantedBook) meta; + + if (that.hasEnchants()) { + this.enchantments = new HashMap(that.enchantments); + } + } + + CraftMetaEnchantedBook(NBTTagCompound tag) { + super(tag); + + if (!tag.hasKey(STORED_ENCHANTMENTS.NBT)) { + return; + } + + enchantments = buildEnchantments(tag, STORED_ENCHANTMENTS); + } + + CraftMetaEnchantedBook(Map map) { + super(map); + + enchantments = buildEnchantments(map, STORED_ENCHANTMENTS); + } + + @Override + void applyToItem(NBTTagCompound itemTag) { + super.applyToItem(itemTag); + + applyEnchantments(enchantments, itemTag, STORED_ENCHANTMENTS); + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case ENCHANTED_BOOK: + return true; + default: + return false; + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isEnchantedEmpty(); + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaEnchantedBook) { + CraftMetaEnchantedBook that = (CraftMetaEnchantedBook) meta; + + return (hasStoredEnchants() ? that.hasStoredEnchants() && this.enchantments.equals(that.enchantments) : !that.hasStoredEnchants()); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaEnchantedBook || isEnchantedEmpty()); + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + + if (hasStoredEnchants()) { + hash = 61 * hash + enchantments.hashCode(); + } + + return original != hash ? CraftMetaEnchantedBook.class.hashCode() ^ hash : hash; + } + + @Override + public CraftMetaEnchantedBook clone() { + CraftMetaEnchantedBook meta = (CraftMetaEnchantedBook) super.clone(); + + if (this.enchantments != null) { + meta.enchantments = new HashMap(this.enchantments); + } + + return meta; + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + + serializeEnchantments(enchantments, builder, STORED_ENCHANTMENTS); + + return builder; + } + + boolean isEnchantedEmpty() { + return !hasStoredEnchants(); + } + + public boolean hasStoredEnchant(Enchantment ench) { + return hasStoredEnchants() && enchantments.containsKey(ench); + } + + public int getStoredEnchantLevel(Enchantment ench) { + Integer level = hasStoredEnchants() ? enchantments.get(ench) : null; + if (level == null) { + return 0; + } + return level; + } + + public Map getStoredEnchants() { + return hasStoredEnchants() ? ImmutableMap.copyOf(enchantments) : ImmutableMap.of(); + } + + public boolean addStoredEnchant(Enchantment ench, int level, boolean ignoreRestrictions) { + if (enchantments == null) { + enchantments = new HashMap(4); + } + + if (ignoreRestrictions || level >= ench.getStartLevel() && level <= ench.getMaxLevel()) { + Integer old = enchantments.put(ench, level); + return old == null || old != level; + } + return false; + } + + public boolean removeStoredEnchant(Enchantment ench) { + return hasStoredEnchants() && enchantments.remove(ench) != null; + } + + public boolean hasStoredEnchants() { + return !(enchantments == null || enchantments.isEmpty()); + } + + public boolean hasConflictingStoredEnchant(Enchantment ench) { + return checkConflictingEnchants(enchantments, ench); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java new file mode 100644 index 000000000000..b82e2fdf7c1f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java @@ -0,0 +1,395 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagList; + +import org.apache.commons.lang.Validate; +import org.bukkit.Color; +import org.bukkit.FireworkEffect; +import org.bukkit.FireworkEffect.Type; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.ItemMetaKey.Specific; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.ItemMetaKey.Specific.To; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.inventory.meta.FireworkMeta; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap.Builder; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaFirework extends CraftMetaItem implements FireworkMeta { + /* + "Fireworks", "Explosion", "Explosions", "Flight", "Type", "Trail", "Flicker", "Colors", "FadeColors"; + + Fireworks + - Compound: Fireworks + -- Byte: Flight + -- List: Explosions + --- Compound: Explosion + ---- IntArray: Colors + ---- Byte: Type + ---- Boolean: Trail + ---- Boolean: Flicker + ---- IntArray: FadeColors + */ + + @Specific(To.NBT) + static final ItemMetaKey FIREWORKS = new ItemMetaKey("Fireworks"); + static final ItemMetaKey FLIGHT = new ItemMetaKey("Flight", "power"); + static final ItemMetaKey EXPLOSIONS = new ItemMetaKey("Explosions", "firework-effects"); + @Specific(To.NBT) + static final ItemMetaKey EXPLOSION_COLORS = new ItemMetaKey("Colors"); + @Specific(To.NBT) + static final ItemMetaKey EXPLOSION_TYPE = new ItemMetaKey("Type"); + @Specific(To.NBT) + static final ItemMetaKey EXPLOSION_TRAIL = new ItemMetaKey("Trail"); + @Specific(To.NBT) + static final ItemMetaKey EXPLOSION_FLICKER = new ItemMetaKey("Flicker"); + @Specific(To.NBT) + static final ItemMetaKey EXPLOSION_FADE = new ItemMetaKey("FadeColors"); + + private List effects; + private int power; + + CraftMetaFirework(CraftMetaItem meta) { + super(meta); + + if (!(meta instanceof CraftMetaFirework)) { + return; + } + + CraftMetaFirework that = (CraftMetaFirework) meta; + + this.power = that.power; + + if (that.hasEffects()) { + this.effects = new ArrayList(that.effects); + } + } + + CraftMetaFirework(NBTTagCompound tag) { + super(tag); + + if (!tag.hasKey(FIREWORKS.NBT)) { + return; + } + + NBTTagCompound fireworks = tag.getCompound(FIREWORKS.NBT); + + power = 0xff & fireworks.getByte(FLIGHT.NBT); + + if (!fireworks.hasKey(EXPLOSIONS.NBT)) { + return; + } + + NBTTagList fireworkEffects = fireworks.getList(EXPLOSIONS.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND); + List effects = this.effects = new ArrayList(fireworkEffects.size()); + + for (int i = 0; i < fireworkEffects.size(); i++) { + effects.add(getEffect((NBTTagCompound) fireworkEffects.get(i))); + } + } + + static FireworkEffect getEffect(NBTTagCompound explosion) { + FireworkEffect.Builder effect = FireworkEffect.builder() + .flicker(explosion.getBoolean(EXPLOSION_FLICKER.NBT)) + .trail(explosion.getBoolean(EXPLOSION_TRAIL.NBT)) + .with(getEffectType(0xff & explosion.getByte(EXPLOSION_TYPE.NBT))); + + int[] colors = explosion.getIntArray(EXPLOSION_COLORS.NBT); + // People using buggy command generators specify a list rather than an int here, so recover with dummy data. + // Wrong: Colors: [1234] + // Right: Colors: [I;1234] + if (colors.length == 0) { + effect.withColor(Color.WHITE); + } + + for (int color : colors) { + effect.withColor(Color.fromRGB(color)); + } + + for (int color : explosion.getIntArray(EXPLOSION_FADE.NBT)) { + effect.withFade(Color.fromRGB(color)); + } + + return effect.build(); + } + + static NBTTagCompound getExplosion(FireworkEffect effect) { + NBTTagCompound explosion = new NBTTagCompound(); + + if (effect.hasFlicker()) { + explosion.setBoolean(EXPLOSION_FLICKER.NBT, true); + } + + if (effect.hasTrail()) { + explosion.setBoolean(EXPLOSION_TRAIL.NBT, true); + } + + addColors(explosion, EXPLOSION_COLORS, effect.getColors()); + addColors(explosion, EXPLOSION_FADE, effect.getFadeColors()); + + explosion.setByte(EXPLOSION_TYPE.NBT, (byte) getNBT(effect.getType())); + + return explosion; + } + + static int getNBT(Type type) { + switch (type) { + case BALL: + return 0; + case BALL_LARGE: + return 1; + case STAR: + return 2; + case CREEPER: + return 3; + case BURST: + return 4; + default: + throw new IllegalArgumentException("Unknown effect type " + type); + } + } + + static Type getEffectType(int nbt) { + switch (nbt) { + case 0: + return Type.BALL; + case 1: + return Type.BALL_LARGE; + case 2: + return Type.STAR; + case 3: + return Type.CREEPER; + case 4: + return Type.BURST; + default: + throw new IllegalArgumentException("Unknown effect type " + nbt); + } + } + + CraftMetaFirework(Map map) { + super(map); + + Integer power = SerializableMeta.getObject(Integer.class, map, FLIGHT.BUKKIT, true); + if (power != null) { + setPower(power); + } + + Iterable effects = SerializableMeta.getObject(Iterable.class, map, EXPLOSIONS.BUKKIT, true); + safelyAddEffects(effects); + } + + public boolean hasEffects() { + return !(effects == null || effects.isEmpty()); + } + + void safelyAddEffects(Iterable collection) { + if (collection == null || (collection instanceof Collection && ((Collection) collection).isEmpty())) { + return; + } + + List effects = this.effects; + if (effects == null) { + effects = this.effects = new ArrayList(); + } + + for (Object obj : collection) { + if (obj instanceof FireworkEffect) { + effects.add((FireworkEffect) obj); + } else { + throw new IllegalArgumentException(obj + " in " + collection + " is not a FireworkEffect"); + } + } + } + + @Override + void applyToItem(NBTTagCompound itemTag) { + super.applyToItem(itemTag); + if (isFireworkEmpty()) { + return; + } + + NBTTagCompound fireworks = itemTag.getCompound(FIREWORKS.NBT); + itemTag.set(FIREWORKS.NBT, fireworks); + + if (hasEffects()) { + NBTTagList effects = new NBTTagList(); + for (FireworkEffect effect : this.effects) { + effects.add(getExplosion(effect)); + } + + if (effects.size() > 0) { + fireworks.set(EXPLOSIONS.NBT, effects); + } + } + + if (hasPower()) { + fireworks.setByte(FLIGHT.NBT, (byte) power); + } + } + + static void addColors(NBTTagCompound compound, ItemMetaKey key, List colors) { + if (colors.isEmpty()) { + return; + } + + final int[] colorArray = new int[colors.size()]; + int i = 0; + for (Color color : colors) { + colorArray[i++] = color.asRGB(); + } + + compound.setIntArray(key.NBT, colorArray); + } + + @Override + boolean applicableTo(Material type) { + switch(type) { + case FIREWORK_ROCKET: + return true; + default: + return false; + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isFireworkEmpty(); + } + + boolean isFireworkEmpty() { + return !(hasEffects() || hasPower()); + } + + boolean hasPower() { + return power != 0; + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + + if (meta instanceof CraftMetaFirework) { + CraftMetaFirework that = (CraftMetaFirework) meta; + + return (hasPower() ? that.hasPower() && this.power == that.power : !that.hasPower()) + && (hasEffects() ? that.hasEffects() && this.effects.equals(that.effects) : !that.hasEffects()); + } + + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaFirework || isFireworkEmpty()); + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + if (hasPower()) { + hash = 61 * hash + power; + } + if (hasEffects()) { + hash = 61 * hash + 13 * effects.hashCode(); + } + return hash != original ? CraftMetaFirework.class.hashCode() ^ hash : hash; + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + + if (hasEffects()) { + builder.put(EXPLOSIONS.BUKKIT, ImmutableList.copyOf(effects)); + } + + if (hasPower()) { + builder.put(FLIGHT.BUKKIT, power); + } + + return builder; + } + + @Override + public CraftMetaFirework clone() { + CraftMetaFirework meta = (CraftMetaFirework) super.clone(); + + if (this.effects != null) { + meta.effects = new ArrayList(this.effects); + } + + return meta; + } + + public void addEffect(FireworkEffect effect) { + Validate.notNull(effect, "Effect cannot be null"); + if (this.effects == null) { + this.effects = new ArrayList(); + } + this.effects.add(effect); + } + + public void addEffects(FireworkEffect...effects) { + Validate.notNull(effects, "Effects cannot be null"); + if (effects.length == 0) { + return; + } + + List list = this.effects; + if (list == null) { + list = this.effects = new ArrayList(); + } + + for (FireworkEffect effect : effects) { + Validate.notNull(effect, "Effect cannot be null"); + list.add(effect); + } + } + + public void addEffects(Iterable effects) { + Validate.notNull(effects, "Effects cannot be null"); + safelyAddEffects(effects); + } + + public List getEffects() { + return this.effects == null ? ImmutableList.of() : ImmutableList.copyOf(this.effects); + } + + public int getEffectsSize() { + return this.effects == null ? 0 : this.effects.size(); + } + + public void removeEffect(int index) { + if (this.effects == null) { + throw new IndexOutOfBoundsException("Index: " + index + ", Size: 0"); + } else { + this.effects.remove(index); + } + } + + public void clearEffects() { + this.effects = null; + } + + public int getPower() { + return this.power; + } + + public void setPower(int power) { + Validate.isTrue(power >= 0, "Power cannot be less than zero: ", power); + Validate.isTrue(power < 0x80, "Power cannot be more than 127: ", power); + this.power = power; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java new file mode 100644 index 000000000000..be2e736eb39a --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java @@ -0,0 +1,1622 @@ +package org.bukkit.craftbukkit.inventory; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.NoSuchElementException; + +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.SetMultimap; +import net.minecraft.server.EnumItemSlot; +import net.minecraft.server.GenericAttributes; +import net.minecraft.server.IChatBaseComponent; +import com.google.common.collect.ImmutableSortedMap; +import net.minecraft.server.NBTBase; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagList; +import net.minecraft.server.NBTTagString; + +import org.apache.commons.lang.Validate; +import org.apache.commons.lang3.EnumUtils; +import org.bukkit.Material; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeModifier; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.configuration.serialization.SerializableAs; +import org.bukkit.craftbukkit.CraftEquipmentSlot; +import org.bukkit.craftbukkit.Overridden; +import org.bukkit.craftbukkit.attribute.CraftAttributeInstance; +import org.bukkit.craftbukkit.attribute.CraftAttributeMap; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.ItemMetaKey.Specific; +import org.bukkit.craftbukkit.inventory.tags.CraftCustomItemTagContainer; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.craftbukkit.util.CraftNBTTagConfigSerializer; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.meta.Damageable; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.Repairable; +import org.bukkit.inventory.meta.tags.CustomItemTagContainer; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; +import com.google.gson.JsonParseException; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.Set; +import java.util.TreeMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import net.minecraft.server.EnumChatFormat; +import net.minecraft.server.NBTCompressedStreamTools; +import org.apache.commons.codec.binary.Base64; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +// Spigot start +import static org.spigotmc.ValidateUtils.*; +// Spigot end + +// Paper start +import com.destroystokyo.paper.Namespaced; +import com.destroystokyo.paper.NamespacedTag; +import java.util.Collections; +// Paper end + +/** + * Children must include the following: + * + *
  • Constructor(CraftMetaItem meta) + *
  • Constructor(NBTTagCompound tag) + *
  • Constructor(Map map) + *

    + *
  • void applyToItem(NBTTagCompound tag) + *
  • boolean applicableTo(Material type) + *

    + *
  • boolean equalsCommon(CraftMetaItem meta) + *
  • boolean notUncommon(CraftMetaItem meta) + *

    + *
  • boolean isEmpty() + *
  • boolean is{Type}Empty() + *

    + *
  • int applyHash() + *
  • public Class clone() + *

    + *
  • Builder serialize(Builder builder) + *
  • SerializableMeta.Deserializers deserializer() + */ +@DelegateDeserialization(CraftMetaItem.SerializableMeta.class) +class CraftMetaItem implements ItemMeta, Damageable, Repairable { + + static class ItemMetaKey { + + @Retention(RetentionPolicy.SOURCE) + @Target(ElementType.FIELD) + @interface Specific { + enum To { + BUKKIT, + NBT, + ; + } + To value(); + } + + final String BUKKIT; + final String NBT; + + ItemMetaKey(final String both) { + this(both, both); + } + + ItemMetaKey(final String nbt, final String bukkit) { + this.NBT = nbt; + this.BUKKIT = bukkit; + } + } + + @SerializableAs("ItemMeta") + public static class SerializableMeta implements ConfigurationSerializable { + static final String TYPE_FIELD = "meta-type"; + + static final ImmutableMap, String> classMap; + static final ImmutableMap> constructorMap; + + static { + classMap = ImmutableMap., String>builder() + .put(CraftMetaBanner.class, "BANNER") + .put(CraftMetaBlockState.class, "TILE_ENTITY") + .put(CraftMetaBook.class, "BOOK") + .put(CraftMetaBookSigned.class, "BOOK_SIGNED") + .put(CraftMetaSkull.class, "SKULL") + .put(CraftMetaLeatherArmor.class, "LEATHER_ARMOR") + .put(CraftMetaMap.class, "MAP") + .put(CraftMetaPotion.class, "POTION") + .put(CraftMetaSpawnEgg.class, "SPAWN_EGG") + .put(CraftMetaEnchantedBook.class, "ENCHANTED") + .put(CraftMetaFirework.class, "FIREWORK") + .put(CraftMetaCharge.class, "FIREWORK_EFFECT") + .put(CraftMetaKnowledgeBook.class, "KNOWLEDGE_BOOK") + .put(CraftMetaTropicalFishBucket.class, "TROPICAL_FISH_BUCKET") + .put(CraftMetaArmorStand.class, "ARMOR_STAND") + .put(CraftMetaItem.class, "UNSPECIFIC") + .build(); + + final ImmutableMap.Builder> classConstructorBuilder = ImmutableMap.builder(); + for (Map.Entry, String> mapping : classMap.entrySet()) { + try { + classConstructorBuilder.put(mapping.getValue(), mapping.getKey().getDeclaredConstructor(Map.class)); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + constructorMap = classConstructorBuilder.build(); + } + + private SerializableMeta() { + } + + public static ItemMeta deserialize(Map map) throws Throwable { + Validate.notNull(map, "Cannot deserialize null map"); + + String type = getString(map, TYPE_FIELD, false); + Constructor constructor = constructorMap.get(type); + + if (constructor == null) { + throw new IllegalArgumentException(type + " is not a valid " + TYPE_FIELD); + } + + try { + return constructor.newInstance(map); + } catch (final InstantiationException e) { + throw new AssertionError(e); + } catch (final IllegalAccessException e) { + throw new AssertionError(e); + } catch (final InvocationTargetException e) { + throw e.getCause(); + } + } + + public Map serialize() { + throw new AssertionError(); + } + + static String getString(Map map, Object field, boolean nullable) { + return getObject(String.class, map, field, nullable); + } + + static boolean getBoolean(Map map, Object field) { + Boolean value = getObject(Boolean.class, map, field, true); + return value != null && value; + } + + static T getObject(Class clazz, Map map, Object field, boolean nullable) { + final Object object = map.get(field); + + if (clazz.isInstance(object)) { + return clazz.cast(object); + } + if (object == null) { + if (!nullable) { + throw new NoSuchElementException(map + " does not contain " + field); + } + return null; + } + throw new IllegalArgumentException(field + "(" + object + ") is not a valid " + clazz); + } + } + + static final ItemMetaKey NAME = new ItemMetaKey("Name", "display-name"); + static final ItemMetaKey LOCNAME = new ItemMetaKey("LocName", "loc-name"); + @Specific(Specific.To.NBT) + static final ItemMetaKey DISPLAY = new ItemMetaKey("display"); + static final ItemMetaKey LORE = new ItemMetaKey("Lore", "lore"); + static final ItemMetaKey ENCHANTMENTS = new ItemMetaKey("Enchantments", "enchants"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ENCHANTMENTS_ID = new ItemMetaKey("id"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ENCHANTMENTS_LVL = new ItemMetaKey("lvl"); + static final ItemMetaKey REPAIR = new ItemMetaKey("RepairCost", "repair-cost"); + static final ItemMetaKey ATTRIBUTES = new ItemMetaKey("AttributeModifiers", "attribute-modifiers"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ATTRIBUTES_IDENTIFIER = new ItemMetaKey("AttributeName"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ATTRIBUTES_NAME = new ItemMetaKey("Name"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ATTRIBUTES_VALUE = new ItemMetaKey("Amount"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ATTRIBUTES_TYPE = new ItemMetaKey("Operation"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ATTRIBUTES_UUID_HIGH = new ItemMetaKey("UUIDMost"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ATTRIBUTES_UUID_LOW = new ItemMetaKey("UUIDLeast"); + @Specific(Specific.To.NBT) + static final ItemMetaKey ATTRIBUTES_SLOT = new ItemMetaKey("Slot"); + @Specific(Specific.To.NBT) + static final ItemMetaKey HIDEFLAGS = new ItemMetaKey("HideFlags", "ItemFlags"); + @Specific(Specific.To.NBT) + static final ItemMetaKey UNBREAKABLE = new ItemMetaKey("Unbreakable"); + @Specific(Specific.To.NBT) + static final ItemMetaKey DAMAGE = new ItemMetaKey("Damage"); + static final ItemMetaKey BUKKIT_CUSTOM_TAG = new ItemMetaKey("PublicBukkitValues"); + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values + static final ItemMetaKey CAN_DESTROY = new ItemMetaKey("CanDestroy"); + static final ItemMetaKey CAN_PLACE_ON = new ItemMetaKey("CanPlaceOn"); + // Paper end + + private IChatBaseComponent displayName; + private IChatBaseComponent locName; + private List lore; + private EnchantmentMap enchantments; // Paper + private Multimap attributeModifiers; + private int repairCost; + private int hideFlag; + private boolean unbreakable; + private int damage; + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values + private Set placeableKeys = Sets.newHashSet(); + private Set destroyableKeys = Sets.newHashSet(); + // Paper end + + private static final Set HANDLED_TAGS = Sets.newHashSet(); + private static final CraftCustomTagTypeRegistry TAG_TYPE_REGISTRY = new CraftCustomTagTypeRegistry(); + + private NBTTagCompound internalTag; + private final Map unhandledTags = new TreeMap<>(); // Paper + private final CraftCustomItemTagContainer publicItemTagContainer = new CraftCustomItemTagContainer(TAG_TYPE_REGISTRY); + + CraftMetaItem(CraftMetaItem meta) { + if (meta == null) { + return; + } + + this.displayName = meta.displayName; + this.locName = meta.locName; + + if (meta.hasLore()) { + this.lore = new ArrayList(meta.lore); + } + + if (meta.enchantments != null) { // Spigot + this.enchantments = new EnchantmentMap(meta.enchantments); // Paper + } + + if (meta.hasAttributeModifiers()) { + this.attributeModifiers = HashMultimap.create(meta.attributeModifiers); + } + + this.repairCost = meta.repairCost; + this.hideFlag = meta.hideFlag; + this.unbreakable = meta.unbreakable; + this.damage = meta.damage; + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values + if (meta.hasPlaceableKeys()) { + this.placeableKeys = new java.util.HashSet<>(meta.placeableKeys); + } + + if (meta.hasDestroyableKeys()) { + this.destroyableKeys = new java.util.HashSet<>(meta.destroyableKeys); + } + // Paper end + this.unhandledTags.putAll(meta.unhandledTags); + this.publicItemTagContainer.putAll(meta.publicItemTagContainer.getRaw()); + + this.internalTag = meta.internalTag; + if (this.internalTag != null) { + deserializeInternal(internalTag, meta); + } + } + + CraftMetaItem(NBTTagCompound tag) { + if (tag.hasKey(DISPLAY.NBT)) { + NBTTagCompound display = tag.getCompound(DISPLAY.NBT); + + if (display.hasKey(NAME.NBT)) { + try { + displayName = IChatBaseComponent.ChatSerializer.a( limit( display.getString(NAME.NBT), 1024 ) ); // Spigot + } catch (JsonParseException ex) { + // Ignore (stripped like Vanilla) + } + } + + if (display.hasKey(LOCNAME.NBT)) { + try { + locName = IChatBaseComponent.ChatSerializer.a( limit( display.getString(LOCNAME.NBT), 1024 ) ); // Spigot + } catch (JsonParseException ex) { + // Ignore (stripped like Vanilla) + } + } + + if (display.hasKey(LORE.NBT)) { + NBTTagList list = display.getList(LORE.NBT, CraftMagicNumbers.NBT.TAG_STRING); + lore = new ArrayList(list.size()); + + for (int index = 0; index < list.size(); index++) { + String line = limit( list.getString(index), 1024 ); // Spigot + lore.add(line); + } + } + } + + this.enchantments = buildEnchantments(tag, ENCHANTMENTS); + this.attributeModifiers = buildModifiers(tag, ATTRIBUTES); + + if (tag.hasKey(REPAIR.NBT)) { + repairCost = tag.getInt(REPAIR.NBT); + } + + if (tag.hasKey(HIDEFLAGS.NBT)) { + hideFlag = tag.getInt(HIDEFLAGS.NBT); + } + if (tag.hasKey(UNBREAKABLE.NBT)) { + unbreakable = tag.getBoolean(UNBREAKABLE.NBT); + } + if (tag.hasKey(DAMAGE.NBT)) { + damage = tag.getInt(DAMAGE.NBT); + } + if (tag.hasKey(BUKKIT_CUSTOM_TAG.NBT)) { + NBTTagCompound compound = tag.getCompound(BUKKIT_CUSTOM_TAG.NBT); + Set keys = compound.getKeys(); + for (String key : keys) { + publicItemTagContainer.put(key, compound.get(key)); + } + } + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values + if (tag.hasKey(CAN_DESTROY.NBT)) { + NBTTagList list = tag.getList(CAN_DESTROY.NBT, CraftMagicNumbers.NBT.TAG_STRING); + for (int i = 0; i < list.size(); i++) { + Namespaced namespaced = this.deserializeNamespaced(list.getString(i)); + if (namespaced == null) { + continue; + } + + this.destroyableKeys.add(namespaced); + } + } + + if (tag.hasKey(CAN_PLACE_ON.NBT)) { + NBTTagList list = tag.getList(CAN_PLACE_ON.NBT, CraftMagicNumbers.NBT.TAG_STRING); + for (int i = 0; i < list.size(); i++) { + Namespaced namespaced = this.deserializeNamespaced(list.getString(i)); + if (namespaced == null) { + continue; + } + + this.placeableKeys.add(namespaced); + } + } + // Paper end + + Set keys = tag.getKeys(); + for (String key : keys) { + if (!getHandledTags().contains(key)) { + unhandledTags.put(key, tag.get(key)); + } + } + } + + static EnchantmentMap buildEnchantments(NBTTagCompound tag, ItemMetaKey key) { // Paper + if (!tag.hasKey(key.NBT)) { + return null; + } + + NBTTagList ench = tag.getList(key.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND); + EnchantmentMap enchantments = new EnchantmentMap(); // Paper + + for (int i = 0; i < ench.size(); i++) { + String id = ((NBTTagCompound) ench.get(i)).getString(ENCHANTMENTS_ID.NBT); + int level = 0xffff & ((NBTTagCompound) ench.get(i)).getShort(ENCHANTMENTS_LVL.NBT); + + Enchantment enchant = Enchantment.getByKey(CraftNamespacedKey.fromStringOrNull(id)); + if (enchant != null) { + enchantments.put(enchant, level); + } + } + + return enchantments; + } + + static Multimap buildModifiers(NBTTagCompound tag, ItemMetaKey key) { + Multimap modifiers = HashMultimap.create(); + if (!tag.hasKeyOfType(key.NBT, CraftMagicNumbers.NBT.TAG_LIST)) { + return modifiers; + } + NBTTagList mods = tag.getList(key.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND); + int size = mods.size(); + + for (int i = 0; i < size; i++) { + NBTTagCompound entry = mods.getCompound(i); + if (entry.isEmpty()) { + // entry is not an actual NBTTagCompound. getCompound returns empty NBTTagCompound in that case + continue; + } + net.minecraft.server.AttributeModifier nmsModifier = GenericAttributes.a(entry); + if (nmsModifier == null) { + continue; + } + + AttributeModifier attribMod = CraftAttributeInstance.convert(nmsModifier); + + String attributeName = entry.getString(ATTRIBUTES_IDENTIFIER.NBT); + if (attributeName == null || attributeName.isEmpty()) { + continue; + } + + Attribute attribute = CraftAttributeMap.fromMinecraft(attributeName); + if (attribute == null) { + continue; + } + + if (entry.hasKeyOfType(ATTRIBUTES_SLOT.NBT, CraftMagicNumbers.NBT.TAG_STRING)) { + String slotName = entry.getString(ATTRIBUTES_SLOT.NBT); + if (slotName == null || slotName.isEmpty()) { + modifiers.put(attribute, attribMod); + continue; + } + + EquipmentSlot slot = null; + try { + slot = CraftEquipmentSlot.getSlot(EnumItemSlot.fromName(slotName.toLowerCase(Locale.ROOT))); + } catch (IllegalArgumentException ex) { + // SPIGOT-4551 - Slot is invalid, should really match nothing but this is undefined behaviour anyway + } + + if (slot == null) { + modifiers.put(attribute, attribMod); + continue; + } + + attribMod = new AttributeModifier(attribMod.getUniqueId(), attribMod.getName(), attribMod.getAmount(), attribMod.getOperation(), slot); + } + modifiers.put(attribute, attribMod); + } + return modifiers; + } + + CraftMetaItem(Map map) { + setDisplayName(SerializableMeta.getString(map, NAME.BUKKIT, true)); + setLocalizedName(SerializableMeta.getString(map, LOCNAME.BUKKIT, true)); + + Iterable lore = SerializableMeta.getObject(Iterable.class, map, LORE.BUKKIT, true); + if (lore != null) { + safelyAdd(lore, this.lore = new ArrayList(), Integer.MAX_VALUE); + } + + enchantments = buildEnchantments(map, ENCHANTMENTS); + attributeModifiers = buildModifiers(map, ATTRIBUTES); + + Integer repairCost = SerializableMeta.getObject(Integer.class, map, REPAIR.BUKKIT, true); + if (repairCost != null) { + setRepairCost(repairCost); + } + + Iterable hideFlags = SerializableMeta.getObject(Iterable.class, map, HIDEFLAGS.BUKKIT, true); + if (hideFlags != null) { + for (Object hideFlagObject : hideFlags) { + String hideFlagString = (String) hideFlagObject; + try { + ItemFlag hideFlatEnum = ItemFlag.valueOf(hideFlagString); + addItemFlags(hideFlatEnum); + } catch (IllegalArgumentException ex) { + // Ignore when we got a old String which does not map to a Enum value anymore + } + } + } + + Boolean unbreakable = SerializableMeta.getObject(Boolean.class, map, UNBREAKABLE.BUKKIT, true); + if (unbreakable != null) { + setUnbreakable(unbreakable); + } + + Integer damage = SerializableMeta.getObject(Integer.class, map, DAMAGE.BUKKIT, true); + if (damage != null) { + setDamage(damage); + } + + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values + Iterable canPlaceOnSerialized = SerializableMeta.getObject(Iterable.class, map, CAN_PLACE_ON.BUKKIT, true); + if (canPlaceOnSerialized != null) { + for (Object canPlaceOnElement : canPlaceOnSerialized) { + String canPlaceOnRaw = (String) canPlaceOnElement; + Namespaced value = this.deserializeNamespaced(canPlaceOnRaw); + if (value == null) { + continue; + } + + this.placeableKeys.add(value); + } + } + + Iterable canDestroySerialized = SerializableMeta.getObject(Iterable.class, map, CAN_DESTROY.BUKKIT, true); + if (canDestroySerialized != null) { + for (Object canDestroyElement : canDestroySerialized) { + String canDestroyRaw = (String) canDestroyElement; + Namespaced value = this.deserializeNamespaced(canDestroyRaw); + if (value == null) { + continue; + } + + this.destroyableKeys.add(value); + } + } + // Paper end + + String internal = SerializableMeta.getString(map, "internal", true); + if (internal != null) { + ByteArrayInputStream buf = new ByteArrayInputStream(Base64.decodeBase64(internal)); + try { + internalTag = NBTCompressedStreamTools.a(buf); + deserializeInternal(internalTag, map); + Set keys = internalTag.getKeys(); + for (String key : keys) { + if (!getHandledTags().contains(key)) { + unhandledTags.put(key, internalTag.get(key)); + } + } + } catch (IOException ex) { + Logger.getLogger(CraftMetaItem.class.getName()).log(Level.SEVERE, null, ex); + } + } + + Map nbtMap = SerializableMeta.getObject(Map.class, map, BUKKIT_CUSTOM_TAG.BUKKIT, true); + if (nbtMap != null) { + this.publicItemTagContainer.putAll((NBTTagCompound) CraftNBTTagConfigSerializer.deserialize(nbtMap)); + } + } + + void deserializeInternal(NBTTagCompound tag, Object context) { + // SPIGOT-4576: Need to migrate from internal to proper data + if (tag.hasKeyOfType(ATTRIBUTES.NBT, CraftMagicNumbers.NBT.TAG_LIST)) { + this.attributeModifiers = buildModifiers(tag, ATTRIBUTES); + } + } + + static EnchantmentMap buildEnchantments(Map map, ItemMetaKey key) { // Paper + Map ench = SerializableMeta.getObject(Map.class, map, key.BUKKIT, true); + if (ench == null) { + return null; + } + + EnchantmentMap enchantments = new EnchantmentMap(); // Paper + for (Map.Entry entry : ench.entrySet()) { + // Doctor older enchants + String enchantKey = entry.getKey().toString(); + if (enchantKey.equals("SWEEPING")) { + enchantKey = "SWEEPING_EDGE"; + } + + Enchantment enchantment = Enchantment.getByName(enchantKey); + if ((enchantment != null) && (entry.getValue() instanceof Integer)) { + enchantments.put(enchantment, (Integer) entry.getValue()); + } + } + + return enchantments; + } + + static Multimap buildModifiers(Map map, ItemMetaKey key) { + Map mods = SerializableMeta.getObject(Map.class, map, key.BUKKIT, true); + Multimap result = HashMultimap.create(); + if (mods == null) { + return result; + } + + for (Object obj : mods.keySet()) { + if (!(obj instanceof String)) { + continue; + } + String attributeName = (String) obj; + if (Strings.isNullOrEmpty(attributeName)) { + continue; + } + List list = SerializableMeta.getObject(List.class, mods, attributeName, true); + if (list == null || list.isEmpty()) { + return result; + } + + for (Object o : list) { + if (!(o instanceof AttributeModifier)) { // this catches null + continue; + } + AttributeModifier modifier = (AttributeModifier) o; + Attribute attribute = EnumUtils.getEnum(Attribute.class, attributeName.toUpperCase(Locale.ROOT)); + if (attribute == null) { + continue; + } + + result.put(attribute, modifier); + } + } + return result; + } + + @Overridden + void applyToItem(NBTTagCompound itemTag) { + if (hasDisplayName()) { + setDisplayTag(itemTag, NAME.NBT, new NBTTagString(CraftChatMessage.toJSON(displayName))); + } + if (hasLocalizedName()){ + setDisplayTag(itemTag, LOCNAME.NBT, new NBTTagString(CraftChatMessage.toJSON(locName))); + } + + if (hasLore()) { + setDisplayTag(itemTag, LORE.NBT, createStringList(lore)); + } + + if (hideFlag != 0) { + itemTag.setInt(HIDEFLAGS.NBT, hideFlag); + } + + applyEnchantments(enchantments, itemTag, ENCHANTMENTS); + applyModifiers(attributeModifiers, itemTag, ATTRIBUTES); + + if (hasRepairCost()) { + itemTag.setInt(REPAIR.NBT, repairCost); + } + + if (isUnbreakable()) { + itemTag.setBoolean(UNBREAKABLE.NBT, unbreakable); + } + + if (hasDamage()) { + itemTag.setInt(DAMAGE.NBT, damage); + } + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values + if (hasPlaceableKeys()) { + List items = this.placeableKeys.stream() + .map(this::serializeNamespaced) + .collect(java.util.stream.Collectors.toList()); + + itemTag.set(CAN_PLACE_ON.NBT, createStringList(items)); + } + + if (hasDestroyableKeys()) { + List items = this.destroyableKeys.stream() + .map(this::serializeNamespaced) + .collect(java.util.stream.Collectors.toList()); + + itemTag.set(CAN_DESTROY.NBT, createStringList(items)); + } + // Paper end + + for (Map.Entry e : unhandledTags.entrySet()) { + itemTag.set(e.getKey(), e.getValue()); + } + + if (!publicItemTagContainer.isEmpty()) { + NBTTagCompound bukkitCustomCompound = new NBTTagCompound(); + Map rawPublicMap = publicItemTagContainer.getRaw(); + + for (Map.Entry nbtBaseEntry : rawPublicMap.entrySet()) { + bukkitCustomCompound.set(nbtBaseEntry.getKey(), nbtBaseEntry.getValue()); + } + itemTag.set(BUKKIT_CUSTOM_TAG.NBT, bukkitCustomCompound); + } + } + + static NBTTagList createStringList(List list) { + if (list == null || list.isEmpty()) { + return null; + } + + NBTTagList tagList = new NBTTagList(); + for (String value : list) { + tagList.add(new NBTTagString(value)); + } + + return tagList; + } + + static void applyEnchantments(Map enchantments, NBTTagCompound tag, ItemMetaKey key) { + if (enchantments == null /*|| enchantments.size() == 0*/) { // Spigot - remove size check + return; + } + + NBTTagList list = new NBTTagList(); + + for (Map.Entry entry : enchantments.entrySet()) { + NBTTagCompound subtag = new NBTTagCompound(); + + subtag.setString(ENCHANTMENTS_ID.NBT, entry.getKey().getKey().toString()); + subtag.setShort(ENCHANTMENTS_LVL.NBT, entry.getValue().shortValue()); + + list.add(subtag); + } + + tag.set(key.NBT, list); + } + + static void applyModifiers(Multimap modifiers, NBTTagCompound tag, ItemMetaKey key) { + if (modifiers == null || modifiers.isEmpty()) { + return; + } + + NBTTagList list = new NBTTagList(); + for (Map.Entry entry : modifiers.entries()) { + if (entry.getKey() == null || entry.getValue() == null) { + continue; + } + net.minecraft.server.AttributeModifier nmsModifier = CraftAttributeInstance.convert(entry.getValue()); + NBTTagCompound sub = GenericAttributes.a(nmsModifier); + if (sub.isEmpty()) { + continue; + } + + String name = CraftAttributeMap.toMinecraft(entry.getKey()); + if (name == null || name.isEmpty()) { + continue; + } + + sub.setString(ATTRIBUTES_IDENTIFIER.NBT, name); // Attribute Name + if (entry.getValue().getSlot() != null) { + EnumItemSlot slot = CraftEquipmentSlot.getNMS(entry.getValue().getSlot()); + if (slot != null) { + sub.setString(ATTRIBUTES_SLOT.NBT, slot.getSlotName()); + } + } + list.add(sub); + } + tag.set(key.NBT, list); + } + + void setDisplayTag(NBTTagCompound tag, String key, NBTBase value) { + final NBTTagCompound display = tag.getCompound(DISPLAY.NBT); + + if (!tag.hasKey(DISPLAY.NBT)) { + tag.set(DISPLAY.NBT, display); + } + + display.set(key, value); + } + + @Overridden + boolean applicableTo(Material type) { + return type != Material.AIR; + } + + @Overridden + boolean isEmpty() { + return !(hasDisplayName() || hasLocalizedName() || hasEnchants() || hasLore() || hasRepairCost() || !unhandledTags.isEmpty() || !publicItemTagContainer.isEmpty() || hideFlag != 0 || isUnbreakable() || hasDamage() || hasAttributeModifiers() + || hasPlaceableKeys() || hasDestroyableKeys()); // Paper - Implement an API for CanPlaceOn and CanDestroy NBT values + } + + public String getDisplayName() { + return CraftChatMessage.fromComponent(displayName, EnumChatFormat.WHITE); + } + + public final void setDisplayName(String name) { + this.displayName = CraftChatMessage.wrapOrNull(name); + } + + public boolean hasDisplayName() { + return displayName != null; + } + + @Override + public String getLocalizedName() { + return CraftChatMessage.fromComponent(locName, EnumChatFormat.WHITE); + } + + @Override + public void setLocalizedName(String name) { + this.locName = CraftChatMessage.wrapOrNull(name); + } + + @Override + public boolean hasLocalizedName() { + return locName != null; + } + + public boolean hasLore() { + return this.lore != null && !this.lore.isEmpty(); + } + + public boolean hasRepairCost() { + return repairCost > 0; + } + + public boolean hasEnchant(Enchantment ench) { + Validate.notNull(ench, "Enchantment cannot be null"); + return hasEnchants() && enchantments.containsKey(ench); + } + + public int getEnchantLevel(Enchantment ench) { + Validate.notNull(ench, "Enchantment cannot be null"); + Integer level = hasEnchants() ? enchantments.get(ench) : null; + if (level == null) { + return 0; + } + return level; + } + + public Map getEnchants() { + return hasEnchants() ? ImmutableSortedMap.copyOfSorted(enchantments) : ImmutableMap.of(); // Paper + } + + public boolean addEnchant(Enchantment ench, int level, boolean ignoreRestrictions) { + Validate.notNull(ench, "Enchantment cannot be null"); + if (enchantments == null) { + enchantments = new EnchantmentMap(); // Paper + } + + if (ignoreRestrictions || level >= ench.getStartLevel() && level <= ench.getMaxLevel()) { + Integer old = enchantments.put(ench, level); + return old == null || old != level; + } + return false; + } + + public boolean removeEnchant(Enchantment ench) { + Validate.notNull(ench, "Enchantment cannot be null"); + // Spigot start + boolean b = hasEnchants() && enchantments.remove( ench ) != null; + if ( enchantments != null && enchantments.isEmpty() ) + { + this.enchantments = null; + } + return b; + // Spigot end + } + + public boolean hasEnchants() { + return !(enchantments == null || enchantments.isEmpty()); + } + + public boolean hasConflictingEnchant(Enchantment ench) { + return checkConflictingEnchants(enchantments, ench); + } + + @Override + public void addItemFlags(ItemFlag... hideFlags) { + for (ItemFlag f : hideFlags) { + this.hideFlag |= getBitModifier(f); + } + } + + @Override + public void removeItemFlags(ItemFlag... hideFlags) { + for (ItemFlag f : hideFlags) { + this.hideFlag &= ~getBitModifier(f); + } + } + + @Override + public Set getItemFlags() { + Set currentFlags = EnumSet.noneOf(ItemFlag.class); + + for (ItemFlag f : ItemFlag.values()) { + if (hasItemFlag(f)) { + currentFlags.add(f); + } + } + + return currentFlags; + } + + @Override + public boolean hasItemFlag(ItemFlag flag) { + int bitModifier = getBitModifier(flag); + return (this.hideFlag & bitModifier) == bitModifier; + } + + private byte getBitModifier(ItemFlag hideFlag) { + return (byte) (1 << hideFlag.ordinal()); + } + + public List getLore() { + return this.lore == null ? null : new ArrayList(this.lore); + } + + public void setLore(List lore) { // too tired to think if .clone is better + if (lore == null) { + this.lore = null; + } else { + if (this.lore == null) { + safelyAdd(lore, this.lore = new ArrayList(lore.size()), Integer.MAX_VALUE); + } else { + this.lore.clear(); + safelyAdd(lore, this.lore, Integer.MAX_VALUE); + } + } + } + + public int getRepairCost() { + return repairCost; + } + + public void setRepairCost(int cost) { // TODO: Does this have limits? + repairCost = cost; + } + + @Override + public boolean isUnbreakable() { + return unbreakable; + } + + @Override + public void setUnbreakable(boolean unbreakable) { + this.unbreakable = unbreakable; + } + + @Override + public boolean hasAttributeModifiers() { + return attributeModifiers != null && !attributeModifiers.isEmpty(); + } + + @Override + public Multimap getAttributeModifiers() { + return hasAttributeModifiers() ? ImmutableMultimap.copyOf(attributeModifiers) : null; + } + + private void checkAttributeList() { + if (attributeModifiers == null) { + attributeModifiers = HashMultimap.create(); + } + } + + @Override + public Multimap getAttributeModifiers(@Nullable EquipmentSlot slot) { + checkAttributeList(); + SetMultimap result = HashMultimap.create(); + for (Map.Entry entry : attributeModifiers.entries()) { + if (entry.getValue().getSlot() == null || entry.getValue().getSlot() == slot) { + result.put(entry.getKey(), entry.getValue()); + } + } + return result; + } + + @Override + public Collection getAttributeModifiers(@Nonnull Attribute attribute) { + Preconditions.checkNotNull(attribute, "Attribute cannot be null"); + return attributeModifiers.containsKey(attribute) ? ImmutableList.copyOf(attributeModifiers.get(attribute)) : null; + } + + @Override + public boolean addAttributeModifier(@Nonnull Attribute attribute, @Nonnull AttributeModifier modifier) { + Preconditions.checkNotNull(attribute, "Attribute cannot be null"); + Preconditions.checkNotNull(modifier, "AttributeModifier cannot be null"); + checkAttributeList(); + for (Map.Entry entry : attributeModifiers.entries()) { + Preconditions.checkArgument(!entry.getValue().getUniqueId().equals(modifier.getUniqueId()), "Cannot register AttributeModifier. Modifier is already applied! %s", modifier); + } + return attributeModifiers.put(attribute, modifier); + } + + @Override + public void setAttributeModifiers(@Nullable Multimap attributeModifiers) { + if (attributeModifiers == null || attributeModifiers.isEmpty()) { + this.attributeModifiers = HashMultimap.create(); + return; + } + Iterator> iterator = attributeModifiers.entries().iterator(); + this.attributeModifiers.clear(); + while (iterator.hasNext()) { + Map.Entry next = iterator.next(); + + if (next.getKey() == null || next.getValue() == null) { + iterator.remove(); + continue; + } + this.attributeModifiers.put(next.getKey(), next.getValue()); + } + } + + @Override + public boolean removeAttributeModifier(@Nonnull Attribute attribute) { + Preconditions.checkNotNull(attribute, "Attribute cannot be null"); + checkAttributeList(); + return !attributeModifiers.removeAll(attribute).isEmpty(); + } + + @Override + public boolean removeAttributeModifier(@Nullable EquipmentSlot slot) { + checkAttributeList(); + int removed = 0; + Iterator> iter = attributeModifiers.entries().iterator(); + + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + // Explicitly match against null because (as of MC 1.13) AttributeModifiers without a - + // set slot are active in any slot. + if (entry.getValue().getSlot() == null || entry.getValue().getSlot() == slot) { + iter.remove(); + ++removed; + } + } + return removed > 0; + } + + @Override + public boolean removeAttributeModifier(@Nonnull Attribute attribute, @Nonnull AttributeModifier modifier) { + Preconditions.checkNotNull(attribute, "Attribute cannot be null"); + Preconditions.checkNotNull(modifier, "AttributeModifier cannot be null"); + checkAttributeList(); + int removed = 0; + Iterator> iter = attributeModifiers.entries().iterator(); + + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + if (entry.getKey() == null || entry.getValue() == null) { + iter.remove(); + ++removed; + continue; // remove all null values while we are here + } + + if (entry.getKey() == attribute && entry.getValue().getUniqueId().equals(modifier.getUniqueId())) { + iter.remove(); + ++removed; + } + } + return removed > 0; + } + + @Override + public CustomItemTagContainer getCustomTagContainer() { + return this.publicItemTagContainer; + } + + private static boolean compareModifiers(Multimap first, Multimap second) { + if (first == null || second == null) { + return false; + } + for (Map.Entry entry : first.entries()) { + if (!second.containsEntry(entry.getKey(), entry.getValue())) { + return false; + } + } + for (Map.Entry entry : second.entries()) { + if (!first.containsEntry(entry.getKey(), entry.getValue())) { + return false; + } + } + return true; + } + + @Override + public boolean hasDamage() { + return damage > 0; + } + + @Override + public int getDamage() { + return damage; + } + + @Override + public void setDamage(int damage) { + this.damage = damage; + } + + @Override + public final boolean equals(Object object) { + if (object == null) { + return false; + } + if (object == this) { + return true; + } + if (!(object instanceof CraftMetaItem)) { + return false; + } + return CraftItemFactory.instance().equals(this, (ItemMeta) object); + } + + /** + * This method is almost as weird as notUncommon. + * Only return false if your common internals are unequal. + * Checking your own internals is redundant if you are not common, as notUncommon is meant for checking those 'not common' variables. + */ + @Overridden + boolean equalsCommon(CraftMetaItem that) { + return ((this.hasDisplayName() ? that.hasDisplayName() && this.displayName.equals(that.displayName) : !that.hasDisplayName())) + && (this.hasLocalizedName()? that.hasLocalizedName()&& this.locName.equals(that.locName) : !that.hasLocalizedName()) + && (this.hasEnchants() ? that.hasEnchants() && this.enchantments.equals(that.enchantments) : !that.hasEnchants()) + && (this.hasLore() ? that.hasLore() && this.lore.equals(that.lore) : !that.hasLore()) + && (this.hasRepairCost() ? that.hasRepairCost() && this.repairCost == that.repairCost : !that.hasRepairCost()) + && (this.hasAttributeModifiers() ? that.hasAttributeModifiers() && compareModifiers(this.attributeModifiers, that.attributeModifiers) : !that.hasAttributeModifiers()) + && (this.unhandledTags.equals(that.unhandledTags)) + && (this.publicItemTagContainer.equals(that.publicItemTagContainer)) + && (this.hideFlag == that.hideFlag) + && (this.isUnbreakable() == that.isUnbreakable()) + && (this.hasDamage() ? that.hasDamage() && this.damage == that.damage : !that.hasDamage()) + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values + && (this.hasPlaceableKeys() ? that.hasPlaceableKeys() && this.placeableKeys.equals(that.placeableKeys) : !that.hasPlaceableKeys()) + && (this.hasDestroyableKeys() ? that.hasDestroyableKeys() && this.destroyableKeys.equals(that.destroyableKeys) : !that.hasDestroyableKeys()); + // Paper end + } + + /** + * This method is a bit weird... + * Return true if you are a common class OR your uncommon parts are empty. + * Empty uncommon parts implies the NBT data would be equivalent if both were applied to an item + */ + @Overridden + boolean notUncommon(CraftMetaItem meta) { + return true; + } + + @Override + public final int hashCode() { + return applyHash(); + } + + @Overridden + int applyHash() { + int hash = 3; + hash = 61 * hash + (hasDisplayName() ? this.displayName.hashCode() : 0); + hash = 61 * hash + (hasLocalizedName()? this.locName.hashCode() : 0); + hash = 61 * hash + (hasLore() ? this.lore.hashCode() : 0); + hash = 61 * hash + (hasEnchants() ? this.enchantments.hashCode() : 0); + hash = 61 * hash + (hasRepairCost() ? this.repairCost : 0); + hash = 61 * hash + unhandledTags.hashCode(); + hash = 61 * hash + (!publicItemTagContainer.isEmpty() ? publicItemTagContainer.hashCode() : 0); + hash = 61 * hash + hideFlag; + hash = 61 * hash + (isUnbreakable() ? 1231 : 1237); + hash = 61 * hash + (hasDamage() ? this.damage : 0); + hash = 61 * hash + (hasAttributeModifiers() ? this.attributeModifiers.hashCode() : 0); + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values + hash = 61 * hash + (hasPlaceableKeys() ? this.placeableKeys.hashCode() : 0); + hash = 61 * hash + (hasDestroyableKeys() ? this.destroyableKeys.hashCode() : 0); + // Paper end + return hash; + } + + @Overridden + @Override + public CraftMetaItem clone() { + try { + CraftMetaItem clone = (CraftMetaItem) super.clone(); + if (this.lore != null) { + clone.lore = new ArrayList(this.lore); + } + if (this.enchantments != null) { + clone.enchantments = new EnchantmentMap(this.enchantments); // Paper + } + if (this.hasAttributeModifiers()) { + clone.attributeModifiers = HashMultimap.create(this.attributeModifiers); + } + clone.hideFlag = this.hideFlag; + clone.unbreakable = this.unbreakable; + clone.damage = this.damage; + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values + if (this.placeableKeys != null) { + clone.placeableKeys = Sets.newHashSet(this.placeableKeys); + } + + if (this.destroyableKeys != null) { + clone.destroyableKeys = Sets.newHashSet(this.destroyableKeys); + } + // Paper end + return clone; + } catch (CloneNotSupportedException e) { + throw new Error(e); + } + } + + public final Map serialize() { + ImmutableMap.Builder map = ImmutableMap.builder(); + map.put(SerializableMeta.TYPE_FIELD, SerializableMeta.classMap.get(getClass())); + serialize(map); + return map.build(); + } + + @Overridden + ImmutableMap.Builder serialize(ImmutableMap.Builder builder) { + if (hasDisplayName()) { + builder.put(NAME.BUKKIT, CraftChatMessage.fromComponent(displayName)); + } + if (hasLocalizedName()) { + builder.put(LOCNAME.BUKKIT, CraftChatMessage.fromComponent(locName)); + } + + if (hasLore()) { + builder.put(LORE.BUKKIT, ImmutableList.copyOf(lore)); + } + + serializeEnchantments(enchantments, builder, ENCHANTMENTS); + serializeModifiers(attributeModifiers, builder, ATTRIBUTES); + + if (hasRepairCost()) { + builder.put(REPAIR.BUKKIT, repairCost); + } + + List hideFlags = new ArrayList(); + for (ItemFlag hideFlagEnum : getItemFlags()) { + hideFlags.add(hideFlagEnum.name()); + } + if (!hideFlags.isEmpty()) { + builder.put(HIDEFLAGS.BUKKIT, hideFlags); + } + + if (isUnbreakable()) { + builder.put(UNBREAKABLE.BUKKIT, unbreakable); + } + + if (hasDamage()) { + builder.put(DAMAGE.BUKKIT, damage); + } + + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values + if (hasPlaceableKeys()) { + List cerealPlaceable = this.placeableKeys.stream() + .map(this::serializeNamespaced) + .collect(java.util.stream.Collectors.toList()); + + builder.put(CAN_PLACE_ON.BUKKIT, cerealPlaceable); + } + + if (hasDestroyableKeys()) { + List cerealDestroyable = this.destroyableKeys.stream() + .map(this::serializeNamespaced) + .collect(java.util.stream.Collectors.toList()); + + builder.put(CAN_DESTROY.BUKKIT, cerealDestroyable); + } + // Paper end + + final Map internalTags = new HashMap(unhandledTags); + serializeInternal(internalTags); + if (!internalTags.isEmpty()) { + NBTTagCompound internal = new NBTTagCompound(); + for (Map.Entry e : internalTags.entrySet()) { + internal.set(e.getKey(), e.getValue()); + } + try { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + NBTCompressedStreamTools.a(internal, buf); + builder.put("internal", Base64.encodeBase64String(buf.toByteArray())); + } catch (IOException ex) { + Logger.getLogger(CraftMetaItem.class.getName()).log(Level.SEVERE, null, ex); + } + } + + if (!publicItemTagContainer.isEmpty()) { // Store custom tags, wrapped in their compound + builder.put(BUKKIT_CUSTOM_TAG.BUKKIT, publicItemTagContainer.serialize()); + } + + return builder; + } + + void serializeInternal(final Map unhandledTags) { + } + + Material updateMaterial(Material material) { + return material; + } + + static void serializeEnchantments(Map enchantments, ImmutableMap.Builder builder, ItemMetaKey key) { + if (enchantments == null || enchantments.isEmpty()) { + return; + } + + ImmutableMap.Builder enchants = ImmutableMap.builder(); + for (Map.Entry enchant : enchantments.entrySet()) { + enchants.put(enchant.getKey().getName(), enchant.getValue()); + } + + builder.put(key.BUKKIT, enchants.build()); + } + + static void serializeModifiers(Multimap modifiers, ImmutableMap.Builder builder, ItemMetaKey key) { + if (modifiers == null || modifiers.isEmpty()) { + return; + } + + Map> mods = new HashMap<>(); + for (Map.Entry entry : modifiers.entries()) { + if (entry.getKey() == null) { + continue; + } + Collection modCollection = modifiers.get(entry.getKey()); + if (modCollection == null || modCollection.isEmpty()) { + continue; + } + mods.put(entry.getKey().name(), new ArrayList<>(modCollection)); + } + builder.put(key.BUKKIT, mods); + } + + static void safelyAdd(Iterable addFrom, Collection addTo, int maxItemLength) { + if (addFrom == null) { + return; + } + + for (Object object : addFrom) { + if (!(object instanceof String)) { + if (object != null) { + throw new IllegalArgumentException(addFrom + " cannot contain non-string " + object.getClass().getName()); + } + + addTo.add(""); + } else { + String page = object.toString(); + + if (page.length() > maxItemLength) { + page = page.substring(0, maxItemLength); + } + + addTo.add(page); + } + } + } + + static boolean checkConflictingEnchants(Map enchantments, Enchantment ench) { + if (enchantments == null || enchantments.isEmpty()) { + return false; + } + + for (Enchantment enchant : enchantments.keySet()) { + if (enchant.conflictsWith(ench)) { + return true; + } + } + + return false; + } + + @Override + public final String toString() { + return SerializableMeta.classMap.get(getClass()) + "_META:" + serialize(); // TODO: cry + } + + public static Set getHandledTags() { + synchronized (HANDLED_TAGS) { + if (HANDLED_TAGS.isEmpty()) { + HANDLED_TAGS.addAll(Arrays.asList( + DISPLAY.NBT, + REPAIR.NBT, + ENCHANTMENTS.NBT, + HIDEFLAGS.NBT, + UNBREAKABLE.NBT, + DAMAGE.NBT, + BUKKIT_CUSTOM_TAG.NBT, + ATTRIBUTES.NBT, + ATTRIBUTES_IDENTIFIER.NBT, + ATTRIBUTES_NAME.NBT, + ATTRIBUTES_VALUE.NBT, + ATTRIBUTES_UUID_HIGH.NBT, + ATTRIBUTES_UUID_LOW.NBT, + ATTRIBUTES_SLOT.NBT, + CraftMetaMap.MAP_SCALING.NBT, + CraftMetaMap.MAP_ID.NBT, + CraftMetaPotion.POTION_EFFECTS.NBT, + CraftMetaPotion.DEFAULT_POTION.NBT, + CraftMetaPotion.POTION_COLOR.NBT, + CraftMetaSkull.SKULL_OWNER.NBT, + CraftMetaSkull.SKULL_PROFILE.NBT, + CraftMetaSpawnEgg.ENTITY_TAG.NBT, + CraftMetaBlockState.BLOCK_ENTITY_TAG.NBT, + CraftMetaBook.BOOK_TITLE.NBT, + CraftMetaBook.BOOK_AUTHOR.NBT, + CraftMetaBook.BOOK_PAGES.NBT, + CraftMetaBook.RESOLVED.NBT, + CraftMetaBook.GENERATION.NBT, + CraftMetaFirework.FIREWORKS.NBT, + CraftMetaEnchantedBook.STORED_ENCHANTMENTS.NBT, + CraftMetaCharge.EXPLOSION.NBT, + CraftMetaBlockState.BLOCK_ENTITY_TAG.NBT, + CraftMetaKnowledgeBook.BOOK_RECIPES.NBT, + CraftMetaTropicalFishBucket.VARIANT.NBT, + // Paper start + CraftMetaArmorStand.ENTITY_TAG.NBT, + CraftMetaArmorStand.INVISIBLE.NBT, + CraftMetaArmorStand.NO_BASE_PLATE.NBT, + CraftMetaArmorStand.SHOW_ARMS.NBT, + CraftMetaArmorStand.SMALL.NBT, + CraftMetaArmorStand.MARKER.NBT, + CAN_DESTROY.NBT, + CAN_PLACE_ON.NBT + // Paper end + )); + } + return HANDLED_TAGS; + } + } + + // Paper start + private static class EnchantmentMap extends TreeMap { + private EnchantmentMap(Map enchantments) { + this(); + putAll(enchantments); + } + + private EnchantmentMap() { + super(Comparator.comparing(o -> o.getKey().toString())); + } + + public EnchantmentMap clone() { + return (EnchantmentMap) super.clone(); + } + } + // Paper end + + // Spigot start + private final Spigot spigot = new Spigot() + { + @Override + public void setUnbreakable(boolean setUnbreakable) + { + CraftMetaItem.this.setUnbreakable(setUnbreakable); + } + + @Override + public boolean isUnbreakable() + { + return CraftMetaItem.this.unbreakable; + } + }; + + @Override + public Spigot spigot() + { + return spigot; + } + // Spigot end + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values + @Override + @SuppressWarnings("deprecation") + public Set getCanDestroy() { + return !hasDestroyableKeys() ? Collections.emptySet() : legacyGetMatsFromKeys(this.destroyableKeys); + } + + @Override + @SuppressWarnings("deprecation") + public void setCanDestroy(Set canDestroy) { + Validate.notNull(canDestroy, "Cannot replace with null set!"); + legacyClearAndReplaceKeys(this.destroyableKeys, canDestroy); + } + + @Override + @SuppressWarnings("deprecation") + public Set getCanPlaceOn() { + return !hasPlaceableKeys() ? Collections.emptySet() : legacyGetMatsFromKeys(this.placeableKeys); + } + + @Override + @SuppressWarnings("deprecation") + public void setCanPlaceOn(Set canPlaceOn) { + Validate.notNull(canPlaceOn, "Cannot replace with null set!"); + legacyClearAndReplaceKeys(this.placeableKeys, canPlaceOn); + } + + @Override + public Set getDestroyableKeys() { + return !hasDestroyableKeys() ? Collections.emptySet() : Sets.newHashSet(this.destroyableKeys); + } + + @Override + public void setDestroyableKeys(Collection canDestroy) { + Validate.notNull(canDestroy, "Cannot replace with null collection!"); + Validate.isTrue(ofAcceptableType(canDestroy), "Can only use NamespacedKey or NamespacedTag objects!"); + this.destroyableKeys.clear(); + this.destroyableKeys.addAll(canDestroy); + } + + @Override + public Set getPlaceableKeys() { + return !hasPlaceableKeys() ? Collections.emptySet() : Sets.newHashSet(this.placeableKeys); + } + + @Override + public void setPlaceableKeys(Collection canPlaceOn) { + Validate.notNull(canPlaceOn, "Cannot replace with null collection!"); + Validate.isTrue(ofAcceptableType(canPlaceOn), "Can only use NamespacedKey or NamespacedTag objects!"); + this.placeableKeys.clear(); + this.placeableKeys.addAll(canPlaceOn); + } + + @Override + public boolean hasPlaceableKeys() { + return this.placeableKeys != null && !this.placeableKeys.isEmpty(); + } + + @Override + public boolean hasDestroyableKeys() { + return this.destroyableKeys != null && !this.destroyableKeys.isEmpty(); + } + + @Deprecated + private void legacyClearAndReplaceKeys(Collection toUpdate, Collection beingSet) { + if (beingSet.stream().anyMatch(Material::isLegacy)) { + throw new IllegalArgumentException("Set must not contain any legacy materials!"); + } + + toUpdate.clear(); + toUpdate.addAll(beingSet.stream().map(Material::getKey).collect(java.util.stream.Collectors.toSet())); + } + + @Deprecated + private Set legacyGetMatsFromKeys(Collection names) { + Set mats = Sets.newHashSet(); + for (Namespaced key : names) { + if (!(key instanceof org.bukkit.NamespacedKey)) { + continue; + } + + Material material = Material.matchMaterial(key.toString(), false); + if (material != null) { + mats.add(material); + } + } + + return mats; + } + + private @Nullable Namespaced deserializeNamespaced(String raw) { + boolean isTag = raw.codePointAt(0) == '#'; + net.minecraft.server.ArgumentBlock blockParser = new net.minecraft.server.ArgumentBlock(new com.mojang.brigadier.StringReader(raw), true); + try { + blockParser = blockParser.parse(false); + } catch (com.mojang.brigadier.exceptions.CommandSyntaxException e) { + e.printStackTrace(); + return null; + } + + net.minecraft.server.MinecraftKey key; + if (isTag) { + key = blockParser.getTagKey(); + } else { + key = blockParser.getBlockKey(); + } + + if (key == null) { + return null; + } + + // don't DC the player if something slips through somehow + Namespaced resource = null; + try { + if (isTag) { + resource = new NamespacedTag(key.b(), key.getKey()); + } else { + resource = CraftNamespacedKey.fromMinecraft(key); + } + } catch (IllegalArgumentException ex) { + org.bukkit.Bukkit.getLogger().warning("Namespaced resource does not validate: " + key.toString()); + ex.printStackTrace(); + } + + return resource; + } + + private @Nonnull String serializeNamespaced(Namespaced resource) { + return resource.toString(); + } + + // not a fan of this + private boolean ofAcceptableType(Collection namespacedResources) { + boolean valid = true; + for (Namespaced resource : namespacedResources) { + if (valid && !(resource instanceof org.bukkit.NamespacedKey || resource instanceof com.destroystokyo.paper.NamespacedTag)) { + valid = false; + } + } + + return valid; + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaKnowledgeBook.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaKnowledgeBook.java new file mode 100644 index 000000000000..29471e0f7418 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaKnowledgeBook.java @@ -0,0 +1,173 @@ +package org.bukkit.craftbukkit.inventory; + +import com.google.common.collect.ImmutableMap.Builder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagList; +import net.minecraft.server.NBTTagString; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.inventory.meta.KnowledgeBookMeta; + +@DelegateDeserialization(SerializableMeta.class) +public class CraftMetaKnowledgeBook extends CraftMetaItem implements KnowledgeBookMeta { + + static final ItemMetaKey BOOK_RECIPES = new ItemMetaKey("Recipes"); + static final int MAX_RECIPES = Short.MAX_VALUE; + + protected List recipes = new ArrayList(); + + CraftMetaKnowledgeBook(CraftMetaItem meta) { + super(meta); + + if (meta instanceof CraftMetaKnowledgeBook) { + CraftMetaKnowledgeBook bookMeta = (CraftMetaKnowledgeBook) meta; + this.recipes.addAll(bookMeta.recipes); + } + } + + CraftMetaKnowledgeBook(NBTTagCompound tag) { + super(tag); + + if (tag.hasKey(BOOK_RECIPES.NBT)) { + NBTTagList pages = tag.getList(BOOK_RECIPES.NBT, 8); + + for (int i = 0; i < pages.size(); i++) { + String recipe = pages.getString(i); + + addRecipe(CraftNamespacedKey.fromString(recipe)); + } + } + } + + CraftMetaKnowledgeBook(Map map) { + super(map); + + Iterable pages = SerializableMeta.getObject(Iterable.class, map, BOOK_RECIPES.BUKKIT, true); + if (pages != null) { + for (Object page : pages) { + if (page instanceof String) { + addRecipe(CraftNamespacedKey.fromString((String) page)); + } + } + } + } + + void applyToItem(NBTTagCompound itemData) { + super.applyToItem(itemData); + + if (hasRecipes()) { + NBTTagList list = new NBTTagList(); + for (NamespacedKey recipe : this.recipes) { + list.add(new NBTTagString(recipe.toString())); + } + itemData.set(BOOK_RECIPES.NBT, list); + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isBookEmpty(); + } + + boolean isBookEmpty() { + return !(hasRecipes()); + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case KNOWLEDGE_BOOK: + return true; + default: + return false; + } + } + + @Override + public boolean hasRecipes() { + return !recipes.isEmpty(); + } + + @Override + public void addRecipe(NamespacedKey... recipes) { + for (NamespacedKey recipe : recipes) { + if (recipe != null) { + if (this.recipes.size() >= MAX_RECIPES) { + return; + } + + this.recipes.add(recipe); + } + } + } + + @Override + public List getRecipes() { + return Collections.unmodifiableList(recipes); + } + + @Override + public void setRecipes(List recipes) { + this.recipes.clear(); + for (NamespacedKey recipe : recipes) { + addRecipe(recipe); + } + } + + @Override + public CraftMetaKnowledgeBook clone() { + CraftMetaKnowledgeBook meta = (CraftMetaKnowledgeBook) super.clone(); + meta.recipes = new ArrayList(recipes); + return meta; + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + if (hasRecipes()) { + hash = 61 * hash + 17 * this.recipes.hashCode(); + } + return original != hash ? CraftMetaKnowledgeBook.class.hashCode() ^ hash : hash; + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaKnowledgeBook) { + CraftMetaKnowledgeBook that = (CraftMetaKnowledgeBook) meta; + + return (hasRecipes() ? that.hasRecipes() && this.recipes.equals(that.recipes) : !that.hasRecipes()); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaKnowledgeBook || isBookEmpty()); + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + + if (hasRecipes()) { + List recipesString = new ArrayList(); + for (NamespacedKey recipe : recipes) { + recipesString.add(recipe.toString()); + } + builder.put(BOOK_RECIPES.BUKKIT, recipesString); + } + + return builder; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java new file mode 100644 index 000000000000..f321d854d49d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java @@ -0,0 +1,139 @@ +package org.bukkit.craftbukkit.inventory; + +import static org.bukkit.craftbukkit.inventory.CraftItemFactory.DEFAULT_LEATHER_COLOR; + +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagInt; + +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.inventory.meta.LeatherArmorMeta; + +import com.google.common.collect.ImmutableMap.Builder; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaLeatherArmor extends CraftMetaItem implements LeatherArmorMeta { + static final ItemMetaKey COLOR = new ItemMetaKey("color"); + + private Color color = DEFAULT_LEATHER_COLOR; + + CraftMetaLeatherArmor(CraftMetaItem meta) { + super(meta); + if (!(meta instanceof CraftMetaLeatherArmor)) { + return; + } + + CraftMetaLeatherArmor armorMeta = (CraftMetaLeatherArmor) meta; + this.color = armorMeta.color; + } + + CraftMetaLeatherArmor(NBTTagCompound tag) { + super(tag); + if (tag.hasKey(DISPLAY.NBT)) { + NBTTagCompound display = tag.getCompound(DISPLAY.NBT); + if (display.hasKey(COLOR.NBT)) { + try { + color = Color.fromRGB(display.getInt(COLOR.NBT)); + } catch (IllegalArgumentException ex) { + // Invalid colour + } + } + } + } + + CraftMetaLeatherArmor(Map map) { + super(map); + setColor(SerializableMeta.getObject(Color.class, map, COLOR.BUKKIT, true)); + } + + @Override + void applyToItem(NBTTagCompound itemTag) { + super.applyToItem(itemTag); + + if (hasColor()) { + setDisplayTag(itemTag, COLOR.NBT, new NBTTagInt(color.asRGB())); + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isLeatherArmorEmpty(); + } + + boolean isLeatherArmorEmpty() { + return !(hasColor()); + } + + @Override + boolean applicableTo(Material type) { + switch(type) { + case LEATHER_HELMET: + case LEATHER_CHESTPLATE: + case LEATHER_LEGGINGS: + case LEATHER_BOOTS: + return true; + default: + return false; + } + } + + @Override + public CraftMetaLeatherArmor clone() { + return (CraftMetaLeatherArmor) super.clone(); + } + + public Color getColor() { + return color; + } + + public void setColor(Color color) { + this.color = color == null ? DEFAULT_LEATHER_COLOR : color; + } + + boolean hasColor() { + return !DEFAULT_LEATHER_COLOR.equals(color); + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + + if (hasColor()) { + builder.put(COLOR.BUKKIT, color); + } + + return builder; + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaLeatherArmor) { + CraftMetaLeatherArmor that = (CraftMetaLeatherArmor) meta; + + return color.equals(that.color); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaLeatherArmor || isLeatherArmorEmpty()); + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + if (hasColor()) { + hash ^= color.hashCode(); + } + return original != hash ? CraftMetaSkull.class.hashCode() ^ hash : hash; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java new file mode 100644 index 000000000000..e5b1a73023e3 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java @@ -0,0 +1,280 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagInt; +import net.minecraft.server.NBTTagString; + +import org.bukkit.Bukkit; +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.inventory.meta.MapMeta; +import org.bukkit.map.MapView; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaMap extends CraftMetaItem implements MapMeta { + static final ItemMetaKey MAP_SCALING = new ItemMetaKey("map_is_scaling", "scaling"); + static final ItemMetaKey MAP_LOC_NAME = new ItemMetaKey("LocName", "display-loc-name"); + static final ItemMetaKey MAP_COLOR = new ItemMetaKey("MapColor", "display-map-color"); + static final ItemMetaKey MAP_ID = new ItemMetaKey("map", "map-id"); + static final byte SCALING_EMPTY = (byte) 0; + static final byte SCALING_TRUE = (byte) 1; + static final byte SCALING_FALSE = (byte) 2; + + private Integer mapId; + private byte scaling = SCALING_EMPTY; + private String locName; + private Color color; + + CraftMetaMap(CraftMetaItem meta) { + super(meta); + + if (!(meta instanceof CraftMetaMap)) { + return; + } + + CraftMetaMap map = (CraftMetaMap) meta; + this.mapId = map.mapId; + this.scaling = map.scaling; + this.locName = map.locName; + this.color = map.color; + } + + CraftMetaMap(NBTTagCompound tag) { + super(tag); + + if (tag.hasKeyOfType(MAP_ID.NBT, CraftMagicNumbers.NBT.TAG_ANY_NUMBER)) { + this.mapId = tag.getInt(MAP_ID.NBT); + } + + if (tag.hasKey(MAP_SCALING.NBT)) { + this.scaling = tag.getBoolean(MAP_SCALING.NBT) ? SCALING_TRUE : SCALING_FALSE; + } + + if (tag.hasKey(DISPLAY.NBT)) { + NBTTagCompound display = tag.getCompound(DISPLAY.NBT); + + if (display.hasKey(MAP_LOC_NAME.NBT)) { + locName = display.getString(MAP_LOC_NAME.NBT); + } + + if (display.hasKey(MAP_COLOR.NBT)) { + color = Color.fromRGB(display.getInt(MAP_COLOR.NBT)); + } + } + } + + CraftMetaMap(Map map) { + super(map); + + Integer id = SerializableMeta.getObject(Integer.class, map, MAP_ID.BUKKIT, true); + if (id != null) { + setMapId(id); + } + + Boolean scaling = SerializableMeta.getObject(Boolean.class, map, MAP_SCALING.BUKKIT, true); + if (scaling != null) { + setScaling(scaling); + } + + String locName = SerializableMeta.getString(map, MAP_LOC_NAME.BUKKIT, true); + if (locName != null) { + setLocationName(locName); + } + + Color color = SerializableMeta.getObject(Color.class, map, MAP_COLOR.BUKKIT, true); + if (color != null) { + setColor(color); + } + } + + @Override + void applyToItem(NBTTagCompound tag) { + super.applyToItem(tag); + + if (hasMapId()){ + tag.setInt(MAP_ID.NBT, getMapId()); + } + + if (hasScaling()) { + tag.setBoolean(MAP_SCALING.NBT, isScaling()); + } + + if (hasLocationName()) { + setDisplayTag(tag, MAP_LOC_NAME.NBT, new NBTTagString(getLocationName())); + } + + if (hasColor()) { + setDisplayTag(tag, MAP_COLOR.NBT, new NBTTagInt(color.asRGB())); + } + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case FILLED_MAP: + return true; + default: + return false; + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isMapEmpty(); + } + + boolean isMapEmpty() { + return !(hasMapId() || hasScaling() | hasLocationName() || hasColor()); + } + + @Override + public boolean hasMapId() { + return mapId != null; + } + + @Override + public int getMapId() { + return mapId; + } + + @Override + public void setMapId(int id) { + this.mapId = id; + } + + @Override + public boolean hasMapView() { + return mapId != null; + } + + @Override + public MapView getMapView() { + Preconditions.checkState(hasMapView(), "Item does not have map associated - check hasMapView() first!"); + return Bukkit.getMap(mapId); + } + + @Override + public void setMapView(MapView map) { + this.mapId = (map != null) ? map.getId() : null; + } + + boolean hasScaling() { + return scaling != SCALING_EMPTY; + } + + public boolean isScaling() { + return scaling == SCALING_TRUE; + } + + public void setScaling(boolean scaling) { + this.scaling = scaling ? SCALING_TRUE : SCALING_FALSE; + } + + @Override + public boolean hasLocationName() { + return this.locName != null; + } + + @Override + public String getLocationName() { + return this.locName; + } + + @Override + public void setLocationName(String name) { + this.locName = name; + } + + @Override + public boolean hasColor() { + return this.color != null; + } + + @Override + public Color getColor() { + return this.color; + } + + @Override + public void setColor(Color color) { + this.color = color; + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaMap) { + CraftMetaMap that = (CraftMetaMap) meta; + + return (this.scaling == that.scaling) + && (hasMapId() ? that.hasMapId() && this.mapId.equals(that.mapId) : !that.hasMapId()) + && (hasLocationName() ? that.hasLocationName() && this.locName.equals(that.locName) : !that.hasLocationName()) + && (hasColor() ? that.hasColor() && this.color.equals(that.color) : !that.hasColor()); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaMap || isMapEmpty()); + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + + if (hasMapId()) { + hash = 61 * hash + mapId.hashCode(); + } + if (hasScaling()) { + hash ^= 0x22222222 << (isScaling() ? 1 : -1); + } + if (hasLocationName()) { + hash = 61 * hash + locName.hashCode(); + } + if (hasColor()) { + hash = 61 * hash + color.hashCode(); + } + + return original != hash ? CraftMetaMap.class.hashCode() ^ hash : hash; + } + + + public CraftMetaMap clone() { + return (CraftMetaMap) super.clone(); + } + + @Override + ImmutableMap.Builder serialize(ImmutableMap.Builder builder) { + super.serialize(builder); + + if (hasMapId()) { + builder.put(MAP_ID.BUKKIT, getMapId()); + } + + if (hasScaling()) { + builder.put(MAP_SCALING.BUKKIT, isScaling()); + } + + if (hasLocationName()) { + builder.put(MAP_LOC_NAME.BUKKIT, getLocationName()); + } + + if (hasColor()) { + builder.put(MAP_COLOR.BUKKIT, getColor()); + } + + return builder; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java new file mode 100644 index 000000000000..29fbdcc83f62 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java @@ -0,0 +1,344 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagInt; +import net.minecraft.server.NBTTagList; + +import org.apache.commons.lang.Validate; +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.potion.PotionData; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.potion.PotionType; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.craftbukkit.potion.CraftPotionUtil; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap.Builder; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaPotion extends CraftMetaItem implements PotionMeta { + static final ItemMetaKey AMPLIFIER = new ItemMetaKey("Amplifier", "amplifier"); + static final ItemMetaKey AMBIENT = new ItemMetaKey("Ambient", "ambient"); + static final ItemMetaKey DURATION = new ItemMetaKey("Duration", "duration"); + static final ItemMetaKey SHOW_PARTICLES = new ItemMetaKey("ShowParticles", "has-particles"); + static final ItemMetaKey SHOW_ICON = new ItemMetaKey("ShowIcon", "has-icon"); + static final ItemMetaKey POTION_EFFECTS = new ItemMetaKey("CustomPotionEffects", "custom-effects"); + static final ItemMetaKey POTION_COLOR = new ItemMetaKey("CustomPotionColor", "custom-color"); + static final ItemMetaKey ID = new ItemMetaKey("Id", "potion-id"); + static final ItemMetaKey DEFAULT_POTION = new ItemMetaKey("Potion", "potion-type"); + + // Having an initial "state" in ItemMeta seems bit dirty but the UNCRAFTABLE potion type + // is treated as the empty form of the meta because it represents an empty potion with no effect + private PotionData type = new PotionData(PotionType.UNCRAFTABLE, false, false); + private List customEffects; + private Color color; + + CraftMetaPotion(CraftMetaItem meta) { + super(meta); + if (!(meta instanceof CraftMetaPotion)) { + return; + } + CraftMetaPotion potionMeta = (CraftMetaPotion) meta; + this.type = potionMeta.type; + this.color = potionMeta.color; + if (potionMeta.hasCustomEffects()) { + this.customEffects = new ArrayList(potionMeta.customEffects); + } + } + + CraftMetaPotion(NBTTagCompound tag) { + super(tag); + if (tag.hasKey(DEFAULT_POTION.NBT)) { + type = CraftPotionUtil.toBukkit(tag.getString(DEFAULT_POTION.NBT)); + } + if (tag.hasKey(POTION_COLOR.NBT)) { + color = Color.fromRGB(tag.getInt(POTION_COLOR.NBT)); + } + if (tag.hasKey(POTION_EFFECTS.NBT)) { + NBTTagList list = tag.getList(POTION_EFFECTS.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND); + int length = list.size(); + customEffects = new ArrayList(length); + + for (int i = 0; i < length; i++) { + NBTTagCompound effect = list.getCompound(i); + PotionEffectType type = PotionEffectType.getById(effect.getByte(ID.NBT)); + // SPIGOT-4047: Vanilla just disregards these + if (type == null) { + continue; + } + + int amp = effect.getByte(AMPLIFIER.NBT); + int duration = effect.getInt(DURATION.NBT); + boolean ambient = effect.getBoolean(AMBIENT.NBT); + boolean particles = tag.hasKeyOfType(SHOW_PARTICLES.NBT, CraftMagicNumbers.NBT.TAG_BYTE) ? effect.getBoolean(SHOW_PARTICLES.NBT) : true; + boolean icon = tag.hasKeyOfType(SHOW_ICON.NBT, CraftMagicNumbers.NBT.TAG_BYTE) ? effect.getBoolean(SHOW_ICON.NBT) : particles; + customEffects.add(new PotionEffect(type, duration, amp, ambient, particles, icon)); + } + } + } + + CraftMetaPotion(Map map) { + super(map); + type = CraftPotionUtil.toBukkit(SerializableMeta.getString(map, DEFAULT_POTION.BUKKIT, true)); + + Color color = SerializableMeta.getObject(Color.class, map, POTION_COLOR.BUKKIT, true); + if (color != null) { + setColor(color); + } + + Iterable rawEffectList = SerializableMeta.getObject(Iterable.class, map, POTION_EFFECTS.BUKKIT, true); + if (rawEffectList == null) { + return; + } + + for (Object obj : rawEffectList) { + if (!(obj instanceof PotionEffect)) { + throw new IllegalArgumentException("Object in effect list is not valid. " + obj.getClass()); + } + addCustomEffect((PotionEffect) obj, true); + } + } + + @Override + void applyToItem(NBTTagCompound tag) { + super.applyToItem(tag); + + tag.setString(DEFAULT_POTION.NBT, CraftPotionUtil.fromBukkit(type)); + + if (hasColor()) { + tag.setInt(POTION_COLOR.NBT, color.asRGB()); + } + + if (customEffects != null) { + NBTTagList effectList = new NBTTagList(); + tag.set(POTION_EFFECTS.NBT, effectList); + + for (PotionEffect effect : customEffects) { + NBTTagCompound effectData = new NBTTagCompound(); + effectData.setByte(ID.NBT, (byte) effect.getType().getId()); + effectData.setByte(AMPLIFIER.NBT, (byte) effect.getAmplifier()); + effectData.setInt(DURATION.NBT, effect.getDuration()); + effectData.setBoolean(AMBIENT.NBT, effect.isAmbient()); + effectData.setBoolean(SHOW_PARTICLES.NBT, effect.hasParticles()); + effectData.setBoolean(SHOW_ICON.NBT, effect.hasIcon()); + effectList.add(effectData); + } + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isPotionEmpty(); + } + + boolean isPotionEmpty() { + return (type.getType() == PotionType.UNCRAFTABLE) && !(hasCustomEffects() || hasColor()); + } + + @Override + boolean applicableTo(Material type) { + switch(type) { + case POTION: + case SPLASH_POTION: + case LINGERING_POTION: + case TIPPED_ARROW: + return true; + default: + return false; + } + } + + @Override + public CraftMetaPotion clone() { + CraftMetaPotion clone = (CraftMetaPotion) super.clone(); + clone.type = type; + if (this.customEffects != null) { + clone.customEffects = new ArrayList(this.customEffects); + } + return clone; + } + + @Override + public void setBasePotionData(PotionData data) { + Validate.notNull(data, "PotionData cannot be null"); + this.type = data; + } + + @Override + public PotionData getBasePotionData() { + return type; + } + + public boolean hasCustomEffects() { + return customEffects != null; + } + + public List getCustomEffects() { + if (hasCustomEffects()) { + return ImmutableList.copyOf(customEffects); + } + return ImmutableList.of(); + } + + public boolean addCustomEffect(PotionEffect effect, boolean overwrite) { + Validate.notNull(effect, "Potion effect must not be null"); + + int index = indexOfEffect(effect.getType()); + if (index != -1) { + if (overwrite) { + PotionEffect old = customEffects.get(index); + if (old.getAmplifier() == effect.getAmplifier() && old.getDuration() == effect.getDuration() && old.isAmbient() == effect.isAmbient()) { + return false; + } + customEffects.set(index, effect); + return true; + } else { + return false; + } + } else { + if (customEffects == null) { + customEffects = new ArrayList(); + } + customEffects.add(effect); + return true; + } + } + + public boolean removeCustomEffect(PotionEffectType type) { + Validate.notNull(type, "Potion effect type must not be null"); + + if (!hasCustomEffects()) { + return false; + } + + boolean changed = false; + Iterator iterator = customEffects.iterator(); + while (iterator.hasNext()) { + PotionEffect effect = iterator.next(); + if (type.equals(effect.getType())) { + iterator.remove(); + changed = true; + } + } + if (customEffects.isEmpty()) { + customEffects = null; + } + return changed; + } + + public boolean hasCustomEffect(PotionEffectType type) { + Validate.notNull(type, "Potion effect type must not be null"); + return indexOfEffect(type) != -1; + } + + public boolean setMainEffect(PotionEffectType type) { + Validate.notNull(type, "Potion effect type must not be null"); + int index = indexOfEffect(type); + if (index == -1 || index == 0) { + return false; + } + + PotionEffect old = customEffects.get(0); + customEffects.set(0, customEffects.get(index)); + customEffects.set(index, old); + return true; + } + + private int indexOfEffect(PotionEffectType type) { + if (!hasCustomEffects()) { + return -1; + } + + for (int i = 0; i < customEffects.size(); i++) { + if (customEffects.get(i).getType().equals(type)) { + return i; + } + } + return -1; + } + + public boolean clearCustomEffects() { + boolean changed = hasCustomEffects(); + customEffects = null; + return changed; + } + + @Override + public boolean hasColor() { + return color != null; + } + + @Override + public Color getColor() { + return color; + } + + @Override + public void setColor(Color color) { + this.color = color; + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + if (type.getType() != PotionType.UNCRAFTABLE) { + hash = 73 * hash + type.hashCode(); + } + if (hasColor()) { + hash = 73 * hash + color.hashCode(); + } + if (hasCustomEffects()) { + hash = 73 * hash + customEffects.hashCode(); + } + return original != hash ? CraftMetaPotion.class.hashCode() ^ hash : hash; + } + + @Override + public boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaPotion) { + CraftMetaPotion that = (CraftMetaPotion) meta; + + return type.equals(that.type) + && (this.hasCustomEffects() ? that.hasCustomEffects() && this.customEffects.equals(that.customEffects) : !that.hasCustomEffects()) + && (this.hasColor() ? that.hasColor() && this.color.equals(that.color) : !that.hasColor()); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaPotion || isPotionEmpty()); + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + if (type.getType() != PotionType.UNCRAFTABLE) { + builder.put(DEFAULT_POTION.BUKKIT, CraftPotionUtil.fromBukkit(type)); + } + + if (hasColor()) { + builder.put(POTION_COLOR.BUKKIT, getColor()); + } + + if (hasCustomEffects()) { + builder.put(POTION_EFFECTS.BUKKIT, ImmutableList.copyOf(this.customEffects)); + } + + return builder; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java new file mode 100644 index 000000000000..87e51cf8adf7 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java @@ -0,0 +1,237 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Map; + +import com.destroystokyo.paper.profile.CraftPlayerProfile; +import com.destroystokyo.paper.profile.PlayerProfile; +import net.minecraft.server.GameProfileSerializer; +import net.minecraft.server.NBTBase; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.TileEntitySkull; +import net.minecraft.server.*; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.inventory.meta.SkullMeta; + +import com.google.common.collect.ImmutableMap.Builder; +import com.mojang.authlib.GameProfile; + +import javax.annotation.Nullable; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaSkull extends CraftMetaItem implements SkullMeta { + + @ItemMetaKey.Specific(ItemMetaKey.Specific.To.NBT) + static final ItemMetaKey SKULL_PROFILE = new ItemMetaKey("SkullProfile"); + + static final ItemMetaKey SKULL_OWNER = new ItemMetaKey("SkullOwner", "skull-owner"); + static final int MAX_OWNER_LENGTH = 16; + + private GameProfile profile; + + CraftMetaSkull(CraftMetaItem meta) { + super(meta); + if (!(meta instanceof CraftMetaSkull)) { + return; + } + CraftMetaSkull skullMeta = (CraftMetaSkull) meta; + this.profile = skullMeta.profile; + } + + CraftMetaSkull(NBTTagCompound tag) { + super(tag); + + if (tag.hasKeyOfType(SKULL_OWNER.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND)) { + profile = GameProfileSerializer.deserialize(tag.getCompound(SKULL_OWNER.NBT)); + } else if (tag.hasKeyOfType(SKULL_OWNER.NBT, CraftMagicNumbers.NBT.TAG_STRING) && !tag.getString(SKULL_OWNER.NBT).isEmpty()) { + profile = new GameProfile(null, tag.getString(SKULL_OWNER.NBT)); + } + } + + CraftMetaSkull(Map map) { + super(map); + if (profile == null) { + setOwner(SerializableMeta.getString(map, SKULL_OWNER.BUKKIT, true)); + } + } + + @Override + void deserializeInternal(NBTTagCompound tag, Object context) { + super.deserializeInternal(tag, context); + + if (tag.hasKeyOfType(SKULL_PROFILE.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND)) { + profile = GameProfileSerializer.deserialize(tag.getCompound(SKULL_PROFILE.NBT)); + } + } + + @Override + void serializeInternal(final Map internalTags) { + if (profile != null) { + NBTTagCompound nbtData = new NBTTagCompound(); + GameProfileSerializer.serialize(nbtData, profile); + internalTags.put(SKULL_PROFILE.NBT, nbtData); + } + } + + @Override + void applyToItem(NBTTagCompound tag) { + super.applyToItem(tag); + + if (profile != null) { + // Fill in textures + // Must be done sync due to way client handles textures + profile = com.google.common.util.concurrent.Futures.getUnchecked(TileEntitySkull.b(profile, com.google.common.base.Predicates.alwaysTrue(), true)); // Spigot + + NBTTagCompound owner = new NBTTagCompound(); + GameProfileSerializer.serialize(owner, profile); + tag.set(SKULL_OWNER.NBT, owner); + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isSkullEmpty(); + } + + boolean isSkullEmpty() { + return profile == null; + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case CREEPER_HEAD: + case CREEPER_WALL_HEAD: + case DRAGON_HEAD: + case DRAGON_WALL_HEAD: + case PLAYER_HEAD: + case PLAYER_WALL_HEAD: + case SKELETON_SKULL: + case SKELETON_WALL_SKULL: + case WITHER_SKELETON_SKULL: + case WITHER_SKELETON_WALL_SKULL: + case ZOMBIE_HEAD: + case ZOMBIE_WALL_HEAD: + return true; + default: + return false; + } + } + + @Override + public CraftMetaSkull clone() { + return (CraftMetaSkull) super.clone(); + } + + public boolean hasOwner() { + return profile != null && profile.getName() != null; + } + + public String getOwner() { + return hasOwner() ? profile.getName() : null; + } + + // Paper start + @Override + public void setPlayerProfile(@Nullable PlayerProfile profile) { + this.profile = (profile == null) ? null : CraftPlayerProfile.asAuthlibCopy(profile); + } + + @Nullable + @Override + public PlayerProfile getPlayerProfile() { + return profile != null ? CraftPlayerProfile.asBukkitCopy(profile) : null; + } + // Paper end + + @Override + public OfflinePlayer getOwningPlayer() { + if (hasOwner()) { + if (profile.getId() != null) { + return Bukkit.getOfflinePlayer(profile.getId()); + } + + if (profile.getName() != null) { + return Bukkit.getOfflinePlayer(profile.getName()); + } + } + + return null; + } + + public boolean setOwner(String name) { + if (name != null && name.length() > MAX_OWNER_LENGTH) { + return false; + } + + if (name == null) { + profile = null; + } else { + // Paper start - Use Online Players Skull + GameProfile newProfile = null; + EntityPlayer player = MinecraftServer.getServer().getPlayerList().getPlayer(name); + if (player != null) newProfile = player.getProfile(); + if (newProfile == null) newProfile = new GameProfile(null, name); + profile = newProfile; + // Paper end + } + + return true; + } + + @Override + public boolean setOwningPlayer(OfflinePlayer owner) { + if (owner == null) { + profile = null; + } else if (owner instanceof CraftPlayer) { + profile = ((CraftPlayer) owner).getProfile(); + } else { + profile = new GameProfile(owner.getUniqueId(), owner.getName()); + } + + return true; + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + if (hasOwner()) { + hash = 61 * hash + profile.hashCode(); + } + return original != hash ? CraftMetaSkull.class.hashCode() ^ hash : hash; + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaSkull) { + CraftMetaSkull that = (CraftMetaSkull) meta; + + return (this.hasOwner() ? that.hasOwner() && this.profile.equals(that.profile) : !that.hasOwner()); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaSkull || isSkullEmpty()); + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + if (hasOwner()) { + return builder.put(SKULL_OWNER.BUKKIT, this.profile.getName()); + } + return builder; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java new file mode 100644 index 000000000000..81f9bb629a14 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java @@ -0,0 +1,269 @@ +package org.bukkit.craftbukkit.inventory; + +import com.google.common.collect.ImmutableMap.Builder; +import com.mojang.datafixers.Dynamic; +import java.util.Map; +import net.minecraft.server.DataConverterTypes; +import net.minecraft.server.DynamicOpsNBT; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.NBTBase; +import net.minecraft.server.NBTTagCompound; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.util.CraftLegacy; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.meta.SpawnEggMeta; +import org.bukkit.material.MaterialData; + +@DelegateDeserialization(CraftMetaItem.SerializableMeta.class) +public class CraftMetaSpawnEgg extends CraftMetaItem implements SpawnEggMeta { + + static final ItemMetaKey ENTITY_TAG = new ItemMetaKey("EntityTag", "entity-tag"); + @ItemMetaKey.Specific(ItemMetaKey.Specific.To.NBT) + static final ItemMetaKey ENTITY_ID = new ItemMetaKey("id"); + + private EntityType spawnedType; + private NBTTagCompound entityTag; + + CraftMetaSpawnEgg(CraftMetaItem meta) { + super(meta); + + if (!(meta instanceof CraftMetaSpawnEgg)) { + return; + } + + CraftMetaSpawnEgg egg = (CraftMetaSpawnEgg) meta; + this.spawnedType = egg.spawnedType; + + updateMaterial(null); // Trigger type population + } + + CraftMetaSpawnEgg(NBTTagCompound tag) { + super(tag); + + if (tag.hasKey(ENTITY_TAG.NBT)) { + entityTag = tag.getCompound(ENTITY_TAG.NBT); + } + } + + CraftMetaSpawnEgg(Map map) { + super(map); + + String entityType = SerializableMeta.getString(map, ENTITY_ID.BUKKIT, true); + if (entityType != null) { + this.spawnedType = EntityType.fromName(entityType); + } + } + + @Override + void deserializeInternal(NBTTagCompound tag, Object context) { + super.deserializeInternal(tag, context); + + if (tag.hasKey(ENTITY_TAG.NBT)) { + entityTag = tag.getCompound(ENTITY_TAG.NBT); + + if (context instanceof Map) { + Map map = (Map) context; + + // Duplicated from constructor + String entityType = SerializableMeta.getString(map, ENTITY_ID.BUKKIT, true); + if (entityType != null) { + this.spawnedType = EntityType.fromName(entityType); + } + } + + if (this.spawnedType != null) { + // We have a valid spawn type, just remove the ID now + entityTag.remove(ENTITY_ID.NBT); + } + + // Tag still has some other data, lets try our luck with a conversion + if (!entityTag.isEmpty()) { + // SPIGOT-4128: This is hopeless until we start versioning stacks. RIP data. + // entityTag = (NBTTagCompound) MinecraftServer.getServer().dataConverterManager.update(DataConverterTypes.ENTITY, new Dynamic(DynamicOpsNBT.a, entityTag), -1, CraftMagicNumbers.DATA_VERSION).getValue(); + } + + // See if we can read a converted ID tag + if (entityTag.hasKey(ENTITY_ID.NBT)) { + this.spawnedType = EntityType.fromName(new MinecraftKey(entityTag.getString(ENTITY_ID.NBT)).getKey()); + } + } + } + + @Override + void serializeInternal(Map internalTags) { + if (entityTag != null && !entityTag.isEmpty()) { + internalTags.put(ENTITY_TAG.NBT, entityTag); + } + } + + @Override + void applyToItem(NBTTagCompound tag) { + super.applyToItem(tag); + + if (!isSpawnEggEmpty() && entityTag == null) { + entityTag = new NBTTagCompound(); + } + + if (entityTag != null) { + tag.set(ENTITY_TAG.NBT, entityTag); + } + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case BAT_SPAWN_EGG: + case BLAZE_SPAWN_EGG: + case CAVE_SPIDER_SPAWN_EGG: + case CHICKEN_SPAWN_EGG: + case COD_SPAWN_EGG: + case COW_SPAWN_EGG: + case CREEPER_SPAWN_EGG: + case DOLPHIN_SPAWN_EGG: + case DROWNED_SPAWN_EGG: + case DONKEY_SPAWN_EGG: + case ELDER_GUARDIAN_SPAWN_EGG: + case ENDERMAN_SPAWN_EGG: + case ENDERMITE_SPAWN_EGG: + case EVOKER_SPAWN_EGG: + case GHAST_SPAWN_EGG: + case GUARDIAN_SPAWN_EGG: + case HORSE_SPAWN_EGG: + case HUSK_SPAWN_EGG: + case LLAMA_SPAWN_EGG: + case MAGMA_CUBE_SPAWN_EGG: + case MOOSHROOM_SPAWN_EGG: + case MULE_SPAWN_EGG: + case OCELOT_SPAWN_EGG: + case PARROT_SPAWN_EGG: + case PHANTOM_SPAWN_EGG: + case PIG_SPAWN_EGG: + case POLAR_BEAR_SPAWN_EGG: + case PUFFERFISH_SPAWN_EGG: + case RABBIT_SPAWN_EGG: + case SALMON_SPAWN_EGG: + case SHEEP_SPAWN_EGG: + case SHULKER_SPAWN_EGG: + case SILVERFISH_SPAWN_EGG: + case SKELETON_HORSE_SPAWN_EGG: + case SKELETON_SPAWN_EGG: + case SLIME_SPAWN_EGG: + case SPIDER_SPAWN_EGG: + case SQUID_SPAWN_EGG: + case STRAY_SPAWN_EGG: + case TROPICAL_FISH_SPAWN_EGG: + case TURTLE_SPAWN_EGG: + case VEX_SPAWN_EGG: + case VILLAGER_SPAWN_EGG: + case VINDICATOR_SPAWN_EGG: + case WITCH_SPAWN_EGG: + case WITHER_SKELETON_SPAWN_EGG: + case WOLF_SPAWN_EGG: + case ZOMBIE_HORSE_SPAWN_EGG: + case ZOMBIE_PIGMAN_SPAWN_EGG: + case ZOMBIE_SPAWN_EGG: + case ZOMBIE_VILLAGER_SPAWN_EGG: + return true; + default: + return false; + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isSpawnEggEmpty(); + } + + boolean isSpawnEggEmpty() { + return !(hasSpawnedType() || entityTag != null); + } + + boolean hasSpawnedType() { + return spawnedType != null; + } + + @Override + public EntityType getSpawnedType() { + throw new UnsupportedOperationException("Must check item type to get spawned type"); + } + + @Override + public void setSpawnedType(EntityType type) { + throw new UnsupportedOperationException("Must change item type to set spawned type"); + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaSpawnEgg) { + CraftMetaSpawnEgg that = (CraftMetaSpawnEgg) meta; + + return hasSpawnedType() ? that.hasSpawnedType() && this.spawnedType.equals(that.spawnedType) : !that.hasSpawnedType() + && entityTag != null ? that.entityTag != null && this.entityTag.equals(that.entityTag) : entityTag == null; + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaSpawnEgg || isSpawnEggEmpty()); + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + + if (hasSpawnedType()) { + hash = 73 * hash + spawnedType.hashCode(); + } + if (entityTag != null) { + hash = 73 * hash + entityTag.hashCode(); + } + + return original != hash ? CraftMetaSpawnEgg.class.hashCode() ^ hash : hash; + } + + @Override + Builder serialize(Builder builder) { + super.serialize(builder); + + return builder; + } + + @Override + public CraftMetaSpawnEgg clone() { + CraftMetaSpawnEgg clone = (CraftMetaSpawnEgg) super.clone(); + + clone.spawnedType = spawnedType; + if (entityTag != null) { + clone.entityTag = entityTag.clone(); + } + + return clone; + } + + @Override + final Material updateMaterial(Material material) { + if (spawnedType == null) { + spawnedType = EntityType.fromId(getDamage()); + setDamage(0); + } + + if (spawnedType != null) { + if (entityTag != null) { + // Remove ID tag as it is now in the material + entityTag.remove(ENTITY_ID.NBT); + } + + return CraftLegacy.fromLegacy(new MaterialData(Material.LEGACY_MONSTER_EGG, (byte) spawnedType.getTypeId())); + } + + return super.updateMaterial(material); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java new file mode 100644 index 000000000000..f27df320b511 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java @@ -0,0 +1,168 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Map; + +import net.minecraft.server.NBTTagCompound; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.inventory.CraftMetaItem.SerializableMeta; + +import com.google.common.collect.ImmutableMap; +import org.bukkit.DyeColor; +import org.bukkit.craftbukkit.entity.CraftTropicalFish; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.TropicalFish; +import org.bukkit.inventory.meta.TropicalFishBucketMeta; + +@DelegateDeserialization(SerializableMeta.class) +class CraftMetaTropicalFishBucket extends CraftMetaItem implements TropicalFishBucketMeta { + static final ItemMetaKey VARIANT = new ItemMetaKey("BucketVariantTag", "fish-variant"); + + private Integer variant; + + CraftMetaTropicalFishBucket(CraftMetaItem meta) { + super(meta); + + if (!(meta instanceof CraftMetaTropicalFishBucket)) { + return; + } + + CraftMetaTropicalFishBucket bucket = (CraftMetaTropicalFishBucket) meta; + this.variant = bucket.variant; + } + + CraftMetaTropicalFishBucket(NBTTagCompound tag) { + super(tag); + + if (tag.hasKeyOfType(VARIANT.NBT, CraftMagicNumbers.NBT.TAG_INT)) { + this.variant = tag.getInt(VARIANT.NBT); + } + } + + CraftMetaTropicalFishBucket(Map map) { + super(map); + + Integer variant = SerializableMeta.getObject(Integer.class, map, VARIANT.BUKKIT, true); + if (variant != null) { + this.variant = variant; + } + } + + @Override + void applyToItem(NBTTagCompound tag) { + super.applyToItem(tag); + + if (hasVariant()) { + tag.setInt(VARIANT.NBT, variant); + } + } + + @Override + boolean applicableTo(Material type) { + switch (type) { + case TROPICAL_FISH_BUCKET: + return true; + default: + return false; + } + } + + @Override + boolean isEmpty() { + return super.isEmpty() && isBucketEmpty(); + } + + boolean isBucketEmpty() { + return !(hasVariant()); + } + + @Override + public DyeColor getPatternColor() { + return CraftTropicalFish.getPatternColor(variant); + } + + @Override + public void setPatternColor(DyeColor color) { + if (variant == null) { + variant = 0; + } + variant = CraftTropicalFish.getData(color, getPatternColor(), getPattern()); + } + + @Override + public DyeColor getBodyColor() { + return CraftTropicalFish.getBodyColor(variant); + } + + @Override + public void setBodyColor(DyeColor color) { + if (variant == null) { + variant = 0; + } + variant = CraftTropicalFish.getData(getPatternColor(), color, getPattern()); + } + + @Override + public TropicalFish.Pattern getPattern() { + return CraftTropicalFish.getPattern(variant); + } + + @Override + public void setPattern(TropicalFish.Pattern pattern) { + if (variant == null) { + variant = 0; + } + variant = CraftTropicalFish.getData(getPatternColor(), getBodyColor(), pattern); + } + + @Override + public boolean hasVariant() { + return variant != null; + } + + @Override + boolean equalsCommon(CraftMetaItem meta) { + if (!super.equalsCommon(meta)) { + return false; + } + if (meta instanceof CraftMetaTropicalFishBucket) { + CraftMetaTropicalFishBucket that = (CraftMetaTropicalFishBucket) meta; + + return (hasVariant() ? that.hasVariant() && this.variant.equals(that.variant) : !that.hasVariant()); + } + return true; + } + + @Override + boolean notUncommon(CraftMetaItem meta) { + return super.notUncommon(meta) && (meta instanceof CraftMetaTropicalFishBucket || isBucketEmpty()); + } + + @Override + int applyHash() { + final int original; + int hash = original = super.applyHash(); + + if (hasVariant()) { + hash = 61 * hash + variant; + } + + return original != hash ? CraftMetaTropicalFishBucket.class.hashCode() ^ hash : hash; + } + + + public CraftMetaTropicalFishBucket clone() { + return (CraftMetaTropicalFishBucket) super.clone(); + } + + @Override + ImmutableMap.Builder serialize(ImmutableMap.Builder builder) { + super.serialize(builder); + + if (hasVariant()) { + builder.put(VARIANT.BUKKIT, variant); + } + + return builder; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java new file mode 100644 index 000000000000..18743dda4955 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java @@ -0,0 +1,62 @@ +package org.bukkit.craftbukkit.inventory; + +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.List; +import net.minecraft.server.RecipeItemStack; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.Recipe; +import org.bukkit.inventory.RecipeChoice; + +public interface CraftRecipe extends Recipe { + + void addToCraftingManager(); + + default RecipeItemStack toNMS(RecipeChoice bukkit, boolean requireNotEmpty) { + RecipeItemStack stack; + + if (bukkit == null) { + stack = RecipeItemStack.a; + } else if (bukkit instanceof RecipeChoice.MaterialChoice) { + stack = new RecipeItemStack(((RecipeChoice.MaterialChoice) bukkit).getChoices().stream().map((mat) -> new net.minecraft.server.RecipeItemStack.StackProvider(CraftItemStack.asNMSCopy(new ItemStack(mat))))); + } else if (bukkit instanceof RecipeChoice.ExactChoice) { + stack = new RecipeItemStack(((RecipeChoice.ExactChoice) bukkit).getChoices().stream().map((mat) -> new net.minecraft.server.RecipeItemStack.StackProvider(CraftItemStack.asNMSCopy(mat)))); + stack.exact = true; + } else { + throw new IllegalArgumentException("Unknown recipe stack instance " + bukkit); + } + + stack.buildChoices(); + if (requireNotEmpty && stack.choices.length == 0) { + throw new IllegalArgumentException("Recipe requires at least one non-air choice!"); + } + + return stack; + } + + public static RecipeChoice toBukkit(RecipeItemStack list) { + list.buildChoices(); + + if (list.choices.length == 0) { + return null; + } + + if (list.exact) { + List choices = new ArrayList<>(list.choices.length); + for (net.minecraft.server.ItemStack i : list.choices) { + choices.add(CraftItemStack.asBukkitCopy(i)); + } + + return new RecipeChoice.ExactChoice(choices); + } else { + + List choices = new ArrayList<>(list.choices.length); + for (net.minecraft.server.ItemStack i : list.choices) { + choices.add(CraftMagicNumbers.getMaterial(i.getItem())); + } + + return new RecipeChoice.MaterialChoice(choices); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftSaddledInventory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftSaddledInventory.java new file mode 100644 index 000000000000..99cfbaf90b5c --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftSaddledInventory.java @@ -0,0 +1,15 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.IInventory; + +import org.bukkit.inventory.AbstractHorseInventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.SaddledHorseInventory; + +public class CraftSaddledInventory extends CraftInventoryAbstractHorse implements SaddledHorseInventory { + + public CraftSaddledInventory(IInventory inventory) { + super(inventory); + } + +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapedRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapedRecipe.java new file mode 100644 index 000000000000..1d06a5976930 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapedRecipe.java @@ -0,0 +1,62 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Map; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.NonNullList; +import net.minecraft.server.RecipeItemStack; +import net.minecraft.server.ShapedRecipes; + +import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.RecipeChoice; +import org.bukkit.inventory.ShapedRecipe; + +public class CraftShapedRecipe extends ShapedRecipe implements CraftRecipe { + // TODO: Could eventually use this to add a matches() method or some such + private ShapedRecipes recipe; + + public CraftShapedRecipe(NamespacedKey key, ItemStack result) { + super(key, result); + } + + public CraftShapedRecipe(ItemStack result, ShapedRecipes recipe) { + this(CraftNamespacedKey.fromMinecraft(recipe.getKey()), result); + this.recipe = recipe; + } + + public static CraftShapedRecipe fromBukkitRecipe(ShapedRecipe recipe) { + if (recipe instanceof CraftShapedRecipe) { + return (CraftShapedRecipe) recipe; + } + CraftShapedRecipe ret = new CraftShapedRecipe(recipe.getKey(), recipe.getResult()); + ret.setGroup(recipe.getGroup()); + String[] shape = recipe.getShape(); + ret.shape(shape); + Map ingredientMap = recipe.getChoiceMap(); + for (char c : ingredientMap.keySet()) { + RecipeChoice stack = ingredientMap.get(c); + if (stack != null) { + ret.setIngredient(c, stack); + } + } + return ret; + } + + public void addToCraftingManager() { + String[] shape = this.getShape(); + Map ingred = this.getChoiceMap(); + int width = shape[0].length(); + NonNullList data = NonNullList.a(shape.length * width, RecipeItemStack.a); + + for (int i = 0; i < shape.length; i++) { + String row = shape[i]; + for (int j = 0; j < row.length(); j++) { + data.set(i * width + j, toNMS(ingred.get(row.charAt(j)), false)); + } + } + + MinecraftServer.getServer().getCraftingManager().a(new ShapedRecipes(CraftNamespacedKey.toMinecraft(this.getKey()), this.getGroup(), width, shape.length, data, CraftItemStack.asNMSCopy(this.getResult()))); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java new file mode 100644 index 000000000000..0773b1359608 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftShapelessRecipe.java @@ -0,0 +1,50 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.List; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.NonNullList; +import net.minecraft.server.RecipeItemStack; +import net.minecraft.server.ShapelessRecipes; + +import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.RecipeChoice; +import org.bukkit.inventory.ShapelessRecipe; + +public class CraftShapelessRecipe extends ShapelessRecipe implements CraftRecipe { + // TODO: Could eventually use this to add a matches() method or some such + private ShapelessRecipes recipe; + + public CraftShapelessRecipe(NamespacedKey key, ItemStack result) { + super(key, result); + } + + public CraftShapelessRecipe(ItemStack result, ShapelessRecipes recipe) { + this(CraftNamespacedKey.fromMinecraft(recipe.getKey()), result); + this.recipe = recipe; + } + + public static CraftShapelessRecipe fromBukkitRecipe(ShapelessRecipe recipe) { + if (recipe instanceof CraftShapelessRecipe) { + return (CraftShapelessRecipe) recipe; + } + CraftShapelessRecipe ret = new CraftShapelessRecipe(recipe.getKey(), recipe.getResult()); + ret.setGroup(recipe.getGroup()); + for (RecipeChoice ingred : recipe.getChoiceList()) { + ret.addIngredient(ingred); + } + return ret; + } + + public void addToCraftingManager() { + List ingred = this.getChoiceList(); + NonNullList data = NonNullList.a(ingred.size(), RecipeItemStack.a); + for (int i = 0; i < ingred.size(); i++) { + data.set(i, toNMS(ingred.get(i), true)); + } + + MinecraftServer.getServer().getCraftingManager().a(new ShapelessRecipes(CraftNamespacedKey.toMinecraft(this.getKey()), this.getGroup(), CraftItemStack.asNMSCopy(this.getResult()), data)); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/InventoryIterator.java b/src/main/java/org/bukkit/craftbukkit/inventory/InventoryIterator.java new file mode 100644 index 000000000000..e3b5f42adac5 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/InventoryIterator.java @@ -0,0 +1,64 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.ListIterator; + +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +public class InventoryIterator implements ListIterator { + private final Inventory inventory; + private int nextIndex; + private Boolean lastDirection; // true = forward, false = backward, null = haven't moved yet + + InventoryIterator(Inventory craftInventory) { + this.inventory = craftInventory; + this.nextIndex = 0; + } + + InventoryIterator(Inventory craftInventory, int index) { + this.inventory = craftInventory; + this.nextIndex = index; + } + + public boolean hasNext() { + return nextIndex < inventory.getSize(); + } + + public ItemStack next() { + lastDirection = true; + return inventory.getItem(nextIndex++); + } + + public int nextIndex() { + return nextIndex; + } + + public boolean hasPrevious() { + return nextIndex > 0; + } + + public ItemStack previous() { + lastDirection = false; + return inventory.getItem(--nextIndex); + } + + public int previousIndex() { + return nextIndex - 1; + } + + public void set(ItemStack item) { + if (lastDirection == null) { + throw new IllegalStateException("No current item!"); + } + int i = lastDirection ? nextIndex - 1 : nextIndex; + inventory.setItem(i, item); + } + + public void add(ItemStack item) { + throw new UnsupportedOperationException("Can't change the size of an inventory!"); + } + + public void remove() { + throw new UnsupportedOperationException("Can't change the size of an inventory!"); + } +} \ No newline at end of file diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/InventoryWrapper.java b/src/main/java/org/bukkit/craftbukkit/inventory/InventoryWrapper.java new file mode 100644 index 000000000000..9c5ec924eb2e --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/InventoryWrapper.java @@ -0,0 +1,191 @@ +package org.bukkit.craftbukkit.inventory; + +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import java.util.ArrayList; +import java.util.List; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.IChatBaseComponent; +import net.minecraft.server.IInventory; +import net.minecraft.server.ItemStack; +import org.bukkit.Location; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; + +public class InventoryWrapper implements IInventory { + + private final Inventory inventory; + private final List viewers = new ArrayList(); + + public InventoryWrapper(Inventory inventory) { + this.inventory = inventory; + } + + @Override + public int getSize() { + return inventory.getSize(); + } + + @Override + public ItemStack getItem(int i) { + return CraftItemStack.asNMSCopy(inventory.getItem(i)); + } + + @Override + public ItemStack splitStack(int i, int j) { + // Copied from CraftItemStack + ItemStack stack = getItem(i); + ItemStack result; + if (stack.isEmpty()) { + return stack; + } + if (stack.getCount() <= j) { + this.setItem(i, ItemStack.a); + result = stack; + } else { + result = CraftItemStack.copyNMSStack(stack, j); + stack.subtract(j); + } + this.update(); + return result; + } + + @Override + public ItemStack splitWithoutUpdate(int i) { + // Copied from CraftItemStack + ItemStack stack = getItem(i); + ItemStack result; + if (stack.isEmpty()) { + return stack; + } + if (stack.getCount() <= 1) { + this.setItem(i, ItemStack.a); + result = stack; + } else { + result = CraftItemStack.copyNMSStack(stack, 1); + stack.subtract(1); + } + return result; + } + + @Override + public void setItem(int i, ItemStack itemstack) { + inventory.setItem(i, CraftItemStack.asBukkitCopy(itemstack)); + } + + @Override + public int getMaxStackSize() { + return inventory.getMaxStackSize(); + } + + @Override + public void update() { + } + + @Override + public boolean a(EntityHuman entityhuman) { + return true; + } + + @Override + public void startOpen(EntityHuman entityhuman) { + } + + @Override + public void closeContainer(EntityHuman entityhuman) { + } + + @Override + public boolean b(int i, ItemStack itemstack) { + return true; + } + + @Override + public int getProperty(int i) { + return 0; + } + + @Override + public void setProperty(int i, int j) { + } + + @Override + public int h() { + return 0; + } + + @Override + public void clear() { + inventory.clear(); + } + + @Override + public List getContents() { + int size = getSize(); + List items = new ArrayList(size); + + for (int i = 0; i < size; i++) { + items.set(i, getItem(i)); + } + + return items; + } + + @Override + public void onOpen(CraftHumanEntity who) { + viewers.add(who); + } + + @Override + public void onClose(CraftHumanEntity who) { + viewers.remove(who); + } + + @Override + public List getViewers() { + return viewers; + } + + @Override + public InventoryHolder getOwner() { + return inventory.getHolder(); + } + + @Override + public void setMaxStackSize(int size) { + inventory.setMaxStackSize(size); + } + + @Override + public IChatBaseComponent getDisplayName() { + return CraftChatMessage.fromStringOrNull(inventory.getName()); + } + + @Override + public IChatBaseComponent getCustomName() { + return getDisplayName(); + } + + @Override + public boolean hasCustomName() { + return inventory.getName() != null; + } + + @Override + public IChatBaseComponent getScoreboardDisplayName() { + return getDisplayName(); + } + + @Override + public Location getLocation() { + return inventory.getLocation(); + } + + @Override + public boolean P_() { + return Iterables.any(inventory, Predicates.notNull()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/RecipeIterator.java b/src/main/java/org/bukkit/craftbukkit/inventory/RecipeIterator.java new file mode 100644 index 000000000000..165225e732d4 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/RecipeIterator.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Iterator; + +import org.bukkit.inventory.Recipe; + +import net.minecraft.server.IRecipe; +import net.minecraft.server.MinecraftServer; + +public class RecipeIterator implements Iterator { + private final Iterator recipes; + + public RecipeIterator() { + this.recipes = MinecraftServer.getServer().getCraftingManager().recipes.values().iterator(); + } + + public boolean hasNext() { + return recipes.hasNext(); + } + + public Recipe next() { + return recipes.next().toBukkitRecipe(); + } + + public void remove() { + recipes.remove(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/tags/CraftCustomItemTagContainer.java b/src/main/java/org/bukkit/craftbukkit/inventory/tags/CraftCustomItemTagContainer.java new file mode 100644 index 000000000000..fe663bbae59d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/tags/CraftCustomItemTagContainer.java @@ -0,0 +1,133 @@ +package org.bukkit.craftbukkit.inventory.tags; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import net.minecraft.server.NBTBase; +import net.minecraft.server.NBTTagCompound; +import org.apache.commons.lang.Validate; +import org.bukkit.NamespacedKey; +import org.bukkit.craftbukkit.inventory.CraftCustomTagTypeRegistry; +import org.bukkit.craftbukkit.util.CraftNBTTagConfigSerializer; +import org.bukkit.inventory.meta.tags.CustomItemTagContainer; +import org.bukkit.inventory.meta.tags.ItemTagAdapterContext; +import org.bukkit.inventory.meta.tags.ItemTagType; + +public final class CraftCustomItemTagContainer implements CustomItemTagContainer { + + private final Map customTags = new HashMap<>(); + private final CraftCustomTagTypeRegistry tagTypeRegistry; + private final CraftItemTagAdapterContext adapterContext; + + public CraftCustomItemTagContainer(Map customTags, CraftCustomTagTypeRegistry tagTypeRegistry) { + this(tagTypeRegistry); + this.customTags.putAll(customTags); + } + + public CraftCustomItemTagContainer(CraftCustomTagTypeRegistry tagTypeRegistry) { + this.tagTypeRegistry = tagTypeRegistry; + this.adapterContext = new CraftItemTagAdapterContext(this.tagTypeRegistry); + } + + @Override + public void setCustomTag(NamespacedKey key, ItemTagType type, Z value) { + Validate.notNull(key, "The provided key for the custom value was null"); + Validate.notNull(type, "The provided type for the custom value was null"); + Validate.notNull(value, "The provided value for the custom value was null"); + + this.customTags.put(key.toString(), tagTypeRegistry.wrap(type.getPrimitiveType(), type.toPrimitive(value, adapterContext))); + } + + @Override + public boolean hasCustomTag(NamespacedKey key, ItemTagType type) { + Validate.notNull(key, "The provided key for the custom value was null"); + Validate.notNull(type, "The provided type for the custom value was null"); + + NBTBase value = this.customTags.get(key.toString()); + if (value == null) { + return false; + } + + return tagTypeRegistry.isInstanceOf(type.getPrimitiveType(), value); + } + + @Override + public Z getCustomTag(NamespacedKey key, ItemTagType type) { + Validate.notNull(key, "The provided key for the custom value was null"); + Validate.notNull(type, "The provided type for the custom value was null"); + + NBTBase value = this.customTags.get(key.toString()); + if (value == null) { + return null; + } + + return type.fromPrimitive(tagTypeRegistry.extract(type.getPrimitiveType(), value), adapterContext); + } + + @Override + public void removeCustomTag(NamespacedKey key) { + Validate.notNull(key, "The provided key for the custom value was null"); + + this.customTags.remove(key.toString()); + } + + @Override + public boolean isEmpty() { + return this.customTags.isEmpty(); + } + + @Override + public ItemTagAdapterContext getAdapterContext() { + return this.adapterContext; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof CraftCustomItemTagContainer)) { + return false; + } + + Map myRawMap = getRaw(); + Map theirRawMap = ((CraftCustomItemTagContainer) obj).getRaw(); + + return Objects.equals(myRawMap, theirRawMap); + } + + public NBTTagCompound toTagCompound() { + NBTTagCompound tag = new NBTTagCompound(); + for (Entry entry : this.customTags.entrySet()) { + tag.set(entry.getKey(), entry.getValue()); + } + return tag; + } + + public void put(String key, NBTBase base) { + this.customTags.put(key, base); + } + + public void putAll(Map map) { + this.customTags.putAll(map); + } + + public void putAll(NBTTagCompound compound) { + for (String key : compound.getKeys()) { + this.customTags.put(key, compound.get(key)); + } + } + + public Map getRaw() { + return this.customTags; + } + + @Override + public int hashCode() { + int hashCode = 3; + hashCode += this.customTags.hashCode(); // We will simply add the maps hashcode + return hashCode; + } + + public Map serialize() { + return (Map) CraftNBTTagConfigSerializer.serialize(toTagCompound()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/tags/CraftItemTagAdapterContext.java b/src/main/java/org/bukkit/craftbukkit/inventory/tags/CraftItemTagAdapterContext.java new file mode 100644 index 000000000000..5b81cad1f41f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/tags/CraftItemTagAdapterContext.java @@ -0,0 +1,24 @@ +package org.bukkit.craftbukkit.inventory.tags; + +import org.bukkit.craftbukkit.inventory.CraftCustomTagTypeRegistry; +import org.bukkit.inventory.meta.tags.CustomItemTagContainer; +import org.bukkit.inventory.meta.tags.ItemTagAdapterContext; + +public final class CraftItemTagAdapterContext implements ItemTagAdapterContext { + + private final CraftCustomTagTypeRegistry registry; + + public CraftItemTagAdapterContext(CraftCustomTagTypeRegistry registry) { + this.registry = registry; + } + + /** + * Creates a new and empty tag container instance + * + * @return the fresh container instance + */ + @Override + public CustomItemTagContainer newTagContainer() { + return new CraftCustomItemTagContainer(this.registry); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftCustomInventoryConverter.java b/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftCustomInventoryConverter.java new file mode 100644 index 000000000000..ed4415f6dd58 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftCustomInventoryConverter.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.inventory.util; + +import org.bukkit.craftbukkit.inventory.CraftInventoryCustom; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; + +public class CraftCustomInventoryConverter implements CraftInventoryCreator.InventoryConverter { + + @Override + public Inventory createInventory(InventoryHolder holder, InventoryType type) { + return new CraftInventoryCustom(holder, type); + } + + @Override + public Inventory createInventory(InventoryHolder owner, InventoryType type, String title) { + return new CraftInventoryCustom(owner, type, title); + } + + public Inventory createInventory(InventoryHolder owner, int size) { + return new CraftInventoryCustom(owner, size); + } + + public Inventory createInventory(InventoryHolder owner, int size, String title) { + return new CraftInventoryCustom(owner, size, title); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftInventoryCreator.java b/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftInventoryCreator.java new file mode 100644 index 000000000000..d25928e09418 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftInventoryCreator.java @@ -0,0 +1,65 @@ +package org.bukkit.craftbukkit.inventory.util; + +import java.util.HashMap; +import java.util.Map; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; + +public final class CraftInventoryCreator { + + public static final CraftInventoryCreator INSTANCE = new CraftInventoryCreator(); + // + private final CraftCustomInventoryConverter DEFAULT_CONVERTER = new CraftCustomInventoryConverter(); + private final Map converterMap = new HashMap<>(); + + private CraftInventoryCreator() { + converterMap.put(InventoryType.CHEST, DEFAULT_CONVERTER); + converterMap.put(InventoryType.DISPENSER, new CraftTileInventoryConverter.Dispenser()); + converterMap.put(InventoryType.DROPPER, new CraftTileInventoryConverter.Dropper()); + converterMap.put(InventoryType.FURNACE, new CraftTileInventoryConverter.Furnace()); + converterMap.put(InventoryType.WORKBENCH, DEFAULT_CONVERTER); + converterMap.put(InventoryType.ENCHANTING, DEFAULT_CONVERTER); + converterMap.put(InventoryType.BREWING, new CraftTileInventoryConverter.BrewingStand()); + converterMap.put(InventoryType.PLAYER, DEFAULT_CONVERTER); + converterMap.put(InventoryType.MERCHANT, DEFAULT_CONVERTER); + converterMap.put(InventoryType.ENDER_CHEST, DEFAULT_CONVERTER); + converterMap.put(InventoryType.ANVIL, DEFAULT_CONVERTER); + converterMap.put(InventoryType.BEACON, new CraftTileInventoryConverter.Beacon()); + converterMap.put(InventoryType.HOPPER, new CraftTileInventoryConverter.Hopper()); + converterMap.put(InventoryType.SHULKER_BOX, DEFAULT_CONVERTER); + } + + public Inventory createInventory(InventoryHolder holder, InventoryType type) { + // Paper start + if (holder != null) { + return DEFAULT_CONVERTER.createInventory(holder, type); + } + //noinspection ConstantConditions // Paper end + return converterMap.get(type).createInventory(holder, type); + } + + public Inventory createInventory(InventoryHolder holder, InventoryType type, String title) { + // Paper start + if (holder != null) { + return DEFAULT_CONVERTER.createInventory(holder, type, title); + } + //noinspection ConstantConditions // Paper end + return converterMap.get(type).createInventory(holder, type, title); + } + + public Inventory createInventory(InventoryHolder holder, int size) { + return DEFAULT_CONVERTER.createInventory(holder, size); + } + + public Inventory createInventory(InventoryHolder holder, int size, String title) { + return DEFAULT_CONVERTER.createInventory(holder, size, title); + } + + public interface InventoryConverter { + + Inventory createInventory(InventoryHolder holder, InventoryType type); + + Inventory createInventory(InventoryHolder holder, InventoryType type, String title); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftTileInventoryConverter.java b/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftTileInventoryConverter.java new file mode 100644 index 000000000000..bb09139f80a4 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftTileInventoryConverter.java @@ -0,0 +1,126 @@ +package org.bukkit.craftbukkit.inventory.util; + +import net.minecraft.server.DimensionManager; +import net.minecraft.server.ITileInventory; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.TileEntityBeacon; +import net.minecraft.server.TileEntityBrewingStand; +import net.minecraft.server.TileEntityDispenser; +import net.minecraft.server.TileEntityDropper; +import net.minecraft.server.TileEntityFurnace; +import net.minecraft.server.TileEntityHopper; +import net.minecraft.server.TileEntityLootable; +import org.bukkit.craftbukkit.inventory.CraftInventory; +import org.bukkit.craftbukkit.inventory.CraftInventoryBeacon; +import org.bukkit.craftbukkit.inventory.CraftInventoryBrewer; +import org.bukkit.craftbukkit.inventory.CraftInventoryFurnace; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; + +public abstract class CraftTileInventoryConverter implements CraftInventoryCreator.InventoryConverter { + + public abstract ITileInventory getTileEntity(); + + @Override + public Inventory createInventory(InventoryHolder holder, InventoryType type) { + return getInventory(getTileEntity()); + } + + @Override + public Inventory createInventory(InventoryHolder holder, InventoryType type, String title) { + ITileInventory te = getTileEntity(); + if (te instanceof TileEntityLootable) { + ((TileEntityLootable) te).setCustomName(CraftChatMessage.fromStringOrNull(title)); + } + + return getInventory(te); + } + + public Inventory getInventory(ITileInventory tileEntity) { + return new CraftInventory(tileEntity); + } + + public static class Furnace extends CraftTileInventoryConverter { + + @Override + public ITileInventory getTileEntity() { + TileEntityFurnace furnace = new TileEntityFurnace(); + furnace.setWorld(MinecraftServer.getServer().getWorldServer(DimensionManager.OVERWORLD)); // TODO: customize this if required + return furnace; + } + + @Override + public Inventory createInventory(InventoryHolder owner, InventoryType type, String title) { + ITileInventory tileEntity = getTileEntity(); + ((TileEntityFurnace) tileEntity).setCustomName(CraftChatMessage.fromStringOrNull(title)); + return getInventory(tileEntity); + } + + @Override + public Inventory getInventory(ITileInventory tileEntity) { + return new CraftInventoryFurnace((TileEntityFurnace) tileEntity); + } + } + + public static class BrewingStand extends CraftTileInventoryConverter { + + @Override + public ITileInventory getTileEntity() { + return new TileEntityBrewingStand(); + } + + @Override + public Inventory createInventory(InventoryHolder holder, InventoryType type, String title) { + // BrewingStand does not extend TileEntityLootable + ITileInventory tileEntity = getTileEntity(); + if (tileEntity instanceof TileEntityBrewingStand) { + ((TileEntityBrewingStand) tileEntity).setCustomName(CraftChatMessage.fromStringOrNull(title)); + } + return getInventory(tileEntity); + } + + @Override + public Inventory getInventory(ITileInventory tileEntity) { + return new CraftInventoryBrewer(tileEntity); + } + } + + public static class Beacon extends CraftTileInventoryConverter { + + @Override + public ITileInventory getTileEntity() { + return new TileEntityBeacon(); + } + + @Override + public Inventory getInventory(ITileInventory tileInventory) { + return new CraftInventoryBeacon((TileEntityBeacon) tileInventory); + } + } + + public static class Dispenser extends CraftTileInventoryConverter { + + @Override + public ITileInventory getTileEntity() { + return new TileEntityDispenser(); + } + } + + public static class Dropper extends CraftTileInventoryConverter { + + @Override + public ITileInventory getTileEntity() { + return new TileEntityDropper(); + } + } + + public static class Hopper extends CraftTileInventoryConverter { + + @Override + public ITileInventory getTileEntity() { + return new TileEntityHopper(); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/map/CraftMapCanvas.java b/src/main/java/org/bukkit/craftbukkit/map/CraftMapCanvas.java new file mode 100644 index 000000000000..7372614c762c --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/map/CraftMapCanvas.java @@ -0,0 +1,111 @@ +package org.bukkit.craftbukkit.map; + +import java.awt.Image; +import java.util.Arrays; +import org.bukkit.map.MapCanvas; +import org.bukkit.map.MapCursorCollection; +import org.bukkit.map.MapFont; +import org.bukkit.map.MapFont.CharacterSprite; +import org.bukkit.map.MapPalette; + +public class CraftMapCanvas implements MapCanvas { + + private final byte[] buffer = new byte[128 * 128]; + private final CraftMapView mapView; + private byte[] base; + private MapCursorCollection cursors = new MapCursorCollection(); + + protected CraftMapCanvas(CraftMapView mapView) { + this.mapView = mapView; + Arrays.fill(buffer, (byte) -1); + } + + public CraftMapView getMapView() { + return mapView; + } + + public MapCursorCollection getCursors() { + return cursors; + } + + public void setCursors(MapCursorCollection cursors) { + this.cursors = cursors; + } + + public void setPixel(int x, int y, byte color) { + if (x < 0 || y < 0 || x >= 128 || y >= 128) + return; + if (buffer[y * 128 + x] != color) { + buffer[y * 128 + x] = color; + mapView.worldMap.flagDirty(x, y); + } + } + + public byte getPixel(int x, int y) { + if (x < 0 || y < 0 || x >= 128 || y >= 128) + return 0; + return buffer[y * 128 + x]; + } + + public byte getBasePixel(int x, int y) { + if (x < 0 || y < 0 || x >= 128 || y >= 128) + return 0; + return base[y * 128 + x]; + } + + protected void setBase(byte[] base) { + this.base = base; + } + + protected byte[] getBuffer() { + return buffer; + } + + public void drawImage(int x, int y, Image image) { + byte[] bytes = MapPalette.imageToBytes(image); + for (int x2 = 0; x2 < image.getWidth(null); ++x2) { + for (int y2 = 0; y2 < image.getHeight(null); ++y2) { + setPixel(x + x2, y + y2, bytes[y2 * image.getWidth(null) + x2]); + } + } + } + + public void drawText(int x, int y, MapFont font, String text) { + int xStart = x; + byte color = MapPalette.DARK_GRAY; + if (!font.isValid(text)) { + throw new IllegalArgumentException("text contains invalid characters"); + } + + for (int i = 0; i < text.length(); ++i) { + char ch = text.charAt(i); + if (ch == '\n') { + x = xStart; + y += font.getHeight() + 1; + continue; + } else if (ch == '\u00A7') { + int j = text.indexOf(';', i); + if (j >= 0) { + try { + color = Byte.parseByte(text.substring(i + 1, j)); + i = j; + continue; + } + catch (NumberFormatException ex) {} + } + throw new IllegalArgumentException("Text contains unterminated color string"); + } + + CharacterSprite sprite = font.getChar(text.charAt(i)); + for (int r = 0; r < font.getHeight(); ++r) { + for (int c = 0; c < sprite.getWidth(); ++c) { + if (sprite.get(r, c)) { + setPixel(x + c, y + r, color); + } + } + } + x += sprite.getWidth() + 1; + } + } + +} diff --git a/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java b/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java new file mode 100644 index 000000000000..e6c6a675d4ee --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java @@ -0,0 +1,50 @@ +package org.bukkit.craftbukkit.map; + +import net.minecraft.server.WorldMap; +import net.minecraft.server.MapIcon; + +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.entity.Player; +import org.bukkit.map.MapCanvas; +import org.bukkit.map.MapCursorCollection; +import org.bukkit.map.MapRenderer; +import org.bukkit.map.MapView; + +public class CraftMapRenderer extends MapRenderer { + + private final WorldMap worldMap; + + public CraftMapRenderer(CraftMapView mapView, WorldMap worldMap) { + super(false); + this.worldMap = worldMap; + } + + @Override + public void render(MapView map, MapCanvas canvas, Player player) { + // Map + for (int x = 0; x < 128; ++x) { + for (int y = 0; y < 128; ++y) { + canvas.setPixel(x, y, worldMap.colors[y * 128 + x]); + } + } + + // Cursors + MapCursorCollection cursors = canvas.getCursors(); + while (cursors.size() > 0) { + cursors.removeCursor(cursors.getCursor(0)); + } + + for (Object key : worldMap.decorations.keySet()) { + // If this cursor is for a player check visibility with vanish system + Player other = Bukkit.getPlayerExact((String) key); + if (other != null && !player.canSee(other)) { + continue; + } + + MapIcon decoration = (MapIcon) worldMap.decorations.get(key); + cursors.addCursor(decoration.getX(), decoration.getY(), (byte) (decoration.getRotation() & 15), decoration.b().a(), true, CraftChatMessage.fromComponent(decoration.g())); + } + } + +} diff --git a/src/main/java/org/bukkit/craftbukkit/map/CraftMapView.java b/src/main/java/org/bukkit/craftbukkit/map/CraftMapView.java new file mode 100644 index 000000000000..d52fcde17957 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/map/CraftMapView.java @@ -0,0 +1,178 @@ +package org.bukkit.craftbukkit.map; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; + +import net.minecraft.server.DimensionManager; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.WorldMap; +import net.minecraft.server.WorldServer; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.map.MapRenderer; +import org.bukkit.map.MapView; + +public final class CraftMapView implements MapView { + + private final Map renderCache = new HashMap(); + private final List renderers = new ArrayList(); + private final Map> canvases = new HashMap>(); + protected final WorldMap worldMap; + + public CraftMapView(WorldMap worldMap) { + this.worldMap = worldMap; + addRenderer(new CraftMapRenderer(this, worldMap)); + } + + @Override + public int getId() { + String text = worldMap.getId(); + if (text.startsWith("map_")) { + try { + return Integer.parseInt(text.substring("map_".length())); + } + catch (NumberFormatException ex) { + throw new IllegalStateException("Map has non-numeric ID"); + } + } else { + throw new IllegalStateException("Map has invalid ID"); + } + } + + public boolean isVirtual() { + return renderers.size() > 0 && !(renderers.get(0) instanceof CraftMapRenderer); + } + + public Scale getScale() { + return Scale.valueOf(worldMap.scale); + } + + public void setScale(Scale scale) { + worldMap.scale = scale.getValue(); + } + + public World getWorld() { + DimensionManager dimension = worldMap.map; + WorldServer world = MinecraftServer.getServer().getWorldServer(dimension); + + return (world == null) ? null : world.getWorld(); + } + + public void setWorld(World world) { + worldMap.map = ((CraftWorld) world).getHandle().dimension; + } + + public int getCenterX() { + return worldMap.centerX; + } + + public int getCenterZ() { + return worldMap.centerZ; + } + + public void setCenterX(int x) { + worldMap.centerX = x; + } + + public void setCenterZ(int z) { + worldMap.centerZ = z; + } + + public List getRenderers() { + return new ArrayList(renderers); + } + + public void addRenderer(MapRenderer renderer) { + if (!renderers.contains(renderer)) { + renderers.add(renderer); + canvases.put(renderer, new HashMap()); + renderer.initialize(this); + } + } + + public boolean removeRenderer(MapRenderer renderer) { + if (renderers.contains(renderer)) { + renderers.remove(renderer); + for (Map.Entry entry : canvases.get(renderer).entrySet()) { + for (int x = 0; x < 128; ++x) { + for (int y = 0; y < 128; ++y) { + entry.getValue().setPixel(x, y, (byte) -1); + } + } + } + canvases.remove(renderer); + return true; + } else { + return false; + } + } + + private boolean isContextual() { + for (MapRenderer renderer : renderers) { + if (renderer.isContextual()) return true; + } + return false; + } + + public RenderData render(CraftPlayer player) { + boolean context = isContextual(); + RenderData render = renderCache.get(context ? player : null); + + if (render == null) { + render = new RenderData(); + renderCache.put(context ? player : null, render); + } + + if (context && renderCache.containsKey(null)) { + renderCache.remove(null); + } + + Arrays.fill(render.buffer, (byte) 0); + render.cursors.clear(); + + for (MapRenderer renderer : renderers) { + CraftMapCanvas canvas = canvases.get(renderer).get(renderer.isContextual() ? player : null); + if (canvas == null) { + canvas = new CraftMapCanvas(this); + canvases.get(renderer).put(renderer.isContextual() ? player : null, canvas); + } + + canvas.setBase(render.buffer); + try { + renderer.render(this, canvas, player); + } catch (Throwable ex) { + Bukkit.getLogger().log(Level.SEVERE, "Could not render map using renderer " + renderer.getClass().getName(), ex); + } + + byte[] buf = canvas.getBuffer(); + for (int i = 0; i < buf.length; ++i) { + byte color = buf[i]; + // There are 208 valid color id's, 0 -> 127 and -128 -> -49 + if (color >= 0 || color <= -49) render.buffer[i] = color; + } + + for (int i = 0; i < canvas.getCursors().size(); ++i) { + render.cursors.add(canvas.getCursors().getCursor(i)); + } + } + + return render; + } + + @Override + public boolean isUnlimitedTracking() { + return worldMap.unlimitedTracking; + } + + @Override + public void setUnlimitedTracking(boolean unlimited) { + worldMap.unlimitedTracking = unlimited; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/map/RenderData.java b/src/main/java/org/bukkit/craftbukkit/map/RenderData.java new file mode 100644 index 000000000000..5768cd512ec1 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/map/RenderData.java @@ -0,0 +1,16 @@ +package org.bukkit.craftbukkit.map; + +import java.util.ArrayList; +import org.bukkit.map.MapCursor; + +public class RenderData { + + public byte[] buffer; // Paper + public final ArrayList cursors; + + public RenderData() { + this.buffer = new byte[128 * 128]; + this.cursors = new ArrayList(); + } + +} diff --git a/src/main/java/org/bukkit/craftbukkit/metadata/BlockMetadataStore.java b/src/main/java/org/bukkit/craftbukkit/metadata/BlockMetadataStore.java new file mode 100644 index 000000000000..6f7102f268eb --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/metadata/BlockMetadataStore.java @@ -0,0 +1,94 @@ +package org.bukkit.craftbukkit.metadata; + +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.metadata.MetadataStore; +import org.bukkit.metadata.MetadataStoreBase; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.plugin.Plugin; + +import java.util.List; + +/** + * A BlockMetadataStore stores metadata values for {@link Block} objects. + */ +public class BlockMetadataStore extends MetadataStoreBase implements MetadataStore { + + private final World owningWorld; + + /** + * Initializes a BlockMetadataStore. + * @param owningWorld The world to which this BlockMetadataStore belongs. + */ + public BlockMetadataStore(World owningWorld) { + this.owningWorld = owningWorld; + } + + /** + * Generates a unique metadata key for a {@link Block} object based on its coordinates in the world. + * @see MetadataStoreBase#disambiguate(Object, String) + * @param block the block + * @param metadataKey The name identifying the metadata value + * @return a unique metadata key + */ + @Override + protected String disambiguate(Block block, String metadataKey) { + return Integer.toString(block.getX()) + ":" + Integer.toString(block.getY()) + ":" + Integer.toString(block.getZ()) + ":" + metadataKey; + } + + /** + * Retrieves the metadata for a {@link Block}, ensuring the block being asked for actually belongs to this BlockMetadataStore's + * owning world. + * @see MetadataStoreBase#getMetadata(Object, String) + */ + @Override + public List getMetadata(Block block, String metadataKey) { + if(block.getWorld() == owningWorld) { + return super.getMetadata(block, metadataKey); + } else { + throw new IllegalArgumentException("Block does not belong to world " + owningWorld.getName()); + } + } + + /** + * Tests to see if a metadata value has been added to a {@link Block}, ensuring the block being interrogated belongs + * to this BlockMetadataStore's owning world. + * @see MetadataStoreBase#hasMetadata(Object, String) + */ + @Override + public boolean hasMetadata(Block block, String metadataKey) { + if(block.getWorld() == owningWorld) { + return super.hasMetadata(block, metadataKey); + } else { + throw new IllegalArgumentException("Block does not belong to world " + owningWorld.getName()); + } + } + + /** + * Removes metadata from from a {@link Block} belonging to a given {@link Plugin}, ensuring the block being deleted from belongs + * to this BlockMetadataStore's owning world. + * @see MetadataStoreBase#removeMetadata(Object, String, org.bukkit.plugin.Plugin) + */ + @Override + public void removeMetadata(Block block, String metadataKey, Plugin owningPlugin) { + if(block.getWorld() == owningWorld) { + super.removeMetadata(block, metadataKey, owningPlugin); + } else { + throw new IllegalArgumentException("Block does not belong to world " + owningWorld.getName()); + } + } + + /** + * Sets or overwrites a metadata value on a {@link Block} from a given {@link Plugin}, ensuring the target block belongs + * to this BlockMetadataStore's owning world. + * @see MetadataStoreBase#setMetadata(Object, String, org.bukkit.metadata.MetadataValue) + */ + @Override + public void setMetadata(Block block, String metadataKey, MetadataValue newMetadataValue) { + if(block.getWorld() == owningWorld) { + super.setMetadata(block, metadataKey, newMetadataValue); + } else { + throw new IllegalArgumentException("Block does not belong to world " + owningWorld.getName()); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/metadata/EntityMetadataStore.java b/src/main/java/org/bukkit/craftbukkit/metadata/EntityMetadataStore.java new file mode 100644 index 000000000000..35c484f46c7d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/metadata/EntityMetadataStore.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.metadata; + +import org.bukkit.entity.Entity; +import org.bukkit.metadata.MetadataStore; +import org.bukkit.metadata.MetadataStoreBase; + +/** + * An EntityMetadataStore stores metadata values for all {@link Entity} classes an their descendants. + */ +public class EntityMetadataStore extends MetadataStoreBase implements MetadataStore { + /** + * Generates a unique metadata key for an {@link Entity} UUID. + * + * @see MetadataStoreBase#disambiguate(Object, String) + * @param entity the entity + * @param metadataKey The name identifying the metadata value + * @return a unique metadata key + */ + @Override + protected String disambiguate(Entity entity, String metadataKey) { + return entity.getUniqueId().toString() + ":" + metadataKey; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/metadata/PlayerMetadataStore.java b/src/main/java/org/bukkit/craftbukkit/metadata/PlayerMetadataStore.java new file mode 100644 index 000000000000..bac5c9e434cd --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/metadata/PlayerMetadataStore.java @@ -0,0 +1,23 @@ +package org.bukkit.craftbukkit.metadata; + +import org.bukkit.OfflinePlayer; +import org.bukkit.metadata.MetadataStore; +import org.bukkit.metadata.MetadataStoreBase; + +/** + * A PlayerMetadataStore stores metadata for {@link org.bukkit.entity.Player} and {@link OfflinePlayer} objects. + */ +public class PlayerMetadataStore extends MetadataStoreBase implements MetadataStore { + /** + * Generates a unique metadata key for {@link org.bukkit.entity.Player} and {@link OfflinePlayer} using the player + * UUID. + * @see MetadataStoreBase#disambiguate(Object, String) + * @param player the player + * @param metadataKey The name identifying the metadata value + * @return a unique metadata key + */ + @Override + protected String disambiguate(OfflinePlayer player, String metadataKey) { + return player.getUniqueId() + ":" + metadataKey; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/metadata/WorldMetadataStore.java b/src/main/java/org/bukkit/craftbukkit/metadata/WorldMetadataStore.java new file mode 100644 index 000000000000..dd37ed29492d --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/metadata/WorldMetadataStore.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.metadata; + +import org.bukkit.World; +import org.bukkit.metadata.MetadataStore; +import org.bukkit.metadata.MetadataStoreBase; + +/** + * An WorldMetadataStore stores metadata values for {@link World} objects. + */ +public class WorldMetadataStore extends MetadataStoreBase implements MetadataStore { + /** + * Generates a unique metadata key for a {@link World} object based on the world UID. + * @see WorldMetadataStore#disambiguate(Object, String) + * @param world the world + * @param metadataKey The name identifying the metadata value + * @return a unique metadata key + */ + @Override + protected String disambiguate(World world, String metadataKey) { + return world.getUID().toString() + ":" + metadataKey; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionBrewer.java b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionBrewer.java new file mode 100644 index 000000000000..14b79c13a40f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionBrewer.java @@ -0,0 +1,48 @@ +package org.bukkit.craftbukkit.potion; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import net.minecraft.server.MobEffect; +import net.minecraft.server.PotionRegistry; + +import org.bukkit.potion.PotionEffectType; +import org.bukkit.potion.PotionType; +import org.bukkit.potion.PotionBrewer; +import org.bukkit.potion.PotionData; +import org.bukkit.potion.PotionEffect; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Maps; + +public class CraftPotionBrewer implements PotionBrewer { + private static final Map> cache = Maps.newHashMap(); + + public Collection getEffects(PotionType damage, boolean upgraded, boolean extended) { + if (cache.containsKey(damage)) + return cache.get(damage); + + List mcEffects = PotionRegistry.a(CraftPotionUtil.fromBukkit(new PotionData(damage, extended, upgraded))).a(); + + ImmutableList.Builder builder = new ImmutableList.Builder(); + for (MobEffect effect : mcEffects) { + builder.add(CraftPotionUtil.toBukkit(effect)); + } + + cache.put(damage, builder.build()); + + return cache.get(damage); + } + + @Override + public Collection getEffectsFromDamage(int damage) { + return new ArrayList(); + } + + @Override + public PotionEffect createEffect(PotionEffectType potion, int duration, int amplifier) { + return new PotionEffect(potion, potion.isInstant() ? 1 : (int) (duration * potion.getDurationModifier()), amplifier); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java new file mode 100644 index 000000000000..035e4fe829f6 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java @@ -0,0 +1,102 @@ +package org.bukkit.craftbukkit.potion; + +import net.minecraft.server.MobEffectList; + +import org.bukkit.Color; +import org.bukkit.potion.PotionEffectType; + +public class CraftPotionEffectType extends PotionEffectType { + private final MobEffectList handle; + + public CraftPotionEffectType(MobEffectList handle) { + super(MobEffectList.getId(handle)); + this.handle = handle; + } + + @Override + public double getDurationModifier() { + return handle.durationModifier; + } + + public MobEffectList getHandle() { + return handle; + } + + @Override + public String getName() { + switch (getId()) { + case 1: + return "SPEED"; + case 2: + return "SLOW"; + case 3: + return "FAST_DIGGING"; + case 4: + return "SLOW_DIGGING"; + case 5: + return "INCREASE_DAMAGE"; + case 6: + return "HEAL"; + case 7: + return "HARM"; + case 8: + return "JUMP"; + case 9: + return "CONFUSION"; + case 10: + return "REGENERATION"; + case 11: + return "DAMAGE_RESISTANCE"; + case 12: + return "FIRE_RESISTANCE"; + case 13: + return "WATER_BREATHING"; + case 14: + return "INVISIBILITY"; + case 15: + return "BLINDNESS"; + case 16: + return "NIGHT_VISION"; + case 17: + return "HUNGER"; + case 18: + return "WEAKNESS"; + case 19: + return "POISON"; + case 20: + return "WITHER"; + case 21: + return "HEALTH_BOOST"; + case 22: + return "ABSORPTION"; + case 23: + return "SATURATION"; + case 24: + return "GLOWING"; + case 25: + return "LEVITATION"; + case 26: + return "LUCK"; + case 27: + return "UNLUCK"; + case 28: + return "SLOW_FALLING"; + case 29: + return "CONDUIT_POWER"; + case 30: + return "DOLPHINS_GRACE"; + default: + return "UNKNOWN_EFFECT_TYPE_" + getId(); + } + } + + @Override + public boolean isInstant() { + return handle.isInstant(); + } + + @Override + public Color getColor() { + return Color.fromRGB(handle.getColor()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java new file mode 100644 index 000000000000..2392b457f674 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java @@ -0,0 +1,122 @@ +package org.bukkit.craftbukkit.potion; + +import com.google.common.base.Preconditions; +import com.google.common.collect.BiMap; +import com.google.common.collect.ImmutableBiMap; + +import net.minecraft.server.MobEffect; +import net.minecraft.server.MobEffectList; + +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.potion.PotionType; +import org.bukkit.potion.PotionData; + +public class CraftPotionUtil { + + private static final BiMap regular = ImmutableBiMap.builder() + .put(PotionType.UNCRAFTABLE, "empty") + .put(PotionType.WATER, "water") + .put(PotionType.MUNDANE, "mundane") + .put(PotionType.THICK, "thick") + .put(PotionType.AWKWARD, "awkward") + .put(PotionType.NIGHT_VISION, "night_vision") + .put(PotionType.INVISIBILITY, "invisibility") + .put(PotionType.JUMP, "leaping") + .put(PotionType.FIRE_RESISTANCE, "fire_resistance") + .put(PotionType.SPEED, "swiftness") + .put(PotionType.SLOWNESS, "slowness") + .put(PotionType.WATER_BREATHING, "water_breathing") + .put(PotionType.INSTANT_HEAL, "healing") + .put(PotionType.INSTANT_DAMAGE, "harming") + .put(PotionType.POISON, "poison") + .put(PotionType.REGEN, "regeneration") + .put(PotionType.STRENGTH, "strength") + .put(PotionType.WEAKNESS, "weakness") + .put(PotionType.LUCK, "luck") + .put(PotionType.TURTLE_MASTER, "turtle_master") + .put(PotionType.SLOW_FALLING, "slow_falling") + .build(); + private static final BiMap upgradeable = ImmutableBiMap.builder() + .put(PotionType.JUMP, "strong_leaping") + .put(PotionType.SPEED, "strong_swiftness") + .put(PotionType.INSTANT_HEAL, "strong_healing") + .put(PotionType.INSTANT_DAMAGE, "strong_harming") + .put(PotionType.POISON, "strong_poison") + .put(PotionType.REGEN, "strong_regeneration") + .put(PotionType.STRENGTH, "strong_strength") + .put(PotionType.SLOWNESS, "strong_slowness") + .put(PotionType.TURTLE_MASTER, "strong_turtle_master") + .build(); + private static final BiMap extendable = ImmutableBiMap.builder() + .put(PotionType.NIGHT_VISION, "long_night_vision") + .put(PotionType.INVISIBILITY, "long_invisibility") + .put(PotionType.JUMP, "long_leaping") + .put(PotionType.FIRE_RESISTANCE, "long_fire_resistance") + .put(PotionType.SPEED, "long_swiftness") + .put(PotionType.SLOWNESS, "long_slowness") + .put(PotionType.WATER_BREATHING, "long_water_breathing") + .put(PotionType.POISON, "long_poison") + .put(PotionType.REGEN, "long_regeneration") + .put(PotionType.STRENGTH, "long_strength") + .put(PotionType.WEAKNESS, "long_weakness") + .put(PotionType.TURTLE_MASTER, "long_turtle_master") + .put(PotionType.SLOW_FALLING, "long_slow_falling") + .build(); + + public static String fromBukkit(PotionData data) { + String type; + if (data.isUpgraded()) { + type = upgradeable.get(data.getType()); + } else if (data.isExtended()) { + type = extendable.get(data.getType()); + } else { + type = regular.get(data.getType()); + } + Preconditions.checkNotNull(type, "Unknown potion type from data " + data); + + return "minecraft:" + type; + } + + public static PotionData toBukkit(String type) { + if (type == null) { + return new PotionData(PotionType.UNCRAFTABLE, false, false); + } + if (type.startsWith("minecraft:")) { + type = type.substring(10); + } + PotionType potionType = null; + potionType = extendable.inverse().get(type); + if (potionType != null) { + return new PotionData(potionType, true, false); + } + potionType = upgradeable.inverse().get(type); + if (potionType != null) { + return new PotionData(potionType, false, true); + } + potionType = regular.inverse().get(type); + if (potionType != null) { + return new PotionData(potionType, false, false); + } + return new PotionData(PotionType.UNCRAFTABLE, false, false); + } + + public static MobEffect fromBukkit(PotionEffect effect) { + MobEffectList type = MobEffectList.fromId(effect.getType().getId()); + return new MobEffect(type, effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles()); + } + + public static PotionEffect toBukkit(MobEffect effect) { + PotionEffectType type = PotionEffectType.getById(MobEffectList.getId(effect.getMobEffect())); + int amp = effect.getAmplifier(); + int duration = effect.getDuration(); + boolean ambient = effect.isAmbient(); + boolean particles = effect.isShowParticles(); + return new PotionEffect(type, duration, amp, ambient, particles); + } + + public static boolean equals(MobEffectList mobEffect, PotionEffectType type) { + PotionEffectType typeV = PotionEffectType.getById(MobEffectList.getId(mobEffect)); + return typeV.equals(type); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java b/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java new file mode 100644 index 000000000000..744da023debb --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java @@ -0,0 +1,161 @@ +package org.bukkit.craftbukkit.projectiles; + +import java.util.Random; + +import org.apache.commons.lang.Validate; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.potion.CraftPotionUtil; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.Egg; +import org.bukkit.entity.EnderPearl; +import org.bukkit.entity.Fireball; +import org.bukkit.entity.LingeringPotion; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.SmallFireball; +import org.bukkit.entity.Snowball; +import org.bukkit.entity.SpectralArrow; +import org.bukkit.entity.ThrownExpBottle; +import org.bukkit.entity.ThrownPotion; +import org.bukkit.entity.TippedArrow; +import org.bukkit.entity.WitherSkull; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionData; +import org.bukkit.potion.PotionType; +import org.bukkit.projectiles.BlockProjectileSource; +import org.bukkit.util.Vector; + +import net.minecraft.server.BlockDispenser; +import net.minecraft.server.EntityArrow; +import net.minecraft.server.EntityEgg; +import net.minecraft.server.EntityEnderPearl; +import net.minecraft.server.EntityFireball; +import net.minecraft.server.EntityLargeFireball; +import net.minecraft.server.EntityPotion; +import net.minecraft.server.EntityProjectile; +import net.minecraft.server.EntitySmallFireball; +import net.minecraft.server.EntitySnowball; +import net.minecraft.server.EntitySpectralArrow; +import net.minecraft.server.EntityThrownExpBottle; +import net.minecraft.server.EntityTippedArrow; +import net.minecraft.server.EntityWitherSkull; +import net.minecraft.server.EnumDirection; +import net.minecraft.server.IPosition; +import net.minecraft.server.IProjectile; +import net.minecraft.server.MathHelper; +import net.minecraft.server.SourceBlock; +import net.minecraft.server.TileEntityDispenser; + +public class CraftBlockProjectileSource implements BlockProjectileSource { + private final TileEntityDispenser dispenserBlock; + + public CraftBlockProjectileSource(TileEntityDispenser dispenserBlock) { + this.dispenserBlock = dispenserBlock; + } + + @Override + public Block getBlock() { + return dispenserBlock.getWorld().getWorld().getBlockAt(dispenserBlock.getPosition().getX(), dispenserBlock.getPosition().getY(), dispenserBlock.getPosition().getZ()); + } + + @Override + public T launchProjectile(Class projectile) { + return launchProjectile(projectile, null); + } + + @Override + public T launchProjectile(Class projectile, Vector velocity) { + Validate.isTrue(getBlock().getType() == Material.DISPENSER, "Block is no longer dispenser"); + // Copied from BlockDispenser.dispense() + SourceBlock isourceblock = new SourceBlock(dispenserBlock.getWorld(), dispenserBlock.getPosition()); + // Copied from DispenseBehaviorProjectile + IPosition iposition = BlockDispenser.a(isourceblock); + EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING); + net.minecraft.server.World world = dispenserBlock.getWorld(); + net.minecraft.server.Entity launch = null; + + if (Snowball.class.isAssignableFrom(projectile)) { + launch = new EntitySnowball(world, iposition.getX(), iposition.getY(), iposition.getZ()); + } else if (Egg.class.isAssignableFrom(projectile)) { + launch = new EntityEgg(world, iposition.getX(), iposition.getY(), iposition.getZ()); + } else if (EnderPearl.class.isAssignableFrom(projectile)) { + launch = new EntityEnderPearl(world, null); + launch.setPosition(iposition.getX(), iposition.getY(), iposition.getZ()); + } else if (ThrownExpBottle.class.isAssignableFrom(projectile)) { + launch = new EntityThrownExpBottle(world, iposition.getX(), iposition.getY(), iposition.getZ()); + } else if (ThrownPotion.class.isAssignableFrom(projectile)) { + if (LingeringPotion.class.isAssignableFrom(projectile)) { + launch = new EntityPotion(world, iposition.getX(), iposition.getY(), iposition.getZ(), CraftItemStack.asNMSCopy(new ItemStack(org.bukkit.Material.LINGERING_POTION, 1))); + } else { + launch = new EntityPotion(world, iposition.getX(), iposition.getY(), iposition.getZ(), CraftItemStack.asNMSCopy(new ItemStack(org.bukkit.Material.SPLASH_POTION, 1))); + } + } else if (Arrow.class.isAssignableFrom(projectile)) { + if (TippedArrow.class.isAssignableFrom(projectile)) { + launch = new EntityTippedArrow(world, iposition.getX(), iposition.getY(), iposition.getZ()); + ((EntityTippedArrow) launch).setType(CraftPotionUtil.fromBukkit(new PotionData(PotionType.WATER, false, false))); + } else if (SpectralArrow.class.isAssignableFrom(projectile)) { + launch = new EntitySpectralArrow(world, iposition.getX(), iposition.getY(), iposition.getZ()); + } else { + launch = new EntityTippedArrow(world, iposition.getX(), iposition.getY(), iposition.getZ()); + } + ((EntityArrow) launch).fromPlayer = EntityArrow.PickupStatus.ALLOWED; + ((EntityArrow) launch).projectileSource = this; + } else if (Fireball.class.isAssignableFrom(projectile)) { + double d0 = iposition.getX() + (double) ((float) enumdirection.getAdjacentX() * 0.3F); + double d1 = iposition.getY() + (double) ((float) enumdirection.getAdjacentY() * 0.3F); + double d2 = iposition.getZ() + (double) ((float) enumdirection.getAdjacentZ() * 0.3F); + Random random = world.random; + double d3 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentX(); + double d4 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentY(); + double d5 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentZ(); + + if (SmallFireball.class.isAssignableFrom(projectile)) { + launch = new EntitySmallFireball(world, null, d0, d1, d2); + } else if (WitherSkull.class.isAssignableFrom(projectile)) { + launch = new EntityWitherSkull(world); + launch.setPosition(d0, d1, d2); + double d6 = (double) MathHelper.sqrt(d3 * d3 + d4 * d4 + d5 * d5); + + ((EntityFireball) launch).dirX = d3 / d6 * 0.1D; + ((EntityFireball) launch).dirY = d4 / d6 * 0.1D; + ((EntityFireball) launch).dirZ = d5 / d6 * 0.1D; + } else { + launch = new EntityLargeFireball(world); + launch.setPosition(d0, d1, d2); + double d6 = (double) MathHelper.sqrt(d3 * d3 + d4 * d4 + d5 * d5); + + ((EntityFireball) launch).dirX = d3 / d6 * 0.1D; + ((EntityFireball) launch).dirY = d4 / d6 * 0.1D; + ((EntityFireball) launch).dirZ = d5 / d6 * 0.1D; + } + + ((EntityFireball) launch).projectileSource = this; + } + + Validate.notNull(launch, "Projectile not supported"); + + if (launch instanceof IProjectile) { + if (launch instanceof EntityProjectile) { + ((EntityProjectile) launch).projectileSource = this; + } + // Values from DispenseBehaviorProjectile + float a = 6.0F; + float b = 1.1F; + if (launch instanceof EntityPotion || launch instanceof ThrownExpBottle) { + // Values from respective DispenseBehavior classes + a *= 0.5F; + b *= 1.25F; + } + // Copied from DispenseBehaviorProjectile + ((IProjectile) launch).shoot((double) enumdirection.getAdjacentX(), (double) ((float) enumdirection.getAdjacentY() + 0.1F), (double) enumdirection.getAdjacentZ(), b, a); + } + + if (velocity != null) { + ((T) launch.getBukkitEntity()).setVelocity(velocity); + } + + world.addEntity(launch); + return (T) launch.getBukkitEntity(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncDebugger.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncDebugger.java new file mode 100644 index 000000000000..c33213124a03 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncDebugger.java @@ -0,0 +1,37 @@ +package org.bukkit.craftbukkit.scheduler; + +import org.bukkit.plugin.Plugin; + + +class CraftAsyncDebugger { + private CraftAsyncDebugger next = null; + private final int expiry; + private final Plugin plugin; + private final Class clazz; + + CraftAsyncDebugger(final int expiry, final Plugin plugin, final Class clazz) { + this.expiry = expiry; + this.plugin = plugin; + this.clazz = clazz; + + } + + final CraftAsyncDebugger getNextHead(final int time) { + CraftAsyncDebugger next, current = this; + while (time > current.expiry && (next = current.next) != null) { + current = next; + } + return current; + } + + final CraftAsyncDebugger setNext(final CraftAsyncDebugger next) { + return this.next = next; + } + + StringBuilder debugTo(final StringBuilder string) { + for (CraftAsyncDebugger next = this; next != null; next = next.next) { + string.append(next.plugin.getDescription().getName()).append(':').append(next.clazz.getName()).append('@').append(next.expiry).append(','); + } + return string; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java new file mode 100644 index 000000000000..3c1992e212a6 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018 Daniel Ennis (Aikar) MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.bukkit.craftbukkit.scheduler; + +import com.destroystokyo.paper.ServerSchedulerReportingWrapper; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import org.bukkit.plugin.Plugin; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class CraftAsyncScheduler extends CraftScheduler { + + private final ThreadPoolExecutor executor = new ThreadPoolExecutor( + 4, Integer.MAX_VALUE,30L, TimeUnit.SECONDS, new SynchronousQueue<>(), + new ThreadFactoryBuilder().setNameFormat("Craft Scheduler Thread - %1$d").build()); + private final Executor management = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder() + .setNameFormat("Craft Async Scheduler Management Thread").build()); + private final List temp = new ArrayList<>(); + + CraftAsyncScheduler() { + super(true); + executor.allowCoreThreadTimeOut(true); + executor.prestartAllCoreThreads(); + } + + @Override + public void cancelTask(int taskId) { + this.management.execute(() -> this.removeTask(taskId)); + } + + private synchronized void removeTask(int taskId) { + parsePending(); + this.pending.removeIf((task) -> { + if (task.getTaskId() == taskId) { + task.cancel0(); + return true; + } + return false; + }); + } + + @Override + public void mainThreadHeartbeat(int currentTick) { + this.currentTick = currentTick; + this.management.execute(() -> this.runTasks(currentTick)); + } + + private synchronized void runTasks(int currentTick) { + parsePending(); + while (!this.pending.isEmpty() && this.pending.peek().getNextRun() <= currentTick) { + CraftTask task = this.pending.remove(); + if (executeTask(task)) { + final long period = task.getPeriod(); + if (period > 0) { + task.setNextRun(currentTick + period); + temp.add(task); + } + } + parsePending(); + } + this.pending.addAll(temp); + temp.clear(); + } + + private boolean executeTask(CraftTask task) { + if (isValid(task)) { + this.runners.put(task.getTaskId(), task); + this.executor.execute(new ServerSchedulerReportingWrapper(task)); + return true; + } + return false; + } + + @Override + public synchronized void cancelTasks(Plugin plugin) { + parsePending(); + for (Iterator iterator = this.pending.iterator(); iterator.hasNext(); ) { + CraftTask task = iterator.next(); + if (task.getTaskId() != -1 && (plugin == null || task.getOwner().equals(plugin))) { + task.cancel0(); + iterator.remove(); + } + } + } + + /** + * Task is not cancelled + * @param runningTask + * @return + */ + static boolean isValid(CraftTask runningTask) { + return runningTask.getPeriod() >= CraftTask.NO_REPEATING; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java new file mode 100644 index 000000000000..26ca44c5d6b2 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java @@ -0,0 +1,109 @@ +package org.bukkit.craftbukkit.scheduler; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.logging.Level; + +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitWorker; + +class CraftAsyncTask extends CraftTask { + + private final LinkedList workers = new LinkedList(); + private final Map runners; + + CraftAsyncTask(final Map runners, final Plugin plugin, final Object task, final int id, final long delay) { + super(plugin, task, id, delay); + this.runners = runners; + } + + @Override + public boolean isSync() { + return false; + } + + @Override + public void run() { + final Thread thread = Thread.currentThread(); + synchronized(workers) { + if (getPeriod() == CraftTask.CANCEL) { + // Never continue running after cancelled. + // Checking this with the lock is important! + return; + } + workers.add( + new BukkitWorker() { + public Thread getThread() { + return thread; + } + + public int getTaskId() { + return CraftAsyncTask.this.getTaskId(); + } + + public Plugin getOwner() { + return CraftAsyncTask.this.getOwner(); + } + }); + } + Throwable thrown = null; + try { + super.run(); + } catch (final Throwable t) { + thrown = t; + getOwner().getLogger().log( + Level.WARNING, + String.format( + "Plugin %s generated an exception while executing task %s", + getOwner().getDescription().getFullName(), + getTaskId()), + thrown); + } finally { + // Cleanup is important for any async task, otherwise ghost tasks are everywhere + synchronized(workers) { + try { + final Iterator workers = this.workers.iterator(); + boolean removed = false; + while (workers.hasNext()) { + if (workers.next().getThread() == thread) { + workers.remove(); + removed = true; // Don't throw exception + break; + } + } + if (!removed) { + throw new IllegalStateException( + String.format( + "Unable to remove worker %s on task %s for %s", + thread.getName(), + getTaskId(), + getOwner().getDescription().getFullName()), + thrown); // We don't want to lose the original exception, if any + } + } finally { + if (getPeriod() < 0 && workers.isEmpty()) { + // At this spot, we know we are the final async task being executed! + // Because we have the lock, nothing else is running or will run because delay < 0 + runners.remove(getTaskId()); + } + } + } + } + } + + LinkedList getWorkers() { + return workers; + } + + boolean cancel0() { + synchronized (workers) { + // Synchronizing here prevents race condition for a completing task + setPeriod(CraftTask.CANCEL); + if (workers.isEmpty()) { + runners.remove(getTaskId()); + } + } + return true; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftFuture.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftFuture.java new file mode 100644 index 000000000000..bb990e5117c0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftFuture.java @@ -0,0 +1,104 @@ +package org.bukkit.craftbukkit.scheduler; + +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.bukkit.plugin.Plugin; + +class CraftFuture extends CraftTask implements Future { + + private final Callable callable; + private T value; + private Exception exception = null; + + CraftFuture(final Callable callable, final Plugin plugin, final int id) { + super(plugin, null, id, CraftTask.NO_REPEATING); + this.callable = callable; + } + + public synchronized boolean cancel(final boolean mayInterruptIfRunning) { + if (getPeriod() != CraftTask.NO_REPEATING) { + return false; + } + setPeriod(CraftTask.CANCEL); + return true; + } + + public boolean isDone() { + final long period = this.getPeriod(); + return period != CraftTask.NO_REPEATING && period != CraftTask.PROCESS_FOR_FUTURE; + } + + public T get() throws CancellationException, InterruptedException, ExecutionException { + try { + return get(0, TimeUnit.MILLISECONDS); + } catch (final TimeoutException e) { + throw new Error(e); + } + } + + public synchronized T get(long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + timeout = unit.toMillis(timeout); + long period = this.getPeriod(); + long timestamp = timeout > 0 ? System.currentTimeMillis() : 0L; + while (true) { + if (period == CraftTask.NO_REPEATING || period == CraftTask.PROCESS_FOR_FUTURE) { + this.wait(timeout); + period = this.getPeriod(); + if (period == CraftTask.NO_REPEATING || period == CraftTask.PROCESS_FOR_FUTURE) { + if (timeout == 0L) { + continue; + } + timeout += timestamp - (timestamp = System.currentTimeMillis()); + if (timeout > 0) { + continue; + } + throw new TimeoutException(); + } + } + if (period == CraftTask.CANCEL) { + throw new CancellationException(); + } + if (period == CraftTask.DONE_FOR_FUTURE) { + if (exception == null) { + return value; + } + throw new ExecutionException(exception); + } + throw new IllegalStateException("Expected " + CraftTask.NO_REPEATING + " to " + CraftTask.DONE_FOR_FUTURE + ", got " + period); + } + } + + @Override + public void run() { + synchronized (this) { + if (getPeriod() == CraftTask.CANCEL) { + return; + } + setPeriod(CraftTask.PROCESS_FOR_FUTURE); + } + try { + value = callable.call(); + } catch (final Exception e) { + exception = e; + } finally { + synchronized (this) { + setPeriod(CraftTask.DONE_FOR_FUTURE); + this.notifyAll(); + } + } + } + + synchronized boolean cancel0() { + if (getPeriod() != CraftTask.NO_REPEATING) { + return false; + } + setPeriod(CraftTask.CANCEL); + notifyAll(); + return true; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java new file mode 100644 index 000000000000..3acfc0766650 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java @@ -0,0 +1,616 @@ +package org.bukkit.craftbukkit.scheduler; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.PriorityQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.logging.Level; + +import co.aikar.timings.MinecraftTimings; // Paper +import com.destroystokyo.paper.event.server.ServerExceptionEvent; +import com.destroystokyo.paper.exception.ServerSchedulerException; +import org.apache.commons.lang.Validate; +import org.bukkit.plugin.IllegalPluginAccessException; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.scheduler.BukkitTask; +import org.bukkit.scheduler.BukkitWorker; + +/** + * The fundamental concepts for this implementation: + *
  • Main thread owns {@link #head} and {@link #currentTick}, but it may be read from any thread
  • + *
  • Main thread exclusively controls {@link #temp} and {@link #pending}. + * They are never to be accessed outside of the main thread; alternatives exist to prevent locking.
  • + *
  • {@link #head} to {@link #tail} act as a linked list/queue, with 1 consumer and infinite producers. + * Adding to the tail is atomic and very efficient; utility method is {@link #handle(CraftTask, long)} or {@link #addTask(CraftTask)}.
  • + *
  • Changing the period on a task is delicate. + * Any future task needs to notify waiting threads. + * Async tasks must be synchronized to make sure that any thread that's finishing will remove itself from {@link #runners}. + * Another utility method is provided for this, {@link #cancelTask(int)}
  • + *
  • {@link #runners} provides a moderately up-to-date view of active tasks. + * If the linked head to tail set is read, all remaining tasks that were active at the time execution started will be located in runners.
  • + *
  • Async tasks are responsible for removing themselves from runners
  • + *
  • Sync tasks are only to be removed from runners on the main thread when coupled with a removal from pending and temp.
  • + *
  • Most of the design in this scheduler relies on queuing special tasks to perform any data changes on the main thread. + * When executed from inside a synchronous method, the scheduler will be updated before next execution by virtue of the frequent {@link #parsePending()} calls.
  • + */ +public class CraftScheduler implements BukkitScheduler { + + /** + * Counter for IDs. Order doesn't matter, only uniqueness. + */ + private final AtomicInteger ids = new AtomicInteger(1); + /** + * Current head of linked-list. This reference is always stale, {@link CraftTask#next} is the live reference. + */ + private volatile CraftTask head = new CraftTask(); + /** + * Tail of a linked-list. AtomicReference only matters when adding to queue + */ + private final AtomicReference tail = new AtomicReference(head); + /** + * Main thread logic only + */ + final PriorityQueue pending = new PriorityQueue(10, // Paper + new Comparator() { + public int compare(final CraftTask o1, final CraftTask o2) { + int value = Long.compare(o1.getNextRun(), o2.getNextRun()); + + // If the tasks should run on the same tick they should be run FIFO + return value != 0 ? value : Integer.compare(o1.getTaskId(), o2.getTaskId()); + } + }); + /** + * Main thread logic only + */ + private final List temp = new ArrayList(); + /** + * These are tasks that are currently active. It's provided for 'viewing' the current state. + */ + final ConcurrentHashMap runners = new ConcurrentHashMap(); // Paper + /** + * The sync task that is currently running on the main thread. + */ + private volatile CraftTask currentTask = null; + volatile int currentTick = -1; // Paper + //private final Executor executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("Craft Scheduler Thread - %d").build()); // Paper - moved to AsyncScheduler + //private CraftAsyncDebugger debugHead = new CraftAsyncDebugger(-1, null, null) {@Override StringBuilder debugTo(StringBuilder string) {return string;}}; // Paper + //private CraftAsyncDebugger debugTail = debugHead; // Paper + private static final int RECENT_TICKS; + + static { + RECENT_TICKS = 30; + } + + + // Paper start + private final CraftScheduler asyncScheduler; + private final boolean isAsyncScheduler; + public CraftScheduler() { + this(false); + } + + public CraftScheduler(boolean isAsync) { + this.isAsyncScheduler = isAsync; + if (isAsync) { + this.asyncScheduler = this; + } else { + this.asyncScheduler = new CraftAsyncScheduler(); + } + } + // Paper end + @Override + public int scheduleSyncDelayedTask(final Plugin plugin, final Runnable task) { + return this.scheduleSyncDelayedTask(plugin, task, 0L); + } + + @Override + public BukkitTask runTask(Plugin plugin, Runnable runnable) { + return runTaskLater(plugin, runnable, 0L); + } + + @Override + public void runTask(Plugin plugin, Consumer task) throws IllegalArgumentException { + runTaskLater(plugin, task, 0L); + } + + @Deprecated + @Override + public int scheduleAsyncDelayedTask(final Plugin plugin, final Runnable task) { + return this.scheduleAsyncDelayedTask(plugin, task, 0L); + } + + @Override + public BukkitTask runTaskAsynchronously(Plugin plugin, Runnable runnable) { + return runTaskLaterAsynchronously(plugin, runnable, 0L); + } + + @Override + public void runTaskAsynchronously(Plugin plugin, Consumer task) throws IllegalArgumentException { + runTaskLaterAsynchronously(plugin, task, 0L); + } + + @Override + public int scheduleSyncDelayedTask(final Plugin plugin, final Runnable task, final long delay) { + return this.scheduleSyncRepeatingTask(plugin, task, delay, CraftTask.NO_REPEATING); + } + + @Override + public BukkitTask runTaskLater(Plugin plugin, Runnable runnable, long delay) { + return runTaskTimer(plugin, runnable, delay, CraftTask.NO_REPEATING); + } + + @Override + public void runTaskLater(Plugin plugin, Consumer task, long delay) throws IllegalArgumentException { + runTaskTimer(plugin, task, delay, CraftTask.NO_REPEATING); + } + + @Deprecated + @Override + public int scheduleAsyncDelayedTask(final Plugin plugin, final Runnable task, final long delay) { + return this.scheduleAsyncRepeatingTask(plugin, task, delay, CraftTask.NO_REPEATING); + } + + @Override + public BukkitTask runTaskLaterAsynchronously(Plugin plugin, Runnable runnable, long delay) { + return runTaskTimerAsynchronously(plugin, runnable, delay, CraftTask.NO_REPEATING); + } + + @Override + public void runTaskLaterAsynchronously(Plugin plugin, Consumer task, long delay) throws IllegalArgumentException { + runTaskTimerAsynchronously(plugin, task, delay, CraftTask.NO_REPEATING); + } + + @Override + public void runTaskTimerAsynchronously(Plugin plugin, Consumer task, long delay, long period) throws IllegalArgumentException { + runTaskTimerAsynchronously(plugin, (Object) task, delay, CraftTask.NO_REPEATING); + } + + @Override + public int scheduleSyncRepeatingTask(final Plugin plugin, final Runnable runnable, long delay, long period) { + return runTaskTimer(plugin, runnable, delay, period).getTaskId(); + } + + @Override + public BukkitTask runTaskTimer(Plugin plugin, Runnable runnable, long delay, long period) { + return runTaskTimer(plugin, (Object) runnable, delay, period); + } + + @Override + public void runTaskTimer(Plugin plugin, Consumer task, long delay, long period) throws IllegalArgumentException { + runTaskTimer(plugin, (Object) task, delay, period); + } + + public BukkitTask runTaskTimer(Plugin plugin, Object runnable, long delay, long period) { + validate(plugin, runnable); + if (delay < 0L) { + delay = 0; + } + if (period == CraftTask.ERROR) { + period = 1L; + } else if (period < CraftTask.NO_REPEATING) { + period = CraftTask.NO_REPEATING; + } + return handle(new CraftTask(plugin, runnable, nextId(), period), delay); + } + + @Deprecated + @Override + public int scheduleAsyncRepeatingTask(final Plugin plugin, final Runnable runnable, long delay, long period) { + return runTaskTimerAsynchronously(plugin, runnable, delay, period).getTaskId(); + } + + @Override + public BukkitTask runTaskTimerAsynchronously(Plugin plugin, Runnable runnable, long delay, long period) { + return runTaskTimerAsynchronously(plugin, (Object) runnable, delay, period); + } + + public BukkitTask runTaskTimerAsynchronously(Plugin plugin, Object runnable, long delay, long period) { + validate(plugin, runnable); + if (delay < 0L) { + delay = 0; + } + if (period == CraftTask.ERROR) { + period = 1L; + } else if (period < CraftTask.NO_REPEATING) { + period = CraftTask.NO_REPEATING; + } + return handle(new CraftAsyncTask(this.asyncScheduler.runners, plugin, runnable, nextId(), period), delay); // Paper + } + + @Override + public Future callSyncMethod(final Plugin plugin, final Callable task) { + validate(plugin, task); + final CraftFuture future = new CraftFuture(task, plugin, nextId()); + handle(future, 0L); + return future; + } + + @Override + public void cancelTask(final int taskId) { + if (taskId <= 0) { + return; + } + // Paper start + if (!this.isAsyncScheduler) { + this.asyncScheduler.cancelTask(taskId); + } + // Paper end + CraftTask task = runners.get(taskId); + if (task != null) { + task.cancel0(); + } + task = new CraftTask( + new Runnable() { + public void run() { + if (!check(CraftScheduler.this.temp)) { + check(CraftScheduler.this.pending); + } + } + private boolean check(final Iterable collection) { + final Iterator tasks = collection.iterator(); + while (tasks.hasNext()) { + final CraftTask task = tasks.next(); + if (task.getTaskId() == taskId) { + task.cancel0(); + tasks.remove(); + if (task.isSync()) { + runners.remove(taskId); + } + return true; + } + } + return false; + }}){{this.timings=co.aikar.timings.MinecraftTimings.getCancelTasksTimer();}}; // Paper + handle(task, 0L); + for (CraftTask taskPending = head.getNext(); taskPending != null; taskPending = taskPending.getNext()) { + if (taskPending == task) { + return; + } + if (taskPending.getTaskId() == taskId) { + taskPending.cancel0(); + } + } + } + + @Override + public void cancelTasks(final Plugin plugin) { + Validate.notNull(plugin, "Cannot cancel tasks of null plugin"); + // Paper start + if (!this.isAsyncScheduler) { + this.asyncScheduler.cancelTasks(plugin); + } + // Paper end + final CraftTask task = new CraftTask( + new Runnable() { + public void run() { + check(CraftScheduler.this.pending); + check(CraftScheduler.this.temp); + } + void check(final Iterable collection) { + final Iterator tasks = collection.iterator(); + while (tasks.hasNext()) { + final CraftTask task = tasks.next(); + if (task.getOwner().equals(plugin)) { + task.cancel0(); + tasks.remove(); + if (task.isSync()) { + runners.remove(task.getTaskId()); + } + } + } + } + }){{this.timings=co.aikar.timings.MinecraftTimings.getCancelTasksTimer(plugin);}}; // Paper + handle(task, 0L); + for (CraftTask taskPending = head.getNext(); taskPending != null; taskPending = taskPending.getNext()) { + if (taskPending == task) { + break; + } + if (taskPending.getTaskId() != -1 && taskPending.getOwner().equals(plugin)) { + taskPending.cancel0(); + } + } + for (CraftTask runner : runners.values()) { + if (runner.getOwner().equals(plugin)) { + runner.cancel0(); + } + } + } + + @Override + public boolean isCurrentlyRunning(final int taskId) { + // Paper start + if (!isAsyncScheduler) { + if (this.asyncScheduler.isCurrentlyRunning(taskId)) { + return true; + } + } + // Paper end + final CraftTask task = runners.get(taskId); + if (task == null) { + return false; + } + if (task.isSync()) { + return (task == currentTask); + } + final CraftAsyncTask asyncTask = (CraftAsyncTask) task; + synchronized (asyncTask.getWorkers()) { + return !asyncTask.getWorkers().isEmpty(); + } + } + + @Override + public boolean isQueued(final int taskId) { + if (taskId <= 0) { + return false; + } + // Paper start + if (!this.isAsyncScheduler && this.asyncScheduler.isQueued(taskId)) { + return true; + } + // Paper end + for (CraftTask task = head.getNext(); task != null; task = task.getNext()) { + if (task.getTaskId() == taskId) { + return task.getPeriod() >= CraftTask.NO_REPEATING; // The task will run + } + } + CraftTask task = runners.get(taskId); + return task != null && task.getPeriod() >= CraftTask.NO_REPEATING; + } + + @Override + public List getActiveWorkers() { + // Paper start + if (!isAsyncScheduler) { + //noinspection TailRecursion + return this.asyncScheduler.getActiveWorkers(); + } + // Paper end + final ArrayList workers = new ArrayList(); + for (final CraftTask taskObj : runners.values()) { + // Iterator will be a best-effort (may fail to grab very new values) if called from an async thread + if (taskObj.isSync()) { + continue; + } + final CraftAsyncTask task = (CraftAsyncTask) taskObj; + synchronized (task.getWorkers()) { + // This will never have an issue with stale threads; it's state-safe + workers.addAll(task.getWorkers()); + } + } + return workers; + } + + @Override + public List getPendingTasks() { + final ArrayList truePending = new ArrayList(); + for (CraftTask task = head.getNext(); task != null; task = task.getNext()) { + if (task.getTaskId() != -1) { + // -1 is special code + truePending.add(task); + } + } + + final ArrayList pending = new ArrayList(); + for (CraftTask task : runners.values()) { + if (task.getPeriod() >= CraftTask.NO_REPEATING) { + pending.add(task); + } + } + + for (final CraftTask task : truePending) { + if (task.getPeriod() >= CraftTask.NO_REPEATING && !pending.contains(task)) { + pending.add(task); + } + } + // Paper start + if (!this.isAsyncScheduler) { + pending.addAll(this.asyncScheduler.getPendingTasks()); + } + // Paper end + return pending; + } + + /** + * This method is designed to never block or wait for locks; an immediate execution of all current tasks. + */ + public void mainThreadHeartbeat(final int currentTick) { + // Paper start + if (!this.isAsyncScheduler) { + this.asyncScheduler.mainThreadHeartbeat(currentTick); + } + // Paper end + this.currentTick = currentTick; + final List temp = this.temp; + parsePending(); + while (isReady(currentTick)) { + final CraftTask task = pending.remove(); + if (task.getPeriod() < CraftTask.NO_REPEATING) { + if (task.isSync()) { + runners.remove(task.getTaskId(), task); + } + parsePending(); + continue; + } + if (task.isSync()) { + currentTask = task; + try { + task.run(); + } catch (final Throwable throwable) { + // Paper start + String msg = String.format( + "Task #%s for %s generated an exception", + task.getTaskId(), + task.getOwner().getDescription().getFullName()); + task.getOwner().getLogger().log( + Level.WARNING, + msg, + throwable); + task.getOwner().getServer().getPluginManager().callEvent( + new ServerExceptionEvent(new ServerSchedulerException(msg, throwable, task)) + ); + // Paper end + } finally { + currentTask = null; + } + parsePending(); + } else { + //debugTail = debugTail.setNext(new CraftAsyncDebugger(currentTick + RECENT_TICKS, task.getOwner(), task.getTaskClass())); // Paper + task.getOwner().getLogger().log(Level.SEVERE, "Unexpected Async Task in the Sync Scheduler. Report this to Paper"); // Paper + // We don't need to parse pending + // (async tasks must live with race-conditions if they attempt to cancel between these few lines of code) + } + final long period = task.getPeriod(); // State consistency + if (period > 0) { + task.setNextRun(currentTick + period); + temp.add(task); + } else if (task.isSync()) { + runners.remove(task.getTaskId()); + } + } + MinecraftTimings.bukkitSchedulerFinishTimer.startTiming(); + pending.addAll(temp); + temp.clear(); + MinecraftTimings.bukkitSchedulerFinishTimer.stopTiming(); + //debugHead = debugHead.getNextHead(currentTick); // Paper + } + + protected void addTask(final CraftTask task) { + final AtomicReference tail = this.tail; + CraftTask tailTask = tail.get(); + while (!tail.compareAndSet(tailTask, task)) { + tailTask = tail.get(); + } + tailTask.setNext(task); + } + + protected CraftTask handle(final CraftTask task, final long delay) { // Paper + // Paper start + if (!this.isAsyncScheduler && !task.isSync()) { + this.asyncScheduler.handle(task, delay); + return task; + } + // Paper end + task.setNextRun(currentTick + delay); + addTask(task); + return task; + } + + private static void validate(final Plugin plugin, final Object task) { + Validate.notNull(plugin, "Plugin cannot be null"); + Validate.notNull(task, "Task cannot be null"); + Validate.isTrue(task instanceof Runnable || task instanceof Consumer || task instanceof Callable, "Task must be Runnable, Consumer, or Callable"); + if (!plugin.isEnabled()) { + throw new IllegalPluginAccessException("Plugin attempted to register task while disabled"); + } + } + + private int nextId() { + return ids.incrementAndGet(); + } + + void parsePending() { // Paper + if (!this.isAsyncScheduler) MinecraftTimings.bukkitSchedulerPendingTimer.startTiming(); // Paper + CraftTask head = this.head; + CraftTask task = head.getNext(); + CraftTask lastTask = head; + for (; task != null; task = (lastTask = task).getNext()) { + if (task.getTaskId() == -1) { + task.run(); + } else if (task.getPeriod() >= CraftTask.NO_REPEATING) { + pending.add(task); + runners.put(task.getTaskId(), task); + } + } + // We split this because of the way things are ordered for all of the async calls in CraftScheduler + // (it prevents race-conditions) + for (task = head; task != lastTask; task = head) { + head = task.getNext(); + task.setNext(null); + } + this.head = lastTask; + if (!this.isAsyncScheduler) MinecraftTimings.bukkitSchedulerPendingTimer.stopTiming(); // Paper + } + + private boolean isReady(final int currentTick) { + return !pending.isEmpty() && pending.peek().getNextRun() <= currentTick; + } + + @Override + public String toString() { + // Paper start + return ""; + /* + int debugTick = currentTick; + StringBuilder string = new StringBuilder("Recent tasks from ").append(debugTick - RECENT_TICKS).append('-').append(debugTick).append('{'); + debugHead.debugTo(string); + return string.append('}').toString(); + */ + // Paper end + } + + @Deprecated + @Override + public int scheduleSyncDelayedTask(Plugin plugin, BukkitRunnable task, long delay) { + return scheduleSyncDelayedTask(plugin, (Runnable) task, delay); + } + + @Deprecated + @Override + public int scheduleSyncDelayedTask(Plugin plugin, BukkitRunnable task) { + return scheduleSyncDelayedTask(plugin, (Runnable) task); + } + + @Deprecated + @Override + public int scheduleSyncRepeatingTask(Plugin plugin, BukkitRunnable task, long delay, long period) { + return scheduleSyncRepeatingTask(plugin, (Runnable) task, delay, period); + } + + @Deprecated + @Override + public BukkitTask runTask(Plugin plugin, BukkitRunnable task) throws IllegalArgumentException { + return runTask(plugin, (Runnable) task); + } + + @Deprecated + @Override + public BukkitTask runTaskAsynchronously(Plugin plugin, BukkitRunnable task) throws IllegalArgumentException { + return runTaskAsynchronously(plugin, (Runnable) task); + } + + @Deprecated + @Override + public BukkitTask runTaskLater(Plugin plugin, BukkitRunnable task, long delay) throws IllegalArgumentException { + return runTaskLater(plugin, (Runnable) task, delay); + } + + @Deprecated + @Override + public BukkitTask runTaskLaterAsynchronously(Plugin plugin, BukkitRunnable task, long delay) throws IllegalArgumentException { + return runTaskLaterAsynchronously(plugin, (Runnable) task, delay); + } + + @Deprecated + @Override + public BukkitTask runTaskTimer(Plugin plugin, BukkitRunnable task, long delay, long period) throws IllegalArgumentException { + return runTaskTimer(plugin, (Runnable) task, delay, period); + } + + @Deprecated + @Override + public BukkitTask runTaskTimerAsynchronously(Plugin plugin, BukkitRunnable task, long delay, long period) throws IllegalArgumentException { + return runTaskTimerAsynchronously(plugin, (Runnable) task, delay, period); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java new file mode 100644 index 000000000000..f32e6601077f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java @@ -0,0 +1,137 @@ +package org.bukkit.craftbukkit.scheduler; + +import java.util.function.Consumer; +import org.bukkit.Bukkit; +import co.aikar.timings.MinecraftTimings; // Paper +import co.aikar.timings.Timing; // Paper +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitTask; + + +public class CraftTask implements BukkitTask, Runnable { // Spigot + + private volatile CraftTask next = null; + public static final int ERROR = 0; + public static final int NO_REPEATING = -1; + public static final int CANCEL = -2; + public static final int PROCESS_FOR_FUTURE = -3; + public static final int DONE_FOR_FUTURE = -4; + /** + * -1 means no repeating
    + * -2 means cancel
    + * -3 means processing for Future
    + * -4 means done for Future
    + * Never 0
    + * >0 means number of ticks to wait between each execution + */ + private volatile long period; + private long nextRun; + public final Runnable rTask; // Paper + public final Consumer cTask; // Paper + public Timing timings; // Paper + private final Plugin plugin; + private final int id; + + CraftTask() { + this(null, null, CraftTask.NO_REPEATING, CraftTask.NO_REPEATING); + } + + CraftTask(final Object task) { + this(null, task, CraftTask.NO_REPEATING, CraftTask.NO_REPEATING); + } + + CraftTask(final Plugin plugin, final Object task, final int id, final long period) { // Paper + this.plugin = plugin; + if (task instanceof Runnable) { + this.rTask = (Runnable) task; + this.cTask = null; + } else if (task instanceof Consumer) { + this.cTask = (Consumer) task; + this.rTask = null; + } else if (task == null) { + // Head or Future task + this.rTask = null; + this.cTask = null; + } else { + throw new AssertionError("Illegal task class " + task); + } + this.id = id; + this.period = period; + timings = task != null ? MinecraftTimings.getPluginTaskTimings(this, period) : null; // Paper + } + + @Override + public final int getTaskId() { + return id; + } + + @Override + public final Plugin getOwner() { + return plugin; + } + + @Override + public boolean isSync() { + return true; + } + + @Override + public void run() { + if (timings != null && isSync()) timings.startTiming(); // Paper + if (rTask != null) { + rTask.run(); + } else { + cTask.accept(this); + } + if (timings != null && isSync()) timings.stopTiming(); // Paper + } + + long getPeriod() { + return period; + } + + void setPeriod(long period) { + this.period = period; + } + + long getNextRun() { + return nextRun; + } + + void setNextRun(long nextRun) { + this.nextRun = nextRun; + } + + CraftTask getNext() { + return next; + } + + void setNext(CraftTask next) { + this.next = next; + } + + public Class getTaskClass() { + return (rTask != null) ? rTask.getClass() : ((cTask != null) ? cTask.getClass() : null); + } + + @Override + public boolean isCancelled() { + return (period == CraftTask.CANCEL); + } + + @Override + public void cancel() { + Bukkit.getScheduler().cancelTask(id); + } + + /** + * This method properly sets the status to cancelled, synchronizing when required. + * + * @return false if it is a craft future task that has already begun execution, true otherwise + */ + boolean cancel0() { + setPeriod(CraftTask.CANCEL); + return true; + } + +} diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java new file mode 100644 index 000000000000..7dedd0222a77 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java @@ -0,0 +1,65 @@ +package org.bukkit.craftbukkit.scoreboard; + +import java.util.Map; + +import net.minecraft.server.IScoreboardCriteria; +import net.minecraft.server.ScoreboardObjective; + +import com.google.common.collect.ImmutableMap; + +final class CraftCriteria { + static final Map DEFAULTS; + static final CraftCriteria DUMMY; + + static { + ImmutableMap.Builder defaults = ImmutableMap.builder(); + + for (Map.Entry entry : ((Map ) IScoreboardCriteria.criteria).entrySet()) { + String name = entry.getKey().toString(); + IScoreboardCriteria criteria = (IScoreboardCriteria) entry.getValue(); + + defaults.put(name, new CraftCriteria(criteria)); + } + + DEFAULTS = defaults.build(); + DUMMY = DEFAULTS.get("dummy"); + } + + final IScoreboardCriteria criteria; + final String bukkitName; + + private CraftCriteria(String bukkitName) { + this.bukkitName = bukkitName; + this.criteria = DUMMY.criteria; + } + + private CraftCriteria(IScoreboardCriteria criteria) { + this.criteria = criteria; + this.bukkitName = criteria.getName(); + } + + static CraftCriteria getFromNMS(ScoreboardObjective objective) { + return DEFAULTS.get(objective.getCriteria().getName()); + } + + static CraftCriteria getFromBukkit(String name) { + final CraftCriteria criteria = DEFAULTS.get(name); + if (criteria != null) { + return criteria; + } + return new CraftCriteria(name); + } + + @Override + public boolean equals(Object that) { + if (!(that instanceof CraftCriteria)) { + return false; + } + return ((CraftCriteria) that).bukkitName.equals(this.bukkitName); + } + + @Override + public int hashCode() { + return this.bukkitName.hashCode() ^ CraftCriteria.class.hashCode(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java new file mode 100644 index 000000000000..dcbbea38a248 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java @@ -0,0 +1,155 @@ +package org.bukkit.craftbukkit.scoreboard; + +import net.minecraft.server.Scoreboard; +import net.minecraft.server.ScoreboardObjective; + +import org.apache.commons.lang.Validate; +import org.bukkit.OfflinePlayer; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.scoreboard.DisplaySlot; +import org.bukkit.scoreboard.Objective; +import org.bukkit.scoreboard.RenderType; +import org.bukkit.scoreboard.Score; + +final class CraftObjective extends CraftScoreboardComponent implements Objective { + private final ScoreboardObjective objective; + private final CraftCriteria criteria; + + CraftObjective(CraftScoreboard scoreboard, ScoreboardObjective objective) { + super(scoreboard); + this.objective = objective; + this.criteria = CraftCriteria.getFromNMS(objective); + } + + ScoreboardObjective getHandle() { + return objective; + } + + public String getName() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return objective.getName(); + } + + public String getDisplayName() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return CraftChatMessage.fromComponent(objective.getDisplayName()); + } + + public void setDisplayName(String displayName) throws IllegalStateException, IllegalArgumentException { + Validate.notNull(displayName, "Display name cannot be null"); + Validate.isTrue(displayName.length() <= 128, "Display name '" + displayName + "' is longer than the limit of 128 characters"); + CraftScoreboard scoreboard = checkState(); + + objective.setDisplayName(CraftChatMessage.fromString(displayName)[0]); // SPIGOT-4112: not nullable + } + + public String getCriteria() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return criteria.bukkitName; + } + + public boolean isModifiable() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return !criteria.criteria.isReadOnly(); + } + + public void setDisplaySlot(DisplaySlot slot) throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + Scoreboard board = scoreboard.board; + ScoreboardObjective objective = this.objective; + + for (int i = 0; i < CraftScoreboardTranslations.MAX_DISPLAY_SLOT; i++) { + if (board.getObjectiveForSlot(i) == objective) { + board.setDisplaySlot(i, null); + } + } + if (slot != null) { + int slotNumber = CraftScoreboardTranslations.fromBukkitSlot(slot); + board.setDisplaySlot(slotNumber, getHandle()); + } + } + + public DisplaySlot getDisplaySlot() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + Scoreboard board = scoreboard.board; + ScoreboardObjective objective = this.objective; + + for (int i = 0; i < CraftScoreboardTranslations.MAX_DISPLAY_SLOT; i++) { + if (board.getObjectiveForSlot(i) == objective) { + return CraftScoreboardTranslations.toBukkitSlot(i); + } + } + return null; + } + + @Override + public void setRenderType(RenderType renderType) throws IllegalStateException { + Validate.notNull(renderType, "RenderType cannot be null"); + CraftScoreboard scoreboard = checkState(); + + this.objective.setRenderType(CraftScoreboardTranslations.fromBukkitRender(renderType)); + } + + @Override + public RenderType getRenderType() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return CraftScoreboardTranslations.toBukkitRender(this.objective.getRenderType()); + } + + public Score getScore(OfflinePlayer player) throws IllegalArgumentException, IllegalStateException { + Validate.notNull(player, "Player cannot be null"); + CraftScoreboard scoreboard = checkState(); + + return new CraftScore(this, player.getName()); + } + + public Score getScore(String entry) throws IllegalArgumentException, IllegalStateException { + Validate.notNull(entry, "Entry cannot be null"); + Validate.isTrue(entry.length() <= 40, "Score '" + entry + "' is longer than the limit of 40 characters"); + CraftScoreboard scoreboard = checkState(); + + return new CraftScore(this, entry); + } + + @Override + public void unregister() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + scoreboard.board.unregisterObjective(objective); + } + + @Override + CraftScoreboard checkState() throws IllegalStateException { + if (getScoreboard().board.getObjective(objective.getName()) == null) { + throw new IllegalStateException("Unregistered scoreboard component"); + } + + return getScoreboard(); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 31 * hash + (this.objective != null ? this.objective.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final CraftObjective other = (CraftObjective) obj; + return !(this.objective != other.objective && (this.objective == null || !this.objective.equals(other.objective))); + } + + +} diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java new file mode 100644 index 000000000000..f378113cec80 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java @@ -0,0 +1,69 @@ +package org.bukkit.craftbukkit.scoreboard; + +import java.util.Map; + +import net.minecraft.server.Scoreboard; +import net.minecraft.server.ScoreboardObjective; +import net.minecraft.server.ScoreboardScore; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.scoreboard.Objective; +import org.bukkit.scoreboard.Score; + +/** + * TL;DR: This class is special and lazily grabs a handle... + * ...because a handle is a full fledged (I think permanent) hashMap for the associated name. + *

    + * Also, as an added perk, a CraftScore will (intentionally) stay a valid reference so long as objective is valid. + */ +final class CraftScore implements Score { + private final String entry; + private final CraftObjective objective; + + CraftScore(CraftObjective objective, String entry) { + this.objective = objective; + this.entry = entry; + } + + public OfflinePlayer getPlayer() { + return Bukkit.getOfflinePlayer(entry); + } + + public String getEntry() { + return entry; + } + + public Objective getObjective() { + return objective; + } + + public int getScore() throws IllegalStateException { + Scoreboard board = objective.checkState().board; + + if (board.getPlayers().contains(entry)) { // Lazy + Map scores = board.getPlayerObjectives(entry); + ScoreboardScore score = scores.get(objective.getHandle()); + if (score != null) { // Lazy + return score.getScore(); + } + } + + return 0; // Lazy + } + + public void setScore(int score) throws IllegalStateException { + objective.checkState().board.getPlayerScoreForObjective(entry, objective.getHandle()).setScore(score); + } + + @Override + public boolean isScoreSet() throws IllegalStateException { + Scoreboard board = objective.checkState().board; + + return board.getPlayers().contains(entry) && board.getPlayerObjectives(entry).containsKey(objective.getHandle()); + } + + public CraftScoreboard getScoreboard() { + return objective.getScoreboard(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java new file mode 100644 index 000000000000..7320f339fdd3 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java @@ -0,0 +1,186 @@ +package org.bukkit.craftbukkit.scoreboard; + +import java.util.Collection; +import net.minecraft.server.Scoreboard; +import net.minecraft.server.ScoreboardObjective; +import net.minecraft.server.ScoreboardTeam; + +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.scoreboard.DisplaySlot; +import org.bukkit.scoreboard.Objective; +import org.bukkit.scoreboard.RenderType; +import org.bukkit.scoreboard.Score; +import org.bukkit.scoreboard.Team; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import org.bukkit.craftbukkit.util.CraftChatMessage; + +public final class CraftScoreboard implements org.bukkit.scoreboard.Scoreboard { + final Scoreboard board; + + CraftScoreboard(Scoreboard board) { + this.board = board; + } + + @Override + public CraftObjective registerNewObjective(String name, String criteria) throws IllegalArgumentException { + return registerNewObjective(name, criteria, name); + } + + @Override + public CraftObjective registerNewObjective(String name, String criteria, String displayName) throws IllegalArgumentException { + return registerNewObjective(name, criteria, displayName, RenderType.INTEGER); + } + + @Override + public CraftObjective registerNewObjective(String name, String criteria, String displayName, RenderType renderType) throws IllegalArgumentException { + Validate.notNull(name, "Objective name cannot be null"); + Validate.notNull(criteria, "Criteria cannot be null"); + Validate.notNull(displayName, "Display name cannot be null"); + Validate.notNull(renderType, "RenderType cannot be null"); + Validate.isTrue(name.length() <= 16, "The name '" + name + "' is longer than the limit of 16 characters"); + Validate.isTrue(displayName.length() <= 128, "The display name '" + displayName + "' is longer than the limit of 128 characters"); + Validate.isTrue(board.getObjective(name) == null, "An objective of name '" + name + "' already exists"); + + CraftCriteria craftCriteria = CraftCriteria.getFromBukkit(criteria); + ScoreboardObjective objective = board.registerObjective(name, craftCriteria.criteria, CraftChatMessage.fromStringOrNull(displayName), CraftScoreboardTranslations.fromBukkitRender(renderType)); + return new CraftObjective(this, objective); + } + + public Objective getObjective(String name) throws IllegalArgumentException { + Validate.notNull(name, "Name cannot be null"); + ScoreboardObjective nms = board.getObjective(name); + return nms == null ? null : new CraftObjective(this, nms); + } + + public ImmutableSet getObjectivesByCriteria(String criteria) throws IllegalArgumentException { + Validate.notNull(criteria, "Criteria cannot be null"); + + ImmutableSet.Builder objectives = ImmutableSet.builder(); + for (ScoreboardObjective netObjective : (Collection) this.board.getObjectives()) { + CraftObjective objective = new CraftObjective(this, netObjective); + if (objective.getCriteria().equals(criteria)) { + objectives.add(objective); + } + } + return objectives.build(); + } + + public ImmutableSet getObjectives() { + return ImmutableSet.copyOf(Iterables.transform((Collection) this.board.getObjectives(), new Function() { + + @Override + public Objective apply(ScoreboardObjective input) { + return new CraftObjective(CraftScoreboard.this, input); + } + })); + } + + public Objective getObjective(DisplaySlot slot) throws IllegalArgumentException { + Validate.notNull(slot, "Display slot cannot be null"); + ScoreboardObjective objective = board.getObjectiveForSlot(CraftScoreboardTranslations.fromBukkitSlot(slot)); + if (objective == null) { + return null; + } + return new CraftObjective(this, objective); + } + + public ImmutableSet getScores(OfflinePlayer player) throws IllegalArgumentException { + Validate.notNull(player, "OfflinePlayer cannot be null"); + + return getScores(player.getName()); + } + + public ImmutableSet getScores(String entry) throws IllegalArgumentException { + Validate.notNull(entry, "Entry cannot be null"); + + ImmutableSet.Builder scores = ImmutableSet.builder(); + for (ScoreboardObjective objective : (Collection) this.board.getObjectives()) { + scores.add(new CraftScore(new CraftObjective(this, objective), entry)); + } + return scores.build(); + } + + public void resetScores(OfflinePlayer player) throws IllegalArgumentException { + Validate.notNull(player, "OfflinePlayer cannot be null"); + + resetScores(player.getName()); + } + + public void resetScores(String entry) throws IllegalArgumentException { + Validate.notNull(entry, "Entry cannot be null"); + + for (ScoreboardObjective objective : (Collection) this.board.getObjectives()) { + board.resetPlayerScores(entry, objective); + } + } + + public Team getPlayerTeam(OfflinePlayer player) throws IllegalArgumentException { + Validate.notNull(player, "OfflinePlayer cannot be null"); + + ScoreboardTeam team = board.getPlayerTeam(player.getName()); + return team == null ? null : new CraftTeam(this, team); + } + + public Team getEntryTeam(String entry) throws IllegalArgumentException { + Validate.notNull(entry, "Entry cannot be null"); + + ScoreboardTeam team = board.getPlayerTeam(entry); + return team == null ? null : new CraftTeam(this, team); + } + + public Team getTeam(String teamName) throws IllegalArgumentException { + Validate.notNull(teamName, "Team name cannot be null"); + + ScoreboardTeam team = board.getTeam(teamName); + return team == null ? null : new CraftTeam(this, team); + } + + public ImmutableSet getTeams() { + return ImmutableSet.copyOf(Iterables.transform((Collection) this.board.getTeams(), new Function() { + + @Override + public Team apply(ScoreboardTeam input) { + return new CraftTeam(CraftScoreboard.this, input); + } + })); + } + + public Team registerNewTeam(String name) throws IllegalArgumentException { + Validate.notNull(name, "Team name cannot be null"); + Validate.isTrue(name.length() <= 16, "Team name '" + name + "' is longer than the limit of 16 characters"); + Validate.isTrue(board.getTeam(name) == null, "Team name '" + name + "' is already in use"); + + return new CraftTeam(this, board.createTeam(name)); + } + + public ImmutableSet getPlayers() { + ImmutableSet.Builder players = ImmutableSet.builder(); + for (Object playerName : board.getPlayers()) { + players.add(Bukkit.getOfflinePlayer(playerName.toString())); + } + return players.build(); + } + + public ImmutableSet getEntries() { + ImmutableSet.Builder entries = ImmutableSet.builder(); + for (Object entry : board.getPlayers()) { + entries.add(entry.toString()); + } + return entries.build(); + } + + public void clearSlot(DisplaySlot slot) throws IllegalArgumentException { + Validate.notNull(slot, "Slot cannot be null"); + board.setDisplaySlot(CraftScoreboardTranslations.fromBukkitSlot(slot), null); + } + + // CraftBukkit method + public Scoreboard getHandle() { + return board; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardComponent.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardComponent.java new file mode 100644 index 000000000000..d26d09d41110 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardComponent.java @@ -0,0 +1,17 @@ +package org.bukkit.craftbukkit.scoreboard; + +abstract class CraftScoreboardComponent { + private CraftScoreboard scoreboard; + + CraftScoreboardComponent(CraftScoreboard scoreboard) { + this.scoreboard = scoreboard; + } + + abstract CraftScoreboard checkState() throws IllegalStateException; + + public CraftScoreboard getScoreboard() { + return scoreboard; + } + + abstract void unregister() throws IllegalStateException; +} diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java new file mode 100644 index 000000000000..9cce899f59b4 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java @@ -0,0 +1,110 @@ +package org.bukkit.craftbukkit.scoreboard; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.IScoreboardCriteria; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.PacketPlayOutScoreboardObjective; +import net.minecraft.server.PacketPlayOutScoreboardTeam; +import net.minecraft.server.Scoreboard; +import net.minecraft.server.ScoreboardObjective; +import net.minecraft.server.ScoreboardScore; +import net.minecraft.server.ScoreboardServer; +import net.minecraft.server.ScoreboardTeam; + +import org.apache.commons.lang.Validate; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.util.WeakCollection; +import org.bukkit.entity.Player; +import org.bukkit.scoreboard.ScoreboardManager; + +public final class CraftScoreboardManager implements ScoreboardManager { + private final CraftScoreboard mainScoreboard; + private final MinecraftServer server; + private final Collection scoreboards = new WeakCollection(); + private final Map playerBoards = new HashMap(); + + public CraftScoreboardManager(MinecraftServer minecraftserver, net.minecraft.server.Scoreboard scoreboardServer) { + mainScoreboard = new CraftScoreboard(scoreboardServer); + server = minecraftserver; + scoreboards.add(mainScoreboard); + } + + public CraftScoreboard getMainScoreboard() { + return mainScoreboard; + } + + public CraftScoreboard getNewScoreboard() { + org.spigotmc.AsyncCatcher.catchOp( "scoreboard creation"); // Spigot + CraftScoreboard scoreboard = new CraftScoreboard(new ScoreboardServer(server)); + scoreboards.add(scoreboard); + return scoreboard; + } + + // CraftBukkit method + public CraftScoreboard getPlayerBoard(CraftPlayer player) { + CraftScoreboard board = playerBoards.get(player); + return (CraftScoreboard) (board == null ? getMainScoreboard() : board); + } + + // CraftBukkit method + public void setPlayerBoard(CraftPlayer player, org.bukkit.scoreboard.Scoreboard bukkitScoreboard) throws IllegalArgumentException { + Validate.isTrue(bukkitScoreboard instanceof CraftScoreboard, "Cannot set player scoreboard to an unregistered Scoreboard"); + + CraftScoreboard scoreboard = (CraftScoreboard) bukkitScoreboard; + net.minecraft.server.Scoreboard oldboard = getPlayerBoard(player).getHandle(); + net.minecraft.server.Scoreboard newboard = scoreboard.getHandle(); + EntityPlayer entityplayer = player.getHandle(); + + if (oldboard == newboard) { + return; + } + + if (scoreboard == mainScoreboard) { + playerBoards.remove(player); + } else { + playerBoards.put(player, (CraftScoreboard) scoreboard); + } + + // Old objective tracking + HashSet removed = new HashSet(); + for (int i = 0; i < 3; ++i) { + ScoreboardObjective scoreboardobjective = oldboard.getObjectiveForSlot(i); + if (scoreboardobjective != null && !removed.contains(scoreboardobjective)) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutScoreboardObjective(scoreboardobjective, 1)); + removed.add(scoreboardobjective); + } + } + + // Old team tracking + Iterator iterator = oldboard.getTeams().iterator(); + while (iterator.hasNext()) { + ScoreboardTeam scoreboardteam = (ScoreboardTeam) iterator.next(); + entityplayer.playerConnection.sendPacket(new PacketPlayOutScoreboardTeam(scoreboardteam, 1)); + } + + // The above is the reverse of the below method. + server.getPlayerList().sendScoreboard((ScoreboardServer) newboard, player.getHandle()); + } + + // CraftBukkit method + public void removePlayer(Player player) { + playerBoards.remove(player); + } + + // CraftBukkit method + public void getScoreboardScores(IScoreboardCriteria criteria, String name, Consumer consumer) { + for (CraftScoreboard scoreboard : scoreboards) { + Scoreboard board = scoreboard.board; + board.getObjectivesForCriteria(criteria, name, (score) -> consumer.accept(score)); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java new file mode 100644 index 000000000000..207ffdd252d3 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java @@ -0,0 +1,35 @@ +package org.bukkit.craftbukkit.scoreboard; + +import net.minecraft.server.IScoreboardCriteria; +import net.minecraft.server.Scoreboard; + +import org.bukkit.scoreboard.DisplaySlot; +import org.bukkit.scoreboard.RenderType; + +import com.google.common.collect.ImmutableBiMap; + +class CraftScoreboardTranslations { + static final int MAX_DISPLAY_SLOT = 3; + static ImmutableBiMap SLOTS = ImmutableBiMap.of( + DisplaySlot.BELOW_NAME, "belowName", + DisplaySlot.PLAYER_LIST, "list", + DisplaySlot.SIDEBAR, "sidebar"); + + private CraftScoreboardTranslations() {} + + static DisplaySlot toBukkitSlot(int i) { + return SLOTS.inverse().get(Scoreboard.getSlotName(i)); + } + + static int fromBukkitSlot(DisplaySlot slot) { + return Scoreboard.getSlotForName(SLOTS.get(slot)); + } + + static RenderType toBukkitRender(IScoreboardCriteria.EnumScoreboardHealthDisplay display) { + return RenderType.valueOf(display.name()); + } + + static IScoreboardCriteria.EnumScoreboardHealthDisplay fromBukkitRender(RenderType render) { + return IScoreboardCriteria.EnumScoreboardHealthDisplay.valueOf(render.name()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java new file mode 100644 index 000000000000..4154b754e70a --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java @@ -0,0 +1,295 @@ +package org.bukkit.craftbukkit.scoreboard; + +import java.util.Set; + +import net.minecraft.server.ScoreboardTeamBase.EnumNameTagVisibility; +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.scoreboard.NameTagVisibility; +import org.bukkit.scoreboard.Team; + +import com.google.common.collect.ImmutableSet; + +import net.minecraft.server.ScoreboardTeam; +import net.minecraft.server.ScoreboardTeamBase; +import org.bukkit.ChatColor; +import org.bukkit.craftbukkit.util.CraftChatMessage; + +final class CraftTeam extends CraftScoreboardComponent implements Team { + private final ScoreboardTeam team; + + CraftTeam(CraftScoreboard scoreboard, ScoreboardTeam team) { + super(scoreboard); + this.team = team; + } + + public String getName() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return team.getName(); + } + + public String getDisplayName() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return CraftChatMessage.fromComponent(team.getDisplayName()); + } + + public void setDisplayName(String displayName) throws IllegalStateException { + Validate.notNull(displayName, "Display name cannot be null"); + Validate.isTrue(displayName.length() <= 128, "Display name '" + displayName + "' is longer than the limit of 128 characters"); + CraftScoreboard scoreboard = checkState(); + + team.setDisplayName(CraftChatMessage.fromString(displayName)[0]); // SPIGOT-4112: not nullable + } + + public String getPrefix() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return CraftChatMessage.fromComponent(team.getPrefix()); + } + + public void setPrefix(String prefix) throws IllegalStateException, IllegalArgumentException { + Validate.notNull(prefix, "Prefix cannot be null"); + Validate.isTrue(prefix.length() <= 64, "Prefix '" + prefix + "' is longer than the limit of 64 characters"); + CraftScoreboard scoreboard = checkState(); + + team.setPrefix(CraftChatMessage.fromStringOrNull(prefix)); + } + + public String getSuffix() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return CraftChatMessage.fromComponent(team.getSuffix()); + } + + public void setSuffix(String suffix) throws IllegalStateException, IllegalArgumentException { + Validate.notNull(suffix, "Suffix cannot be null"); + Validate.isTrue(suffix.length() <= 64, "Suffix '" + suffix + "' is longer than the limit of 64 characters"); + CraftScoreboard scoreboard = checkState(); + + team.setSuffix(CraftChatMessage.fromStringOrNull(suffix)); + } + + @Override + public ChatColor getColor() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return CraftChatMessage.getColor(team.getColor()); + } + + @Override + public void setColor(ChatColor color) { + Validate.notNull(color, "Color cannot be null"); + CraftScoreboard scoreboard = checkState(); + + team.setColor(CraftChatMessage.getColor(color)); + } + + public boolean allowFriendlyFire() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return team.allowFriendlyFire(); + } + + public void setAllowFriendlyFire(boolean enabled) throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + team.setAllowFriendlyFire(enabled); + } + + public boolean canSeeFriendlyInvisibles() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return team.canSeeFriendlyInvisibles(); + } + + public void setCanSeeFriendlyInvisibles(boolean enabled) throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + team.setCanSeeFriendlyInvisibles(enabled); + } + + public NameTagVisibility getNameTagVisibility() throws IllegalArgumentException { + CraftScoreboard scoreboard = checkState(); + + return notchToBukkit(team.getNameTagVisibility()); + } + + public void setNameTagVisibility(NameTagVisibility visibility) throws IllegalArgumentException { + CraftScoreboard scoreboard = checkState(); + + team.setNameTagVisibility(bukkitToNotch(visibility)); + } + + public Set getPlayers() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + ImmutableSet.Builder players = ImmutableSet.builder(); + for (String playerName : team.getPlayerNameSet()) { + players.add(Bukkit.getOfflinePlayer(playerName)); + } + return players.build(); + } + + @Override + public Set getEntries() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + ImmutableSet.Builder entries = ImmutableSet.builder(); + for (String playerName: team.getPlayerNameSet()){ + entries.add(playerName); + } + return entries.build(); + } + + public int getSize() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + return team.getPlayerNameSet().size(); + } + + public void addPlayer(OfflinePlayer player) throws IllegalStateException, IllegalArgumentException { + Validate.notNull(player, "OfflinePlayer cannot be null"); + addEntry(player.getName()); + } + + public void addEntry(String entry) throws IllegalStateException, IllegalArgumentException { + Validate.notNull(entry, "Entry cannot be null"); + CraftScoreboard scoreboard = checkState(); + + scoreboard.board.addPlayerToTeam(entry, team); + } + + public boolean removePlayer(OfflinePlayer player) throws IllegalStateException, IllegalArgumentException { + Validate.notNull(player, "OfflinePlayer cannot be null"); + return removeEntry(player.getName()); + } + + public boolean removeEntry(String entry) throws IllegalStateException, IllegalArgumentException { + Validate.notNull(entry, "Entry cannot be null"); + CraftScoreboard scoreboard = checkState(); + + if (!team.getPlayerNameSet().contains(entry)) { + return false; + } + + scoreboard.board.removePlayerFromTeam(entry, team); + return true; + } + + public boolean hasPlayer(OfflinePlayer player) throws IllegalArgumentException, IllegalStateException { + Validate.notNull(player, "OfflinePlayer cannot be null"); + return hasEntry(player.getName()); + } + + public boolean hasEntry(String entry) throws IllegalArgumentException, IllegalStateException { + Validate.notNull("Entry cannot be null"); + + CraftScoreboard scoreboard = checkState(); + + return team.getPlayerNameSet().contains(entry); + } + + @Override + public void unregister() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + scoreboard.board.removeTeam(team); + } + + @Override + public OptionStatus getOption(Option option) throws IllegalStateException { + checkState(); + + switch (option) { + case NAME_TAG_VISIBILITY: + return OptionStatus.values()[team.getNameTagVisibility().ordinal()]; + case DEATH_MESSAGE_VISIBILITY: + return OptionStatus.values()[team.getDeathMessageVisibility().ordinal()]; + case COLLISION_RULE: + return OptionStatus.values()[team.getCollisionRule().ordinal()]; + default: + throw new IllegalArgumentException("Unrecognised option " + option); + } + } + + @Override + public void setOption(Option option, OptionStatus status) throws IllegalStateException { + checkState(); + + switch (option) { + case NAME_TAG_VISIBILITY: + team.setNameTagVisibility(EnumNameTagVisibility.values()[status.ordinal()]); + break; + case DEATH_MESSAGE_VISIBILITY: + team.setDeathMessageVisibility(EnumNameTagVisibility.values()[status.ordinal()]); + break; + case COLLISION_RULE: + team.setCollisionRule(ScoreboardTeamBase.EnumTeamPush.values()[status.ordinal()]); + break; + default: + throw new IllegalArgumentException("Unrecognised option " + option); + } + } + + public static EnumNameTagVisibility bukkitToNotch(NameTagVisibility visibility) { + switch (visibility) { + case ALWAYS: + return EnumNameTagVisibility.ALWAYS; + case NEVER: + return EnumNameTagVisibility.NEVER; + case HIDE_FOR_OTHER_TEAMS: + return EnumNameTagVisibility.HIDE_FOR_OTHER_TEAMS; + case HIDE_FOR_OWN_TEAM: + return EnumNameTagVisibility.HIDE_FOR_OWN_TEAM; + default: + throw new IllegalArgumentException("Unknown visibility level " + visibility); + } + } + + public static NameTagVisibility notchToBukkit(EnumNameTagVisibility visibility) { + switch (visibility) { + case ALWAYS: + return NameTagVisibility.ALWAYS; + case NEVER: + return NameTagVisibility.NEVER; + case HIDE_FOR_OTHER_TEAMS: + return NameTagVisibility.HIDE_FOR_OTHER_TEAMS; + case HIDE_FOR_OWN_TEAM: + return NameTagVisibility.HIDE_FOR_OWN_TEAM; + default: + throw new IllegalArgumentException("Unknown visibility level " + visibility); + } + } + + @Override + CraftScoreboard checkState() throws IllegalStateException { + if (getScoreboard().board.getTeam(team.getName()) == null) { + throw new IllegalStateException("Unregistered scoreboard component"); + } + + return getScoreboard(); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 43 * hash + (this.team != null ? this.team.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final CraftTeam other = (CraftTeam) obj; + return !(this.team != other.team && (this.team == null || !this.team.equals(other.team))); + } + +} diff --git a/src/main/java/org/bukkit/craftbukkit/tag/CraftBlockTag.java b/src/main/java/org/bukkit/craftbukkit/tag/CraftBlockTag.java new file mode 100644 index 000000000000..2fe308d91b3a --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/tag/CraftBlockTag.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.tag; + +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; +import net.minecraft.server.Block; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.TagsServer; +import org.bukkit.Material; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; + +public class CraftBlockTag extends CraftTag { + + public CraftBlockTag(TagsServer registry, MinecraftKey tag) { + super(registry, tag); + } + + @Override + public boolean isTagged(Material item) { + return getHandle().isTagged(CraftMagicNumbers.getBlock(item)); + } + + @Override + public Set getValues() { + return Collections.unmodifiableSet(getHandle().a().stream().map((block) -> CraftMagicNumbers.getMaterial(block)).collect(Collectors.toSet())); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/tag/CraftItemTag.java b/src/main/java/org/bukkit/craftbukkit/tag/CraftItemTag.java new file mode 100644 index 000000000000..4a1a45257f06 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/tag/CraftItemTag.java @@ -0,0 +1,27 @@ +package org.bukkit.craftbukkit.tag; + +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; +import net.minecraft.server.Item; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.TagsServer; +import org.bukkit.Material; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; + +public class CraftItemTag extends CraftTag { + + public CraftItemTag(TagsServer registry, MinecraftKey tag) { + super(registry, tag); + } + + @Override + public boolean isTagged(Material item) { + return getHandle().isTagged(CraftMagicNumbers.getItem(item)); + } + + @Override + public Set getValues() { + return Collections.unmodifiableSet(getHandle().a().stream().map((item) -> CraftMagicNumbers.getMaterial(item)).collect(Collectors.toSet())); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/tag/CraftTag.java b/src/main/java/org/bukkit/craftbukkit/tag/CraftTag.java new file mode 100644 index 000000000000..c08671e23241 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/tag/CraftTag.java @@ -0,0 +1,36 @@ +package org.bukkit.craftbukkit.tag; + +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.TagsServer; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Tag; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; + +public abstract class CraftTag implements Tag { + + private final net.minecraft.server.TagsServer registry; + private final MinecraftKey tag; + // + private int version = -1; + private net.minecraft.server.Tag handle; + + public CraftTag(TagsServer registry, MinecraftKey tag) { + this.registry = registry; + this.tag = tag; + } + + protected net.minecraft.server.Tag getHandle() { + if (version != registry.version) { + handle = registry.b(tag); + version = registry.version; + } + + return handle; + } + + @Override + public NamespacedKey getKey() { + return CraftNamespacedKey.fromMinecraft(tag); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/AsynchronousExecutor.java b/src/main/java/org/bukkit/craftbukkit/util/AsynchronousExecutor.java new file mode 100644 index 000000000000..cf1258c559ce --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/AsynchronousExecutor.java @@ -0,0 +1,360 @@ +package org.bukkit.craftbukkit.util; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +import org.apache.commons.lang.Validate; + +/** + * Executes tasks using a multi-stage process executor. Synchronous executions are via {@link AsynchronousExecutor#finishActive()} or the {@link AsynchronousExecutor#get(Object)} methods. + *

  • Stage 1 creates the object from a parameter, and is usually called asynchronously. + *
  • Stage 2 takes the parameter and object from stage 1 and does any synchronous processing to prepare it. + *
  • Stage 3 takes the parameter and object from stage 1, as well as a callback that was registered, and performs any synchronous calculations. + * + * @param

    The type of parameter you provide to make the object that will be created. It should implement {@link Object#hashCode()} and {@link Object#equals(Object)} if you want to get the value early. + * @param The type of object you provide. This is created in stage 1, and passed to stage 2, 3, and returned if get() is called. + * @param The type of callback you provide. You may register many of these to be passed to the provider in stage 3, one at a time. + * @param A type of exception you may throw and expect to be handled by the main thread + * @author Wesley Wolfe (c) 2012, 2014 + */ +public final class AsynchronousExecutor { + + public static interface CallBackProvider extends ThreadFactory { + + /** + * Normally an asynchronous call, but can be synchronous + * + * @param parameter parameter object provided + * @return the created object + */ + T callStage1(P parameter) throws E; + + /** + * Synchronous call + * + * @param parameter parameter object provided + * @param object the previously created object + */ + void callStage2(P parameter, T object) throws E; + + /** + * Synchronous call, called multiple times, once per registered callback + * + * @param parameter parameter object provided + * @param object the previously created object + * @param callback the current callback to execute + */ + void callStage3(P parameter, T object, C callback) throws E; + } + + @SuppressWarnings("rawtypes") + static final AtomicIntegerFieldUpdater STATE_FIELD = AtomicIntegerFieldUpdater.newUpdater(AsynchronousExecutor.Task.class, "state"); + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static boolean set(AsynchronousExecutor.Task $this, int expected, int value) { + return STATE_FIELD.compareAndSet($this, expected, value); + } + + class Task implements Runnable { + static final int PENDING = 0x0; + static final int STAGE_1_ASYNC = PENDING + 1; + static final int STAGE_1_SYNC = STAGE_1_ASYNC + 1; + static final int STAGE_1_COMPLETE = STAGE_1_SYNC + 1; + static final int FINISHED = STAGE_1_COMPLETE + 1; + + volatile int state = PENDING; + final P parameter; + T object; + final List callbacks = new LinkedList(); + E t = null; + + Task(final P parameter) { + this.parameter = parameter; + } + + public void run() { + if (initAsync()) { + finished.add(this); + } + } + + boolean initAsync() { + if (set(this, PENDING, STAGE_1_ASYNC)) { + boolean ret = true; + + try { + init(); + } finally { + if (set(this, STAGE_1_ASYNC, STAGE_1_COMPLETE)) { + // No one is/will be waiting + } else { + // We know that the sync thread will be waiting + synchronized (this) { + if (state != STAGE_1_SYNC) { + // They beat us to the synchronized block + this.notifyAll(); + } else { + // We beat them to the synchronized block + } + state = STAGE_1_COMPLETE; // They're already synchronized, atomic locks are not needed + } + // We want to return false, because we know a synchronous task already handled the finish() + ret = false; // Don't return inside finally; VERY bad practice. + } + } + + return ret; + } else { + return false; + } + } + + void initSync() { + if (set(this, PENDING, STAGE_1_COMPLETE)) { + // If we succeed that variable switch, good as done + init(); + } else if (set(this, STAGE_1_ASYNC, STAGE_1_SYNC)) { + // Async thread is running, but this shouldn't be likely; we need to sync to wait on them because of it. + synchronized (this) { + if (set(this, STAGE_1_SYNC, PENDING)) { // They might NOT synchronized yet, atomic lock IS needed + // We are the first into the lock + while (state != STAGE_1_COMPLETE) { + try { + this.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Unable to handle interruption on " + parameter, e); + } + } + } else { + // They beat us to the synchronized block + } + } + } else { + // Async thread is not pending, the more likely situation for a task not pending + } + } + + @SuppressWarnings("unchecked") + void init() { + try { + object = provider.callStage1(parameter); + } catch (final Throwable t) { + this.t = (E) t; + } + } + + @SuppressWarnings("unchecked") + T get() throws E { + initSync(); + if (callbacks.isEmpty()) { + // 'this' is a placeholder to prevent callbacks from being empty during finish call + // See get method below + callbacks.add((C) this); + } + finish(); + return object; + } + + void finish() throws E { + switch (state) { + default: + case PENDING: + case STAGE_1_ASYNC: + case STAGE_1_SYNC: + throw new IllegalStateException("Attempting to finish unprepared(" + state + ") task(" + parameter + ")"); + case STAGE_1_COMPLETE: + try { + if (t != null) { + throw t; + } + if (callbacks.isEmpty()) { + return; + } + + final CallBackProvider provider = AsynchronousExecutor.this.provider; + final P parameter = this.parameter; + final T object = this.object; + + provider.callStage2(parameter, object); + for (C callback : callbacks) { + if (callback == this) { + // 'this' is a placeholder to prevent callbacks from being empty on a get() call + // See get method above + continue; + } + provider.callStage3(parameter, object, callback); + } + } finally { + tasks.remove(parameter); + state = FINISHED; + } + case FINISHED: + } + } + + boolean drop() { + if (set(this, PENDING, FINISHED)) { + // If we succeed that variable switch, good as forgotten + tasks.remove(parameter); + return true; + } else { + // We need the async thread to finish normally to properly dispose of the task + return false; + } + } + } + + final CallBackProvider provider; + final Queue finished = new ConcurrentLinkedQueue(); + final Map tasks = new HashMap(); + final ThreadPoolExecutor pool; + + /** + * Uses a thread pool to pass executions to the provider. + * @see AsynchronousExecutor + */ + public AsynchronousExecutor(final CallBackProvider provider, final int coreSize) { + Validate.notNull(provider, "Provider cannot be null"); + this.provider = provider; + + // We have an unbound queue size so do not need a max thread size + pool = new ThreadPoolExecutor(coreSize, Integer.MAX_VALUE, 60l, TimeUnit.SECONDS, new LinkedBlockingQueue(), provider); + } + + /** + * Adds a callback to the parameter provided, adding parameter to the queue if needed. + *

    + * This should always be synchronous. + */ + public void add(P parameter, C callback) { + Task task = tasks.get(parameter); + if (task == null) { + tasks.put(parameter, task = new Task(parameter)); + pool.execute(task); + } + task.callbacks.add(callback); + } + + /** + * This removes a particular callback from the specified parameter. + *

    + * If no callbacks remain for a given parameter, then the {@link CallBackProvider CallBackProvider's} stages may be omitted from execution. + * Stage 3 will have no callbacks, stage 2 will be skipped unless a {@link #get(Object)} is used, and stage 1 will be avoided on a best-effort basis. + *

    + * Subsequent calls to {@link #getSkipQueue(Object)} will always work. + *

    + * Subsequent calls to {@link #get(Object)} might work. + *

    + * This should always be synchronous + * @return true if no further execution for the parameter is possible, such that, no exceptions will be thrown in {@link #finishActive()} for the parameter, and {@link #get(Object)} will throw an {@link IllegalStateException}, false otherwise + * @throws IllegalStateException if parameter is not in the queue anymore + * @throws IllegalStateException if the callback was not specified for given parameter + */ + public boolean drop(P parameter, C callback) throws IllegalStateException { + final Task task = tasks.get(parameter); + if (task == null) { + return true; + } + if (!task.callbacks.remove(callback)) { + throw new IllegalStateException("Unknown " + callback + " for " + parameter); + } + if (task.callbacks.isEmpty()) { + return task.drop(); + } + return false; + } + + /** + * This method attempts to skip the waiting period for said parameter. + *

    + * This should always be synchronous. + * @throws IllegalStateException if the parameter is not in the queue anymore, or sometimes if called from asynchronous thread + */ + public T get(P parameter) throws E, IllegalStateException { + final Task task = tasks.get(parameter); + if (task == null) { + throw new IllegalStateException("Unknown " + parameter); + } + return task.get(); + } + + /** + * Processes a parameter as if it was in the queue, without ever passing to another thread. + */ + public T getSkipQueue(P parameter) throws E { + return skipQueue(parameter); + } + + /** + * Processes a parameter as if it was in the queue, without ever passing to another thread. + */ + public T getSkipQueue(P parameter, C callback) throws E { + final T object = skipQueue(parameter); + provider.callStage3(parameter, object, callback); + return object; + } + + /** + * Processes a parameter as if it was in the queue, without ever passing to another thread. + */ + public T getSkipQueue(P parameter, C...callbacks) throws E { + final CallBackProvider provider = this.provider; + final T object = skipQueue(parameter); + for (C callback : callbacks) { + provider.callStage3(parameter, object, callback); + } + return object; + } + + /** + * Processes a parameter as if it was in the queue, without ever passing to another thread. + */ + public T getSkipQueue(P parameter, Iterable callbacks) throws E { + final CallBackProvider provider = this.provider; + final T object = skipQueue(parameter); + for (C callback : callbacks) { + provider.callStage3(parameter, object, callback); + } + return object; + } + + private T skipQueue(P parameter) throws E { + Task task = tasks.get(parameter); + if (task != null) { + return task.get(); + } + T object = provider.callStage1(parameter); + provider.callStage2(parameter, object); + return object; + } + + /** + * This is the 'heartbeat' that should be called synchronously to finish any pending tasks + */ + public void finishActive() throws E { + final Queue finished = this.finished; + while (!finished.isEmpty()) { + finished.poll().finish(); + } + } + + public void setActiveThreads(final int coreSize) { + pool.setCorePoolSize(coreSize); + } + + // Paper start + public boolean hasTask(P parameter) throws IllegalStateException { + return tasks.get(parameter) != null; + } + // Paper end +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java b/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java new file mode 100644 index 000000000000..165843ddfea2 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java @@ -0,0 +1,48 @@ +package org.bukkit.craftbukkit.util; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +import net.minecraft.server.BlockPosition; +import net.minecraft.server.IBlockData; +import net.minecraft.server.World; + +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.block.CraftBlockState; + +public class BlockStateListPopulator extends DummyGeneratorAccess { + private final World world; + private final LinkedHashMap list; + + public BlockStateListPopulator(World world) { + this(world, new LinkedHashMap<>()); + } + + public BlockStateListPopulator(World world, LinkedHashMap list) { + this.world = world; + this.list = list; + } + + @Override + public boolean setTypeAndData(BlockPosition position, IBlockData data, int flag) { + CraftBlockState state = CraftBlockState.getBlockState(world, position, flag); + state.setData(data); + list.put(position, state); + return true; + } + + public void updateList() { + for (BlockState state : list.values()) { + state.update(true); + } + } + + public List getList() { + return new ArrayList<>(list.values()); + } + + public World getWorld() { + return world; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java new file mode 100644 index 000000000000..b66323220124 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java @@ -0,0 +1,434 @@ +package org.bukkit.craftbukkit.util; + +import com.google.common.io.ByteStreams; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.zip.ZipEntry; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import joptsimple.OptionSpec; +import org.bukkit.Material; +import org.bukkit.plugin.AuthorNagException; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +import javax.annotation.Nonnull; + +/** + * This file is imported from Commodore. + * + * @author md_5 + */ +public class Commodore +{ + + private static final Set EVIL = new HashSet<>( Arrays.asList( + "org/bukkit/World (III)I getBlockTypeIdAt", + "org/bukkit/World (Lorg/bukkit/Location;)I getBlockTypeIdAt", + "org/bukkit/block/Block ()I getTypeId", + "org/bukkit/block/Block (I)Z setTypeId", + "org/bukkit/block/Block (IZ)Z setTypeId", + "org/bukkit/block/Block (IBZ)Z setTypeIdAndData", + "org/bukkit/block/Block (B)V setData", + "org/bukkit/block/Block (BZ)V setData", + "org/bukkit/inventory/ItemStack ()I getTypeId", + "org/bukkit/inventory/ItemStack (I)V setTypeId" + ) ); + + // Paper start - Plugin rewrites + private static final Map SEARCH_AND_REMOVE = initReplacementsMap(); + private static Map initReplacementsMap() + { + Map getAndRemove = new HashMap<>(); + // Be wary of maven shade's relocations + getAndRemove.put( "org/bukkit/".concat( "craftbukkit/libs/it/unimi/dsi/fastutil/" ), "org/bukkit/".concat( "craftbukkit/libs/" ) ); // Remap fastutil to our location + + if ( Boolean.getBoolean( "debug.rewriteForIde" ) ) + { + // unversion incoming calls for pre-relocate debug work + final String NMS_REVISION_PACKAGE = "v1_13_R2/"; + + getAndRemove.put( "net/minecraft/".concat( "server/" + NMS_REVISION_PACKAGE ), NMS_REVISION_PACKAGE ); + getAndRemove.put( "org/bukkit/".concat( "craftbukkit/" + NMS_REVISION_PACKAGE ), NMS_REVISION_PACKAGE ); + } + + return getAndRemove; + } + + @Nonnull + private static String getOriginalOrRewrite(@Nonnull String original) + { + String rewrite = null; + for ( Map.Entry entry : SEARCH_AND_REMOVE.entrySet() ) + { + if ( original.contains( entry.getKey() ) ) + { + rewrite = original.replace( entry.getValue(), "" ); + } + } + + return rewrite != null ? rewrite : original; + } + // Paper end + + public static void main(String[] args) + { + OptionParser parser = new OptionParser(); + OptionSpec inputFlag = parser.acceptsAll( Arrays.asList( "i", "input" ) ).withRequiredArg().ofType( File.class ).required(); + OptionSpec outputFlag = parser.acceptsAll( Arrays.asList( "o", "output" ) ).withRequiredArg().ofType( File.class ).required(); + + OptionSet options = parser.parse( args ); + + File input = options.valueOf( inputFlag ); + File output = options.valueOf( outputFlag ); + + if ( input.isDirectory() ) + { + if ( !output.isDirectory() ) + { + System.err.println( "If input directory specified, output directory required too" ); + return; + } + + for ( File in : input.listFiles() ) + { + if ( in.getName().endsWith( ".jar" ) ) + { + convert( in, new File( output, in.getName() ) ); + } + } + } else + { + convert( input, output ); + } + } + + private static void convert(File in, File out) + { + System.out.println( "Attempting to convert " + in + " to " + out ); + + try + { + try ( JarFile inJar = new JarFile( in, false ) ) + { + JarEntry entry = inJar.getJarEntry( ".commodore" ); + if ( entry != null ) + { + return; + } + + try ( JarOutputStream outJar = new JarOutputStream( new FileOutputStream( out ) ) ) + { + for ( Enumeration entries = inJar.entries(); entries.hasMoreElements(); ) + { + entry = entries.nextElement(); + + try ( InputStream is = inJar.getInputStream( entry ) ) + { + byte[] b = ByteStreams.toByteArray( is ); + + if ( entry.getName().endsWith( ".class" ) ) + { + b = convert( b, false ); + entry = new JarEntry( entry.getName() ); + } + + outJar.putNextEntry( entry ); + outJar.write( b ); + } + } + + outJar.putNextEntry( new ZipEntry( ".commodore" ) ); + } + } + } catch ( Exception ex ) + { + System.err.println( "Fatal error trying to convert " + in ); + ex.printStackTrace(); + } + } + + public static byte[] convert(byte[] b, final boolean modern) + { + ClassReader cr = new ClassReader( b ); + ClassWriter cw = new ClassWriter( cr, 0 ); + + cr.accept( new ClassVisitor( Opcodes.ASM7, cw ) + { + // Paper start - Rewrite plugins + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) + { + desc = getOriginalOrRewrite( desc ); + if ( signature != null ) { + signature = getOriginalOrRewrite( signature ); + } + + return super.visitField( access, name, desc, signature, value) ; + } + // Paper end + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) + { + return new MethodVisitor( api, super.visitMethod( access, name, desc, signature, exceptions ) ) + { + // Paper start - Plugin rewrites + @Override + public void visitInvokeDynamicInsn(String name, String desc, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) + { + // Paper start - Rewrite plugins + name = getOriginalOrRewrite( name ); + if ( desc != null ) + { + desc = getOriginalOrRewrite( desc ); + } + // Paper end + + super.visitInvokeDynamicInsn( name, desc, bootstrapMethodHandle, bootstrapMethodArguments ); + } + + @Override + public void visitTypeInsn(int opcode, String type) + { + type = getOriginalOrRewrite( type ); + + super.visitTypeInsn( opcode, type ); + } + + @Override + public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { + for ( int i = 0; i < local.length; i++ ) + { + if ( !( local[i] instanceof String ) ) { continue; } + + local[i] = getOriginalOrRewrite( (String) local[i] ); + } + + for ( int i = 0; i < stack.length; i++ ) + { + if ( !( stack[i] instanceof String ) ) { continue; } + + stack[i] = getOriginalOrRewrite( (String) stack[i] ); + } + + super.visitFrame( type, nLocal, local, nStack, stack ); + } + + + + @Override + public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) + { + descriptor = getOriginalOrRewrite( descriptor ); + + super.visitLocalVariable( name, descriptor, signature, start, end, index ); + } + // Paper end + + + + @Override + public void visitFieldInsn(int opcode, String owner, String name, String desc) + { + // Paper start - Rewrite plugins + owner = getOriginalOrRewrite( owner ); + if ( desc != null ) + { + desc = getOriginalOrRewrite( desc ); + } + // Paper end + + if ( modern ) + { + super.visitFieldInsn( opcode, owner, name, desc ); + return; + } + + if ( owner.equals( "org/bukkit/Material" ) ) + { + try + { + Material.valueOf( "LEGACY_" + name ); + } catch ( IllegalArgumentException ex ) + { + throw new AuthorNagException( "No legacy enum constant for " + name + ". Did you forget to define api-version: 1.13 in your plugin.yml?" ); + } + + super.visitFieldInsn( opcode, owner, "LEGACY_" + name, desc ); + return; + } + + if ( owner.equals( "org/bukkit/Art" ) ) + { + switch ( name ) + { + case "BURNINGSKULL": + super.visitFieldInsn( opcode, owner, "BURNING_SKULL", desc ); + return; + case "DONKEYKONG": + super.visitFieldInsn( opcode, owner, "DONKEY_KONG", desc ); + return; + } + } + + if ( owner.equals( "org/bukkit/DyeColor" ) ) + { + switch ( name ) + { + case "SILVER": + super.visitFieldInsn( opcode, owner, "LIGHT_GRAY", desc ); + return; + } + } + + if ( owner.equals( "org/bukkit/Particle" ) ) + { + switch ( name ) + { + case "BLOCK_CRACK": + case "BLOCK_DUST": + case "FALLING_DUST": + super.visitFieldInsn( opcode, owner, "LEGACY_" + name, desc ); + return; + } + } + + super.visitFieldInsn( opcode, owner, name, desc ); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) + { + // SPIGOT-4496 + if ( owner.equals( "org/bukkit/map/MapView" ) && name.equals( "getId" ) && desc.equals( "()S" ) ) + { + // Should be same size on stack so just call other method + super.visitMethodInsn( opcode, owner, name, "()I", itf ); + return; + } + // SPIGOT-4608 + if ( (owner.equals( "org/bukkit/Bukkit" ) || owner.equals( "org/bukkit/Server" ) ) && name.equals( "getMap" ) && desc.equals( "(S)Lorg/bukkit/map/MapView;" ) ) + { + // Should be same size on stack so just call other method + super.visitMethodInsn( opcode, owner, name, "(I)Lorg/bukkit/map/MapView;", itf ); + return; + } + + // Paper start - Rewrite plugins + owner = getOriginalOrRewrite( owner) ; + if (desc != null) + { + desc = getOriginalOrRewrite(desc); + } + // Paper end + + if ( modern ) + { + if ( owner.equals( "org/bukkit/Material" ) ) + { + switch ( name ) + { + case "values": + super.visitMethodInsn( opcode, "org/bukkit/craftbukkit/util/CraftLegacy", "modern_" + name, desc, itf ); + return; + case "ordinal": + super.visitMethodInsn( Opcodes.INVOKESTATIC, "org/bukkit/craftbukkit/util/CraftLegacy", "modern_" + name, "(Lorg/bukkit/Material;)I", false ); + return; + } + } + + super.visitMethodInsn( opcode, owner, name, desc, itf ); + return; + } + + if ( owner.equals( "org/bukkit/ChunkSnapshot" ) && name.equals( "getBlockData" ) && desc.equals( "(III)I" ) ) + { + super.visitMethodInsn( opcode, owner, "getData", desc, itf ); + return; + } + + Type retType = Type.getReturnType( desc ); + + if ( EVIL.contains( owner + " " + desc + " " + name ) + || ( owner.startsWith( "org/bukkit/block/" ) && ( desc + " " + name ).equals( "()I getTypeId" ) ) + || ( owner.startsWith( "org/bukkit/block/" ) && ( desc + " " + name ).equals( "(I)Z setTypeId" ) ) + || ( owner.startsWith( "org/bukkit/block/" ) && ( desc + " " + name ).equals( "()Lorg/bukkit/Material; getType" ) ) ) + { + Type[] args = Type.getArgumentTypes( desc ); + Type[] newArgs = new Type[ args.length + 1 ]; + newArgs[0] = Type.getObjectType( owner ); + System.arraycopy( args, 0, newArgs, 1, args.length ); + + super.visitMethodInsn( Opcodes.INVOKESTATIC, "org/bukkit/craftbukkit/util/CraftEvil", name, Type.getMethodDescriptor( retType, newArgs ), false ); + return; + } + + if ( owner.equals( "org/bukkit/DyeColor" ) ) + { + if ( name.equals( "valueOf" ) && desc.equals( "(Ljava/lang/String;)Lorg/bukkit/DyeColor;" ) ) + { + super.visitMethodInsn( opcode, owner, "legacyValueOf", desc, itf ); + return; + } + } + + if ( owner.equals( "org/bukkit/Material" ) ) + { + if ( name.equals( "getMaterial" ) && desc.equals( "(I)Lorg/bukkit/Material;" ) ) + { + super.visitMethodInsn( opcode, "org/bukkit/craftbukkit/util/CraftEvil", name, desc, itf ); + return; + } + + switch ( name ) + { + case "values": + case "valueOf": + case "getMaterial": + case "matchMaterial": + super.visitMethodInsn( opcode, "org/bukkit/craftbukkit/util/CraftLegacy", name, desc, itf ); + return; + case "ordinal": + super.visitMethodInsn( Opcodes.INVOKESTATIC, "org/bukkit/craftbukkit/util/CraftLegacy", "ordinal", "(Lorg/bukkit/Material;)I", false ); + return; + case "name": + case "toString": + super.visitMethodInsn( Opcodes.INVOKESTATIC, "org/bukkit/craftbukkit/util/CraftLegacy", name, "(Lorg/bukkit/Material;)Ljava/lang/String;", false ); + return; + } + } + + if ( retType.getSort() == Type.OBJECT && retType.getInternalName().equals( "org/bukkit/Material" ) && owner.startsWith( "org/bukkit" ) ) + { + super.visitMethodInsn( opcode, owner, name, desc, itf ); + super.visitMethodInsn( Opcodes.INVOKESTATIC, "org/bukkit/craftbukkit/util/CraftLegacy", "toLegacy", "(Lorg/bukkit/Material;)Lorg/bukkit/Material;", false ); + return; + } + + super.visitMethodInsn( opcode, owner, name, desc, itf ); + } + }; + } + }, 0 ); + + return cw.toByteArray(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java b/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java new file mode 100644 index 000000000000..5e20a9a619e2 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java @@ -0,0 +1,276 @@ +package org.bukkit.craftbukkit.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import net.minecraft.server.ChatClickable; +import net.minecraft.server.ChatComponentText; +import net.minecraft.server.ChatModifier; +import net.minecraft.server.EnumChatFormat; +import net.minecraft.server.ChatClickable.EnumClickAction; +import net.minecraft.server.IChatBaseComponent; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import net.minecraft.server.ChatMessage; +import org.bukkit.ChatColor; + +public final class CraftChatMessage { + + private static final Pattern LINK_PATTERN = Pattern.compile("((?:(?:https?):\\/\\/)?(?:[-\\w_\\.]{2,}\\.[a-z]{2,4}.*?(?=[\\.\\?!,;:]?(?:[" + String.valueOf(org.bukkit.ChatColor.COLOR_CHAR) + " \\n]|$))))"); + private static final Map formatMap; + + static { + Builder builder = ImmutableMap.builder(); + for (EnumChatFormat format : EnumChatFormat.values()) { + builder.put(Character.toLowerCase(format.toString().charAt(1)), format); + } + formatMap = builder.build(); + } + + public static EnumChatFormat getColor(ChatColor color) { + return formatMap.get(color.getChar()); + } + + public static ChatColor getColor(EnumChatFormat format) { + return ChatColor.getByChar(format.character); + } + + private static class StringMessage { + private static final Pattern INCREMENTAL_PATTERN = Pattern.compile("(" + String.valueOf(org.bukkit.ChatColor.COLOR_CHAR) + "[0-9a-fk-or])|(\\n)|((?:(?:https?):\\/\\/)?(?:[-\\w_\\.]{2,}\\.[a-z]{2,4}.*?(?=[\\.\\?!,;:]?(?:[" + String.valueOf(org.bukkit.ChatColor.COLOR_CHAR) + " \\n]|$))))", Pattern.CASE_INSENSITIVE); + + private final List list = new ArrayList(); + private IChatBaseComponent currentChatComponent = new ChatComponentText(""); + private ChatModifier modifier = new ChatModifier(); + private final IChatBaseComponent[] output; + private int currentIndex; + private final String message; + + private StringMessage(String message, boolean keepNewlines) { + this.message = message; + if (message == null) { + output = new IChatBaseComponent[] { currentChatComponent }; + return; + } + list.add(currentChatComponent); + + Matcher matcher = INCREMENTAL_PATTERN.matcher(message); + String match = null; + while (matcher.find()) { + int groupId = 0; + while ((match = matcher.group(++groupId)) == null) { + // NOOP + } + appendNewComponent(matcher.start(groupId)); + switch (groupId) { + case 1: + EnumChatFormat format = formatMap.get(match.toLowerCase(java.util.Locale.ENGLISH).charAt(1)); + if (format == EnumChatFormat.RESET) { + modifier = new ChatModifier(); + } else if (format.isFormat()) { + switch (format) { + case BOLD: + modifier.setBold(Boolean.TRUE); + break; + case ITALIC: + modifier.setItalic(Boolean.TRUE); + break; + case STRIKETHROUGH: + modifier.setStrikethrough(Boolean.TRUE); + break; + case UNDERLINE: + modifier.setUnderline(Boolean.TRUE); + break; + case OBFUSCATED: + modifier.setRandom(Boolean.TRUE); + break; + default: + throw new AssertionError("Unexpected message format"); + } + } else { // Color resets formatting + modifier = new ChatModifier().setColor(format); + } + break; + case 2: + if (keepNewlines) { + currentChatComponent.addSibling(new ChatComponentText("\n")); + } else { + currentChatComponent = null; + } + break; + case 3: + if ( !( match.startsWith( "http://" ) || match.startsWith( "https://" ) ) ) { + match = "http://" + match; + } + modifier.setChatClickable(new ChatClickable(EnumClickAction.OPEN_URL, match)); + appendNewComponent(matcher.end(groupId)); + modifier.setChatClickable((ChatClickable) null); + } + currentIndex = matcher.end(groupId); + } + + if (currentIndex < message.length()) { + appendNewComponent(message.length()); + } + + output = list.toArray(new IChatBaseComponent[list.size()]); + } + + private void appendNewComponent(int index) { + if (index <= currentIndex) { + return; + } + IChatBaseComponent addition = new ChatComponentText(message.substring(currentIndex, index)).setChatModifier(modifier); + currentIndex = index; + modifier = modifier.clone(); + if (currentChatComponent == null) { + currentChatComponent = new ChatComponentText(""); + list.add(currentChatComponent); + } + currentChatComponent.addSibling(addition); + } + + private IChatBaseComponent[] getOutput() { + return output; + } + } + + public static IChatBaseComponent wrapOrNull(String message) { + return (message == null || message.isEmpty()) ? null : new ChatComponentText(message); + } + + public static IChatBaseComponent fromStringOrNull(String message) { + // Paper start - fix up spigot tab API + return fromStringOrNull(message, false); + } + + public static IChatBaseComponent fromStringOrNull(String message, boolean keepNewlines) { + return (message == null || message.isEmpty()) ? null : fromString(message, keepNewlines)[0]; + // Paper end - fix up spigot tab API + } + + public static IChatBaseComponent[] fromString(String message) { + return fromString(message, false); + } + + public static IChatBaseComponent[] fromString(String message, boolean keepNewlines) { + return new StringMessage(message, keepNewlines).getOutput(); + } + + public static String fromComponent(IChatBaseComponent component) { + return fromComponent(component, EnumChatFormat.BLACK); + } + + public static String toJSON(IChatBaseComponent component) { + return IChatBaseComponent.ChatSerializer.a(component); + } + + public static String fromComponent(IChatBaseComponent component, EnumChatFormat defaultColor) { + if (component == null) return ""; + StringBuilder out = new StringBuilder(); + + for (IChatBaseComponent c : (Iterable) component) { + ChatModifier modi = c.getChatModifier(); + out.append(modi.getColor() == null ? defaultColor : modi.getColor()); + if (modi.isBold()) { + out.append(EnumChatFormat.BOLD); + } + if (modi.isItalic()) { + out.append(EnumChatFormat.ITALIC); + } + if (modi.isUnderlined()) { + out.append(EnumChatFormat.UNDERLINE); + } + if (modi.isStrikethrough()) { + out.append(EnumChatFormat.STRIKETHROUGH); + } + if (modi.isRandom()) { + out.append(EnumChatFormat.OBFUSCATED); + } + out.append(c.getText()); + } + return out.toString().replaceFirst("^(" + defaultColor + ")*", ""); + } + + public static IChatBaseComponent fixComponent(IChatBaseComponent component) { + Matcher matcher = LINK_PATTERN.matcher(""); + return fixComponent(component, matcher); + } + + private static IChatBaseComponent fixComponent(IChatBaseComponent component, Matcher matcher) { + if (component instanceof ChatComponentText) { + ChatComponentText text = ((ChatComponentText) component); + String msg = text.getText(); + if (matcher.reset(msg).find()) { + matcher.reset(); + + ChatModifier modifier = text.getChatModifier() != null ? + text.getChatModifier() : new ChatModifier(); + List extras = new ArrayList(); + List extrasOld = new ArrayList(text.a()); + component = text = new ChatComponentText(""); + + int pos = 0; + while (matcher.find()) { + String match = matcher.group(); + + if ( !( match.startsWith( "http://" ) || match.startsWith( "https://" ) ) ) { + match = "http://" + match; + } + + ChatComponentText prev = new ChatComponentText(msg.substring(pos, matcher.start())); + prev.setChatModifier(modifier); + extras.add(prev); + + ChatComponentText link = new ChatComponentText(matcher.group()); + ChatModifier linkModi = modifier.clone(); + linkModi.setChatClickable(new ChatClickable(EnumClickAction.OPEN_URL, match)); + link.setChatModifier(linkModi); + extras.add(link); + + pos = matcher.end(); + } + + ChatComponentText prev = new ChatComponentText(msg.substring(pos)); + prev.setChatModifier(modifier); + extras.add(prev); + extras.addAll(extrasOld); + + for (IChatBaseComponent c : extras) { + text.addSibling(c); + } + } + } + + List extras = component.a(); + for (int i = 0; i < extras.size(); i++) { + IChatBaseComponent comp = (IChatBaseComponent) extras.get(i); + if (comp.getChatModifier() != null && comp.getChatModifier().h() == null) { + extras.set(i, fixComponent(comp, matcher)); + } + } + + if (component instanceof ChatMessage) { + Object[] subs = ((ChatMessage) component).l(); + for (int i = 0; i < subs.length; i++) { + Object comp = subs[i]; + if (comp instanceof IChatBaseComponent) { + IChatBaseComponent c = (IChatBaseComponent) comp; + if (c.getChatModifier() != null && c.getChatModifier().h() == null) { + subs[i] = fixComponent(c, matcher); + } + } else if (comp instanceof String && matcher.reset((String)comp).find()) { + subs[i] = fixComponent(new ChatComponentText((String) comp), matcher); + } + } + } + + return component; + } + + private CraftChatMessage() { + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftDamageSource.java b/src/main/java/org/bukkit/craftbukkit/util/CraftDamageSource.java new file mode 100644 index 000000000000..a8e2b5e20afb --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftDamageSource.java @@ -0,0 +1,31 @@ +package org.bukkit.craftbukkit.util; + +import net.minecraft.server.DamageSource; + +// Util class to create custom DamageSources. +public final class CraftDamageSource extends DamageSource { + public static DamageSource copyOf(final DamageSource original) { + CraftDamageSource newSource = new CraftDamageSource(original.translationIndex); + + // Check ignoresArmor + if (original.ignoresArmor()) { + newSource.setIgnoreArmor(); + } + + // Check magic + if (original.isMagic()) { + newSource.setMagic(); + } + + // Check fire + if (original.isExplosion()) { + newSource.setExplosion(); + } + + return newSource; + } + + private CraftDamageSource(String identifier) { + super(identifier); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftEvil.java b/src/main/java/org/bukkit/craftbukkit/util/CraftEvil.java new file mode 100644 index 000000000000..db77fbe71c9b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftEvil.java @@ -0,0 +1,98 @@ +package org.bukkit.craftbukkit.util; + +import com.google.common.base.Preconditions; +import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.craftbukkit.block.CraftBlockState; +import org.bukkit.inventory.ItemStack; + +/** + * @deprecated do not use for any reason + */ +@Deprecated +public class CraftEvil { + + private static final Int2ObjectMap byId = new Int2ObjectLinkedOpenHashMap<>(); + + static { + for (Material material : Material.values()) { + Preconditions.checkState(!byId.containsKey(material.getId()), "Duplicate material ID for", material); + byId.put(material.getId(), material); + } + } + + public static int getBlockTypeIdAt(World world, int x, int y, int z) { + return getId(world.getBlockAt(x, y, z).getType()); + } + + public static int getBlockTypeIdAt(World world, Location location) { + return getId(world.getBlockAt(location).getType()); + } + + public static Material getType(Block block) { + return CraftLegacy.toLegacyMaterial(((CraftBlock) block).getNMS()); + } + + public static Material getType(BlockState block) { + return CraftLegacy.toLegacyMaterial(((CraftBlockState) block).getHandle()); + } + + public static int getTypeId(Block block) { + return getId(block.getType()); + } + + public static boolean setTypeId(Block block, int type) { + block.setType(getMaterial(type)); + return true; + } + + public static boolean setTypeId(Block block, int type, boolean applyPhysics) { + block.setType(getMaterial(type), applyPhysics); + return true; + } + + public static boolean setTypeIdAndData(Block block, int type, byte data, boolean applyPhysics) { + block.setType(getMaterial(type), applyPhysics); + setData(block, data); + return true; + } + + public static void setData(Block block, byte data) { + ((CraftBlock) block).setData(data); + } + + public static void setData(Block block, byte data, boolean applyPhysics) { + ((CraftBlock) block).setData(data, applyPhysics); + } + + public static int getTypeId(BlockState state) { + return getId(state.getType()); + } + + public static boolean setTypeId(BlockState state, int type) { + state.setType(getMaterial(type)); + return true; + } + + public static int getTypeId(ItemStack stack) { + return getId(stack.getType()); + } + + public static void setTypeId(ItemStack stack, int type) { + stack.setType(getMaterial(type)); + } + + public static Material getMaterial(int id) { + return byId.get(id); + } + + public static int getId(Material material) { + return CraftLegacy.toLegacy(material).getId(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftIconCache.java b/src/main/java/org/bukkit/craftbukkit/util/CraftIconCache.java new file mode 100644 index 000000000000..3d90b3426873 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftIconCache.java @@ -0,0 +1,12 @@ +package org.bukkit.craftbukkit.util; + +import org.bukkit.util.CachedServerIcon; + +public class CraftIconCache implements CachedServerIcon { + public final String value; + + public String getData() { return value; } // Paper + public CraftIconCache(final String value) { + this.value = value; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftLegacy.java b/src/main/java/org/bukkit/craftbukkit/util/CraftLegacy.java new file mode 100644 index 000000000000..c6aae8071646 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftLegacy.java @@ -0,0 +1,437 @@ +package org.bukkit.craftbukkit.util; + +import com.google.common.base.Preconditions; +import com.mojang.datafixers.Dynamic; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import net.minecraft.server.Block; +import net.minecraft.server.BlockStateList; +import net.minecraft.server.Blocks; +import net.minecraft.server.DataConverterFlattenData; +import net.minecraft.server.DataConverterMaterialId; +import net.minecraft.server.DataConverterRegistry; +import net.minecraft.server.DataConverterTypes; +import net.minecraft.server.DispenserRegistry; +import net.minecraft.server.DynamicOpsNBT; +import net.minecraft.server.IBlockData; +import net.minecraft.server.IBlockState; +import net.minecraft.server.IRegistry; +import net.minecraft.server.Item; +import net.minecraft.server.Items; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.NBTBase; +import net.minecraft.server.NBTTagCompound; +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.material.MaterialData; + +/** + * This class may seem unnecessarily slow and complicated/repetitive however it + * is able to handle a lot more edge cases and invertible transformations (many + * of which are not immediately obvious) than any other alternative. If you do + * make changes to this class please make sure to contribute them back + * https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/browse so + * that all may benefit. + * + * @deprecated legacy use only + */ +@Deprecated +public class CraftLegacy { + + private static final Map SPAWN_EGGS = new HashMap<>(); + private static final Set whitelistedStates = new HashSet<>(Arrays.asList("explode", "check_decay", "decayable")); + private static final Map materialToItem = new HashMap<>(16384); + private static final Map itemToMaterial = new HashMap<>(1024); + private static final Map materialToData = new HashMap<>(4096); + private static final Map dataToMaterial = new HashMap<>(4096); + private static final Map materialToBlock = new HashMap<>(4096); + private static final Map blockToMaterial = new HashMap<>(1024); + + public static Material toLegacy(Material material) { + if (material == null || material.isLegacy()) { + return material; + } + + return toLegacyData(material).getItemType(); + } + + public static MaterialData toLegacyData(Material material) { + Preconditions.checkArgument(!material.isLegacy(), "toLegacy on legacy Material"); + MaterialData mappedData; + + if (material.isBlock()) { + Block block = CraftMagicNumbers.getBlock(material); + IBlockData blockData = block.getBlockData(); + + // Try exact match first + mappedData = dataToMaterial.get(blockData); + // Fallback to any block + if (mappedData == null) { + mappedData = blockToMaterial.get(block); + // Fallback to matching item + if (mappedData == null) { + mappedData = itemToMaterial.get(block.getItem()); + } + } + } else { + Item item = CraftMagicNumbers.getItem(material); + mappedData = itemToMaterial.get(item); + } + + return (mappedData == null) ? new MaterialData(Material.LEGACY_AIR) : mappedData; + } + + public static IBlockData fromLegacyData(Material material, Block block, byte data) { + Preconditions.checkArgument(material.isLegacy(), "fromLegacyData on modern Material"); + + MaterialData materialData = new MaterialData(material, data); + + // Try exact match first + IBlockData converted = materialToData.get(materialData); + if (converted != null) { + return converted; + } + + // Fallback to any block + Block convertedBlock = materialToBlock.get(materialData); + if (convertedBlock != null) { + return convertedBlock.getBlockData(); + } + + // Return existing block + return block.getBlockData(); + } + + public static Item fromLegacyData(Material material, Item item, short data) { + Preconditions.checkArgument(material.isLegacy(), "fromLegacyData on modern Material. Did you forget to define api-version: 1.13 in your plugin.yml?"); + + MaterialData materialData = new MaterialData(material, (byte) data); + + // First try matching item + Item convertedItem = materialToItem.get(materialData); + if (convertedItem != null) { + return convertedItem; + } + + // Fallback to matching block + if (material.isBlock()) { + // Try exact match first + IBlockData converted = materialToData.get(materialData); + if (converted != null) { + return converted.getBlock().getItem(); + } + + // Fallback to any block + Block convertedBlock = materialToBlock.get(materialData); + if (convertedBlock != null) { + return convertedBlock.getItem(); + } + } + + // Return existing item + return item; + } + + public static byte toLegacyData(IBlockData blockData) { + return toLegacy(blockData).getData(); + } + + public static Material toLegacyMaterial(IBlockData blockData) { + return toLegacy(blockData).getItemType(); + } + + public static MaterialData toLegacy(IBlockData blockData) { + MaterialData mappedData; + + // Try exact match first + mappedData = dataToMaterial.get(blockData); + // Fallback to any block + if (mappedData == null) { + mappedData = blockToMaterial.get(blockData.getBlock()); + } + + return (mappedData == null) ? new MaterialData(Material.LEGACY_AIR) : mappedData; + } + + public static Material fromLegacy(Material material) { + if (material == null || !material.isLegacy()) { + return material; + } + + return fromLegacy(new MaterialData(material)); + } + + public static Material fromLegacy(MaterialData materialData) { + return fromLegacy(materialData, false); + } + + public static Material fromLegacy(MaterialData materialData, boolean itemPriority) { + Material material = materialData.getItemType(); + if (material == null || !material.isLegacy()) { + return material; + } + + Material mappedData = null; + + // Try item first + if (itemPriority) { + Item item = materialToItem.get(materialData); + if (item != null) { + mappedData = CraftMagicNumbers.getMaterial(item); + } + } + + if (mappedData == null && material.isBlock()) { + // Try exact match first + IBlockData iblock = materialToData.get(materialData); + if (iblock != null) { + mappedData = CraftMagicNumbers.getMaterial(iblock.getBlock()); + } + + // Fallback to any block + if (mappedData == null) { + Block block = materialToBlock.get(materialData); + if (block != null) { + mappedData = CraftMagicNumbers.getMaterial(block); + } + } + } + + // Fallback to matching item + if (!itemPriority && mappedData == null) { + Item item = materialToItem.get(materialData); + if (item != null) { + mappedData = CraftMagicNumbers.getMaterial(item); + } + } + + return (mappedData == null) ? Material.AIR : mappedData; + } + + public static Material[] values() { + Material[] values = Material.values(); + return Arrays.copyOfRange(values, Material.LEGACY_AIR.ordinal(), values.length); + } + + public static Material valueOf(String name) { + return (name.startsWith(Material.LEGACY_PREFIX)) ? Material.valueOf(name) : Material.valueOf(Material.LEGACY_PREFIX + name); + } + + public static Material getMaterial(String name) { + return (name.startsWith(Material.LEGACY_PREFIX)) ? Material.getMaterial(name) : Material.getMaterial(Material.LEGACY_PREFIX + name); + } + + public static Material matchMaterial(String name) { + return (name.startsWith(Material.LEGACY_PREFIX)) ? Material.matchMaterial(name) : Material.matchMaterial(Material.LEGACY_PREFIX + name); + } + + public static int ordinal(Material material) { + Preconditions.checkArgument(material.isLegacy(), "ordinal on modern Material"); + + return material.ordinal() - Material.LEGACY_AIR.ordinal(); + } + + public static String name(Material material) { + return material.name().substring(Material.LEGACY_PREFIX.length()); + } + + public static String toString(Material material) { + return name(material); + } + + public static Material[] modern_values() { + Material[] values = Material.values(); + return Arrays.copyOfRange(values, 0, Material.LEGACY_AIR.ordinal()); + } + + public static int modern_ordinal(Material material) { + if (material.isLegacy()) { + // SPIGOT-4002: Fix for eclipse compiler manually compiling in default statements to lookupswitch + throw new NoSuchFieldError("Legacy field ordinal: " + material); + } + + return material.ordinal(); + } + + static { + SPAWN_EGGS.put((byte) 0, Material.PIG_SPAWN_EGG); // Will be fixed by updateMaterial if possible + + SPAWN_EGGS.put((byte) EntityType.BAT.getTypeId(), Material.BAT_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.BLAZE.getTypeId(), Material.BLAZE_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.CAVE_SPIDER.getTypeId(), Material.CAVE_SPIDER_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.CHICKEN.getTypeId(), Material.CHICKEN_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.COD.getTypeId(), Material.COD_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.COW.getTypeId(), Material.COW_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.CREEPER.getTypeId(), Material.CREEPER_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.DOLPHIN.getTypeId(), Material.DOLPHIN_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.DONKEY.getTypeId(), Material.DONKEY_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.ELDER_GUARDIAN.getTypeId(), Material.ELDER_GUARDIAN_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.ENDERMAN.getTypeId(), Material.ENDERMAN_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.ENDERMITE.getTypeId(), Material.ENDERMITE_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.EVOKER.getTypeId(), Material.EVOKER_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.GHAST.getTypeId(), Material.GHAST_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.GUARDIAN.getTypeId(), Material.GUARDIAN_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.HORSE.getTypeId(), Material.HORSE_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.HUSK.getTypeId(), Material.HUSK_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.LLAMA.getTypeId(), Material.LLAMA_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.MAGMA_CUBE.getTypeId(), Material.MAGMA_CUBE_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.MUSHROOM_COW.getTypeId(), Material.MOOSHROOM_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.MULE.getTypeId(), Material.MULE_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.OCELOT.getTypeId(), Material.OCELOT_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.PARROT.getTypeId(), Material.PARROT_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.PIG.getTypeId(), Material.PIG_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.PHANTOM.getTypeId(), Material.PHANTOM_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.POLAR_BEAR.getTypeId(), Material.POLAR_BEAR_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.PUFFERFISH.getTypeId(), Material.PUFFERFISH_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.RABBIT.getTypeId(), Material.RABBIT_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.SALMON.getTypeId(), Material.SALMON_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.SHEEP.getTypeId(), Material.SHEEP_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.SHULKER.getTypeId(), Material.SHULKER_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.SILVERFISH.getTypeId(), Material.SILVERFISH_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.SKELETON.getTypeId(), Material.SKELETON_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.SKELETON_HORSE.getTypeId(), Material.SKELETON_HORSE_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.SLIME.getTypeId(), Material.SLIME_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.SPIDER.getTypeId(), Material.SPIDER_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.SQUID.getTypeId(), Material.SQUID_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.STRAY.getTypeId(), Material.STRAY_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.TROPICAL_FISH.getTypeId(), Material.TROPICAL_FISH_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.TURTLE.getTypeId(), Material.TURTLE_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.VEX.getTypeId(), Material.VEX_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.VILLAGER.getTypeId(), Material.VILLAGER_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.VINDICATOR.getTypeId(), Material.VINDICATOR_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.WITCH.getTypeId(), Material.WITCH_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.WITHER_SKELETON.getTypeId(), Material.WITHER_SKELETON_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.WOLF.getTypeId(), Material.WOLF_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.ZOMBIE.getTypeId(), Material.ZOMBIE_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.ZOMBIE_HORSE.getTypeId(), Material.ZOMBIE_HORSE_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.PIG_ZOMBIE.getTypeId(), Material.ZOMBIE_PIGMAN_SPAWN_EGG); + SPAWN_EGGS.put((byte) EntityType.ZOMBIE_VILLAGER.getTypeId(), Material.ZOMBIE_VILLAGER_SPAWN_EGG); + + DispenserRegistry.c(); + + for (Material material : Material.values()) { + if (!material.isLegacy()) { + continue; + } + + // Handle blocks + if (material.isBlock()) { + for (byte data = 0; data < 16; data++) { + MaterialData matData = new MaterialData(material, data); + Dynamic blockTag = DataConverterFlattenData.b(material.getId() << 4 | data); + // TODO: better skull conversion, chests + if (blockTag.getString("Name").contains("%%FILTER_ME%%")) { + continue; + } + + String name = blockTag.getString("Name"); + // TODO: need to fix + if (name.equals("minecraft:portal")) { + name = "minecraft:nether_portal"; + } + + Block block = IRegistry.BLOCK.get(new MinecraftKey(name)); + if (block == null) { + continue; + } + IBlockData blockData = block.getBlockData(); + BlockStateList states = block.getStates(); + + Optional propMap = blockTag.get("Properties"); + if (propMap.isPresent()) { + NBTTagCompound properties = (NBTTagCompound) propMap.get().getValue(); + for (String dataKey : properties.getKeys()) { + IBlockState state = states.a(dataKey); + + if (state == null) { + if (whitelistedStates.contains(dataKey)) { + continue; + } + throw new IllegalStateException("No state for " + dataKey); + } + + Preconditions.checkState(!properties.getString(dataKey).isEmpty(), "Empty data string"); + Optional opt = state.b(properties.getString(dataKey)); + + blockData = blockData.set(state, (Comparable) opt.get()); + } + } + + if (block == Blocks.AIR) { + continue; + } + + materialToData.put(matData, blockData); + if (!dataToMaterial.containsKey(blockData)) { + dataToMaterial.put(blockData, matData); + } + + materialToBlock.put(matData, block); + if (!blockToMaterial.containsKey(block)) { + blockToMaterial.put(block, matData); + } + } + } + + // Handle items (and second fallback for blocks) + int maxData = material.getMaxDurability() == 0 ? 16 : 1; + // Manually do oldold spawn eggs + if (material == Material.LEGACY_MONSTER_EGG) { + maxData = 121; // Vilager + 1 + } + + for (byte data = 0; data < maxData; data++) { + // Manually skip invalid oldold spawn + if (material == Material.LEGACY_MONSTER_EGG /*&& data != 0 && EntityType.fromId(data) == null*/) { // Mojang broke 18w19b + continue; + } + // Skip non item stacks for now (18w19b) + if (DataConverterMaterialId.a(material.getId()) == null) { + continue; + } + + MaterialData matData = new MaterialData(material, data); + + NBTTagCompound stack = new NBTTagCompound(); + stack.setInt("id", material.getId()); + stack.setShort("Damage", data); + + Dynamic converted = DataConverterRegistry.a().update(DataConverterTypes.ITEM_STACK, new Dynamic(DynamicOpsNBT.a, stack), -1, CraftMagicNumbers.INSTANCE.getDataVersion()); + + String newId = converted.getString("id"); + // Recover spawn eggs with invalid data + if (newId.equals("minecraft:spawn_egg")) { + newId = "minecraft:pig_spawn_egg"; + } + + // Preconditions.checkState(newId.contains("minecraft:"), "Unknown new material for " + matData); + Item newMaterial = IRegistry.ITEM.get(new MinecraftKey(newId)); + + if (newMaterial == Items.AIR) { + continue; + } + + materialToItem.put(matData, newMaterial); + if (!itemToMaterial.containsKey(newMaterial)) { + itemToMaterial.put(newMaterial, matData); + } + } + + for (Map.Entry entry : SPAWN_EGGS.entrySet()) { + MaterialData matData = new MaterialData(Material.LEGACY_MONSTER_EGG, entry.getKey()); + Item newMaterial = CraftMagicNumbers.getItem(entry.getValue()); + + materialToItem.put(matData, newMaterial); + itemToMaterial.put(newMaterial, matData); + } + } + } + + public static void main(String[] args) { + System.err.println(""); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java new file mode 100644 index 000000000000..4342a9f43292 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java @@ -0,0 +1,267 @@ +package org.bukkit.craftbukkit.util; + +import com.google.common.base.Charsets; +import com.google.common.collect.Maps; +import com.google.common.io.Files; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.minecraft.server.AdvancementDataWorld; +import net.minecraft.server.Block; +import net.minecraft.server.ChatDeserializer; +import net.minecraft.server.IBlockData; +import net.minecraft.server.IRegistry; +import net.minecraft.server.Item; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.MojangsonParser; +import net.minecraft.server.NBTTagCompound; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.UnsafeValues; +import org.bukkit.advancement.Advancement; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.inventory.ItemStack; +import org.bukkit.material.MaterialData; +import org.bukkit.plugin.InvalidPluginException; +import org.bukkit.plugin.PluginDescriptionFile; + +@SuppressWarnings("deprecation") +public final class CraftMagicNumbers implements UnsafeValues { + public static final UnsafeValues INSTANCE = new CraftMagicNumbers(); + + private CraftMagicNumbers() {} + + public static IBlockData getBlock(MaterialData material) { + return getBlock(material.getItemType(), material.getData()); + } + + public static IBlockData getBlock(Material material, byte data) { + return CraftLegacy.fromLegacyData(CraftLegacy.toLegacy(material), getBlock(material), data); + } + + public static MaterialData getMaterial(IBlockData data) { + return CraftLegacy.toLegacy(getMaterial(data.getBlock())).getNewData(toLegacyData(data)); + } + + public static Item getItem(Material material, short data) { + if (material.isLegacy()) { + return CraftLegacy.fromLegacyData(CraftLegacy.toLegacy(material), getItem(material), data); + } + + return getItem(material); + } + + public static MaterialData getMaterialData(Item item) { + return CraftLegacy.toLegacyData(getMaterial(item)); + } + + // ======================================================================== + private static final Map BLOCK_MATERIAL = new HashMap<>(); + private static final Map ITEM_MATERIAL = new HashMap<>(); + private static final Map MATERIAL_ITEM = new HashMap<>(); + private static final Map MATERIAL_BLOCK = new HashMap<>(); + + static { + for (Block block : (Iterable) IRegistry.BLOCK) { // Eclipse fail + BLOCK_MATERIAL.put(block, Material.getMaterial(IRegistry.BLOCK.getKey(block).getKey().toUpperCase(Locale.ROOT))); + } + + for (Item item : (Iterable) IRegistry.ITEM) { // Eclipse fail + ITEM_MATERIAL.put(item, Material.getMaterial(IRegistry.ITEM.getKey(item).getKey().toUpperCase(Locale.ROOT))); + } + + for (Material material : Material.values()) { + MinecraftKey key = key(material); + // TODO: only register if block/item? + MATERIAL_ITEM.put(material, IRegistry.ITEM.get(key)); + MATERIAL_BLOCK.put(material, IRegistry.BLOCK.get(key)); + } + } + + public static Material getMaterial(Block block) { + return BLOCK_MATERIAL.get(block); + } + + public static Material getMaterial(Item item) { + return ITEM_MATERIAL.getOrDefault(item, Material.AIR); + } + + public static Item getItem(Material material) { + return MATERIAL_ITEM.get(material); + } + + public static Block getBlock(Material material) { + return MATERIAL_BLOCK.get(material); + } + + public static MinecraftKey key(Material mat) { + if (mat.isLegacy()) { + mat = CraftLegacy.fromLegacy(mat); + } + + return CraftNamespacedKey.toMinecraft(mat.getKey()); + } + // ======================================================================== + + public static byte toLegacyData(IBlockData data) { + return CraftLegacy.toLegacyData(data); + } + + @Override + public Material toLegacy(Material material) { + return CraftLegacy.toLegacy(material); + } + + @Override + public Material fromLegacy(Material material) { + return CraftLegacy.fromLegacy(material); + } + + @Override + public Material fromLegacy(MaterialData material) { + return CraftLegacy.fromLegacy(material); + } + + @Override + public Material fromLegacy(MaterialData material, boolean itemPriority) { + return CraftLegacy.fromLegacy(material, itemPriority); + } + + @Override + public BlockData fromLegacy(Material material, byte data) { + return CraftBlockData.fromData(getBlock(material, data)); + } + + /** + * This string should be changed if the NMS mappings do. + * + * It has no meaning and should only be used as an equality check. Plugins + * which are sensitive to the NMS mappings may read it and refuse to load if + * it cannot be found or is different to the expected value. + * + * Remember: NMS is not supported API and may break at any time for any + * reason irrespective of this. There is often supported API to do the same + * thing as many common NMS usages. If not, you are encouraged to open a + * feature and/or pull request for consideration, or use a well abstracted + * third-party API such as ProtocolLib. + * + * @return string + */ + public String getMappingsVersion() { + return "00ed8e5c39debc3ed194ad7c5645cc45"; + } + + @Override + public int getDataVersion() { + return 1631; + } + + @Override + public ItemStack modifyItemStack(ItemStack stack, String arguments) { + net.minecraft.server.ItemStack nmsStack = CraftItemStack.asNMSCopy(stack); + + try { + nmsStack.setTag((NBTTagCompound) MojangsonParser.parse(arguments)); + } catch (CommandSyntaxException ex) { + Logger.getLogger(CraftMagicNumbers.class.getName()).log(Level.SEVERE, null, ex); + } + + stack.setItemMeta(CraftItemStack.getItemMeta(nmsStack)); + + return stack; + } + + @Override + public Advancement loadAdvancement(NamespacedKey key, String advancement) { + if (Bukkit.getAdvancement(key) != null) { + throw new IllegalArgumentException("Advancement " + key + " already exists."); + } + + net.minecraft.server.Advancement.SerializedAdvancement nms = (net.minecraft.server.Advancement.SerializedAdvancement) ChatDeserializer.a(AdvancementDataWorld.DESERIALIZER, advancement, net.minecraft.server.Advancement.SerializedAdvancement.class); + if (nms != null) { + AdvancementDataWorld.REGISTRY.a(Maps.newHashMap(Collections.singletonMap(CraftNamespacedKey.toMinecraft(key), nms))); + Advancement bukkit = Bukkit.getAdvancement(key); + + if (bukkit != null) { + File file = new File(MinecraftServer.getServer().bukkitDataPackFolder, "data" + File.separator + key.getNamespace() + File.separator + "advancements" + File.separator + key.getKey() + ".json"); + file.getParentFile().mkdirs(); + + try { + Files.write(advancement, file, Charsets.UTF_8); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Error saving advancement " + key, ex); + } + + MinecraftServer.getServer().getPlayerList().reload(); + + return bukkit; + } + } + + return null; + } + + @Override + public boolean removeAdvancement(NamespacedKey key) { + File file = new File(MinecraftServer.getServer().bukkitDataPackFolder, "data" + File.separator + key.getNamespace() + File.separator + "advancements" + File.separator + key.getKey() + ".json"); + return file.delete(); + } + + @Override + public void checkSupported(PluginDescriptionFile pdf) throws InvalidPluginException { + if (pdf.getAPIVersion() != null) { + if (!pdf.getAPIVersion().equals("1.13")) { + throw new InvalidPluginException("Unsupported API version " + pdf.getAPIVersion()); + } + } + } + + public static boolean isLegacy(PluginDescriptionFile pdf) { + return pdf.getAPIVersion() == null; + } + + @Override + public byte[] processClass(PluginDescriptionFile pdf, String path, byte[] clazz) { + try { + clazz = Commodore.convert(clazz, !isLegacy(pdf)); + } catch (Exception ex) { + Bukkit.getLogger().log(Level.SEVERE, "Fatal error trying to convert " + pdf.getFullName() + ":" + path, ex); + } + + return clazz; + } + + /** + * This helper class represents the different NBT Tags. + *

    + * These should match NBTBase#getTypeId + */ + public static class NBT { + + public static final int TAG_END = 0; + public static final int TAG_BYTE = 1; + public static final int TAG_SHORT = 2; + public static final int TAG_INT = 3; + public static final int TAG_LONG = 4; + public static final int TAG_FLOAT = 5; + public static final int TAG_DOUBLE = 6; + public static final int TAG_BYTE_ARRAY = 7; + public static final int TAG_STRING = 8; + public static final int TAG_LIST = 9; + public static final int TAG_COMPOUND = 10; + public static final int TAG_INT_ARRAY = 11; + public static final int TAG_ANY_NUMBER = 99; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftNBTTagConfigSerializer.java b/src/main/java/org/bukkit/craftbukkit/util/CraftNBTTagConfigSerializer.java new file mode 100644 index 000000000000..8b7c29403b31 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftNBTTagConfigSerializer.java @@ -0,0 +1,98 @@ +package org.bukkit.craftbukkit.util; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import net.minecraft.server.MojangsonParser; +import net.minecraft.server.NBTBase; +import net.minecraft.server.NBTList; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagDouble; +import net.minecraft.server.NBTTagInt; +import net.minecraft.server.NBTTagList; +import net.minecraft.server.NBTTagString; + +public class CraftNBTTagConfigSerializer { + + private static final Pattern ARRAY = Pattern.compile("^\\[.*]"); + private static final Pattern INTEGER = Pattern.compile("[-+]?(?:0|[1-9][0-9]*)?i", Pattern.CASE_INSENSITIVE); + private static final Pattern DOUBLE = Pattern.compile("[-+]?(?:[0-9]+[.]?|[0-9]*[.][0-9]+)(?:e[-+]?[0-9]+)?d", Pattern.CASE_INSENSITIVE); + private static final MojangsonParser MOJANGSON_PARSER = new MojangsonParser(new StringReader("")); + + public static Object serialize(NBTBase base) { + if (base instanceof NBTTagCompound) { + Map innerMap = new HashMap<>(); + for (String key : ((NBTTagCompound) base).getKeys()) { + innerMap.put(key, serialize(((NBTTagCompound) base).get(key))); + } + + return innerMap; + } else if (base instanceof NBTTagList) { + List baseList = new ArrayList<>(); + for (int i = 0; i < ((NBTList) base).size(); i++) { + baseList.add(serialize(((NBTList) base).get(i))); + } + + return baseList; + } else if (base instanceof NBTTagString) { + return base.asString(); + } else if (base instanceof NBTTagInt) { // No need to check for doubles, those are covered by the double itself + return base.toString() + "i"; + } + + return base.toString(); + } + + public static NBTBase deserialize(Object object) { + if (object instanceof Map) { + NBTTagCompound compound = new NBTTagCompound(); + for (Map.Entry entry : ((Map) object).entrySet()) { + compound.set(entry.getKey(), deserialize(entry.getValue())); + } + + return compound; + } else if (object instanceof List) { + List list = (List) object; + if (list.isEmpty()) { + return new NBTTagList(); // Default + } + + NBTTagList tagList = new NBTTagList(); + for (Object tag : list) { + tagList.add(deserialize(tag)); + } + + return tagList; + } else if (object instanceof String) { + String string = (String) object; + + if (ARRAY.matcher(string).matches()) { + try { + return new MojangsonParser(new StringReader(string)).parseArray(); + } catch (CommandSyntaxException e) { + throw new RuntimeException("Could not deserialize found list ", e); + } + } else if (INTEGER.matcher(string).matches()) { //Read integers on our own + return new NBTTagInt(Integer.parseInt(string.substring(0, string.length() - 1))); + } else if (DOUBLE.matcher(string).matches()) { + return new NBTTagDouble(Double.parseDouble(string.substring(0, string.length() - 1))); + } else { + NBTBase nbtBase = MOJANGSON_PARSER.parseLiteral(string); + + if (nbtBase instanceof NBTTagInt) { // If this returns an integer, it did not use our method from above + return new NBTTagString(nbtBase.asString()); // It then is a string that was falsely read as an int + } else if (nbtBase instanceof NBTTagDouble) { + return new NBTTagString(String.valueOf(((NBTTagDouble) nbtBase).asDouble())); // Doubles add "d" at the end + } else { + return nbtBase; + } + } + } + + throw new RuntimeException("Could not deserialize NBTBase"); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftNamespacedKey.java b/src/main/java/org/bukkit/craftbukkit/util/CraftNamespacedKey.java new file mode 100644 index 000000000000..f1f41262db3f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftNamespacedKey.java @@ -0,0 +1,30 @@ +package org.bukkit.craftbukkit.util; + +import net.minecraft.server.MinecraftKey; +import org.bukkit.NamespacedKey; + +public final class CraftNamespacedKey { + + public CraftNamespacedKey() { + } + + public static NamespacedKey fromStringOrNull(String string) { + if (string == null || string.isEmpty()) { + return null; + } + MinecraftKey minecraft = MinecraftKey.a(string); + return (minecraft == null) ? null : fromMinecraft(minecraft); + } + + public static NamespacedKey fromString(String string) { + return fromMinecraft(new MinecraftKey(string)); + } + + public static NamespacedKey fromMinecraft(MinecraftKey minecraft) { + return new NamespacedKey(minecraft.b(), minecraft.getKey()); + } + + public static MinecraftKey toMinecraft(NamespacedKey key) { + return new MinecraftKey(key.getNamespace(), key.getKey()); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftRayTraceResult.java b/src/main/java/org/bukkit/craftbukkit/util/CraftRayTraceResult.java new file mode 100644 index 000000000000..050f4d3a8eda --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftRayTraceResult.java @@ -0,0 +1,42 @@ +package org.bukkit.craftbukkit.util; + +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.entity.Entity; +import org.bukkit.util.RayTraceResult; +import org.bukkit.util.Vector; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.MovingObjectPosition; +import net.minecraft.server.MovingObjectPosition.EnumMovingObjectType; +import net.minecraft.server.Vec3D; + +public class CraftRayTraceResult { + + private CraftRayTraceResult() {} + + public static RayTraceResult fromNMS(World world, MovingObjectPosition nmsHitResult) { + if (nmsHitResult == null || nmsHitResult.type == EnumMovingObjectType.MISS) return null; + + Vec3D nmsHitPos = nmsHitResult.pos; + Vector hitPosition = new Vector(nmsHitPos.x, nmsHitPos.y, nmsHitPos.z); + BlockFace hitBlockFace = null; + + if (nmsHitResult.direction != null) { + hitBlockFace = CraftBlock.notchToBlockFace(nmsHitResult.direction); + } + + if (nmsHitResult.entity != null) { + Entity hitEntity = nmsHitResult.entity.getBukkitEntity(); + return new RayTraceResult(hitPosition, hitEntity, hitBlockFace); + } + + Block hitBlock = null; + BlockPosition nmsBlockPos = nmsHitResult.getBlockPosition(); + if (nmsBlockPos != null && world != null) { + hitBlock = world.getBlockAt(nmsBlockPos.getX(), nmsBlockPos.getY(), nmsBlockPos.getZ()); + } + return new RayTraceResult(hitPosition, hitBlock, hitBlockFace); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/DatFileFilter.java b/src/main/java/org/bukkit/craftbukkit/util/DatFileFilter.java new file mode 100644 index 000000000000..712c44f14555 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/DatFileFilter.java @@ -0,0 +1,10 @@ +package org.bukkit.craftbukkit.util; + +import java.io.File; +import java.io.FilenameFilter; + +public class DatFileFilter implements FilenameFilter { + public boolean accept(File dir, String name) { + return name.endsWith(".dat"); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java b/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java new file mode 100644 index 000000000000..b1ecf5a5309b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java @@ -0,0 +1,235 @@ +package org.bukkit.craftbukkit.util; + +import java.util.Random; +import java.util.function.Predicate; +import net.minecraft.server.BiomeBase; +import net.minecraft.server.Block; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.DifficultyDamageScaler; +import net.minecraft.server.Entity; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.EnumDirection; +import net.minecraft.server.EnumSkyBlock; +import net.minecraft.server.Fluid; +import net.minecraft.server.FluidType; +import net.minecraft.server.GeneratorAccess; +import net.minecraft.server.HeightMap; +import net.minecraft.server.IBlockData; +import net.minecraft.server.IChunkAccess; +import net.minecraft.server.IChunkProvider; +import net.minecraft.server.IDataManager; +import net.minecraft.server.ParticleParam; +import net.minecraft.server.PersistentCollection; +import net.minecraft.server.SoundCategory; +import net.minecraft.server.SoundEffect; +import net.minecraft.server.TickList; +import net.minecraft.server.TileEntity; +import net.minecraft.server.VoxelShape; +import net.minecraft.server.World; +import net.minecraft.server.WorldBorder; +import net.minecraft.server.WorldData; +import net.minecraft.server.WorldProvider; +import org.bukkit.event.entity.CreatureSpawnEvent; + +public class DummyGeneratorAccess implements GeneratorAccess { + + public static final GeneratorAccess INSTANCE = new DummyGeneratorAccess(); + + protected DummyGeneratorAccess() { + } + + @Override + public long getSeed() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public TickList getBlockTickList() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public TickList getFluidTickList() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public IChunkAccess getChunkAt(int i, int i1) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public World getMinecraftWorld() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public WorldData getWorldData() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public DifficultyDamageScaler getDamageScaler(BlockPosition bp) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public IChunkProvider getChunkProvider() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public IDataManager getDataManager() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Random m() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void update(BlockPosition bp, Block block) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public BlockPosition getSpawn() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void a(EntityHuman eh, BlockPosition bp, SoundEffect se, SoundCategory sc, float f, float f1) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void addParticle(ParticleParam pp, double d, double d1, double d2, double d3, double d4, double d5) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isEmpty(BlockPosition bp) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public BiomeBase getBiome(BlockPosition bp) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int getBrightness(EnumSkyBlock esb, BlockPosition bp) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int getLightLevel(BlockPosition bp, int i) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isChunkLoaded(int i, int i1, boolean bln) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean e(BlockPosition bp) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int a(HeightMap.Type type, int i, int i1) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public EntityHuman a(double d, double d1, double d2, double d3, Predicate prdct) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int c() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public WorldBorder getWorldBorder() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean a(Entity entity, VoxelShape vs) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int a(BlockPosition bp, EnumDirection ed) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean e() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int getSeaLevel() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public WorldProvider o() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public TileEntity getTileEntity(BlockPosition bp) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public IBlockData getType(BlockPosition bp) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Fluid getFluid(BlockPosition bp) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public PersistentCollection h() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean setTypeAndData(BlockPosition blockposition, IBlockData iblockdata, int i) { + return false; + } + + @Override + public boolean addEntity(Entity entity) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean addEntity(Entity entity, CreatureSpawnEvent.SpawnReason reason) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean setAir(BlockPosition blockposition) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void a(EnumSkyBlock enumskyblock, BlockPosition blockposition, int i) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean setAir(BlockPosition blockposition, boolean flag) { + throw new UnsupportedOperationException("Not supported yet."); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/ForwardLogHandler.java b/src/main/java/org/bukkit/craftbukkit/util/ForwardLogHandler.java new file mode 100644 index 000000000000..74ce4b2ec75b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/ForwardLogHandler.java @@ -0,0 +1,52 @@ +package org.bukkit.craftbukkit.util; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +public class ForwardLogHandler extends ConsoleHandler { + private Map cachedLoggers = new ConcurrentHashMap(); + + private Logger getLogger(String name) { + Logger logger = cachedLoggers.get(name); + if (logger == null) { + logger = LogManager.getLogger(name); + cachedLoggers.put(name, logger); + } + + return logger; + } + + @Override + public void publish(LogRecord record) { + Logger logger = getLogger(String.valueOf(record.getLoggerName())); // See SPIGOT-1230 + Throwable exception = record.getThrown(); + Level level = record.getLevel(); + String message = getFormatter().formatMessage(record); + + if (level == Level.SEVERE) { + logger.error(message, exception); + } else if (level == Level.WARNING) { + logger.warn(message, exception); + } else if (level == Level.INFO) { + logger.info(message, exception); + } else if (level == Level.CONFIG) { + logger.debug(message, exception); + } else { + logger.trace(message, exception); + } + } + + @Override + public void flush() { + } + + @Override + public void close() throws SecurityException { + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/HashTreeSet.java b/src/main/java/org/bukkit/craftbukkit/util/HashTreeSet.java new file mode 100644 index 000000000000..cd864c404747 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/HashTreeSet.java @@ -0,0 +1,117 @@ +package org.bukkit.craftbukkit.util; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.TreeSet; + +public class HashTreeSet implements Set { + + private Set hash = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet(); //Paper - Replace java.util.HashSet with ObjectOpenHashSet + private TreeSet tree = new TreeSet(); + + public HashTreeSet() { + + } + + @Override + public int size() { + return hash.size(); + } + + @Override + public boolean isEmpty() { + return hash.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return hash.contains(o); + } + + @Override + public Iterator iterator() { + return new Iterator() { + + private Iterator it = tree.iterator(); + private V last; + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public V next() { + return last = it.next(); + } + + @Override + public void remove() { + if (last == null) { + throw new IllegalStateException(); + } + it.remove(); + hash.remove(last); + last = null; + } + }; + } + + @Override + public Object[] toArray() { + return hash.toArray(); + } + + @Override + public Object[] toArray(Object[] a) { + return hash.toArray(a); + } + + @Override + public boolean add(V e) { + hash.add(e); + return tree.add(e); + } + + @Override + public boolean remove(Object o) { + hash.remove(o); + return tree.remove(o); + } + + @Override + public boolean containsAll(Collection c) { + return hash.containsAll(c); + } + + @Override + public boolean addAll(Collection c) { + tree.addAll(c); + return hash.addAll(c); + } + + @Override + public boolean retainAll(Collection c) { + tree.retainAll(c); + return hash.retainAll(c); + } + + @Override + public boolean removeAll(Collection c) { + tree.removeAll(c); + return hash.removeAll(c); + } + + @Override + public void clear() { + hash.clear(); + tree.clear(); + } + + public V first() { + return tree.first(); + } + +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/LazyHashSet.java b/src/main/java/org/bukkit/craftbukkit/util/LazyHashSet.java new file mode 100644 index 000000000000..2ae000d4baec --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/LazyHashSet.java @@ -0,0 +1,97 @@ +package org.bukkit.craftbukkit.util; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +public abstract class LazyHashSet implements Set { + Set reference = null; + + public int size() { + return getReference().size(); + } + + public boolean isEmpty() { + return getReference().isEmpty(); + } + + public boolean contains(Object o) { + return getReference().contains(o); + } + + public Iterator iterator() { + return getReference().iterator(); + } + + public Object[] toArray() { + return getReference().toArray(); + } + + public T[] toArray(T[] a) { + return getReference().toArray(a); + } + + public boolean add(E o) { + return getReference().add(o); + } + + public boolean remove(Object o) { + return getReference().remove(o); + } + + public boolean containsAll(Collection c) { + return getReference().containsAll(c); + } + + public boolean addAll(Collection c) { + return getReference().addAll(c); + } + + public boolean retainAll(Collection c) { + return getReference().retainAll(c); + } + + public boolean removeAll(Collection c) { + return getReference().removeAll(c); + } + + public void clear() { + getReference().clear(); + } + + public Set getReference() { + Set reference = this.reference ; + if (reference != null) { + return reference; + } + return this.reference = makeReference(); + } + + abstract Set makeReference(); + + public boolean isLazy() { + return reference == null; + } + + @Override + public int hashCode() { + return 157 * getReference().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || this.getClass() != obj.getClass()) { + return false; + } + LazyHashSet that = (LazyHashSet) obj; + return (this.isLazy() && that.isLazy()) || this.getReference().equals(that.getReference()); + } + + @Override + public String toString() { + return getReference().toString(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/LazyPlayerSet.java b/src/main/java/org/bukkit/craftbukkit/util/LazyPlayerSet.java new file mode 100644 index 000000000000..457d19e9c187 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/LazyPlayerSet.java @@ -0,0 +1,30 @@ +package org.bukkit.craftbukkit.util; + +import java.util.HashSet; +import java.util.List; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.MinecraftServer; + +import org.bukkit.entity.Player; + +public class LazyPlayerSet extends LazyHashSet { + + private final MinecraftServer server; + + public LazyPlayerSet(MinecraftServer server) { + this.server = server; + } + + @Override + HashSet makeReference() { + if (reference != null) { + throw new IllegalStateException("Reference already created!"); + } + List players = server.getPlayerList().players; + HashSet reference = new HashSet(players.size()); + for (EntityPlayer player : players) { + reference.add(player.getBukkitEntity()); + } + return reference; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/LongHash.java b/src/main/java/org/bukkit/craftbukkit/util/LongHash.java new file mode 100644 index 000000000000..691cafd03d5b --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/LongHash.java @@ -0,0 +1,15 @@ +package org.bukkit.craftbukkit.util; + +public class LongHash { + public static long toLong(int msw, int lsw) { + return ((long) msw << 32) + lsw - Integer.MIN_VALUE; + } + + public static int msw(long l) { + return (int) (l >> 32); + } + + public static int lsw(long l) { + return (int) (l & 0xFFFFFFFF) + Integer.MIN_VALUE; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/LongHashSet.java b/src/main/java/org/bukkit/craftbukkit/util/LongHashSet.java new file mode 100644 index 000000000000..a21357922094 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/LongHashSet.java @@ -0,0 +1,302 @@ +/* + Based on CompactHashSet Copyright 2011 Ontopia Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package org.bukkit.craftbukkit.util; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; + +public class LongHashSet { + private final static int INITIAL_SIZE = 3; + private final static double LOAD_FACTOR = 0.75; + + private final static long FREE = 0; + private final static long REMOVED = Long.MIN_VALUE; + + private int freeEntries; + private int elements; + private long[] values; + private int modCount; + + public LongHashSet() { + this(INITIAL_SIZE); + } + + public LongHashSet(int size) { + values = new long[(size==0 ? 1 : size)]; + elements = 0; + freeEntries = values.length; + modCount = 0; + } + + public Iterator iterator() { + return new Itr(); + } + + public int size() { + return elements; + } + + public boolean isEmpty() { + return elements == 0; + } + + public boolean contains(int msw, int lsw) { + return contains(LongHash.toLong(msw, lsw)); + } + + public boolean contains(long value) { + int hash = hash(value); + int index = (hash & 0x7FFFFFFF) % values.length; + int offset = 1; + + // search for the object (continue while !null and !this object) + while(values[index] != FREE && !(hash(values[index]) == hash && values[index] == value)) { + index = ((index + offset) & 0x7FFFFFFF) % values.length; + offset = offset * 2 + 1; + + if (offset == -1) { + offset = 2; + } + } + + return values[index] != FREE; + } + + public boolean add(int msw, int lsw) { + return add(LongHash.toLong(msw, lsw)); + } + + public boolean add(long value) { + int hash = hash(value); + int index = (hash & 0x7FFFFFFF) % values.length; + int offset = 1; + int deletedix = -1; + + // search for the object (continue while !null and !this object) + while(values[index] != FREE && !(hash(values[index]) == hash && values[index] == value)) { + // if there's a deleted object here we can put this object here, + // provided it's not in here somewhere else already + if (values[index] == REMOVED) { + deletedix = index; + } + + index = ((index + offset) & 0x7FFFFFFF) % values.length; + offset = offset * 2 + 1; + + if (offset == -1) { + offset = 2; + } + } + + if (values[index] == FREE) { + if (deletedix != -1) { // reusing a deleted cell + index = deletedix; + } else { + freeEntries--; + } + + modCount++; + elements++; + values[index] = value; + + if (1 - (freeEntries / (double) values.length) > LOAD_FACTOR) { + rehash(); + } + + return true; + } else { + return false; + } + } + + public void remove(int msw, int lsw) { + remove(LongHash.toLong(msw, lsw)); + } + + public boolean remove(long value) { + int hash = hash(value); + int index = (hash & 0x7FFFFFFF) % values.length; + int offset = 1; + + // search for the object (continue while !null and !this object) + while(values[index] != FREE && !(hash(values[index]) == hash && values[index] == value)) { + index = ((index + offset) & 0x7FFFFFFF) % values.length; + offset = offset * 2 + 1; + + if (offset == -1) { + offset = 2; + } + } + + if (values[index] != FREE) { + values[index] = REMOVED; + modCount++; + elements--; + return true; + } else { + return false; + } + } + + public void clear() { + elements = 0; + for (int ix = 0; ix < values.length; ix++) { + values[ix] = FREE; + } + + freeEntries = values.length; + modCount++; + } + + public long[] toArray() { + long[] result = new long[elements]; + long[] values = Arrays.copyOf(this.values, this.values.length); + int pos = 0; + + for (long value : values) { + if (value != FREE && value != REMOVED) { + result[pos++] = value; + } + } + + return result; + } + + public long popFirst() { + for (long value : values) { + if (value != FREE && value != REMOVED) { + remove(value); + return value; + } + } + + return 0; + } + + public long[] popAll() { + long[] ret = toArray(); + clear(); + return ret; + } + + // This method copied from Murmur3, written by Austin Appleby released under Public Domain + private int hash(long value) { + value ^= value >>> 33; + value *= 0xff51afd7ed558ccdL; + value ^= value >>> 33; + value *= 0xc4ceb9fe1a85ec53L; + value ^= value >>> 33; + return (int) value; + } + + private void rehash() { + int gargagecells = values.length - (elements + freeEntries); + if (gargagecells / (double) values.length > 0.05) { + rehash(values.length); + } else { + rehash(values.length * 2 + 1); + } + } + + private void rehash(int newCapacity) { + long[] newValues = new long[newCapacity]; + + for (long value : values) { + if (value == FREE || value == REMOVED) { + continue; + } + + int hash = hash(value); + int index = (hash & 0x7FFFFFFF) % newCapacity; + int offset = 1; + + // search for the object + while (newValues[index] != FREE) { + index = ((index + offset) & 0x7FFFFFFF) % newCapacity; + offset = offset * 2 + 1; + + if (offset == -1) { + offset = 2; + } + } + + newValues[index] = value; + } + + values = newValues; + freeEntries = values.length - elements; + } + + private class Itr implements Iterator { + private int index; + private int lastReturned = -1; + private int expectedModCount; + + public Itr() { + for (index = 0; index < values.length && (values[index] == FREE || values[index] == REMOVED); index++) { + // This is just to drive the index forward to the first valid entry + } + expectedModCount = modCount; + } + + public boolean hasNext() { + return index != values.length; + } + + public Long next() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + int length = values.length; + if (index >= length) { + lastReturned = -2; + throw new NoSuchElementException(); + } + + lastReturned = index; + for (index += 1; index < length && (values[index] == FREE || values[index] == REMOVED); index++) { + // This is just to drive the index forward to the next valid entry + } + + if (values[lastReturned] == FREE) { + return FREE; + } else { + return values[lastReturned]; + } + } + + public void remove() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + if (lastReturned == -1 || lastReturned == -2) { + throw new IllegalStateException(); + } + + if (values[lastReturned] != FREE && values[lastReturned] != REMOVED) { + values[lastReturned] = REMOVED; + elements--; + modCount++; + expectedModCount = modCount; + } + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/LongObjectHashMap.java b/src/main/java/org/bukkit/craftbukkit/util/LongObjectHashMap.java new file mode 100644 index 000000000000..0cd430a3adbe --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/LongObjectHashMap.java @@ -0,0 +1,422 @@ +package org.bukkit.craftbukkit.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.AbstractSet; +import java.util.Arrays; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +@SuppressWarnings("unchecked") +public class LongObjectHashMap implements Cloneable, Serializable { + static final long serialVersionUID = 2841537710170573815L; + + private static final long EMPTY_KEY = Long.MIN_VALUE; + private static final int BUCKET_SIZE = 4096; + + private transient long[][] keys; + private transient V[][] values; + private transient int modCount; + private transient int size; + + public LongObjectHashMap() { + initialize(); + } + + public LongObjectHashMap(Map map) { + this(); + putAll(map); + } + + public int size() { + return size; + } + + public boolean isEmpty() { + return size == 0; + } + + public boolean containsKey(long key) { + return get(key) != null; + } + + public boolean containsValue(V value) { + for (V val : values()) { + if (val == value || val.equals(value)) { + return true; + } + } + + return false; + } + + public V get(long key) { + int index = (int) (keyIndex(key) & (BUCKET_SIZE - 1)); + long[] inner = keys[index]; + if (inner == null) return null; + + for (int i = 0; i < inner.length; i++) { + long innerKey = inner[i]; + if (innerKey == EMPTY_KEY) { + return null; + } else if (innerKey == key) { + return values[index][i]; + } + } + + return null; + } + + public V put(long key, V value) { + int index = (int) (keyIndex(key) & (BUCKET_SIZE - 1)); + long[] innerKeys = keys[index]; + V[] innerValues = values[index]; + modCount++; + + if (innerKeys == null) { + // need to make a new chain + keys[index] = innerKeys = new long[8]; + Arrays.fill(innerKeys, EMPTY_KEY); + values[index] = innerValues = (V[]) new Object[8]; + innerKeys[0] = key; + innerValues[0] = value; + size++; + } else { + int i; + for (i = 0; i < innerKeys.length; i++) { + // found an empty spot in the chain to put this + if (innerKeys[i] == EMPTY_KEY) { + size++; + innerKeys[i] = key; + innerValues[i] = value; + return null; + } + + // found an existing entry in the chain with this key, replace it + if (innerKeys[i] == key) { + V oldValue = innerValues[i]; + innerKeys[i] = key; + innerValues[i] = value; + return oldValue; + } + } + + // chain is full, resize it and add our new entry + keys[index] = innerKeys = Arrays.copyOf(innerKeys, i << 1); + Arrays.fill(innerKeys, i, innerKeys.length, EMPTY_KEY); + values[index] = innerValues = Arrays.copyOf(innerValues, i << 1); + innerKeys[i] = key; + innerValues[i] = value; + size++; + } + + return null; + } + + public V remove(long key) { + int index = (int) (keyIndex(key) & (BUCKET_SIZE - 1)); + long[] inner = keys[index]; + if (inner == null) { + return null; + } + + for (int i = 0; i < inner.length; i++) { + // hit the end of the chain, didn't find this entry + if (inner[i] == EMPTY_KEY) { + break; + } + + if (inner[i] == key) { + V value = values[index][i]; + + for (i++; i < inner.length; i++) { + if (inner[i] == EMPTY_KEY) { + break; + } + + inner[i - 1] = inner[i]; + values[index][i - 1] = values[index][i]; + } + + inner[i - 1] = EMPTY_KEY; + values[index][i - 1] = null; + size--; + modCount++; + return value; + } + } + + return null; + } + + public void putAll(Map map) { + for (Map.Entry entry : map.entrySet()) { + put((Long) entry.getKey(), (V) entry.getValue()); + } + } + + public void clear() { + if (size == 0) { + return; + } + + modCount++; + size = 0; + Arrays.fill(keys, null); + Arrays.fill(values, null); + } + + public Set keySet() { + return new KeySet(); + } + + public Collection values() { + return new ValueCollection(); + } + + /** + * Returns a Set of Entry objects for the HashMap. This is not how the internal + * implementation is laid out so this constructs the entire Set when called. For + * this reason it should be avoided if at all possible. + * + * @return Set of Entry objects + * @deprecated + */ + @Deprecated + public Set> entrySet() { + HashSet> set = new HashSet>(); + for (long key : keySet()) { + set.add(new Entry(key, get(key))); + } + + return set; + } + + public Object clone() throws CloneNotSupportedException { + LongObjectHashMap clone = (LongObjectHashMap) super.clone(); + // Make sure we clear any existing information from the clone + clone.clear(); + // Make sure the clone is properly setup for new entries + clone.initialize(); + + // Iterate through the data normally to do a safe clone + for (long key : keySet()) { + final V value = get(key); + clone.put(key, value); + } + + return clone; + } + + private void initialize() { + keys = new long[BUCKET_SIZE][]; + values = (V[][]) new Object[BUCKET_SIZE][]; + } + + private long keyIndex(long key) { + key ^= key >>> 33; + key *= 0xff51afd7ed558ccdL; + key ^= key >>> 33; + key *= 0xc4ceb9fe1a85ec53L; + key ^= key >>> 33; + return key; + } + + private void writeObject(ObjectOutputStream outputStream) throws IOException { + outputStream.defaultWriteObject(); + + for (long key : keySet()) { + V value = get(key); + outputStream.writeLong(key); + outputStream.writeObject(value); + } + + outputStream.writeLong(EMPTY_KEY); + outputStream.writeObject(null); + } + + private void readObject(ObjectInputStream inputStream) throws ClassNotFoundException, IOException { + inputStream.defaultReadObject(); + initialize(); + + while (true) { + long key = inputStream.readLong(); + V value = (V) inputStream.readObject(); + if (key == EMPTY_KEY && value == null) { + break; + } + + put(key, value); + } + } + + + private class ValueIterator implements Iterator { + private int count; + private int index; + private int innerIndex; + private int expectedModCount; + private long lastReturned = EMPTY_KEY; + + long prevKey = EMPTY_KEY; + V prevValue; + + ValueIterator() { + expectedModCount = LongObjectHashMap.this.modCount; + } + + public boolean hasNext() { + return count < LongObjectHashMap.this.size; + } + + public void remove() { + if (LongObjectHashMap.this.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + if (lastReturned == EMPTY_KEY) { + throw new IllegalStateException(); + } + + count--; + LongObjectHashMap.this.remove(lastReturned); + lastReturned = EMPTY_KEY; + expectedModCount = LongObjectHashMap.this.modCount; + } + + public V next() { + if (LongObjectHashMap.this.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + if (!hasNext()) { + throw new NoSuchElementException(); + } + + long[][] keys = LongObjectHashMap.this.keys; + count++; + + if (prevKey != EMPTY_KEY) { + innerIndex++; + } + + for (; index < keys.length; index++) { + if (keys[index] != null) { + for (; innerIndex < keys[index].length; innerIndex++) { + long key = keys[index][innerIndex]; + V value = values[index][innerIndex]; + if (key == EMPTY_KEY) { + break; + } + + lastReturned = key; + prevKey = key; + prevValue = value; + return prevValue; + } + innerIndex = 0; + } + } + + throw new NoSuchElementException(); + } + } + + private class KeyIterator implements Iterator { + final ValueIterator iterator; + + public KeyIterator() { + iterator = new ValueIterator(); + } + + public void remove() { + iterator.remove(); + } + + public boolean hasNext() { + return iterator.hasNext(); + } + + public Long next() { + iterator.next(); + return iterator.prevKey; + } + } + + + private class KeySet extends AbstractSet { + public void clear() { + LongObjectHashMap.this.clear(); + } + + public int size() { + return LongObjectHashMap.this.size(); + } + + public boolean contains(Object key) { + return key instanceof Long && LongObjectHashMap.this.containsKey((Long) key); + + } + + public boolean remove(Object key) { + return LongObjectHashMap.this.remove((Long) key) != null; + } + + public Iterator iterator() { + return new KeyIterator(); + } + } + + + private class ValueCollection extends AbstractCollection { + public void clear() { + LongObjectHashMap.this.clear(); + } + + public int size() { + return LongObjectHashMap.this.size(); + } + + public boolean contains(Object value) { + return LongObjectHashMap.this.containsValue((V) value); + } + + public Iterator iterator() { + return new ValueIterator(); + } + } + + + private class Entry implements Map.Entry { + private final Long key; + private V value; + + Entry(long k, V v) { + key = k; + value = v; + } + + public Long getKey() { + return key; + } + + public V getValue() { + return value; + } + + public V setValue(V v) { + V old = value; + value = v; + put(key, v); + return old; + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/MojangNameLookup.java b/src/main/java/org/bukkit/craftbukkit/util/MojangNameLookup.java new file mode 100644 index 000000000000..93a8f0bd7076 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/MojangNameLookup.java @@ -0,0 +1,63 @@ +package org.bukkit.craftbukkit.util; + +import com.google.common.base.Charsets; +import com.google.gson.Gson; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.UUID; +import org.apache.commons.io.IOUtils; + +public class MojangNameLookup { + private static final Logger logger = LogManager.getFormatterLogger(MojangNameLookup.class); + + public static String lookupName(UUID id) { + if (id == null) { + return null; + } + + InputStream inputStream = null; + try { + URL url = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + id.toString().replace("-", "")); + URLConnection connection = url.openConnection(); + connection.setConnectTimeout(15000); + connection.setReadTimeout(15000); + connection.setUseCaches(false); + inputStream = connection.getInputStream(); + String result = IOUtils.toString(inputStream, Charsets.UTF_8); + Gson gson = new Gson(); + Response response = gson.fromJson(result, Response.class); + if (response == null || response.name == null) { + logger.warn("Failed to lookup name from UUID"); + return null; + } + + if (response.cause != null && response.cause.length() > 0) { + logger.warn("Failed to lookup name from UUID: %s", response.errorMessage); + return null; + } + + return response.name; + } catch (MalformedURLException ex) { + logger.warn("Malformed URL in UUID lookup"); + return null; + } catch (IOException ex) { + IOUtils.closeQuietly(inputStream); + } finally { + IOUtils.closeQuietly(inputStream); + } + + return null; + } + + private class Response { + String errorMessage; + String cause; + String name; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java new file mode 100644 index 000000000000..bbb5a84f3681 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java @@ -0,0 +1,28 @@ +package org.bukkit.craftbukkit.util; + +import net.minecraft.server.ExceptionWorldConflict; +import net.minecraft.server.MinecraftServer; + +public class ServerShutdownThread extends Thread { + private final MinecraftServer server; + + public ServerShutdownThread(MinecraftServer server) { + this.server = server; + } + + @Override + public void run() { + try { + org.spigotmc.AsyncCatcher.enabled = false; // Spigot + org.spigotmc.AsyncCatcher.shuttingDown = true; // Paper + server.stop(); + } catch (ExceptionWorldConflict ex) { + ex.printStackTrace(); + } finally { + try { + net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender + } catch (Exception e) { + } + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/ShortConsoleLogFormatter.java b/src/main/java/org/bukkit/craftbukkit/util/ShortConsoleLogFormatter.java new file mode 100644 index 000000000000..2dbfef963cd2 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/ShortConsoleLogFormatter.java @@ -0,0 +1,61 @@ +package org.bukkit.craftbukkit.util; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.SimpleDateFormat; +import java.util.logging.Formatter; +import java.util.logging.LogRecord; +import joptsimple.OptionException; +import joptsimple.OptionSet; +import net.minecraft.server.MinecraftServer; + +public class ShortConsoleLogFormatter extends Formatter { + private final SimpleDateFormat date; + + public ShortConsoleLogFormatter(MinecraftServer server) { + OptionSet options = server.options; + SimpleDateFormat date = null; + + if (options.has("date-format")) { + try { + Object object = options.valueOf("date-format"); + + if ((object != null) && (object instanceof SimpleDateFormat)) { + date = (SimpleDateFormat) object; + } + } catch (OptionException ex) { + System.err.println("Given date format is not valid. Falling back to default."); + } + } else if (options.has("nojline")) { + date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + } + + if (date == null) { + date = new SimpleDateFormat("HH:mm:ss"); + } + + this.date = date; + } + + @Override + public String format(LogRecord record) { + StringBuilder builder = new StringBuilder(); + Throwable ex = record.getThrown(); + + builder.append(date.format(record.getMillis())); + builder.append(" ["); + builder.append(record.getLevel().getLocalizedName().toUpperCase()); + builder.append("] "); + builder.append(formatMessage(record)); + builder.append('\n'); + + if (ex != null) { + StringWriter writer = new StringWriter(); + ex.printStackTrace(new PrintWriter(writer)); + builder.append(writer); + } + + return builder.toString(); + } + +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/UnsafeList.java b/src/main/java/org/bukkit/craftbukkit/util/UnsafeList.java new file mode 100644 index 000000000000..08d105603c60 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/UnsafeList.java @@ -0,0 +1,278 @@ +package org.bukkit.craftbukkit.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Arrays; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.RandomAccess; + +// implementation of an ArrayList that offers a getter without range checks +@SuppressWarnings("unchecked") +public class UnsafeList extends AbstractList implements List, RandomAccess, Cloneable, Serializable { + private static final long serialVersionUID = 8683452581112892191L; + + private transient Object[] data; + private int size; + private int initialCapacity; + + private Iterator[] iterPool = new Iterator[1]; + private int maxPool; + private int poolCounter; + + public UnsafeList(int capacity, int maxIterPool) { + super(); + if (capacity < 0) capacity = 32; + int rounded = Integer.highestOneBit(capacity - 1) << 1; + data = new Object[rounded]; + initialCapacity = rounded; + maxPool = maxIterPool; + iterPool[0] = new Itr(); + } + + public UnsafeList(int capacity) { + this(capacity, 5); + } + + public UnsafeList() { + this(32); + } + + public E get(int index) { + rangeCheck(index); + + return (E) data[index]; + } + + public E unsafeGet(int index) { + return (E) data[index]; + } + + public E set(int index, E element) { + rangeCheck(index); + + E old = (E) data[index]; + data[index] = element; + return old; + } + + public boolean add(E element) { + growIfNeeded(); + data[size++] = element; + return true; + } + + public void add(int index, E element) { + growIfNeeded(); + System.arraycopy(data, index, data, index + 1, size - index); + data[index] = element; + size++; + } + + public E remove(int index) { + rangeCheck(index); + + E old = (E) data[index]; + int movedCount = size - index - 1; + if (movedCount > 0) { + System.arraycopy(data, index + 1, data, index, movedCount); + } + data[--size] = null; + + return old; + } + + public boolean remove(Object o) { + int index = indexOf(o); + if (index >= 0) { + remove(index); + return true; + } + + return false; + } + + public int indexOf(Object o) { + for (int i = 0; i < size; i++) { + if (o == data[i] || o.equals(data[i])) { + return i; + } + } + + return -1; + } + + public boolean contains(Object o) { + return indexOf(o) >= 0; + } + + public void clear() { + // Create new array to reset memory usage to initial capacity + size = 0; + + // If array has grown too large create new one, otherwise just clear it + if (data.length > initialCapacity << 3) { + data = new Object[initialCapacity]; + } else { + for (int i = 0; i < data.length; i++) { + data[i] = null; + } + } + } + + // actually rounds up to nearest power of two + public void trimToSize() { + int old = data.length; + int rounded = Integer.highestOneBit(size - 1) << 1; + if (rounded < old) { + data = Arrays.copyOf(data, rounded); + } + } + + public int size() { + return size; + } + + public boolean isEmpty() { + return size == 0; + } + + public Object clone() throws CloneNotSupportedException { + UnsafeList copy = (UnsafeList) super.clone(); + copy.data = Arrays.copyOf(data, size); + copy.size = size; + copy.initialCapacity = initialCapacity; + copy.iterPool = new Iterator[1]; + copy.iterPool[0] = new Itr(); + copy.maxPool = maxPool; + copy.poolCounter = 0; + return copy; + } + + public Iterator iterator() { + // Try to find an iterator that isn't in use + for (Iterator iter : iterPool) { + if (!((Itr) iter).valid) { + Itr iterator = (Itr) iter; + iterator.reset(); + return iterator; + } + } + + // Couldn't find one, see if we can grow our pool size + if (iterPool.length < maxPool) { + Iterator[] newPool = new Iterator[iterPool.length + 1]; + System.arraycopy(iterPool, 0, newPool, 0, iterPool.length); + iterPool = newPool; + + iterPool[iterPool.length - 1] = new Itr(); + return iterPool[iterPool.length - 1]; + } + + // Still couldn't find a free one, round robin replace one with a new iterator + // This is done in the hope that the new one finishes so can be reused + poolCounter = ++poolCounter % iterPool.length; + iterPool[poolCounter] = new Itr(); + return iterPool[poolCounter]; + } + + private void rangeCheck(int index) { + if (index >= size || index < 0) { + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); + } + } + + private void growIfNeeded() { + if (size == data.length) { + Object[] newData = new Object[data.length << 1]; + System.arraycopy(data, 0, newData, 0, size); + data = newData; + } + } + + private void writeObject(ObjectOutputStream os) throws IOException { + os.defaultWriteObject(); + + os.writeInt(size); + os.writeInt(initialCapacity); + for (int i = 0; i < size; i++) { + os.writeObject(data[i]); + } + os.writeInt(maxPool); + } + + private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { + is.defaultReadObject(); + + size = is.readInt(); + initialCapacity = is.readInt(); + data = new Object[Integer.highestOneBit(size - 1) << 1]; + for (int i = 0; i < size; i++) { + data[i] = is.readObject(); + } + maxPool = is.readInt(); + iterPool = new Iterator[1]; + iterPool[0] = new Itr(); + } + + public class Itr implements Iterator { + int index; + int lastRet = -1; + int expectedModCount = modCount; + public boolean valid = true; + + public void reset() { + index = 0; + lastRet = -1; + expectedModCount = modCount; + valid = true; + } + + public boolean hasNext() { + valid = index != size; + return valid; + } + + public E next() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + int i = index; + if (i >= size) { + throw new NoSuchElementException(); + } + + if (i >= data.length) { + throw new ConcurrentModificationException(); + } + + index = i + 1; + return (E) data[lastRet = i]; + } + + public void remove() { + if (lastRet < 0) { + throw new IllegalStateException(); + } + + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + try { + UnsafeList.this.remove(lastRet); + index = lastRet; + lastRet = -1; + expectedModCount = modCount; + } catch (IndexOutOfBoundsException ex) { + throw new ConcurrentModificationException(); + } + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java new file mode 100644 index 000000000000..674096cab190 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java @@ -0,0 +1,29 @@ +package org.bukkit.craftbukkit.util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.bukkit.Bukkit; + +public final class Versioning { + public static String getBukkitVersion() { + String result = "Unknown-Version"; + + InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/com.destroystokyo.paper/paper-api/pom.properties"); + Properties properties = new Properties(); + + if (stream != null) { + try { + properties.load(stream); + + result = properties.getProperty("version"); + } catch (IOException ex) { + Logger.getLogger(Versioning.class.getName()).log(Level.SEVERE, "Could not get Bukkit version!", ex); + } + } + + return result; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/Waitable.java b/src/main/java/org/bukkit/craftbukkit/util/Waitable.java new file mode 100644 index 000000000000..5cd115434864 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/Waitable.java @@ -0,0 +1,46 @@ +package org.bukkit.craftbukkit.util; + +import java.util.concurrent.ExecutionException; + + +public abstract class Waitable implements Runnable { + private enum Status { + WAITING, + RUNNING, + FINISHED, + } + Throwable t = null; + T value = null; + Status status = Status.WAITING; + + public final void run() { + synchronized (this) { + if (status != Status.WAITING) { + throw new IllegalStateException("Invalid state " + status); + } + status = Status.RUNNING; + } + try { + value = evaluate(); + } catch (Throwable t) { + this.t = t; + } finally { + synchronized (this) { + status = Status.FINISHED; + this.notifyAll(); + } + } + } + + protected abstract T evaluate(); + + public synchronized T get() throws InterruptedException, ExecutionException { + while (status != Status.FINISHED) { + this.wait(); + } + if (t != null) { + throw new ExecutionException(t); + } + return value; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/WeakCollection.java b/src/main/java/org/bukkit/craftbukkit/util/WeakCollection.java new file mode 100644 index 000000000000..7e7363f527d5 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/WeakCollection.java @@ -0,0 +1,169 @@ +package org.bukkit.craftbukkit.util; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import org.apache.commons.lang.Validate; + +public final class WeakCollection implements Collection { + static final Object NO_VALUE = new Object(); + private final Collection> collection; + + public WeakCollection() { + collection = new ArrayList>(); + } + + public boolean add(T value) { + Validate.notNull(value, "Cannot add null value"); + return collection.add(new WeakReference(value)); + } + + public boolean addAll(Collection collection) { + Collection> values = this.collection; + boolean ret = false; + for (T value : collection) { + Validate.notNull(value, "Cannot add null value"); + ret |= values.add(new WeakReference(value)); + } + return ret; + } + + public void clear() { + collection.clear(); + } + + public boolean contains(Object object) { + if (object == null) { + return false; + } + for (T compare : this) { + if (object.equals(compare)) { + return true; + } + } + return false; + } + + public boolean containsAll(Collection collection) { + return toCollection().containsAll(collection); + } + + public boolean isEmpty() { + return !iterator().hasNext(); + } + + public Iterator iterator() { + return new Iterator() { + Iterator> it = collection.iterator(); + Object value = NO_VALUE; + + public boolean hasNext() { + Object value = this.value; + if (value != null && value != NO_VALUE) { + return true; + } + + Iterator> it = this.it; + value = null; + + while (it.hasNext()) { + WeakReference ref = it.next(); + value = ref.get(); + if (value == null) { + it.remove(); + } else { + this.value = value; + return true; + } + } + return false; + } + + public T next() throws NoSuchElementException { + if (!hasNext()) { + throw new NoSuchElementException("No more elements"); + } + + @SuppressWarnings("unchecked") + T value = (T) this.value; + this.value = NO_VALUE; + return value; + } + + public void remove() throws IllegalStateException { + if (value != NO_VALUE) { + throw new IllegalStateException("No last element"); + } + + value = null; + it.remove(); + } + }; + } + + public boolean remove(Object object) { + if (object == null) { + return false; + } + + Iterator it = this.iterator(); + while (it.hasNext()) { + if (object.equals(it.next())) { + it.remove(); + return true; + } + } + return false; + } + + public boolean removeAll(Collection collection) { + Iterator it = this.iterator(); + boolean ret = false; + while (it.hasNext()) { + if (collection.contains(it.next())) { + ret = true; + it.remove(); + } + } + return ret; + } + + public boolean retainAll(Collection collection) { + Iterator it = this.iterator(); + boolean ret = false; + while (it.hasNext()) { + if (!collection.contains(it.next())) { + ret = true; + it.remove(); + } + } + return ret; + } + + public int size() { + int s = 0; + for (T value : this) { + s++; + } + return s; + } + + public Object[] toArray() { + return this.toArray(new Object[0]); + } + + public T[] toArray(T[] array) { + return toCollection().toArray(array); + } + + private Collection toCollection() { + ArrayList collection = new ArrayList(); + for (T value : this) { + collection.add(value); + } + return collection; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java b/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java new file mode 100644 index 000000000000..3a10eb639b09 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/permissions/CommandPermissions.java @@ -0,0 +1,38 @@ +package org.bukkit.craftbukkit.util.permissions; + +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.bukkit.util.permissions.DefaultPermissions; + +public final class CommandPermissions { + private static final String ROOT = "minecraft.command"; + private static final String PREFIX = ROOT + "."; + + private CommandPermissions() {} + + public static Permission registerPermissions(Permission parent) { + Permission commands = DefaultPermissions.registerPermission(ROOT, "Gives the user the ability to use all vanilla minecraft commands", parent); + + DefaultPermissions.registerPermission(PREFIX + "kill", "Allows the user to commit suicide", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "me", "Allows the user to perform a chat action", PermissionDefault.TRUE, commands); + DefaultPermissions.registerPermission(PREFIX + "msg", "Allows the user to privately message another player", PermissionDefault.TRUE, commands); + DefaultPermissions.registerPermission(PREFIX + "help", "Allows the user to access Vanilla command help", PermissionDefault.TRUE, commands); + DefaultPermissions.registerPermission(PREFIX + "say", "Allows the user to talk as the console", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "give", "Allows the user to give items to players", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "teleport", "Allows the user to teleport players", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "kick", "Allows the user to kick players", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "stop", "Allows the user to stop the server", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "list", "Allows the user to list all online players", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "gamemode", "Allows the user to change the gamemode of another player", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "xp", "Allows the user to give themselves or others arbitrary values of experience", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "toggledownfall", "Allows the user to toggle rain on/off for a given world", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "defaultgamemode", "Allows the user to change the default gamemode of the server", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "seed", "Allows the user to view the seed of the world", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "effect", "Allows the user to add/remove effects on players", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "selector", "Allows the use of selectors", PermissionDefault.OP, commands); + DefaultPermissions.registerPermission(PREFIX + "trigger", "Allows the use of the trigger command", PermissionDefault.TRUE, commands); + + commands.recalculatePermissibles(); + return commands; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java b/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java new file mode 100644 index 000000000000..69fe8421354f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.util.permissions; + +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.bukkit.util.permissions.DefaultPermissions; + +public final class CraftDefaultPermissions { + private static final String ROOT= "minecraft"; + + private CraftDefaultPermissions() {} + + public static void registerCorePermissions() { + Permission parent = DefaultPermissions.registerPermission(ROOT, "Gives the user the ability to use all vanilla utilities and commands"); + CommandPermissions.registerPermissions(parent); + // Spigot start + DefaultPermissions.registerPermission(ROOT + ".nbt.place", "Gives the user the ability to place restricted blocks with NBT in creative", PermissionDefault.OP, parent); + DefaultPermissions.registerPermission(ROOT + ".nbt.copy", "Gives the user the ability to copy NBT in creative", PermissionDefault.TRUE, parent); + DefaultPermissions.registerPermission(ROOT + ".debugstick", "Gives the user the ability to use the debug stick creative", PermissionDefault.OP, parent); + // Spigot end + parent.recalculatePermissibles(); + } +} diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java new file mode 100644 index 000000000000..081789a8fed1 --- /dev/null +++ b/src/main/java/org/spigotmc/ActivationRange.java @@ -0,0 +1,333 @@ +package org.spigotmc; + +import java.util.List; +import java.util.Set; + +import co.aikar.timings.MinecraftTimings; +import net.minecraft.server.AxisAlignedBB; +import net.minecraft.server.Chunk; +import net.minecraft.server.Entity; +import net.minecraft.server.EntityAmbient; +import net.minecraft.server.EntityAnimal; +import net.minecraft.server.EntityArrow; +import net.minecraft.server.EntityComplexPart; +import net.minecraft.server.EntityCreature; +import net.minecraft.server.EntityCreeper; +import net.minecraft.server.EntityEnderCrystal; +import net.minecraft.server.EntityEnderDragon; +import net.minecraft.server.EntityFallingBlock; +import net.minecraft.server.EntityFireball; +import net.minecraft.server.EntityFireworks; +import net.minecraft.server.EntityFish; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.EntityInsentient; +import net.minecraft.server.EntityLiving; +import net.minecraft.server.EntityLlama; +import net.minecraft.server.EntityMonster; +import net.minecraft.server.EntityProjectile; +import net.minecraft.server.EntitySheep; +import net.minecraft.server.EntitySlime; +import net.minecraft.server.EntityTNTPrimed; +import net.minecraft.server.EntityThrownTrident; +import net.minecraft.server.EntityVillager; +import net.minecraft.server.EntityWaterAnimal; +import net.minecraft.server.EntityWeather; +import net.minecraft.server.EntityWither; +import net.minecraft.server.MCUtil; +import net.minecraft.server.MathHelper; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.NavigationGuardian; +import net.minecraft.server.World; + +public class ActivationRange +{ + + static AxisAlignedBB maxBB = new AxisAlignedBB( 0, 0, 0, 0, 0, 0 ); + static AxisAlignedBB miscBB = new AxisAlignedBB( 0, 0, 0, 0, 0, 0 ); + static AxisAlignedBB animalBB = new AxisAlignedBB( 0, 0, 0, 0, 0, 0 ); + static AxisAlignedBB waterBB = new AxisAlignedBB( 0, 0, 0, 0, 0, 0 ); // Paper + static AxisAlignedBB monsterBB = new AxisAlignedBB( 0, 0, 0, 0, 0, 0 ); + + /** + * Initializes an entities type on construction to specify what group this + * entity is in for activation ranges. + * + * @param entity + * @return group id + */ + public static byte initializeEntityActivationType(Entity entity) + { + if (entity instanceof EntityWaterAnimal) { return 4; } // Paper + if ( entity instanceof EntityMonster || entity instanceof EntitySlime ) + { + return 1; // Monster + } else if ( entity instanceof EntityCreature || entity instanceof EntityAmbient ) + { + return 2; // Animal + } else + { + return 3; // Misc + } + } + + /** + * These entities are excluded from Activation range checks. + * + * @param entity Entity to initialize + * @param config Spigot config to determine ranges + * @return boolean If it should always tick. + */ + public static boolean initializeEntityActivationState(Entity entity, SpigotWorldConfig config) + { + if ( ( entity.activationType == 3 && config.miscActivationRange == 0 ) + || ( entity.activationType == 2 && config.animalActivationRange == 0 ) + || ( entity.activationType == 1 && config.monsterActivationRange == 0 ) + || ( entity.activationType == 4 && config.waterActivationRange == 0 ) // Paper + || entity instanceof EntityHuman + || entity instanceof EntityProjectile + || entity instanceof EntityEnderDragon + || entity instanceof EntityComplexPart + || entity instanceof EntityWither + || entity instanceof EntityFireball + || entity instanceof EntityWeather + || entity instanceof EntityTNTPrimed + || entity instanceof EntityFallingBlock // Paper - Always tick falling blocks + || entity instanceof EntityEnderCrystal + || entity instanceof EntityFireworks + || entity instanceof EntityThrownTrident ) + { + return true; + } + + return false; + } + + /** + * Find what entities are in range of the players in the world and set + * active if in range. + * + * @param world + */ + public static void activateEntities(World world) + { + MinecraftTimings.entityActivationCheckTimer.startTiming(); + final int miscActivationRange = world.spigotConfig.miscActivationRange; + final int animalActivationRange = world.spigotConfig.animalActivationRange; + final int monsterActivationRange = world.spigotConfig.monsterActivationRange; + final int waterActivationRange = world.spigotConfig.waterActivationRange; // Paper + + int maxRange = Math.max( monsterActivationRange, animalActivationRange ); + maxRange = Math.max( maxRange, miscActivationRange ); + //maxRange = Math.min( ( world.spigotConfig.viewDistance << 4 ) - 8, maxRange ); Paper - Use player view distance API below instead + + Chunk chunk; // Paper + for ( EntityHuman player : world.players ) + { + int playerMaxRange = maxRange = Math.min( ( player.getViewDistance() << 4 ) - 8, maxRange ); // Paper - Use player view distance API + player.activatedTick = MinecraftServer.currentTick; + maxBB = player.getBoundingBox().grow( playerMaxRange, 256, playerMaxRange ); // Paper - Use player view distance API + miscBB = player.getBoundingBox().grow( miscActivationRange, 256, miscActivationRange ); + animalBB = player.getBoundingBox().grow( animalActivationRange, 256, animalActivationRange ); + waterBB = player.getBoundingBox().grow( waterActivationRange, 256, waterActivationRange ); // Paper + monsterBB = player.getBoundingBox().grow( monsterActivationRange, 256, monsterActivationRange ); + + int i = MathHelper.floor( maxBB.minX / 16.0D ); + int j = MathHelper.floor( maxBB.maxX / 16.0D ); + int k = MathHelper.floor( maxBB.minZ / 16.0D ); + int l = MathHelper.floor( maxBB.maxZ / 16.0D ); + + for ( int i1 = i; i1 <= j; ++i1 ) + { + for ( int j1 = k; j1 <= l; ++j1 ) + { + if ( (chunk = world.getChunkIfLoaded(i1, j1 )) != null ) // Paper + { + activateChunkEntities( chunk ); // Paper + } + } + } + } + MinecraftTimings.entityActivationCheckTimer.stopTiming(); + } + + /** + * Checks for the activation state of all entities in this chunk. + * + * @param chunk + */ + private static void activateChunkEntities(Chunk chunk) + { + for ( List slice : chunk.entitySlices ) + { + for ( Entity entity : slice ) + { + if ( MinecraftServer.currentTick > entity.activatedTick ) + { + if ( entity.defaultActivationState ) + { + entity.activatedTick = MinecraftServer.currentTick; + continue; + } + switch ( entity.activationType ) + { + case 1: + if ( monsterBB.c( entity.getBoundingBox() ) ) + { + entity.activatedTick = MinecraftServer.currentTick; + } + break; + case 2: + if ( animalBB.c( entity.getBoundingBox() ) ) + { + entity.activatedTick = MinecraftServer.currentTick; + } + break; + // Paper start + case 4: + if ( waterBB.c( entity.getBoundingBox() ) ) + { + entity.activatedTick = MinecraftServer.currentTick; + } + break; + // Paper end + case 3: + default: + if ( miscBB.c( entity.getBoundingBox() ) ) + { + entity.activatedTick = MinecraftServer.currentTick; + } + } + } + } + } + } + + /** + * If an entity is not in range, do some more checks to see if we should + * give it a shot. + * + * @param entity + * @return + */ + public static boolean checkEntityImmunities(Entity entity) + { + // Paper start - optimize Water cases + if (entity instanceof EntityFish) { + return false; + } + if (entity.inWater && (!(entity instanceof EntityInsentient) || !(((EntityInsentient) entity).getNavigation() instanceof NavigationGuardian))) { + return true; + } + if (entity.fireTicks > 0) { + return true; + } + // Paper end + if ( !( entity instanceof EntityArrow ) ) + { + if ( !entity.onGround || !entity.passengers.isEmpty() || entity.isPassenger() ) + { + return true; + } + } else if ( !( (EntityArrow) entity ).inGround ) + { + return true; + } + // special cases. + if ( entity instanceof EntityLiving ) + { + EntityLiving living = (EntityLiving) entity; + if ( living.lastDamageByPlayerTime > 0 || living.hurtTicks > 0 || living.effects.size() > 0 ) // Paper + { + return true; + } + if ( entity instanceof EntityCreature ) + { + // Paper start + EntityCreature creature = (EntityCreature) entity; + if (creature.getGoalTarget() != null || creature.getMovingTarget() != null) { + return true; + } + // Paper end + } + if ( entity instanceof EntityVillager && ( (EntityVillager) entity ).isInLove() ) + { + return true; + } + // Paper start + if ( entity instanceof EntityLlama && ( (EntityLlama ) entity ).inCaravan() ) + { + return true; + } + // Paper end + if ( entity instanceof EntityAnimal ) + { + EntityAnimal animal = (EntityAnimal) entity; + if ( animal.isBaby() || animal.isInLove() ) + { + return true; + } + if ( entity instanceof EntitySheep && ( (EntitySheep) entity ).isSheared() ) + { + return true; + } + } + if (entity instanceof EntityCreeper && ((EntityCreeper) entity).isIgnited()) { // isExplosive + return true; + } + } + return false; + } + + /** + * Checks if the entity is active for this tick. + * + * @param entity + * @return + */ + public static boolean checkIfActive(Entity entity) + { + // Never safe to skip fireworks or entities not yet added to chunk + if ( !entity.inChunk || entity instanceof EntityFireworks ) { + return true; + } + + boolean isActive = entity.activatedTick >= MinecraftServer.currentTick || entity.defaultActivationState; + + // Should this entity tick? + if ( !isActive ) + { + if ( ( MinecraftServer.currentTick - entity.activatedTick - 1 ) % 20 == 0 ) + { + // Check immunities every 20 ticks. + if ( checkEntityImmunities( entity ) ) + { + // Triggered some sort of immunity, give 20 full ticks before we check again. + entity.activatedTick = MinecraftServer.currentTick + 20; + } + isActive = true; + // Paper start + } else if (entity instanceof EntityInsentient && ((EntityInsentient) entity).targetSelector.hasTasks()) { + isActive = true; + } + // Paper end + // Add a little performance juice to active entities. Skip 1/4 if not immune. + } else if ( !entity.defaultActivationState && entity.ticksLived % 4 == 0 && !(entity instanceof EntityInsentient && ((EntityInsentient) entity).targetSelector.hasTasks()) && !checkEntityImmunities( entity ) ) // Paper - add targetSelector.hasTasks + { + isActive = false; + } + //int x = MathHelper.floor( entity.locX ); // Paper + //int z = MathHelper.floor( entity.locZ ); // Paper + // Make sure not on edge of unloaded chunk + Chunk chunk = entity.getChunkAtLocation(); // Paper + if ( isActive && !( chunk != null && chunk.areNeighborsLoaded( 1 ) ) ) + { + isActive = false; + } + // Paper start - Skip ticking in chunks scheduled for unload + else if (entity.world.paperConfig.skipEntityTickingInChunksScheduledForUnload && (chunk == null || chunk.scheduledForUnload != null)) { + isActive = false; + } + // Paper end + return isActive; + } +} diff --git a/src/main/java/org/spigotmc/AsyncCatcher.java b/src/main/java/org/spigotmc/AsyncCatcher.java new file mode 100644 index 000000000000..e44c2301654b --- /dev/null +++ b/src/main/java/org/spigotmc/AsyncCatcher.java @@ -0,0 +1,18 @@ +package org.spigotmc; + +import net.minecraft.server.MinecraftServer; + +public class AsyncCatcher +{ + + public static boolean enabled = true; + public static boolean shuttingDown = false; // Paper + + public static void catchOp(String reason) + { + if ( enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread ) + { + throw new IllegalStateException( "Asynchronous " + reason + "!" ); + } + } +} diff --git a/src/main/java/org/spigotmc/LimitStream.java b/src/main/java/org/spigotmc/LimitStream.java new file mode 100644 index 000000000000..8c32e8b62d47 --- /dev/null +++ b/src/main/java/org/spigotmc/LimitStream.java @@ -0,0 +1,39 @@ +package org.spigotmc; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import net.minecraft.server.NBTReadLimiter; + +public class LimitStream extends FilterInputStream +{ + + private final NBTReadLimiter limit; + + public LimitStream(InputStream is, NBTReadLimiter limit) + { + super( is ); + this.limit = limit; + } + + @Override + public int read() throws IOException + { + limit.a( 8 ); + return super.read(); + } + + @Override + public int read(byte[] b) throws IOException + { + limit.a( b.length * 8 ); + return super.read( b ); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException + { + limit.a( len * 8 ); + return super.read( b, off, len ); + } +} diff --git a/src/main/java/org/spigotmc/Metrics.java b/src/main/java/org/spigotmc/Metrics.java new file mode 100644 index 000000000000..e7d5e8ab6aa2 --- /dev/null +++ b/src/main/java/org/spigotmc/Metrics.java @@ -0,0 +1,645 @@ +/* + * Copyright 2011-2013 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ +package org.spigotmc; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.scheduler.BukkitTask; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import net.minecraft.server.MinecraftServer; + +/** + *

    The metrics class obtains data about a plugin and submits statistics about it to the metrics backend.

    + * Public methods provided by this class:

    + * + * Graph createGraph(String name);
    + * void addCustomData(BukkitMetrics.Plotter plotter);
    + * void start();
    + *
    + */ +public class Metrics { + + /** + * The current revision number + */ + private final static int REVISION = 6; + /** + * The base url of the metrics domain + */ + private static final String BASE_URL = "https://mcstats.spigotmc.org"; + /** + * The url used to report a server's status + */ + private static final String REPORT_URL = "/report/%s"; + /** + * The separator to use for custom data. This MUST NOT change unless you are hosting your own version of metrics and + * want to change it. + */ + private static final String CUSTOM_DATA_SEPARATOR = "~~"; + /** + * Interval of time to ping (in minutes) + */ + private static final int PING_INTERVAL = 10; + /** + * All of the custom graphs to submit to metrics + */ + private final Set graphs = Collections.synchronizedSet(new HashSet()); + /** + * The default graph, used for addCustomData when you don't want a specific graph + */ + private final Graph defaultGraph = new Graph("Default"); + /** + * The plugin configuration file + */ + private final YamlConfiguration configuration; + /** + * The plugin configuration file + */ + private final File configurationFile; + /** + * Unique server id + */ + private final String guid; + /** + * Debug mode + */ + private final boolean debug; + /** + * Lock for synchronization + */ + private final Object optOutLock = new Object(); + /** + * The scheduled task + */ + private volatile Timer task = null; + + public Metrics() throws IOException { + // load the config + configurationFile = getConfigFile(); + configuration = YamlConfiguration.loadConfiguration(configurationFile); + + // add some defaults + configuration.addDefault("opt-out", false); + configuration.addDefault("guid", UUID.randomUUID().toString()); + configuration.addDefault("debug", false); + + // Do we need to create the file? + if (configuration.get("guid", null) == null) { + configuration.options().header("http://mcstats.org").copyDefaults(true); + configuration.save(configurationFile); + } + + // Load the guid then + guid = configuration.getString("guid"); + debug = configuration.getBoolean("debug", false); + } + + /** + * Construct and create a Graph that can be used to separate specific plotters to their own graphs on the metrics + * website. Plotters can be added to the graph object returned. + * + * @param name The name of the graph + * @return Graph object created. Will never return NULL under normal circumstances unless bad parameters are given + */ + public Graph createGraph(final String name) { + if (name == null) { + throw new IllegalArgumentException("Graph name cannot be null"); + } + + // Construct the graph object + final Graph graph = new Graph(name); + + // Now we can add our graph + graphs.add(graph); + + // and return back + return graph; + } + + /** + * Add a Graph object to BukkitMetrics that represents data for the plugin that should be sent to the backend + * + * @param graph The name of the graph + */ + public void addGraph(final Graph graph) { + if (graph == null) { + throw new IllegalArgumentException("Graph cannot be null"); + } + + graphs.add(graph); + } + + /** + * Adds a custom data plotter to the default graph + * + * @param plotter The plotter to use to plot custom data + */ + public void addCustomData(final Plotter plotter) { + if (plotter == null) { + throw new IllegalArgumentException("Plotter cannot be null"); + } + + // Add the plotter to the graph o/ + defaultGraph.addPlotter(plotter); + + // Ensure the default graph is included in the submitted graphs + graphs.add(defaultGraph); + } + + /** + * Start measuring statistics. This will immediately create an async repeating task as the plugin and send the + * initial data to the metrics backend, and then after that it will post in increments of PING_INTERVAL * 1200 + * ticks. + * + * @return True if statistics measuring is running, otherwise false. + */ + public boolean start() { + synchronized (optOutLock) { + // Did we opt out? + if (isOptOut()) { + return false; + } + + // Is metrics already running? + if (task != null) { + return true; + } + + // Begin hitting the server with glorious data + task = new Timer("Spigot Metrics Thread", true); + + task.scheduleAtFixedRate(new TimerTask() { + private boolean firstPost = true; + + public void run() { + try { + // This has to be synchronized or it can collide with the disable method. + synchronized (optOutLock) { + // Disable Task, if it is running and the server owner decided to opt-out + if (isOptOut() && task != null) { + task.cancel(); + task = null; + // Tell all plotters to stop gathering information. + for (Graph graph : graphs) { + graph.onOptOut(); + } + } + } + + // We use the inverse of firstPost because if it is the first time we are posting, + // it is not a interval ping, so it evaluates to FALSE + // Each time thereafter it will evaluate to TRUE, i.e PING! + postPlugin(!firstPost); + + // After the first post we set firstPost to false + // Each post thereafter will be a ping + firstPost = false; + } catch (IOException e) { + if (debug) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage()); + } + } + } + }, 0, TimeUnit.MINUTES.toMillis(PING_INTERVAL)); + + return true; + } + } + + /** + * Has the server owner denied plugin metrics? + * + * @return true if metrics should be opted out of it + */ + public boolean isOptOut() { + synchronized (optOutLock) { + try { + // Reload the metrics file + configuration.load(getConfigFile()); + } catch (IOException ex) { + if (debug) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + } + return true; + } catch (InvalidConfigurationException ex) { + if (debug) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + } + return true; + } + return configuration.getBoolean("opt-out", false); + } + } + + /** + * Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task. + * + * @throws java.io.IOException + */ + public void enable() throws IOException { + // This has to be synchronized or it can collide with the check in the task. + synchronized (optOutLock) { + // Check if the server owner has already set opt-out, if not, set it. + if (isOptOut()) { + configuration.set("opt-out", false); + configuration.save(configurationFile); + } + + // Enable Task, if it is not running + if (task == null) { + start(); + } + } + } + + /** + * Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task. + * + * @throws java.io.IOException + */ + public void disable() throws IOException { + // This has to be synchronized or it can collide with the check in the task. + synchronized (optOutLock) { + // Check if the server owner has already set opt-out, if not, set it. + if (!isOptOut()) { + configuration.set("opt-out", true); + configuration.save(configurationFile); + } + + // Disable Task, if it is running + if (task != null) { + task.cancel(); + task = null; + } + } + } + + /** + * Gets the File object of the config file that should be used to store data such as the GUID and opt-out status + * + * @return the File object for the config file + */ + public File getConfigFile() { + // I believe the easiest way to get the base folder (e.g craftbukkit set via -P) for plugins to use + // is to abuse the plugin object we already have + // plugin.getDataFolder() => base/plugins/PluginA/ + // pluginsFolder => base/plugins/ + // The base is not necessarily relative to the startup directory. + // File pluginsFolder = plugin.getDataFolder().getParentFile(); + + // return => base/plugins/PluginMetrics/config.yml + return new File(new File((File) MinecraftServer.getServer().options.valueOf("plugins"), "PluginMetrics"), "config.yml"); + } + + /** + * Generic method that posts a plugin to the metrics website + */ + private void postPlugin(final boolean isPing) throws IOException { + // Server software specific section + String pluginName = "Spigot"; + boolean onlineMode = Bukkit.getServer().getOnlineMode(); // TRUE if online mode is enabled + String pluginVersion = (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown"; + String serverVersion = Bukkit.getVersion(); + int playersOnline = Bukkit.getServer().getOnlinePlayers().size(); + + // END server software specific section -- all code below does not use any code outside of this class / Java + + // Construct the post data + final StringBuilder data = new StringBuilder(); + + // The plugin's description file containg all of the plugin data such as name, version, author, etc + data.append(encode("guid")).append('=').append(encode(guid)); + encodeDataPair(data, "version", pluginVersion); + encodeDataPair(data, "server", serverVersion); + encodeDataPair(data, "players", Integer.toString(playersOnline)); + encodeDataPair(data, "revision", String.valueOf(REVISION)); + + // New data as of R6 + String osname = System.getProperty("os.name"); + String osarch = System.getProperty("os.arch"); + String osversion = System.getProperty("os.version"); + String java_version = System.getProperty("java.version"); + int coreCount = Runtime.getRuntime().availableProcessors(); + + // normalize os arch .. amd64 -> x86_64 + if (osarch.equals("amd64")) { + osarch = "x86_64"; + } + + encodeDataPair(data, "osname", osname); + encodeDataPair(data, "osarch", osarch); + encodeDataPair(data, "osversion", osversion); + encodeDataPair(data, "cores", Integer.toString(coreCount)); + encodeDataPair(data, "online-mode", Boolean.toString(onlineMode)); + encodeDataPair(data, "java_version", java_version); + + // If we're pinging, append it + if (isPing) { + encodeDataPair(data, "ping", "true"); + } + + // Acquire a lock on the graphs, which lets us make the assumption we also lock everything + // inside of the graph (e.g plotters) + synchronized (graphs) { + final Iterator iter = graphs.iterator(); + + while (iter.hasNext()) { + final Graph graph = iter.next(); + + for (Plotter plotter : graph.getPlotters()) { + // The key name to send to the metrics server + // The format is C-GRAPHNAME-PLOTTERNAME where separator - is defined at the top + // Legacy (R4) submitters use the format Custom%s, or CustomPLOTTERNAME + final String key = String.format("C%s%s%s%s", CUSTOM_DATA_SEPARATOR, graph.getName(), CUSTOM_DATA_SEPARATOR, plotter.getColumnName()); + + // The value to send, which for the foreseeable future is just the string + // value of plotter.getValue() + final String value = Integer.toString(plotter.getValue()); + + // Add it to the http post data :) + encodeDataPair(data, key, value); + } + } + } + + // Create the url + URL url = new URL(BASE_URL + String.format(REPORT_URL, encode(pluginName))); + + // Connect to the website + URLConnection connection; + + // Mineshafter creates a socks proxy, so we can safely bypass it + // It does not reroute POST requests so we need to go around it + if (isMineshafterPresent()) { + connection = url.openConnection(Proxy.NO_PROXY); + } else { + connection = url.openConnection(); + } + + connection.setDoOutput(true); + + // Write the data + final OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream()); + writer.write(data.toString()); + writer.flush(); + + // Now read the response + final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + final String response = reader.readLine(); + + // close resources + writer.close(); + reader.close(); + + if (response == null || response.startsWith("ERR")) { + throw new IOException(response); //Throw the exception + } else { + // Is this the first update this hour? + if (response.contains("OK This is your first update this hour")) { + synchronized (graphs) { + final Iterator iter = graphs.iterator(); + + while (iter.hasNext()) { + final Graph graph = iter.next(); + + for (Plotter plotter : graph.getPlotters()) { + plotter.reset(); + } + } + } + } + } + } + + /** + * Check if mineshafter is present. If it is, we need to bypass it to send POST requests + * + * @return true if mineshafter is installed on the server + */ + private boolean isMineshafterPresent() { + try { + Class.forName("mineshafter.MineServer"); + return true; + } catch (Exception e) { + return false; + } + } + + /** + *

    Encode a key/value data pair to be used in a HTTP post request. This INCLUDES a & so the first key/value pair + * MUST be included manually, e.g:

    + * + * StringBuffer data = new StringBuffer(); + * data.append(encode("guid")).append('=').append(encode(guid)); + * encodeDataPair(data, "version", description.getVersion()); + * + * + * @param buffer the stringbuilder to append the data pair onto + * @param key the key value + * @param value the value + */ + private static void encodeDataPair(final StringBuilder buffer, final String key, final String value) throws UnsupportedEncodingException { + buffer.append('&').append(encode(key)).append('=').append(encode(value)); + } + + /** + * Encode text as UTF-8 + * + * @param text the text to encode + * @return the encoded text, as UTF-8 + */ + private static String encode(final String text) throws UnsupportedEncodingException { + return URLEncoder.encode(text, "UTF-8"); + } + + /** + * Represents a custom graph on the website + */ + public static class Graph { + + /** + * The graph's name, alphanumeric and spaces only :) If it does not comply to the above when submitted, it is + * rejected + */ + private final String name; + /** + * The set of plotters that are contained within this graph + */ + private final Set plotters = new LinkedHashSet(); + + private Graph(final String name) { + this.name = name; + } + + /** + * Gets the graph's name + * + * @return the Graph's name + */ + public String getName() { + return name; + } + + /** + * Add a plotter to the graph, which will be used to plot entries + * + * @param plotter the plotter to add to the graph + */ + public void addPlotter(final Plotter plotter) { + plotters.add(plotter); + } + + /** + * Remove a plotter from the graph + * + * @param plotter the plotter to remove from the graph + */ + public void removePlotter(final Plotter plotter) { + plotters.remove(plotter); + } + + /** + * Gets an unmodifiable set of the plotter objects in the graph + * + * @return an unmodifiable {@link java.util.Set} of the plotter objects + */ + public Set getPlotters() { + return Collections.unmodifiableSet(plotters); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(final Object object) { + if (!(object instanceof Graph)) { + return false; + } + + final Graph graph = (Graph) object; + return graph.name.equals(name); + } + + /** + * Called when the server owner decides to opt-out of BukkitMetrics while the server is running. + */ + protected void onOptOut() { + } + } + + /** + * Interface used to collect custom data for a plugin + */ + public static abstract class Plotter { + + /** + * The plot's name + */ + private final String name; + + /** + * Construct a plotter with the default plot name + */ + public Plotter() { + this("Default"); + } + + /** + * Construct a plotter with a specific plot name + * + * @param name the name of the plotter to use, which will show up on the website + */ + public Plotter(final String name) { + this.name = name; + } + + /** + * Get the current value for the plotted point. Since this function defers to an external function it may or may + * not return immediately thus cannot be guaranteed to be thread friendly or safe. This function can be called + * from any thread so care should be taken when accessing resources that need to be synchronized. + * + * @return the current value for the point to be plotted. + */ + public abstract int getValue(); + + /** + * Get the column name for the plotted point + * + * @return the plotted point's column name + */ + public String getColumnName() { + return name; + } + + /** + * Called after the website graphs have been updated + */ + public void reset() { + } + + @Override + public int hashCode() { + return getColumnName().hashCode(); + } + + @Override + public boolean equals(final Object object) { + if (!(object instanceof Plotter)) { + return false; + } + + final Plotter plotter = (Plotter) object; + return plotter.name.equals(name) && plotter.getValue() == getValue(); + } + } +} diff --git a/src/main/java/org/spigotmc/RestartCommand.java b/src/main/java/org/spigotmc/RestartCommand.java new file mode 100644 index 000000000000..061cbe7fcf05 --- /dev/null +++ b/src/main/java/org/spigotmc/RestartCommand.java @@ -0,0 +1,168 @@ +package org.spigotmc; + +import java.io.File; +import java.util.List; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.MinecraftServer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +public class RestartCommand extends Command +{ + + public RestartCommand(String name) + { + super( name ); + this.description = "Restarts the server"; + this.usageMessage = "/restart"; + this.setPermission( "bukkit.command.restart" ); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) + { + if ( testPermission( sender ) ) + { + MinecraftServer.getServer().processQueue.add( new Runnable() + { + @Override + public void run() + { + restart(); + } + } ); + } + return true; + } + + public static void restart() + { + restart( SpigotConfig.restartScript ); + } + + private static void restart(final String restartScript) + { + AsyncCatcher.enabled = false; // Disable async catcher incase it interferes with us + org.spigotmc.AsyncCatcher.shuttingDown = true; // Paper + try + { + // Paper - extract method and cleanup + boolean isRestarting = addShutdownHook(restartScript); + if (isRestarting) { + System.out.println("Attempting to restart with " + SpigotConfig.restartScript); + } else { + System.out.println( "Startup script '" + SpigotConfig.restartScript + "' does not exist! Stopping server." ); + } + + // Stop the watchdog + WatchdogThread.doStop(); + + shutdownServer(isRestarting); + } catch ( Exception ex ) + { + ex.printStackTrace(); + } + } + + // Paper start - sync copied from above with minor changes, async added + private static void shutdownServer(boolean isRestarting) + { + if (MinecraftServer.getServer().isMainThread()) + { + // Kick all players + for ( EntityPlayer p : com.google.common.collect.ImmutableList.copyOf( MinecraftServer.getServer().getPlayerList().players ) ) + { + p.playerConnection.disconnect(SpigotConfig.restartMessage); + } + // Give the socket a chance to send the packets + try + { + Thread.sleep( 100 ); + } catch ( InterruptedException ex ) + { + } + + closeSocket(); + + // Actually shutdown + try + { + MinecraftServer.getServer().stop(); + } catch ( Throwable t ) + { + } + + // Actually stop the JVM + System.exit(0); + + } else + { + // Mark the server to shutdown at the end of the tick + MinecraftServer.getServer().safeShutdown(isRestarting); + + + + // wait 10 seconds to see if we're actually going to try shutdown + try + { + Thread.sleep(10000); + } + catch (InterruptedException ignored) + { + } + + // Check if we've actually hit a state where the server is going to safely shutdown + // if we have, let the server stop as usual + if (MinecraftServer.getServer().isStopped()) return; + + // If the server hasn't stopped by now, assume worse case and kill + closeSocket(); + System.exit( 0 ); + } + } + + // Paper - Split from moved code + private static void closeSocket() { + // Close the socket so we can rebind with the new process + MinecraftServer.getServer().getServerConnection().b(); + + // Give time for it to kick in + try + { + Thread.sleep( 100 ); + } catch ( InterruptedException ex ) + { + } + } + // Paper end + + // Paper - copied from above and modified to return if the hook registered + private static boolean addShutdownHook(final String restartScript) { + + String[] split = restartScript.split( " " ); + if ( split.length > 0 && new File( split[0] ).isFile() ) + { + Thread shutdownHook = new Thread() { + @Override + public void run() { + try { + String os = System.getProperty("os.name").toLowerCase(java.util.Locale.ENGLISH); + if (os.contains("win")) { + Runtime.getRuntime().exec("cmd /c start " + restartScript); + } else { + Runtime.getRuntime().exec( "sh " + restartScript ); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }; + + shutdownHook.setDaemon(true); + Runtime.getRuntime().addShutdownHook(shutdownHook); + return true; + } else { + return false; + } + } +} diff --git a/src/main/java/org/spigotmc/SlackActivityAccountant.java b/src/main/java/org/spigotmc/SlackActivityAccountant.java new file mode 100644 index 000000000000..aabc7ad20c1f --- /dev/null +++ b/src/main/java/org/spigotmc/SlackActivityAccountant.java @@ -0,0 +1,78 @@ +package org.spigotmc; + +/** + * Keeps track of the time spent doing main thread activities that can be spread across ticks, + * so that such work doesn't exceed the current tick's estimated available slack time. Each + * activity is allotted a proportion of the expected slack time according to its weight, versus the + * estimated total weight of all activities. + */ +public class SlackActivityAccountant { + private double prevTickSlackWeightReciprocal = 1 / MIN_SLACK_WEIGHT; + private static final double MIN_SLACK_WEIGHT = 1 / 65536.0; + private double averageTickNonSlackNanos = 0; + private static final double AVERAGING_FACTOR = 0.375; + + private long currentActivityStartNanos; + private static final long OFF = -1; + private long currentActivityEndNanos; + private double tickSlackWeight; + private long tickSlackNanos; + + private double getSlackFraction(double slackWeight) { + return Math.min(slackWeight * this.prevTickSlackWeightReciprocal, 1); + } + + private int getEstimatedSlackNanos() { + return (int) Math.max(net.minecraft.server.MinecraftServer.TICK_TIME - (long) this.averageTickNonSlackNanos, 0); + } + + public void tickStarted() { + this.currentActivityStartNanos = OFF; + this.tickSlackWeight = 0; + this.tickSlackNanos = 0; + } + + public void startActivity(double slackWeight) { + double slackFraction0 = getSlackFraction(this.tickSlackWeight); + this.tickSlackWeight += slackWeight; + double slackFraction1 = getSlackFraction(this.tickSlackWeight); + + long t = System.nanoTime(); + this.currentActivityStartNanos = t; + this.currentActivityEndNanos = t + ((int) ((slackFraction1 - slackFraction0) * this.getEstimatedSlackNanos())); + } + + private void endActivity(long endNanos) { + this.tickSlackNanos += endNanos - this.currentActivityStartNanos; + this.currentActivityStartNanos = OFF; + } + + public boolean activityTimeIsExhausted() { + if (this.currentActivityStartNanos == OFF) { + return true; + } + + long t = System.nanoTime(); + if (t <= this.currentActivityEndNanos) { + return false; + } else { + this.endActivity(this.currentActivityEndNanos); + return true; + } + } + + public void endActivity() { + if (this.currentActivityStartNanos == OFF) { + return; + } + + this.endActivity(Math.min(System.nanoTime(), this.currentActivityEndNanos)); + } + + public void tickEnded(long tickNanos) { + this.prevTickSlackWeightReciprocal = 1 / Math.max(this.tickSlackWeight, MIN_SLACK_WEIGHT); + + long tickNonSlackNanos = tickNanos - this.tickSlackNanos; + this.averageTickNonSlackNanos = this.averageTickNonSlackNanos * (1 - AVERAGING_FACTOR) + tickNonSlackNanos * AVERAGING_FACTOR; + } +} diff --git a/src/main/java/org/spigotmc/SpigotCommand.java b/src/main/java/org/spigotmc/SpigotCommand.java new file mode 100644 index 000000000000..d0be3c3b593a --- /dev/null +++ b/src/main/java/org/spigotmc/SpigotCommand.java @@ -0,0 +1,44 @@ +package org.spigotmc; + +import java.io.File; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.WorldServer; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +public class SpigotCommand extends Command { + + public SpigotCommand(String name) { + super(name); + this.description = "Spigot related commands"; + this.usageMessage = "/spigot reload"; + this.setPermission("bukkit.command.spigot"); + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + if (!testPermission(sender)) return true; + + if (args.length != 1) { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + if (args[0].equals("reload")) { + Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues."); + Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server."); + + MinecraftServer console = MinecraftServer.getServer(); + org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); + for (WorldServer world : console.getWorlds()) { + world.spigotConfig.init(); + } + console.server.reloadCount++; + + Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Reload complete."); + } + + return true; + } +} diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java new file mode 100644 index 000000000000..fb09fb097da8 --- /dev/null +++ b/src/main/java/org/spigotmc/SpigotConfig.java @@ -0,0 +1,396 @@ +package org.spigotmc; + +import com.google.common.base.Throwables; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import net.minecraft.server.AttributeRanged; +import net.minecraft.server.GenericAttributes; +import net.minecraft.server.IRegistry; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.MinecraftServer; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +public class SpigotConfig +{ + + private static File CONFIG_FILE; + private static final String HEADER = "This is the main configuration file for Spigot.\n" + + "As you can see, there's tons to configure. Some options may impact gameplay, so use\n" + + "with caution, and make sure you know what each option does before configuring.\n" + + "For a reference for any variable inside this file, check out the Spigot wiki at\n" + + "http://www.spigotmc.org/wiki/spigot-configuration/\n" + + "\n" + + "If you need help with the configuration or have any questions related to Spigot,\n" + + "join us at the IRC or drop by our forums and leave a post.\n" + + "\n" + + "IRC: #spigot @ irc.spi.gt ( http://www.spigotmc.org/pages/irc/ )\n" + + "Forums: http://www.spigotmc.org/\n"; + /*========================================================================*/ + public static YamlConfiguration config; + static int version; + static Map commands; + /*========================================================================*/ + private static Metrics metrics; + + public static void init(File configFile) + { + CONFIG_FILE = configFile; + config = new YamlConfiguration(); + try + { + config.load( CONFIG_FILE ); + } catch ( IOException ex ) + { + } catch ( InvalidConfigurationException ex ) + { + Bukkit.getLogger().log( Level.SEVERE, "Could not load spigot.yml, please correct your syntax errors", ex ); + throw Throwables.propagate( ex ); + } + + config.options().header( HEADER ); + config.options().copyDefaults( true ); + + commands = new HashMap(); + commands.put( "spigot", new SpigotCommand( "spigot" ) ); + + version = getInt( "config-version", 11 ); + set( "config-version", 11 ); + readConfig( SpigotConfig.class, null ); + } + + public static void registerCommands() + { + for ( Map.Entry entry : commands.entrySet() ) + { + MinecraftServer.getServer().server.getCommandMap().register( entry.getKey(), "Spigot", entry.getValue() ); + } + + /* // Paper - Replace with our own + if ( metrics == null ) + { + try + { + metrics = new Metrics(); + metrics.start(); + } catch ( IOException ex ) + { + Bukkit.getServer().getLogger().log( Level.SEVERE, "Could not start metrics service", ex ); + } + } + */ // Paper end + } + + static void readConfig(Class clazz, Object instance) + { + for ( Method method : clazz.getDeclaredMethods() ) + { + if ( Modifier.isPrivate( method.getModifiers() ) ) + { + if ( method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE ) + { + try + { + method.setAccessible( true ); + method.invoke( instance ); + } catch ( InvocationTargetException ex ) + { + throw Throwables.propagate( ex.getCause() ); + } catch ( Exception ex ) + { + Bukkit.getLogger().log( Level.SEVERE, "Error invoking " + method, ex ); + } + } + } + } + + try + { + config.save( CONFIG_FILE ); + } catch ( IOException ex ) + { + Bukkit.getLogger().log( Level.SEVERE, "Could not save " + CONFIG_FILE, ex ); + } + } + + private static void set(String path, Object val) + { + config.set( path, val ); + } + + private static boolean getBoolean(String path, boolean def) + { + config.addDefault( path, def ); + return config.getBoolean( path, config.getBoolean( path ) ); + } + + private static int getInt(String path, int def) + { + config.addDefault( path, def ); + return config.getInt( path, config.getInt( path ) ); + } + + private static List getList(String path, T def) + { + config.addDefault( path, def ); + return (List) config.getList( path, config.getList( path ) ); + } + + private static String getString(String path, String def) + { + config.addDefault( path, def ); + return config.getString( path, config.getString( path ) ); + } + + private static double getDouble(String path, double def) + { + config.addDefault( path, def ); + return config.getDouble( path, config.getDouble( path ) ); + } + + public static boolean logCommands; + private static void logCommands() + { + logCommands = getBoolean( "commands.log", true ); + } + + public static int tabComplete; + public static boolean sendNamespaced; + private static void tabComplete() + { + if ( version < 6 ) + { + boolean oldValue = getBoolean( "commands.tab-complete", true ); + if ( oldValue ) + { + set( "commands.tab-complete", 0 ); + } else + { + set( "commands.tab-complete", -1 ); + } + } + tabComplete = getInt( "commands.tab-complete", 0 ); + sendNamespaced = getBoolean( "commands.send-namespaced", true ); + } + + public static String whitelistMessage; + public static String unknownCommandMessage; + public static String serverFullMessage; + public static String outdatedClientMessage = "Outdated client! Please use {0}"; + public static String outdatedServerMessage = "Outdated server! I\'m still on {0}"; + private static String transform(String s) + { + return ChatColor.translateAlternateColorCodes( '&', s ).replaceAll( "\\\\n", "\n" ); + } + private static void messages() + { + if (version < 8) + { + set( "messages.outdated-client", outdatedClientMessage ); + set( "messages.outdated-server", outdatedServerMessage ); + } + + whitelistMessage = transform( getString( "messages.whitelist", "You are not whitelisted on this server!" ) ); + unknownCommandMessage = transform( getString( "messages.unknown-command", "Unknown command. Type \"/help\" for help." ) ); + serverFullMessage = transform( getString( "messages.server-full", "The server is full!" ) ); + outdatedClientMessage = transform( getString( "messages.outdated-client", outdatedClientMessage ) ); + outdatedServerMessage = transform( getString( "messages.outdated-server", outdatedServerMessage ) ); + } + + public static int timeoutTime = 60; + public static boolean restartOnCrash = true; + public static String restartScript = "./start.sh"; + public static String restartMessage; + private static void watchdog() + { + timeoutTime = getInt( "settings.timeout-time", timeoutTime ); + restartOnCrash = getBoolean( "settings.restart-on-crash", restartOnCrash ); + restartScript = getString( "settings.restart-script", restartScript ); + restartMessage = transform( getString( "messages.restart", "Server is restarting" ) ); + commands.put( "restart", new RestartCommand( "restart" ) ); + //WatchdogThread.doStart( timeoutTime, restartOnCrash ); // Paper - moved to PaperConfig + } + + public static boolean bungee; + private static void bungee() { + if ( version < 4 ) + { + set( "settings.bungeecord", false ); + System.out.println( "Oudated config, disabling BungeeCord support!" ); + } + bungee = getBoolean( "settings.bungeecord", false ); + } + + private static void nettyThreads() + { + int count = getInt( "settings.netty-threads", 4 ); + System.setProperty( "io.netty.eventLoopThreads", Integer.toString( count ) ); + Bukkit.getLogger().log( Level.INFO, "Using {0} threads for Netty based IO", count ); + } + + public static boolean lateBind; + private static void lateBind() { + lateBind = getBoolean( "settings.late-bind", false ); + } + + public static boolean disableStatSaving; + public static Map forcedStats = new HashMap<>(); + private static void stats() + { + disableStatSaving = getBoolean( "stats.disable-saving", false ); + + if ( !config.contains( "stats.forced-stats" ) ) { + config.createSection( "stats.forced-stats" ); + } + + ConfigurationSection section = config.getConfigurationSection( "stats.forced-stats" ); + for ( String name : section.getKeys( true ) ) + { + if ( section.isInt( name ) ) + { + try + { + MinecraftKey key = new MinecraftKey( name ); + if ( IRegistry.CUSTOM_STAT.get( key ) == null ) + { + Bukkit.getLogger().log(Level.WARNING, "Ignoring non existent stats.forced-stats " + name); + continue; + } + forcedStats.put( key, section.getInt( name ) ); + } catch (Exception ex) + { + Bukkit.getLogger().log(Level.WARNING, "Ignoring invalid stats.forced-stats " + name); + } + } + } + } + + private static void tpsCommand() + { + commands.put( "tps", new TicksPerSecondCommand( "tps" ) ); + } + + public static int playerSample; + private static void playerSample() + { + playerSample = Math.max(getInt( "settings.sample-count", 12 ), 0); // Paper - Avoid negative counts + Bukkit.getLogger().log( Level.INFO, "Server Ping Player Sample Count: {0}", playerSample ); // Paper - Use logger + } + + public static int playerShuffle; + private static void playerShuffle() + { + playerShuffle = getInt( "settings.player-shuffle", 0 ); + } + + public static List spamExclusions; + private static void spamExclusions() + { + spamExclusions = getList( "commands.spam-exclusions", Arrays.asList( new String[] + { + "/skill" + } ) ); + } + + public static boolean silentCommandBlocks; + private static void silentCommandBlocks() + { + silentCommandBlocks = getBoolean( "commands.silent-commandblock-console", false ); + } + + public static Set replaceCommands; + private static void replaceCommands() + { + if ( config.contains( "replace-commands" ) ) + { + set( "commands.replace-commands", config.getStringList( "replace-commands" ) ); + config.set( "replace-commands", null ); + } + replaceCommands = new HashSet( (List) getList( "commands.replace-commands", + Arrays.asList( "setblock", "summon", "testforblock", "tellraw" ) ) ); + } + + public static int userCacheCap; + private static void userCacheCap() + { + userCacheCap = getInt( "settings.user-cache-size", 1000 ); + } + + public static boolean saveUserCacheOnStopOnly; + private static void saveUserCacheOnStopOnly() + { + saveUserCacheOnStopOnly = getBoolean( "settings.save-user-cache-on-stop-only", false ); + } + + public static double movedWronglyThreshold; + private static void movedWronglyThreshold() + { + movedWronglyThreshold = getDouble( "settings.moved-wrongly-threshold", 0.0625D ); + } + + public static double movedTooQuicklyMultiplier; + private static void movedTooQuicklyMultiplier() + { + movedTooQuicklyMultiplier = getDouble( "settings.moved-too-quickly-multiplier", 10.0D ); + } + + public static double maxHealth = 2048; + public static double movementSpeed = 2048; + public static double attackDamage = 2048; + private static void attributeMaxes() + { + maxHealth = getDouble( "settings.attribute.maxHealth.max", maxHealth ); + ( (AttributeRanged) GenericAttributes.maxHealth ).maximum = maxHealth; + movementSpeed = getDouble( "settings.attribute.movementSpeed.max", movementSpeed ); + ( (AttributeRanged) GenericAttributes.MOVEMENT_SPEED ).maximum = movementSpeed; + attackDamage = getDouble( "settings.attribute.attackDamage.max", attackDamage ); + ( (AttributeRanged) GenericAttributes.ATTACK_DAMAGE ).maximum = attackDamage; + } + + public static boolean debug; + private static void debug() + { + debug = getBoolean( "settings.debug", false ); + + if ( debug && !LogManager.getRootLogger().isTraceEnabled() ) + { + // Enable debug logging + LoggerContext ctx = (LoggerContext) LogManager.getContext( false ); + Configuration conf = ctx.getConfiguration(); + conf.getLoggerConfig( LogManager.ROOT_LOGGER_NAME ).setLevel( org.apache.logging.log4j.Level.ALL ); + ctx.updateLoggers( conf ); + } + + if ( LogManager.getRootLogger().isTraceEnabled() ) + { + Bukkit.getLogger().info( "Debug logging is enabled" ); + } else + { + Bukkit.getLogger().info( "Debug logging is disabled" ); + } + } + + public static boolean disableAdvancementSaving; + public static List disabledAdvancements; + private static void disabledAdvancements() { + disableAdvancementSaving = getBoolean("advancements.disable-saving", false); + disabledAdvancements = getList("advancements.disabled", Arrays.asList(new String[]{"minecraft:story/disabled"})); + } +} diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java new file mode 100644 index 000000000000..809ce1d6aa1e --- /dev/null +++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java @@ -0,0 +1,323 @@ +package org.spigotmc; + +import java.util.List; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; + +public class SpigotWorldConfig +{ + + private final String worldName; + private final YamlConfiguration config; + private boolean verbose; + + public SpigotWorldConfig(String worldName) + { + this.worldName = worldName; + this.config = SpigotConfig.config; + init(); + } + + public void init() + { + this.verbose = getBoolean( "verbose", true ); + + log( "-------- World Settings For [" + worldName + "] --------" ); + SpigotConfig.readConfig( SpigotWorldConfig.class, this ); + } + + private void log(String s) + { + if ( verbose ) + { + Bukkit.getLogger().info( s ); + } + } + + private void set(String path, Object val) + { + config.set( "world-settings.default." + path, val ); + } + + public boolean getBoolean(String path, boolean def) + { + config.addDefault( "world-settings.default." + path, def ); + return config.getBoolean( "world-settings." + worldName + "." + path, config.getBoolean( "world-settings.default." + path ) ); + } + + public double getDouble(String path, double def) + { + config.addDefault( "world-settings.default." + path, def ); + return config.getDouble( "world-settings." + worldName + "." + path, config.getDouble( "world-settings.default." + path ) ); + } + + public int getInt(String path, int def) + { + config.addDefault( "world-settings.default." + path, def ); + return config.getInt( "world-settings." + worldName + "." + path, config.getInt( "world-settings.default." + path ) ); + } + + public List getList(String path, T def) + { + config.addDefault( "world-settings.default." + path, def ); + return (List) config.getList( "world-settings." + worldName + "." + path, config.getList( "world-settings.default." + path ) ); + } + + public String getString(String path, String def) + { + config.addDefault( "world-settings.default." + path, def ); + return config.getString( "world-settings." + worldName + "." + path, config.getString( "world-settings.default." + path ) ); + } + + // Crop growth rates + public int cactusModifier; + public int caneModifier; + public int melonModifier; + public int mushroomModifier; + public int pumpkinModifier; + public int saplingModifier; + public int beetrootModifier; + public int carrotModifier; + public int potatoModifier; + public int wheatModifier; + public int wartModifier; + public int vineModifier; + public int cocoaModifier; + private int getAndValidateGrowth(String crop) + { + int modifier = getInt( "growth." + crop.toLowerCase(java.util.Locale.ENGLISH) + "-modifier", 100 ); + if ( modifier == 0 ) + { + log( "Cannot set " + crop + " growth to zero, defaulting to 100" ); + modifier = 100; + } + log( crop + " Growth Modifier: " + modifier + "%" ); + + return modifier; + } + private void growthModifiers() + { + cactusModifier = getAndValidateGrowth( "Cactus" ); + caneModifier = getAndValidateGrowth( "Cane" ); + melonModifier = getAndValidateGrowth( "Melon" ); + mushroomModifier = getAndValidateGrowth( "Mushroom" ); + pumpkinModifier = getAndValidateGrowth( "Pumpkin" ); + saplingModifier = getAndValidateGrowth( "Sapling" ); + beetrootModifier = getAndValidateGrowth( "Beetroot" ); + carrotModifier = getAndValidateGrowth( "Carrot" ); + potatoModifier = getAndValidateGrowth( "Potato" ); + wheatModifier = getAndValidateGrowth( "Wheat" ); + wartModifier = getAndValidateGrowth( "NetherWart" ); + vineModifier = getAndValidateGrowth( "Vine" ); + cocoaModifier = getAndValidateGrowth( "Cocoa" ); + } + + public double itemMerge; + private void itemMerge() + { + itemMerge = getDouble("merge-radius.item", 2.5 ); + log( "Item Merge Radius: " + itemMerge ); + } + + public double expMerge; + private void expMerge() + { + expMerge = getDouble("merge-radius.exp", 3.0 ); + log( "Experience Merge Radius: " + expMerge ); + } + + public int viewDistance; + private void viewDistance() + { + viewDistance = getInt( "view-distance", Bukkit.getViewDistance() ); + log( "View Distance: " + viewDistance ); + } + + public byte mobSpawnRange; + private void mobSpawnRange() + { + mobSpawnRange = (byte) getInt( "mob-spawn-range", 8 ); // Paper - Vanilla + log( "Mob Spawn Range: " + mobSpawnRange ); + } + + public int itemDespawnRate; + private void itemDespawnRate() + { + itemDespawnRate = getInt( "item-despawn-rate", 6000 ); + log( "Item Despawn Rate: " + itemDespawnRate ); + } + + public int animalActivationRange = 32; + public int monsterActivationRange = 32; + public int miscActivationRange = 16; + public int waterActivationRange = 16; // Paper + public boolean tickInactiveVillagers = true; + private void activationRange() + { + animalActivationRange = getInt( "entity-activation-range.animals", animalActivationRange ); + monsterActivationRange = getInt( "entity-activation-range.monsters", monsterActivationRange ); + miscActivationRange = getInt( "entity-activation-range.misc", miscActivationRange ); + waterActivationRange = getInt( "entity-activation-range.water", waterActivationRange ); // Paper + tickInactiveVillagers = getBoolean( "entity-activation-range.tick-inactive-villagers", tickInactiveVillagers ); + log( "Entity Activation Range: An " + animalActivationRange + " / Mo " + monsterActivationRange + " / Mi " + miscActivationRange + " / Tiv " + tickInactiveVillagers ); + } + + public int playerTrackingRange = 48; + public int animalTrackingRange = 48; + public int monsterTrackingRange = 48; + public int miscTrackingRange = 32; + public int otherTrackingRange = 64; + private void trackingRange() + { + playerTrackingRange = getInt( "entity-tracking-range.players", playerTrackingRange ); + animalTrackingRange = getInt( "entity-tracking-range.animals", animalTrackingRange ); + monsterTrackingRange = getInt( "entity-tracking-range.monsters", monsterTrackingRange ); + miscTrackingRange = getInt( "entity-tracking-range.misc", miscTrackingRange ); + otherTrackingRange = getInt( "entity-tracking-range.other", otherTrackingRange ); + log( "Entity Tracking Range: Pl " + playerTrackingRange + " / An " + animalTrackingRange + " / Mo " + monsterTrackingRange + " / Mi " + miscTrackingRange + " / Other " + otherTrackingRange ); + } + + public int hopperTransfer; + public int hopperCheck; + public int hopperAmount; + private void hoppers() + { + // Set the tick delay between hopper item movements + hopperTransfer = getInt( "ticks-per.hopper-transfer", 8 ); + if ( SpigotConfig.version < 11 ) + { + set( "ticks-per.hopper-check", 1 ); + } + hopperCheck = getInt( "ticks-per.hopper-check", 1 ); + hopperAmount = getInt( "hopper-amount", 1 ); + log( "Hopper Transfer: " + hopperTransfer + " Hopper Check: " + hopperCheck + " Hopper Amount: " + hopperAmount ); + } + + public boolean randomLightUpdates; + private void lightUpdates() + { + randomLightUpdates = getBoolean( "random-light-updates", false ); + log( "Random Lighting Updates: " + randomLightUpdates ); + } + + public int arrowDespawnRate; + private void arrowDespawnRate() + { + arrowDespawnRate = getInt( "arrow-despawn-rate", 1200 ); + log( "Arrow Despawn Rate: " + arrowDespawnRate ); + } + + public boolean zombieAggressiveTowardsVillager; + private void zombieAggressiveTowardsVillager() + { + zombieAggressiveTowardsVillager = getBoolean( "zombie-aggressive-towards-villager", true ); + log( "Zombie Aggressive Towards Villager: " + zombieAggressiveTowardsVillager ); + } + + public boolean nerfSpawnerMobs; + private void nerfSpawnerMobs() + { + nerfSpawnerMobs = getBoolean( "nerf-spawner-mobs", false ); + log( "Nerfing mobs spawned from spawners: " + nerfSpawnerMobs ); + } + + public boolean enableZombiePigmenPortalSpawns; + private void enableZombiePigmenPortalSpawns() + { + enableZombiePigmenPortalSpawns = getBoolean( "enable-zombie-pigmen-portal-spawns", true ); + log( "Allow Zombie Pigmen to spawn from portal blocks: " + enableZombiePigmenPortalSpawns ); + } + + public int dragonDeathSoundRadius; + private void keepDragonDeathPerWorld() + { + dragonDeathSoundRadius = getInt( "dragon-death-sound-radius", 0 ); + } + + public int witherSpawnSoundRadius; + private void witherSpawnSoundRadius() + { + witherSpawnSoundRadius = getInt( "wither-spawn-sound-radius", 0 ); + } + + public int villageSeed; + public int desertSeed; + public int iglooSeed; + public int jungleSeed; + public int swampSeed; + public int monumentSeed; + public int oceanSeed; + public int shipwreckSeed; + public int slimeSeed; + private void initWorldGenSeeds() + { + villageSeed = getInt( "seed-village", 10387312 ); + desertSeed = getInt( "seed-desert", 14357617 ); + iglooSeed = getInt( "seed-igloo", 14357618 ); + jungleSeed = getInt( "seed-jungle", 14357619 ); + swampSeed = getInt( "seed-swamp", 14357620 ); + monumentSeed = getInt( "seed-monument", 10387313 ); + shipwreckSeed = getInt( "seed-shipwreck", 165745295 ); + oceanSeed = getInt( "seed-ocean", 14357621 ); + slimeSeed = getInt( "seed-slime", 987234911 ); + log( "Custom Map Seeds: Village: " + villageSeed + " Desert: " + desertSeed + " Igloo: " + iglooSeed + " Jungle: " + jungleSeed + " Swamp: " + swampSeed + " Monument: " + monumentSeed + "Ocean: " + oceanSeed + " Shipwreck: " + shipwreckSeed + " Slime: " + slimeSeed ); + } + + public float jumpWalkExhaustion; + public float jumpSprintExhaustion; + public float combatExhaustion; + public float regenExhaustion; + public float swimMultiplier; + public float sprintMultiplier; + public float otherMultiplier; + private void initHunger() + { + if ( SpigotConfig.version < 10 ) + { + set( "hunger.walk-exhaustion", null ); + set( "hunger.sprint-exhaustion", null ); + set( "hunger.combat-exhaustion", 0.1 ); + set( "hunger.regen-exhaustion", 6.0 ); + } + + jumpWalkExhaustion = (float) getDouble( "hunger.jump-walk-exhaustion", 0.05 ); + jumpSprintExhaustion = (float) getDouble( "hunger.jump-sprint-exhaustion", 0.2 ); + combatExhaustion = (float) getDouble( "hunger.combat-exhaustion", 0.1 ); + regenExhaustion = (float) getDouble( "hunger.regen-exhaustion", 6.0 ); + swimMultiplier = (float) getDouble( "hunger.swim-multiplier", 0.01 ); + sprintMultiplier = (float) getDouble( "hunger.sprint-multiplier", 0.1 ); + otherMultiplier = (float) getDouble( "hunger.other-multiplier", 0.0 ); + } + + public int currentPrimedTnt = 0; + public int maxTntTicksPerTick; + private void maxTntPerTick() { + if ( SpigotConfig.version < 7 ) + { + set( "max-tnt-per-tick", 100 ); + } + maxTntTicksPerTick = getInt( "max-tnt-per-tick", 100 ); + log( "Max TNT Explosions: " + maxTntTicksPerTick ); + } + + public int hangingTickFrequency; + private void hangingTickFrequency() + { + hangingTickFrequency = getInt( "hanging-tick-frequency", 100 ); + } + + public int tileMaxTickTime; + public int entityMaxTickTime; + private void maxTickTimes() + { + tileMaxTickTime = getInt("max-tick-time.tile", 50); + entityMaxTickTime = getInt("max-tick-time.entity", 50); + log("Tile Max Tick Time: " + tileMaxTickTime + "ms Entity max Tick Time: " + entityMaxTickTime + "ms"); + } + + public double squidSpawnRangeMin; + private void squidSpawnRange() + { + squidSpawnRangeMin = getDouble("squid-spawn-range.min", 45.0D); + } +} diff --git a/src/main/java/org/spigotmc/SupplierUtils.java b/src/main/java/org/spigotmc/SupplierUtils.java new file mode 100644 index 000000000000..f63ce98bf8f9 --- /dev/null +++ b/src/main/java/org/spigotmc/SupplierUtils.java @@ -0,0 +1,69 @@ +package org.spigotmc; + +import java.util.function.Supplier; +import javax.annotation.Nullable; + +/** + * Utilities for creating and working with {@code Supplier} instances. + */ +public class SupplierUtils { + + /** + * Repeatedly supplies the first value from a given sequence; the value is + * obtained only when required. + */ + public static class LazyHeadSupplier implements Supplier { + + private @Nullable Supplier completion; + private @Nullable V value; + + public LazyHeadSupplier(Supplier completion) { + this.completion = completion; + } + + public synchronized @Nullable V get() { + if (this.completion != null) { + this.value = this.completion.get(); + this.completion = null; + } + + return this.value; + } + } + + /** + * Repeatedly supplies the given value. + */ + public static class ValueSupplier implements Supplier { + + private final @Nullable V value; + + public ValueSupplier(@Nullable V value) { + this.value = value; + } + + public @Nullable V get() { + return this.value; + } + } + + /** + * Creates a new {@code Supplier} that supplies the given {@code Supplier}'s + * first value. + * + * @param doLazily {@code false}, if {@code completion.get()} should be + * called immediately, or {@code true}, if {@code completion.get()} should + * be called only when the value is first needed. + */ + public static Supplier createUnivaluedSupplier(Supplier completion, boolean doLazily) { + return doLazily ? new LazyHeadSupplier(completion) : new ValueSupplier(completion.get()); + } + + /** + * Returns {@code supplier.get()}, if {@code supplier} is non-{@code null} + * (or {@code null}, otherwise). + */ + public static @Nullable V getIfExists(@Nullable Supplier supplier) { + return supplier != null ? supplier.get() : null; + } +} diff --git a/src/main/java/org/spigotmc/TickLimiter.java b/src/main/java/org/spigotmc/TickLimiter.java new file mode 100644 index 000000000000..23a39382be15 --- /dev/null +++ b/src/main/java/org/spigotmc/TickLimiter.java @@ -0,0 +1,20 @@ +package org.spigotmc; + +public class TickLimiter { + + private final int maxTime; + private long startTime; + + public TickLimiter(int maxtime) { + this.maxTime = maxtime; + } + + public void initTick() { + startTime = System.currentTimeMillis(); + } + + public boolean shouldContinue() { + long remaining = System.currentTimeMillis() - startTime; + return remaining < maxTime; + } +} diff --git a/src/main/java/org/spigotmc/TicksPerSecondCommand.java b/src/main/java/org/spigotmc/TicksPerSecondCommand.java new file mode 100644 index 000000000000..6d21c326922a --- /dev/null +++ b/src/main/java/org/spigotmc/TicksPerSecondCommand.java @@ -0,0 +1,44 @@ +package org.spigotmc; + +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +public class TicksPerSecondCommand extends Command +{ + + public TicksPerSecondCommand(String name) + { + super( name ); + this.description = "Gets the current ticks per second for the server"; + this.usageMessage = "/tps"; + this.setPermission( "bukkit.command.tps" ); + } + + @Override + public boolean execute(CommandSender sender, String currentAlias, String[] args) + { + if ( !testPermission( sender ) ) + { + return true; + } + + // Paper start - Further improve tick handling + double[] tps = org.bukkit.Bukkit.getTPS(); + String[] tpsAvg = new String[tps.length]; + + for ( int i = 0; i < tps.length; i++) { + tpsAvg[i] = format( tps[i] ); + } + sender.sendMessage( ChatColor.GOLD + "TPS from last 1m, 5m, 15m: " + org.apache.commons.lang.StringUtils.join(tpsAvg, ", ")); + // Paper end + + return true; + } + + private static String format(double tps) // Paper - Made static + { + return ( ( tps > 18.0 ) ? ChatColor.GREEN : ( tps > 16.0 ) ? ChatColor.YELLOW : ChatColor.RED ).toString() + + ( ( tps > 20.0 ) ? "*" : "" ) + Math.min( Math.round( tps * 100.0 ) / 100.0, 20.0 ); + } +} diff --git a/src/main/java/org/spigotmc/TrackingRange.java b/src/main/java/org/spigotmc/TrackingRange.java new file mode 100644 index 000000000000..4bf4d2ac64b1 --- /dev/null +++ b/src/main/java/org/spigotmc/TrackingRange.java @@ -0,0 +1,51 @@ +package org.spigotmc; + +import net.minecraft.server.Entity; +import net.minecraft.server.EntityExperienceOrb; +import net.minecraft.server.EntityGhast; +import net.minecraft.server.EntityItem; +import net.minecraft.server.EntityItemFrame; +import net.minecraft.server.EntityPainting; +import net.minecraft.server.EntityPlayer; + +public class TrackingRange +{ + + /** + * Gets the range an entity should be 'tracked' by players and visible in + * the client. + * + * @param entity + * @param defaultRange Default range defined by Mojang + * @return + */ + public static int getEntityTrackingRange(Entity entity, int defaultRange) + { + SpigotWorldConfig config = entity.world.spigotConfig; + if ( entity instanceof EntityPlayer ) + { + return config.playerTrackingRange; + } else if ( entity.activationType == 1 ) + { + return config.monsterTrackingRange; + } else if ( entity instanceof EntityGhast ) + { + if ( config.monsterTrackingRange > config.monsterActivationRange ) + { + return config.monsterTrackingRange; + } else + { + return config.monsterActivationRange; + } + } else if ( entity.activationType == 2 ) + { + return config.animalTrackingRange; + } else if ( entity instanceof EntityItemFrame || entity instanceof EntityPainting || entity instanceof EntityItem || entity instanceof EntityExperienceOrb ) + { + return config.miscTrackingRange; + } else + { + return config.otherTrackingRange; + } + } +} diff --git a/src/main/java/org/spigotmc/ValidateUtils.java b/src/main/java/org/spigotmc/ValidateUtils.java new file mode 100644 index 000000000000..58a9534816c7 --- /dev/null +++ b/src/main/java/org/spigotmc/ValidateUtils.java @@ -0,0 +1,14 @@ +package org.spigotmc; + +public class ValidateUtils +{ + + public static String limit(String str, int limit) + { + if ( str.length() > limit ) + { + return str.substring( 0, limit ); + } + return str; + } +} diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java new file mode 100644 index 000000000000..5447bc9cc266 --- /dev/null +++ b/src/main/java/org/spigotmc/WatchdogThread.java @@ -0,0 +1,177 @@ +package org.spigotmc; + +import java.lang.management.ManagementFactory; +import java.lang.management.MonitorInfo; +import java.lang.management.ThreadInfo; +import java.util.logging.Level; +import java.util.logging.Logger; +import com.destroystokyo.paper.PaperConfig; +import net.minecraft.server.MinecraftServer; +import org.bukkit.Bukkit; + +public class WatchdogThread extends Thread +{ + + private static WatchdogThread instance; + private final long timeoutTime; + private final long earlyWarningEvery; // Paper - Timeout time for just printing a dump but not restarting + private final long earlyWarningDelay; // Paper + public static volatile boolean hasStarted; // Paper + private long lastEarlyWarning; // Paper - Keep track of short dump times to avoid spamming console with short dumps + private final boolean restart; + private volatile long lastTick; + private volatile boolean stopping; + + private WatchdogThread(long timeoutTime, boolean restart) + { + super( "Paper Watchdog Thread" ); + this.timeoutTime = timeoutTime; + this.restart = restart; + earlyWarningEvery = Math.min(PaperConfig.watchdogPrintEarlyWarningEvery, timeoutTime); // Paper + earlyWarningDelay = Math.min(PaperConfig.watchdogPrintEarlyWarningDelay, timeoutTime); // Paper + } + + private static long monotonicMillis() + { + return System.nanoTime() / 1000000L; + } + + public static void doStart(int timeoutTime, boolean restart) + { + if ( instance == null ) + { + instance = new WatchdogThread( timeoutTime * 1000L, restart ); + instance.start(); + } + } + + public static void tick() + { + instance.lastTick = monotonicMillis(); + } + + public static void doStop() + { + if ( instance != null ) + { + instance.stopping = true; + } + } + + @Override + public void run() + { + while ( !stopping ) + { + // Paper start + Logger log = Bukkit.getServer().getLogger(); + long currentTime = monotonicMillis(); + if ( lastTick != 0 && currentTime > lastTick + earlyWarningEvery && !Boolean.getBoolean("disable.watchdog") ) + { + boolean isLongTimeout = currentTime > lastTick + timeoutTime; + // Don't spam early warning dumps + if ( !isLongTimeout && (earlyWarningEvery <= 0 || !hasStarted || currentTime < lastEarlyWarning + earlyWarningEvery || currentTime < lastTick + earlyWarningDelay)) continue; + lastEarlyWarning = currentTime; + if (isLongTimeout) { + // Paper end + log.log( Level.SEVERE, "------------------------------" ); + log.log( Level.SEVERE, "The server has stopped responding! This is (probably) not a Paper bug." ); // Paper + log.log( Level.SEVERE, "If you see a plugin in the Server thread dump below, then please report it to that author" ); + log.log( Level.SEVERE, "\t *Especially* if it looks like HTTP or MySQL operations are occurring" ); + log.log( Level.SEVERE, "If you see a world save or edit, then it means you did far more than your server can handle at once" ); + log.log( Level.SEVERE, "\t If this is the case, consider increasing timeout-time in spigot.yml but note that this will replace the crash with LARGE lag spikes" ); + log.log( Level.SEVERE, "If you are unsure or still think this is a Paper bug, please report this to https://github.com/PaperMC/Paper/issues" ); + log.log( Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports" ); + log.log( Level.SEVERE, "Paper version: " + Bukkit.getServer().getVersion() ); + // + if ( net.minecraft.server.World.lastPhysicsProblem != null ) + { + log.log( Level.SEVERE, "------------------------------" ); + log.log( Level.SEVERE, "During the run of the server, a physics stackoverflow was supressed" ); + log.log( Level.SEVERE, "near " + net.minecraft.server.World.lastPhysicsProblem ); + } + // Paper start - Warn in watchdog if an excessive velocity was ever set + if ( org.bukkit.craftbukkit.CraftServer.excessiveVelEx != null ) + { + log.log( Level.SEVERE, "------------------------------" ); + log.log( Level.SEVERE, "During the run of the server, a plugin set an excessive velocity on an entity" ); + log.log( Level.SEVERE, "This may be the cause of the issue, or it may be entirely unrelated" ); + log.log( Level.SEVERE, org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getMessage()); + for ( StackTraceElement stack : org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getStackTrace() ) + { + log.log( Level.SEVERE, "\t\t" + stack ); + } + } + // Paper end + } else + { + log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---"); + log.log(Level.SEVERE, "The server has not responded for " + (currentTime - lastTick) / 1000 + " seconds! Creating thread dump"); + } + // Paper end - Different message for short timeout + log.log( Level.SEVERE, "------------------------------" ); + log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); + dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().primaryThread.getId(), Integer.MAX_VALUE ), log ); + log.log( Level.SEVERE, "------------------------------" ); + // + // Paper start - Only print full dump on long timeouts + if ( isLongTimeout ) + { + log.log( Level.SEVERE, "Entire Thread Dump:" ); + ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads( true, true ); + for ( ThreadInfo thread : threads ) + { + dumpThread( thread, log ); + } + } else { + log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---"); + } + + + log.log( Level.SEVERE, "------------------------------" ); + + if ( isLongTimeout ) + { + if ( restart ) + { + RestartCommand.restart(); + } + break; + } // Paper end + } + + try + { + sleep( 1000 ); // Paper - Reduce check time to every second instead of every ten seconds, more consistent and allows for short timeout + } catch ( InterruptedException ex ) + { + interrupt(); + } + } + } + + private static void dumpThread(ThreadInfo thread, Logger log) + { + log.log( Level.SEVERE, "------------------------------" ); + // + log.log( Level.SEVERE, "Current Thread: " + thread.getThreadName() ); + log.log( Level.SEVERE, "\tPID: " + thread.getThreadId() + + " | Suspended: " + thread.isSuspended() + + " | Native: " + thread.isInNative() + + " | State: " + thread.getThreadState() ); + if ( thread.getLockedMonitors().length != 0 ) + { + log.log( Level.SEVERE, "\tThread is waiting on monitor(s):" ); + for ( MonitorInfo monitor : thread.getLockedMonitors() ) + { + log.log( Level.SEVERE, "\t\tLocked on:" + monitor.getLockedStackFrame() ); + } + } + log.log( Level.SEVERE, "\tStack:" ); + // + for ( StackTraceElement stack : thread.getStackTrace() ) + { + log.log( Level.SEVERE, "\t\t" + stack ); + } + } +} diff --git a/src/main/resources/configurations/bukkit.yml b/src/main/resources/configurations/bukkit.yml new file mode 100644 index 000000000000..301e28b2cf94 --- /dev/null +++ b/src/main/resources/configurations/bukkit.yml @@ -0,0 +1,37 @@ +# This is the main configuration file for Bukkit. +# As you can see, there's actually not that much to configure without any plugins. +# For a reference for any variable inside this file, check out the Bukkit Wiki at +# https://www.spigotmc.org/go/bukkit-yml +# +# If you need help on this file, feel free to join us on irc or leave a message +# on the forums asking for advice. +# +# IRC: #spigot @ irc.spi.gt +# (If this means nothing to you, just go to https://www.spigotmc.org/go/irc ) +# Forums: https://www.spigotmc.org/ +# Bug tracker: https://www.spigotmc.org/go/bugs + + +settings: + allow-end: true + warn-on-overload: true + permissions-file: permissions.yml + update-folder: update + plugin-profiling: false + connection-throttle: 4000 + query-plugins: true + deprecated-verbose: default + shutdown-message: Server closed +spawn-limits: + monsters: 70 + animals: 10 + water-animals: 15 + ambient: 15 +chunk-gc: + period-in-ticks: 600 + load-threshold: 0 +ticks-per: + animal-spawns: 400 + monster-spawns: 1 + autosave: 6000 +aliases: now-in-commands.yml diff --git a/src/main/resources/configurations/commands.yml b/src/main/resources/configurations/commands.yml new file mode 100644 index 000000000000..0f45c397df38 --- /dev/null +++ b/src/main/resources/configurations/commands.yml @@ -0,0 +1,17 @@ +# This is the commands configuration file for Bukkit. +# For documentation on how to make use of this file, check out the Bukkit Wiki at +# https://www.spigotmc.org/go/commands-yml +# +# If you need help on this file, feel free to join us on irc or leave a message +# on the forums asking for advice. +# +# IRC: #spigot @ irc.spi.gt +# (If this means nothing to you, just go to https://www.spigotmc.org/go/irc ) +# Forums: https://www.spigotmc.org/ +# Bug tracker: https://www.spigotmc.org/go/bugs + +command-block-overrides: [] +ignore-vanilla-permissions: false +aliases: + icanhasbukkit: + - "version $1-" diff --git a/src/main/resources/configurations/help.yml b/src/main/resources/configurations/help.yml new file mode 100644 index 000000000000..15c3d0707062 --- /dev/null +++ b/src/main/resources/configurations/help.yml @@ -0,0 +1,55 @@ +# This is the help configuration file for Bukkit. +# +# By default you do not need to modify this file. Help topics for all plugin commands are automatically provided by +# or extracted from your installed plugins. You only need to modify this file if you wish to add new help pages to +# your server or override the help pages of existing plugin commands. +# +# This file is divided up into the following parts: +# -- general-topics: lists admin defined help topics +# -- index-topics: lists admin defined index topics +# -- amend-topics: lists topic amendments to apply to existing help topics +# -- ignore-plugins: lists any plugins that should be excluded from help +# +# Examples are given below. When amending command topic, the string will be replaced with the existing value +# in the help topic. Color codes can be used in topic text. The color code character is & followed by 0-F. +# ================================================================ +# +# Set this to true to list the individual command help topics in the master help. +# command-topics-in-master-index: true +# +# Each general topic will show up as a separate topic in the help index along with all the plugin command topics. +# general-topics: +# Rules: +# shortText: Rules of the server +# fullText: | +# &61. Be kind to your fellow players. +# &B2. No griefing. +# &D3. No swearing. +# permission: topics.rules +# +# Each index topic will show up as a separate sub-index in the help index along with all the plugin command topics. +# To override the default help index (displayed when the user executes /help), name the index topic "Default". +# index-topics: +# Ban Commands: +# shortText: Player banning commands +# preamble: Moderator - do not abuse these commands +# permission: op +# commands: +# - /ban +# - /ban-ip +# - /banlist +# +# Topic amendments are used to change the content of automatically generated plugin command topics. +# amended-topics: +# /stop: +# shortText: Stops the server cold....in its tracks! +# fullText: - This kills the server. +# permission: you.dont.have +# +# Any plugin in the ignored plugins list will be excluded from help. The name must match the name displayed by +# the /plugins command. Ignore "Bukkit" to remove the standard bukkit commands from the index. Ignore "All" +# to completely disable automatic help topic generation. +# ignore-plugins: +# - PluginNameOne +# - PluginNameTwo +# - PluginNameThree diff --git a/src/main/resources/log4j2.component.properties b/src/main/resources/log4j2.component.properties new file mode 100644 index 000000000000..ee7c90784c80 --- /dev/null +++ b/src/main/resources/log4j2.component.properties @@ -0,0 +1 @@ +Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml new file mode 100644 index 000000000000..6711e6dff9ca --- /dev/null +++ b/src/main/resources/log4j2.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/org/bukkit/ArtTest.java b/src/test/java/org/bukkit/ArtTest.java new file mode 100644 index 000000000000..e6888dec5373 --- /dev/null +++ b/src/test/java/org/bukkit/ArtTest.java @@ -0,0 +1,69 @@ +package org.bukkit; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +import java.util.Collections; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.minecraft.server.IRegistry; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.Paintings; + +import org.bukkit.craftbukkit.CraftArt; +import org.junit.Test; + +import com.google.common.collect.Lists; +import org.bukkit.support.AbstractTestingBase; + +public class ArtTest extends AbstractTestingBase { + private static final int UNIT_MULTIPLIER = 16; + + @Test + public void verifyMapping() { + List arts = Lists.newArrayList(Art.values()); + + for (MinecraftKey key : IRegistry.MOTIVE.keySet()) { + Paintings enumArt = IRegistry.MOTIVE.get(key); + String name = key.getKey(); + int width = enumArt.b() / UNIT_MULTIPLIER; + int height = enumArt.c() / UNIT_MULTIPLIER; + + Art subject = CraftArt.NotchToBukkit(enumArt); + + String message = String.format("org.bukkit.Art is missing '%s'", name); + assertNotNull(message, subject); + + assertThat(Art.getByName(name), is(subject)); + assertThat("Art." + subject + "'s width", subject.getBlockWidth(), is(width)); + assertThat("Art." + subject + "'s height", subject.getBlockHeight(), is(height)); + + arts.remove(subject); + } + + assertThat("org.bukkit.Art has too many arts", arts, is(Collections.EMPTY_LIST)); + } + + @Test + public void testCraftArtToNotch() { + Map cache = new HashMap<>(); + for (Art art : Art.values()) { + Paintings enumArt = CraftArt.BukkitToNotch(art); + assertNotNull(art.name(), enumArt); + assertThat(art.name(), cache.put(enumArt, art), is(nullValue())); + } + } + + @Test + public void testCraftArtToBukkit() { + Map cache = new EnumMap(Art.class); + for (Paintings enumArt : (Iterable) IRegistry.MOTIVE) { // Eclipse fail + Art art = CraftArt.NotchToBukkit(enumArt); + assertNotNull("Could not CraftArt.NotchToBukkit " + enumArt, art); + assertThat("Duplicate artwork " + enumArt, cache.put(art, enumArt), is(nullValue())); + } + } +} diff --git a/src/test/java/org/bukkit/BiomeTest.java b/src/test/java/org/bukkit/BiomeTest.java new file mode 100644 index 000000000000..913c4cf3fc29 --- /dev/null +++ b/src/test/java/org/bukkit/BiomeTest.java @@ -0,0 +1,26 @@ +package org.bukkit; + +import net.minecraft.server.BiomeBase; +import net.minecraft.server.IRegistry; +import org.bukkit.block.Biome; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Assert; +import org.junit.Test; + +public class BiomeTest extends AbstractTestingBase { + + @Test + public void testBukkitToMinecraft() { + for (Biome biome : Biome.values()) { + Assert.assertNotNull("No NMS mapping for " + biome, CraftBlock.biomeToBiomeBase(biome)); + } + } + + @Test + public void testMinecraftToBukkit() { + for (Object biome : IRegistry.BIOME) { + Assert.assertNotNull("No Bukkit mapping for " + biome, CraftBlock.biomeBaseToBiome((BiomeBase) biome)); + } + } +} diff --git a/src/test/java/org/bukkit/BlockDataConversionTest.java b/src/test/java/org/bukkit/BlockDataConversionTest.java new file mode 100644 index 000000000000..46cc26b316f0 --- /dev/null +++ b/src/test/java/org/bukkit/BlockDataConversionTest.java @@ -0,0 +1,38 @@ +package org.bukkit; + +import java.util.ArrayList; +import java.util.List; +import net.minecraft.server.Block; +import net.minecraft.server.IBlockData; +import net.minecraft.server.IRegistry; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/** + * This test class ensures that all Blocks (as registered in IRegistry.BLOCK) + * can be converted into their CraftBlockData equivalent. + */ +@RunWith(Parameterized.class) +public class BlockDataConversionTest extends AbstractTestingBase { + + @Parameterized.Parameters(name = "{index}: {0}") + public static List args() { + List list = new ArrayList<>(); + for (Block block : (Iterable) IRegistry.BLOCK) { + list.add(new Object[]{block.getBlockData()}); + } + return list; + } + + @Parameterized.Parameter(0) public IBlockData data; + + @Test + public void testNotNull() { + Assert.assertNotNull(data); + Assert.assertNotNull(CraftBlockData.fromData(data)); + } +} diff --git a/src/test/java/org/bukkit/BlockDataTest.java b/src/test/java/org/bukkit/BlockDataTest.java new file mode 100644 index 000000000000..11b600696044 --- /dev/null +++ b/src/test/java/org/bukkit/BlockDataTest.java @@ -0,0 +1,172 @@ +package org.bukkit; + +import net.minecraft.server.BlockCake; +import net.minecraft.server.BlockChest; +import net.minecraft.server.Blocks; +import net.minecraft.server.EnumDirection; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.type.Cake; +import org.bukkit.block.data.type.Chest; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.support.AbstractTestingBase; +import static org.hamcrest.Matchers.*; +import org.junit.Assert; +import org.junit.Test; + +public class BlockDataTest extends AbstractTestingBase { + + @Test + public void testParsing() { + BlockData cakeTest = CraftBlockData.fromData(Blocks.CAKE.getBlockData().set(BlockCake.BITES, 3)); + + BlockData materialString = CraftBlockData.newData(Material.CAKE, "[bites=3]"); + Assert.assertThat(materialString, is(cakeTest)); + + BlockData combined = CraftBlockData.newData(null, "cake[bites=3]"); + Assert.assertThat(combined, is(cakeTest)); + + BlockData combinedMinecraft = CraftBlockData.newData(null, "minecraft:cake[bites=3]"); + Assert.assertThat(combinedMinecraft, is(cakeTest)); + + BlockData inverted = CraftBlockData.newData(null, cakeTest.getAsString()); + Assert.assertThat(inverted, is(cakeTest)); + } + + @Test(expected = IllegalArgumentException.class) + public void testBadMaterial() { + CraftBlockData.newData(null, "invalid"); + } + + @Test(expected = IllegalArgumentException.class) + public void testBadSyntax() { + CraftBlockData.newData(null, "minecraft:cake[bites=3"); + } + + @Test(expected = IllegalArgumentException.class) + public void testDoubleMaterial() { + CraftBlockData.newData(Material.CAKE, "minecraft:cake[bites=3]"); + } + + @Test(expected = IllegalArgumentException.class) + public void testMistake() { + BlockData cakeTest = CraftBlockData.fromData(Blocks.CAKE.getBlockData().set(BlockCake.BITES, 3)); + + CraftBlockData.newData(Material.CAKE, cakeTest.toString()); + } + + @Test(expected = IllegalArgumentException.class) + public void testItem() { + CraftBlockData.newData(Material.DIAMOND_AXE, null); + } + + @Test(expected = IllegalArgumentException.class) + public void testItemParse() { + CraftBlockData.newData(null, "minecraft:diamond_axe"); + } + + @Test + public void testClone() { + Cake cakeTest = (Cake) CraftBlockData.fromData(Blocks.CAKE.getBlockData().set(BlockCake.BITES, 3)); + Cake clone = (Cake) cakeTest.clone(); + + Assert.assertFalse("Clone did not return new object", cakeTest == clone); + Assert.assertThat("Clone is not equal", clone, is(cakeTest)); + + clone.setBites(1); + Assert.assertThat("Clone is not actually clone", clone, is(not(cakeTest))); + } + + @Test + public void testMerge() { + Chest trueTarget = (Chest) CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]"); + Chest falseTarget = (Chest) CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=false]"); + Chest waterlogged = (Chest) CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"); + + BlockData candidate; + + Assert.assertFalse("Target and match are not yet equal", trueTarget.equals(waterlogged)); + candidate = trueTarget.merge(waterlogged); + Assert.assertTrue("Target and candidate are now equal", trueTarget.equals(candidate)); + + Assert.assertFalse("Target and match are not yet equal", falseTarget.equals(waterlogged)); + candidate = falseTarget.merge(waterlogged); + Assert.assertFalse("Target and candidate are still not equal", falseTarget.equals(candidate)); + } + + @Test + public void testMergeAny() { + Chest trueTarget = (Chest) CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]"); + Chest falseTarget = (Chest) CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=false]"); + Chest any = (Chest) CraftBlockData.newData(null, "minecraft:chest"); + + BlockData candidate; + + Assert.assertFalse("Target and match are not yet equal", trueTarget.equals(any)); + candidate = trueTarget.merge(any); + Assert.assertTrue("Target and candidate are now equal", trueTarget.equals(candidate)); + + Assert.assertFalse("Target and match are not yet equal", falseTarget.equals(any)); + candidate = falseTarget.merge(any); + Assert.assertTrue("Target and candidate are now equal", falseTarget.equals(candidate)); + } + + @Test(expected = IllegalArgumentException.class) + public void testCannotMerge1() { + Chest one = (Chest) CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]"); + Chest two = (Chest) CraftBlockData.fromData(Blocks.CHEST.getBlockData()); + + one.merge(two); + } + + @Test(expected = IllegalArgumentException.class) + public void testCannotMerge2() { + Chest one = (Chest) CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"); + Chest two = (Chest) CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"); + + one.merge(two); + + two.setFacing(BlockFace.NORTH); + one.merge(two); + } + + @Test(expected = IllegalArgumentException.class) + public void testCannotMerge3() { + Chest one = (Chest) CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"); + Chest two = (Chest) CraftBlockData.newData(null, "minecraft:trapped_chest[waterlogged=true]"); + + one.merge(two); + } + + @Test + public void testMatch() { + Assert.assertTrue(CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]").matches(CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"))); + Assert.assertFalse(CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=false]").matches(CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"))); + Assert.assertTrue(CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]").matches(CraftBlockData.newData(null, "minecraft:chest"))); + Assert.assertFalse(CraftBlockData.newData(null, "minecraft:trapped_chest[facing=east,waterlogged=false]").matches(CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]"))); + Assert.assertTrue(CraftBlockData.newData(null, "minecraft:chest[facing=east,waterlogged=true]").matches(CraftBlockData.newData(null, "minecraft:chest[waterlogged=true,facing=east]"))); + + Chest one = (Chest) CraftBlockData.fromData(Blocks.CHEST.getBlockData().set(BlockChest.FACING, EnumDirection.EAST)); + Chest two = (Chest) CraftBlockData.newData(null, "minecraft:chest[waterlogged=false]"); + + Assert.assertTrue(one.matches(two)); + Assert.assertFalse(two.matches(one)); + } + + @Test + public void testGetAsString() { + String dataString = "minecraft:chest[facing=east,waterlogged=true]"; + BlockData data = CraftBlockData.newData(null, dataString); + + Assert.assertThat(data.getAsString(true), is(dataString)); + Assert.assertThat(data.getAsString(false), is("minecraft:chest[facing=east,type=single,waterlogged=true]")); + } + + @Test + public void testGetAsString2() { + Chest data = (Chest) CraftBlockData.fromData(Blocks.CHEST.getBlockData().set(BlockChest.FACING, EnumDirection.EAST)); + + Assert.assertThat(data.getAsString(true), is("minecraft:chest[facing=east,type=single,waterlogged=false]")); + Assert.assertThat(data.getAsString(false), is("minecraft:chest[facing=east,type=single,waterlogged=false]")); + } +} diff --git a/src/test/java/org/bukkit/ChatTest.java b/src/test/java/org/bukkit/ChatTest.java new file mode 100644 index 000000000000..97467c26fb70 --- /dev/null +++ b/src/test/java/org/bukkit/ChatTest.java @@ -0,0 +1,41 @@ +package org.bukkit; + +import net.minecraft.server.EnumChatFormat; +import net.minecraft.server.IChatBaseComponent; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ChatTest { + + @Test + public void testColors() { + for (ChatColor color : ChatColor.values()) { + Assert.assertNotNull(CraftChatMessage.getColor(color)); + Assert.assertEquals(color, CraftChatMessage.getColor(CraftChatMessage.getColor(color))); + } + + for (EnumChatFormat format : EnumChatFormat.values()) { + Assert.assertNotNull(CraftChatMessage.getColor(format)); + Assert.assertEquals(format, CraftChatMessage.getColor(CraftChatMessage.getColor(format))); + } + } + + @Test + public void testURLJsonConversion() { + IChatBaseComponent[] components; + components = CraftChatMessage.fromString("https://spigotmc.org/test Test Message"); + assertEquals("{\"extra\":[{\"clickEvent\":{\"action\":\"open_url\",\"value\":\"https://spigotmc.org/test\"},\"text\":\"https://spigotmc.org/test\"},{\"text\":\" Test Message\"}],\"text\":\"\"}", + CraftChatMessage.toJSON(components[0])); + + components = CraftChatMessage.fromString("123 " + ChatColor.GOLD + "https://spigotmc.org " + ChatColor.BOLD + "test"); + assertEquals("{\"extra\":[{\"text\":\"123 \"},{\"color\":\"gold\",\"clickEvent\":{\"action\":\"open_url\",\"value\":\"https://spigotmc.org\"},\"text\":\"https://spigotmc.org\"},{\"color\":\"gold\",\"text\":\" \"},{\"bold\":true,\"color\":\"gold\",\"text\":\"test\"}],\"text\":\"\"}", + CraftChatMessage.toJSON(components[0])); + + components = CraftChatMessage.fromString("multiCase http://SpigotMC.ORg/SpOngeBobMeEMeGoESHeRE"); + assertEquals("{\"extra\":[{\"text\":\"multiCase \"},{\"clickEvent\":{\"action\":\"open_url\",\"value\":\"http://SpigotMC.ORg/SpOngeBobMeEMeGoESHeRE\"},\"text\":\"http://SpigotMC.ORg/SpOngeBobMeEMeGoESHeRE\"}],\"text\":\"\"}", + CraftChatMessage.toJSON(components[0])); + } +} diff --git a/src/test/java/org/bukkit/DyeColorsTest.java b/src/test/java/org/bukkit/DyeColorsTest.java new file mode 100644 index 000000000000..45a5caed2ba5 --- /dev/null +++ b/src/test/java/org/bukkit/DyeColorsTest.java @@ -0,0 +1,46 @@ +package org.bukkit; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.server.EnumColor; + +import org.bukkit.support.AbstractTestingBase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class DyeColorsTest extends AbstractTestingBase { + + @Parameters(name= "{index}: {0}") + public static List data() { + List list = new ArrayList(); + for (DyeColor dye : DyeColor.values()) { + list.add(new Object[] {dye}); + } + return list; + } + + @Parameter public DyeColor dye; + + @Test + public void checkColor() { + Color color = dye.getColor(); + float[] nmsColorArray = EnumColor.fromColorIndex(dye.getWoolData()).d(); + Color nmsColor = Color.fromRGB((int) (nmsColorArray[0] * 255), (int) (nmsColorArray[1] * 255), (int) (nmsColorArray[2] * 255)); + assertThat(color, is(nmsColor)); + } + + @Test + public void checkFireworkColor() { + Color color = dye.getFireworkColor(); + int nmsColor = EnumColor.fromColorIndex(dye.getWoolData()).f(); + assertThat(color, is(Color.fromRGB(nmsColor))); + } +} diff --git a/src/test/java/org/bukkit/EnchantmentTest.java b/src/test/java/org/bukkit/EnchantmentTest.java new file mode 100644 index 000000000000..e69283b1f217 --- /dev/null +++ b/src/test/java/org/bukkit/EnchantmentTest.java @@ -0,0 +1,25 @@ +package org.bukkit; + +import net.minecraft.server.IRegistry; +import net.minecraft.server.MinecraftKey; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Assert; +import org.junit.Test; + +public class EnchantmentTest extends AbstractTestingBase { + + @Test + public void verifyMapping() { + for (MinecraftKey key : IRegistry.ENCHANTMENT.keySet()) { + net.minecraft.server.Enchantment nms = IRegistry.ENCHANTMENT.get(key); + + Enchantment bukkitById = Enchantment.getByKey(CraftNamespacedKey.fromMinecraft(key)); + + Assert.assertFalse("Unknown enchant name for " + key, bukkitById.getName().startsWith("UNKNOWN")); + + Assert.assertNotNull("Unknown target for " + key, bukkitById.getItemTarget()); + } + } +} diff --git a/src/test/java/org/bukkit/GameRuleTest.java b/src/test/java/org/bukkit/GameRuleTest.java new file mode 100644 index 000000000000..883f5081839c --- /dev/null +++ b/src/test/java/org/bukkit/GameRuleTest.java @@ -0,0 +1,56 @@ +package org.bukkit; + +import java.util.Map; +import java.util.TreeMap; +import net.minecraft.server.GameRules; +import org.junit.Assert; +import org.junit.Test; + +public class GameRuleTest { + + @Test + public void testBukkitRules() { + GameRule[] rules = GameRule.values(); + + for (GameRule rule : rules) { + GameRule registeredRule = GameRule.getByName(rule.getName()); + Assert.assertNotNull("Null GameRule", registeredRule); + Assert.assertEquals("Invalid GameRule equality", rule, registeredRule); + } + } + + @Test + public void testMinecraftRules() { + TreeMap minecraftRules = GameRules.getGameRules(); + + for (Map.Entry entry : minecraftRules.entrySet()) { + GameRule bukkitRule = GameRule.getByName(entry.getKey()); + + Assert.assertNotNull(bukkitRule); + Assert.assertEquals("Invalid GameRule Name", bukkitRule.getName(), entry.getKey()); + } + } + + @Test(expected = NullPointerException.class) + public void nullGameRuleName() { + GameRule.getByName(null); + } + + @Test + public void emptyGameRuleName() { + Assert.assertNull(GameRule.getByName("")); + } + + @Test + public void incorrectGameRuleName() { + Assert.assertNull(GameRule.getByName("doAnnounceAdvancements")); + Assert.assertNull(GameRule.getByName("sendCommandBlockFeedback")); + } + + @Test + public void invalidCasing() { + Assert.assertNull(GameRule.getByName("CommandBlockOutput")); + Assert.assertNull(GameRule.getByName("spAwnRadius")); + Assert.assertNull(GameRule.getByName("rand0mTickSpeEd")); + } +} diff --git a/src/test/java/org/bukkit/LegacyTest.java b/src/test/java/org/bukkit/LegacyTest.java new file mode 100644 index 000000000000..b441d40d9dcb --- /dev/null +++ b/src/test/java/org/bukkit/LegacyTest.java @@ -0,0 +1,92 @@ +package org.bukkit; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import org.bukkit.craftbukkit.util.CraftLegacy; +import org.bukkit.material.MaterialData; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Assert; +import org.junit.Test; + +public class LegacyTest extends AbstractTestingBase { + + private final Set INVALIDATED_MATERIALS = new HashSet<>(Arrays.asList(Material.ACACIA_BUTTON, Material.ACACIA_PRESSURE_PLATE, Material.ACACIA_TRAPDOOR, Material.AIR, Material.ATTACHED_MELON_STEM, Material.ATTACHED_PUMPKIN_STEM, + Material.BIRCH_BUTTON, Material.BIRCH_PRESSURE_PLATE, Material.BIRCH_TRAPDOOR, Material.BLACK_WALL_BANNER, Material.BLUE_WALL_BANNER, Material.BROWN_WALL_BANNER, Material.BUBBLE_COLUMN, Material.CAVE_AIR, Material.CREEPER_WALL_HEAD, + Material.CYAN_WALL_BANNER, Material.DARK_OAK_BUTTON, Material.DARK_OAK_PRESSURE_PLATE, Material.DARK_OAK_TRAPDOOR, Material.DARK_PRISMARINE_SLAB, Material.DARK_PRISMARINE_STAIRS, Material.DEBUG_STICK, Material.DONKEY_SPAWN_EGG, + Material.DRAGON_WALL_HEAD, Material.DRIED_KELP, Material.DRIED_KELP_BLOCK, Material.ELDER_GUARDIAN_SPAWN_EGG, Material.EVOKER_SPAWN_EGG, Material.GRAY_WALL_BANNER, Material.GREEN_WALL_BANNER, Material.HUSK_SPAWN_EGG, + Material.JUNGLE_BUTTON, Material.JUNGLE_PRESSURE_PLATE, Material.JUNGLE_TRAPDOOR, Material.KELP, Material.KELP_PLANT, Material.LIGHT_BLUE_WALL_BANNER, Material.LIGHT_GRAY_WALL_BANNER, Material.LIME_WALL_BANNER, Material.LLAMA_SPAWN_EGG, + Material.MAGENTA_WALL_BANNER, Material.MULE_SPAWN_EGG, Material.ORANGE_WALL_BANNER, Material.PARROT_SPAWN_EGG, Material.PHANTOM_SPAWN_EGG, Material.PINK_WALL_BANNER, Material.PLAYER_WALL_HEAD, Material.POLAR_BEAR_SPAWN_EGG, + Material.POTTED_ACACIA_SAPLING, Material.POTTED_ALLIUM, Material.POTTED_AZURE_BLUET, Material.POTTED_BIRCH_SAPLING, Material.POTTED_BLUE_ORCHID, Material.POTTED_BROWN_MUSHROOM, Material.POTTED_DANDELION, Material.POTTED_DARK_OAK_SAPLING, + Material.POTTED_DEAD_BUSH, Material.POTTED_FERN, Material.POTTED_JUNGLE_SAPLING, Material.POTTED_OAK_SAPLING, Material.POTTED_ORANGE_TULIP, Material.POTTED_OXEYE_DAISY, Material.POTTED_PINK_TULIP, Material.POTTED_POPPY, + Material.POTTED_RED_MUSHROOM, Material.POTTED_RED_TULIP, Material.POTTED_SPRUCE_SAPLING, Material.POTTED_WHITE_TULIP, Material.PRISMARINE_BRICK_SLAB, Material.PRISMARINE_BRICK_STAIRS, Material.PRISMARINE_SLAB, Material.PRISMARINE_STAIRS, + Material.PUMPKIN, Material.PURPLE_WALL_BANNER, Material.RED_WALL_BANNER, Material.SEAGRASS, Material.SKELETON_HORSE_SPAWN_EGG, Material.SKELETON_WALL_SKULL, Material.SPRUCE_BUTTON, Material.SPRUCE_PRESSURE_PLATE, Material.SPRUCE_TRAPDOOR, + Material.STRAY_SPAWN_EGG, Material.STRIPPED_ACACIA_LOG, Material.STRIPPED_BIRCH_LOG, Material.STRIPPED_DARK_OAK_LOG, Material.STRIPPED_JUNGLE_LOG, Material.STRIPPED_OAK_LOG, Material.STRIPPED_SPRUCE_LOG, Material.TALL_SEAGRASS, + Material.TRIDENT, Material.TURTLE_EGG, Material.TURTLE_HELMET, Material.SCUTE, Material.TURTLE_SPAWN_EGG, Material.VEX_SPAWN_EGG, Material.VINDICATOR_SPAWN_EGG, Material.VOID_AIR, Material.WHITE_BED, + Material.WITHER_SKELETON_SPAWN_EGG, Material.WITHER_SKELETON_WALL_SKULL, Material.YELLOW_WALL_BANNER, Material.ZOMBIE_HORSE_SPAWN_EGG, Material.ZOMBIE_VILLAGER_SPAWN_EGG, Material.ZOMBIE_WALL_HEAD, + Material.COD_BUCKET, Material.COD_SPAWN_EGG, Material.PUFFERFISH_BUCKET, Material.PUFFERFISH_SPAWN_EGG, Material.SALMON_BUCKET, Material.SALMON_SPAWN_EGG, + Material.TROPICAL_FISH_BUCKET, Material.DROWNED_SPAWN_EGG, Material.SHULKER_BOX, Material.TROPICAL_FISH_SPAWN_EGG, + Material.BLUE_ICE, Material.BRAIN_CORAL, Material.BRAIN_CORAL_BLOCK, Material.BRAIN_CORAL_FAN, Material.BUBBLE_CORAL, Material.BUBBLE_CORAL_BLOCK, Material.BUBBLE_CORAL_FAN, Material.CONDUIT, Material.DEAD_BRAIN_CORAL_BLOCK, + Material.DEAD_BUBBLE_CORAL_BLOCK, Material.DEAD_FIRE_CORAL_BLOCK, Material.DEAD_HORN_CORAL_BLOCK, Material.DEAD_TUBE_CORAL_BLOCK, Material.DOLPHIN_SPAWN_EGG, Material.FIRE_CORAL, Material.FIRE_CORAL_BLOCK, Material.FIRE_CORAL_FAN, + Material.HEART_OF_THE_SEA, Material.HORN_CORAL, Material.HORN_CORAL_BLOCK, Material.HORN_CORAL_FAN, Material.NAUTILUS_SHELL, Material.PHANTOM_MEMBRANE, Material.SEA_PICKLE, Material.TUBE_CORAL, Material.TUBE_CORAL_BLOCK, + Material.TUBE_CORAL_FAN, Material.STRIPPED_ACACIA_WOOD, Material.STRIPPED_BIRCH_WOOD, Material.STRIPPED_DARK_OAK_WOOD, Material.STRIPPED_JUNGLE_WOOD, Material.STRIPPED_OAK_WOOD, Material.STRIPPED_SPRUCE_WOOD, + Material.ACACIA_WOOD, Material.BIRCH_WOOD, Material.DARK_OAK_WOOD, Material.JUNGLE_WOOD, Material.OAK_WOOD, Material.SPRUCE_WOOD, + Material.TUBE_CORAL_WALL_FAN, Material.BRAIN_CORAL_WALL_FAN, Material.BUBBLE_CORAL_WALL_FAN, Material.FIRE_CORAL_WALL_FAN, Material.HORN_CORAL_WALL_FAN, Material.DEAD_TUBE_CORAL_WALL_FAN, Material.DEAD_BRAIN_CORAL_WALL_FAN, + Material.DEAD_BUBBLE_CORAL_WALL_FAN, Material.DEAD_FIRE_CORAL_WALL_FAN, Material.DEAD_HORN_CORAL_WALL_FAN, Material.DEAD_TUBE_CORAL_FAN, Material.DEAD_BRAIN_CORAL_FAN, Material.DEAD_BUBBLE_CORAL_FAN, Material.DEAD_FIRE_CORAL_FAN, + Material.DEAD_HORN_CORAL_FAN, Material.DEAD_BRAIN_CORAL, Material.DEAD_BUBBLE_CORAL, Material.DEAD_FIRE_CORAL, Material.DEAD_HORN_CORAL, Material.DEAD_TUBE_CORAL, + // + Material.LEGACY_AIR, Material.LEGACY_DEAD_BUSH, Material.LEGACY_BURNING_FURNACE, Material.LEGACY_WALL_SIGN, Material.LEGACY_REDSTONE_TORCH_OFF, Material.LEGACY_SKULL, Material.LEGACY_REDSTONE_COMPARATOR_ON, Material.LEGACY_WALL_BANNER, Material.LEGACY_MONSTER_EGG)); + + private final Set INVERSION_FAILS = new HashSet<>(Arrays.asList(Material.LEGACY_DOUBLE_STEP, Material.LEGACY_GLOWING_REDSTONE_ORE, Material.LEGACY_DIODE_BLOCK_ON, Material.LEGACY_REDSTONE_LAMP_ON, Material.LEGACY_WOOD_DOUBLE_STEP, + Material.LEGACY_DAYLIGHT_DETECTOR_INVERTED, Material.LEGACY_DOUBLE_STONE_SLAB2, Material.LEGACY_PURPUR_DOUBLE_SLAB, Material.LEGACY_WHEAT, Material.LEGACY_SIGN, Material.LEGACY_WOOD_DOOR, Material.LEGACY_IRON_DOOR, Material.LEGACY_SUGAR_CANE, + Material.LEGACY_CAKE, Material.LEGACY_BED, Material.LEGACY_DIODE, Material.LEGACY_NETHER_STALK, Material.LEGACY_BREWING_STAND_ITEM, Material.LEGACY_CAULDRON_ITEM, Material.LEGACY_REDSTONE_COMPARATOR, Material.LEGACY_SPRUCE_DOOR_ITEM, + Material.LEGACY_BIRCH_DOOR_ITEM, Material.LEGACY_JUNGLE_DOOR_ITEM, Material.LEGACY_ACACIA_DOOR_ITEM, Material.LEGACY_DARK_OAK_DOOR_ITEM, Material.LEGACY_STATIONARY_LAVA, Material.LEGACY_STATIONARY_WATER)); + + @Test + public void toLegacyMaterial() { + for (Material material : Material.values()) { + if (!INVALIDATED_MATERIALS.contains(material) && !material.isLegacy()) { + MaterialData converted = CraftLegacy.toLegacyData(material); + + Assert.assertNotEquals("Could not toLegacy " + material, Material.LEGACY_AIR, converted.getItemType()); + + if (!INVALIDATED_MATERIALS.contains(converted.getItemType())) { + Assert.assertNotEquals("Could not fromLegacy(toLegacy) " + converted + "(" + material + ")", Material.AIR, CraftLegacy.fromLegacy(converted)); + } + if (!INVERSION_FAILS.contains(material)) { + Assert.assertEquals("Could not fromLegacy(toLegacy) " + converted + "(" + material + ")", material, CraftLegacy.fromLegacy(converted)); + } + } + } + + Assert.assertEquals("Could not toLegacy Air", Material.LEGACY_AIR, CraftLegacy.toLegacy(Material.AIR)); + } + + @Test + public void fromLegacyMaterial() { + for (Material material : Material.values()) { + if (!INVALIDATED_MATERIALS.contains(material) && material.isLegacy()) { + Material converted = CraftLegacy.fromLegacy(material); + Assert.assertNotEquals("Could not fromLegacy " + material, Material.AIR, converted); + + Assert.assertNotEquals("Could not toLegacy(fromLegacy) " + converted + "(" + material + ")", Material.AIR, CraftLegacy.toLegacy(converted)); + if (!INVERSION_FAILS.contains(material)) { + Assert.assertEquals("Could not toLegacy(fromLegacy) " + converted + "(" + material + ")", material, CraftLegacy.toLegacy(converted)); + } + } + } + + Assert.assertEquals("Could not fromLegacy Air", Material.AIR, CraftLegacy.fromLegacy(Material.LEGACY_AIR)); + } + + @Test + public void testRestricted() { + for (Material material : CraftLegacy.values()) { + Assert.assertTrue("Must iterate only legacy materials", material.isLegacy()); + } + + for (Material material : CraftLegacy.modern_values()) { + Assert.assertFalse("Must iterate only modern materials", material.isLegacy()); + } + } +} diff --git a/src/test/java/org/bukkit/LootTablesTest.java b/src/test/java/org/bukkit/LootTablesTest.java new file mode 100644 index 000000000000..8d2cbb44ccfc --- /dev/null +++ b/src/test/java/org/bukkit/LootTablesTest.java @@ -0,0 +1,22 @@ +package org.bukkit; + +import org.bukkit.loot.LootTable; +import org.bukkit.loot.LootTables; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Assert; +import org.junit.Test; + +public class LootTablesTest extends AbstractTestingBase { + + @Test + public void testLootTablesEnumExists() { + LootTables[] tables = LootTables.values(); + + for (LootTables table : tables) { + LootTable lootTable = Bukkit.getLootTable(table.getKey()); + + Assert.assertNotNull("Unknown LootTable " + table.getKey(), lootTable); + Assert.assertEquals(lootTable.getKey(), table.getKey()); + } + } +} diff --git a/src/test/java/org/bukkit/MaterialTest.java b/src/test/java/org/bukkit/MaterialTest.java new file mode 100644 index 000000000000..8c9724478f6e --- /dev/null +++ b/src/test/java/org/bukkit/MaterialTest.java @@ -0,0 +1,50 @@ +package org.bukkit; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +import java.util.Collections; +import java.util.Map; + +import net.minecraft.server.IRegistry; +import net.minecraft.server.Item; +import net.minecraft.server.MinecraftKey; + +import org.bukkit.support.AbstractTestingBase; +import org.junit.Test; + +import com.google.common.collect.Maps; +import java.util.Iterator; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; + +public class MaterialTest extends AbstractTestingBase { + + @Test + public void verifyMapping() { + Map materials = Maps.newHashMap(); + for (Material material : Material.values()) { + if (INVALIDATED_MATERIALS.contains(material)) { + continue; + } + + materials.put(CraftMagicNumbers.key(material), material); + } + + Iterator items = IRegistry.ITEM.iterator(); + + while (items.hasNext()) { + Item item = items.next(); + if (item == null) continue; + + MinecraftKey id = IRegistry.ITEM.getKey(item); + String name = item.getName(); + + Material material = materials.remove(id); + + assertThat("Missing " + name + "(" + id + ")", material, is(not(nullValue()))); + assertNotNull("No item mapping for " + name, CraftMagicNumbers.getMaterial(item)); + } + + assertThat(materials, is(Collections.EMPTY_MAP)); + } +} diff --git a/src/test/java/org/bukkit/NibbleArrayTest.java b/src/test/java/org/bukkit/NibbleArrayTest.java new file mode 100644 index 000000000000..0131ff04a3dd --- /dev/null +++ b/src/test/java/org/bukkit/NibbleArrayTest.java @@ -0,0 +1,29 @@ +package org.bukkit; + +import java.util.Random; +import net.minecraft.server.NibbleArray; +import org.junit.Assert; +import org.junit.Test; + +public class NibbleArrayTest { + + private static final int NIBBLE_SIZE = 4096; + + @Test + public void testNibble() { + Random random = new Random(); + byte[] classic = new byte[NIBBLE_SIZE]; + NibbleArray nibble = new NibbleArray(); + + for (int i = 0; i < classic.length; i++) { + byte number = (byte) (random.nextInt() & 0xF); + + classic[i] = number; + nibble.a(i, number); + } + + for (int i = 0; i < classic.length; i++) { + Assert.assertEquals("Nibble array mismatch", classic[i], nibble.a(i)); + } + } +} diff --git a/src/test/java/org/bukkit/ParticleTest.java b/src/test/java/org/bukkit/ParticleTest.java new file mode 100644 index 000000000000..829dfa112e6a --- /dev/null +++ b/src/test/java/org/bukkit/ParticleTest.java @@ -0,0 +1,35 @@ +package org.bukkit; + +import net.minecraft.server.IRegistry; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.CraftParticle; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.inventory.ItemStack; +import org.bukkit.material.MaterialData; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Assert; +import org.junit.Test; + +public class ParticleTest extends AbstractTestingBase { + + @Test + public void verifyMapping() { + for (Particle bukkit : Particle.values()) { + Object data = null; + if (bukkit.getDataType().equals(ItemStack.class)) { + data = new ItemStack(Material.STONE); + } else if (bukkit.getDataType() == MaterialData.class) { + data = new MaterialData(Material.LEGACY_STONE); + } else if (bukkit.getDataType() == Particle.DustOptions.class) { + data = new Particle.DustOptions(Color.BLACK, 0); + } else if (bukkit.getDataType() == BlockData.class) { + data = CraftBlockData.newData(Material.STONE, ""); + } + + Assert.assertNotNull("Missing Bukkit->NMS particle mapping for " + bukkit, CraftParticle.toNMS(bukkit, data)); + } + for (net.minecraft.server.Particle nms : (Iterable>) IRegistry.PARTICLE_TYPE) { // Eclipse fail + Assert.assertNotNull("Missing NMS->Bukkit particle mapping for " + nms, CraftParticle.toBukkit(nms)); + } + } +} diff --git a/src/test/java/org/bukkit/PerMaterialTest.java b/src/test/java/org/bukkit/PerMaterialTest.java new file mode 100644 index 000000000000..7967c4c39879 --- /dev/null +++ b/src/test/java/org/bukkit/PerMaterialTest.java @@ -0,0 +1,239 @@ +package org.bukkit; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +import java.util.List; + +import net.minecraft.server.BlockFalling; +import net.minecraft.server.BlockFire; +import net.minecraft.server.Item; +import net.minecraft.server.ItemFood; +import net.minecraft.server.ItemRecord; +import net.minecraft.server.TileEntityFurnace; + +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.inventory.ItemStack; +import org.bukkit.support.AbstractTestingBase; +import org.bukkit.support.Util; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.collect.Lists; +import java.util.Map; +import net.minecraft.server.Block; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.Blocks; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.EnumDirection; +import net.minecraft.server.EnumHand; +import net.minecraft.server.IBlockData; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.enchantments.EnchantmentTarget; + +@RunWith(Parameterized.class) +public class PerMaterialTest extends AbstractTestingBase { + private static Map fireValues; + + @BeforeClass + public static void getFireValues() { + fireValues = Util.getInternalState(BlockFire.class, Blocks.FIRE, "flameChances"); + } + + @Parameters(name= "{index}: {0}") + public static List data() { + List list = Lists.newArrayList(); + for (Material material : Material.values()) { + if (!material.isLegacy()) { + list.add(new Object[] {material}); + } + } + return list; + } + + @Parameter public Material material; + + @Test + public void isBlock() { + if (material != Material.AIR && material != Material.CAVE_AIR && material != Material.VOID_AIR) { + assertThat(material.isBlock(), is(not(CraftMagicNumbers.getBlock(material) == null))); + } + } + + @Test + public void isSolid() { + if (material == Material.AIR) { + assertFalse(material.isSolid()); + } else if (material.isBlock()) { + assertThat(material.isSolid(), is(CraftMagicNumbers.getBlock(material).getBlockData().getMaterial().isSolid())); + } else { + assertFalse(material.isSolid()); + } + } + + @Test + public void isEdible() { + assertThat(material.isEdible(), is(CraftMagicNumbers.getItem(material) instanceof ItemFood)); + } + + @Test + public void isRecord() { + assertThat(material.isRecord(), is(CraftMagicNumbers.getItem(material) instanceof ItemRecord)); + } + + @Test + public void maxDurability() { + if (INVALIDATED_MATERIALS.contains(material)) return; + + if (material == Material.AIR) { + assertThat((int) material.getMaxDurability(), is(0)); + } else if (material.isBlock()){ + Item item = CraftMagicNumbers.getItem(material); + assertThat((int) material.getMaxDurability(), is(item.getMaxDurability())); + } + } + + @Test + public void maxStackSize() { + if (INVALIDATED_MATERIALS.contains(material)) return; + + final ItemStack bukkit = new ItemStack(material); + final CraftItemStack craft = CraftItemStack.asCraftCopy(bukkit); + if (material == Material.AIR) { + final int MAX_AIR_STACK = 0 /* Why can't I hold all of these AIR? */; + assertThat(material.getMaxStackSize(), is(MAX_AIR_STACK)); + assertThat(bukkit.getMaxStackSize(), is(MAX_AIR_STACK)); + assertThat(craft.getMaxStackSize(), is(MAX_AIR_STACK)); + } else { + assertThat(material.getMaxStackSize(), is(CraftMagicNumbers.getItem(material).getMaxStackSize())); + assertThat(bukkit.getMaxStackSize(), is(material.getMaxStackSize())); + assertThat(craft.getMaxStackSize(), is(material.getMaxStackSize())); + } + } + + @Test + public void isTransparent() { + if (material == Material.AIR) { + assertTrue(material.isTransparent()); + } else if (material.isBlock()) { + // assertThat(material.isTransparent(), is(not(CraftMagicNumbers.getBlock(material).getBlockData().getMaterial().blocksLight()))); // PAIL: not unit testable anymore (17w50a) + } else { + assertFalse(material.isTransparent()); + } + } + + @Test + public void isFlammable() { + if (material != Material.AIR && material.isBlock()) { + assertThat(material.isFlammable(), is(CraftMagicNumbers.getBlock(material).getBlockData().getMaterial().isBurnable())); + } else { + assertFalse(material.isFlammable()); + } + } + + @Test + public void isBurnable() { + if (material.isBlock()) { + Block block = CraftMagicNumbers.getBlock(material); + assertThat(material.isBurnable(), is(fireValues.containsKey(block) && fireValues.get(block) > 0)); + } else { + assertFalse(material.isBurnable()); + } + } + + @Test + public void isFuel() { + assertThat(material.isFuel(), is(TileEntityFurnace.isFuel(new net.minecraft.server.ItemStack(CraftMagicNumbers.getItem(material))))); + } + + @Test + public void isOccluding() { + if (material.isBlock()) { + assertThat(material.isOccluding(), is(CraftMagicNumbers.getBlock(material).isOccluding(CraftMagicNumbers.getBlock(material).getBlockData()))); + } else { + assertFalse(material.isOccluding()); + } + } + + @Test + public void hasGravity() { + if (material.isBlock()) { + assertThat(material.hasGravity(), is(CraftMagicNumbers.getBlock(material) instanceof BlockFalling)); + } else { + assertFalse(material.hasGravity()); + } + } + + @Test + public void usesDurability() { + if (!material.isBlock()) { + assertThat(EnchantmentTarget.BREAKABLE.includes(material), is(CraftMagicNumbers.getItem(material).usesDurability())); + } else { + assertFalse(EnchantmentTarget.BREAKABLE.includes(material)); + } + } + + @Test + public void testDurability() { + if (!material.isBlock()) { + assertThat(material.getMaxDurability(), is((short) CraftMagicNumbers.getItem(material).getMaxDurability())); + } else { + assertThat(material.getMaxDurability(), is((short) 0)); + } + } + + @Test + public void testBlock() { + if (material == Material.AIR) { + assertTrue(material.isBlock()); + } else { + assertThat(material.isBlock(), is(equalTo(CraftMagicNumbers.getBlock(material) != null))); + } + } + + @Test + public void testItem() { + if (material == Material.AIR) { + assertTrue(material.isItem()); + } else { + assertThat(material.isItem(), is(equalTo(CraftMagicNumbers.getItem(material) != null))); + } + } + + @Test + public void testInteractable() throws ReflectiveOperationException { + if (material.isBlock()) { + assertThat(material.isInteractable(), + is(!CraftMagicNumbers.getBlock(material).getClass() + .getMethod("interact", IBlockData.class, net.minecraft.server.World.class, BlockPosition.class, EntityHuman.class, EnumHand.class, EnumDirection.class, float.class, float.class, float.class) + .getDeclaringClass().equals(Block.class))); + } else { + assertFalse(material.isInteractable()); + } + } + + @Test + public void testBlockHardness() { + if (material.isBlock()) { + assertThat(material.getHardness(), is(CraftMagicNumbers.getBlock(material).strength)); + } + } + + @Test + public void testBlastResistance() { + if (material.isBlock()) { + assertThat(material.getBlastResistance(), is(CraftMagicNumbers.getBlock(material).getDurability())); + } + } + + @Test + public void testBlockDataCreation() { + if (material.isBlock()) { + assertNotNull(material.createBlockData()); + } + } +} diff --git a/src/test/java/org/bukkit/SoundTest.java b/src/test/java/org/bukkit/SoundTest.java new file mode 100644 index 000000000000..da8c3dcbb60d --- /dev/null +++ b/src/test/java/org/bukkit/SoundTest.java @@ -0,0 +1,42 @@ +package org.bukkit; + +import net.minecraft.server.IRegistry; +import net.minecraft.server.MinecraftKey; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +import org.bukkit.craftbukkit.CraftSound; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Test; + +public class SoundTest extends AbstractTestingBase { + + @Test + public void testGetSound() { + for (Sound sound : Sound.values()) { + assertThat(sound.name(), CraftSound.getSound(sound), is(not(nullValue()))); + } + } + + @Test + public void testReverse() { + for (MinecraftKey effect : IRegistry.SOUND_EVENT.keySet()) { + assertNotNull(effect + "", Sound.valueOf(effect.getKey().replace('.', '_').toUpperCase(java.util.Locale.ENGLISH))); + } + } + + @Test + public void testCategory() { + for (SoundCategory category : SoundCategory.values()) { + assertNotNull(category + "", net.minecraft.server.SoundCategory.valueOf(category.name())); + } + } + + @Test + public void testCategoryReverse() { + for (net.minecraft.server.SoundCategory category : net.minecraft.server.SoundCategory.values()) { + assertNotNull(category + "", SoundCategory.valueOf(category.name())); + } + } +} diff --git a/src/test/java/org/bukkit/StatisticsAndAchievementsTest.java b/src/test/java/org/bukkit/StatisticsAndAchievementsTest.java new file mode 100644 index 000000000000..4edbc126cf66 --- /dev/null +++ b/src/test/java/org/bukkit/StatisticsAndAchievementsTest.java @@ -0,0 +1,60 @@ +package org.bukkit; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +import net.minecraft.server.EntityTypes; +import net.minecraft.server.IRegistry; +import net.minecraft.server.StatisticWrapper; + +import org.bukkit.entity.EntityType; +import org.bukkit.craftbukkit.CraftStatistic; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Test; + +import com.google.common.collect.HashMultiset; + +public class StatisticsAndAchievementsTest extends AbstractTestingBase { + + @Test + @SuppressWarnings("unchecked") + public void verifyEntityMapping() throws Throwable { + for (Statistic statistic : Statistic.values()) { + if (statistic.getType() == Statistic.Type.ENTITY) { + for (EntityType entity : EntityType.values()) { + if (entity.getName() != null) { + assertNotNull(statistic + " missing for " + entity, CraftStatistic.getEntityStatistic(statistic, entity)); + } + } + } + } + } + + @Test + @SuppressWarnings("unchecked") + public void verifyStatisticMapping() throws Throwable { + HashMultiset statistics = HashMultiset.create(); + for (StatisticWrapper wrapper : (Iterable>) IRegistry.STATS) { // Eclipse fail + for (Object child : wrapper.a()) { + net.minecraft.server.Statistic statistic = wrapper.b(child); + String message = String.format("org.bukkit.Statistic is missing: '%s'", statistic); + + Statistic subject = CraftStatistic.getBukkitStatistic(statistic); + assertThat(message, subject, is(not(nullValue()))); + + if (wrapper.a() == IRegistry.BLOCK || wrapper.a() == IRegistry.ITEM) { + assertNotNull("Material type map missing for " + child, CraftStatistic.getMaterialFromStatistic(statistic)); + } else if (wrapper.a() == IRegistry.ENTITY_TYPE) { + assertNotNull("Entity type map missing for " + EntityTypes.getName((EntityTypes) child), CraftStatistic.getEntityTypeFromStatistic((net.minecraft.server.Statistic>) statistic)); + } + + statistics.add(subject); + } + } + + for (Statistic statistic : Statistic.values()) { + String message = String.format("org.bukkit.Statistic.%s does not have a corresponding minecraft statistic", statistic.name()); + assertThat(message, statistics.remove(statistic, statistics.count(statistic)), is(greaterThan(0))); + } + } +} diff --git a/src/test/java/org/bukkit/StructureTypeTest.java b/src/test/java/org/bukkit/StructureTypeTest.java new file mode 100644 index 000000000000..ad1c7a4d7ddf --- /dev/null +++ b/src/test/java/org/bukkit/StructureTypeTest.java @@ -0,0 +1,45 @@ +package org.bukkit; + +import java.util.Map; +import net.minecraft.server.WorldGenFactory; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * This test makes sure that Bukkit always has Minecraft structure types up to + * date. + */ +public class StructureTypeTest extends AbstractTestingBase { + + private static Map structures; + + @BeforeClass + public static void setUp() { + structures = StructureType.getStructureTypes(); + } + + @Test + public void testMinecraftToBukkit() { + for (String key : WorldGenFactory.structureStartMap.keySet()) { + Assert.assertNotNull(structures.get(key)); + } + } + + @Test + public void testBukkit() { + for (Map.Entry entry : structures.entrySet()) { + Assert.assertNotNull(StructureType.getStructureTypes().get(entry.getKey())); + Assert.assertNotNull(StructureType.getStructureTypes().get(entry.getValue().getName())); + } + } + + @Test + public void testBukkitToMinecraft() { + for (Map.Entry entry : structures.entrySet()) { + Assert.assertNotNull(WorldGenFactory.structureStartMap.get(entry.getKey())); + Assert.assertNotNull(WorldGenFactory.structureStartMap.get(entry.getValue().getName())); + } + } +} diff --git a/src/test/java/org/bukkit/WorldTypeTest.java b/src/test/java/org/bukkit/WorldTypeTest.java new file mode 100644 index 000000000000..4d35a1064bfa --- /dev/null +++ b/src/test/java/org/bukkit/WorldTypeTest.java @@ -0,0 +1,19 @@ +package org.bukkit; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +import net.minecraft.server.WorldType; +import org.junit.Test; + +public class WorldTypeTest { + @Test + public void testTypes() { + for (WorldType type : WorldType.types) { + if (type == null) continue; + if (type == WorldType.DEBUG_ALL_BLOCK_STATES) continue; // Doesn't work anyway + + assertThat(type.name() + " has no Bukkit world", org.bukkit.WorldType.getByName(type.name()), is(not(nullValue()))); + } + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/generator/ChunkDataTest.java b/src/test/java/org/bukkit/craftbukkit/generator/ChunkDataTest.java new file mode 100644 index 000000000000..c5194e3802ea --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/generator/ChunkDataTest.java @@ -0,0 +1,81 @@ +package org.bukkit.craftbukkit.generator; + +import org.bukkit.Material; +import org.bukkit.block.data.BlockData; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Test; +import static org.junit.Assert.*; + +public class ChunkDataTest extends AbstractTestingBase { + + private static final BlockData RED_WOOL = Material.RED_WOOL.createBlockData(); + private static final BlockData AIR = Material.AIR.createBlockData(); + + private boolean testSetBlock(CraftChunkData data, int x, int y, int z, BlockData type, BlockData expected) { + data.setBlock(x, y, z, type); + return expected.equals(data.getBlockData(x, y, z)) && expected.getMaterial().equals(data.getType(x, y, z)); + } + + private void testSetRegion(CraftChunkData data, int minx, int miny, int minz, int maxx, int maxy, int maxz, BlockData type) { + data.setRegion(minx, miny, minz, maxx, maxy, maxz, type); + for (int y = 0; y < data.getMaxHeight(); y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + boolean inRegion = miny <= y && y < maxy && minx <= x && x < maxx && minz <= z && z < maxz; + if (inRegion != type.equals(data.getBlockData(x, y, z))) { + throw new IllegalStateException( + "setRegion(" + minx + ", " + miny + ", " + minz + ", " + maxx + ", " + maxy + ", " + maxz + ", " + type + ")" + + "-> block at " + x + ", " + y + ", " + z + " is " + data.getBlockData(x, y, z)); + } + } + } + } + } + + @Test + public void testMaxHeight() { + CraftChunkData data = new CraftChunkData(128); + assertTrue("Could not set block above max height", testSetBlock(data, 0, 128, 0, RED_WOOL, AIR)); + assertTrue("Could set block below max height", testSetBlock(data, 0, 127, 0, RED_WOOL, RED_WOOL)); + } + + @Test + public void testBoundsCheckingSingle() { + CraftChunkData data = new CraftChunkData(256); + assertTrue("Can set block inside chunk bounds", testSetBlock(data, 0, 0, 0, RED_WOOL, RED_WOOL)); + assertTrue("Can set block inside chunk bounds", testSetBlock(data, 15, 255, 15, RED_WOOL, RED_WOOL)); + assertTrue("Can no set block outside chunk bounds", testSetBlock(data, -1, 0, 0, RED_WOOL, AIR)); + assertTrue("Can no set block outside chunk bounds", testSetBlock(data, 0, -1, 0, RED_WOOL, AIR)); + assertTrue("Can no set block outside chunk bounds", testSetBlock(data, 0, 0, -1, RED_WOOL, AIR)); + assertTrue("Can no set block outside chunk bounds", testSetBlock(data, 16, 0, 0, RED_WOOL, AIR)); + assertTrue("Can no set block outside chunk bounds", testSetBlock(data, 0, 256, 0, RED_WOOL, AIR)); + assertTrue("Can no set block outside chunk bounds", testSetBlock(data, 0, 0, 16, RED_WOOL, AIR)); + } + + @Test + public void testSetRegion() { + CraftChunkData data = new CraftChunkData(256); + testSetRegion(data, -100, 0, -100, 0, 256, 0, RED_WOOL); // exclusively outside + testSetRegion(data, 16, 256, 16, 0, 0, 0, RED_WOOL); // minimum >= maximum + testSetRegion(data, 0, 0, 0, 0, 0, 0, RED_WOOL); // minimum == maximum + testSetRegion(data, 0, 0, 0, 16, 16, 16, RED_WOOL); // Whole Chunk Section + data.setRegion(0, 0, 0, 16, 256, 16, AIR); + testSetRegion(data, 0, 8, 0, 16, 24, 16, RED_WOOL); // Start middle of this section, end middle of next + data.setRegion(0, 0, 0, 16, 256, 16, AIR); + testSetRegion(data, 0, 4, 0, 16, 12, 16, RED_WOOL); // Start in this section, end in this section + data.setRegion(0, 0, 0, 16, 256, 16, AIR); + testSetRegion(data, 0, 0, 0, 16, 16, 1, RED_WOOL); // Whole Chunk Section + data.setRegion(0, 0, 0, 16, 256, 16, AIR); + testSetRegion(data, 0, 8, 0, 16, 24, 1, RED_WOOL); // Start middle of this section, end middle of next + data.setRegion(0, 0, 0, 16, 256, 16, AIR); + testSetRegion(data, 0, 4, 0, 16, 12, 1, RED_WOOL); // Start in this section, end in this section + data.setRegion(0, 0, 0, 16, 256, 16, AIR); + testSetRegion(data, 0, 0, 0, 1, 16, 1, RED_WOOL); // Whole Chunk Section + data.setRegion(0, 0, 0, 16, 256, 16, AIR); + testSetRegion(data, 0, 8, 0, 1, 24, 1, RED_WOOL); // Start middle of this section, end middle of next + data.setRegion(0, 0, 0, 16, 256, 16, AIR); + testSetRegion(data, 0, 4, 0, 1, 12, 1, RED_WOOL); // Start in this section, end in this section + data.setRegion(0, 0, 0, 16, 256, 16, AIR); + testSetRegion(data, 0, 0, 0, 1, 1, 1, RED_WOOL); // Set single block. + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/CompositeSerialization.java b/src/test/java/org/bukkit/craftbukkit/inventory/CompositeSerialization.java new file mode 100644 index 000000000000..2a0791bb98fe --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/CompositeSerialization.java @@ -0,0 +1,62 @@ +package org.bukkit.craftbukkit.inventory; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Material; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Test; + + +public class CompositeSerialization extends AbstractTestingBase { + + public YamlConfiguration getConfig() { + return new YamlConfiguration(); + } + + @Test + public void testSaveRestoreCompositeList() throws InvalidConfigurationException { + YamlConfiguration out = getConfig(); + + List stacks = new ArrayList(); + stacks.add(new ItemStack(Material.STONE)); + stacks.add(new ItemStack(Material.GRASS)); + stacks.add(new ItemStack(Material.DIRT)); + stacks.add(new ItemStack(Material.COBBLESTONE, 17)); + stacks.add(new ItemStack(Material.OAK_PLANKS, 63)); + stacks.add(new ItemStack(Material.OAK_SAPLING, 1, (short) 1)); + stacks.add(new ItemStack(Material.OAK_LEAVES, 32, (short) 2)); + + ItemStack item7 = new ItemStack(Material.IRON_SHOVEL); + item7.addUnsafeEnchantment(Enchantment.PROTECTION_FIRE, 1); + stacks.add(item7); + + ItemStack item8 = new ItemStack(Material.IRON_PICKAXE); + item8.addUnsafeEnchantment(Enchantment.PROTECTION_FALL, 2); + item8.addUnsafeEnchantment(Enchantment.PROTECTION_EXPLOSIONS, 1); + item8.addUnsafeEnchantment(Enchantment.PROTECTION_PROJECTILE, 5); + item8.addUnsafeEnchantment(Enchantment.OXYGEN, 4); + stacks.add(item8); + + out.set("composite-list.abc.def", stacks); + String yaml = out.saveToString(); + + YamlConfiguration in = new YamlConfiguration(); + in.loadFromString(yaml); + List raw = in.getList("composite-list.abc.def"); + + assertThat(stacks, hasSize(raw.size())); + + for (int i = 0; i < 9; i++) { + assertThat(String.valueOf(i), (Object) stacks.get(i), is((Object) raw.get(i))); + } + } +} + diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/FactoryItemMaterialTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/FactoryItemMaterialTest.java new file mode 100644 index 000000000000..1e5c3ef2d002 --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/FactoryItemMaterialTest.java @@ -0,0 +1,156 @@ +package org.bukkit.craftbukkit.inventory; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang.ArrayUtils; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class FactoryItemMaterialTest extends AbstractTestingBase { + static final ItemFactory factory = CraftItemFactory.instance(); + static final StringBuilder buffer = new StringBuilder(); + static final Material[] materials; + + static { + Material[] local_materials = Material.values(); + List list = new ArrayList(local_materials.length); + for (Material material : local_materials) { + if (INVALIDATED_MATERIALS.contains(material)) { + continue; + } + + list.add(material); + } + materials = list.toArray(new Material[list.size()]); + } + + static String name(Enum from, Enum to) { + if (from.getClass() == to.getClass()) { + return buffer.delete(0, Integer.MAX_VALUE).append(from.getClass().getName()).append(' ').append(from.name()).append(" to ").append(to.name()).toString(); + } + return buffer.delete(0, Integer.MAX_VALUE).append(from.getClass().getName()).append('(').append(from.name()).append(") to ").append(to.getClass().getName()).append('(').append(to.name()).append(')').toString(); + } + + @Parameters(name="Material[{index}]:{0}") + public static List data() { + List list = new ArrayList(); + for (Material material : materials) { + list.add(new Object[] {material}); + } + return list; + } + + @Parameter(0) public Material material; + + @Test + public void itemStack() { + ItemStack bukkitStack = new ItemStack(material); + CraftItemStack craftStack = CraftItemStack.asCraftCopy(bukkitStack); + ItemMeta meta = factory.getItemMeta(material); + if (meta == null) { + assertThat(material, is(Material.AIR)); + } else { + assertTrue(factory.isApplicable(meta, bukkitStack)); + assertTrue(factory.isApplicable(meta, craftStack)); + } + } + + @Test + public void generalCase() { + CraftMetaItem meta = (CraftMetaItem) factory.getItemMeta(material); + if (meta == null) { + assertThat(material, is(Material.AIR)); + } else { + assertTrue(factory.isApplicable(meta, material)); + assertTrue(meta.applicableTo(material)); + + meta = meta.clone(); + assertTrue(factory.isApplicable(meta, material)); + assertTrue(meta.applicableTo(material)); + } + } + + @Test + public void asMetaFor() { + final CraftMetaItem baseMeta = (CraftMetaItem) factory.getItemMeta(material); + if (baseMeta == null) { + assertThat(material, is(Material.AIR)); + return; + } + + for (Material other : materials) { + final ItemStack bukkitStack = new ItemStack(other); + final CraftItemStack craftStack = CraftItemStack.asCraftCopy(bukkitStack); + final CraftMetaItem otherMeta = (CraftMetaItem) factory.asMetaFor(baseMeta, other); + + final String testName = name(material, other); + + if (otherMeta == null) { + assertThat(testName, other, is(Material.AIR)); + continue; + } + + assertTrue(testName, factory.isApplicable(otherMeta, craftStack)); + assertTrue(testName, factory.isApplicable(otherMeta, bukkitStack)); + assertTrue(testName, factory.isApplicable(otherMeta, other)); + assertTrue(testName, otherMeta.applicableTo(other)); + } + } + + @Test + public void blankEqualities() { + if (material == Material.AIR) { + return; + } + final CraftMetaItem baseMeta = (CraftMetaItem) factory.getItemMeta(material); + final CraftMetaItem baseMetaClone = baseMeta.clone(); + + final ItemStack baseMetaStack = new ItemStack(material); + baseMetaStack.setItemMeta(baseMeta); + + assertThat(baseMeta, is(not(sameInstance(baseMetaStack.getItemMeta())))); + + assertTrue(factory.equals(baseMeta, null)); + assertTrue(factory.equals(null, baseMeta)); + + assertTrue(factory.equals(baseMeta, baseMetaClone)); + assertTrue(factory.equals(baseMetaClone, baseMeta)); + + assertThat(baseMeta, is(not(sameInstance(baseMetaClone)))); + + assertThat(baseMeta, is(baseMetaClone)); + assertThat(baseMetaClone, is(baseMeta)); + + for (Material other : materials) { + final String testName = name(material, other); + + final CraftMetaItem otherMeta = (CraftMetaItem) factory.asMetaFor(baseMetaClone, other); + + if (otherMeta == null) { + assertThat(testName, other, is(Material.AIR)); + continue; + } + + assertTrue(testName, factory.equals(baseMeta, otherMeta)); + assertTrue(testName, factory.equals(otherMeta, baseMeta)); + + assertThat(testName, baseMeta, is(otherMeta)); + assertThat(testName, otherMeta, is(baseMeta)); + + assertThat(testName, baseMeta.hashCode(), is(otherMeta.hashCode())); + } + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemFactoryTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemFactoryTest.java new file mode 100644 index 000000000000..18f3e1ff655c --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemFactoryTest.java @@ -0,0 +1,47 @@ +package org.bukkit.craftbukkit.inventory; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.HashSet; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import net.minecraft.server.IAnimal; +import net.minecraft.server.IAttribute; + +import org.bukkit.support.AbstractTestingBase; +import org.junit.Test; + +public class ItemFactoryTest extends AbstractTestingBase { + + @Test + public void testKnownAttributes() throws Throwable { + final ZipInputStream nmsZipStream = new ZipInputStream(IAnimal.class/* Magic class that isn't imported! */.getProtectionDomain().getCodeSource().getLocation().openStream()); + final Collection names = new HashSet(); + for (ZipEntry clazzEntry; (clazzEntry = nmsZipStream.getNextEntry()) != null; ) { + final String entryName = clazzEntry.getName(); + if (!(entryName.endsWith(".class") && entryName.startsWith("net/minecraft/server/"))) { + continue; + } + + final Class clazz = Class.forName(entryName.substring(0, entryName.length() - ".class".length()).replace('/', '.')); + assertThat(entryName, clazz, is(not(nullValue()))); + for (final Field field : clazz.getDeclaredFields()) { + if (IAttribute.class.isAssignableFrom(field.getType()) && Modifier.isStatic(field.getModifiers())) { + field.setAccessible(true); + final String attributeName = ((IAttribute) field.get(null)).getName(); + assertThat("Logical error: duplicate name `" + attributeName + "' in " + clazz.getName(), names.add(attributeName), is(true)); + assertThat(clazz.getName(), CraftItemFactory.KNOWN_NBT_ATTRIBUTE_NAMES, hasItem(attributeName)); + } + } + } + + nmsZipStream.close(); + + assertThat("Extra values detected", CraftItemFactory.KNOWN_NBT_ATTRIBUTE_NAMES, is(names)); + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaCloneTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaCloneTest.java new file mode 100644 index 000000000000..834fb297b4d8 --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaCloneTest.java @@ -0,0 +1,22 @@ +package org.bukkit.craftbukkit.inventory; + +import java.lang.reflect.Method; +import org.bukkit.Material; +import org.junit.Test; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +public class ItemMetaCloneTest { + + @Test + public void testClone() throws Throwable { + for (Material material : ItemStackTest.COMPOUND_MATERIALS) { + Class clazz = CraftItemFactory.instance().getItemMeta(material).getClass(); + + Method clone = clazz.getDeclaredMethod("clone"); + assertNotNull("Class " + clazz + " does not override clone()", clone); + assertThat("Class " + clazz + " clone return type does not match", clone.getReturnType(), is(equalTo(clazz))); + } + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaCustomValueTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaCustomValueTest.java new file mode 100644 index 000000000000..fa303d1fa7f4 --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaCustomValueTest.java @@ -0,0 +1,327 @@ +package org.bukkit.craftbukkit.inventory; + +import java.io.StringReader; +import java.lang.reflect.Array; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Map; +import java.util.UUID; +import net.minecraft.server.NBTBase; +import net.minecraft.server.NBTTagCompound; +import net.minecraft.server.NBTTagIntArray; +import net.minecraft.server.NBTTagList; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.craftbukkit.inventory.tags.CraftCustomItemTagContainer; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.tags.CustomItemTagContainer; +import org.bukkit.inventory.meta.tags.ItemTagAdapterContext; +import org.bukkit.inventory.meta.tags.ItemTagType; +import org.bukkit.support.AbstractTestingBase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; + +public class ItemMetaCustomValueTest extends AbstractTestingBase { + + private static NamespacedKey VALID_KEY; + + @Before + public void setup() { + VALID_KEY = new NamespacedKey("test", "validkey"); + } + + /* + Sets a test + */ + @Test(expected = IllegalArgumentException.class) + public void testSetNoAdapter() { + ItemMeta itemMeta = createNewItemMeta(); + itemMeta.getCustomTagContainer().setCustomTag(VALID_KEY, new PrimitiveTagType<>(boolean.class), true); + } + + /* + Contains a tag + */ + @Test(expected = IllegalArgumentException.class) + public void testHasNoAdapter() { + ItemMeta itemMeta = createNewItemMeta(); + itemMeta.getCustomTagContainer().setCustomTag(VALID_KEY, ItemTagType.INTEGER, 1); // We gotta set this so we at least try to compare it + itemMeta.getCustomTagContainer().hasCustomTag(VALID_KEY, new PrimitiveTagType<>(boolean.class)); + } + + /* + Getting a tag + */ + @Test(expected = IllegalArgumentException.class) + public void testGetNoAdapter() { + ItemMeta itemMeta = createNewItemMeta(); + itemMeta.getCustomTagContainer().setCustomTag(VALID_KEY, ItemTagType.INTEGER, 1); //We gotta set this so we at least try to compare it + itemMeta.getCustomTagContainer().getCustomTag(VALID_KEY, new PrimitiveTagType<>(boolean.class)); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetWrongType() { + ItemMeta itemMeta = createNewItemMeta(); + itemMeta.getCustomTagContainer().setCustomTag(VALID_KEY, ItemTagType.INTEGER, 1); + itemMeta.getCustomTagContainer().getCustomTag(VALID_KEY, ItemTagType.STRING); + } + + @Test + public void testDifferentNamespace() { + NamespacedKey namespacedKeyA = new NamespacedKey("plugin-a", "damage"); + NamespacedKey namespacedKeyB = new NamespacedKey("plugin-b", "damage"); + + ItemMeta meta = createNewItemMeta(); + meta.getCustomTagContainer().setCustomTag(namespacedKeyA, ItemTagType.LONG, 15L); + meta.getCustomTagContainer().setCustomTag(namespacedKeyB, ItemTagType.LONG, 160L); + + assertEquals(15L, (long) meta.getCustomTagContainer().getCustomTag(namespacedKeyA, ItemTagType.LONG)); + assertEquals(160L, (long) meta.getCustomTagContainer().getCustomTag(namespacedKeyB, ItemTagType.LONG)); + } + + private ItemMeta createNewItemMeta() { + return Bukkit.getItemFactory().getItemMeta(Material.DIAMOND_PICKAXE); + } + + private NamespacedKey requestKey(String keyName) { + return new NamespacedKey("test-plugin", keyName.toLowerCase()); + } + + /* + Removing a tag + */ + @Test + public void testNBTTagStoring() { + CraftMetaItem itemMeta = createComplexItemMeta(); + + NBTTagCompound compound = new NBTTagCompound(); + itemMeta.applyToItem(compound); + + assertEquals(itemMeta, new CraftMetaItem(compound)); + } + + @Test + public void testMapStoring() { + CraftMetaItem itemMeta = createComplexItemMeta(); + + Map serialize = itemMeta.serialize(); + assertEquals(itemMeta, new CraftMetaItem(serialize)); + } + + @Test + public void testYAMLStoring() { + ItemStack stack = new ItemStack(Material.DIAMOND); + CraftMetaItem meta = createComplexItemMeta(); + stack.setItemMeta(meta); + + YamlConfiguration configuration = new YamlConfiguration(); + configuration.set("testpath", stack); + + String configValue = configuration.saveToString(); + YamlConfiguration loadedConfig = YamlConfiguration.loadConfiguration(new StringReader(configValue)); + + assertEquals(stack, loadedConfig.getSerializable("testpath", ItemStack.class)); + assertNotEquals(new ItemStack(Material.DIAMOND), loadedConfig.getSerializable("testpath", ItemStack.class)); + } + + @Test + public void testCorrectType() { + ItemStack stack = new ItemStack(Material.DIAMOND); + CraftMetaItem meta = createComplexItemMeta(); + + meta.getCustomTagContainer().setCustomTag(requestKey("int"), ItemTagType.STRING, "1"); + meta.getCustomTagContainer().setCustomTag(requestKey("double"), ItemTagType.STRING, "1.33"); + stack.setItemMeta(meta); + + YamlConfiguration configuration = new YamlConfiguration(); + configuration.set("testpath", stack); + + String configValue = configuration.saveToString(); + YamlConfiguration loadedConfig = YamlConfiguration.loadConfiguration(new StringReader(configValue)); + ItemStack newStack = loadedConfig.getSerializable("testpath", ItemStack.class); + + assertTrue(newStack.getItemMeta().getCustomTagContainer().hasCustomTag(requestKey("int"), ItemTagType.STRING)); + assertEquals(newStack.getItemMeta().getCustomTagContainer().getCustomTag(requestKey("int"), ItemTagType.STRING), "1"); + + assertTrue(newStack.getItemMeta().getCustomTagContainer().hasCustomTag(requestKey("double"), ItemTagType.STRING)); + assertEquals(newStack.getItemMeta().getCustomTagContainer().getCustomTag(requestKey("double"), ItemTagType.STRING), "1.33"); + } + + private CraftMetaItem createComplexItemMeta() { + CraftMetaItem itemMeta = (CraftMetaItem) createNewItemMeta(); + itemMeta.setDisplayName("Item Display Name"); + + itemMeta.getCustomTagContainer().setCustomTag(requestKey("custom-long"), ItemTagType.LONG, 4L); //Add random primitive values + itemMeta.getCustomTagContainer().setCustomTag(requestKey("custom-byte-array"), ItemTagType.BYTE_ARRAY, new byte[]{ + 0, 1, 2, 10 + }); + itemMeta.getCustomTagContainer().setCustomTag(requestKey("custom-string"), ItemTagType.STRING, "Hello there world"); + itemMeta.getCustomTagContainer().setCustomTag(requestKey("custom-int"), ItemTagType.INTEGER, 3); + itemMeta.getCustomTagContainer().setCustomTag(requestKey("custom-double"), ItemTagType.DOUBLE, 3.123); + + CustomItemTagContainer innerContainer = itemMeta.getCustomTagContainer().getAdapterContext().newTagContainer(); //Add a inner container + innerContainer.setCustomTag(VALID_KEY, ItemTagType.LONG, 5L); + itemMeta.getCustomTagContainer().setCustomTag(requestKey("custom-inner-compound"), ItemTagType.TAG_CONTAINER, innerContainer); + + Map rawMap = ((CraftCustomItemTagContainer) itemMeta.getCustomTagContainer()).getRaw(); //Adds a tag list as well (even tho is has no API yet) + NBTTagList nbtList = new NBTTagList(); + nbtList.add(new NBTTagIntArray(Arrays.asList(1, 5, 3))); + nbtList.add(new NBTTagIntArray(Arrays.asList(42, 51))); + rawMap.put("nbttaglist", nbtList); + return itemMeta; + } + + /* + Test complex object storage + */ + @Test + public void storeUUIDOnItemTest() { + ItemMeta itemMeta = createNewItemMeta(); + UUIDItemTagType uuidItemTagType = new UUIDItemTagType(); + UUID uuid = UUID.fromString("434eea72-22a6-4c61-b5ef-945874a5c478"); + + itemMeta.getCustomTagContainer().setCustomTag(VALID_KEY, uuidItemTagType, uuid); + assertTrue(itemMeta.getCustomTagContainer().hasCustomTag(VALID_KEY, uuidItemTagType)); + assertEquals(uuid, itemMeta.getCustomTagContainer().getCustomTag(VALID_KEY, uuidItemTagType)); + } + + @Test + public void encapsulatedContainers() { + NamespacedKey innerKey = new NamespacedKey("plugin-a", "inner"); + + ItemMeta meta = createNewItemMeta(); + ItemTagAdapterContext context = meta.getCustomTagContainer().getAdapterContext(); + + CustomItemTagContainer thirdContainer = context.newTagContainer(); + thirdContainer.setCustomTag(VALID_KEY, ItemTagType.LONG, 3L); + + CustomItemTagContainer secondContainer = context.newTagContainer(); + secondContainer.setCustomTag(VALID_KEY, ItemTagType.LONG, 2L); + secondContainer.setCustomTag(innerKey, ItemTagType.TAG_CONTAINER, thirdContainer); + + meta.getCustomTagContainer().setCustomTag(VALID_KEY, ItemTagType.LONG, 1L); + meta.getCustomTagContainer().setCustomTag(innerKey, ItemTagType.TAG_CONTAINER, secondContainer); + + assertEquals(3L, meta.getCustomTagContainer() + .getCustomTag(innerKey, ItemTagType.TAG_CONTAINER) + .getCustomTag(innerKey, ItemTagType.TAG_CONTAINER) + .getCustomTag(VALID_KEY, ItemTagType.LONG).longValue()); + + assertEquals(2L, meta.getCustomTagContainer() + .getCustomTag(innerKey, ItemTagType.TAG_CONTAINER) + .getCustomTag(VALID_KEY, ItemTagType.LONG).longValue()); + + assertEquals(1L, meta.getCustomTagContainer() + .getCustomTag(VALID_KEY, ItemTagType.LONG).longValue()); + } + + class UUIDItemTagType implements ItemTagType { + + @Override + public Class getPrimitiveType() { + return byte[].class; + } + + @Override + public Class getComplexType() { + return UUID.class; + } + + @Override + public byte[] toPrimitive(UUID complex, ItemTagAdapterContext context) { + ByteBuffer bb = ByteBuffer.wrap(new byte[16]); + bb.putLong(complex.getMostSignificantBits()); + bb.putLong(complex.getLeastSignificantBits()); + return bb.array(); + } + + @Override + public UUID fromPrimitive(byte[] primitive, ItemTagAdapterContext context) { + ByteBuffer bb = ByteBuffer.wrap(primitive); + long firstLong = bb.getLong(); + long secondLong = bb.getLong(); + return new UUID(firstLong, secondLong); + } + } + + @Test + public void testPrimitiveCustomTags() { + ItemMeta itemMeta = createNewItemMeta(); + + testPrimitiveCustomTag(itemMeta, ItemTagType.BYTE, (byte) 1); + testPrimitiveCustomTag(itemMeta, ItemTagType.SHORT, (short) 1); + testPrimitiveCustomTag(itemMeta, ItemTagType.INTEGER, 1); + testPrimitiveCustomTag(itemMeta, ItemTagType.LONG, 1L); + testPrimitiveCustomTag(itemMeta, ItemTagType.FLOAT, 1.34F); + testPrimitiveCustomTag(itemMeta, ItemTagType.DOUBLE, 151.123); + + testPrimitiveCustomTag(itemMeta, ItemTagType.STRING, "test"); + + testPrimitiveCustomTag(itemMeta, ItemTagType.BYTE_ARRAY, new byte[]{ + 1, 4, 2, Byte.MAX_VALUE + }); + testPrimitiveCustomTag(itemMeta, ItemTagType.INTEGER_ARRAY, new int[]{ + 1, 4, 2, Integer.MAX_VALUE + }); + testPrimitiveCustomTag(itemMeta, ItemTagType.LONG_ARRAY, new long[]{ + 1L, 4L, 2L, Long.MAX_VALUE + }); + } + + private void testPrimitiveCustomTag(ItemMeta meta, ItemTagType type, Z value) { + NamespacedKey tagKey = new NamespacedKey("test", String.valueOf(type.hashCode())); + + meta.getCustomTagContainer().setCustomTag(tagKey, type, value); + assertTrue(meta.getCustomTagContainer().hasCustomTag(tagKey, type)); + + Z foundValue = meta.getCustomTagContainer().getCustomTag(tagKey, type); + if (foundValue.getClass().isArray()) { // Compare arrays using reflection access + int length = Array.getLength(foundValue); + int originalLength = Array.getLength(value); + for (int i = 0; i < length && i < originalLength; i++) { + assertEquals(Array.get(value, i), Array.get(foundValue, i)); + } + } else { + assertEquals(foundValue, value); + } + + meta.getCustomTagContainer().removeCustomTag(tagKey); + assertFalse(meta.getCustomTagContainer().hasCustomTag(tagKey, type)); + } + + class PrimitiveTagType implements ItemTagType { + + private final Class primitiveType; + + PrimitiveTagType(Class primitiveType) { + this.primitiveType = primitiveType; + } + + @Override + public Class getPrimitiveType() { + return primitiveType; + } + + @Override + public Class getComplexType() { + return primitiveType; + } + + @Override + public T toPrimitive(T complex, ItemTagAdapterContext context) { + return complex; + } + + @Override + public T fromPrimitive(T primitive, ItemTagAdapterContext context) { + return primitive; + } + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaImplementationOverrideTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaImplementationOverrideTest.java new file mode 100644 index 000000000000..f1b4ec0da83b --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaImplementationOverrideTest.java @@ -0,0 +1,80 @@ +package org.bukkit.craftbukkit.inventory; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; + +import org.bukkit.Material; +import org.bukkit.configuration.serialization.DelegateDeserialization; +import org.bukkit.craftbukkit.Overridden; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class ItemMetaImplementationOverrideTest { + static final Class parent = CraftMetaItem.class; + + @Parameters(name="[{index}]:{1}") + public static List data() { + final List testData = new ArrayList(); + List> classes = new ArrayList>(); + + for (Material material : ItemStackTest.COMPOUND_MATERIALS) { + Class clazz = CraftItemFactory.instance().getItemMeta(material).getClass().asSubclass(parent); + if (clazz != parent) { + classes.add(clazz); + } + } + + List list = new ArrayList(); + + for (Method method: parent.getDeclaredMethods()) { + if (method.isAnnotationPresent(Overridden.class)) { + list.add(method); + } + } + + for (final Class clazz : classes) { + for (final Method method : list) { + testData.add( + new Object[] { + new Callable() { + public Method call() throws Exception { + return clazz.getDeclaredMethod(method.getName(), method.getParameterTypes()); + } + }, + clazz.getSimpleName() + " contains " + method.getName() + } + ); + } + + testData.add( + new Object[] { + new Callable() { + public DelegateDeserialization call() throws Exception { + return clazz.getAnnotation(DelegateDeserialization.class); + } + }, + clazz.getSimpleName() + " contains annotation " + DelegateDeserialization.class + } + ); + } + + return testData; + } + + @Parameter(0) public Callable test; + @Parameter(1) public String name; + + @Test + public void testClass() throws Throwable { + assertThat(name, test.call(), is(not(nullValue()))); + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java new file mode 100644 index 000000000000..7455461f1307 --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java @@ -0,0 +1,423 @@ +package org.bukkit.craftbukkit.inventory; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; // Paper +import java.util.List; +import java.util.UUID; + +import com.destroystokyo.paper.inventory.meta.ArmorStandMeta; // Paper +import net.minecraft.server.Block; +import net.minecraft.server.IRegistry; +import net.minecraft.server.ITileEntity; +import net.minecraft.server.Item; +import net.minecraft.server.ItemBlock; +import net.minecraft.server.ItemBlockWallable; +import net.minecraft.server.NBTTagInt; + +import org.bukkit.Bukkit; +import org.bukkit.Color; +import org.bukkit.DyeColor; +import org.bukkit.FireworkEffect; +import org.bukkit.Material; +import org.bukkit.FireworkEffect.Type; +import org.bukkit.NamespacedKey; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeModifier; +import org.bukkit.block.banner.Pattern; +import org.bukkit.block.banner.PatternType; +import org.bukkit.craftbukkit.inventory.ItemStackTest.StackProvider; +import org.bukkit.craftbukkit.inventory.ItemStackTest.StackWrapper; +import org.bukkit.craftbukkit.inventory.ItemStackTest.BukkitWrapper; +import org.bukkit.craftbukkit.inventory.ItemStackTest.CraftWrapper; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.TropicalFish; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BannerMeta; +import org.bukkit.inventory.meta.BlockStateMeta; +import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; +import org.bukkit.inventory.meta.FireworkEffectMeta; +import org.bukkit.inventory.meta.FireworkMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.KnowledgeBookMeta; +import org.bukkit.inventory.meta.LeatherArmorMeta; +import org.bukkit.inventory.meta.MapMeta; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.inventory.meta.SpawnEggMeta; +import org.bukkit.inventory.meta.TropicalFishBucketMeta; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.potion.PotionData; +import org.bukkit.potion.PotionType; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Test; + +public class ItemMetaTest extends AbstractTestingBase { + + static final int MAX_FIREWORK_POWER = 127; // Please update ItemStackFireworkTest if/when this gets changed. + + @Test(expected=IllegalArgumentException.class) + public void testPowerLimitExact() { + newFireworkMeta().setPower(MAX_FIREWORK_POWER + 1); + } + + @Test(expected=IllegalArgumentException.class) + public void testPowerLimitMax() { + newFireworkMeta().setPower(Integer.MAX_VALUE); + } + + @Test(expected=IllegalArgumentException.class) + public void testPowerLimitMin() { + newFireworkMeta().setPower(Integer.MIN_VALUE); + } + + @Test(expected=IllegalArgumentException.class) + public void testPowerLimitNegative() { + newFireworkMeta().setPower(-1); + } + + @Test + public void testPowers() { + for (int i = 0; i <= MAX_FIREWORK_POWER; i++) { + FireworkMeta firework = newFireworkMeta(); + firework.setPower(i); + assertThat(String.valueOf(i), firework.getPower(), is(i)); + } + } + + @Test + public void testConflictingEnchantment() { + ItemMeta itemMeta = Bukkit.getItemFactory().getItemMeta(Material.DIAMOND_PICKAXE); + assertThat(itemMeta.hasConflictingEnchant(Enchantment.DURABILITY), is(false)); + + itemMeta.addEnchant(Enchantment.SILK_TOUCH, 1, false); + assertThat(itemMeta.hasConflictingEnchant(Enchantment.DURABILITY), is(false)); + assertThat(itemMeta.hasConflictingEnchant(Enchantment.LOOT_BONUS_BLOCKS), is(true)); + assertThat(itemMeta.hasConflictingEnchant(null), is(false)); + } + + @Test + public void testConflictingStoredEnchantment() { + EnchantmentStorageMeta itemMeta = (EnchantmentStorageMeta) Bukkit.getItemFactory().getItemMeta(Material.ENCHANTED_BOOK); + assertThat(itemMeta.hasConflictingStoredEnchant(Enchantment.DURABILITY), is(false)); + + itemMeta.addStoredEnchant(Enchantment.SILK_TOUCH, 1, false); + assertThat(itemMeta.hasConflictingStoredEnchant(Enchantment.DURABILITY), is(false)); + assertThat(itemMeta.hasConflictingStoredEnchant(Enchantment.LOOT_BONUS_BLOCKS), is(true)); + assertThat(itemMeta.hasConflictingStoredEnchant(null), is(false)); + } + + @Test + public void testConflictingEnchantments() { + ItemMeta itemMeta = Bukkit.getItemFactory().getItemMeta(Material.DIAMOND_PICKAXE); + itemMeta.addEnchant(Enchantment.DURABILITY, 6, true); + itemMeta.addEnchant(Enchantment.DIG_SPEED, 6, true); + assertThat(itemMeta.hasConflictingEnchant(Enchantment.LOOT_BONUS_BLOCKS), is(false)); + + itemMeta.addEnchant(Enchantment.SILK_TOUCH, 1, false); + assertThat(itemMeta.hasConflictingEnchant(Enchantment.LOOT_BONUS_BLOCKS), is(true)); + assertThat(itemMeta.hasConflictingEnchant(null), is(false)); + } + + @Test + public void testConflictingStoredEnchantments() { + EnchantmentStorageMeta itemMeta = (EnchantmentStorageMeta) Bukkit.getItemFactory().getItemMeta(Material.ENCHANTED_BOOK); + itemMeta.addStoredEnchant(Enchantment.DURABILITY, 6, true); + itemMeta.addStoredEnchant(Enchantment.DIG_SPEED, 6, true); + assertThat(itemMeta.hasConflictingStoredEnchant(Enchantment.LOOT_BONUS_BLOCKS), is(false)); + + itemMeta.addStoredEnchant(Enchantment.SILK_TOUCH, 1, false); + assertThat(itemMeta.hasConflictingStoredEnchant(Enchantment.LOOT_BONUS_BLOCKS), is(true)); + assertThat(itemMeta.hasConflictingStoredEnchant(null), is(false)); + } + + private static FireworkMeta newFireworkMeta() { + return ((FireworkMeta) Bukkit.getItemFactory().getItemMeta(Material.FIREWORK_ROCKET)); + } + + @Test + public void testCrazyEquality() { + CraftItemStack craft = CraftItemStack.asCraftCopy(new ItemStack(Material.STONE)); + craft.setItemMeta(craft.getItemMeta()); + ItemStack bukkit = new ItemStack(craft); + assertThat(craft, is(bukkit)); + assertThat(bukkit, is((ItemStack) craft)); + } + + @Test + public void testTaggedButNotMeta() { + CraftItemStack craft = CraftItemStack.asCraftCopy(new ItemStack(Material.SHEARS)); + craft.handle.setDamage(0); + + assertThat("Should have NBT tag", CraftItemStack.hasItemMeta(craft.handle), is(true)); + assertThat("NBT Tag should contain Damage", craft.handle.getTag().get("Damage"), instanceOf(NBTTagInt.class)); + assertThat("But we should not have meta", craft.hasItemMeta(), is(false)); + + ItemStack pureBukkit = new ItemStack(Material.SHEARS); + assertThat("Bukkit and craft stacks should be similar", craft.isSimilar(pureBukkit), is(true)); + assertThat("Bukkit and craft stacks should be equal", craft.equals(pureBukkit), is(true)); + // Paper start - test additional ItemMeta damage cases + ItemStack clone = CraftItemStack.asBukkitCopy(CraftItemStack.asNMSCopy(craft)); + assertThat("Bukkit and craft stacks should be similar", craft.isSimilar(clone), is(true)); + assertThat("Bukkit and craft stacks should be equal", craft.equals(clone), is(true)); + + pureBukkit = new ItemStack(Material.DIAMOND_SWORD); + pureBukkit.setDurability((short) 2); + net.minecraft.server.ItemStack nms = CraftItemStack.asNMSCopy(pureBukkit); + ItemStack other = CraftItemStack.asBukkitCopy(nms); + + assertThat("Bukkit and NMS ItemStack copies should be similar", pureBukkit.isSimilar(other), is(true)); + assertThat("Bukkit and NMS ItemStack copies should be equal", pureBukkit.equals(other), is(true)); + } + + private void testItemMeta(ItemStack stack) { + assertThat("Should not have ItemMeta", stack.hasItemMeta(), is(false)); + + stack.setDurability((short) 0); + assertThat("ItemStack with zero durability should not have ItemMeta", stack.hasItemMeta(), is(false)); + + stack.setDurability((short) 2); + assertThat("ItemStack with non-zero durability should have ItemMeta", stack.hasItemMeta(), is(true)); + + stack.setLore(Collections.singletonList("Lore")); + assertThat("ItemStack with lore and durability should have ItemMeta", stack.hasItemMeta(), is(true)); + + stack.setDurability((short) 0); + assertThat("ItemStack with lore should have ItemMeta", stack.hasItemMeta(), is(true)); + + stack.setLore(null); + } + + @Test + public void testHasItemMeta() { + ItemStack itemStack = new ItemStack(Material.SHEARS); + + testItemMeta(itemStack); + testItemMeta(CraftItemStack.asCraftCopy(itemStack)); + } + // Paper end + + @Test + public void testBlockStateMeta() { + List queue = new ArrayList<>(); + + for (Item item : (Iterable) IRegistry.ITEM) { // Eclipse fail + if (item instanceof ItemBlock) { + queue.add(((ItemBlock) item).getBlock()); + } + if (item instanceof ItemBlockWallable) { + queue.add(((ItemBlockWallable) item).wallBlock); + } + } + + for (Block block : queue) { + if (block != null) { + ItemStack stack = CraftItemStack.asNewCraftStack(Item.getItemOf(block)); + + // Command blocks aren't unit testable atm + if (stack.getType() == Material.COMMAND_BLOCK || stack.getType() == Material.CHAIN_COMMAND_BLOCK || stack.getType() == Material.REPEATING_COMMAND_BLOCK) { + return; + } + + ItemMeta meta = stack.getItemMeta(); + if (block instanceof ITileEntity) { + assertTrue(stack + " has meta of type " + meta + " expected BlockStateMeta", meta instanceof BlockStateMeta); + + BlockStateMeta blockState = (BlockStateMeta) meta; + assertNotNull(stack + " has null block state", blockState.getBlockState()); + + blockState.setBlockState(blockState.getBlockState()); + } else { + assertTrue(stack + " has unexpected meta of type BlockStateMeta (but is not a tile)", !(meta instanceof BlockStateMeta)); + } + } + } + } + + @Test + public void testEachExtraData() { + final List providers = Arrays.asList( + new StackProvider(Material.WRITABLE_BOOK) { + @Override ItemStack operate(final ItemStack cleanStack) { + final BookMeta meta = (BookMeta) cleanStack.getItemMeta(); + meta.setAuthor("Some author"); + meta.setPages("Page 1", "Page 2"); + meta.setTitle("A title"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new StackProvider(Material.WRITTEN_BOOK) { + @Override ItemStack operate(final ItemStack cleanStack) { + final BookMeta meta = (BookMeta) cleanStack.getItemMeta(); + meta.setAuthor("Some author"); + meta.setPages("Page 1", "Page 2"); + meta.setTitle("A title"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + /* Skulls rely on a running server instance + new StackProvider(Material.SKULL_ITEM) { + @Override ItemStack operate(final ItemStack cleanStack) { + final SkullMeta meta = (SkullMeta) cleanStack.getItemMeta(); + meta.setOwner("Notch"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + */ + new StackProvider(Material.FILLED_MAP) { + @Override ItemStack operate(final ItemStack cleanStack) { + final MapMeta meta = (MapMeta) cleanStack.getItemMeta(); + meta.setScaling(true); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new StackProvider(Material.LEATHER_BOOTS) { + @Override ItemStack operate(final ItemStack cleanStack) { + final LeatherArmorMeta meta = (LeatherArmorMeta) cleanStack.getItemMeta(); + meta.setColor(Color.FUCHSIA); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new StackProvider(Material.POTION) { + @Override ItemStack operate(final ItemStack cleanStack) { + final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta(); + meta.setBasePotionData(new PotionData(PotionType.UNCRAFTABLE, false, false)); + meta.addCustomEffect(PotionEffectType.CONFUSION.createEffect(1, 1), false); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new StackProvider(Material.FIREWORK_ROCKET) { + @Override ItemStack operate(final ItemStack cleanStack) { + final FireworkMeta meta = (FireworkMeta) cleanStack.getItemMeta(); + meta.addEffect(FireworkEffect.builder().withColor(Color.GREEN).withFade(Color.OLIVE).with(Type.BALL_LARGE).build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new StackProvider(Material.ENCHANTED_BOOK) { + @Override ItemStack operate(final ItemStack cleanStack) { + final EnchantmentStorageMeta meta = (EnchantmentStorageMeta) cleanStack.getItemMeta(); + meta.addStoredEnchant(Enchantment.ARROW_FIRE, 1, true); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new StackProvider(Material.FIREWORK_STAR) { + @Override ItemStack operate(final ItemStack cleanStack) { + final FireworkEffectMeta meta = (FireworkEffectMeta) cleanStack.getItemMeta(); + meta.setEffect(FireworkEffect.builder().withColor(Color.MAROON, Color.BLACK).with(Type.CREEPER).withFlicker().build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new StackProvider(Material.WHITE_BANNER) { + @Override ItemStack operate(ItemStack cleanStack) { + final BannerMeta meta = (BannerMeta) cleanStack.getItemMeta(); + meta.setBaseColor(DyeColor.CYAN); + meta.addPattern(new Pattern(DyeColor.WHITE, PatternType.BRICKS)); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + /* No distinguishing features, add back with virtual entity API + new StackProvider(Material.ZOMBIE_SPAWN_EGG) { + @Override ItemStack operate(ItemStack cleanStack) { + final SpawnEggMeta meta = (SpawnEggMeta) cleanStack.getItemMeta(); + meta.setSpawnedType(EntityType.ZOMBIE); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + */ + new StackProvider(Material.KNOWLEDGE_BOOK) { + @Override ItemStack operate(ItemStack cleanStack) { + final KnowledgeBookMeta meta = (KnowledgeBookMeta) cleanStack.getItemMeta(); + meta.addRecipe(new NamespacedKey("minecraft", "test"), new NamespacedKey("plugin", "test")); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new StackProvider(Material.TROPICAL_FISH_BUCKET) { + @Override ItemStack operate(ItemStack cleanStack) { + final TropicalFishBucketMeta meta = (TropicalFishBucketMeta) cleanStack.getItemMeta(); + meta.setBodyColor(DyeColor.ORANGE); + meta.setPatternColor(DyeColor.BLACK); + meta.setPattern(TropicalFish.Pattern.DASHER); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + // Paper start + new StackProvider(Material.ARMOR_STAND) { + @Override + ItemStack operate(ItemStack cleanStack) { + final ArmorStandMeta meta = (ArmorStandMeta) cleanStack.getItemMeta(); + meta.setInvisible(true); + cleanStack.setItemMeta(meta); + return cleanStack; + } + } + // paper end + ); + + assertThat("Forgotten test?", providers, hasSize(ItemStackTest.COMPOUND_MATERIALS.length - 4/* Normal item meta, skulls, eggs and tile entities */)); + + for (final StackProvider provider : providers) { + downCastTest(new BukkitWrapper(provider)); + downCastTest(new CraftWrapper(provider)); + } + } + + @Test + public void testAttributeModifiers() { + UUID sameUUID = UUID.randomUUID(); + ItemMeta itemMeta = Bukkit.getItemFactory().getItemMeta(Material.DIAMOND_PICKAXE); + itemMeta.addAttributeModifier(Attribute.GENERIC_ATTACK_SPEED, new AttributeModifier(sameUUID, "Test Modifier", 10, AttributeModifier.Operation.ADD_NUMBER)); + + ItemMeta equalMeta = Bukkit.getItemFactory().getItemMeta(Material.DIAMOND_PICKAXE); + equalMeta.addAttributeModifier(Attribute.GENERIC_ATTACK_SPEED, new AttributeModifier(sameUUID, "Test Modifier", 10, AttributeModifier.Operation.ADD_NUMBER)); + + assertThat(itemMeta.equals(equalMeta), is(true)); + + ItemMeta itemMeta2 = Bukkit.getItemFactory().getItemMeta(Material.DIAMOND_PICKAXE); + itemMeta2.addAttributeModifier(Attribute.GENERIC_ATTACK_SPEED, new AttributeModifier(sameUUID, "Test Modifier", 10, AttributeModifier.Operation.ADD_NUMBER)); + + ItemMeta notEqualMeta2 = Bukkit.getItemFactory().getItemMeta(Material.DIAMOND_PICKAXE); + notEqualMeta2.addAttributeModifier(Attribute.GENERIC_ATTACK_SPEED, new AttributeModifier(sameUUID, "Test Modifier", 11, AttributeModifier.Operation.ADD_NUMBER)); + + assertThat(itemMeta2.equals(notEqualMeta2), is(false)); + } + + private void downCastTest(final StackWrapper provider) { + final String name = provider.toString(); + final ItemStack blank = new ItemStack(Material.STONE); + final ItemStack craftBlank = CraftItemStack.asCraftCopy(blank); + + downCastTest(name, provider.stack(), blank); + blank.setItemMeta(blank.getItemMeta()); + downCastTest(name, provider.stack(), blank); + + downCastTest(name, provider.stack(), craftBlank); + craftBlank.setItemMeta(craftBlank.getItemMeta()); + downCastTest(name, provider.stack(), craftBlank); + } + + private void downCastTest(final String name, final ItemStack stack, final ItemStack blank) { + assertThat(name, stack, is(not(blank))); + assertThat(name, stack.getItemMeta(), is(not(blank.getItemMeta()))); + + stack.setType(Material.STONE); + + assertThat(name, stack, is(blank)); + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackBookTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackBookTest.java new file mode 100644 index 000000000000..c16c5611d5bf --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackBookTest.java @@ -0,0 +1,213 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BookMeta; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.base.Joiner; + +@RunWith(Parameterized.class) +public class ItemStackBookTest extends ItemStackTest { + + @Parameters(name="[{index}]:{" + NAME_PARAMETER + "}") + public static List data() { + return StackProvider.compound(operators(), "%s %s", NAME_PARAMETER, Material.WRITTEN_BOOK, Material.WRITABLE_BOOK); + } + + @SuppressWarnings("unchecked") + static List operators() { + return CompoundOperator.compound( + Joiner.on('+'), + NAME_PARAMETER, + Long.parseLong("1110", 2), + ItemStackLoreEnchantmentTest.operators(), + Arrays.asList( + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + BookMeta meta = (BookMeta) cleanStack.getItemMeta(); + meta.addPage("Page 1", "Page 2"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "Pages vs. Null" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + BookMeta meta = (BookMeta) cleanStack.getItemMeta(); + meta.addPage("Page 1", "Page 2"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + cleanStack.setItemMeta(cleanStack.getItemMeta()); + return cleanStack; + } + }, + "Pages vs. blank" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + BookMeta meta = (BookMeta) cleanStack.getItemMeta(); + meta.addPage("Page 1", "Page 2"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + BookMeta meta = (BookMeta) cleanStack.getItemMeta(); + meta.addPage("Page 2", "Page 1"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Pages switched" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + BookMeta meta = (BookMeta) cleanStack.getItemMeta(); + meta.addPage("Page 1", "Page 2"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + BookMeta meta = (BookMeta) cleanStack.getItemMeta(); + meta.addPage("Page 1"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Pages short" + } + ), + Arrays.asList( + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + BookMeta meta = (BookMeta) cleanStack.getItemMeta(); + meta.setAuthor("AnAuthor"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "Author vs. Null" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + BookMeta meta = (BookMeta) cleanStack.getItemMeta(); + meta.setAuthor("AnAuthor"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + cleanStack.setItemMeta(cleanStack.getItemMeta()); + return cleanStack; + } + }, + "Author vs. blank" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + BookMeta meta = (BookMeta) cleanStack.getItemMeta(); + meta.setAuthor("AnAuthor"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + BookMeta meta = (BookMeta) cleanStack.getItemMeta(); + meta.setAuthor("AnotherAuthor"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Authors" + } + ), + Arrays.asList( + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + BookMeta meta = (BookMeta) cleanStack.getItemMeta(); + meta.setTitle("Some title"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "Title vs. Null" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + BookMeta meta = (BookMeta) cleanStack.getItemMeta(); + meta.setTitle("Some title"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + cleanStack.setItemMeta(cleanStack.getItemMeta()); + return cleanStack; + } + }, + "title vs. blank" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + BookMeta meta = (BookMeta) cleanStack.getItemMeta(); + meta.setTitle("Some title"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + BookMeta meta = (BookMeta) cleanStack.getItemMeta(); + meta.setTitle("Different title"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Titles" + } + ) + ); + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackEnchantStorageTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackEnchantStorageTest.java new file mode 100644 index 000000000000..a0499b8ad327 --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackEnchantStorageTest.java @@ -0,0 +1,108 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.base.Joiner; + +@RunWith(Parameterized.class) +public class ItemStackEnchantStorageTest extends ItemStackTest { + + @Parameters(name="[{index}]:{" + NAME_PARAMETER + "}") + public static List data() { + return StackProvider.compound(operators(), "%s %s", NAME_PARAMETER, Material.ENCHANTED_BOOK); + } + + @SuppressWarnings("unchecked") + static List operators() { + return CompoundOperator.compound( + Joiner.on('+'), + NAME_PARAMETER, + Long.parseLong("10", 2), + ItemStackLoreEnchantmentTest.operators(), + Arrays.asList( + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + EnchantmentStorageMeta meta = (EnchantmentStorageMeta) cleanStack.getItemMeta(); + meta.addStoredEnchant(Enchantment.DURABILITY, 1, true); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + EnchantmentStorageMeta meta = (EnchantmentStorageMeta) cleanStack.getItemMeta(); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Enchantable vs Blank" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + EnchantmentStorageMeta meta = (EnchantmentStorageMeta) cleanStack.getItemMeta(); + meta.addStoredEnchant(Enchantment.KNOCKBACK, 1, true); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "Enchantable vs Null" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + EnchantmentStorageMeta meta = (EnchantmentStorageMeta) cleanStack.getItemMeta(); + meta.addStoredEnchant(Enchantment.DAMAGE_UNDEAD, 1, true); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + EnchantmentStorageMeta meta = (EnchantmentStorageMeta) cleanStack.getItemMeta(); + meta.addStoredEnchant(Enchantment.DAMAGE_UNDEAD, 1, true); + meta.addStoredEnchant(Enchantment.FIRE_ASPECT, 1, true); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Enchantable vs More" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + EnchantmentStorageMeta meta = (EnchantmentStorageMeta) cleanStack.getItemMeta(); + meta.addStoredEnchant(Enchantment.PROTECTION_FIRE, 1, true); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + EnchantmentStorageMeta meta = (EnchantmentStorageMeta) cleanStack.getItemMeta(); + meta.addEnchant(Enchantment.PROTECTION_FIRE, 2, true); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Enchantable vs Other" + } + ) + ); + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackFireworkChargeTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackFireworkChargeTest.java new file mode 100644 index 000000000000..fbfce58c5f77 --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackFireworkChargeTest.java @@ -0,0 +1,128 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Color; +import org.bukkit.FireworkEffect; +import org.bukkit.FireworkEffect.Type; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.FireworkEffectMeta; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.base.Joiner; + +@RunWith(Parameterized.class) +public class ItemStackFireworkChargeTest extends ItemStackTest { + + @Parameters(name="[{index}]:{" + NAME_PARAMETER + "}") + public static List data() { + return StackProvider.compound(operators(), "%s %s", NAME_PARAMETER, Material.FIREWORK_STAR); + } + + @SuppressWarnings("unchecked") + static List operators() { + return CompoundOperator.compound( + Joiner.on('+'), + NAME_PARAMETER, + Long.parseLong("10", 2), + ItemStackLoreEnchantmentTest.operators(), + Arrays.asList( + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkEffectMeta meta = (FireworkEffectMeta) cleanStack.getItemMeta(); + meta.setEffect(FireworkEffect.builder().withColor(Color.WHITE).build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkEffectMeta meta = (FireworkEffectMeta) cleanStack.getItemMeta(); + meta.setEffect(FireworkEffect.builder().withColor(Color.BLACK).build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Effect Color 1 vs. Effect Color 2" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkEffectMeta meta = (FireworkEffectMeta) cleanStack.getItemMeta(); + meta.setEffect(FireworkEffect.builder().withColor(Color.WHITE).with(Type.CREEPER).build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkEffectMeta meta = (FireworkEffectMeta) cleanStack.getItemMeta(); + meta.setEffect(FireworkEffect.builder().withColor(Color.WHITE).with(Type.BURST).build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Effect type 1 vs. Effect type 2" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkEffectMeta meta = (FireworkEffectMeta) cleanStack.getItemMeta(); + meta.setEffect(FireworkEffect.builder().withColor(Color.WHITE).withFade(Color.BLUE).build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkEffectMeta meta = (FireworkEffectMeta) cleanStack.getItemMeta(); + meta.setEffect(FireworkEffect.builder().withColor(Color.WHITE).withFade(Color.RED).build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Effect fade 1 vs. Effect fade 2" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkEffectMeta meta = (FireworkEffectMeta) cleanStack.getItemMeta(); + meta.setEffect(FireworkEffect.builder().withColor(Color.WHITE).withFlicker().build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkEffectMeta meta = (FireworkEffectMeta) cleanStack.getItemMeta(); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Effect vs. Null" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkEffectMeta meta = (FireworkEffectMeta) cleanStack.getItemMeta(); + meta.setEffect(FireworkEffect.builder().withColor(Color.WHITE).withTrail().build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "Effect vs. None" + } + ) + ); + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackFireworkTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackFireworkTest.java new file mode 100644 index 000000000000..55e6629bc473 --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackFireworkTest.java @@ -0,0 +1,184 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Color; +import org.bukkit.FireworkEffect; +import org.bukkit.Material; +import org.bukkit.FireworkEffect.Type; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.FireworkMeta; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.base.Joiner; + +@RunWith(Parameterized.class) +public class ItemStackFireworkTest extends ItemStackTest { + + @Parameters(name="[{index}]:{" + NAME_PARAMETER + "}") + public static List data() { + return StackProvider.compound(operators(), "%s %s", NAME_PARAMETER, Material.FIREWORK_ROCKET); + } + + @SuppressWarnings("unchecked") + static List operators() { + return CompoundOperator.compound( + Joiner.on('+'), + NAME_PARAMETER, + Long.parseLong("110", 2), + ItemStackLoreEnchantmentTest.operators(), + Arrays.asList( + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkMeta meta = (FireworkMeta) cleanStack.getItemMeta(); + meta.addEffect(FireworkEffect.builder().withColor(Color.WHITE).build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkMeta meta = (FireworkMeta) cleanStack.getItemMeta(); + meta.addEffect(FireworkEffect.builder().withColor(Color.BLACK).build()); + meta.addEffect(FireworkEffect.builder().withColor(Color.GREEN).build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Effect Color 1 vs. Effect Color 2" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkMeta meta = (FireworkMeta) cleanStack.getItemMeta(); + meta.addEffect(FireworkEffect.builder().withColor(Color.WHITE).with(Type.CREEPER).build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkMeta meta = (FireworkMeta) cleanStack.getItemMeta(); + meta.addEffect(FireworkEffect.builder().withColor(Color.WHITE).with(Type.BURST).build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Effect type 1 vs. Effect type 2" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkMeta meta = (FireworkMeta) cleanStack.getItemMeta(); + meta.addEffect(FireworkEffect.builder().withColor(Color.WHITE).withFade(Color.BLUE).build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkMeta meta = (FireworkMeta) cleanStack.getItemMeta(); + meta.addEffect(FireworkEffect.builder().withColor(Color.WHITE).withFade(Color.RED).build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Effect fade 1 vs. Effect fade 2" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkMeta meta = (FireworkMeta) cleanStack.getItemMeta(); + meta.addEffect(FireworkEffect.builder().withColor(Color.WHITE).withFlicker().build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkMeta meta = (FireworkMeta) cleanStack.getItemMeta(); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Effect vs. Null" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkMeta meta = (FireworkMeta) cleanStack.getItemMeta(); + meta.addEffect(FireworkEffect.builder().withColor(Color.WHITE).withTrail().build()); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "Effect vs. None" + } + ), + Arrays.asList( + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkMeta meta = (FireworkMeta) cleanStack.getItemMeta(); + meta.setPower(127); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkMeta meta = (FireworkMeta) cleanStack.getItemMeta(); + meta.setPower(100); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Height vs. Other" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkMeta meta = (FireworkMeta) cleanStack.getItemMeta(); + meta.setPower(42); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkMeta meta = (FireworkMeta) cleanStack.getItemMeta(); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Height vs. Null" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + FireworkMeta meta = (FireworkMeta) cleanStack.getItemMeta(); + meta.setPower(10); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "Height vs. None" + } + ) + ); + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackLeatherTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackLeatherTest.java new file mode 100644 index 000000000000..6d68e1f46828 --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackLeatherTest.java @@ -0,0 +1,89 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.LeatherArmorMeta; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.base.Joiner; + +@RunWith(Parameterized.class) +public class ItemStackLeatherTest extends ItemStackTest { + + @Parameters(name="[{index}]:{" + NAME_PARAMETER + "}") + public static List data() { + return StackProvider.compound(operators(), "%s %s", NAME_PARAMETER, Material.LEATHER_BOOTS, Material.LEATHER_CHESTPLATE, Material.LEATHER_HELMET, Material.LEATHER_LEGGINGS); + } + + @SuppressWarnings("unchecked") + static List operators() { + return CompoundOperator.compound( + Joiner.on('+'), + NAME_PARAMETER, + Long.parseLong("10", 2), + ItemStackLoreEnchantmentTest.operators(), + Arrays.asList( + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + LeatherArmorMeta meta = (LeatherArmorMeta) cleanStack.getItemMeta(); + meta.setColor(Color.FUCHSIA); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "Color vs Null" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + LeatherArmorMeta meta = (LeatherArmorMeta) cleanStack.getItemMeta(); + meta.setColor(Color.GRAY); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + LeatherArmorMeta meta = (LeatherArmorMeta) cleanStack.getItemMeta(); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Color vs Blank" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + LeatherArmorMeta meta = (LeatherArmorMeta) cleanStack.getItemMeta(); + meta.setColor(Color.MAROON); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + LeatherArmorMeta meta = (LeatherArmorMeta) cleanStack.getItemMeta(); + meta.setColor(Color.ORANGE); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Color vs Other" + } + ) + ); + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackLoreEnchantmentTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackLoreEnchantmentTest.java new file mode 100644 index 000000000000..32a9184fd250 --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackLoreEnchantmentTest.java @@ -0,0 +1,297 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.Repairable; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.base.Joiner; + +@RunWith(Parameterized.class) +public class ItemStackLoreEnchantmentTest extends ItemStackTest { + + @Parameters(name="[{index}]:{" + NAME_PARAMETER + "}") + public static List data() { + return StackProvider.compound(operators(), "%s %s", NAME_PARAMETER, ItemStackTest.COMPOUND_MATERIALS); + } + + @SuppressWarnings("unchecked") + static List operators() { + return CompoundOperator.compound( + Joiner.on('+'), + NAME_PARAMETER, + ~0l, + Arrays.asList( + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + meta.setLore(Arrays.asList("First Lore", "Second Lore")); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "Lore vs Null" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + meta.setLore(Arrays.asList("Some lore")); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Lore vs Blank" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + meta.setLore(Arrays.asList("Some more lore", "Another lore")); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + meta.setLore(Arrays.asList("Some more lore")); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Lore vs Other" + } + ), + Arrays.asList( + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + meta.setDisplayName("TestItemName"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "Name vs Null" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + meta.setDisplayName("AnotherItemName"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Name vs Blank" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + meta.setDisplayName("The original ItemName"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + meta.setDisplayName("The other name"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Name vs Other" + } + ), + Arrays.asList( + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + cleanStack.addUnsafeEnchantment(Enchantment.DIG_SPEED, 2); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "EnchantStack vs Null" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + cleanStack.addUnsafeEnchantment(Enchantment.OXYGEN, 1); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "EnchantStack vs Blank" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + cleanStack.addUnsafeEnchantment(Enchantment.ARROW_DAMAGE, 1); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + cleanStack.addUnsafeEnchantment(Enchantment.ARROW_FIRE, 1); + return cleanStack; + } + }, + "EnchantStack vs OtherEnchantStack" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + meta.addEnchant(Enchantment.DURABILITY, 1, true); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Enchant vs Blank" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + meta.addEnchant(Enchantment.KNOCKBACK, 1, true); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "Enchant vs Null" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + meta.addEnchant(Enchantment.PROTECTION_FIRE, 1, true); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + meta.addEnchant(Enchantment.PROTECTION_FIRE, 2, true); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Enchant vs Other" + } + ), + Arrays.asList( + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + ((Repairable) meta).setRepairCost(42); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "Repair vs Null" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + ((Repairable) meta).setRepairCost(36); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Repair vs Blank" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + ((Repairable) meta).setRepairCost(89); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + ItemMeta meta = cleanStack.getItemMeta(); + ((Repairable) meta).setRepairCost(88); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Repair vs Other" + } + ) + ); + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackMapTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackMapTest.java new file mode 100644 index 000000000000..c13f6d579088 --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackMapTest.java @@ -0,0 +1,121 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.MapMeta; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.base.Joiner; + +@RunWith(Parameterized.class) +public class ItemStackMapTest extends ItemStackTest { + + @Parameters(name="[{index}]:{" + NAME_PARAMETER + "}") + public static List data() { + return StackProvider.compound(operators(), "%s %s", NAME_PARAMETER, Material.FILLED_MAP); + } + + @SuppressWarnings("unchecked") + static List operators() { + return CompoundOperator.compound( + Joiner.on('+'), + NAME_PARAMETER, + Long.parseLong("10", 2), + ItemStackLoreEnchantmentTest.operators(), + Arrays.asList( + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + MapMeta meta = (MapMeta) cleanStack.getItemMeta(); + meta.setScaling(true); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + MapMeta meta = (MapMeta) cleanStack.getItemMeta(); + meta.setScaling(false); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Scale vs. Unscale" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + MapMeta meta = (MapMeta) cleanStack.getItemMeta(); + meta.setScaling(true); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + MapMeta meta = (MapMeta) cleanStack.getItemMeta(); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Scale vs. Blank" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + MapMeta meta = (MapMeta) cleanStack.getItemMeta(); + meta.setScaling(false); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + MapMeta meta = (MapMeta) cleanStack.getItemMeta(); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Unscale vs. Blank" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + MapMeta meta = (MapMeta) cleanStack.getItemMeta(); + meta.setScaling(true); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "Scale vs. None" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + MapMeta meta = (MapMeta) cleanStack.getItemMeta(); + meta.setScaling(false); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "Unscale vs. None" + } + ) + ); + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackPotionsTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackPotionsTest.java new file mode 100644 index 000000000000..c1f9fb745b47 --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackPotionsTest.java @@ -0,0 +1,146 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.potion.PotionEffectType; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.base.Joiner; + +@RunWith(Parameterized.class) +public class ItemStackPotionsTest extends ItemStackTest { + + @Parameters(name="[{index}]:{" + NAME_PARAMETER + "}") + public static List data() { + return StackProvider.compound(operators(), "%s %s", NAME_PARAMETER, Material.POTION); + } + + @SuppressWarnings("unchecked") + static List operators() { + return CompoundOperator.compound( + Joiner.on('+'), + NAME_PARAMETER, + Long.parseLong("10", 2), + ItemStackLoreEnchantmentTest.operators(), + Arrays.asList( + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta(); + meta.addCustomEffect(PotionEffectType.CONFUSION.createEffect(1, 1), false); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "Potion vs Null" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta(); + meta.addCustomEffect(PotionEffectType.HARM.createEffect(2, 1), false); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta(); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Potion vs Blank" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta(); + meta.addCustomEffect(PotionEffectType.SLOW_DIGGING.createEffect(1, 1), false); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta(); + meta.addCustomEffect(PotionEffectType.FAST_DIGGING.createEffect(1, 1), false); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Potion vs Harder" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta(); + meta.addCustomEffect(PotionEffectType.JUMP.createEffect(1, 1), false); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta(); + meta.addCustomEffect(PotionEffectType.JUMP.createEffect(1, 1), false); + meta.addCustomEffect(PotionEffectType.REGENERATION.createEffect(1, 1), false); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Potion vs Better" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta(); + meta.addCustomEffect(PotionEffectType.SPEED.createEffect(10, 1), false); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta(); + meta.addCustomEffect(PotionEffectType.SPEED.createEffect(5, 1), false); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Potion vs Faster" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta(); + meta.addCustomEffect(PotionEffectType.INCREASE_DAMAGE.createEffect(1, 1), false); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + final PotionMeta meta = (PotionMeta) cleanStack.getItemMeta(); + meta.addCustomEffect(PotionEffectType.INCREASE_DAMAGE.createEffect(1, 2), false); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Potion vs Stronger" + } + ) + ); + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackSkullTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackSkullTest.java new file mode 100644 index 000000000000..1561bf885d94 --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackSkullTest.java @@ -0,0 +1,88 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.base.Joiner; + +@RunWith(Parameterized.class) +public class ItemStackSkullTest extends ItemStackTest { + + @Parameters(name="[{index}]:{" + NAME_PARAMETER + "}") + public static List data() { + return StackProvider.compound(operators(), "%s %s", NAME_PARAMETER, Material.PLAYER_HEAD); + } + + @SuppressWarnings("unchecked") + static List operators() { + return CompoundOperator.compound( + Joiner.on('+'), + NAME_PARAMETER, + Long.parseLong("10", 2), + ItemStackLoreEnchantmentTest.operators(), + Arrays.asList( + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + SkullMeta meta = (SkullMeta) cleanStack.getItemMeta(); + meta.setOwner("Notch"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + SkullMeta meta = (SkullMeta) cleanStack.getItemMeta(); + meta.setOwner("Dinnerbone"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Name 1 vs. Name 2" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + SkullMeta meta = (SkullMeta) cleanStack.getItemMeta(); + meta.setOwner("Notch"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + SkullMeta meta = (SkullMeta) cleanStack.getItemMeta(); + meta.setOwner(null); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + "Name vs. Null" + }, + new Object[] { + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + SkullMeta meta = (SkullMeta) cleanStack.getItemMeta(); + meta.setOwner("Notch"); + cleanStack.setItemMeta(meta); + return cleanStack; + } + }, + new Operator() { + public ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + }, + "Name vs. None" + } + ) + ); + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackTest.java new file mode 100644 index 000000000000..6140edeec404 --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemStackTest.java @@ -0,0 +1,493 @@ +package org.bukkit.craftbukkit.inventory; + +import static org.bukkit.support.Matchers.sameHash; +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.bukkit.Material; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.support.AbstractTestingBase; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.util.io.BukkitObjectOutputStream; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; +import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; + +@RunWith(Parameterized.class) +public class ItemStackTest extends AbstractTestingBase { + static abstract class StackProvider { + final Material material; + + StackProvider(Material material) { + this.material = material; + } + + ItemStack bukkit() { + return operate(cleanStack(material, false)); + } + + ItemStack craft() { + return operate(cleanStack(material, true)); + } + + abstract ItemStack operate(ItemStack cleanStack); + + static ItemStack cleanStack(Material material, boolean craft) { + final ItemStack stack = new ItemStack(material); + return craft ? CraftItemStack.asCraftCopy(stack) : stack; + } + + @Override + public String toString() { + return material.toString(); + } + + /** + * For each item in parameterList, it will apply nameFormat at nameIndex. + * For each item in parameterList for each item in materials, it will create a stack provider at each array index that contains an Operator. + * + * @param parameterList + * @param nameFormat + * @param nameIndex + * @param materials + * @return + */ + static List compound(final List parameterList, final String nameFormat, final int nameIndex, final Material...materials) { + final List out = new ArrayList(); + for (Object[] params : parameterList) { + final int len = params.length; + for (final Material material : materials) { + final Object[] paramsOut = params.clone(); + for (int i = 0; i < len; i++) { + final Object param = paramsOut[i]; + if (param instanceof Operator) { + final Operator operator = (Operator) param; + paramsOut[i] = new StackProvider(material) { + @Override + ItemStack operate(ItemStack cleanStack) { + return operator.operate(cleanStack); + } + }; + } + } + paramsOut[nameIndex] = String.format(nameFormat, paramsOut[nameIndex], material); + out.add(paramsOut); + } + } + return out; + } + } + + interface Operator { + ItemStack operate(ItemStack cleanStack); + } + + static class CompoundOperator implements Operator { + static class RecursiveContainer { + final Joiner joiner; + final Object[] strings; + final int nameParameter; + final List stack; + final List out; + final List[] lists; + + RecursiveContainer(Joiner joiner, Object[] strings, int nameParameter, List stack, List out, List[] lists) { + this.joiner = joiner; + this.strings = strings; + this.nameParameter = nameParameter; + this.stack = stack; + this.out = out; + this.lists = lists; + } + } + final Operator[] operators; + + CompoundOperator(Operator...operators) { + this.operators = operators; + } + + public ItemStack operate(ItemStack cleanStack) { + for (Operator operator : operators) { + operator.operate(cleanStack); + } + return cleanStack; + } + + @Override + public String toString() { + return Arrays.toString(operators); + } + + + /** + * This combines different tests into one large collection, combining no two tests from the same list. + * @param joiner used to join names + * @param nameParameter index of the name parameter + * @param singletonBitmask a list of bits representing the 'singletons' located in your originalLists. Lowest order bits represent the first items in originalLists. + * Singletons are exponentially linked with each other, such that, + * the output will contain every unique subset of only items from the singletons, + * as well as every unique subset that contains at least one item from each non-singleton. + * @param originalLists + * @return + */ + static List compound(final Joiner joiner, final int nameParameter, final long singletonBitmask, final List...originalLists) { + + final List out = new ArrayList(); + final List> singletons = new ArrayList>(); + final List> notSingletons = new ArrayList>(); + + { // Separate and prime the 'singletons' + int i = 0; + for (List list : originalLists) { + (((singletonBitmask >>> i++) & 0x1) == 0x1 ? singletons : notSingletons).add(list); + } + } + + for (final List primarySingleton : singletons) { + // Iterate over our singletons, to multiply the 'out' each time + for (final Object[] entry : out.toArray(EMPTY_ARRAY)) { + // Iterate over a snapshot of 'out' to prevent CMEs / infinite iteration + final int len = entry.length; + for (final Object[] singleton : primarySingleton) { + // Iterate over each item in our singleton for the current 'out' entry + final Object[] toOut = entry.clone(); + for (int i = 0; i < len; i++) { + // Iterate over each parameter + if (i == nameParameter) { + toOut[i] = joiner.join(toOut[i], singleton[i]); + } else if (toOut[i] instanceof Operator) { + final Operator op1 = (Operator) toOut[i]; + final Operator op2 = (Operator) singleton[i]; + toOut[i] = new Operator() { + public ItemStack operate(final ItemStack cleanStack) { + return op2.operate(op1.operate(cleanStack)); + } + }; + } + } + out.add(toOut); + } + } + out.addAll(primarySingleton); + } + + @SuppressWarnings("unchecked") + final List[] lists = new List[notSingletons.size() + 1]; + notSingletons.toArray(lists); + lists[lists.length - 1] = out; + + final RecursiveContainer methodParams = new RecursiveContainer(joiner, new Object[lists.length], nameParameter, new ArrayList(lists.length), new ArrayList(), lists); + + recursivelyCompound(methodParams, 0); + methodParams.out.addAll(out); + + return methodParams.out; + } + + private static void recursivelyCompound(final RecursiveContainer methodParams, final int level) { + final List stack = methodParams.stack; + + if (level == methodParams.lists.length) { + final Object[] firstParams = stack.get(0); + final int len = firstParams.length; + final int stackSize = stack.size(); + final Object[] params = new Object[len]; + + for (int i = 0; i < len; i++) { + final Object firstParam = firstParams[i]; + + if (firstParam instanceof Operator) { + final Operator[] operators = new Operator[stackSize]; + for (int j = 0; j < stackSize; j++) { + operators[j] = (Operator) stack.get(j)[i]; + } + + params[i] = new CompoundOperator(operators); + } else if (i == methodParams.nameParameter) { + final Object[] strings = methodParams.strings; + for (int j = 0; j < stackSize; j++) { + strings[j] = stack.get(j)[i]; + } + + params[i] = methodParams.joiner.join(strings); + } else { + params[i] = firstParam; + } + } + + methodParams.out.add(params); + } else { + final int marker = stack.size(); + + for (final Object[] params : methodParams.lists[level]) { + stack.add(params); + recursivelyCompound(methodParams, level + 1); + stack.remove(marker); + } + } + } + } + + interface StackWrapper { + ItemStack stack(); + } + + static class CraftWrapper implements StackWrapper { + final StackProvider provider; + + CraftWrapper(StackProvider provider) { + this.provider = provider; + } + + public ItemStack stack() { + return provider.craft(); + } + + @Override + public String toString() { + return "Craft " + provider; + } + } + + static class BukkitWrapper implements StackWrapper { + final StackProvider provider; + + BukkitWrapper(StackProvider provider) { + this.provider = provider; + } + + public ItemStack stack() { + return provider.bukkit(); + } + + @Override + public String toString() { + return "Bukkit " + provider; + } + } + + static class NoOpProvider extends StackProvider { + + NoOpProvider(Material material) { + super(material); + } + + @Override + ItemStack operate(ItemStack cleanStack) { + return cleanStack; + } + + @Override + public String toString() { + return "NoOp " + super.toString(); + } + } + + @Parameters(name="[{index}]:{" + NAME_PARAMETER + "}") + public static List data() { + return ImmutableList.of(); // TODO, test basic durability issues + } + + static final Object[][] EMPTY_ARRAY = new Object[0][]; + /** + * Materials that generate unique item meta types. + */ + static final Material[] COMPOUND_MATERIALS; + static final int NAME_PARAMETER = 2; + static { + final ItemFactory factory = CraftItemFactory.instance(); + final Map, Material> possibleMaterials = new HashMap, Material>(); + ItemMeta meta; + for (final Material material : Material.values()) { + meta = factory.getItemMeta(material); + if (meta == null || possibleMaterials.containsKey(meta.getClass())) + continue; + possibleMaterials.put(meta.getClass(), material); + + } + COMPOUND_MATERIALS = possibleMaterials.values().toArray(new Material[possibleMaterials.size()]); + } + + @Parameter(0) public StackProvider provider; + @Parameter(1) public StackProvider unequalProvider; + @Parameter(NAME_PARAMETER) public String name; + + @Test + public void testBukkitInequality() { + final StackWrapper bukkitWrapper = new CraftWrapper(provider); + testInequality(bukkitWrapper, new BukkitWrapper(unequalProvider)); + testInequality(bukkitWrapper, new BukkitWrapper(new NoOpProvider(provider.material))); + } + + @Test + public void testCraftInequality() { + final StackWrapper craftWrapper = new CraftWrapper(provider); + testInequality(craftWrapper, new CraftWrapper(unequalProvider)); + testInequality(craftWrapper, new CraftWrapper(new NoOpProvider(provider.material))); + } + + @Test + public void testMixedInequality() { + final StackWrapper craftWrapper = new CraftWrapper(provider); + testInequality(craftWrapper, new BukkitWrapper(unequalProvider)); + testInequality(craftWrapper, new BukkitWrapper(new NoOpProvider(provider.material))); + + final StackWrapper bukkitWrapper = new CraftWrapper(provider); + testInequality(bukkitWrapper, new CraftWrapper(unequalProvider)); + testInequality(bukkitWrapper, new CraftWrapper(new NoOpProvider(provider.material))); + } + + static void testInequality(StackWrapper provider, StackWrapper unequalProvider) { + final ItemStack stack = provider.stack(); + final ItemStack stack2 = provider.stack(); + assertThat(stack, allOf(equalTo(stack), sameHash(stack))); + assertThat(stack, is(not(sameInstance(stack2)))); + assertThat(stack, allOf(equalTo(stack2), sameHash(stack2))); + + final ItemStack unequalStack = unequalProvider.stack(); + final ItemStack unequalStack2 = unequalProvider.stack(); + assertThat(unequalStack, allOf(equalTo(unequalStack), sameHash(unequalStack))); + assertThat(unequalStack, is(not(sameInstance(unequalStack2)))); + assertThat(unequalStack, allOf(equalTo(unequalStack2), sameHash(unequalStack2))); + + assertThat(stack, is(not(unequalStack))); + assertThat(unequalStack, is(not(stack))); + + final ItemStack newStack = new ItemStack(stack2); + assertThat(newStack, allOf(equalTo(stack), sameHash(stack))); + assertThat(newStack, is(not(unequalStack))); + assertThat(newStack.getItemMeta(), allOf(equalTo(stack.getItemMeta()), sameHash(stack.getItemMeta()))); + assertThat(newStack.getItemMeta(), is(not(unequalStack.getItemMeta()))); + + final ItemStack craftStack = CraftItemStack.asCraftCopy(stack2); + assertThat(craftStack, allOf(equalTo(stack), sameHash(stack))); + assertThat(craftStack, is(not(unequalStack))); + assertThat(craftStack.getItemMeta(), allOf(equalTo(stack.getItemMeta()), sameHash(stack.getItemMeta()))); + assertThat(craftStack.getItemMeta(), is(not(unequalStack.getItemMeta()))); + + final ItemStack newUnequalStack = new ItemStack(unequalStack2); + assertThat(newUnequalStack, allOf(equalTo(unequalStack), sameHash(unequalStack))); + assertThat(newUnequalStack, is(not(stack))); + assertThat(newUnequalStack.getItemMeta(), allOf(equalTo(unequalStack.getItemMeta()), sameHash(unequalStack.getItemMeta()))); + assertThat(newUnequalStack.getItemMeta(), is(not(stack.getItemMeta()))); + + final ItemStack newUnequalCraftStack = CraftItemStack.asCraftCopy(unequalStack2); + assertThat(newUnequalCraftStack, allOf(equalTo(unequalStack), sameHash(unequalStack))); + assertThat(newUnequalCraftStack, is(not(stack))); + assertThat(newUnequalCraftStack.getItemMeta(), allOf(equalTo(unequalStack.getItemMeta()), sameHash(unequalStack.getItemMeta()))); + assertThat(newUnequalCraftStack.getItemMeta(), is(not(stack.getItemMeta()))); + } + + @Test + public void testBukkitYamlDeserialize() throws Throwable { + testYamlDeserialize(new BukkitWrapper(provider), new BukkitWrapper(unequalProvider)); + } + + @Test + public void testCraftYamlDeserialize() throws Throwable { + testYamlDeserialize(new CraftWrapper(provider), new CraftWrapper(unequalProvider)); + } + + @Test + public void testBukkitStreamDeserialize() throws Throwable { + testStreamDeserialize(new BukkitWrapper(provider), new BukkitWrapper(unequalProvider)); + } + + @Test + public void testCraftStreamDeserialize() throws Throwable { + testStreamDeserialize(new CraftWrapper(provider), new CraftWrapper(unequalProvider)); + } + + static void testStreamDeserialize(StackWrapper provider, StackWrapper unequalProvider) throws Throwable { + final ItemStack stack = provider.stack(); + final ItemStack unequalStack = unequalProvider.stack(); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ObjectOutputStream oos = null; + try { + oos = new BukkitObjectOutputStream(out); + + oos.writeObject(stack); + oos.writeObject(unequalStack); + } finally { + if (oos != null) { + try { + oos.close(); + } catch (IOException ex) { + } + } + } + + final String data = new String(Base64Coder.encode(out.toByteArray())); + + ObjectInputStream ois = null; + + final ItemStack readFirst; + final ItemStack readSecond; + + try { + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + ois = new BukkitObjectInputStream(in); + + readFirst = (ItemStack) ois.readObject(); + readSecond = (ItemStack) ois.readObject(); + } finally { + if (ois != null) { + try { + ois.close(); + } catch (IOException ex) { + } + } + } + + testEqualities(data, readFirst, readSecond, stack, unequalStack); + } + + static void testYamlDeserialize(StackWrapper provider, StackWrapper unequalProvider) { + final ItemStack stack = provider.stack(); + final ItemStack unequalStack = unequalProvider.stack(); + final YamlConfiguration configOut = new YamlConfiguration(); + + configOut.set("provider", stack); + configOut.set("unequal", unequalStack); + + final String out = '\n' + configOut.saveToString(); + final YamlConfiguration configIn = new YamlConfiguration(); + + try { + configIn.loadFromString(out); + } catch (InvalidConfigurationException ex) { + throw new RuntimeException(out, ex); + } + + testEqualities(out, configIn.getItemStack("provider"), configIn.getItemStack("unequal"), stack, unequalStack); + } + + static void testEqualities(String information, ItemStack primaryRead, ItemStack unequalRead, ItemStack primaryOriginal, ItemStack unequalOriginal) { + assertThat(information, primaryRead, allOf(equalTo(primaryOriginal), sameHash(primaryOriginal))); + assertThat(information, unequalRead, allOf(equalTo(unequalOriginal), sameHash(unequalOriginal))); + assertThat(information, primaryRead, is(not(unequalOriginal))); + assertThat(information, primaryRead, is(not(unequalRead))); + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/NMSCraftItemStackTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/NMSCraftItemStackTest.java new file mode 100644 index 000000000000..5a43fd867f0b --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/NMSCraftItemStackTest.java @@ -0,0 +1,35 @@ +package org.bukkit.craftbukkit.inventory; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; + +import net.minecraft.server.Enchantments; + +import org.bukkit.inventory.ItemStack; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Test; + +public class NMSCraftItemStackTest extends AbstractTestingBase { + + @Test + public void testCloneEnchantedItem() throws Exception { + net.minecraft.server.ItemStack nmsItemStack = new net.minecraft.server.ItemStack(net.minecraft.server.Items.POTION); + nmsItemStack.addEnchantment(Enchantments.DAMAGE_ALL, 1); + ItemStack itemStack = CraftItemStack.asCraftMirror(nmsItemStack); + ItemStack clone = itemStack.clone(); + assertThat(clone.getType(), is(itemStack.getType())); + assertThat(clone.getAmount(), is(itemStack.getAmount())); + assertThat(clone.getDurability(), is(itemStack.getDurability())); + assertThat(clone.getEnchantments(), is(itemStack.getEnchantments())); + assertThat(clone.getData(), is(itemStack.getData())); + assertThat(clone, is(itemStack)); + } + + @Test + public void testCloneNullItem() throws Exception { + net.minecraft.server.ItemStack nmsItemStack = null; + ItemStack itemStack = CraftItemStack.asCraftMirror(nmsItemStack); + ItemStack clone = itemStack.clone(); + assertThat(clone, is(itemStack)); + } +} diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/PlayerInventoryTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/PlayerInventoryTest.java new file mode 100644 index 000000000000..fc0da1a9bf35 --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/inventory/PlayerInventoryTest.java @@ -0,0 +1,60 @@ +package org.bukkit.craftbukkit.inventory; + +import net.minecraft.server.ItemStack; +import net.minecraft.server.Items; +import net.minecraft.server.PlayerInventory; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Test; +import static org.junit.Assert.*; + +public class PlayerInventoryTest extends AbstractTestingBase { + + @Test + public void testCanHold() throws Exception { + ItemStack itemStackApple = new ItemStack(Items.APPLE); + ItemStack itemStack1Coal = new ItemStack(Items.COAL); + ItemStack itemStack32Coal = new ItemStack(Items.COAL, 32); + ItemStack itemStack63Coal = new ItemStack(Items.COAL, 63); + ItemStack itemStack64Coal = new ItemStack(Items.COAL, 64); + + // keep one slot empty + PlayerInventory inventory = new PlayerInventory(null); + for (int i = 0; i < inventory.items.size() - 1; i++) { + inventory.setItem(i, itemStackApple); + } + + // one slot empty + assertEquals(1, inventory.canHold(itemStack1Coal)); + assertEquals(32, inventory.canHold(itemStack32Coal)); + assertEquals(64, inventory.canHold(itemStack64Coal)); + + // no free space with a stack of the item to check in the inventory + inventory.setItem(inventory.items.size() - 1, itemStack64Coal); + + assertEquals(0, inventory.canHold(itemStack1Coal)); + assertEquals(0, inventory.canHold(itemStack32Coal)); + assertEquals(0, inventory.canHold(itemStack64Coal)); + + // no free space without a stack of the item to check in the inventory + inventory.setItem(inventory.items.size() - 1, itemStackApple); + + assertEquals(0, inventory.canHold(itemStack1Coal)); + assertEquals(0, inventory.canHold(itemStack32Coal)); + assertEquals(0, inventory.canHold(itemStack64Coal)); + + // free space for 32 items in one slot + inventory.setItem(inventory.items.size() - 1, itemStack32Coal); + + assertEquals(1, inventory.canHold(itemStack1Coal)); + assertEquals(32, inventory.canHold(itemStack32Coal)); + assertEquals(32, inventory.canHold(itemStack64Coal)); + + // free space for 1 item in two slots + inventory.setItem(inventory.items.size() - 1, itemStack63Coal); + inventory.setItem(inventory.items.size() - 2, itemStack63Coal); + + assertEquals(1, inventory.canHold(itemStack1Coal)); + assertEquals(2, inventory.canHold(itemStack32Coal)); + assertEquals(2, inventory.canHold(itemStack64Coal)); + } +} diff --git a/src/test/java/org/bukkit/entity/EnderDragonPhaseTest.java b/src/test/java/org/bukkit/entity/EnderDragonPhaseTest.java new file mode 100644 index 000000000000..7e8d5a0e543d --- /dev/null +++ b/src/test/java/org/bukkit/entity/EnderDragonPhaseTest.java @@ -0,0 +1,50 @@ +package org.bukkit.entity; + +import net.minecraft.server.DragonControllerPhase; +import org.bukkit.craftbukkit.entity.CraftEnderDragon; +import org.junit.Assert; +import org.junit.Test; + +import static junit.framework.TestCase.assertNotNull; + +public class EnderDragonPhaseTest { + + @Test + public void testNotNull() { + for (EnderDragon.Phase phase : EnderDragon.Phase.values()) { + DragonControllerPhase dragonControllerPhase = CraftEnderDragon.getMinecraftPhase(phase); + assertNotNull(phase.name(), dragonControllerPhase); + assertNotNull(phase.name(), CraftEnderDragon.getBukkitPhase(dragonControllerPhase)); + } + } + + @Test + public void testBukkitToMinecraft() { + Assert.assertEquals("CIRCLING", CraftEnderDragon.getMinecraftPhase(EnderDragon.Phase.CIRCLING), DragonControllerPhase.HOLDING_PATTERN); + Assert.assertEquals("STRAFING", CraftEnderDragon.getMinecraftPhase(EnderDragon.Phase.STRAFING), DragonControllerPhase.STRAFE_PLAYER); + Assert.assertEquals("FLY_TO_PORTAL", CraftEnderDragon.getMinecraftPhase(EnderDragon.Phase.FLY_TO_PORTAL), DragonControllerPhase.LANDING_APPROACH); + Assert.assertEquals("LAND_ON_PORTAL", CraftEnderDragon.getMinecraftPhase(EnderDragon.Phase.LAND_ON_PORTAL), DragonControllerPhase.LANDING); + Assert.assertEquals("LEAVE_PORTAL", CraftEnderDragon.getMinecraftPhase(EnderDragon.Phase.LEAVE_PORTAL), DragonControllerPhase.TAKEOFF); + Assert.assertEquals("BREATH_ATTACK", CraftEnderDragon.getMinecraftPhase(EnderDragon.Phase.BREATH_ATTACK), DragonControllerPhase.SITTING_FLAMING); + Assert.assertEquals("SEARCH_FOR_BREATH_ATTACK_TARGET", CraftEnderDragon.getMinecraftPhase(EnderDragon.Phase.SEARCH_FOR_BREATH_ATTACK_TARGET), DragonControllerPhase.SITTING_SCANNING); + Assert.assertEquals("ROAR_BEFORE_ATTACK", CraftEnderDragon.getMinecraftPhase(EnderDragon.Phase.ROAR_BEFORE_ATTACK), DragonControllerPhase.SITTING_ATTACKING); + Assert.assertEquals("CHARGE_PLAYER", CraftEnderDragon.getMinecraftPhase(EnderDragon.Phase.CHARGE_PLAYER), DragonControllerPhase.CHARGING_PLAYER); + Assert.assertEquals("DYING", CraftEnderDragon.getMinecraftPhase(EnderDragon.Phase.DYING), DragonControllerPhase.DYING); + Assert.assertEquals("HOVER", CraftEnderDragon.getMinecraftPhase(EnderDragon.Phase.HOVER), DragonControllerPhase.HOVER); + } + + @Test + public void testMinecraftToBukkit() { + Assert.assertEquals("CIRCLING", CraftEnderDragon.getBukkitPhase(DragonControllerPhase.HOLDING_PATTERN), EnderDragon.Phase.CIRCLING); + Assert.assertEquals("STRAFING", CraftEnderDragon.getBukkitPhase(DragonControllerPhase.STRAFE_PLAYER), EnderDragon.Phase.STRAFING); + Assert.assertEquals("FLY_TO_PORTAL", CraftEnderDragon.getBukkitPhase(DragonControllerPhase.LANDING_APPROACH), EnderDragon.Phase.FLY_TO_PORTAL); + Assert.assertEquals("LAND_ON_PORTAL", CraftEnderDragon.getBukkitPhase(DragonControllerPhase.LANDING), EnderDragon.Phase.LAND_ON_PORTAL); + Assert.assertEquals("LEAVE_PORTAL", CraftEnderDragon.getBukkitPhase(DragonControllerPhase.TAKEOFF), EnderDragon.Phase.LEAVE_PORTAL); + Assert.assertEquals("BREATH_ATTACK", CraftEnderDragon.getBukkitPhase(DragonControllerPhase.SITTING_FLAMING), EnderDragon.Phase.BREATH_ATTACK); + Assert.assertEquals("SEARCH_FOR_BREATH_ATTACK_TARGET", CraftEnderDragon.getBukkitPhase(DragonControllerPhase.SITTING_SCANNING), EnderDragon.Phase.SEARCH_FOR_BREATH_ATTACK_TARGET); + Assert.assertEquals("ROAR_BEFORE_ATTACK", CraftEnderDragon.getBukkitPhase(DragonControllerPhase.SITTING_ATTACKING), EnderDragon.Phase.ROAR_BEFORE_ATTACK); + Assert.assertEquals("CHARGE_PLAYER", CraftEnderDragon.getBukkitPhase(DragonControllerPhase.CHARGING_PLAYER), EnderDragon.Phase.CHARGE_PLAYER); + Assert.assertEquals("DYING", CraftEnderDragon.getBukkitPhase(DragonControllerPhase.DYING), EnderDragon.Phase.DYING); + Assert.assertEquals("HOVER", CraftEnderDragon.getBukkitPhase(DragonControllerPhase.HOVER), EnderDragon.Phase.HOVER); + } +} diff --git a/src/test/java/org/bukkit/entity/EntityTypesTest.java b/src/test/java/org/bukkit/entity/EntityTypesTest.java new file mode 100644 index 000000000000..465bc9735e9f --- /dev/null +++ b/src/test/java/org/bukkit/entity/EntityTypesTest.java @@ -0,0 +1,31 @@ +package org.bukkit.entity; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; +import net.minecraft.server.EntityTypes; +import net.minecraft.server.IRegistry; +import net.minecraft.server.MinecraftKey; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Assert; +import org.junit.Test; + +public class EntityTypesTest extends AbstractTestingBase { + + @Test + public void testMaps() { + Set allBukkit = Arrays.stream(EntityType.values()).filter((b) -> b.getName() != null).collect(Collectors.toSet()); + + for (Object o : IRegistry.ENTITY_TYPE) { + EntityTypes nms = (EntityTypes) o; // Eclipse fail + MinecraftKey key = EntityTypes.getName(nms); + + EntityType bukkit = EntityType.fromName(key.getKey()); + Assert.assertNotNull("Missing nms->bukkit " + key, bukkit); + + Assert.assertTrue("Duplicate entity nms->" + bukkit, allBukkit.remove(bukkit)); + } + + Assert.assertTrue("Unmapped bukkit entities " + allBukkit, allBukkit.isEmpty()); + } +} diff --git a/src/test/java/org/bukkit/entity/TropicalFishTest.java b/src/test/java/org/bukkit/entity/TropicalFishTest.java new file mode 100644 index 000000000000..b65dc340a817 --- /dev/null +++ b/src/test/java/org/bukkit/entity/TropicalFishTest.java @@ -0,0 +1,45 @@ +package org.bukkit.entity; + +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; +import org.bukkit.DyeColor; +import org.bukkit.craftbukkit.entity.CraftTropicalFish; +import org.bukkit.entity.TropicalFish.Pattern; + +import org.junit.Test; + +public class TropicalFishTest { + + @Test + public void testVariants() { + testVariant(65536, DyeColor.ORANGE, DyeColor.WHITE, Pattern.KOB); + testVariant(917504, DyeColor.RED, DyeColor.WHITE, Pattern.KOB); + testVariant(918273, DyeColor.RED, DyeColor.WHITE, Pattern.BLOCKFISH); + testVariant(918529, DyeColor.RED, DyeColor.WHITE, Pattern.BETTY); + testVariant(16778497, DyeColor.WHITE, DyeColor.ORANGE, Pattern.CLAYFISH); + testVariant(50660352, DyeColor.LIME, DyeColor.LIGHT_BLUE, Pattern.BRINELY); + testVariant(50726144, DyeColor.PINK, DyeColor.LIGHT_BLUE, Pattern.SPOTTY); + testVariant(50790656, DyeColor.GRAY, DyeColor.LIGHT_BLUE, Pattern.SUNSTREAK); + testVariant(67108865, DyeColor.WHITE, DyeColor.YELLOW, Pattern.FLOPPER); + testVariant(67110144, DyeColor.WHITE, DyeColor.YELLOW, Pattern.SPOTTY); + testVariant(67371265, DyeColor.YELLOW, DyeColor.YELLOW, Pattern.STRIPEY); + testVariant(67764993, DyeColor.PURPLE, DyeColor.YELLOW, Pattern.BLOCKFISH); + testVariant(101253888, DyeColor.CYAN, DyeColor.PINK, Pattern.DASHER); + testVariant(117441025, DyeColor.WHITE, DyeColor.GRAY, Pattern.GLITTER); + testVariant(117441280, DyeColor.WHITE, DyeColor.GRAY, Pattern.DASHER); + testVariant(117441536, DyeColor.WHITE, DyeColor.GRAY, Pattern.BRINELY); + testVariant(117506305, DyeColor.ORANGE, DyeColor.GRAY, Pattern.STRIPEY); + testVariant(117899265, DyeColor.GRAY, DyeColor.GRAY, Pattern.FLOPPER); + testVariant(118161664, DyeColor.BLUE, DyeColor.GRAY, Pattern.SUNSTREAK); + testVariant(134217984, DyeColor.WHITE, DyeColor.LIGHT_GRAY, Pattern.SUNSTREAK); + testVariant(234882305, DyeColor.WHITE, DyeColor.RED, Pattern.CLAYFISH); + testVariant(235340288, DyeColor.GRAY, DyeColor.RED, Pattern.SNOOPER); + } + + private void testVariant(int variant, DyeColor bodyColor, DyeColor patternColor, Pattern pattern) { + assertThat("variant write", CraftTropicalFish.getData(patternColor, bodyColor, pattern), is(variant)); + assertThat("pattern colour read", CraftTropicalFish.getPatternColor(variant), is(patternColor)); + assertThat("body colour read", CraftTropicalFish.getBodyColor(variant), is(bodyColor)); + assertThat("pattern read", CraftTropicalFish.getPattern(variant), is(pattern)); + } +} diff --git a/src/test/java/org/bukkit/map/MapTest.java b/src/test/java/org/bukkit/map/MapTest.java new file mode 100644 index 000000000000..2dde26d3fd4a --- /dev/null +++ b/src/test/java/org/bukkit/map/MapTest.java @@ -0,0 +1,62 @@ + +package org.bukkit.map; + +import java.awt.Color; +import java.util.logging.Level; +import java.util.logging.Logger; +import net.minecraft.server.MaterialMapColor; +import org.junit.Assert; +import org.junit.Test; + +public class MapTest { + + private static final Logger logger = Logger.getLogger("MapTest"); + + private static final int[] modifiers = {180, 220, 255, 135}; + + @Test + public void testColors() { + MaterialMapColor[] nmsColors = MaterialMapColor.a; + Color[] bukkitColors = MapPalette.colors; + + boolean fail = false; + for (int i = 0; i < nmsColors.length; i++) { + if (nmsColors[i] == null) { + break; + } + int rgb = nmsColors[i].rgb; + + int r = (rgb >> 16) & 0xFF; + int g = (rgb >> 8) & 0xFF; + int b = rgb & 0xFF; + + if (i + 1 > bukkitColors.length / 4) { + for (int modi : modifiers) { + int mr = (r * modi) / 255; + int mg = (g * modi) / 255; + int mb = (b * modi) / 255; + logger.log(Level.WARNING, "Missing color: c({0}, {1}, {2})", new Object[]{mr, mg, mb}); + } + fail = true; + } else { + for (int j = 0; j < modifiers.length; j++) { + int modi = modifiers[j]; + Color bukkit = bukkitColors[i * 4 + j]; + int mr = (r * modi) / 255; + int mg = (g * modi) / 255; + int mb = (b * modi) / 255; + + if (bukkit.getRed() != mr || bukkit.getGreen() != mg || bukkit.getBlue() != mb) { + logger.log(Level.WARNING, "Incorrect color: {6} {7} c({0}, {1}, {2}) != c({3}, {4}, {5})", new Object[]{ + bukkit.getRed(), bukkit.getGreen(), bukkit.getBlue(), + mr, mg, mb, + i, j + }); + fail = true; + } + } + } + } + Assert.assertFalse(fail); + } +} diff --git a/src/test/java/org/bukkit/potion/PotionTest.java b/src/test/java/org/bukkit/potion/PotionTest.java new file mode 100644 index 000000000000..f6c23ebba439 --- /dev/null +++ b/src/test/java/org/bukkit/potion/PotionTest.java @@ -0,0 +1,35 @@ +package org.bukkit.potion; + +import static org.junit.Assert.*; + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import net.minecraft.server.IRegistry; + +import net.minecraft.server.MobEffect; +import net.minecraft.server.MobEffectList; +import net.minecraft.server.PotionRegistry; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Test; + +public class PotionTest extends AbstractTestingBase { + @Test + public void testEffectCompleteness() throws Throwable { + Map effects = new EnumMap(PotionType.class); + for (Object reg : IRegistry.POTION) { + List eff = ((PotionRegistry)reg).a(); + if (eff.size() != 1) continue; + int id = MobEffectList.getId(eff.get(0).getMobEffect()); + PotionEffectType type = PotionEffectType.getById(id); + assertNotNull(String.valueOf(id), PotionEffectType.getById(id)); + + PotionType enumType = PotionType.getByEffect(type); + assertNotNull(type.getName(), enumType); + + effects.put(enumType, enumType.name()); + } + + assertEquals(effects.entrySet().size(), PotionType.values().length - /* PotionTypes with no/shared Effects */ 6); + } +} diff --git a/src/test/java/org/bukkit/support/AbstractTestingBase.java b/src/test/java/org/bukkit/support/AbstractTestingBase.java new file mode 100644 index 000000000000..53cbb525e468 --- /dev/null +++ b/src/test/java/org/bukkit/support/AbstractTestingBase.java @@ -0,0 +1,53 @@ +package org.bukkit.support; + +import com.google.common.collect.ImmutableList; +import java.util.Collections; +import java.util.List; +import net.minecraft.server.DispenserRegistry; +import net.minecraft.server.EnumResourcePackType; +import net.minecraft.server.LootTableRegistry; +import net.minecraft.server.ResourceManager; +import net.minecraft.server.ResourcePackVanilla; +import net.minecraft.server.TagRegistry; +import org.bukkit.Material; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.junit.Assert; + +/** + * If you are getting: java.lang.ExceptionInInitializerError + * at net.minecraft.server.StatisticList.(SourceFile:58) + * at net.minecraft.server.Item.(SourceFile:252) + * at net.minecraft.server.Block.(Block.java:577) + * + * extend this class to solve it. + */ +public abstract class AbstractTestingBase { + // Materials that only exist in block form (or are legacy) + public static final List INVALIDATED_MATERIALS; + + public static final LootTableRegistry LOOT_TABLE_REGISTRY; + public static final TagRegistry TAG_REGISTRY; + + static { + DispenserRegistry.c(); + // Set up resource manager + ResourceManager resourceManager = new ResourceManager(EnumResourcePackType.SERVER_DATA); + // add tags and loot tables for unit tests + resourceManager.a(TAG_REGISTRY = new TagRegistry()); + resourceManager.a(LOOT_TABLE_REGISTRY = new LootTableRegistry()); + // Register vanilla pack + resourceManager.a(Collections.singletonList(new ResourcePackVanilla("minecraft"))); + + DummyServer.setup(); + DummyEnchantments.setup(); + + ImmutableList.Builder builder = ImmutableList.builder(); + for (Material m : Material.values()) { + if (m.isLegacy() || CraftMagicNumbers.getItem(m) == null) { + builder.add(m); + } + } + INVALIDATED_MATERIALS = builder.build(); + Assert.assertEquals("Expected 543 invalidated materials (got " + INVALIDATED_MATERIALS.size() + ")", 543, INVALIDATED_MATERIALS.size()); + } +} diff --git a/src/test/java/org/bukkit/support/DummyEnchantments.java b/src/test/java/org/bukkit/support/DummyEnchantments.java new file mode 100644 index 000000000000..f3cc27e713a3 --- /dev/null +++ b/src/test/java/org/bukkit/support/DummyEnchantments.java @@ -0,0 +1,12 @@ +package org.bukkit.support; + +import net.minecraft.server.Enchantments; + +public class DummyEnchantments { + static { + Enchantments.DAMAGE_ALL.getClass(); + org.bukkit.enchantments.Enchantment.stopAcceptingRegistrations(); + } + + public static void setup() {} +} diff --git a/src/test/java/org/bukkit/support/DummyServer.java b/src/test/java/org/bukkit/support/DummyServer.java new file mode 100644 index 000000000000..3c489b6cc7d9 --- /dev/null +++ b/src/test/java/org/bukkit/support/DummyServer.java @@ -0,0 +1,111 @@ +package org.bukkit.support; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.HashMap; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Server; +import org.bukkit.craftbukkit.CraftLootTable; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.inventory.CraftItemFactory; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.craftbukkit.util.Versioning; + +public class DummyServer implements InvocationHandler { + private static interface MethodHandler { + Object handle(DummyServer server, Object[] args); + } + private static final HashMap methods = new HashMap(); + static { + try { + methods.put( + Server.class.getMethod("getItemFactory"), + new MethodHandler() { + public Object handle(DummyServer server, Object[] args) { + return CraftItemFactory.instance(); + } + } + ); + methods.put( + Server.class.getMethod("getName"), + new MethodHandler() { + public Object handle(DummyServer server, Object[] args) { + return DummyServer.class.getName(); + } + } + ); + methods.put( + Server.class.getMethod("getVersion"), + new MethodHandler() { + public Object handle(DummyServer server, Object[] args) { + return DummyServer.class.getPackage().getImplementationVersion(); + } + } + ); + methods.put( + Server.class.getMethod("getBukkitVersion"), + new MethodHandler() { + public Object handle(DummyServer server, Object[] args) { + return Versioning.getBukkitVersion(); + } + } + ); + methods.put( + Server.class.getMethod("getLogger"), + new MethodHandler() { + final Logger logger = Logger.getLogger(DummyServer.class.getCanonicalName()); + public Object handle(DummyServer server, Object[] args) { + return logger; + } + } + ); + methods.put( + Server.class.getMethod("getUnsafe"), + new MethodHandler() { + public Object handle(DummyServer server, Object[] args) { + return CraftMagicNumbers.INSTANCE; + } + } + ); + methods.put( + Server.class.getMethod("createBlockData", Material.class), + new MethodHandler() { + final Logger logger = Logger.getLogger(DummyServer.class.getCanonicalName()); + public Object handle(DummyServer server, Object[] args) { + return CraftBlockData.newData((Material) args[0], null); + } + } + ); + methods.put(Server.class.getMethod("getLootTable", NamespacedKey.class), + new MethodHandler() { + @Override + public Object handle(DummyServer server, Object[] args) { + NamespacedKey key = (NamespacedKey) args[0]; + return new CraftLootTable(key, AbstractTestingBase.LOOT_TABLE_REGISTRY.getLootTable(CraftNamespacedKey.toMinecraft(key))); + } + } + ); + Bukkit.setServer(Proxy.getProxyClass(Server.class.getClassLoader(), Server.class).asSubclass(Server.class).getConstructor(InvocationHandler.class).newInstance(new DummyServer())); + } catch (Throwable t) { + throw new Error(t); + } + } + + public static void setup() {} + + private DummyServer() {}; + + public Object invoke(Object proxy, Method method, Object[] args) { + MethodHandler handler = methods.get(method); + if (handler != null) { + return handler.handle(this, args); + } + throw new UnsupportedOperationException(String.valueOf(method)); + } +} diff --git a/src/test/java/org/bukkit/support/Matchers.java b/src/test/java/org/bukkit/support/Matchers.java new file mode 100644 index 000000000000..b190c6736975 --- /dev/null +++ b/src/test/java/org/bukkit/support/Matchers.java @@ -0,0 +1,30 @@ +package org.bukkit.support; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +public final class Matchers { + + private Matchers() {} + + public static Matcher sameHash(T value) { + return new SameHash(value); + } + + static class SameHash extends BaseMatcher { + private final int expected; + + SameHash(T object) { + expected = object.hashCode(); + } + + public boolean matches(Object item) { + return item.hashCode() == expected; + } + + public void describeTo(Description description) { + description.appendValue(expected); + } + } +} diff --git a/src/test/java/org/bukkit/support/Util.java b/src/test/java/org/bukkit/support/Util.java new file mode 100644 index 000000000000..2f24d9a40836 --- /dev/null +++ b/src/test/java/org/bukkit/support/Util.java @@ -0,0 +1,31 @@ +package org.bukkit.support; + +import java.lang.reflect.Field; + +public class Util { + /* + public static T getInternalState(Object object, String fieldName) { + return getInternalState(object.getClass(), object, fieldName); + } + */ + + @SuppressWarnings("unchecked") + public static T getInternalState(Class clazz, Object object, String fieldName) { + Field field; + try { + field = clazz.getDeclaredField(fieldName); + } catch (SecurityException e) { + throw new RuntimeException("Not allowed to access " + clazz, e); + } catch (NoSuchFieldException e) { + throw new RuntimeException("Unable to find field " + fieldName, e); + } + + field.setAccessible(true); + try { + return (T) field.get(object); + } catch (IllegalArgumentException e) { + } catch (IllegalAccessException e) { + } + throw new RuntimeException("Unable to get internal value"); + } +}