Repository: H4PM/Elywing Branch: master Commit: 2308c301023b Files: 1161 Total size: 3.6 MB Directory structure: gitextract_wdvdv96d/ ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── src/ │ ├── pocketmine/ │ │ ├── Achievement.php │ │ ├── Collectable.php │ │ ├── CompatibleClassLoader.php │ │ ├── CrashDump.php │ │ ├── IPlayer.php │ │ ├── MemoryManager.php │ │ ├── OfflinePlayer.php │ │ ├── Player.php │ │ ├── PocketMine.php │ │ ├── Server.php │ │ ├── Thread.php │ │ ├── ThreadManager.php │ │ ├── Worker.php │ │ ├── block/ │ │ │ ├── AcaciaDoor.php │ │ │ ├── AcaciaDoorBlock.php │ │ │ ├── AcaciaWoodStairs.php │ │ │ ├── ActivatorRail.php │ │ │ ├── ActiveRedstoneLamp.php │ │ │ ├── Air.php │ │ │ ├── Anvil.php │ │ │ ├── Beacon.php │ │ │ ├── Bed.php │ │ │ ├── Bedrock.php │ │ │ ├── Beetroot.php │ │ │ ├── BirchDoor.php │ │ │ ├── BirchDoorBlock.php │ │ │ ├── BirchWoodStairs.php │ │ │ ├── Block.php │ │ │ ├── BlockIds.php │ │ │ ├── Bookshelf.php │ │ │ ├── BrewingStand.php │ │ │ ├── BrickStairs.php │ │ │ ├── Bricks.php │ │ │ ├── BrownMushroom.php │ │ │ ├── BrownMushroomBlock.php │ │ │ ├── BurningFurnace.php │ │ │ ├── Cactus.php │ │ │ ├── Cake.php │ │ │ ├── Carpet.php │ │ │ ├── Carrot.php │ │ │ ├── Cauldron.php │ │ │ ├── CauldronBlock.php │ │ │ ├── Chest.php │ │ │ ├── ChorusPlant.php │ │ │ ├── Clay.php │ │ │ ├── Coal.php │ │ │ ├── CoalOre.php │ │ │ ├── Cobblestone.php │ │ │ ├── CobblestoneStairs.php │ │ │ ├── Cobweb.php │ │ │ ├── CocoaBlock.php │ │ │ ├── ComparatorBlock.php │ │ │ ├── Crops.php │ │ │ ├── Dandelion.php │ │ │ ├── DarkOakDoor.php │ │ │ ├── DarkOakDoorBlock.php │ │ │ ├── DarkOakWoodStairs.php │ │ │ ├── DaylightDetector.php │ │ │ ├── DaylightDetectorInverted.php │ │ │ ├── DaylightSensor.php │ │ │ ├── DaylightSensorInverted.php │ │ │ ├── DeadBush.php │ │ │ ├── DetectorRail.php │ │ │ ├── Diamond.php │ │ │ ├── DiamondOre.php │ │ │ ├── Dirt.php │ │ │ ├── Dispenser.php │ │ │ ├── Door.php │ │ │ ├── DoublePlant.php │ │ │ ├── DoubleRedSandstoneSlab.php │ │ │ ├── DoubleSlab.php │ │ │ ├── DoubleWoodSlab.php │ │ │ ├── DragonEgg.php │ │ │ ├── Dropper.php │ │ │ ├── ElectricalAppliance.php │ │ │ ├── Emerald.php │ │ │ ├── EmeraldOre.php │ │ │ ├── EnchantingTable.php │ │ │ ├── EndPortalFrame.php │ │ │ ├── EndRod.php │ │ │ ├── EndStone.php │ │ │ ├── EndStoneBricks.php │ │ │ ├── EnderChest.php │ │ │ ├── Fallable.php │ │ │ ├── Farmland.php │ │ │ ├── Fence.php │ │ │ ├── FenceGate.php │ │ │ ├── FenceGateAcacia.php │ │ │ ├── FenceGateBirch.php │ │ │ ├── FenceGateDarkOak.php │ │ │ ├── FenceGateJungle.php │ │ │ ├── FenceGateSpruce.php │ │ │ ├── Fire.php │ │ │ ├── Flowable.php │ │ │ ├── Flower.php │ │ │ ├── FlowerPot.php │ │ │ ├── Furnace.php │ │ │ ├── Glass.php │ │ │ ├── GlassPane.php │ │ │ ├── GlowingObsidian.php │ │ │ ├── GlowingRedstoneOre.php │ │ │ ├── Glowstone.php │ │ │ ├── Gold.php │ │ │ ├── GoldOre.php │ │ │ ├── Grass.php │ │ │ ├── GrassPath.php │ │ │ ├── Gravel.php │ │ │ ├── HardenedClay.php │ │ │ ├── HayBale.php │ │ │ ├── HeavyWeightedPressurePlate.php │ │ │ ├── Hopper.php │ │ │ ├── HopperBlock.php │ │ │ ├── Ice.php │ │ │ ├── InactiveRedstoneLamp.php │ │ │ ├── InvisibleBedrock.php │ │ │ ├── Iron.php │ │ │ ├── IronBars.php │ │ │ ├── IronDoor.php │ │ │ ├── IronOre.php │ │ │ ├── IronTrapdoor.php │ │ │ ├── ItemFrame.php │ │ │ ├── ItemFrameBlock.php │ │ │ ├── JungleDoor.php │ │ │ ├── JungleDoorBlock.php │ │ │ ├── JungleWoodStairs.php │ │ │ ├── Ladder.php │ │ │ ├── Lapis.php │ │ │ ├── LapisOre.php │ │ │ ├── Lava.php │ │ │ ├── Leaves.php │ │ │ ├── Leaves2.php │ │ │ ├── Lever.php │ │ │ ├── LightWeightedPressurePlate.php │ │ │ ├── Liquid.php │ │ │ ├── LitPumpkin.php │ │ │ ├── LitRedstoneLamp.php │ │ │ ├── LitRedstoneTorch.php │ │ │ ├── Melon.php │ │ │ ├── MelonStem.php │ │ │ ├── MobHead.php │ │ │ ├── MonsterEggBlock.php │ │ │ ├── MonsterSpawner.php │ │ │ ├── MossStone.php │ │ │ ├── Mycelium.php │ │ │ ├── NetherBrick.php │ │ │ ├── NetherBrickFence.php │ │ │ ├── NetherBrickStairs.php │ │ │ ├── NetherQuartzOre.php │ │ │ ├── NetherReactor.php │ │ │ ├── NetherWart.php │ │ │ ├── NetherWartBlock.php │ │ │ ├── Netherrack.php │ │ │ ├── Noteblock.php │ │ │ ├── Obsidian.php │ │ │ ├── PackedIce.php │ │ │ ├── Piston.php │ │ │ ├── PistonHead.php │ │ │ ├── Planks.php │ │ │ ├── Podzol.php │ │ │ ├── Portal.php │ │ │ ├── PortalBlock.php │ │ │ ├── Potato.php │ │ │ ├── PoweredComparatorBlock.php │ │ │ ├── PoweredRail.php │ │ │ ├── PoweredRepeater.php │ │ │ ├── PoweredRepeaterBlock.php │ │ │ ├── PressurePlate.php │ │ │ ├── Prismarine.php │ │ │ ├── Pumpkin.php │ │ │ ├── PumpkinStem.php │ │ │ ├── PurpurBlock.php │ │ │ ├── PurpurStairs.php │ │ │ ├── Quartz.php │ │ │ ├── QuartzStairs.php │ │ │ ├── Rail.php │ │ │ ├── RedMushroom.php │ │ │ ├── RedMushroomBlock.php │ │ │ ├── RedSandstone.php │ │ │ ├── RedSandstoneSlab.php │ │ │ ├── RedSandstoneStairs.php │ │ │ ├── Redstone.php │ │ │ ├── RedstoneLamp.php │ │ │ ├── RedstoneOre.php │ │ │ ├── RedstoneSource.php │ │ │ ├── RedstoneTorch.php │ │ │ ├── RedstoneWire.php │ │ │ ├── RepeaterBlock.php │ │ │ ├── Sand.php │ │ │ ├── Sandstone.php │ │ │ ├── SandstoneStairs.php │ │ │ ├── Sapling.php │ │ │ ├── SeaLantern.php │ │ │ ├── SignPost.php │ │ │ ├── SkullBlock.php │ │ │ ├── Slab.php │ │ │ ├── Slab2.php │ │ │ ├── SlimeBlock.php │ │ │ ├── Snow.php │ │ │ ├── SnowLayer.php │ │ │ ├── Solid.php │ │ │ ├── SolidLight.php │ │ │ ├── SoulSand.php │ │ │ ├── Sponge.php │ │ │ ├── SpruceDoor.php │ │ │ ├── SpruceDoorBlock.php │ │ │ ├── SpruceWoodStairs.php │ │ │ ├── StainedClay.php │ │ │ ├── Stair.php │ │ │ ├── StickyPiston.php │ │ │ ├── StillLava.php │ │ │ ├── StillWater.php │ │ │ ├── Stone.php │ │ │ ├── StoneBrickStairs.php │ │ │ ├── StoneBricks.php │ │ │ ├── StoneButton.php │ │ │ ├── StonePressurePlate.php │ │ │ ├── StoneWall.php │ │ │ ├── Stonecutter.php │ │ │ ├── Sugarcane.php │ │ │ ├── TNT.php │ │ │ ├── TallGrass.php │ │ │ ├── Thin.php │ │ │ ├── Torch.php │ │ │ ├── Transparent.php │ │ │ ├── Trapdoor.php │ │ │ ├── TrappedChest.php │ │ │ ├── Tripwire.php │ │ │ ├── TripwireHook.php │ │ │ ├── UnlitRedstoneTorch.php │ │ │ ├── UnpoweredRepeater.php │ │ │ ├── Vine.php │ │ │ ├── WallSign.php │ │ │ ├── Water.php │ │ │ ├── WaterLily.php │ │ │ ├── WeightedPressurePlateHeavy.php │ │ │ ├── WeightedPressurePlateLight.php │ │ │ ├── Wheat.php │ │ │ ├── Wood.php │ │ │ ├── Wood2.php │ │ │ ├── WoodDoor.php │ │ │ ├── WoodSlab.php │ │ │ ├── WoodStairs.php │ │ │ ├── WoodenButton.php │ │ │ ├── WoodenPressurePlate.php │ │ │ ├── Wool.php │ │ │ └── Workbench.php │ │ ├── command/ │ │ │ ├── Command.php │ │ │ ├── CommandExecutor.php │ │ │ ├── CommandMap.php │ │ │ ├── CommandReader.php │ │ │ ├── CommandSender.php │ │ │ ├── ConsoleCommandSender.php │ │ │ ├── FormattedCommandAlias.php │ │ │ ├── PluginCommand.php │ │ │ ├── PluginIdentifiableCommand.php │ │ │ ├── RemoteConsoleCommandSender.php │ │ │ ├── SimpleCommandMap.php │ │ │ └── defaults/ │ │ │ ├── BanCidByNameCommand.php │ │ │ ├── BanCidCommand.php │ │ │ ├── BanCommand.php │ │ │ ├── BanIpByNameCommand.php │ │ │ ├── BanIpCommand.php │ │ │ ├── BanListCommand.php │ │ │ ├── DefaultGamemodeCommand.php │ │ │ ├── DeopCommand.php │ │ │ ├── DifficultyCommand.php │ │ │ ├── DumpMemoryCommand.php │ │ │ ├── EffectCommand.php │ │ │ ├── EnchantCommand.php │ │ │ ├── GamemodeCommand.php │ │ │ ├── GarbageCollectorCommand.php │ │ │ ├── GiveCommand.php │ │ │ ├── HelpCommand.php │ │ │ ├── KickCommand.php │ │ │ ├── KillCommand.php │ │ │ ├── ListCommand.php │ │ │ ├── MeCommand.php │ │ │ ├── OpCommand.php │ │ │ ├── PardonCidCommand.php │ │ │ ├── PardonCommand.php │ │ │ ├── PardonIpCommand.php │ │ │ ├── ParticleCommand.php │ │ │ ├── PluginsCommand.php │ │ │ ├── ReloadCommand.php │ │ │ ├── SaveCommand.php │ │ │ ├── SaveOffCommand.php │ │ │ ├── SaveOnCommand.php │ │ │ ├── SayCommand.php │ │ │ ├── SeedCommand.php │ │ │ ├── SetBlockCommand.php │ │ │ ├── SetWorldSpawnCommand.php │ │ │ ├── SpawnpointCommand.php │ │ │ ├── StatusCommand.php │ │ │ ├── StopCommand.php │ │ │ ├── SummonCommand.php │ │ │ ├── TeleportCommand.php │ │ │ ├── TellCommand.php │ │ │ ├── TimeCommand.php │ │ │ ├── TimingsCommand.php │ │ │ ├── TransferCommand.php │ │ │ ├── VanillaCommand.php │ │ │ ├── VersionCommand.php │ │ │ ├── WeatherCommand.php │ │ │ ├── WhitelistCommand.php │ │ │ └── XpCommand.php │ │ ├── entity/ │ │ │ ├── Ageable.php │ │ │ ├── Animal.php │ │ │ ├── Arrow.php │ │ │ ├── Attachable.php │ │ │ ├── Attribute.php │ │ │ ├── AttributeMap.php │ │ │ ├── Bat.php │ │ │ ├── Blaze.php │ │ │ ├── Boat.php │ │ │ ├── CaveSpider.php │ │ │ ├── Chicken.php │ │ │ ├── Colorable.php │ │ │ ├── Cow.php │ │ │ ├── Creature.php │ │ │ ├── Creeper.php │ │ │ ├── Damageable.php │ │ │ ├── Dragon.php │ │ │ ├── DragonFireBall.php │ │ │ ├── Effect.php │ │ │ ├── Egg.php │ │ │ ├── EnderCrystal.php │ │ │ ├── EnderPearl.php │ │ │ ├── Enderman.php │ │ │ ├── Entity.php │ │ │ ├── Explosive.php │ │ │ ├── FallingSand.php │ │ │ ├── FishingHook.php │ │ │ ├── FlyingAnimal.php │ │ │ ├── Ghast.php │ │ │ ├── Hanging.php │ │ │ ├── Human.php │ │ │ ├── Husk.php │ │ │ ├── InstantEffect.php │ │ │ ├── IronGolem.php │ │ │ ├── Item.php │ │ │ ├── LavaSlime.php │ │ │ ├── Lightning.php │ │ │ ├── Living.php │ │ │ ├── Minecart.php │ │ │ ├── MinecartChest.php │ │ │ ├── MinecartHopper.php │ │ │ ├── MinecartTNT.php │ │ │ ├── Monster.php │ │ │ ├── Mooshroom.php │ │ │ ├── NPC.php │ │ │ ├── Ocelot.php │ │ │ ├── Painting.php │ │ │ ├── Pig.php │ │ │ ├── PigZombie.php │ │ │ ├── PolarBear.php │ │ │ ├── PrimedTNT.php │ │ │ ├── Projectile.php │ │ │ ├── ProjectileSource.php │ │ │ ├── Rabbit.php │ │ │ ├── Rideable.php │ │ │ ├── Sheep.php │ │ │ ├── Shulker.php │ │ │ ├── ShulkerBullet.php │ │ │ ├── Silverfish.php │ │ │ ├── Skeleton.php │ │ │ ├── Slime.php │ │ │ ├── SnowGolem.php │ │ │ ├── Snowball.php │ │ │ ├── Spider.php │ │ │ ├── Squid.php │ │ │ ├── Stray.php │ │ │ ├── Tameable.php │ │ │ ├── ThrownExpBottle.php │ │ │ ├── ThrownPotion.php │ │ │ ├── Vehicle.php │ │ │ ├── Villager.php │ │ │ ├── WaterAnimal.php │ │ │ ├── Witch.php │ │ │ ├── WitherSkeleton.php │ │ │ ├── Wolf.php │ │ │ ├── XPOrb.php │ │ │ ├── Zombie.php │ │ │ └── ZombieVillager.php │ │ ├── event/ │ │ │ ├── Cancellable.php │ │ │ ├── Event.php │ │ │ ├── EventPriority.php │ │ │ ├── HandlerList.php │ │ │ ├── LevelTimings.php │ │ │ ├── Listener.php │ │ │ ├── TextContainer.php │ │ │ ├── Timings.php │ │ │ ├── TimingsHandler.php │ │ │ ├── TranslationContainer.php │ │ │ ├── block/ │ │ │ │ ├── BlockBreakEvent.php │ │ │ │ ├── BlockBurnEvent.php │ │ │ │ ├── BlockEvent.php │ │ │ │ ├── BlockFormEvent.php │ │ │ │ ├── BlockGrowEvent.php │ │ │ │ ├── BlockPlaceEvent.php │ │ │ │ ├── BlockSpreadEvent.php │ │ │ │ ├── BlockUpdateEvent.php │ │ │ │ ├── ItemFrameDropItemEvent.php │ │ │ │ ├── LeavesDecayEvent.php │ │ │ │ └── SignChangeEvent.php │ │ │ ├── entity/ │ │ │ │ ├── CreeperPowerEvent.php │ │ │ │ ├── EntityArmorChangeEvent.php │ │ │ │ ├── EntityBlockChangeEvent.php │ │ │ │ ├── EntityCombustByBlockEvent.php │ │ │ │ ├── EntityCombustByEntityEvent.php │ │ │ │ ├── EntityCombustEvent.php │ │ │ │ ├── EntityDamageByBlockEvent.php │ │ │ │ ├── EntityDamageByChildEntityEvent.php │ │ │ │ ├── EntityDamageByEntityEvent.php │ │ │ │ ├── EntityDamageEvent.php │ │ │ │ ├── EntityDeathEvent.php │ │ │ │ ├── EntityDespawnEvent.php │ │ │ │ ├── EntityDrinkPotionEvent.php │ │ │ │ ├── EntityEatBlockEvent.php │ │ │ │ ├── EntityEatEvent.php │ │ │ │ ├── EntityEatItemEvent.php │ │ │ │ ├── EntityEffectAddEvent.php │ │ │ │ ├── EntityEffectRemoveEvent.php │ │ │ │ ├── EntityEvent.php │ │ │ │ ├── EntityExplodeEvent.php │ │ │ │ ├── EntityGenerateEvent.php │ │ │ │ ├── EntityInventoryChangeEvent.php │ │ │ │ ├── EntityLevelChangeEvent.php │ │ │ │ ├── EntityMotionEvent.php │ │ │ │ ├── EntityRegainHealthEvent.php │ │ │ │ ├── EntityShootBowEvent.php │ │ │ │ ├── EntitySpawnEvent.php │ │ │ │ ├── EntityTeleportEvent.php │ │ │ │ ├── ExplosionPrimeEvent.php │ │ │ │ ├── ItemDespawnEvent.php │ │ │ │ ├── ItemSpawnEvent.php │ │ │ │ ├── ProjectileHitEvent.php │ │ │ │ └── ProjectileLaunchEvent.php │ │ │ ├── inventory/ │ │ │ │ ├── CraftItemEvent.php │ │ │ │ ├── FurnaceBurnEvent.php │ │ │ │ ├── FurnaceSmeltEvent.php │ │ │ │ ├── InventoryCloseEvent.php │ │ │ │ ├── InventoryEvent.php │ │ │ │ ├── InventoryOpenEvent.php │ │ │ │ ├── InventoryPickupArrowEvent.php │ │ │ │ ├── InventoryPickupItemEvent.php │ │ │ │ └── InventoryTransactionEvent.php │ │ │ ├── level/ │ │ │ │ ├── ChunkEvent.php │ │ │ │ ├── ChunkLoadEvent.php │ │ │ │ ├── ChunkPopulateEvent.php │ │ │ │ ├── ChunkUnloadEvent.php │ │ │ │ ├── LevelEvent.php │ │ │ │ ├── LevelInitEvent.php │ │ │ │ ├── LevelLoadEvent.php │ │ │ │ ├── LevelSaveEvent.php │ │ │ │ ├── LevelUnloadEvent.php │ │ │ │ ├── SpawnChangeEvent.php │ │ │ │ └── WeatherChangeEvent.php │ │ │ ├── player/ │ │ │ │ ├── PlayerAchievementAwardedEvent.php │ │ │ │ ├── PlayerAnimationEvent.php │ │ │ │ ├── PlayerBedEnterEvent.php │ │ │ │ ├── PlayerBedLeaveEvent.php │ │ │ │ ├── PlayerBucketEmptyEvent.php │ │ │ │ ├── PlayerBucketEvent.php │ │ │ │ ├── PlayerBucketFillEvent.php │ │ │ │ ├── PlayerChatEvent.php │ │ │ │ ├── PlayerCommandPreprocessEvent.php │ │ │ │ ├── PlayerCreationEvent.php │ │ │ │ ├── PlayerDeathEvent.php │ │ │ │ ├── PlayerDropItemEvent.php │ │ │ │ ├── PlayerEvent.php │ │ │ │ ├── PlayerExhaustEvent.php │ │ │ │ ├── PlayerExperienceChangeEvent.php │ │ │ │ ├── PlayerFishEvent.php │ │ │ │ ├── PlayerGameModeChangeEvent.php │ │ │ │ ├── PlayerGlassBottleEvent.php │ │ │ │ ├── PlayerHungerChangeEvent.php │ │ │ │ ├── PlayerInteractEvent.php │ │ │ │ ├── PlayerItemConsumeEvent.php │ │ │ │ ├── PlayerItemHeldEvent.php │ │ │ │ ├── PlayerJoinEvent.php │ │ │ │ ├── PlayerKickEvent.php │ │ │ │ ├── PlayerLoginEvent.php │ │ │ │ ├── PlayerMoveEvent.php │ │ │ │ ├── PlayerPickupExpOrbEvent.php │ │ │ │ ├── PlayerPreLoginEvent.php │ │ │ │ ├── PlayerQuitEvent.php │ │ │ │ ├── PlayerRespawnEvent.php │ │ │ │ ├── PlayerTextPreSendEvent.php │ │ │ │ ├── PlayerToggleFlightEvent.php │ │ │ │ ├── PlayerToggleGlideEvent.php │ │ │ │ ├── PlayerToggleSneakEvent.php │ │ │ │ ├── PlayerToggleSprintEvent.php │ │ │ │ └── PlayerUseFishingRodEvent.php │ │ │ ├── plugin/ │ │ │ │ ├── PluginDisableEvent.php │ │ │ │ ├── PluginEnableEvent.php │ │ │ │ └── PluginEvent.php │ │ │ └── server/ │ │ │ ├── DataPacketReceiveEvent.php │ │ │ ├── DataPacketSendEvent.php │ │ │ ├── LowMemoryEvent.php │ │ │ ├── QueryRegenerateEvent.php │ │ │ ├── RemoteServerCommandEvent.php │ │ │ ├── ServerCommandEvent.php │ │ │ └── ServerEvent.php │ │ ├── inventory/ │ │ │ ├── AnvilInventory.php │ │ │ ├── BaseInventory.php │ │ │ ├── BaseTransaction.php │ │ │ ├── BigShapedRecipe.php │ │ │ ├── BigShapelessRecipe.php │ │ │ ├── BrewingInventory.php │ │ │ ├── BrewingRecipe.php │ │ │ ├── ChestInventory.php │ │ │ ├── ContainerInventory.php │ │ │ ├── CraftingInventory.php │ │ │ ├── CraftingManager.php │ │ │ ├── CustomInventory.php │ │ │ ├── DispenserInventory.php │ │ │ ├── DoubleChestInventory.php │ │ │ ├── DropItemTransaction.php │ │ │ ├── DropperInventory.php │ │ │ ├── EnchantInventory.php │ │ │ ├── EnderChestInventory.php │ │ │ ├── FakeBlockMenu.php │ │ │ ├── FloatingInventory.php │ │ │ ├── Fuel.php │ │ │ ├── FurnaceInventory.php │ │ │ ├── FurnaceRecipe.php │ │ │ ├── HopperInventory.php │ │ │ ├── Inventory.php │ │ │ ├── InventoryHolder.php │ │ │ ├── InventoryType.php │ │ │ ├── MultiRecipe.php │ │ │ ├── PlayerInventory.php │ │ │ ├── Recipe.php │ │ │ ├── ShapedRecipe.php │ │ │ ├── ShapedRecipeFromJson.php │ │ │ ├── ShapelessRecipe.php │ │ │ ├── SimpleTransactionQueue.php │ │ │ ├── TemporaryInventory.php │ │ │ ├── Transaction.php │ │ │ └── TransactionQueue.php │ │ ├── item/ │ │ │ ├── AcaciaDoor.php │ │ │ ├── Apple.php │ │ │ ├── Armor.php │ │ │ ├── Arrow.php │ │ │ ├── BakedPotato.php │ │ │ ├── Bed.php │ │ │ ├── Beetroot.php │ │ │ ├── BeetrootSeeds.php │ │ │ ├── BeetrootSoup.php │ │ │ ├── BirchDoor.php │ │ │ ├── BlazePowder.php │ │ │ ├── Boat.php │ │ │ ├── Bone.php │ │ │ ├── Book.php │ │ │ ├── Bow.php │ │ │ ├── Bowl.php │ │ │ ├── Bread.php │ │ │ ├── BrewingStand.php │ │ │ ├── Brick.php │ │ │ ├── Bucket.php │ │ │ ├── Cake.php │ │ │ ├── Camera.php │ │ │ ├── Carrot.php │ │ │ ├── Cauldron.php │ │ │ ├── ChainBoots.php │ │ │ ├── ChainChestplate.php │ │ │ ├── ChainHelmet.php │ │ │ ├── ChainLeggings.php │ │ │ ├── ChorusFruit.php │ │ │ ├── Clay.php │ │ │ ├── Clock.php │ │ │ ├── Coal.php │ │ │ ├── Compass.php │ │ │ ├── CookedChicken.php │ │ │ ├── CookedFish.php │ │ │ ├── CookedMutton.php │ │ │ ├── CookedPorkchop.php │ │ │ ├── CookedRabbit.php │ │ │ ├── Cookie.php │ │ │ ├── DarkOakDoor.php │ │ │ ├── Diamond.php │ │ │ ├── DiamondAxe.php │ │ │ ├── DiamondBoots.php │ │ │ ├── DiamondChestplate.php │ │ │ ├── DiamondHelmet.php │ │ │ ├── DiamondHoe.php │ │ │ ├── DiamondLeggings.php │ │ │ ├── DiamondPickaxe.php │ │ │ ├── DiamondShovel.php │ │ │ ├── DiamondSword.php │ │ │ ├── Door.php │ │ │ ├── Dye.php │ │ │ ├── Egg.php │ │ │ ├── Elytra.php │ │ │ ├── Emerald.php │ │ │ ├── EnchantedBook.php │ │ │ ├── EnchantedGoldenApple.php │ │ │ ├── EnchantingBottle.php │ │ │ ├── EnderPearl.php │ │ │ ├── EyeOfEnder.php │ │ │ ├── Feather.php │ │ │ ├── FermentedSpiderEye.php │ │ │ ├── Fish.php │ │ │ ├── FishingRod.php │ │ │ ├── Flint.php │ │ │ ├── FlintSteel.php │ │ │ ├── FlowerPot.php │ │ │ ├── Food.php │ │ │ ├── FoodSource.php │ │ │ ├── GlassBottle.php │ │ │ ├── GlisteringMelon.php │ │ │ ├── GlowstoneDust.php │ │ │ ├── GoldAxe.php │ │ │ ├── GoldBoots.php │ │ │ ├── GoldChestplate.php │ │ │ ├── GoldHelmet.php │ │ │ ├── GoldHoe.php │ │ │ ├── GoldIngot.php │ │ │ ├── GoldLeggings.php │ │ │ ├── GoldNugget.php │ │ │ ├── GoldPickaxe.php │ │ │ ├── GoldShovel.php │ │ │ ├── GoldSword.php │ │ │ ├── GoldenApple.php │ │ │ ├── GoldenCarrot.php │ │ │ ├── Gunpowder.php │ │ │ ├── Hopper.php │ │ │ ├── IronAxe.php │ │ │ ├── IronBoots.php │ │ │ ├── IronChestplate.php │ │ │ ├── IronDoor.php │ │ │ ├── IronHelmet.php │ │ │ ├── IronHoe.php │ │ │ ├── IronIngot.php │ │ │ ├── IronLeggings.php │ │ │ ├── IronPickaxe.php │ │ │ ├── IronShovel.php │ │ │ ├── IronSword.php │ │ │ ├── Item.php │ │ │ ├── ItemBlock.php │ │ │ ├── ItemFrame.php │ │ │ ├── ItemIds.php │ │ │ ├── ItemString.php │ │ │ ├── JungleDoor.php │ │ │ ├── Leather.php │ │ │ ├── LeatherBoots.php │ │ │ ├── LeatherCap.php │ │ │ ├── LeatherPants.php │ │ │ ├── LeatherTunic.php │ │ │ ├── MagmaCream.php │ │ │ ├── Melon.php │ │ │ ├── MelonSeeds.php │ │ │ ├── Minecart.php │ │ │ ├── MobHead.php │ │ │ ├── MushroomStew.php │ │ │ ├── NetherBrick.php │ │ │ ├── NetherQuartz.php │ │ │ ├── NetherStar.php │ │ │ ├── NetherWart.php │ │ │ ├── Painting.php │ │ │ ├── Paper.php │ │ │ ├── Potato.php │ │ │ ├── Potion.php │ │ │ ├── PrismarineCrystals.php │ │ │ ├── PrismarineShard.php │ │ │ ├── PumpkinPie.php │ │ │ ├── PumpkinSeeds.php │ │ │ ├── Quartz.php │ │ │ ├── RabbitStew.php │ │ │ ├── RawBeef.php │ │ │ ├── RawChicken.php │ │ │ ├── RawMutton.php │ │ │ ├── RawPorkchop.php │ │ │ ├── RawRabbit.php │ │ │ ├── Redstone.php │ │ │ ├── Repeater.php │ │ │ ├── RottenFlesh.php │ │ │ ├── Shears.php │ │ │ ├── Sign.php │ │ │ ├── Skull.php │ │ │ ├── Slimeball.php │ │ │ ├── Snowball.php │ │ │ ├── SpawnEgg.php │ │ │ ├── SpiderEye.php │ │ │ ├── SplashPotion.php │ │ │ ├── SpruceDoor.php │ │ │ ├── Steak.php │ │ │ ├── Stick.php │ │ │ ├── StoneAxe.php │ │ │ ├── StoneHoe.php │ │ │ ├── StonePickaxe.php │ │ │ ├── StoneShovel.php │ │ │ ├── StoneSword.php │ │ │ ├── StringItem.php │ │ │ ├── Sugar.php │ │ │ ├── Sugarcane.php │ │ │ ├── Tool.php │ │ │ ├── Wheat.php │ │ │ ├── WheatSeeds.php │ │ │ ├── WoodenAxe.php │ │ │ ├── WoodenDoor.php │ │ │ ├── WoodenHoe.php │ │ │ ├── WoodenPickaxe.php │ │ │ ├── WoodenShovel.php │ │ │ ├── WoodenSword.php │ │ │ └── enchantment/ │ │ │ ├── Enchantment.php │ │ │ ├── EnchantmentEntry.php │ │ │ ├── EnchantmentLevelTable.php │ │ │ └── EnchantmentList.php │ │ ├── lang/ │ │ │ ├── BaseLang.php │ │ │ ├── Installer/ │ │ │ │ ├── chs.ini │ │ │ │ ├── deu.ini │ │ │ │ ├── eng.ini │ │ │ │ ├── fra.ini │ │ │ │ ├── ita.ini │ │ │ │ ├── jpn.ini │ │ │ │ ├── kor.ini │ │ │ │ ├── rus.ini │ │ │ │ ├── ukr.ini │ │ │ │ └── zho.ini │ │ │ └── locale/ │ │ │ ├── ces.ini │ │ │ ├── chs.ini │ │ │ ├── deu.ini │ │ │ ├── eng.ini │ │ │ ├── fra.ini │ │ │ ├── jpn.ini │ │ │ ├── kor.ini │ │ │ ├── rus.ini │ │ │ ├── tur.ini │ │ │ ├── ukr.ini │ │ │ ├── vie.ini │ │ │ └── zho.ini │ │ ├── level/ │ │ │ ├── ChunkLoader.php │ │ │ ├── ChunkManager.php │ │ │ ├── Explosion.php │ │ │ ├── Level.php │ │ │ ├── LevelException.php │ │ │ ├── Location.php │ │ │ ├── MovingObjectPosition.php │ │ │ ├── Position.php │ │ │ ├── SimpleChunkManager.php │ │ │ ├── WeakPosition.php │ │ │ ├── format/ │ │ │ │ ├── Chunk.php │ │ │ │ ├── EmptySubChunk.php │ │ │ │ ├── SubChunk.php │ │ │ │ └── io/ │ │ │ │ ├── BaseLevelProvider.php │ │ │ │ ├── ChunkException.php │ │ │ │ ├── ChunkRequestTask.php │ │ │ │ ├── ChunkUtils.php │ │ │ │ ├── LevelProvider.php │ │ │ │ ├── LevelProviderManager.php │ │ │ │ └── region/ │ │ │ │ ├── Anvil.php │ │ │ │ ├── McRegion.php │ │ │ │ ├── PMAnvil.php │ │ │ │ └── RegionLoader.php │ │ │ ├── generator/ │ │ │ │ ├── Flat.php │ │ │ │ ├── GenerationTask.php │ │ │ │ ├── Generator.php │ │ │ │ ├── GeneratorRegisterTask.php │ │ │ │ ├── GeneratorUnregisterTask.php │ │ │ │ ├── LightPopulationTask.php │ │ │ │ ├── PopulationTask.php │ │ │ │ ├── Void.php │ │ │ │ ├── biome/ │ │ │ │ │ ├── Biome.php │ │ │ │ │ └── BiomeSelector.php │ │ │ │ ├── nether/ │ │ │ │ │ ├── Nether.php │ │ │ │ │ ├── biome/ │ │ │ │ │ │ └── HellBiome.php │ │ │ │ │ ├── object/ │ │ │ │ │ │ └── NetherOre.php │ │ │ │ │ └── populator/ │ │ │ │ │ ├── GroundFire.php │ │ │ │ │ ├── NetherGlowStone.php │ │ │ │ │ ├── NetherLava.php │ │ │ │ │ └── NetherOre.php │ │ │ │ ├── noise/ │ │ │ │ │ ├── Noise.php │ │ │ │ │ ├── Perlin.php │ │ │ │ │ └── Simplex.php │ │ │ │ ├── normal/ │ │ │ │ │ ├── Normal.php │ │ │ │ │ ├── Normal2.php │ │ │ │ │ ├── biome/ │ │ │ │ │ │ ├── BeachBiome.php │ │ │ │ │ │ ├── DesertBiome.php │ │ │ │ │ │ ├── ForestBiome.php │ │ │ │ │ │ ├── GrassyBiome.php │ │ │ │ │ │ ├── IcePlainsBiome.php │ │ │ │ │ │ ├── MesaBiome.php │ │ │ │ │ │ ├── MountainsBiome.php │ │ │ │ │ │ ├── NormalBiome.php │ │ │ │ │ │ ├── OceanBiome.php │ │ │ │ │ │ ├── PlainBiome.php │ │ │ │ │ │ ├── RiverBiome.php │ │ │ │ │ │ ├── SandyBiome.php │ │ │ │ │ │ ├── SmallMountainsBiome.php │ │ │ │ │ │ ├── SnowyBiome.php │ │ │ │ │ │ ├── SwampBiome.php │ │ │ │ │ │ ├── TaigaBiome.php │ │ │ │ │ │ └── WateryBiome.php │ │ │ │ │ ├── object/ │ │ │ │ │ │ ├── AcaciaTree.php │ │ │ │ │ │ ├── BigTree.php │ │ │ │ │ │ ├── BirchTree.php │ │ │ │ │ │ ├── CactusStack.php │ │ │ │ │ │ ├── DarkOakTree.php │ │ │ │ │ │ ├── JungleTree.php │ │ │ │ │ │ ├── OakTree.php │ │ │ │ │ │ ├── Ore.php │ │ │ │ │ │ ├── OreType.php │ │ │ │ │ │ ├── Pond.php │ │ │ │ │ │ ├── SpruceTree.php │ │ │ │ │ │ ├── SugarCaneStack.php │ │ │ │ │ │ ├── TallGrass.php │ │ │ │ │ │ └── Tree.php │ │ │ │ │ └── populator/ │ │ │ │ │ ├── Cactus.php │ │ │ │ │ ├── Cave.php │ │ │ │ │ ├── DeadBush.php │ │ │ │ │ ├── Flower.php │ │ │ │ │ ├── GroundCover.php │ │ │ │ │ ├── LilyPad.php │ │ │ │ │ ├── Mineshaft.php │ │ │ │ │ ├── MossStone.php │ │ │ │ │ ├── Mushroom.php │ │ │ │ │ ├── Ore.php │ │ │ │ │ ├── Pond.php │ │ │ │ │ ├── SugarCane.php │ │ │ │ │ ├── TallGrass.php │ │ │ │ │ ├── Tree.php │ │ │ │ │ └── WaterPit.php │ │ │ │ ├── object/ │ │ │ │ │ └── Object.php │ │ │ │ └── populator/ │ │ │ │ ├── Populator.php │ │ │ │ └── VariableAmountPopulator.php │ │ │ ├── particle/ │ │ │ │ ├── AngryVillagerParticle.php │ │ │ │ ├── BlockForceFieldParticle.php │ │ │ │ ├── BubbleParticle.php │ │ │ │ ├── CriticalParticle.php │ │ │ │ ├── DestroyBlockParticle.php │ │ │ │ ├── DustParticle.php │ │ │ │ ├── EnchantParticle.php │ │ │ │ ├── EnchantmentTableParticle.php │ │ │ │ ├── EntityFlameParticle.php │ │ │ │ ├── ExplodeParticle.php │ │ │ │ ├── FlameParticle.php │ │ │ │ ├── FloatingTextParticle.php │ │ │ │ ├── GenericParticle.php │ │ │ │ ├── HappyVillagerParticle.php │ │ │ │ ├── HeartParticle.php │ │ │ │ ├── HugeExplodeParticle.php │ │ │ │ ├── HugeExplodeSeedParticle.php │ │ │ │ ├── InkParticle.php │ │ │ │ ├── InstantEnchantParticle.php │ │ │ │ ├── ItemBreakParticle.php │ │ │ │ ├── LavaDripParticle.php │ │ │ │ ├── LavaParticle.php │ │ │ │ ├── MobSpawnParticle.php │ │ │ │ ├── MobSpellParticle.php │ │ │ │ ├── Particle.php │ │ │ │ ├── PortalParticle.php │ │ │ │ ├── RainSplashParticle.php │ │ │ │ ├── RedstoneParticle.php │ │ │ │ ├── SmokeParticle.php │ │ │ │ ├── SpellParticle.php │ │ │ │ ├── SplashParticle.php │ │ │ │ ├── SporeParticle.php │ │ │ │ ├── TerrainParticle.php │ │ │ │ ├── WaterDripParticle.php │ │ │ │ ├── WaterParticle.php │ │ │ │ └── WhiteSmokeParticle.php │ │ │ ├── sound/ │ │ │ │ ├── AnvilFallSound.php │ │ │ │ ├── AnvilUseSound.php │ │ │ │ ├── BatSound.php │ │ │ │ ├── BlazeShootSound.php │ │ │ │ ├── BlockPlaceSound.php │ │ │ │ ├── ButtonClickSound.php │ │ │ │ ├── ClickSound.php │ │ │ │ ├── DoorBumpSound.php │ │ │ │ ├── DoorCrashSound.php │ │ │ │ ├── DoorSound.php │ │ │ │ ├── EndermanTeleportSound.php │ │ │ │ ├── ExpPickupSound.php │ │ │ │ ├── ExplodeSound.php │ │ │ │ ├── FizzSound.php │ │ │ │ ├── GenericSound.php │ │ │ │ ├── GhastShootSound.php │ │ │ │ ├── GhastSound.php │ │ │ │ ├── GraySplashSound.php │ │ │ │ ├── LaunchSound.php │ │ │ │ ├── NoteblockSound.php │ │ │ │ ├── PopSound.php │ │ │ │ ├── Sound.php │ │ │ │ ├── SpellSound.php │ │ │ │ ├── SplashSound.php │ │ │ │ ├── TNTPrimeSound.php │ │ │ │ ├── ZombieHealSound.php │ │ │ │ └── ZombieInfectSound.php │ │ │ └── weather/ │ │ │ └── Weather.php │ │ ├── math/ │ │ │ ├── AxisAlignedBB.php │ │ │ ├── Math.php │ │ │ ├── Matrix.php │ │ │ ├── Vector2.php │ │ │ ├── Vector3.php │ │ │ └── VectorMath.php │ │ ├── metadata/ │ │ │ ├── BlockMetadataStore.php │ │ │ ├── EntityMetadataStore.php │ │ │ ├── LevelMetadataStore.php │ │ │ ├── MetadataStore.php │ │ │ ├── MetadataValue.php │ │ │ ├── Metadatable.php │ │ │ └── PlayerMetadataStore.php │ │ ├── nbt/ │ │ │ ├── NBT.php │ │ │ └── tag/ │ │ │ ├── ByteArrayTag.php │ │ │ ├── ByteTag.php │ │ │ ├── CompoundTag.php │ │ │ ├── DoubleTag.php │ │ │ ├── EndTag.php │ │ │ ├── FloatTag.php │ │ │ ├── IntArrayTag.php │ │ │ ├── IntTag.php │ │ │ ├── ListTag.php │ │ │ ├── LongTag.php │ │ │ ├── NamedTag.php │ │ │ ├── ShortTag.php │ │ │ ├── StringTag.php │ │ │ └── Tag.php │ │ ├── network/ │ │ │ ├── AdvancedSourceInterface.php │ │ │ ├── CachedEncapsulatedPacket.php │ │ │ ├── CompressBatchedTask.php │ │ │ ├── Network.php │ │ │ ├── RakLibInterface.php │ │ │ ├── SourceInterface.php │ │ │ ├── protocol/ │ │ │ │ ├── AddEntityPacket.php │ │ │ │ ├── AddHangingEntityPacket.php │ │ │ │ ├── AddItemEntityPacket.php │ │ │ │ ├── AddItemPacket.php │ │ │ │ ├── AddPaintingPacket.php │ │ │ │ ├── AddPlayerPacket.php │ │ │ │ ├── AdventureSettingsPacket.php │ │ │ │ ├── AnimatePacket.php │ │ │ │ ├── AvailableCommandsPacket.php │ │ │ │ ├── BatchPacket.php │ │ │ │ ├── BlockEntityDataPacket.php │ │ │ │ ├── BlockEventPacket.php │ │ │ │ ├── BossEventPacket.php │ │ │ │ ├── CameraPacket.php │ │ │ │ ├── ChangeDimensionPacket.php │ │ │ │ ├── ChunkRadiusUpdatedPacket.php │ │ │ │ ├── CommandStepPacket.php │ │ │ │ ├── ContainerClosePacket.php │ │ │ │ ├── ContainerOpenPacket.php │ │ │ │ ├── ContainerSetContentPacket.php │ │ │ │ ├── ContainerSetDataPacket.php │ │ │ │ ├── ContainerSetSlotPacket.php │ │ │ │ ├── CraftingDataPacket.php │ │ │ │ ├── CraftingEventPacket.php │ │ │ │ ├── DataPacket.php │ │ │ │ ├── DisconnectPacket.php │ │ │ │ ├── DropItemPacket.php │ │ │ │ ├── EntityEventPacket.php │ │ │ │ ├── ExplodePacket.php │ │ │ │ ├── FullChunkDataPacket.php │ │ │ │ ├── HurtArmorPacket.php │ │ │ │ ├── Info.php │ │ │ │ ├── InteractPacket.php │ │ │ │ ├── InventoryActionPacket.php │ │ │ │ ├── ItemFrameDropItemPacket.php │ │ │ │ ├── LevelEventPacket.php │ │ │ │ ├── LevelSoundEventPacket.php │ │ │ │ ├── LoginPacket.php │ │ │ │ ├── MobArmorEquipmentPacket.php │ │ │ │ ├── MobEffectPacket.php │ │ │ │ ├── MobEquipmentPacket.php │ │ │ │ ├── MoveEntityPacket.php │ │ │ │ ├── MovePlayerPacket.php │ │ │ │ ├── PlayStatusPacket.php │ │ │ │ ├── PlayerActionPacket.php │ │ │ │ ├── PlayerFallPacket.php │ │ │ │ ├── PlayerInputPacket.php │ │ │ │ ├── PlayerListPacket.php │ │ │ │ ├── RemoveBlockPacket.php │ │ │ │ ├── RemoveEntityPacket.php │ │ │ │ ├── ReplaceItemInSlotPacket.php │ │ │ │ ├── RequestChunkRadiusPacket.php │ │ │ │ ├── ResourcePackClientResponsePacket.php │ │ │ │ ├── ResourcePacksInfoPacket.php │ │ │ │ ├── RespawnPacket.php │ │ │ │ ├── SetCommandsEnabledPacket.php │ │ │ │ ├── SetDifficultyPacket.php │ │ │ │ ├── SetEntityDataPacket.php │ │ │ │ ├── SetEntityLinkPacket.php │ │ │ │ ├── SetEntityMotionPacket.php │ │ │ │ ├── SetHealthPacket.php │ │ │ │ ├── SetPlayerGameTypePacket.php │ │ │ │ ├── SetSpawnPositionPacket.php │ │ │ │ ├── SetTimePacket.php │ │ │ │ ├── ShowCreditsPacket.php │ │ │ │ ├── SpawnExperienceOrbPacket.php │ │ │ │ ├── StartGamePacket.php │ │ │ │ ├── StrangePacket.php │ │ │ │ ├── TakeItemEntityPacket.php │ │ │ │ ├── TextPacket.php │ │ │ │ ├── TransferPacket.php │ │ │ │ ├── UpdateAttributesPacket.php │ │ │ │ ├── UpdateBlockPacket.php │ │ │ │ └── UseItemPacket.php │ │ │ ├── query/ │ │ │ │ └── QueryHandler.php │ │ │ ├── rcon/ │ │ │ │ ├── RCON.php │ │ │ │ └── RCONInstance.php │ │ │ └── upnp/ │ │ │ └── UPnP.php │ │ ├── permission/ │ │ │ ├── BanEntry.php │ │ │ ├── BanList.php │ │ │ ├── DefaultPermissions.php │ │ │ ├── Permissible.php │ │ │ ├── PermissibleBase.php │ │ │ ├── Permission.php │ │ │ ├── PermissionAttachment.php │ │ │ ├── PermissionAttachmentInfo.php │ │ │ ├── PermissionRemovedExecutor.php │ │ │ └── ServerOperator.php │ │ ├── plugin/ │ │ │ ├── EventExecutor.php │ │ │ ├── MethodEventExecutor.php │ │ │ ├── PharPluginLoader.php │ │ │ ├── Plugin.php │ │ │ ├── PluginBase.php │ │ │ ├── PluginDescription.php │ │ │ ├── PluginLoadOrder.php │ │ │ ├── PluginLoader.php │ │ │ ├── PluginLogger.php │ │ │ ├── PluginManager.php │ │ │ ├── RegisteredListener.php │ │ │ └── ScriptPluginLoader.php │ │ ├── resourcepacks/ │ │ │ └── ResourcePackInfoEntry.php │ │ ├── resources/ │ │ │ ├── command_default.json │ │ │ ├── creativeitems.json │ │ │ ├── genisys_chs.yml │ │ │ ├── genisys_eng.yml │ │ │ ├── genisys_kor.yml │ │ │ ├── genisys_vie.yml │ │ │ ├── genisys_zho.yml │ │ │ ├── pocketmine.yml │ │ │ └── recipes.json │ │ ├── scheduler/ │ │ │ ├── AsyncPool.php │ │ │ ├── AsyncTask.php │ │ │ ├── AsyncWorker.php │ │ │ ├── CallbackTask.php │ │ │ ├── DServerTask.php │ │ │ ├── FileWriteTask.php │ │ │ ├── GarbageCollectionTask.php │ │ │ ├── PluginTask.php │ │ │ ├── SendUsageTask.php │ │ │ ├── ServerScheduler.php │ │ │ ├── Task.php │ │ │ └── TaskHandler.php │ │ ├── tile/ │ │ │ ├── Beacon.php │ │ │ ├── BrewingStand.php │ │ │ ├── Cauldron.php │ │ │ ├── Chest.php │ │ │ ├── Container.php │ │ │ ├── DLDetector.php │ │ │ ├── Dispenser.php │ │ │ ├── Dropper.php │ │ │ ├── EnchantTable.php │ │ │ ├── EnderChest.php │ │ │ ├── FlowerPot.php │ │ │ ├── Furnace.php │ │ │ ├── Hopper.php │ │ │ ├── ItemFrame.php │ │ │ ├── MobSpawner.php │ │ │ ├── Nameable.php │ │ │ ├── Sign.php │ │ │ ├── Skull.php │ │ │ ├── Spawnable.php │ │ │ └── Tile.php │ │ ├── utils/ │ │ │ ├── Binary.php │ │ │ ├── BinaryStream.php │ │ │ ├── BlockIterator.php │ │ │ ├── ChunkException.php │ │ │ ├── Color.php │ │ │ ├── Config.php │ │ │ ├── LevelException.php │ │ │ ├── MainLogger.php │ │ │ ├── MonkeyPatch.php │ │ │ ├── Patchable.php │ │ │ ├── PluginException.php │ │ │ ├── Random.php │ │ │ ├── Range.php │ │ │ ├── ReversePriorityQueue.php │ │ │ ├── ServerException.php │ │ │ ├── ServerKiller.php │ │ │ ├── Terminal.php │ │ │ ├── TextFormat.php │ │ │ ├── UUID.php │ │ │ ├── Utils.php │ │ │ ├── VectorIterator.php │ │ │ └── VersionString.php │ │ └── wizard/ │ │ ├── Installer.php │ │ └── InstallerLang.php │ ├── raklib/ │ │ ├── Binary.php │ │ ├── RakLib.php │ │ ├── protocol/ │ │ │ ├── ACK.php │ │ │ ├── ADVERTISE_SYSTEM.php │ │ │ ├── AcknowledgePacket.php │ │ │ ├── CLIENT_CONNECT_DataPacket.php │ │ │ ├── CLIENT_DISCONNECT_DataPacket.php │ │ │ ├── CLIENT_HANDSHAKE_DataPacket.php │ │ │ ├── DATA_PACKET_0.php │ │ │ ├── DATA_PACKET_1.php │ │ │ ├── DATA_PACKET_2.php │ │ │ ├── DATA_PACKET_3.php │ │ │ ├── DATA_PACKET_4.php │ │ │ ├── DATA_PACKET_5.php │ │ │ ├── DATA_PACKET_6.php │ │ │ ├── DATA_PACKET_7.php │ │ │ ├── DATA_PACKET_8.php │ │ │ ├── DATA_PACKET_9.php │ │ │ ├── DATA_PACKET_A.php │ │ │ ├── DATA_PACKET_B.php │ │ │ ├── DATA_PACKET_C.php │ │ │ ├── DATA_PACKET_D.php │ │ │ ├── DATA_PACKET_E.php │ │ │ ├── DATA_PACKET_F.php │ │ │ ├── DataPacket.php │ │ │ ├── EncapsulatedPacket.php │ │ │ ├── NACK.php │ │ │ ├── OPEN_CONNECTION_REPLY_1.php │ │ │ ├── OPEN_CONNECTION_REPLY_2.php │ │ │ ├── OPEN_CONNECTION_REQUEST_1.php │ │ │ ├── OPEN_CONNECTION_REQUEST_2.php │ │ │ ├── PING_DataPacket.php │ │ │ ├── PONG_DataPacket.php │ │ │ ├── Packet.php │ │ │ ├── PacketReliability.php │ │ │ ├── SERVER_HANDSHAKE_DataPacket.php │ │ │ ├── UNCONNECTED_PING.php │ │ │ ├── UNCONNECTED_PING_OPEN_CONNECTIONS.php │ │ │ └── UNCONNECTED_PONG.php │ │ └── server/ │ │ ├── RakLibServer.php │ │ ├── ServerHandler.php │ │ ├── ServerInstance.php │ │ ├── Session.php │ │ ├── SessionManager.php │ │ └── UDPServerSocket.php │ └── spl/ │ ├── ArrayOutOfBoundsException.php │ ├── AttachableLogger.php │ ├── AttachableThreadedLogger.php │ ├── BaseClassLoader.php │ ├── ClassCastException.php │ ├── ClassLoader.php │ ├── ClassNotFoundException.php │ ├── InvalidArgumentCountException.php │ ├── InvalidKeyException.php │ ├── InvalidStateException.php │ ├── LogLevel.php │ ├── Logger.php │ ├── LoggerAttachment.php │ ├── SplFixedByteArray.php │ ├── StringOutOfBoundsException.php │ ├── ThreadedLogger.php │ ├── ThreadedLoggerAttachment.php │ ├── UndefinedConstantException.php │ ├── UndefinedPropertyException.php │ └── UndefinedVariableException.php ├── start.cmd └── start.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.iml .idea/misc.xml *.xml .buildpath .project .settings/org.eclipse.php.core.prefs ================================================ FILE: CONTRIBUTING.md ================================================ ![](http://cdn.pocketmine.net/img/PocketMine-MP-h.png) # PocketMine-MP Contribution Guidelines You must follow these guidelines if you wish to contribute to the PocketMine-MP code base, or participate in issue tracking. ## I have a question * For questions, please refer to the _#pmmp_ or _#pocketmine_ IRC channel on Freenode. There is a [WebIRC](http://webchat.freenode.net?channels=pmmp,pocketmine&uio=d4) if you do not want to install an IRC client. * You can ask directly to _[@PocketMine](https://twitter.com/PocketMine)_ in Twitter, but don't expect an immediate reply. * You may use our [Forum](http://forums.pocketmine.net) to ask questions. * We do not accept questions or support requests in our issue tracker. ## Creating an Issue * First, use the [Issue Search](https://github.com/pmmp/PocketMine-MP/search?ref=cmdform&type=Issues) to check if anyone has reported it. Check also closed issues, as an issue you think is valid may actually be invalid. * If an issue has been _fixed_ and closed, create another issue. * If your issue is related to a plugin, do **not** report here. Contact the plugin's original author instead. * **Support requests are not bugs.** Issues such as "How do I do this" are not bugs and are closed as soon as a collaborator spots it. They are referred to our Forum to seek assistance. Please refer to the section [I have a quesetion](#i-have-a-question) instead. * **No generic titles** such as "Question", "Help", "Crash Report" etc. * If you just got a crash report but you don't understand it, please look for a line starting with `Message`. It summarizes the bug. * Information must be provided in the issue body, not in the title. No tags like `[BUG]` are allowed in the title, including `[SOLVED]` for solved issues. * Similarly, no generic issue reports. For bugs, it is the issue author's responsibility to provide us an issue that is **trackable, debuggable, reproducible, reported professionally and is an actual bug**. If you do not provide us with a summary or instructions on how to reproduce the issue, it is a support request until the actual bug has been found and therefore the issue is closed. * In simple words, if your issue does not appear to be a bug or a feature request, or if the issue cannot be properly confirmed to be valid, the issue will be closed until further information is provided. * To express appreciation, objection, confusion or other supported reactions on pull requests, issues or comments on them, use GitHub [reactions](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments) rather than posting an individual comment with an emoji only. This helps keeping the issue/pull rqeuest conversation clean and readable. * If your issue is related to the Pocketmine-MP website, forums, etc., please [talk to a human directly](#i-have-a-question). ## Contributing Code * Use the [Pull Request](https://github.com/pmmp/PocketMine-MP/pull/new) system, your request will be checked and discussed. * Create each pull request on a new branch. Do not create a pull request on commits that exist in another pull request. * Code should use the same syntax as in PocketMine-MP. See below for an example. * The code must be clear and written in English, comments included. * Use descriptive commit titles * **Keep each pull request only contain one feature**. The only exception is when all features in the pull request are related to each other, and share the same core changes. * **Do not create pull requests that only bump the MCPE version**. If it is ready to be updated, the team will update the values directly. Do not change the MCPE version or protocol version in a pull request, unless you have updated the protocol (all packets) entirely. **Thanks for contributing to PocketMine-MP!** ### Code Syntax It is mainly [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md) with a few exceptions. * Opening braces MUST go on the same line, and MUST NOT have spaces before. * `else if` MUST be written as `elseif`. _(It is in PSR-2, but using a SHOULD)_ * Control structure keywords or opening braces MUST NOT have one space before or after them. * Code MUST use tabs for indenting. * Long arrays MAY be split across multiple lines, where each subsequent line is indented once. * Files MUST use only the `` tag. * Code MUST use namespaces. * Strings SHOULD use the double quote `"` except when the single quote is required. ```php examplePrivateVariable = [ 0 => "value1", 1 => "value2", 2 => "value3", 3 => "value4", 4 => "value5", 5 => "value6", ]; }; } } } ``` ### RFC and Voting * These are big Pull Requests or contributions that change important behavior. * RFCs will be tagged with the *PR: RFC* label * A vote will be held once the RFC is ready. All users can vote commenting on the Pull Request * Comments MUST use "Yes" or "No" on the FIRST sentence to signify the vote, except when they don't want it to be counted. * If your comment is a voting comment, specify the reason of your vote or it won't be counted. * After voting has been closed, no further votes will be counted. * An RFC will be rejected if less than 50% + 1 (simple majority) has voted Yes. * If the RFC is approved, Team Members have the final word on its implementation or rejection. * RFCs with complex voting options will specify the vote percentage or other details. ## Bug Tracking for Collaborators ### Labels To provide a concise bug tracking environment, prevent the issue tracker from over flowing and to keep support requests out of the bug tracker, PocketMine-MP uses a label scheme a bit different from the default GitHub Issues labels. PocketMine-MP uses Labels to identify the types and status of issues and pull requests. #### Categories Category labels are prefixed by `Related:`. Multiple category labels may be applied to a single issue(but try to keep this to a minimum and do not overuse category labels). * `Related: Core` - This label is applied when the bug results in a fatal crash, or is related to neither Gameplay nor Plugin API. * `Related: Gameplay` - This label is applied when the bug effects the gameplay. * `Related: Plugin API` - This label is applied when the bug effects the Plugin API. #### Pull Requests Pull Requests are prefixed by `PR:`. Only one label may be applied for a Pull Request. * PR: Bug Fix - This label is applied when the Pull Request fixes a bug. * PR: Addition - This label is applied when the Pull Request contributes new features or improvements, but does not fix a bug, nor controversial enough to be an RFC. * PR: RFC - Request for Comments. Refer to [RFC and Voting](#rfc-and-voting). #### Status Status labels show the status of the issue. Multiple status labels may be applied. * `Status: Reproduced` - This label is applied when the bug has been reproduced, or multiple people are reporting the same issue and symptoms in which case it is automatically assumed that the bug has been reproduced in different environments. * `Status: Debugged` - This label is applied when the cause of the bug has been found. * `Status: High Priority` - This label is applied when the bug is easy to fix, or if the scale of the bug is global. * `Status: Insufficiently tested` - This label is applied for pull requests that have not undergone tests strict enough. #### Miscellaneous Miscellaneous labels are labels that show status not related to debugging that bug. The To-Do label and the Mojang label may not be applied to a single issue at the same time. * `Affiliation: Mojang` - This label is applied when the issue is suspected of being caused by the Minecraft client, but has not been confirmed. * `Affiliation: MCPE` - Same as `Affiliated: Mojang`, but only applied if the issue is only specific to the Pocket Edition (but not the Windows 10 Edition) * `Affiliation: Windows 10` - Same as `Affiliated: Mojang`, but only applied if the issue is only specific to the Windows 10 Edition (but not the Pocket Edition) * `Affiliation: Meta` - This label is applied for issues or pull requests that are related to this GitHub repo. **Still, do not report bugs related to other parts of PocketMine-MP.** * `Category: Bug` - This label is applied to issues that are bugs, but not necessarily reproduced. * `Category: To-Do` - This label is applied when the issue is not a bug, but a feature request or a list of features to be implemented that count towards a milestone. * `Category: Protocol update` - This label is applied if the issue or pull request is related to client protocol updates. * `Category: Invalid` - This label is applied when the issue is reporting a false bug that works as intended, a support request, etc. *This label may only be applied to a closed issue.* * `Category: Won't fix` - This label is applied if the bug has been decided not be fixed for some reason. e.g. when the bug benefits gameplay. _This label may only be applied to a closed issue._ ### Closing Issues To keep the bug tracker clear of non-related issues and to prevent it from overflowing, **issues must be closed as soon as possible** (This may sound unethical, but it is MUCH better than having the BUG TRACKER filled with SUPPORT REQUESTS and "I NEED HELP"). If an issue does not conform to the "Creating an Issue" guidelines above, the issue should be closed. ### Milestones PocketMine-MP uses GitHub Milestones to set a goal for a new release. A milestone is set on the following occasions. - A new Beta release - A new Stable release A milestone must use the following format: ``` Alpha_ [release_title][release_version] ``` For example: ``` Alpha_1.4 beta2 ``` ================================================ FILE: LICENSE ================================================ 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. ================================================ FILE: README.md ================================================ ##Elywing has been discontinued. Thanks for everything ! ================================================ FILE: src/pocketmine/Achievement.php ================================================ array( "name" => "Taking Inventory", "requires" => [], ),*/ "mineWood" => [ "name" => "Getting Wood", "requires" => [ //"openInventory", ], ], "buildWorkBench" => [ "name" => "Benchmarking", "requires" => [ "mineWood", ], ], "buildPickaxe" => [ "name" => "Time to Mine!", "requires" => [ "buildWorkBench", ], ], "buildFurnace" => [ "name" => "Hot Topic", "requires" => [ "buildPickaxe", ], ], "acquireIron" => [ "name" => "Acquire hardware", "requires" => [ "buildFurnace", ], ], "buildHoe" => [ "name" => "Time to Farm!", "requires" => [ "buildWorkBench", ], ], "makeBread" => [ "name" => "Bake Bread", "requires" => [ "buildHoe", ], ], "bakeCake" => [ "name" => "The Lie", "requires" => [ "buildHoe", ], ], "buildBetterPickaxe" => [ "name" => "Getting an Upgrade", "requires" => [ "buildPickaxe", ], ], "buildSword" => [ "name" => "Time to Strike!", "requires" => [ "buildWorkBench", ], ], "diamonds" => [ "name" => "DIAMONDS!", "requires" => [ "acquireIron", ], ], ]; public static function broadcast(Player $player, $achievementId){ if(isset(Achievement::$list[$achievementId])){ $translation = new TranslationContainer("chat.type.achievement", [$player->getDisplayName(), TextFormat::GREEN . Achievement::$list[$achievementId]["name"]]); if(Server::getInstance()->getConfigString("announce-player-achievements", true) === true){ Server::getInstance()->broadcastMessage($translation); }else{ $player->sendMessage($translation); } return true; } return false; } public static function add($achievementId, $achievementName, array $requires = []){ if(!isset(Achievement::$list[$achievementId])){ Achievement::$list[$achievementId] = [ "name" => $achievementName, "requires" => $requires, ]; return true; } return false; } } ================================================ FILE: src/pocketmine/Collectable.php ================================================ isGarbage; } public function setGarbage(){ $this->isGarbage = true; } } ================================================ FILE: src/pocketmine/CompatibleClassLoader.php ================================================ time = time(); $this->server = $server; $this->path = $this->server->getCrashPath() . "CrashDump_" . date("D_M_j-H.i.s-T_Y", $this->time) . ".log"; $this->fp = @fopen($this->path, "wb"); if(!is_resource($this->fp)){ throw new \RuntimeException("Could not create Crash Dump"); } $this->data["time"] = $this->time; $this->addLine($this->server->getName() . " Crash Dump " . date("D M j H:i:s T Y", $this->time)); $this->addLine(); try{ $this->baseCrash(); }catch(\Exception $e){ //Attempt to fix incomplete crashdumps $this->addLine("CrashDump crashed while generating base crash data"); $this->addLine(); } $this->generalData(); $this->pluginsData(); $this->extraData(); //$this->encodeData(); } public function getPath(){ return $this->path; } public function getEncodedData(){ return $this->encodedData; } public function getData(){ return $this->data; } private function encodeData(){ $this->addLine(); $this->addLine("----------------------REPORT THE DATA BELOW THIS LINE-----------------------"); $this->addLine(); $this->addLine("===BEGIN CRASH DUMP==="); $this->encodedData = zlib_encode(json_encode($this->data, JSON_UNESCAPED_SLASHES), ZLIB_ENCODING_DEFLATE, 9); foreach(str_split(base64_encode($this->encodedData), 76) as $line){ $this->addLine($line); } $this->addLine("===END CRASH DUMP==="); } private function pluginsData(){ if(class_exists("pocketmine\\plugin\\PluginManager", false)){ $this->addLine(); $this->addLine("Loaded plugins:"); $this->data["plugins"] = []; foreach($this->server->getPluginManager()->getPlugins() as $p){ $d = $p->getDescription(); $this->data["plugins"][$d->getName()] = [ "name" => $d->getName(), "version" => $d->getVersion(), "authors" => $d->getAuthors(), "api" => $d->getCompatibleApis(), "enabled" => $p->isEnabled(), "depends" => $d->getDepend(), "softDepends" => $d->getSoftDepend(), "main" => $d->getMain(), "load" => $d->getOrder() === PluginLoadOrder::POSTWORLD ? "POSTWORLD" : "STARTUP", "website" => $d->getWebsite() ]; $this->addLine($d->getName() . " " . $d->getVersion() . " by " . implode(", ", $d->getAuthors()) . " for API(s) " . implode(", ", $d->getCompatibleApis())); } } } private function extraData(){ global $arguments; if($this->server->getProperty("auto-report.send-settings", true) !== false){ $this->data["parameters"] = (array) $arguments; $this->data["server.properties"] = @file_get_contents($this->server->getDataPath() . "server.properties"); $this->data["server.properties"] = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $this->data["server.properties"]); $this->data["pocketmine.yml"] = @file_get_contents($this->server->getDataPath() . "pocketmine.yml"); }else{ $this->data["pocketmine.yml"] = ""; $this->data["server.properties"] = ""; $this->data["parameters"] = []; } $extensions = []; foreach(get_loaded_extensions() as $ext){ $extensions[$ext] = phpversion($ext); } $this->data["extensions"] = $extensions; if($this->server->getProperty("auto-report.send-phpinfo", true) !== false){ ob_start(); phpinfo(); $this->data["phpinfo"] = ob_get_contents(); ob_end_clean(); } } private function baseCrash(){ global $lastExceptionError, $lastError; if(isset($lastExceptionError)){ $error = $lastExceptionError; }else{ $error = (array) error_get_last(); $error["trace"] = @getTrace(3); $errorConversion = [ E_ERROR => "E_ERROR", E_WARNING => "E_WARNING", E_PARSE => "E_PARSE", E_NOTICE => "E_NOTICE", E_CORE_ERROR => "E_CORE_ERROR", E_CORE_WARNING => "E_CORE_WARNING", E_COMPILE_ERROR => "E_COMPILE_ERROR", E_COMPILE_WARNING => "E_COMPILE_WARNING", E_USER_ERROR => "E_USER_ERROR", E_USER_WARNING => "E_USER_WARNING", E_USER_NOTICE => "E_USER_NOTICE", E_STRICT => "E_STRICT", E_RECOVERABLE_ERROR => "E_RECOVERABLE_ERROR", E_DEPRECATED => "E_DEPRECATED", E_USER_DEPRECATED => "E_USER_DEPRECATED", ]; $error["fullFile"] = $error["file"]; $error["file"] = cleanPath($error["file"]); $error["type"] = isset($errorConversion[$error["type"]]) ? $errorConversion[$error["type"]] : $error["type"]; if(($pos = strpos($error["message"], "\n")) !== false){ $error["message"] = substr($error["message"], 0, $pos); } } if(isset($lastError)){ $this->data["lastError"] = $lastError; } $this->data["error"] = $error; unset($this->data["error"]["fullFile"]); unset($this->data["error"]["trace"]); $this->addLine("Error: " . $error["message"]); $this->addLine("File: " . $error["file"]); $this->addLine("Line: " . $error["line"]); $this->addLine("Type: " . $error["type"]); if(strpos($error["file"], "src/pocketmine/") === false and strpos($error["file"], "src/raklib/") === false and file_exists($error["fullFile"])){ $this->addLine(); $this->addLine("THIS CRASH WAS CAUSED BY A PLUGIN"); $this->data["plugin"] = true; $reflection = new \ReflectionClass(PluginBase::class); $file = $reflection->getProperty("file"); $file->setAccessible(true); foreach($this->server->getPluginManager()->getPlugins() as $plugin){ $filePath = \pocketmine\cleanPath($file->getValue($plugin)); if(strpos($error["file"], $filePath) === 0){ $this->data["plugin"] = $plugin->getName(); $this->addLine("BAD PLUGIN : " . $plugin->getDescription()->getFullName()); break; } } }else{ $this->data["plugin"] = false; } $this->addLine(); $this->addLine("Code:"); $this->data["code"] = []; if($this->server->getProperty("auto-report.send-code", true) !== false){ $file = @file($error["fullFile"], FILE_IGNORE_NEW_LINES); for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10; ++$l){ $this->addLine("[" . ($l + 1) . "] " . @$file[$l]); $this->data["code"][$l + 1] = @$file[$l]; } } $this->addLine(); $this->addLine("Backtrace:"); foreach(($this->data["trace"] = $error["trace"]) as $line){ $this->addLine($line); } $this->addLine(); } private function generalData(){ $version = new VersionString(); $this->data["general"] = []; $this->data["general"]["protocol"] = Info::CURRENT_PROTOCOL; $this->data["general"]["api"] = \pocketmine\API_VERSION; $this->data["general"]["git"] = \pocketmine\GIT_COMMIT; $this->data["general"]["raklib"] = RakLib::VERSION; $this->data["general"]["uname"] = php_uname("a"); $this->data["general"]["php"] = phpversion(); $this->data["general"]["zend"] = zend_version(); $this->data["general"]["php_os"] = PHP_OS; $this->data["general"]["os"] = Utils::getOS(); $this->addLine("Genisys version: " . \pocketmine\GIT_COMMIT . " [Protocol " . Info::CURRENT_PROTOCOL . "; API " . API_VERSION . "]"); $this->addLine("uname -a: " . php_uname("a")); $this->addLine("PHP version: " . phpversion()); $this->addLine("Zend version: " . zend_version()); $this->addLine("OS : " . PHP_OS . ", " . Utils::getOS()); $this->addLine(); $this->addLine("Server uptime: " . $this->server->getUptime()); $this->addLine("Number of loaded worlds: " . count($this->server->getLevels())); $this->addLine("Players online: ".count($this->server->getOnlinePlayers())."/".$this->server->getMaxPlayers()); } public function addLine($line = ""){ fwrite($this->fp, $line . PHP_EOL); } public function add($str){ fwrite($this->fp, $str); } } ================================================ FILE: src/pocketmine/IPlayer.php ================================================ server = $server; $this->init(); } private function init(){ $this->memoryLimit = ((int) $this->server->getProperty("memory.main-limit", 0)) * 1024 * 1024; $defaultMemory = 1024; if(preg_match("/([0-9]+)([KMGkmg])/", $this->server->getConfigString("memory-limit", ""), $matches) > 0){ $m = (int) $matches[1]; if($m <= 0){ $defaultMemory = 0; }else{ switch(strtoupper($matches[2])){ case "K": $defaultMemory = $m / 1024; break; case "M": $defaultMemory = $m; break; case "G": $defaultMemory = $m * 1024; break; default: $defaultMemory = $m; break; } } } $hardLimit = ((int) $this->server->getProperty("memory.main-hard-limit", $defaultMemory)); if($hardLimit <= 0){ ini_set("memory_limit", -1); }else{ ini_set("memory_limit", $hardLimit . "M"); } $this->globalMemoryLimit = ((int) $this->server->getProperty("memory.global-limit", 0)) * 1024 * 1024; $this->checkRate = (int) $this->server->getProperty("memory.check-rate", 20); $this->continuousTrigger = (bool) $this->server->getProperty("memory.continuous-trigger", true); $this->continuousTriggerRate = (int) $this->server->getProperty("memory.continuous-trigger-rate", 30); $this->garbageCollectionPeriod = (int) $this->server->getProperty("memory.garbage-collection.period", 36000); $this->garbageCollectionTrigger = (bool) $this->server->getProperty("memory.garbage-collection.low-memory-trigger", true); $this->garbageCollectionAsync = (bool) $this->server->getProperty("memory.garbage-collection.collect-async-worker", true); $this->chunkLimit = (int) $this->server->getProperty("memory.max-chunks.trigger-limit", 96); $this->chunkCollect = (bool) $this->server->getProperty("memory.max-chunks.trigger-chunk-collect", true); $this->chunkTrigger = (bool) $this->server->getProperty("memory.max-chunks.low-memory-trigger", true); $this->chunkCache = (bool) $this->server->getProperty("memory.world-caches.disable-chunk-cache", true); $this->cacheTrigger = (bool) $this->server->getProperty("memory.world-caches.low-memory-trigger", true); gc_enable(); } public function isLowMemory(){ return $this->lowMemory; } public function canUseChunkCache(){ return !($this->lowMemory and $this->chunkTrigger); } public function getViewDistance($distance){ return $this->lowMemory ? min($this->chunkLimit, $distance) : $distance; } public function trigger($memory, $limit, $global = false, $triggerCount = 0){ $this->server->getLogger()->debug("[Memory Manager] ".($global ? "Global " : "") ."Low memory triggered, limit ". round(($limit / 1024) / 1024, 2)."MB, using ". round(($memory / 1024) / 1024, 2)."MB"); if($this->cacheTrigger){ foreach($this->server->getLevels() as $level){ $level->clearCache(true); } } if($this->chunkTrigger and $this->chunkCollect){ foreach($this->server->getLevels() as $level){ $level->doChunkGarbageCollection(); } } $ev = new LowMemoryEvent($memory, $limit, $global, $triggerCount); $this->server->getPluginManager()->callEvent($ev); $cycles = 0; if($this->garbageCollectionTrigger){ $cycles = $this->triggerGarbageCollector(); } $this->server->getLogger()->debug("[Memory Manager] Freed " . round(($ev->getMemoryFreed() / 1024) / 1024, 2)."MB, $cycles cycles"); } public function check(){ Timings::$memoryManagerTimer->startTiming(); if(($this->memoryLimit > 0 or $this->globalMemoryLimit > 0) and ++$this->checkTicker >= $this->checkRate){ $this->checkTicker = 0; $memory = Utils::getMemoryUsage(true); $trigger = false; if($this->memoryLimit > 0 and $memory[0] > $this->memoryLimit){ $trigger = 0; }elseif($this->globalMemoryLimit > 0 and $memory[1] > $this->globalMemoryLimit){ $trigger = 1; } if($trigger !== false){ if($this->lowMemory and $this->continuousTrigger){ if(++$this->continuousTriggerTicker >= $this->continuousTriggerRate){ $this->continuousTriggerTicker = 0; $this->trigger($memory[$trigger], $this->memoryLimit, $trigger > 0, ++$this->continuousTriggerCount); } }else{ $this->lowMemory = true; $this->continuousTriggerCount = 0; $this->trigger($memory[$trigger], $this->memoryLimit, $trigger > 0); } }else{ $this->lowMemory = false; } } if($this->garbageCollectionPeriod > 0 and ++$this->garbageCollectionTicker >= $this->garbageCollectionPeriod){ $this->garbageCollectionTicker = 0; $this->triggerGarbageCollector(); } Timings::$memoryManagerTimer->stopTiming(); } public function triggerGarbageCollector(){ Timings::$garbageCollectorTimer->startTiming(); if($this->garbageCollectionAsync){ $size = $this->server->getScheduler()->getAsyncTaskPoolSize(); for($i = 0; $i < $size; ++$i){ $this->server->getScheduler()->scheduleAsyncTaskToWorker(new GarbageCollectionTask(), $i); } } $cycles = gc_collect_cycles(); foreach($this->server->getLevels() as $level){ $level->doChunkGarbageCollection(); } Timings::$garbageCollectorTimer->stopTiming(); return $cycles; } /** * @param object $object * * @return string Object identifier for future checks */ public function addObjectWatcher($object){ if(!is_object($object)){ throw new \InvalidArgumentException("Not an object!"); } $identifier = spl_object_hash($object) . ":" . get_class($object); if(isset($this->leakInfo[$identifier])){ return $this->leakInfo["id"]; } $this->leakInfo[$identifier] = [ "id" => $id = md5($identifier . ":" . $this->leakSeed++), "class" => get_class($object), "hash" => $identifier ]; $this->leakInfo[$id] = $this->leakInfo[$identifier]; $this->leakWatch[$id] = new \WeakRef($object); return $id; } public function isObjectAlive($id){ if(isset($this->leakWatch[$id])){ return $this->leakWatch[$id]->valid(); } return false; } public function removeObjectWatch($id){ if(!isset($this->leakWatch[$id])){ return; } unset($this->leakInfo[$this->leakInfo[$id]["hash"]]); unset($this->leakInfo[$id]); unset($this->leakWatch[$id]); } public function doObjectCleanup(){ foreach($this->leakWatch as $id => $w){ if(!$w->valid()){ $this->removeObjectWatch($id); } } } public function getObjectInformation($id, $includeObject = false){ if(!isset($this->leakWatch[$id])){ return null; } $valid = false; $references = 0; $object = null; if($this->leakWatch[$id]->acquire()){ $object = $this->leakWatch[$id]->get(); $this->leakWatch[$id]->release(); $valid = true; $references = getReferenceCount($object, false); } return [ "id" => $id, "class" => $this->leakInfo[$id]["class"], "hash" => $this->leakInfo[$id]["hash"], "valid" => $valid, "references" => $references, "object" => $includeObject ? $object : null ]; } public function dumpServerMemory($outputFolder, $maxNesting, $maxStringSize){ gc_disable(); ini_set("memory_limit",-1); if(!file_exists($outputFolder)){ mkdir($outputFolder, 0777, true); } $this->server->getLogger()->notice("[Dump] After the memory dump is done, the server will shut down"); $obData = fopen($outputFolder . "/objects.js", "wb+"); $staticProperties = []; $data = []; $objects = []; $refCounts = []; $this->continueDump($this->server, $data, $objects, $refCounts, 0, $maxNesting, $maxStringSize); do{ $continue = false; foreach($objects as $hash => $object){ if(!is_object($object)){ continue; } $continue = true; $className = get_class($object); $objects[$hash] = true; $reflection = new \ReflectionObject($object); $info = [ "information" => "$hash@$className", "properties" => [] ]; if($reflection->getParentClass()){ $info["parent"] = $reflection->getParentClass()->getName(); } if(count($reflection->getInterfaceNames()) > 0){ $info["implements"] = implode(", ", $reflection->getInterfaceNames()); } foreach($reflection->getProperties() as $property){ if($property->isStatic()){ continue; } if(!$property->isPublic()){ $property->setAccessible(true); } $this->continueDump($property->getValue($object), $info["properties"][$property->getName()], $objects, $refCounts, 0, $maxNesting, $maxStringSize); } fwrite($obData, "$hash@$className: ". json_encode($info, JSON_UNESCAPED_SLASHES) . "\n"); if(!isset($objects["staticProperties"][$className])){ $staticProperties[$className] = []; foreach($reflection->getProperties() as $property){ if(!$property->isStatic() or $property->getDeclaringClass()->getName() !== $className){ continue; } if(!$property->isPublic()){ $property->setAccessible(true); } $this->continueDump($property->getValue($object), $staticProperties[$className][$property->getName()], $objects, $refCounts, 0, $maxNesting, $maxStringSize); } } } echo "[Dump] Wrote " . count($objects) . " objects\n"; }while($continue); file_put_contents($outputFolder . "/staticProperties.js", json_encode($staticProperties, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); file_put_contents($outputFolder . "/serverEntry.js", json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); file_put_contents($outputFolder . "/referenceCounts.js", json_encode($refCounts, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); echo "[Dump] Finished!\n"; gc_enable(); $this->server->forceShutdown(); } private function continueDump($from, &$data, &$objects, &$refCounts, $recursion, $maxNesting, $maxStringSize){ if($maxNesting <= 0){ $data = "(error) NESTING LIMIT REACHED"; return; } --$maxNesting; if(is_object($from)){ if(!isset($objects[$hash = spl_object_hash($from)])){ $objects[$hash] = $from; $refCounts[$hash] = 0; } ++$refCounts[$hash]; $data = "(object) $hash@" . get_class($from); }elseif(is_array($from)){ if($recursion >= 5){ $data = "(error) ARRAY RECURSION LIMIT REACHED"; return; } $data = []; foreach($from as $key => $value){ $this->continueDump($value, $data[$key], $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize); } }elseif(is_string($from)){ $data = "(string) len(". strlen($from) .") " . substr(Utils::printable($from), 0, $maxStringSize); }elseif(is_resource($from)){ $data = "(resource) " . print_r($from, true); }else{ $data = $from; } } } ================================================ FILE: src/pocketmine/OfflinePlayer.php ================================================ server = $server; $this->name = $name; if(file_exists($this->server->getDataPath() . "players/" . strtolower($this->getName()) . ".dat")){ $this->namedtag = $this->server->getOfflinePlayerData($this->name); }else{ $this->namedtag = null; } } public function isOnline(){ return $this->getPlayer() !== null; } public function getName(){ return $this->name; } public function getServer(){ return $this->server; } public function isOp(){ return $this->server->isOp(strtolower($this->getName())); } public function setOp($value){ if($value === $this->isOp()){ return; } if($value === true){ $this->server->addOp(strtolower($this->getName())); }else{ $this->server->removeOp(strtolower($this->getName())); } } public function isBanned(){ return $this->server->getNameBans()->isBanned(strtolower($this->getName())); } public function setBanned($value){ if($value === true){ $this->server->getNameBans()->addBan($this->getName(), null, null, null); }else{ $this->server->getNameBans()->remove($this->getName()); } } public function isWhitelisted(){ return $this->server->isWhitelisted(strtolower($this->getName())); } public function setWhitelisted($value){ if($value === true){ $this->server->addWhitelist(strtolower($this->getName())); }else{ $this->server->removeWhitelist(strtolower($this->getName())); } } public function getPlayer(){ return $this->server->getPlayerExact($this->getName()); } public function getFirstPlayed(){ return $this->namedtag instanceof CompoundTag ? $this->namedtag["firstPlayed"] : null; } public function getLastPlayed(){ return $this->namedtag instanceof CompoundTag ? $this->namedtag["lastPlayed"] : null; } public function hasPlayedBefore(){ return $this->namedtag instanceof CompoundTag; } public function setMetadata($metadataKey, MetadataValue $metadataValue){ $this->server->getPlayerMetadata()->setMetadata($this, $metadataKey, $metadataValue); } public function getMetadata($metadataKey){ return $this->server->getPlayerMetadata()->getMetadata($this, $metadataKey); } public function hasMetadata($metadataKey){ return $this->server->getPlayerMetadata()->hasMetadata($this, $metadataKey); } public function removeMetadata($metadataKey, Plugin $plugin){ $this->server->getPlayerMetadata()->removeMetadata($this, $metadataKey, $plugin); } } ================================================ FILE: src/pocketmine/Player.php ================================================ */ protected $windows; /** @var Inventory[] */ protected $windowIndex = []; protected $messageCounter = 2; protected $sendIndex = 0; private $clientSecret; /** @var Vector3 */ public $speed = null; public $blocked = false; public $achievements = []; public $lastCorrect; public $craftingType = self::CRAFTING_SMALL; //0 = 2x2 crafting, 1 = 3x3 crafting, 2 = anvil, 3 = enchanting protected $isCrafting = false; public $creationTime = 0; protected $randomClientId; protected $protocol; protected $lastMovement = 0; /** @var Vector3 */ protected $forceMovement = null; /** @var Vector3 */ protected $teleportPosition = null; protected $connected = true; protected $ip; protected $removeFormat = false; protected $port; protected $username; protected $iusername; protected $displayName; protected $deviceModel; protected $startAction = -1; /** @var Vector3 */ protected $sleeping = null; protected $clientID = null; private $loaderId = null; protected $stepHeight = 0.6; public $usedChunks = []; protected $chunkLoadCount = 0; protected $loadQueue = []; protected $nextChunkOrderRun = 5; /** @var Player[] */ protected $hiddenPlayers = []; /** @var Vector3 */ protected $newPosition; protected $viewDistance; protected $chunksPerTick; protected $spawnThreshold; /** @var null|WeakPosition */ private $spawnPosition = null; protected $inAirTicks = 0; protected $startAirTicks = 5; //TODO: Abilities protected $autoJump = true; protected $allowFlight = false; protected $flying = false; private $needACK = []; private $batchedPackets = []; /** @var PermissibleBase */ private $perm = null; public $weatherData = [0, 0, 0]; /** @var Vector3 */ public $fromPos = null; private $portalTime = 0; protected $shouldSendStatus = false; /** @var Position */ private $shouldResPos; /** @var FishingHook */ public $fishingHook = null; /** @var Position[] */ public $selectedPos = []; /** @var Level[] */ public $selectedLev = []; /** @var Item[] */ protected $personalCreativeItems = []; public function linkHookToPlayer(FishingHook $entity){ if($entity->isAlive()){ $this->setFishingHook($entity); $pk = new EntityEventPacket(); $pk->eid = $this->getFishingHook()->getId(); $pk->event = EntityEventPacket::FISH_HOOK_POSITION; $this->server->broadcastPacket($this->level->getPlayers(), $pk); return true; } return false; } public function unlinkHookFromPlayer(){ if($this->fishingHook instanceof FishingHook){ $pk = new EntityEventPacket(); $pk->eid = $this->fishingHook->getId(); $pk->event = EntityEventPacket::FISH_HOOK_TEASE; $this->server->broadcastPacket($this->level->getPlayers(), $pk); $this->setFishingHook(); return true; } return false; } public function isFishing(){ return ($this->fishingHook instanceof FishingHook); } public function getFishingHook(){ return $this->fishingHook; } public function setFishingHook(FishingHook $entity = null){ if($entity == null and $this->fishingHook instanceof FishingHook){ $this->fishingHook->close(); } $this->fishingHook = $entity; } public function getItemInHand(){ return $this->inventory->getItemInHand(); } public function getLeaveMessage(){ return new TranslationContainer(TextFormat::YELLOW . "%multiplayer.player.left", [ $this->getDisplayName() ]); } /** * @deprecated Use Human::setTotalXp($xp), this method will be removed in the future. */ public function setExperienceAndLevel(int $exp, int $level){ trigger_error("This method is deprecated, do not use it", E_USER_DEPRECATED); return $this->setTotalXp(self::getTotalXpRequirement($level) + $exp); } /** * @deprecated Use Human::setTotalXp($xp), this method will be removed in the future. */ public function setExp(int $exp){ trigger_error("This method is deprecated, do not use it", E_USER_DEPRECATED); return $this->setTotalXp($exp); } /** * @deprecated Use Human::setXpLevel($level), this method will be removed in the future. */ public function setExpLevel(int $level){ trigger_error("This method is deprecated, do not use it", E_USER_DEPRECATED); return $this->setXpLevel($level); } /** * @deprecated Use Human::getTotalXpRequirement($level), this method will be removed in the future. */ public function getExpectedExperience(){ trigger_error("This method is deprecated, do not use it", E_USER_DEPRECATED); return self::getTotalXpRequirement($this->getXpLevel() + 1); } /** * @deprecated Use Human::getLevelXpRequirement($level), this method will be removed in the future. */ public function getLevelUpExpectedExperience(){ trigger_error("This method is deprecated, do not use it", E_USER_DEPRECATED); return self::getLevelXpRequirement($this->getXpLevel() + 1); } /** * @deprecated */ public function calcExpLevel(){ trigger_error("This method is deprecated, do not use it", E_USER_DEPRECATED); } /** * @deprecated Use Human::addXp($xp), this method will be removed in the future. */ public function addExperience(int $exp){ trigger_error("This method is deprecated, do not use it", E_USER_DEPRECATED); return $this->addXp($exp); } /** * @deprecated Use Human::addXpLevel(), this method will be removed in the future. */ public function addExpLevel(int $level){ trigger_error("This method is deprecated, do not use it", E_USER_DEPRECATED); return $this->addXpLevel($level); } /** * @deprecated Use Human::getTotalXp(), this method will be removed in the future. */ public function getExp(){ trigger_error("This method is deprecated, do not use it", E_USER_DEPRECATED); return $this->getTotalXp(); } /** * @deprecated Use Human::getXpLevel(), this method will be removed in the future. */ public function getExpLevel(){ trigger_error("This method is deprecated, do not use it", E_USER_DEPRECATED); return $this->getXpLevel(); } /** * @deprecated Use Human::canPickupXp(), this method will be removed in the future. */ public function canPickupExp(): bool{ trigger_error("This method is deprecated, do not use it", E_USER_DEPRECATED); return $this->canPickupXp(); } /** * @deprecated Use Human::resetXpCooldown(), this method will be removed in the future. */ public function resetExpCooldown(){ trigger_error("This method is deprecated, do not use it", E_USER_DEPRECATED); $this->resetXpCooldown(); } /** * @deprecated */ public function updateExperience(){ trigger_error("This method is deprecated, do not use it", E_USER_DEPRECATED); } /** * This might disappear in the future. * Please use getUniqueId() instead (IP + clientId + name combo, in the future it'll change to real UUID for online * auth) */ public function getClientId(){ return $this->randomClientId; } public function getDeviceModel(){ return $this->deviceModel; } public function getClientSecret(){ return $this->clientSecret; } public function isBanned(){ return $this->server->getNameBans()->isBanned(strtolower($this->getName())); } public function setBanned($value){ if($value === true){ $this->server->getNameBans()->addBan($this->getName(), null, null, null); $this->kick(TextFormat::RED . "You have been banned"); }else{ $this->server->getNameBans()->remove($this->getName()); } } public function isWhitelisted() : bool{ return $this->server->isWhitelisted(strtolower($this->getName())); } public function setWhitelisted($value){ if($value === true){ $this->server->addWhitelist(strtolower($this->getName())); }else{ $this->server->removeWhitelist(strtolower($this->getName())); } } public function getPlayer(){ return $this; } public function getFirstPlayed(){ return $this->namedtag instanceof CompoundTag ? $this->namedtag["firstPlayed"] : null; } public function getLastPlayed(){ return $this->namedtag instanceof CompoundTag ? $this->namedtag["lastPlayed"] : null; } public function hasPlayedBefore(){ return $this->playedBefore; } public function setAllowFlight($value){ $this->allowFlight = (bool) $value; $this->sendSettings(); } public function usingElytra() { if ($this->getInventory()->getChestplate() instanceof Elytra) { return true; } return false; } public function getAllowFlight() : bool{ return $this->allowFlight; } public function setFlying(bool $value){ $this->flying = $value; $this->sendSettings(); } public function isFlying() : bool{ return $this->flying; } public function setAutoJump($value){ $this->autoJump = $value; $this->sendSettings(); } public function hasAutoJump() : bool{ return $this->autoJump; } /** * @param Player $player */ public function spawnTo(Player $player){ if($this->spawned and $player->spawned and $this->isAlive() and $player->isAlive() and $player->getLevel() === $this->level and $player->canSee($this) and !$this->isSpectator()){ parent::spawnTo($player); } } /** * @return Server */ public function getServer(){ return $this->server; } /** * @return bool */ public function getRemoveFormat(){ return $this->removeFormat; } /** * @param bool $remove */ public function setRemoveFormat($remove = true){ $this->removeFormat = (bool) $remove; } /** * @param Player $player * * @return bool */ public function canSee(Player $player) : bool{ return !isset($this->hiddenPlayers[$player->getRawUniqueId()]); } /** * @param Player $player */ public function hidePlayer(Player $player){ if($player === $this){ return; } $this->hiddenPlayers[$player->getRawUniqueId()] = $player; $player->despawnFrom($this); } /** * @param Player $player */ public function showPlayer(Player $player){ if($player === $this){ return; } unset($this->hiddenPlayers[$player->getRawUniqueId()]); if($player->isOnline()){ $player->spawnTo($this); } } public function canCollideWith(Entity $entity) : bool{ return false; } public function resetFallDistance(){ parent::resetFallDistance(); if($this->inAirTicks !== 0){ $this->startAirTicks = 5; } $this->inAirTicks = 0; } /** * @return bool */ public function isOnline() : bool{ return $this->connected === true and $this->loggedIn === true; } /** * @return bool */ public function isOp() : bool{ return $this->server->isOp($this->getName()); } /** * @param bool $value */ public function setOp($value){ if($value === $this->isOp()){ return; } if($value === true){ $this->server->addOp($this->getName()); }else{ $this->server->removeOp($this->getName()); } $this->recalculatePermissions(); $this->sendSettings(); } /** * @param permission\Permission|string $name * * @return bool */ public function isPermissionSet($name){ return $this->perm->isPermissionSet($name); } /** * @param permission\Permission|string $name * * @return bool */ public function hasPermission($name) : bool{ if($this->perm == null) return false;else return $this->perm->hasPermission($name); } /** * @param Plugin $plugin * @param string $name * @param bool $value * * @return permission\PermissionAttachment */ public function addAttachment(Plugin $plugin, $name = null, $value = null){ if($this->perm == null) return false; return $this->perm->addAttachment($plugin, $name, $value); } /** * @param PermissionAttachment $attachment * @return bool */ public function removeAttachment(PermissionAttachment $attachment){ if($this->perm == null){ return false; } $this->perm->removeAttachment($attachment); return true; } public function recalculatePermissions(){ $this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this); $this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this); if($this->perm === null){ return; } $this->perm->recalculatePermissions(); if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){ $this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this); } if($this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){ $this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this); } $this->sendCommandData(); } /** * @return permission\PermissionAttachmentInfo[] */ public function getEffectivePermissions(){ return $this->perm->getEffectivePermissions(); } public function sendCommandData(){ $data = new \stdClass(); $count = 0; foreach($this->server->getCommandMap()->getCommands() as $command){ if(($cmdData = $command->generateCustomCommandData($this)) !== null){ ++$count; $data->{$command->getName()}->versions[0] = $cmdData; } } if($count > 0){ //TODO: structure checking $pk = new AvailableCommandsPacket(); $pk->commands = json_encode($data); $this->dataPacket($pk); } } /** * @param SourceInterface $interface * @param null $clientID * @param string $ip * @param int $port */ public function __construct(SourceInterface $interface, $clientID, $ip, $port){ $this->interface = $interface; $this->windows = new \SplObjectStorage(); $this->perm = new PermissibleBase($this); $this->namedtag = new CompoundTag(); $this->server = Server::getInstance(); $this->lastBreak = PHP_INT_MAX; $this->ip = $ip; $this->port = $port; $this->clientID = $clientID; $this->loaderId = Level::generateChunkLoaderId($this); $this->chunksPerTick = (int) $this->server->getProperty("chunk-sending.per-tick", 4); $this->spawnThreshold = (int) $this->server->getProperty("chunk-sending.spawn-threshold", 56); $this->spawnPosition = null; $this->gamemode = $this->server->getGamemode(); $this->setLevel($this->server->getDefaultLevel()); $this->viewDistance = $this->server->getViewDistance(); $this->newPosition = new Vector3(0, 0, 0); $this->boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0); $this->uuid = null; $this->rawUUID = null; $this->creationTime = microtime(true); $this->exp = 0; $this->expLevel = 0; $this->food = 20; Entity::setHealth(20); } /** * @param string $achievementId */ public function removeAchievement($achievementId){ if($this->hasAchievement($achievementId)){ $this->achievements[$achievementId] = false; } } /** * @param string $achievementId * * @return bool */ public function hasAchievement($achievementId) : bool{ if(!isset(Achievement::$list[$achievementId]) or !isset($this->achievements)){ $this->achievements = []; return false; } return isset($this->achievements[$achievementId]) and $this->achievements[$achievementId] != false; } /** * @return bool */ public function isConnected() : bool{ return $this->connected === true; } /** * Gets the "friendly" name to display of this player to use in the chat. * * @return string */ public function getDisplayName(){ return $this->displayName; } /** * @param string $name */ public function setDisplayName($name){ $this->displayName = $name; if($this->spawned){ $this->server->updatePlayerListData($this->getUniqueId(), $this->getId(), $this->getDisplayName(), $this->getSkinId(), $this->getSkinData()); } } public function setSkin($str, $skinId){ parent::setSkin($str, $skinId); if($this->spawned){ $this->server->updatePlayerListData($this->getUniqueId(), $this->getId(), $this->getDisplayName(), $skinId, $str); } } /** * Gets the player IP address * * @return string */ public function getAddress() : string{ return $this->ip; } /** * @return int */ public function getPort() : int{ return $this->port; } public function getNextPosition(){ return $this->newPosition !== null ? new Position($this->newPosition->x, $this->newPosition->y, $this->newPosition->z, $this->level) : $this->getPosition(); } /** * @return bool */ public function isSleeping() : bool{ return $this->sleeping !== null; } public function getInAirTicks(){ return $this->inAirTicks; } protected function switchLevel(Level $targetLevel){ $oldLevel = $this->level; if(parent::switchLevel($targetLevel)){ foreach($this->usedChunks as $index => $d){ Level::getXZ($index, $X, $Z); $this->unloadChunk($X, $Z, $oldLevel); } $this->usedChunks = []; $pk = new SetTimePacket(); $pk->time = $this->level->getTime(); $pk->started = $this->level->stopTime == false; $this->dataPacket($pk); if($targetLevel->getDimension() != $oldLevel->getDimension()){ $pk = new ChangeDimensionPacket(); $pk->dimension = $targetLevel->getDimension(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $this->dataPacket($pk); $this->shouldSendStatus = true; } $targetLevel->getWeather()->sendWeather($this); if($this->spawned){ $this->spawnToAll(); } } } private function unloadChunk($x, $z, Level $level = null){ $level = $level === null ? $this->level : $level; $index = Level::chunkHash($x, $z); if(isset($this->usedChunks[$index])){ $chunk = $level->getChunk($x, $z); foreach($chunk->getEntities() as $entity){ if($entity !== $this){ $entity->despawnFrom($this); } } if($level !== $this->level){ $pk = new FullChunkDataPacket(); $pk->chunkX = $x; $pk->chunkZ = $z; $pk->data = chr($chunk->getSubChunkSendCount()); $this->dataPacket($pk); } unset($this->usedChunks[$index]); } $level->unregisterChunkLoader($this, $x, $z); unset($this->loadQueue[$index]); } /** * @return Position */ public function getSpawn(){ if($this->hasValidSpawnPosition()){ return $this->spawnPosition; }else{ $level = $this->server->getDefaultLevel(); return $level->getSafeSpawn(); } } /** * @return bool */ public function hasValidSpawnPosition() : bool{ return $this->spawnPosition instanceof WeakPosition and $this->spawnPosition->isValid(); } public function sendChunk($x, $z, $payload){ if($this->connected === false){ return; } $this->usedChunks[Level::chunkHash($x, $z)] = true; $this->chunkLoadCount++; if($payload instanceof DataPacket){ $this->dataPacket($payload); }else{ $pk = new FullChunkDataPacket(); $pk->chunkX = $x; $pk->chunkZ = $z; $pk->data = $payload; $this->batchDataPacket($pk); } if($this->spawned){ foreach($this->level->getChunkEntities($x, $z) as $entity){ if($entity !== $this and !$entity->closed and $entity->isAlive()){ $entity->spawnTo($this); } } } } protected function sendNextChunk(){ if($this->connected === false){ return; } Timings::$playerChunkSendTimer->startTiming(); $count = 0; foreach($this->loadQueue as $index => $distance){ if($count >= $this->chunksPerTick){ break; } $X = null; $Z = null; Level::getXZ($index, $X, $Z); ++$count; $this->usedChunks[$index] = false; $this->level->registerChunkLoader($this, $X, $Z, true); if(!$this->level->populateChunk($X, $Z)){ if($this->spawned and $this->teleportPosition === null){ continue; }else{ break; } } unset($this->loadQueue[$index]); $this->level->requestChunk($X, $Z, $this); if((count($this->loadQueue) == 0) and $this->shouldSendStatus){ $this->shouldSendStatus = false; $pk = new PlayStatusPacket(); $pk->status = PlayStatusPacket::PLAYER_SPAWN; $this->dataPacket($pk); /*$pk = new RespawnPacket(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $this->dataPacket($pk);*/ } } if($this->chunkLoadCount >= $this->spawnThreshold and $this->spawned === false and $this->teleportPosition === null){ $this->doFirstSpawn(); } Timings::$playerChunkSendTimer->stopTiming(); } protected function doFirstSpawn(){ $this->spawned = true; $this->sendPotionEffects($this); $this->sendData($this); $pk = new SetTimePacket(); $pk->time = $this->level->getTime(); $pk->started = $this->level->stopTime == false; $this->dataPacket($pk); $pos = $this->level->getSafeSpawn($this); $this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $pos)); $pos = $ev->getRespawnPosition(); if($pos->getY() < 127) $pos = $pos->add(0, 0.2, 0); /*$pk = new RespawnPacket(); $pk->x = $pos->x; $pk->y = $pos->y; $pk->z = $pos->z; $this->dataPacket($pk);*/ $pk = new PlayStatusPacket(); $pk->status = PlayStatusPacket::PLAYER_SPAWN; $this->dataPacket($pk); $this->noDamageTicks = 60; foreach($this->usedChunks as $index => $c){ Level::getXZ($index, $chunkX, $chunkZ); foreach($this->level->getChunkEntities($chunkX, $chunkZ) as $entity){ if($entity !== $this and !$entity->closed and $entity->isAlive()){ $entity->spawnTo($this); } } } $this->teleport($pos); $this->allowFlight = (($this->gamemode == 3) or ($this->gamemode == 1)); $this->setHealth($this->getHealth()); $this->server->getPluginManager()->callEvent($ev = new PlayerJoinEvent($this, new TranslationContainer(TextFormat::YELLOW . "%multiplayer.player.joined", [ $this->getDisplayName() ]))); $this->sendSettings(); if(strlen(trim($msg = $ev->getJoinMessage())) > 0){ if($this->server->playerMsgType === Server:: PLAYER_MSG_TYPE_MESSAGE) $this->server->broadcastMessage($msg); elseif($this->server->playerMsgType === Server::PLAYER_MSG_TYPE_TIP) $this->server->broadcastTip(str_replace("@player", $this->getName(), $this->server->playerLoginMsg)); elseif($this->server->playerMsgType === Server::PLAYER_MSG_TYPE_POPUP) $this->server->broadcastPopup(str_replace("@player", $this->getName(), $this->server->playerLoginMsg)); } $this->server->onPlayerLogin($this); $this->spawnToAll(); $this->level->getWeather()->sendWeather($this); if($this->server->dserverConfig["enable"] and $this->server->dserverConfig["queryAutoUpdate"]){ $this->server->updateQuery(); } /*if($this->server->getUpdater()->hasUpdate() and $this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){ $this->server->getUpdater()->showPlayerUpdate($this); }*/ if($this->getHealth() <= 0){ $pk = new RespawnPacket(); $pos = $this->getSpawn(); $pk->x = $pos->x; $pk->y = $pos->y; $pk->z = $pos->z; $this->dataPacket($pk); } $this->inventory->sendContents($this); $this->inventory->sendArmorContents($this); } protected function orderChunks(){ if($this->connected === false){ return false; } Timings::$playerChunkOrderTimer->startTiming(); $this->nextChunkOrderRun = 200; $viewDistance = $this->server->getMemoryManager()->getViewDistance($this->viewDistance); $newOrder = []; $lastChunk = $this->usedChunks; $centerX = $this->x >> 4; $centerZ = $this->z >> 4; $layer = 1; $leg = 0; $x = 0; $z = 0; for($i = 0; $i < $viewDistance; ++$i){ $chunkX = $x + $centerX; $chunkZ = $z + $centerZ; if(!isset($this->usedChunks[$index = Level::chunkHash($chunkX, $chunkZ)]) or $this->usedChunks[$index] === false){ $newOrder[$index] = true; } unset($lastChunk[$index]); switch($leg){ case 0: ++$x; if($x === $layer){ ++$leg; } break; case 1: ++$z; if($z === $layer){ ++$leg; } break; case 2: --$x; if(-$x === $layer){ ++$leg; } break; case 3: --$z; if(-$z === $layer){ $leg = 0; ++$layer; } break; } } foreach($lastChunk as $index => $bool){ Level::getXZ($index, $X, $Z); $this->unloadChunk($X, $Z); } $this->loadQueue = $newOrder; Timings::$playerChunkOrderTimer->stopTiming(); return true; } /** * Batch a Data packet into the channel list to send at the end of the tick * * @param DataPacket $packet * * @return bool */ public function batchDataPacket(DataPacket $packet){ if($this->connected === false){ return false; } $timings = Timings::getSendDataPacketTimings($packet); $timings->startTiming(); $this->server->getPluginManager()->callEvent($ev = new DataPacketSendEvent($this, $packet)); if($ev->isCancelled()){ $timings->stopTiming(); return false; } if(!isset($this->batchedPackets)){ $this->batchedPackets = []; } $this->batchedPackets[] = clone $packet; $timings->stopTiming(); return true; } /** * Sends an ordered DataPacket to the send buffer * * @param DataPacket $packet * @param bool $needACK * * @return int|bool */ public function dataPacket(DataPacket $packet, $needACK = false){ if(!$this->connected){ return false; } $timings = Timings::getSendDataPacketTimings($packet); $timings->startTiming(); $this->server->getPluginManager()->callEvent($ev = new DataPacketSendEvent($this, $packet)); if($ev->isCancelled()){ $timings->stopTiming(); return false; } $identifier = $this->interface->putPacket($this, $packet, $needACK, false); if($needACK and $identifier !== null){ $this->needACK[$identifier] = false; $timings->stopTiming(); return $identifier; } $timings->stopTiming(); return true; } /** * @param DataPacket $packet * @param bool $needACK * * @return bool|int */ public function directDataPacket(DataPacket $packet, $needACK = false){ if($this->connected === false){ return false; } $timings = Timings::getSendDataPacketTimings($packet); $timings->startTiming(); $this->server->getPluginManager()->callEvent($ev = new DataPacketSendEvent($this, $packet)); if($ev->isCancelled()){ $timings->stopTiming(); return false; } $identifier = $this->interface->putPacket($this, $packet, $needACK, true); if($needACK and $identifier !== null){ $this->needACK[$identifier] = false; $timings->stopTiming(); return $identifier; } $timings->stopTiming(); return true; } /** * @param Vector3 $pos * * @return boolean */ public function sleepOn(Vector3 $pos){ if(!$this->isOnline()){ return false; } foreach($this->level->getNearbyEntities($this->boundingBox->grow(2, 1, 2), $this) as $p){ if($p instanceof Player){ if($p->sleeping !== null and $pos->distance($p->sleeping) <= 0.1){ return false; } } } $this->server->getPluginManager()->callEvent($ev = new PlayerBedEnterEvent($this, $this->level->getBlock($pos))); if($ev->isCancelled()){ return false; } $this->sleeping = clone $pos; $this->setDataProperty(self::DATA_PLAYER_BED_POSITION, self::DATA_TYPE_POS, [$pos->x, $pos->y, $pos->z]); $this->setDataFlag(self::DATA_PLAYER_FLAGS, self::DATA_PLAYER_FLAG_SLEEP, true, self::DATA_TYPE_BYTE); $this->setSpawn($pos); $this->level->sleepTicks = 60; return true; } /** * Sets the spawnpoint of the player (and the compass direction) to a Vector3, or set it on another world with a * Position object * * @param Vector3|Position $pos */ public function setSpawn(Vector3 $pos){ if(!($pos instanceof Position)){ $level = $this->level; }else{ $level = $pos->getLevel(); } $this->spawnPosition = new WeakPosition($pos->x, $pos->y, $pos->z, $level); $pk = new SetSpawnPositionPacket(); $pk->x = (int) $this->spawnPosition->x; $pk->y = (int) $this->spawnPosition->y; $pk->z = (int) $this->spawnPosition->z; $this->dataPacket($pk); } public function stopSleep(){ if($this->sleeping instanceof Vector3){ $this->server->getPluginManager()->callEvent($ev = new PlayerBedLeaveEvent($this, $this->level->getBlock($this->sleeping))); $this->sleeping = null; $this->setDataProperty(self::DATA_PLAYER_BED_POSITION, self::DATA_TYPE_POS, [0, 0, 0]); $this->setDataFlag(self::DATA_PLAYER_FLAGS, self::DATA_PLAYER_FLAG_SLEEP, false, self::DATA_TYPE_BYTE); $this->level->sleepTicks = 0; $pk = new AnimatePacket(); $pk->eid = 0; $pk->action = PlayerAnimationEvent::WAKE_UP; $this->dataPacket($pk); } } /** * @param string $achievementId * * @return bool */ public function awardAchievement($achievementId){ if(isset(Achievement::$list[$achievementId]) and !$this->hasAchievement($achievementId)){ foreach(Achievement::$list[$achievementId]["requires"] as $requirementId){ if(!$this->hasAchievement($requirementId)){ return false; } } $this->server->getPluginManager()->callEvent($ev = new PlayerAchievementAwardedEvent($this, $achievementId)); if(!$ev->isCancelled()){ $this->achievements[$achievementId] = true; Achievement::broadcast($this, $achievementId); return true; }else{ return false; } } return false; } /** * @return int */ public function getGamemode() : int{ return $this->gamemode; } /** * Sets the gamemode, and if needed, kicks the Player. * * @param int $gm * @param bool $client if the client made this change in their GUI * * @return bool */ public function setGamemode(int $gm, bool $client = false){ if($gm < 0 or $gm > 3 or $this->gamemode === $gm){ return false; } $this->server->getPluginManager()->callEvent($ev = new PlayerGameModeChangeEvent($this, $gm)); if($ev->isCancelled()){ if($client){ //gamemode change by client in the GUI $pk = new SetPlayerGameTypePacket(); $pk->gamemode = $this->gamemode & 0x01; $this->dataPacket($pk); $this->sendSettings(); } return false; } if($this->server->autoClearInv){ $this->inventory->clearAll(); } $this->gamemode = $gm; $this->allowFlight = $this->isCreative(); if($this->isSpectator()){ $this->flying = true; $this->despawnFromAll(); // Client automatically turns off flight controls when on the ground. // A combination of this hack and a new AdventureSettings flag FINALLY // fixes spectator flight controls. Thank @robske110 for this hack. $this->teleport($this->temporalVector->setComponents($this->x, $this->y + 0.1, $this->z)); }else{ if($this->isSurvival()){ $this->flying = false; } $this->spawnToAll(); } $this->resetFallDistance(); $this->namedtag->playerGameType = new IntTag("playerGameType", $this->gamemode); if(!$client){ //Gamemode changed by server, do not send for client changes $pk = new SetPlayerGameTypePacket(); $pk->gamemode = $this->gamemode & 0x01; $this->dataPacket($pk); }else{ Command::broadcastCommandMessage($this, new TranslationContainer("commands.gamemode.success.self", [Server::getGamemodeString($gm)])); } if($this->gamemode === Player::SPECTATOR){ $pk = new ContainerSetContentPacket(); $pk->windowid = ContainerSetContentPacket::SPECIAL_CREATIVE; $this->dataPacket($pk); }else{ $pk = new ContainerSetContentPacket(); $pk->windowid = ContainerSetContentPacket::SPECIAL_CREATIVE; $pk->slots = array_merge(Item::getCreativeItems(), $this->personalCreativeItems); $this->dataPacket($pk); } $this->sendSettings(); $this->inventory->sendContents($this); $this->inventory->sendContents($this->getViewers()); $this->inventory->sendHeldItem($this->hasSpawned); return true; } /** * Sends all the option flags */ public function sendSettings(){ $pk = new AdventureSettingsPacket(); $pk->flags = 0; $pk->worldImmutable = $this->isAdventure(); $pk->autoJump = $this->autoJump; $pk->allowFlight = $this->allowFlight; $pk->noClip = $this->isSpectator(); $pk->isFlying = $this->flying; $pk->userPermission = ($this->isOp() ? AdventureSettingsPacket::PERMISSION_OPERATOR : AdventureSettingsPacket::PERMISSION_NORMAL); $this->dataPacket($pk); } public function isSurvival() : bool{ return ($this->gamemode & 0x01) === 0; } public function isCreative() : bool{ return ($this->gamemode & 0x01) > 0; } public function isSpectator() : bool{ return $this->gamemode === 3; } public function isAdventure() : bool{ return ($this->gamemode & 0x02) > 0; } public function isFireProof() : bool{ return $this->isCreative(); } public function getDrops(){ if(!$this->isCreative()){ return parent::getDrops(); } return []; } public function setDataProperty($id, $type, $value){ if(parent::setDataProperty($id, $type, $value)){ $this->sendData($this, [$id => $this->dataProperties[$id]]); return true; } return false; } protected function checkGroundState($movX, $movY, $movZ, $dx, $dy, $dz){ if(!$this->onGround or $movY != 0){ $bb = clone $this->boundingBox; $bb->maxY = $bb->minY + 0.5; $bb->minY -= 1; if(count($this->level->getCollisionBlocks($bb, true)) > 0){ $this->onGround = true; }else{ $this->onGround = false; } } $this->isCollided = $this->onGround; } protected function checkBlockCollision(){ foreach($blocksaround = $this->getBlocksAround() as $block){ $block->onEntityCollide($this); if($this->getServer()->redstoneEnabled){ if($block instanceof PressurePlate){ $this->activatedPressurePlates[Level::blockHash($block->x, $block->y, $block->z)] = $block; } } } if($this->getServer()->redstoneEnabled){ /** @var \pocketmine\block\PressurePlate $block * */ foreach($this->activatedPressurePlates as $key => $block){ if(!isset($blocksaround[$key])) $block->checkActivation(); } } } protected function checkNearEntities($tickDiff){ foreach($this->level->getNearbyEntities($this->boundingBox->grow(0.5, 0.5, 0.5), $this) as $entity){ $entity->scheduleUpdate(); if(!$entity->isAlive()){ continue; } if($entity instanceof Arrow and $entity->hadCollision){ $item = Item::get(Item::ARROW, $entity->getPotionId(), 1); $add = false; if(!$this->server->allowInventoryCheats and !$this->isCreative()){ if(!$this->getFloatingInventory()->canAddItem($item) or !$this->inventory->canAddItem($item)){ //The item is added to the floating inventory to allow client to handle the pickup //We have to also check if it can be added to the real inventory before sending packets. continue; } $add = true; } $this->server->getPluginManager()->callEvent($ev = new InventoryPickupArrowEvent($this->inventory, $entity)); if($ev->isCancelled()){ continue; } $pk = new TakeItemEntityPacket(); $pk->eid = $this->getId(); $pk->target = $entity->getId(); $this->server->broadcastPacket($entity->getViewers(), $pk); $pk = new TakeItemEntityPacket(); $pk->eid = 0; $pk->target = $entity->getId(); $this->dataPacket($pk); if($add){ $this->getFloatingInventory()->addItem(clone $item); } $entity->kill(); }elseif($entity instanceof DroppedItem){ if($entity->getPickupDelay() <= 0){ $item = $entity->getItem(); if($item instanceof Item){ $add = false; if(!$this->server->allowInventoryCheats and !$this->isCreative()){ if(!$this->getFloatingInventory()->canAddItem($item) or !$this->inventory->canAddItem($item)){ continue; } $add = true; } $this->server->getPluginManager()->callEvent($ev = new InventoryPickupItemEvent($this->inventory, $entity)); if($ev->isCancelled()){ continue; } switch($item->getId()){ case Item::WOOD: $this->awardAchievement("mineWood"); break; case Item::DIAMOND: $this->awardAchievement("diamond"); break; } $pk = new TakeItemEntityPacket(); $pk->eid = $this->getId(); $pk->target = $entity->getId(); $this->server->broadcastPacket($entity->getViewers(), $pk); $pk = new TakeItemEntityPacket(); $pk->eid = 0; $pk->target = $entity->getId(); $this->dataPacket($pk); if($add){ $this->getFloatingInventory()->addItem(clone $item); } $entity->kill(); } } } } } protected function processMovement($tickDiff){ if(!$this->isAlive() or !$this->spawned or $this->newPosition === null or $this->teleportPosition !== null or $this->isSleeping()){ $this->setMoving(false); return; } $newPos = $this->newPosition; $distanceSquared = $newPos->distanceSquared($this); $revert = false; if($this->server->checkMovement){ if(($distanceSquared / ($tickDiff ** 2)) > 200){ $revert = true; }else{ if($this->chunk === null or !$this->chunk->isGenerated()){ $chunk = $this->level->getChunk($newPos->x >> 4, $newPos->z >> 4, false); if($chunk === null or !$chunk->isGenerated()){ $revert = true; $this->nextChunkOrderRun = 0; }else{ if($this->chunk !== null){ $this->chunk->removeEntity($this); } $this->chunk = $chunk; } } } }else{ if($this->chunk === null or !$this->chunk->isGenerated()){ $chunk = $this->level->getChunk($newPos->x >> 4, $newPos->z >> 4, false); if($chunk === null or !$chunk->isGenerated()){ $revert = true; $this->nextChunkOrderRun = 0; }else{ if($this->chunk !== null){ $this->chunk->removeEntity($this); } $this->chunk = $chunk; } } } if(!$revert and $distanceSquared != 0){ $dx = $newPos->x - $this->x; $dy = $newPos->y - $this->y; $dz = $newPos->z - $this->z; $this->move($dx, $dy, $dz); $diffX = $this->x - $newPos->x; $diffY = $this->y - $newPos->y; $diffZ = $this->z - $newPos->z; $yS = 0.5 + $this->ySize; if($diffY >= -$yS or $diffY <= $yS){ $diffY = 0; } $diff = ($diffX ** 2 + $diffY ** 2 + $diffZ ** 2) / ($tickDiff ** 2); if($diff > 0){ $this->x = $newPos->x; $this->y = $newPos->y; $this->z = $newPos->z; $radius = $this->width / 2; $this->boundingBox->setBounds($this->x - $radius, $this->y, $this->z - $radius, $this->x + $radius, $this->y + $this->height, $this->z + $radius); } } $from = new Location($this->lastX, $this->lastY, $this->lastZ, $this->lastYaw, $this->lastPitch, $this->level); $to = $this->getLocation(); $delta = pow($this->lastX - $to->x, 2) + pow($this->lastY - $to->y, 2) + pow($this->lastZ - $to->z, 2); $deltaAngle = abs($this->lastYaw - $to->yaw) + abs($this->lastPitch - $to->pitch); if(!$revert and ($delta > (1 / 16) or $deltaAngle > 10)){ $isFirst = ($this->lastX === null or $this->lastY === null or $this->lastZ === null); $this->lastX = $to->x; $this->lastY = $to->y; $this->lastZ = $to->z; $this->lastYaw = $to->yaw; $this->lastPitch = $to->pitch; if(!$isFirst){ $ev = new PlayerMoveEvent($this, $from, $to); $this->setMoving(true); $this->server->getPluginManager()->callEvent($ev); if(!($revert = $ev->isCancelled())){ //Yes, this is intended if($this->server->netherEnabled){ if($this->isInsideOfPortal()){ if($this->portalTime == 0){ $this->portalTime = $this->server->getTick(); } }else{ $this->portalTime = 0; } } if($to->distanceSquared($ev->getTo()) > 0.01){ //If plugins modify the destination $this->teleport($ev->getTo()); }else{ $this->addMovement($this->x, $this->y + $this->getEyeHeight(), $this->z, $this->yaw, $this->pitch, $this->yaw); } if($this->fishingHook instanceof FishingHook){ if($this->distance($this->fishingHook) > 33 or $this->inventory->getItemInHand()->getId() !== Item::FISHING_ROD){ $this->setFishingHook(); } } } } if(!$this->isSpectator()){ $this->checkNearEntities($tickDiff); } $this->speed = ($to->subtract($from))->divide($tickDiff); }elseif($distanceSquared == 0){ $this->speed = new Vector3(0, 0, 0); $this->setMoving(false); } if($revert){ $this->lastX = $from->x; $this->lastY = $from->y; $this->lastZ = $from->z; $this->lastYaw = $from->yaw; $this->lastPitch = $from->pitch; $this->sendPosition($from, $from->yaw, $from->pitch, MovePlayerPacket::MODE_RESET); $this->forceMovement = new Vector3($from->x, $from->y, $from->z); }else{ $this->forceMovement = null; if($distanceSquared != 0 and $this->nextChunkOrderRun > 20){ $this->nextChunkOrderRun = 20; } } $this->newPosition = null; } public function addMovement($x, $y, $z, $yaw, $pitch, $headYaw = null){ if($this->chunk !== null){ $this->level->addPlayerMovement($this->chunk->getX(), $this->chunk->getZ(), $this->id, $x, $y, $z, $yaw, $pitch, $this->onGround, $headYaw === null ? $yaw : $headYaw); } } public function setMotion(Vector3 $mot){ if(parent::setMotion($mot)){ if($this->chunk !== null){ $this->level->addEntityMotion($this->chunk->getX(), $this->chunk->getZ(), $this->getId(), $this->motionX, $this->motionY, $this->motionZ); $pk = new SetEntityMotionPacket(); $pk->eid = 0; $pk->motionX = $mot->x; $pk->motionY = $mot->y; $pk->motionZ = $mot->z; $this->dataPacket($pk); } if($this->motionY > 0){ $this->startAirTicks = (-(log($this->gravity / ($this->gravity + $this->drag * $this->motionY))) / $this->drag) * 2 + 5; } return true; } return false; } protected function updateMovement(){ } public $foodTick = 0; public $starvationTick = 0; public $foodUsageTime = 0; protected $moving = false; public function setMoving($moving){ $this->moving = $moving; } public function isMoving() : bool{ return $this->moving; } public function sendAttributes(bool $sendAll = false){ $entries = $sendAll ? $this->attributeMap->getAll() : $this->attributeMap->needSend(); if(count($entries) > 0){ $pk = new UpdateAttributesPacket(); $pk->entityId = 0; $pk->entries = $entries; $this->dataPacket($pk); foreach($entries as $entry){ $entry->markSynchronized(); } } } public function onUpdate($currentTick){ if(!$this->loggedIn){ return false; } $tickDiff = $currentTick - $this->lastUpdate; if($tickDiff <= 0){ return true; } $this->messageCounter = 2; $this->lastUpdate = $currentTick; $this->sendAttributes(); if(!$this->isAlive() and $this->spawned){ ++$this->deadTicks; if($this->deadTicks >= 10){ $this->despawnFromAll(); } return true; } $this->timings->startTiming(); if($this->spawned){ if($this->server->netherEnabled){ if(($this->isCreative() or $this->isSurvival() and $this->server->getTick() - $this->portalTime >= 80) and $this->portalTime > 0){ $netherLevel = null; if($this->server->isLevelLoaded($this->server->netherName) or $this->server->loadLevel($this->server->netherName)){ $netherLevel = $this->server->getLevelByName($this->server->netherName); } if($netherLevel instanceof Level){ if($this->getLevel() !== $netherLevel){ $this->fromPos = $this->getPosition(); $this->fromPos->x = ((int) $this->fromPos->x) + 0.5; $this->fromPos->z = ((int) $this->fromPos->z) + 0.5; $this->teleport($this->shouldResPos = $netherLevel->getSafeSpawn()); }elseif($this->fromPos instanceof Position){ if(!($this->getLevel()->isChunkLoaded($this->fromPos->x, $this->fromPos->z))){ $this->getLevel()->loadChunk($this->fromPos->x, $this->fromPos->z); } $add = [1, 0, -1, 0, 0, 1, 0, -1]; $tempos = null; for($j = 2; $j < 5; $j++){ for($i = 0; $i < 4; $i++){ if($this->fromPos->getLevel()->getBlock($this->temporalVector->fromObjectAdd($this->fromPos, $add[$i] * $j, 0, $add[$i + 4] * $j))->getId() === Block::AIR){ if($this->fromPos->getLevel()->getBlock($this->temporalVector->fromObjectAdd($this->fromPos, $add[$i] * $j, 1, $add[$i + 4] * $j))->getId() === Block::AIR){ $tempos = $this->fromPos->add($add[$i] * $j, 0, $add[$i + 4] * $j); //$this->getLevel()->getServer()->getLogger()->debug($tempos); break; } } } if($tempos != null){ break; } } if($tempos === null){ $tempos = $this->fromPos->add(mt_rand(-2, 2), 0, mt_rand(-2, 2)); } $this->teleport($this->shouldResPos = $tempos); $add = null; $tempos = null; $this->fromPos = null; }else{ $this->teleport($this->shouldResPos = $this->server->getDefaultLevel()->getSafeSpawn()); } $this->portalTime = 0; } } } $this->processMovement($tickDiff); $this->entityBaseTick($tickDiff); if($this->isOnFire() or $this->lastUpdate % 10 == 0){ if($this->isCreative() and !$this->isInsideOfFire()){ $this->extinguish(); }elseif($this->getLevel()->getWeather()->isRainy()){ if($this->getLevel()->canBlockSeeSky($this)){ $this->extinguish(); } } } if($this->server->antiFly){ if(!$this->isSpectator() and $this->speed !== null){ if($this->onGround){ if($this->inAirTicks !== 0){ $this->startAirTicks = 5; } $this->inAirTicks = 0; }else{ if(!$this->usingElytra() and !$this->allowFlight and $this->inAirTicks > 10 and !$this->isSleeping() and !$this->isImmobile()){ //expectedVelocity here is not calculated correctly //This causes players to fall too fast when bouncing on slime when antiFly is enabled $expectedVelocity = (-$this->gravity) / $this->drag - ((-$this->gravity) / $this->drag) * exp(-$this->drag * ($this->inAirTicks - $this->startAirTicks)); $diff = ($this->speed->y - $expectedVelocity) ** 2; if(!$this->hasEffect(Effect::JUMP) and $diff > 0.6 and $expectedVelocity < $this->speed->y and !$this->server->getAllowFlight()){ $this->setMotion($this->temporalVector->setComponents(0, $expectedVelocity, 0)); } } ++$this->inAirTicks; } } } if($this->getTransactionQueue() !== null){ $this->getTransactionQueue()->execute(); } } $this->checkTeleportPosition(); $this->timings->stopTiming(); return true; } public function checkNetwork(){ if(!$this->isOnline()){ return; } if($this->nextChunkOrderRun-- <= 0 or $this->chunk === null){ $this->orderChunks(); } if(count($this->loadQueue) > 0 or !$this->spawned){ $this->sendNextChunk(); } if(count($this->batchedPackets) > 0){ $this->server->batchPackets([$this], $this->batchedPackets, false); $this->batchedPackets = []; } } public function canInteract(Vector3 $pos, $maxDistance, $maxDiff = 0.5){ $eyePos = $this->getPosition()->add(0, $this->getEyeHeight(), 0); if($eyePos->distanceSquared($pos) > $maxDistance ** 2){ return false; } $dV = $this->getDirectionPlane(); $dot = $dV->dot(new Vector2($eyePos->x, $eyePos->z)); $dot1 = $dV->dot(new Vector2($pos->x, $pos->z)); return ($dot1 - $dot) >= -$maxDiff; } public function onPlayerPreLogin(){ $pk = new PlayStatusPacket(); $pk->status = PlayStatusPacket::LOGIN_SUCCESS; $this->dataPacket($pk); $this->processLogin(); } public function clearCreativeItems(){ $this->personalCreativeItems = []; } public function getCreativeItems() : array{ return $this->personalCreativeItems; } public function addCreativeItem(Item $item){ $this->personalCreativeItems[] = Item::get($item->getId(), $item->getDamage()); } public function removeCreativeItem(Item $item){ $index = $this->getCreativeItemIndex($item); if($index !== -1){ unset($this->personalCreativeItems[$index]); } } public function getCreativeItemIndex(Item $item) : int{ foreach($this->personalCreativeItems as $i => $d){ if($item->equals($d, !$item->isTool())){ return $i; } } return -1; } protected function processLogin(){ if(!$this->server->isWhitelisted(strtolower($this->getName()))){ $this->close($this->getLeaveMessage(), "Server is white-listed"); return; }elseif($this->server->getNameBans()->isBanned(strtolower($this->getName())) or $this->server->getIPBans()->isBanned($this->getAddress()) or $this->server->getCIDBans()->isBanned($this->randomClientId)){ $this->close($this->getLeaveMessage(), TextFormat::RED . "You are banned"); return; } if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){ $this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this); } if($this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){ $this->server->getPluginManager()->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this); } foreach($this->server->getOnlinePlayers() as $p){ if($p !== $this and strtolower($p->getName()) === strtolower($this->getName())){ if($p->kick("logged in from another location") === false){ $this->close($this->getLeaveMessage(), "Logged in from another location"); return; } }elseif($p->loggedIn and $this->getUniqueId()->equals($p->getUniqueId())){ if($p->kick("logged in from another location") === false){ $this->close($this->getLeaveMessage(), "Logged in from another location"); return; } } } $this->setNameTag($this->getDisplayName()); $nbt = $this->server->getOfflinePlayerData($this->username); $this->playedBefore = ($nbt["lastPlayed"] - $nbt["firstPlayed"]) > 1; if(!isset($nbt->NameTag)){ $nbt->NameTag = new StringTag("NameTag", $this->username); }else{ $nbt["NameTag"] = $this->username; } if(!isset($nbt->Hunger) or !isset($nbt->Health) or !isset($nbt->MaxHealth)){ $nbt->Hunger = new ShortTag("Hunger", 20); $nbt->Health = new ShortTag("Health", 20); $nbt->MaxHealth = new ShortTag("MaxHealth", 20); } $this->food = $nbt["Hunger"]; $this->setMaxHealth($nbt["MaxHealth"]); Entity::setHealth(($nbt["Health"] <= 0) ? 20 : $nbt["Health"]); $this->gamemode = $nbt["playerGameType"] & 0x03; if($this->server->getForceGamemode()){ $this->gamemode = $this->server->getGamemode(); $nbt->playerGameType = new IntTag("playerGameType", $this->gamemode); } $this->allowFlight = $this->isCreative(); if(($level = $this->server->getLevelByName($nbt["Level"])) === null){ $this->setLevel($this->server->getDefaultLevel()); $nbt["Level"] = $this->level->getName(); $nbt["Pos"][0] = $this->level->getSpawnLocation()->x; $nbt["Pos"][1] = $this->level->getSpawnLocation()->y; $nbt["Pos"][2] = $this->level->getSpawnLocation()->z; }else{ $this->setLevel($level); } if(!($nbt instanceof CompoundTag)){ $this->close($this->getLeaveMessage(), "Invalid data"); return; } $this->achievements = []; /** @var ByteTag $achievement */ foreach($nbt->Achievements as $achievement){ $this->achievements[$achievement->getName()] = $achievement->getValue() > 0 ? true : false; } $nbt->lastPlayed = new LongTag("lastPlayed", floor(microtime(true) * 1000)); if($this->server->getAutoSave()){ $this->server->saveOfflinePlayerData($this->username, $nbt, true); } parent::__construct($this->level->getChunk($nbt["Pos"][0] >> 4, $nbt["Pos"][2] >> 4, true), $nbt); $this->loggedIn = true; $this->server->addOnlinePlayer($this); $this->server->getPluginManager()->callEvent($ev = new PlayerLoginEvent($this, "Plugin reason")); if($ev->isCancelled()){ $this->close($this->getLeaveMessage(), $ev->getKickMessage()); return; } if(!$this->isConnected()){ return; } $this->dataPacket(new ResourcePacksInfoPacket()); if(!$this->hasValidSpawnPosition() and isset($this->namedtag->SpawnLevel) and ($level = $this->server->getLevelByName($this->namedtag["SpawnLevel"])) instanceof Level){ $this->spawnPosition = new WeakPosition($this->namedtag["SpawnX"], $this->namedtag["SpawnY"], $this->namedtag["SpawnZ"], $level); } $spawnPosition = $this->getSpawn(); $pk = new StartGamePacket(); $pk->entityUniqueId = 0; $pk->entityRuntimeId = 0; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->seed = -1; $pk->dimension = $this->level->getDimension(); $pk->gamemode = $this->gamemode & 0x01; $pk->difficulty = $this->server->getDifficulty(); $pk->spawnX = $spawnPosition->getFloorX(); $pk->spawnY = $spawnPosition->getFloorY(); $pk->spawnZ = $spawnPosition->getFloorZ(); $pk->hasBeenLoadedInCreative = 1; $pk->dayCycleStopTime = -1; //TODO: implement this properly $pk->eduMode = 0; $pk->rainLevel = 0; //TODO: implement these properly $pk->lightningLevel = 0; $pk->commandsEnabled = 1; $pk->unknown = "UNKNOWN"; $pk->worldName = $this->server->getMotd(); $this->dataPacket($pk); $pk = new SetTimePacket(); $pk->time = $this->level->getTime(); $pk->started = $this->level->stopTime == false; $this->dataPacket($pk); $this->sendAttributes(true); $this->setNameTagVisible(true); $this->setNameTagAlwaysVisible(true); $this->server->getLogger()->info($this->getServer()->getLanguage()->translateString("pocketmine.player.logIn", [ TextFormat::AQUA . $this->username . TextFormat::WHITE, $this->ip, $this->port, TextFormat::GREEN . $this->randomClientId . TextFormat::WHITE, $this->id, $this->level->getName(), round($this->x, 4), round($this->y, 4), round($this->z, 4), TextFormat::YELLOW . $this->deviceModel . TextFormat::WHITE, ])); /*if($this->isOp()){ $this->setRemoveFormat(false); }*/ if($this->gamemode === Player::SPECTATOR){ $pk = new ContainerSetContentPacket(); $pk->windowid = ContainerSetContentPacket::SPECIAL_CREATIVE; $this->dataPacket($pk); }else{ $pk = new ContainerSetContentPacket(); $pk->windowid = ContainerSetContentPacket::SPECIAL_CREATIVE; $pk->slots = array_merge(Item::getCreativeItems(), $this->personalCreativeItems); $this->dataPacket($pk); } $this->sendCommandData(); $this->level->getWeather()->sendWeather($this); $this->forceMovement = $this->teleportPosition = $this->getPosition(); } public function getProtocol(){ return $this->protocol; } /** * Handles a Minecraft packet * TODO: Separate all of this in handlers * * WARNING: Do not use this, it's only for internal use. * Changes to this function won't be recorded on the version. * * @param DataPacket $packet */ public function handleDataPacket(DataPacket $packet){ if($this->connected === false){ return; } if($packet::NETWORK_ID === ProtocolInfo::BATCH_PACKET){ /** @var BatchPacket $packet */ $this->server->getNetwork()->processBatch($packet, $this); return; } $timings = Timings::getReceiveDataPacketTimings($packet); $timings->startTiming(); $this->server->getPluginManager()->callEvent($ev = new DataPacketReceiveEvent($this, $packet)); if($ev->isCancelled()){ $timings->stopTiming(); return; } switch($packet::NETWORK_ID){ case ProtocolInfo::ITEM_FRAME_DROP_ITEM_PACKET: $tile = $this->level->getTile($this->temporalVector->setComponents($packet->x, $packet->y, $packet->z)); if($tile instanceof ItemFrame){ $block = $this->level->getBlock($tile); $this->server->getPluginManager()->callEvent($ev = new BlockBreakEvent($this, $block, $this->getInventory()->getItemInHand(), true)); if(!$ev->isCancelled()){ $item = $tile->getItem(); $this->server->getPluginManager()->callEvent($ev = new ItemFrameDropItemEvent($this, $block, $tile, $item)); if(!$ev->isCancelled()){ if($item->getId() !== Item::AIR){ if((mt_rand(0, 10) / 10) < $tile->getItemDropChance()){ $this->level->dropItem($tile, $item); } $tile->setItem(Item::get(Item::AIR)); $tile->setItemRotation(0); } }else $tile->spawnTo($this); }else $tile->spawnTo($this); } break; case ProtocolInfo::REQUEST_CHUNK_RADIUS_PACKET: /*if($this->spawned){ $this->viewDistance = $packet->radius ** 2; }*/ $pk = new ChunkRadiusUpdatedPacket(); $pk->radius = ($this->server->chunkRadius != -1) ? $this->server->chunkRadius : $packet->radius; $this->dataPacket($pk); break; case ProtocolInfo::PLAYER_INPUT_PACKET: break; case ProtocolInfo::LOGIN_PACKET: if($this->loggedIn){ break; } $pk = new PlayStatusPacket(); $pk->status = PlayStatusPacket::LOGIN_SUCCESS; $this->dataPacket($pk); $this->username = TextFormat::clean($packet->username); $this->displayName = $this->username; $this->setNameTag($this->username); $this->iusername = strtolower($this->username); $this->protocol = $packet->protocol; $this->deviceModel = $packet->deviceModel; if($this->server->getConfigBoolean("online-mode", false) && $packet->identityPublicKey === null){ $this->kick("disconnectionScreen.notAuthenticated", false); break; } if(count($this->server->getOnlinePlayers()) >= $this->server->getMaxPlayers() and $this->kick("disconnectionScreen.serverFull", false)){ break; } if($packet->protocol !== ProtocolInfo::CURRENT_PROTOCOL){ if($packet->protocol < ProtocolInfo::CURRENT_PROTOCOL){ $message = "disconnectionScreen.outdatedClient"; $pk = new PlayStatusPacket(); $pk->status = PlayStatusPacket::LOGIN_FAILED_CLIENT; $this->directDataPacket($pk); }else{ $message = "disconnectionScreen.outdatedServer"; $pk = new PlayStatusPacket(); $pk->status = PlayStatusPacket::LOGIN_FAILED_SERVER; $this->directDataPacket($pk); } $this->close("", $message, false); break; } $this->randomClientId = $packet->clientId; $this->uuid = UUID::fromString($packet->clientUUID); $this->rawUUID = $this->uuid->toBinary(); $valid = true; $len = strlen($packet->username); if($len > 16 or $len < 3){ $valid = false; } for($i = 0; $i < $len and $valid; ++$i){ $c = ord($packet->username{$i}); if(($c >= ord("a") and $c <= ord("z")) or ($c >= ord("A") and $c <= ord("Z")) or ($c >= ord("0") and $c <= ord("9")) or $c === ord("_")){ continue; } $valid = false; break; } if(!$valid or $this->iusername === "rcon" or $this->iusername === "console"){ $this->close("", "disconnectionScreen.invalidName"); break; } if((strlen($packet->skin) != 64 * 64 * 4) and (strlen($packet->skin) != 64 * 32 * 4)){ $this->close("", "disconnectionScreen.invalidSkin"); break; } $this->setSkin($packet->skin, $packet->skinId); $this->server->getPluginManager()->callEvent($ev = new PlayerPreLoginEvent($this, "Plugin reason")); if($ev->isCancelled()){ $this->close("", $ev->getKickMessage()); break; } if($this->isConnected()){ $this->onPlayerPreLogin(); } break; case ProtocolInfo::MOVE_PLAYER_PACKET: if($this->linkedEntity instanceof Entity){ $entity = $this->linkedEntity; if($entity instanceof Boat){ $entity->setPosition($this->temporalVector->setComponents($packet->x, $packet->y - 0.3, $packet->z)); } /*if($entity instanceof Minecart){ $entity->isFreeMoving = true; $entity->motionX = -sin($packet->yaw / 180 * M_PI); $entity->motionZ = cos($packet->yaw / 180 * M_PI); }*/ } $newPos = new Vector3($packet->x, $packet->y - $this->getEyeHeight(), $packet->z); if($newPos->distanceSquared($this) < 0.01 and ($packet->yaw % 360) === $this->yaw and ($packet->pitch % 360) === $this->pitch){ //player hasn't moved, just client spamming packets break; } $revert = false; if(!$this->isAlive() or $this->spawned !== true){ $revert = true; $this->forceMovement = new Vector3($this->x, $this->y, $this->z); } if($this->forceMovement instanceof Vector3 and (($dist = $newPos->distanceSquared($this->forceMovement)) > 0.1 or $revert)){ $this->sendPosition($this->forceMovement, $packet->yaw, $packet->pitch, MovePlayerPacket::MODE_RESET); }else{ $packet->yaw %= 360; $packet->pitch %= 360; if($packet->yaw < 0){ $packet->yaw += 360; } $this->setRotation($packet->yaw, $packet->pitch); $this->newPosition = $newPos; $this->forceMovement = null; } break; case ProtocolInfo::ADVENTURE_SETTINGS_PACKET: //TODO: player abilities, check for other changes if($packet->isFlying and !$this->allowFlight){ $this->kick("Flying is not enabled on this server"); break; }else{ $this->server->getPluginManager()->callEvent($ev = new PlayerToggleFlightEvent($this, $packet->isFlying)); if($ev->isCancelled()){ $this->sendSettings(); }else{ $this->flying = $ev->isFlying(); } break; } break; case ProtocolInfo::MOB_EQUIPMENT_PACKET: if($this->spawned === false or !$this->isAlive()){ break; } /** * Handle hotbar slot remapping * This is the only time and place when hotbar mapping should ever be changed. * Changing hotbar slot mapping at will has been deprecated because it causes far too many * issues with Windows 10 Edition Beta. */ $this->inventory->setHeldItemIndex($packet->selectedSlot, false, $packet->slot); $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); break; case ProtocolInfo::USE_ITEM_PACKET: if($this->spawned === false or !$this->isAlive() or $this->blocked){ break; } $blockVector = new Vector3($packet->x, $packet->y, $packet->z); $this->craftingType = self::CRAFTING_SMALL; if($packet->face >= 0 and $packet->face <= 5){ //Use Block, place $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13) or $this->isSpectator()){ }elseif($this->isCreative()){ $item = $this->inventory->getItemInHand(); if($this->level->useItemOn($blockVector, $item, $packet->face, $packet->fx, $packet->fy, $packet->fz, $this) === true){ break; } }elseif(!$this->inventory->getItemInHand()->deepEquals($packet->item)){ $this->inventory->sendHeldItem($this); }else{ $item = $this->inventory->getItemInHand(); $oldItem = clone $item; if($this->level->useItemOn($blockVector, $item, $packet->face, $packet->fx, $packet->fy, $packet->fz, $this)){ if(!$item->deepEquals($oldItem) or $item->getCount() !== $oldItem->getCount()){ $this->inventory->setItemInHand($item); $this->inventory->sendHeldItem($this->hasSpawned); } break; } } $this->inventory->sendHeldItem($this); if($blockVector->distanceSquared($this) > 10000){ break; } $target = $this->level->getBlock($blockVector); $block = $target->getSide($packet->face); $this->level->sendBlocks([$this], [$target, $block], UpdateBlockPacket::FLAG_ALL_PRIORITY); break; }elseif($packet->face === -1){ $aimPos = (new Vector3($packet->x / 32768, $packet->y / 32768, $packet->z / 32768))->normalize(); if($this->isCreative()){ $item = $this->inventory->getItemInHand(); }elseif(!$this->inventory->getItemInHand()->deepEquals($packet->item)){ $this->inventory->sendHeldItem($this); break; }else{ $item = $this->inventory->getItemInHand(); } $ev = new PlayerInteractEvent($this, $item, $aimPos, $packet->face, PlayerInteractEvent::RIGHT_CLICK_AIR); $this->server->getPluginManager()->callEvent($ev); if($ev->isCancelled()){ $this->inventory->sendHeldItem($this); break; } if($item->getId() === Item::FISHING_ROD){ if($this->isFishing()){ $this->server->getPluginManager()->callEvent($ev = new PlayerUseFishingRodEvent($this, PlayerUseFishingRodEvent::ACTION_STOP_FISHING)); }else{ $this->server->getPluginManager()->callEvent($ev = new PlayerUseFishingRodEvent($this, PlayerUseFishingRodEvent::ACTION_START_FISHING)); } if(!$ev->isCancelled()){ if($this->isFishing()){ $this->setFishingHook(); }else{ $nbt = new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $this->x), new DoubleTag("", $this->y + $this->getEyeHeight()), new DoubleTag("", $this->z) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", -sin($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)), new DoubleTag("", -sin($this->pitch / 180 * M_PI)), new DoubleTag("", cos($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", $this->yaw), new FloatTag("", $this->pitch) ]) ]); $f = 0.6; $this->fishingHook = new FishingHook($this->chunk, $nbt, $this); $this->fishingHook->setMotion($this->fishingHook->getMotion()->multiply($f)); $this->fishingHook->spawnToAll(); } } }elseif($item->getId() === Item::SNOWBALL){ $nbt = new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $this->x), new DoubleTag("", $this->y + $this->getEyeHeight()), new DoubleTag("", $this->z) ]), "Motion" => new ListTag("Motion", [ /*new DoubleTag("", $aimPos->x), new DoubleTag("", $aimPos->y), new DoubleTag("", $aimPos->z)*/ //TODO: remove this because of a broken client new DoubleTag("", -sin($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)), new DoubleTag("", -sin($this->pitch / 180 * M_PI)), new DoubleTag("", cos($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", $this->yaw), new FloatTag("", $this->pitch) ]), ]); $f = 1.5; $snowball = Entity::createEntity("Snowball", $this->chunk, $nbt, $this); $snowball->setMotion($snowball->getMotion()->multiply($f)); if($this->isSurvival()){ $item->setCount($item->getCount() - 1); $this->inventory->setItemInHand($item->getCount() > 0 ? $item : Item::get(Item::AIR)); } if($snowball instanceof Projectile){ $this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($snowball)); if($projectileEv->isCancelled()){ $snowball->kill(); }else{ $snowball->spawnToAll(); $this->level->addSound(new LaunchSound($this), $this->getViewers()); } }else{ $snowball->spawnToAll(); } }elseif($item->getId() === Item::EGG){ $nbt = new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $this->x), new DoubleTag("", $this->y + $this->getEyeHeight()), new DoubleTag("", $this->z) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", -sin($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)), new DoubleTag("", -sin($this->pitch / 180 * M_PI)), new DoubleTag("", cos($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", $this->yaw), new FloatTag("", $this->pitch) ]), ]); $f = 1.5; $egg = Entity::createEntity("Egg", $this->chunk, $nbt, $this); $egg->setMotion($egg->getMotion()->multiply($f)); if($this->isSurvival()){ $item->setCount($item->getCount() - 1); $this->inventory->setItemInHand($item->getCount() > 0 ? $item : Item::get(Item::AIR)); } if($egg instanceof Projectile){ $this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($egg)); if($projectileEv->isCancelled()){ $egg->kill(); }else{ $egg->spawnToAll(); $this->level->addSound(new LaunchSound($this), $this->getViewers()); } }else{ $egg->spawnToAll(); } }elseif($item->getId() == Item::ENCHANTING_BOTTLE){ $nbt = new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $this->x), new DoubleTag("", $this->y + $this->getEyeHeight()), new DoubleTag("", $this->z) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", -sin($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)), new DoubleTag("", -sin($this->pitch / 180 * M_PI)), new DoubleTag("", cos($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", $this->yaw), new FloatTag("", $this->pitch) ]), ]); $f = 1.1; $thrownExpBottle = new ThrownExpBottle($this->chunk, $nbt, $this); $thrownExpBottle->setMotion($thrownExpBottle->getMotion()->multiply($f)); if($this->isSurvival()){ $item->setCount($item->getCount() - 1); $this->inventory->setItemInHand($item->getCount() > 0 ? $item : Item::get(Item::AIR)); } if($thrownExpBottle instanceof Projectile){ $this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($thrownExpBottle)); if($projectileEv->isCancelled()){ $thrownExpBottle->kill(); }else{ $thrownExpBottle->spawnToAll(); $this->level->addSound(new LaunchSound($this), $this->getViewers()); } }else{ $thrownExpBottle->spawnToAll(); } }elseif($item->getId() == Item::SPLASH_POTION and $this->server->allowSplashPotion){ $nbt = new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $this->x), new DoubleTag("", $this->y + $this->getEyeHeight()), new DoubleTag("", $this->z) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", -sin($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)), new DoubleTag("", -sin($this->pitch / 180 * M_PI)), new DoubleTag("", cos($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", $this->yaw), new FloatTag("", $this->pitch) ]), "PotionId" => new ShortTag("PotionId", $item->getDamage()), ]); $f = 1.1; $thrownPotion = new ThrownPotion($this->chunk, $nbt, $this); $thrownPotion->setMotion($thrownPotion->getMotion()->multiply($f)); if($this->isSurvival()){ $item->setCount($item->getCount() - 1); $this->inventory->setItemInHand($item->getCount() > 0 ? $item : Item::get(Item::AIR)); } if($thrownPotion instanceof Projectile){ $this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($thrownPotion)); if($projectileEv->isCancelled()){ $thrownPotion->kill(); }else{ $thrownPotion->spawnToAll(); $this->level->addSound(new LaunchSound($this), $this->getViewers()); } }else{ $thrownPotion->spawnToAll(); } }elseif($item->getId() === Item::ENDER_PEARL){ $nbt = new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $this->x), new DoubleTag("", $this->y + $this->getEyeHeight()), new DoubleTag("", $this->z) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", -sin($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)), new DoubleTag("", -sin($this->pitch / 180 * M_PI)), new DoubleTag("", cos($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", $this->yaw), new FloatTag("", $this->pitch) ]), ]); $f = 1.5; $enderpearl = Entity::createEntity("EnderPearl", $this->chunk, $nbt, $this); $enderpearl->setMotion($enderpearl->getMotion()->multiply($f)); $enderpearl->setSpawner($this); if($this->isSurvival()){ $item->setCount($item->getCount() - 1); $this->inventory->setItemInHand($item->getCount() > 0 ? $item : Item::get(Item::AIR)); } if($enderpearl instanceof Projectile){ $this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($enderpearl)); if($projectileEv->isCancelled()){ $enderpearl->close(); $this->teleport($enderpearl); }else{ $enderpearl->spawnToAll(); $this->level->addSound(new LaunchSound($this), $this->getViewers()); } }else{ $enderpearl->spawnToAll(); } } $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, true); $this->startAction = $this->server->getTick(); } break; case ProtocolInfo::PLAYER_ACTION_PACKET: if($this->spawned === false or $this->blocked === true or (!$this->isAlive() and $packet->action !== PlayerActionPacket::ACTION_SPAWN_SAME_DIMENSION and $packet->action !== PlayerActionPacket::ACTION_SPAWN_OVERWORLD)){ break; } $packet->eid = $this->id; $pos = new Vector3($packet->x, $packet->y, $packet->z); switch($packet->action){ case PlayerActionPacket::ACTION_START_BREAK: if($this->lastBreak !== PHP_INT_MAX or $pos->distanceSquared($this) > 10000){ break; } $target = $this->level->getBlock($pos); $ev = new PlayerInteractEvent($this, $this->inventory->getItemInHand(), $target, $packet->face, $target->getId() === 0 ? PlayerInteractEvent::LEFT_CLICK_AIR : PlayerInteractEvent::LEFT_CLICK_BLOCK); $this->getServer()->getPluginManager()->callEvent($ev); if(!$ev->isCancelled()){ $side = $target->getSide($packet->face); if($side instanceof Fire){ $side->getLevel()->setBlock($side, new Air()); break; } $this->lastBreak = microtime(true); }else{ $this->inventory->sendHeldItem($this); } break; case PlayerActionPacket::ACTION_ABORT_BREAK: $this->lastBreak = PHP_INT_MAX; break; case PlayerActionPacket::ACTION_STOP_BREAK: break; case PlayerActionPacket::ACTION_RELEASE_ITEM: if($this->startAction > -1 and $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION)){ if($this->inventory->getItemInHand()->getId() === Item::BOW){ $bow = $this->inventory->getItemInHand(); if($this->isSurvival() and !$this->inventory->contains(Item::get(Item::ARROW, -1))){ $this->inventory->sendContents($this); break; } $arrow = null; $index = $this->inventory->first(Item::get(Item::ARROW, -1)); if($index !== -1){ $arrow = $this->inventory->getItem($index); $arrow->setCount(1); }elseif($this->isCreative()){ $arrow = Item::get(Item::ARROW, 0, 1); }else{ $this->inventory->sendContents($this); break; } $nbt = new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $this->x), new DoubleTag("", $this->y + $this->getEyeHeight()), new DoubleTag("", $this->z) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", -sin($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)), new DoubleTag("", -sin($this->pitch / 180 * M_PI)), new DoubleTag("", cos($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", $this->yaw), new FloatTag("", $this->pitch) ]), "Fire" => new ShortTag("Fire", $this->isOnFire() ? 45 * 60 : 0), "Potion" => new ShortTag("Potion", $arrow->getDamage()) ]); $diff = ($this->server->getTick() - $this->startAction); $p = $diff / 20; $f = min((($p ** 2) + $p * 2) / 3, 1) * 2; $ev = new EntityShootBowEvent($this, $bow, Entity::createEntity("Arrow", $this->chunk, $nbt, $this, $f == 2 ? true : false), $f); if($f < 0.1 or $diff < 5){ $ev->setCancelled(); } $this->server->getPluginManager()->callEvent($ev); if($ev->isCancelled()){ $ev->getProjectile()->kill(); $this->inventory->sendContents($this); }else{ $ev->getProjectile()->setMotion($ev->getProjectile()->getMotion()->multiply($ev->getForce())); if($this->isSurvival()){ $this->inventory->removeItem(Item::get(Item::ARROW, $arrow->getDamage(), 1)); $bow->setDamage($bow->getDamage() + 1); if($bow->getDamage() >= 385){ $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 0)); }else{ $this->inventory->setItemInHand($bow); } } if($ev->getProjectile() instanceof Projectile){ $this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($ev->getProjectile())); if($projectileEv->isCancelled()){ $ev->getProjectile()->kill(); }else{ $ev->getProjectile()->spawnToAll(); $this->level->addSound(new LaunchSound($this), $this->getViewers()); } }else{ $ev->getProjectile()->spawnToAll(); } } } }elseif($this->inventory->getItemInHand()->getId() === Item::BUCKET and $this->inventory->getItemInHand()->getDamage() === 1){ //Milk! $this->server->getPluginManager()->callEvent($ev = new PlayerItemConsumeEvent($this, $this->inventory->getItemInHand())); if($ev->isCancelled()){ $this->inventory->sendContents($this); break; } $pk = new EntityEventPacket(); $pk->eid = $this->getId(); $pk->event = EntityEventPacket::USE_ITEM; //$pk; $this->dataPacket($pk); $this->server->broadcastPacket($this->getViewers(), $pk); if($this->isSurvival()){ $slot = $this->inventory->getItemInHand(); --$slot->count; $this->inventory->setItemInHand($slot); $this->inventory->addItem(Item::get(Item::BUCKET, 0, 1)); } $this->removeAllEffects(); }else{ $this->inventory->sendContents($this); } break; case PlayerActionPacket::ACTION_STOP_SLEEPING: $this->stopSleep(); break; case PlayerActionPacket::ACTION_SPAWN_SAME_DIMENSION: case PlayerActionPacket::ACTION_SPAWN_OVERWORLD: if($this->isAlive() or !$this->isOnline()){ break; } if($this->server->isHardcore()){ $this->setBanned(true); break; } $this->craftingType = self::CRAFTING_SMALL; if($this->server->netherEnabled){ if($this->level === $this->server->getLevelByName($this->server->netherName)){ $this->teleport($pos = $this->server->getDefaultLevel()->getSafeSpawn()); } } $this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $this->getSpawn())); $this->teleport($ev->getRespawnPosition()); $this->setSprinting(false); $this->setSneaking(false); $this->setGliding(false); $this->extinguish(); $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, 400); $this->deadTicks = 0; $this->noDamageTicks = 60; $this->removeAllEffects(); $this->setHealth($this->getMaxHealth()); $this->setFood(20); $this->starvationTick = 0; $this->foodTick = 0; $this->foodUsageTime = 0; $this->sendData($this); $this->sendSettings(); $this->inventory->sendContents($this); $this->inventory->sendArmorContents($this); $this->blocked = false; $this->spawnToAll(); $this->scheduleUpdate(); break; case PlayerActionPacket::ACTION_JUMP: break 2; case PlayerActionPacket::ACTION_START_SPRINT: $ev = new PlayerToggleSprintEvent($this, true); $this->server->getPluginManager()->callEvent($ev); if($ev->isCancelled()){ $this->sendData($this); }else{ $this->setSprinting(true); } break 2; case PlayerActionPacket::ACTION_STOP_SPRINT: $ev = new PlayerToggleSprintEvent($this, false); $this->server->getPluginManager()->callEvent($ev); if($ev->isCancelled()){ $this->sendData($this); }else{ $this->setSprinting(false); } break 2; case PlayerActionPacket::ACTION_START_SNEAK: $ev = new PlayerToggleSneakEvent($this, true); $this->server->getPluginManager()->callEvent($ev); if($ev->isCancelled()){ $this->sendData($this); }else{ $this->setSneaking(true); } break 2; case PlayerActionPacket::ACTION_STOP_SNEAK: $ev = new PlayerToggleSneakEvent($this, false); $this->server->getPluginManager()->callEvent($ev); if($ev->isCancelled()){ $this->sendData($this); }else{ $this->setSneaking(false); } break 2; case PlayerActionPacket::ACTION_START_GLIDE: $ev = new PlayerToggleGlideEvent($this, true); $this->server->getPluginManager()->callEvent($ev); if($ev->isCancelled()){ $this->sendData($this); }else{ $this->setGliding(true); } break 2; case PlayerActionPacket::ACTION_STOP_GLIDE: $ev = new PlayerToggleGlideEvent($this, false); $this->server->getPluginManager()->callEvent($ev); if($ev->isCancelled()){ $this->sendData($this); }else{ $this->setGliding(false); } break 2; default: assert(false, "Unhandled player action " . $packet->action . " from " . $this->getName()); } $this->startAction = -1; $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); break; case ProtocolInfo::REMOVE_BLOCK_PACKET: if($this->spawned === false or $this->blocked === true or !$this->isAlive()){ break; } $this->craftingType = self::CRAFTING_SMALL; $vector = new Vector3($packet->x, $packet->y, $packet->z); $item = $this->inventory->getItemInHand(); $oldItem = clone $item; if($this->canInteract($vector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 6) and $this->level->useBreakOn($vector, $item, $this, $this->server->destroyBlockParticle)){ if($this->isSurvival()){ if(!$item->equals($oldItem) or $item->getCount() !== $oldItem->getCount()){ $this->inventory->setItemInHand($item); $this->inventory->sendHeldItem($this); } $this->exhaust(0.025, PlayerExhaustEvent::CAUSE_MINING); } break; } $this->inventory->sendContents($this); $target = $this->level->getBlock($vector); $tile = $this->level->getTile($vector); $this->level->sendBlocks([$this], [$target], UpdateBlockPacket::FLAG_ALL_PRIORITY); $this->inventory->sendHeldItem($this); if($tile instanceof Spawnable){ $tile->spawnTo($this); } break; case ProtocolInfo::MOB_ARMOR_EQUIPMENT_PACKET: //This packet is ignored. Armour changes are also sent by ContainerSetSlotPackets, and are handled there instead. break; case ProtocolInfo::INTERACT_PACKET: if($this->spawned === false or !$this->isAlive() or $this->blocked){ break; } $this->craftingType = self::CRAFTING_SMALL; $target = $this->level->getEntity($packet->target); $cancelled = false; if($target instanceof Player and $this->server->getConfigBoolean("pvp", true) === false){ $cancelled = true; } if($target instanceof Boat or ($target instanceof Minecart and $target->getType() == Minecart::TYPE_NORMAL)){ if($packet->action === InteractPacket::ACTION_RIGHT_CLICK){ $this->linkEntity($target); }elseif($packet->action === InteractPacket::ACTION_LEFT_CLICK){ if($this->linkedEntity == $target){ $target->setLinked(0, $this); } $target->close(); }elseif($packet->action === InteractPacket::ACTION_LEAVE_VEHICLE){ $this->setLinked(0, $target); } return; } if($packet->action === InteractPacket::ACTION_RIGHT_CLICK){ if($target instanceof Animal and $this->getInventory()->getItemInHand()){ //TODO: Feed } break; }elseif($packet->action === InteractPacket::ACTION_MOUSEOVER){ break; } if($target instanceof Entity and $this->getGamemode() !== Player::VIEW and $this->isAlive() and $target->isAlive()){ if($target instanceof DroppedItem or $target instanceof Arrow){ $this->kick("Attempting to attack an invalid entity"); $this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidEntity", [$this->getName()])); break; } $item = $this->inventory->getItemInHand(); $damage = [ EntityDamageEvent::MODIFIER_BASE => $item->getModifyAttackDamage($target), ]; if(!$this->canInteract($target, 8)){ $cancelled = true; }elseif($target instanceof Player){ if(($target->getGamemode() & 0x01) > 0){ break; }elseif($this->server->getConfigBoolean("pvp") !== true or $this->server->getDifficulty() === 0){ $cancelled = true; } } $ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $damage, 0.4 + $item->getEnchantmentLevel(Enchantment::TYPE_WEAPON_KNOCKBACK) * 0.15); if($cancelled){ $ev->setCancelled(); } if($target->attack($ev->getFinalDamage(), $ev) === true){ $fireAspectL = $item->getEnchantmentLevel(Enchantment::TYPE_WEAPON_FIRE_ASPECT); if($fireAspectL > 0){ $fireEv = new EntityCombustByEntityEvent($this, $target, $fireAspectL * 4, $ev->getFireProtectL()); Server::getInstance()->getPluginManager()->callEvent($fireEv); if(!$fireEv->isCancelled()){ $target->setOnFire($fireEv->getDuration()); } } //Thorns if($this->isSurvival()){ $ev->createThornsDamage(); if($ev->getThornsDamage() > 0){ $thornsEvent = new EntityDamageByEntityEvent($target, $this, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $ev->getThornsDamage(), 0); if(!$thornsEvent->isCancelled()){ if($this->attack($thornsEvent->getFinalDamage(), $thornsEvent) === true){ $thornsEvent->useArmors(); $ev->setThornsArmorUse(); } } } } $ev->useArmors(); } if($ev->isCancelled()){ if($item->isTool() and $this->isSurvival()){ $this->inventory->sendContents($this); } break; } if($this->isSurvival()){ if($item->isTool()){ if($item->useOn($target) and $item->getDamage() >= $item->getMaxDurability()){ $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1)); }else{ $this->inventory->setItemInHand($item); } } $this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK); } } break; case ProtocolInfo::ANIMATE_PACKET: if($this->spawned === false or !$this->isAlive()){ break; } $this->server->getPluginManager()->callEvent($ev = new PlayerAnimationEvent($this, $packet->action)); if($ev->isCancelled()){ break; } $pk = new AnimatePacket(); $pk->eid = $this->getId(); $pk->action = $ev->getAnimationType(); $this->server->broadcastPacket($this->getViewers(), $pk); break; case ProtocolInfo::SET_HEALTH_PACKET: //Not used break; case ProtocolInfo::ENTITY_EVENT_PACKET: if($this->spawned === false or $this->blocked === true or !$this->isAlive()){ break; } $this->craftingType = self::CRAFTING_SMALL; $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); //TODO: check if this should be true switch($packet->event){ case EntityEventPacket::USE_ITEM: //Eating $slot = $this->inventory->getItemInHand(); if($slot->canBeConsumed()){ $ev = new PlayerItemConsumeEvent($this, $slot); if(!$slot->canBeConsumedBy($this)){ $ev->setCancelled(); } $this->server->getPluginManager()->callEvent($ev); if(!$ev->isCancelled()){ $slot->onConsume($this); }else{ $this->inventory->sendContents($this); } } break; } break; case ProtocolInfo::DROP_ITEM_PACKET: if($this->spawned === false or $this->blocked === true or !$this->isAlive()){ break; } if($packet->item->getId() === Item::AIR){ /** * This is so stupid it's unreal. * Windows 10 Edition Beta drops the contents of the crafting grid when the inventory closes - including air. */ break; } if(($this->isCreative() and $this->server->limitedCreative)){ break; } $this->getTransactionQueue()->addTransaction(new DropItemTransaction($packet->item)); break; case ProtocolInfo::COMMAND_STEP_PACKET: if($this->spawned === false or !$this->isAlive()){ break; } $this->craftingType = 0; $commandText = $packet->command; if($packet->args !== null){ foreach($packet->args as $arg){ //command ordering will be an issue $commandText .= " " . $arg; } } $this->server->getPluginManager()->callEvent($ev = new PlayerCommandPreprocessEvent($this, "/" . $commandText)); if($ev->isCancelled()){ break; } Timings::$playerCommandTimer->startTiming(); $this->server->dispatchCommand($ev->getPlayer(), substr($ev->getMessage(), 1)); Timings::$playerCommandTimer->stopTiming(); break; case ProtocolInfo::TEXT_PACKET: if($this->spawned === false or !$this->isAlive()){ break; } $this->craftingType = self::CRAFTING_SMALL; if($packet->type === TextPacket::TYPE_CHAT){ $packet->message = TextFormat::clean($packet->message, $this->removeFormat); foreach(explode("\n", $packet->message) as $message){ if(trim($message) != "" and strlen($message) <= 255 and $this->messageCounter-- > 0){ if(substr($message, 0, 2) === "./"){ //Command (./ = fast hack for old plugins post 0.16) $message = substr($message, 1); } $ev = new PlayerCommandPreprocessEvent($this, $message); if(mb_strlen($ev->getMessage(), "UTF-8") > 320){ $ev->setCancelled(); } $this->server->getPluginManager()->callEvent($ev); if($ev->isCancelled()){ break; } if(substr($ev->getMessage(), 0, 1) === "/"){ Timings::$playerCommandTimer->startTiming(); $this->server->dispatchCommand($ev->getPlayer(), substr($ev->getMessage(), 1)); Timings::$playerCommandTimer->stopTiming(); }else{ $this->server->getPluginManager()->callEvent($ev = new PlayerChatEvent($this, $ev->getMessage())); if(!$ev->isCancelled()){ $this->server->broadcastMessage($this->getServer()->getLanguage()->translateString($ev->getFormat(), [ $ev->getPlayer()->getDisplayName(), $ev->getMessage() ]), $ev->getRecipients()); } } } } } break; case ProtocolInfo::CONTAINER_CLOSE_PACKET: if($this->spawned === false or $packet->windowid === 0){ break; } $this->craftingType = self::CRAFTING_SMALL; if(isset($this->windowIndex[$packet->windowid])){ $this->server->getPluginManager()->callEvent(new InventoryCloseEvent($this->windowIndex[$packet->windowid], $this)); $this->removeWindow($this->windowIndex[$packet->windowid]); } /** * Drop anything still left in the crafting inventory * This will usually never be needed since Windows 10 clients will send DropItemPackets * which will cause this to happen anyway, but this is here for when transactions * fail and items end up stuck in the crafting inventory. */ foreach($this->getFloatingInventory()->getContents() as $item){ $this->getTransactionQueue()->addTransaction(new DropItemTransaction($item)); } break; case ProtocolInfo::CRAFTING_EVENT_PACKET: if($this->spawned === false or !$this->isAlive()){ break; } /** * For some annoying reason, anvils send window ID 255 when crafting with them instead of the _actual_ anvil window ID * The result of this is anvils immediately closing when used. This is highly unusual, especially since the * container set slot packets send the correct window ID, but... eh */ /*elseif(!isset($this->windowIndex[$packet->windowId])){ $this->inventory->sendContents($this); $pk = new ContainerClosePacket(); $pk->windowid = $packet->windowId; $this->dataPacket($pk); break; }*/ $recipe = $this->server->getCraftingManager()->getRecipe($packet->id); if($this->craftingType === self::CRAFTING_ANVIL){ $anvilInventory = $this->windowIndex[$packet->windowId] ?? null; if($anvilInventory === null){ foreach($this->windowIndex as $window){ if($window instanceof AnvilInventory){ $anvilInventory = $window; break; } } if($anvilInventory === null){ //If it's _still_ null, then the player doesn't have a valid anvil window, cannot proceed. $this->getServer()->getLogger()->debug("Couldn't find an anvil window for ".$this->getName().", exiting"); $this->inventory->sendContents($this); break; } } if($recipe === null){ //Item renamed if(!$anvilInventory->onRename($this, $packet->output[0])){ $this->getServer()->getLogger()->debug($this->getName()." failed to rename an item in an anvil"); $this->inventory->sendContents($this); } }else{ //TODO: Anvil crafting recipes } break; }elseif(($recipe instanceof BigShapelessRecipe or $recipe instanceof BigShapedRecipe) and $this->craftingType === 0){ $this->server->getLogger()->debug("Received big crafting recipe from ".$this->getName()." with no crafting table open"); $this->inventory->sendContents($this); break; }elseif($recipe === null){ $this->server->getLogger()->debug("Null (unknown) crafting recipe received from ".$this->getName()." for ".$packet->output[0]); $this->inventory->sendContents($this); break; } $canCraft = true; if(count($packet->input) === 0){ /* If the packet "input" field is empty this needs to be handled differently. * "input" is used to tell the server what items to remove from the client's inventory * Because crafting takes the materials in the crafting grid, nothing needs to be taken from the inventory * Instead, we take the materials from the crafting inventory * To know what materials we need to take, we have to guess the crafting recipe used based on the * output item and the materials stored in the crafting items * The reason we have to guess is because Win10 sometimes sends a different recipe UUID * say, if you put the wood for a door in the right hand side of the crafting grid instead of the left * it will send the recipe UUID for a wooden pressure plate. Unknown currently whether this is a client * bug or if there is something wrong with the way the server handles recipes. * TODO: Remove recipe correction and fix desktop crafting recipes properly. * In fact, TODO: Rewrite crafting entirely. */ $possibleRecipes = $this->server->getCraftingManager()->getRecipesByResult($packet->output[0]); if(!$packet->output[0]->equals($recipe->getResult())){ $this->server->getLogger()->debug("Mismatched desktop recipe received from player ".$this->getName().", expected ".$recipe->getResult().", got ".$packet->output[0]); } $recipe = null; foreach($possibleRecipes as $r){ /* Check the ingredient list and see if it matches the ingredients we've put into the crafting grid * As soon as we find a recipe that we have all the ingredients for, take it and run with it. */ //Make a copy of the floating inventory that we can make changes to. $floatingInventory = clone $this->floatingInventory; $ingredients = $r->getIngredientList(); //Check we have all the necessary ingredients. foreach($ingredients as $ingredient){ if(!$floatingInventory->contains($ingredient)){ //We're short on ingredients, try the next recipe $canCraft = false; break; } //This will only be reached if we have the item to take away. $floatingInventory->removeItem($ingredient); } if($canCraft){ //Found a recipe that works, take it and run with it. $recipe = $r; break; } } if($recipe !== null){ $this->server->getPluginManager()->callEvent($ev = new CraftItemEvent($this, $ingredients, $recipe)); if($ev->isCancelled()){ $this->inventory->sendContents($this); break; } $this->floatingInventory = $floatingInventory; //Set player crafting inv to the idea one created in this process $this->floatingInventory->addItem(clone $recipe->getResult()); //Add the result to our picture of the crafting inventory }else{ $this->server->getLogger()->debug("Unmatched desktop crafting recipe " . $packet->id . " from player " . $this->getName()); $this->inventory->sendContents($this); break; } }else{ if($recipe instanceof ShapedRecipe){ for($x = 0; $x < 3 and $canCraft; ++$x){ for($y = 0; $y < 3; ++$y){ $item = $packet->input[$y * 3 + $x]; $ingredient = $recipe->getIngredient($x, $y); if($item->getCount() > 0 and $item->getId() > 0){ if($ingredient == null){ $canCraft = false; break; } if($ingredient->getId() != 0 and !$ingredient->equals($item, !$ingredient->hasAnyDamageValue(), $ingredient->hasCompoundTag())){ $canCraft = false; break; } }elseif($ingredient !== null and $item->getId() !== 0){ $canCraft = false; break; } } } }elseif($recipe instanceof ShapelessRecipe){ $needed = $recipe->getIngredientList(); for($x = 0; $x < 3 and $canCraft; ++$x){ for($y = 0; $y < 3; ++$y){ $item = clone $packet->input[$y * 3 + $x]; foreach($needed as $k => $n){ if($n->deepEquals($item, !$n->hasAnyDamageValue(), $n->hasCompoundTag())){ $remove = min($n->getCount(), $item->getCount()); $n->setCount($n->getCount() - $remove); $item->setCount($item->getCount() - $remove); if($n->getCount() === 0){ unset($needed[$k]); } } } if($item->getCount() > 0){ $canCraft = false; break; } } } if(count($needed) > 0){ $canCraft = false; } }else{ $canCraft = false; } /** @var Item[] $ingredients */ $ingredients = $packet->input; $result = $packet->output[0]; if(!$canCraft or !$recipe->getResult()->deepEquals($result)){ $this->server->getLogger()->debug("Unmatched recipe " . $recipe->getId() . " from player " . $this->getName() . ": expected " . $recipe->getResult() . ", got " . $result . ", using: " . implode(", ", $ingredients)); $this->inventory->sendContents($this); break; } $used = array_fill(0, $this->inventory->getSize(), 0); foreach($ingredients as $ingredient){ $slot = -1; foreach($this->inventory->getContents() as $index => $item){ if($ingredient->getId() !== 0 and $ingredient->deepEquals($item, !$ingredient->hasAnyDamageValue(), $ingredient->hasCompoundTag()) and ($item->getCount() - $used[$index]) >= 1){ $slot = $index; $used[$index]++; break; } } if($ingredient->getId() !== 0 and $slot === -1){ $canCraft = false; break; } } if(!$canCraft){ $this->server->getLogger()->debug("Unmatched recipe " . $recipe->getId() . " from player " . $this->getName() . ": client does not have enough items, using: " . implode(", ", $ingredients)); $this->inventory->sendContents($this); break; } $this->server->getPluginManager()->callEvent($ev = new CraftItemEvent($this, $ingredients, $recipe)); if($ev->isCancelled()){ $this->inventory->sendContents($this); break; } foreach($used as $slot => $count){ if($count === 0){ continue; } $item = $this->inventory->getItem($slot); if($item->getCount() > $count){ $newItem = clone $item; $newItem->setCount($item->getCount() - $count); }else{ $newItem = Item::get(Item::AIR, 0, 0); } $this->inventory->setItem($slot, $newItem); } $extraItem = $this->inventory->addItem($recipe->getResult()); if(count($extraItem) > 0 and !$this->isCreative()){ //Could not add all the items to our inventory (not enough space) foreach($extraItem as $item){ $this->level->dropItem($this, $item); } } } switch($recipe->getResult()->getId()){ case Item::WORKBENCH: $this->awardAchievement("buildWorkBench"); break; case Item::WOODEN_PICKAXE: $this->awardAchievement("buildPickaxe"); break; case Item::FURNACE: $this->awardAchievement("buildFurnace"); break; case Item::WOODEN_HOE: $this->awardAchievement("buildHoe"); break; case Item::BREAD: $this->awardAchievement("makeBread"); break; case Item::CAKE: //TODO: detect complex recipes like cake that leave remains $this->awardAchievement("bakeCake"); $this->inventory->addItem(Item::get(Item::BUCKET, 0, 3)); break; case Item::STONE_PICKAXE: case Item::GOLD_PICKAXE: case Item::IRON_PICKAXE: case Item::DIAMOND_PICKAXE: $this->awardAchievement("buildBetterPickaxe"); break; case Item::WOODEN_SWORD: $this->awardAchievement("buildSword"); break; case Item::DIAMOND: $this->awardAchievement("diamond"); break; } break; case ProtocolInfo::CONTAINER_SET_SLOT_PACKET: if($this->spawned === false or $this->blocked === true or !$this->isAlive()){ break; } if($packet->slot < 0){ break; } if($packet->windowid === 0){ //Our inventory if($packet->slot >= $this->inventory->getSize()){ break; } $transaction = new BaseTransaction($this->inventory, $packet->slot, $packet->item); }elseif($packet->windowid === ContainerSetContentPacket::SPECIAL_ARMOR){ //Our armor if($packet->slot >= 4){ break; } $transaction = new BaseTransaction($this->inventory, $packet->slot + $this->inventory->getSize(), $packet->item); }elseif(isset($this->windowIndex[$packet->windowid])){ //Transaction for non-player-inventory window, such as anvil, chest, etc. $inv = $this->windowIndex[$packet->windowid]; $achievements = []; if($inv instanceof FurnaceInventory and $inv->getItem($packet->slot)->getId() === Item::IRON_INGOT and $packet->slot === FurnaceInventory::RESULT){ $achievements[] = "acquireIron"; }elseif($inv instanceof EnchantInventory and $packet->item->hasEnchantments()){ $inv->onEnchant($this, $inv->getItem($packet->slot), $packet->item); } $transaction = new BaseTransaction($inv, $packet->slot, $packet->item, $achievements); }else{ //Client sent a transaction for a window which the server doesn't think they have open break; } $this->getTransactionQueue()->addTransaction($transaction); break; case ProtocolInfo::BLOCK_ENTITY_DATA_PACKET: if($this->spawned === false or $this->blocked === true or !$this->isAlive()){ break; } $this->craftingType = self::CRAFTING_SMALL; $pos = new Vector3($packet->x, $packet->y, $packet->z); if($pos->distanceSquared($this) > 10000){ break; } $t = $this->level->getTile($pos); if($t instanceof Spawnable){ $nbt = new NBT(NBT::LITTLE_ENDIAN); $nbt->read($packet->namedtag, false, true); $nbt = $nbt->getData(); if(!$t->updateCompoundTag($nbt, $this)){ $t->spawnTo($this); } } else if ($t instanceof Sign){ $nbt = new NBT(NBT::LITTLE_ENDIAN); $nbt->read($packet->namedtag); $nbt = $nbt->getData(); if($nbt["id"] !== Tile::SIGN){ $t->spawnTo($this); }else{ $ev = new SignChangeEvent($t->getBlock(), $this, [ TextFormat::clean($nbt["Text1"], $this->removeFormat), TextFormat::clean($nbt["Text2"], $this->removeFormat), TextFormat::clean($nbt["Text3"], $this->removeFormat), TextFormat::clean($nbt["Text4"], $this->removeFormat) ]); if(!isset($t->namedtag->Creator) or $t->namedtag["Creator"] !== $this->getRawUniqueId()){ $ev->setCancelled(); } $this->server->getPluginManager()->callEvent($ev); if(!$ev->isCancelled()){ $t->setText($ev->getLine(0), $ev->getLine(1), $ev->getLine(2), $ev->getLine(3)); }else{ $t->spawnTo($this); } } } break; case ProtocolInfo::SET_PLAYER_GAME_TYPE_PACKET: if($packet->gamemode !== ($this->gamemode & 0x01)){ //GUI gamemode change, set it back to original for now (only possible through client bug or hack with current allowed client permissions) $pk = new SetPlayerGameTypePacket(); $pk->gamemode = $this->gamemode & 0x01; $this->dataPacket($pk); $this->sendSettings(); } break; case ProtocolInfo::ITEM_FRAME_DROP_ITEM_PACKET: if($this->spawned === false or $this->blocked === true or !$this->isAlive()){ break; } if(($tile = $this->level->getTile($this->temporalVector->setComponents($packet->x, $packet->y, $packet->z))) instanceof ItemFrame){ if(!$tile->getItem()->equals($packet->item) and !$this->isCreative(true)){ $tile->spawnTo($this); break; } if(lcg_value() <= $tile->getItemDropChance() and $packet->item->getId() !== Item::AIR){ $this->level->dropItem($tile->getBlock(), $packet->item); //Use the packet item to handle creative drops correctly } $tile->setItem(null); $tile->setItemRotation(0); } break; default: break; } $timings->stopTiming(); } /** * Kicks a player from the server * * @param string $reason * @param bool $isAdmin * * @return bool */ public function kick($reason = "", $isAdmin = true){ $this->server->getPluginManager()->callEvent($ev = new PlayerKickEvent($this, $reason, $this->getLeaveMessage())); if(!$ev->isCancelled()){ if($isAdmin){ $message = "Kicked by admin." . ($reason !== "" ? " Reason: " . $reason : ""); }else{ if($reason === ""){ $message = "disconnectionScreen.noReason"; }else{ $message = $reason; } } $this->close($ev->getQuitMessage(), $message); return true; } return false; } /** * @param Item $item * * Drops the specified item in front of the player. */ public function dropItem(Item $item){ if($this->spawned === false or $this->blocked === true or !$this->isAlive()){ return; } if(($this->isCreative() and $this->server->limitedCreative) or $this->isSpectator()){ //Ignore for limited creative return; } if($item->getId() === Item::AIR or $item->getCount() < 1){ //Ignore dropping air or items with bad counts return; } $ev = new PlayerDropItemEvent($this, $item); $this->server->getPluginManager()->callEvent($ev); if($ev->isCancelled()){ $this->getFloatingInventory()->removeItem($item); $this->getInventory()->addItem($item); return; } $motion = $this->getDirectionVector()->multiply(0.4); $this->level->dropItem($this->add(0, 1.3, 0), $item, $motion, 40); $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); } /** * Sends a direct chat message to a player * * @param string|TextContainer $message * @return bool */ public function sendMessage($message){ if($message instanceof TextContainer){ if($message instanceof TranslationContainer){ $this->sendTranslation($message->getText(), $message->getParameters()); return false; } $message = $message->getText(); } $mes = explode("\n", $this->server->getLanguage()->translateString($message)); foreach($mes as $m){ if($m !== ""){ $this->server->getPluginManager()->callEvent($ev = new PlayerTextPreSendEvent($this, $m, PlayerTextPreSendEvent::MESSAGE)); if(!$ev->isCancelled()){ $pk = new TextPacket(); $pk->type = TextPacket::TYPE_RAW; $pk->message = $ev->getMessage(); $this->dataPacket($pk); } } } return true; } public function sendTranslation($message, array $parameters = []){ $pk = new TextPacket(); if(!$this->server->isLanguageForced()){ $pk->type = TextPacket::TYPE_TRANSLATION; $pk->message = $this->server->getLanguage()->translateString($message, $parameters, "pocketmine."); foreach($parameters as $i => $p){ $parameters[$i] = $this->server->getLanguage()->translateString($p, $parameters, "pocketmine."); } $pk->parameters = $parameters; }else{ $pk->type = TextPacket::TYPE_RAW; $pk->message = $this->server->getLanguage()->translateString($message, $parameters); } $ev = new PlayerTextPreSendEvent($this, $pk->message, PlayerTextPreSendEvent::TRANSLATED_MESSAGE); $this->server->getPluginManager()->callEvent($ev); if(!$ev->isCancelled()){ $this->dataPacket($pk); return true; } return false; } public function sendPopup($message, $subtitle = ""){ $ev = new PlayerTextPreSendEvent($this, $message, PlayerTextPreSendEvent::POPUP); $this->server->getPluginManager()->callEvent($ev); if(!$ev->isCancelled()){ $pk = new TextPacket(); $pk->type = TextPacket::TYPE_POPUP; $pk->source = $ev->getMessage(); $pk->message = $subtitle; $this->dataPacket($pk); return true; } return false; } /** * @param $message * @return bool */ public function sendTip($message){ $ev = new PlayerTextPreSendEvent($this, $message, PlayerTextPreSendEvent::TIP); $this->server->getPluginManager()->callEvent($ev); if(!$ev->isCancelled()){ $pk = new TextPacket(); $pk->type = TextPacket::TYPE_TIP; $pk->message = $ev->getMessage(); $this->dataPacket($pk); return true; } return false; } /** * Note for plugin developers: use kick() with the isAdmin * flag set to kick without the "Kicked by admin" part instead of this method. * * @param string $message Message to be broadcasted * @param string $reason Reason showed in console * @param bool $notify */ public final function close($message = "", $reason = "generic reason", $notify = true){ if($this->connected and !$this->closed){ if($notify and strlen((string) $reason) > 0){ $pk = new DisconnectPacket; $pk->message = $reason; $this->directDataPacket($pk); } //$this->setLinked(); if($this->fishingHook instanceof FishingHook){ $this->fishingHook->close(); $this->fishingHook = null; } $this->removeEffect(Effect::HEALTH_BOOST); $this->connected = false; if(strlen($this->getName()) > 0){ $this->server->getPluginManager()->callEvent($ev = new PlayerQuitEvent($this, $message, true)); if($this->loggedIn === true and $ev->getAutoSave()){ $this->save(); } } foreach($this->server->getOnlinePlayers() as $player){ if(!$player->canSee($this)){ $player->showPlayer($this); } } $this->hiddenPlayers = []; foreach($this->windowIndex as $window){ $this->removeWindow($window); } foreach($this->usedChunks as $index => $d){ Level::getXZ($index, $chunkX, $chunkZ); $this->level->unregisterChunkLoader($this, $chunkX, $chunkZ); unset($this->usedChunks[$index]); } parent::close(); $this->interface->close($this, $notify ? $reason : ""); if($this->loggedIn){ $this->server->removeOnlinePlayer($this); } $this->loggedIn = false; if(isset($ev) and $this->username != "" and $this->spawned !== false and $ev->getQuitMessage() != ""){ if($this->server->playerMsgType === Server::PLAYER_MSG_TYPE_MESSAGE) $this->server->broadcastMessage($ev->getQuitMessage()); elseif($this->server->playerMsgType === Server::PLAYER_MSG_TYPE_TIP) $this->server->broadcastTip(str_replace("@player", $this->getName(), $this->server->playerLogoutMsg)); elseif($this->server->playerMsgType === Server::PLAYER_MSG_TYPE_POPUP) $this->server->broadcastPopup(str_replace("@player", $this->getName(), $this->server->playerLogoutMsg)); } $this->server->getPluginManager()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this); $this->spawned = false; $this->server->getLogger()->info($this->getServer()->getLanguage()->translateString("pocketmine.player.logOut", [ TextFormat::AQUA . $this->getName() . TextFormat::WHITE, $this->ip, $this->port, $this->getServer()->getLanguage()->translateString($reason) ])); $this->windows = new \SplObjectStorage(); $this->windowIndex = []; $this->usedChunks = []; $this->loadQueue = []; $this->hasSpawned = []; $this->spawnPosition = null; unset($this->buffer); if($this->server->dserverConfig["enable"] and $this->server->dserverConfig["queryAutoUpdate"]) $this->server->updateQuery(); } if($this->perm !== null){ $this->perm->clearPermissions(); $this->perm = null; } $this->inventory = null; $this->transactionQueue = null; $this->chunk = null; $this->server->removePlayer($this); } public function __debugInfo(){ return []; } /** * Handles player data saving */ public function save($async = false){ if($this->closed){ throw new \InvalidStateException("Tried to save closed player"); } parent::saveNBT(); if($this->level instanceof Level){ $this->namedtag->Level = new StringTag("Level", $this->level->getName()); if($this->hasValidSpawnPosition()){ $this->namedtag["SpawnLevel"] = $this->spawnPosition->getLevel()->getName(); $this->namedtag["SpawnX"] = (int) $this->spawnPosition->x; $this->namedtag["SpawnY"] = (int) $this->spawnPosition->y; $this->namedtag["SpawnZ"] = (int) $this->spawnPosition->z; } foreach($this->achievements as $achievement => $status){ $this->namedtag->Achievements[$achievement] = new ByteTag($achievement, $status === true ? 1 : 0); } $this->namedtag["playerGameType"] = $this->gamemode; $this->namedtag["lastPlayed"] = new LongTag("lastPlayed", floor(microtime(true) * 1000)); $this->namedtag["Hunger"] = new ShortTag("Hunger", $this->food); $this->namedtag["Health"] = new ShortTag("Health", $this->getHealth()); $this->namedtag["MaxHealth"] = new ShortTag("MaxHealth", $this->getMaxHealth()); $this->namedtag["Experience"] = new LongTag("Experience", $this->exp); $this->namedtag["ExpLevel"] = new LongTag("ExpLevel", $this->expLevel); if($this->username != "" and $this->namedtag instanceof CompoundTag){ $this->server->saveOfflinePlayerData($this->username, $this->namedtag, $async); } } } /** * Gets the username * * @return string */ public function getName(){ return $this->username; } public function kill(){ if(!$this->spawned){ return; } $message = "death.attack.generic"; $params = [ $this->getDisplayName() ]; $cause = $this->getLastDamageCause(); switch($cause === null ? EntityDamageEvent::CAUSE_CUSTOM : $cause->getCause()){ case EntityDamageEvent::CAUSE_ENTITY_ATTACK: if($cause instanceof EntityDamageByEntityEvent){ $e = $cause->getDamager(); if($e instanceof Player){ $message = "death.attack.player"; $params[] = $e->getDisplayName(); break; }elseif($e instanceof Living){ $message = "death.attack.mob"; $params[] = $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName(); break; }else{ $params[] = "Unknown"; } } break; case EntityDamageEvent::CAUSE_PROJECTILE: if($cause instanceof EntityDamageByEntityEvent){ $e = $cause->getDamager(); if($e instanceof Player){ $message = "death.attack.arrow"; $params[] = $e->getDisplayName(); }elseif($e instanceof Living){ $message = "death.attack.arrow"; $params[] = $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName(); break; }else{ $params[] = "Unknown"; } } break; case EntityDamageEvent::CAUSE_SUICIDE: $message = "death.attack.generic"; break; case EntityDamageEvent::CAUSE_VOID: $message = "death.attack.outOfWorld"; break; case EntityDamageEvent::CAUSE_FALL: if($cause instanceof EntityDamageEvent){ if($cause->getFinalDamage() > 2){ $message = "death.fell.accident.generic"; break; } } $message = "death.attack.fall"; break; case EntityDamageEvent::CAUSE_SUFFOCATION: $message = "death.attack.inWall"; break; case EntityDamageEvent::CAUSE_LAVA: $message = "death.attack.lava"; break; case EntityDamageEvent::CAUSE_FIRE: $message = "death.attack.onFire"; break; case EntityDamageEvent::CAUSE_FIRE_TICK: $message = "death.attack.inFire"; break; case EntityDamageEvent::CAUSE_DROWNING: $message = "death.attack.drown"; break; case EntityDamageEvent::CAUSE_CONTACT: if($cause instanceof EntityDamageByBlockEvent){ if($cause->getDamager()->getId() === Block::CACTUS){ $message = "death.attack.cactus"; } } break; case EntityDamageEvent::CAUSE_BLOCK_EXPLOSION: case EntityDamageEvent::CAUSE_ENTITY_EXPLOSION: if($cause instanceof EntityDamageByEntityEvent){ $e = $cause->getDamager(); if($e instanceof Player){ $message = "death.attack.explosion.player"; $params[] = $e->getDisplayName(); }elseif($e instanceof Living){ $message = "death.attack.explosion.player"; $params[] = $e->getNameTag() !== "" ? $e->getNameTag() : $e->getName(); break; } }else{ $message = "death.attack.explosion"; } break; case EntityDamageEvent::CAUSE_MAGIC: $message = "death.attack.magic"; break; case EntityDamageEvent::CAUSE_CUSTOM: break; default: } Entity::kill(); $ev = new PlayerDeathEvent($this, $this->getDrops(), new TranslationContainer($message, $params)); $ev->setKeepInventory($this->server->keepInventory); $ev->setKeepExperience($this->server->keepExperience); $this->server->getPluginManager()->callEvent($ev); if(!$ev->getKeepInventory()){ foreach($ev->getDrops() as $item){ $this->level->dropItem($this, $item); } if($this->inventory !== null){ $this->inventory->clearAll(); } } if($this->server->expEnabled and !$ev->getKeepExperience()){ $exp = min(91, $this->getTotalXp()); //Max 7 levels of exp dropped $this->getLevel()->spawnXPOrb($this->add(0, 0.2, 0), $exp); $this->setTotalXp(0, true); } if($ev->getDeathMessage() != ""){ $this->server->broadcast($ev->getDeathMessage(), Server::BROADCAST_CHANNEL_USERS); } $pos = $this->getSpawn(); $this->setHealth(0); $pk = new RespawnPacket(); $pk->x = $pos->x; $pk->y = $pos->y; $pk->z = $pos->z; $this->dataPacket($pk); } public function setHealth($amount){ parent::setHealth($amount); if($this->spawned === true){ $this->foodTick = 0; $this->getAttributeMap()->getAttribute(Attribute::HEALTH)->setMaxValue($this->getMaxHealth())->setValue($amount, true); } } public function attack($damage, EntityDamageEvent $source){ if(!$this->isAlive()){ return; } if($this->isCreative() and $source->getCause() !== EntityDamageEvent::CAUSE_MAGIC and $source->getCause() !== EntityDamageEvent::CAUSE_SUICIDE and $source->getCause() !== EntityDamageEvent::CAUSE_VOID ){ $source->setCancelled(); }elseif($this->allowFlight and $source->getCause() === EntityDamageEvent::CAUSE_FALL){ $source->setCancelled(); } parent::attack($damage, $source); if($source->isCancelled()){ return; }elseif($this->getLastDamageCause() === $source and $this->spawned){ $pk = new EntityEventPacket(); $pk->eid = 0; $pk->event = EntityEventPacket::HURT_ANIMATION; $this->dataPacket($pk); if($this->isSurvival()){ $this->exhaust(0.3, PlayerExhaustEvent::CAUSE_DAMAGE); } } return true; } public function sendPosition(Vector3 $pos, $yaw = null, $pitch = null, $mode = MovePlayerPacket::MODE_NORMAL, array $targets = null){ $yaw = $yaw === null ? $this->yaw : $yaw; $pitch = $pitch === null ? $this->pitch : $pitch; $pk = new MovePlayerPacket(); $pk->eid = $this->getId(); $pk->x = $pos->x; $pk->y = $pos->y + $this->getEyeHeight(); $pk->z = $pos->z; $pk->bodyYaw = $yaw; $pk->pitch = $pitch; $pk->yaw = $yaw; $pk->mode = $mode; if($targets !== null){ $this->server->broadcastPacket($targets, $pk); }else{ $pk->eid = 0; $this->dataPacket($pk); } } protected function checkChunks(){ if($this->chunk === null or ($this->chunk->getX() !== ($this->x >> 4) or $this->chunk->getZ() !== ($this->z >> 4))){ if($this->chunk !== null){ $this->chunk->removeEntity($this); } $this->chunk = $this->level->getChunk($this->x >> 4, $this->z >> 4, true); if(!$this->justCreated){ $newChunk = $this->level->getChunkPlayers($this->x >> 4, $this->z >> 4); unset($newChunk[$this->getLoaderId()]); /** @var Player[] $reload */ $reload = []; foreach($this->hasSpawned as $player){ if(!isset($newChunk[$player->getLoaderId()])){ $this->despawnFrom($player); }else{ unset($newChunk[$player->getLoaderId()]); $reload[] = $player; } } foreach($newChunk as $player){ $this->spawnTo($player); } } if($this->chunk === null){ return; } $this->chunk->addEntity($this); } } protected function checkTeleportPosition(){ if($this->teleportPosition !== null){ $chunkX = $this->teleportPosition->x >> 4; $chunkZ = $this->teleportPosition->z >> 4; for($X = -1; $X <= 1; ++$X){ for($Z = -1; $Z <= 1; ++$Z){ if(!isset($this->usedChunks[$index = Level::chunkHash($chunkX + $X, $chunkZ + $Z)]) or $this->usedChunks[$index] === false){ return false; } } } $this->sendPosition($this, null, null, MovePlayerPacket::MODE_RESET); $this->spawnToAll(); $this->forceMovement = $this->teleportPosition; $this->teleportPosition = null; return true; } return true; } /** * @param Vector3|Position|Location $pos * @param float $yaw * @param float $pitch * * @return bool */ public function teleport(Vector3 $pos, $yaw = null, $pitch = null){ if(!$this->isOnline()){ return false; } $oldPos = $this->getPosition(); if(parent::teleport($pos, $yaw, $pitch)){ foreach($this->windowIndex as $window){ if($window === $this->inventory){ continue; } $this->removeWindow($window); } $this->teleportPosition = new Vector3($this->x, $this->y, $this->z); if(!$this->checkTeleportPosition()){ $this->forceMovement = $oldPos; }else{ $this->spawnToAll(); } $this->resetFallDistance(); $this->nextChunkOrderRun = 0; $this->newPosition = null; $this->stopSleep(); return true; } return false; } /** * This method may not be reliable. Clients don't like to be moved into unloaded chunks. * Use teleport() for a delayed teleport after chunks have been sent. * * @param Vector3 $pos * @param float $yaw * @param float $pitch */ public function teleportImmediate(Vector3 $pos, $yaw = null, $pitch = null){ if(parent::teleport($pos, $yaw, $pitch)){ foreach($this->windowIndex as $window){ if($window === $this->inventory){ continue; } $this->removeWindow($window); } $this->forceMovement = new Vector3($this->x, $this->y, $this->z); $this->sendPosition($this, $this->yaw, $this->pitch, MovePlayerPacket::MODE_RESET); $this->resetFallDistance(); $this->orderChunks(); $this->nextChunkOrderRun = 0; $this->newPosition = null; } } /** * @param Inventory $inventory * * @return int */ public function getWindowId(Inventory $inventory) : int{ if($this->windows->contains($inventory)){ return $this->windows[$inventory]; } return -1; } /** * Returns the created/existing window id * * @param Inventory $inventory * @param int $forceId * * @return int */ public function addWindow(Inventory $inventory, $forceId = null) : int{ if($this->windows->contains($inventory)){ return $this->windows[$inventory]; } if($forceId === null){ $this->windowCnt = $cnt = max(2, ++$this->windowCnt % 99); }else{ $cnt = (int) $forceId; } $this->windowIndex[$cnt] = $inventory; $this->windows->attach($inventory, $cnt); if($inventory->open($this)){ return $cnt; }else{ $this->removeWindow($inventory); return -1; } } public function removeWindow(Inventory $inventory){ $inventory->close($this); if($this->windows->contains($inventory)){ $id = $this->windows[$inventory]; $this->windows->detach($this->windowIndex[$id]); unset($this->windowIndex[$id]); } } public function setMetadata($metadataKey, MetadataValue $metadataValue){ $this->server->getPlayerMetadata()->setMetadata($this, $metadataKey, $metadataValue); } public function getMetadata($metadataKey){ return $this->server->getPlayerMetadata()->getMetadata($this, $metadataKey); } public function hasMetadata($metadataKey){ return $this->server->getPlayerMetadata()->hasMetadata($this, $metadataKey); } public function removeMetadata($metadataKey, Plugin $plugin){ $this->server->getPlayerMetadata()->removeMetadata($this, $metadataKey, $plugin); } public function onChunkChanged(Chunk $chunk){ $this->loadQueue[Level::chunkHash($chunk->getX(), $chunk->getZ())] = abs(($this->x >> 4) - $chunk->getX()) + abs(($this->z >> 4) - $chunk->getZ()); } public function onChunkLoaded(Chunk $chunk){ } public function onChunkPopulated(Chunk $chunk){ } public function onChunkUnloaded(Chunk $chunk){ } public function onBlockChanged(Vector3 $block){ } public function getLoaderId(){ return $this->loaderId; } public function isLoaderActive(){ return $this->isConnected(); } /** * @param $chunkX * @param $chunkZ * @param $payload * * @return BatchPacket|FullChunkDataPacket */ public static function getChunkCacheFromData($chunkX, $chunkZ, $payload){ $pk = new FullChunkDataPacket(); $pk->chunkX = $chunkX; $pk->chunkZ = $chunkZ; $pk->data = $payload; if(Network::$BATCH_THRESHOLD >= 0){ $pk->encode(); $batch = new BatchPacket(); $batch->payload = zlib_encode(Binary::writeUnsignedVarInt(strlen($pk->getBuffer())) . $pk->getBuffer(), ZLIB_ENCODING_DEFLATE, Server::getInstance()->networkCompressionLevel); $batch->encode(); $batch->isEncoded = true; return $batch; } return $pk; } /** * @param Effect */ public function addEffect(Effect $effect){//Overwrite if($effect->isBad() && $this->isCreative()){ return; } parent::addEffect($effect); } } ================================================ FILE: src/pocketmine/PocketMine.php ================================================ $value){ echo str_repeat(" ", $cnt + 1) . "[" . (is_integer($key) ? $key : '"' . $key . '"') . "]=>" . PHP_EOL; ++$cnt; safe_var_dump($value); --$cnt; } echo str_repeat(" ", $cnt) . "}" . PHP_EOL; break; case is_int($var): echo str_repeat(" ", $cnt) . "int(" . $var . ")" . PHP_EOL; break; case is_float($var): echo str_repeat(" ", $cnt) . "float(" . $var . ")" . PHP_EOL; break; case is_bool($var): echo str_repeat(" ", $cnt) . "bool(" . ($var === true ? "true" : "false") . ")" . PHP_EOL; break; case is_string($var): echo str_repeat(" ", $cnt) . "string(" . strlen($var) . ") \"$var\"" . PHP_EOL; break; case is_resource($var): echo str_repeat(" ", $cnt) . "resource() of type (" . get_resource_type($var) . ")" . PHP_EOL; break; case is_object($var): echo str_repeat(" ", $cnt) . "object(" . get_class($var) . ")" . PHP_EOL; break; case is_null($var): echo str_repeat(" ", $cnt) . "NULL" . PHP_EOL; break; } } } function dummy(){ } } namespace pocketmine { use pocketmine\utils\Binary; use pocketmine\utils\MainLogger; use pocketmine\utils\ServerKiller; use pocketmine\utils\Terminal; use pocketmine\utils\Utils; use pocketmine\wizard\Installer; const VERSION = "1.0"; const API_VERSION = "3.0.0-ALPHA1"; const CODENAME = "Birpen"; const MINECRAFT_VERSION = "v1.0.0 alpha"; const MINECRAFT_VERSION_NETWORK = "1.0.0"; const GENISYS_API_VERSION = '1.9.3'; /* * Startup code. Do not look at it, it may harm you. * Most of them are hacks to fix date-related bugs, or basic functions used after this * This is the only non-class based file on this project. * Enjoy it as much as I did writing it. I don't want to do it again. */ if(\Phar::running(true) !== ""){ @define('pocketmine\PATH', \Phar::running(true) . "/"); }else{ @define('pocketmine\PATH', \getcwd() . DIRECTORY_SEPARATOR); } if(version_compare("7.0", PHP_VERSION) > 0){ echo "[CRITICAL] You must use PHP >= 7.0" . PHP_EOL; echo "[CRITICAL] Please use the installer provided on the homepage." . PHP_EOL; exit(1); } if(!extension_loaded("pthreads")){ echo "[CRITICAL] Unable to find the pthreads extension." . PHP_EOL; echo "[CRITICAL] Please use the installer provided on the homepage." . PHP_EOL; exit(1); } if(!class_exists("ClassLoader", false)){ require_once(\pocketmine\PATH . "src/spl/ClassLoader.php"); require_once(\pocketmine\PATH . "src/spl/BaseClassLoader.php"); require_once(\pocketmine\PATH . "src/pocketmine/CompatibleClassLoader.php"); } $autoloader = new CompatibleClassLoader(); $autoloader->addPath(\pocketmine\PATH . "src"); $autoloader->addPath(\pocketmine\PATH . "src" . DIRECTORY_SEPARATOR . "spl"); $autoloader->register(true); set_time_limit(0); //Who set it to 30 seconds?!?! gc_enable(); error_reporting(-1); ini_set("allow_url_fopen", 1); ini_set("display_errors", 1); ini_set("display_startup_errors", 1); ini_set("default_charset", "utf-8"); ini_set("memory_limit", -1); define('pocketmine\START_TIME', microtime(true)); $opts = getopt("", ["data:", "plugins:", "no-wizard", "enable-profiler"]); define('pocketmine\DATA', isset($opts["data"]) ? $opts["data"] . DIRECTORY_SEPARATOR : \getcwd() . DIRECTORY_SEPARATOR); define('pocketmine\PLUGIN_PATH', isset($opts["plugins"]) ? $opts["plugins"] . DIRECTORY_SEPARATOR : \getcwd() . DIRECTORY_SEPARATOR . "plugins" . DIRECTORY_SEPARATOR); Terminal::init(); define('pocketmine\ANSI', Terminal::hasFormattingCodes()); if(!file_exists(\pocketmine\DATA)){ mkdir(\pocketmine\DATA, 0777, true); } //Logger has a dependency on timezone, so we'll set it to UTC until we can get the actual timezone. date_default_timezone_set("UTC"); $logger = new MainLogger(\pocketmine\DATA . "server.log", \pocketmine\ANSI); if(!ini_get("date.timezone")){ if(($timezone = detect_system_timezone()) and date_default_timezone_set($timezone)){ //Success! Timezone has already been set and validated in the if statement. //This here is just for redundancy just in case some program wants to read timezone data from the ini. ini_set("date.timezone", $timezone); }else{ //If system timezone detection fails or timezone is an invalid value. if($response = Utils::getURL("http://ip-api.com/json") and $ip_geolocation_data = json_decode($response, true) and $ip_geolocation_data['status'] !== 'fail' and date_default_timezone_set($ip_geolocation_data['timezone']) ){ //Again, for redundancy. ini_set("date.timezone", $ip_geolocation_data['timezone']); }else{ ini_set("date.timezone", "UTC"); date_default_timezone_set("UTC"); $logger->warning("Timezone could not be automatically determined. An incorrect timezone will result in incorrect timestamps on console logs. It has been set to \"UTC\" by default. You can change it on the php.ini file."); } } }else{ /* * This is here so that people don't come to us complaining and fill up the issue tracker when they put * an incorrect timezone abbreviation in php.ini apparently. */ $timezone = ini_get("date.timezone"); if(strpos($timezone, "/") === false){ $default_timezone = timezone_name_from_abbr($timezone); ini_set("date.timezone", $default_timezone); date_default_timezone_set($default_timezone); } else { date_default_timezone_set($timezone); } } function detect_system_timezone(){ switch(Utils::getOS()){ case 'win': $regex = '/(UTC)(\+*\-*\d*\d*\:*\d*\d*)/'; /* * wmic timezone get Caption * Get the timezone offset * * Sample Output var_dump * array(3) { * [0] => * string(7) "Caption" * [1] => * string(20) "(UTC+09:30) Adelaide" * [2] => * string(0) "" * } */ exec("wmic timezone get Caption", $output); $string = trim(implode("\n", $output)); //Detect the Time Zone string preg_match($regex, $string, $matches); if(!isset($matches[2])){ return false; } $offset = $matches[2]; if($offset == ""){ return "UTC"; } return parse_offset($offset); break; case 'linux': // Ubuntu / Debian. if(file_exists('/etc/timezone')){ $data = file_get_contents('/etc/timezone'); if($data){ return trim($data); } } // RHEL / CentOS if(file_exists('/etc/sysconfig/clock')){ $data = parse_ini_file('/etc/sysconfig/clock'); if(!empty($data['ZONE'])){ return trim($data['ZONE']); } } //Portable method for incompatible linux distributions. $offset = trim(exec('date +%:z')); if($offset == "+00:00"){ return "UTC"; } return parse_offset($offset); break; case 'mac': if(is_link('/etc/localtime')){ $filename = readlink('/etc/localtime'); if(strpos($filename, '/usr/share/zoneinfo/') === 0){ $timezone = substr($filename, 20); return trim($timezone); } } return false; break; default: return false; break; } } /** * @param string $offset In the format of +09:00, +02:00, -04:00 etc. * * @return string */ function parse_offset($offset){ //Make signed offsets unsigned for date_parse if(strpos($offset, '-') !== false){ $negative_offset = true; $offset = str_replace('-', '', $offset); }else{ if(strpos($offset, '+') !== false){ $negative_offset = false; $offset = str_replace('+', '', $offset); }else{ return false; } } $parsed = date_parse($offset); $offset = $parsed['hour'] * 3600 + $parsed['minute'] * 60 + $parsed['second']; //After date_parse is done, put the sign back if($negative_offset == true){ $offset = -abs($offset); } //And then, look the offset up. //timezone_name_from_abbr is not used because it returns false on some(most) offsets because it's mapping function is weird. //That's been a bug in PHP since 2008! foreach(timezone_abbreviations_list() as $zones){ foreach($zones as $timezone){ if($timezone['offset'] == $offset){ return $timezone['timezone_id']; } } } return false; } if(isset($opts["enable-profiler"])){ if(function_exists("profiler_enable")){ \profiler_enable(); $logger->notice("Execution is being profiled"); }else{ $logger->notice("No profiler found. Please install https://github.com/krakjoe/profiler"); } } function kill($pid){ switch(Utils::getOS()){ case "win": exec("taskkill.exe /F /PID " . ((int) $pid) . " > NUL"); break; case "mac": case "linux": default: if(function_exists("posix_kill")){ posix_kill($pid, SIGKILL); }else{ exec("kill -9 " . ((int)$pid) . " > /dev/null 2>&1"); } } } /** * @param object $value * @param bool $includeCurrent * * @return int */ function getReferenceCount($value, $includeCurrent = true){ ob_start(); debug_zval_dump($value); $ret = explode("\n", ob_get_contents()); ob_end_clean(); if(count($ret) >= 1 and preg_match('/^.* refcount\\(([0-9]+)\\)\\{$/', trim($ret[0]), $m) > 0){ return ((int) $m[1]) - ($includeCurrent ? 3 : 4); //$value + zval call + extra call } return -1; } function getTrace($start = 1, $trace = null){ if($trace === null){ if(function_exists("xdebug_get_function_stack")){ $trace = array_reverse(xdebug_get_function_stack()); }else{ $e = new \Exception(); $trace = $e->getTrace(); } } $messages = []; $j = 0; for($i = (int) $start; isset($trace[$i]); ++$i, ++$j){ $params = ""; if(isset($trace[$i]["args"]) or isset($trace[$i]["params"])){ if(isset($trace[$i]["args"])){ $args = $trace[$i]["args"]; }else{ $args = $trace[$i]["params"]; } foreach($args as $name => $value){ $params .= (is_object($value) ? get_class($value) . " " . (method_exists($value, "__toString") ? $value->__toString() : "object") : gettype($value) . " " . (is_array($value) ? "Array()" : Utils::printable(@strval($value)))) . ", "; } } $messages[] = "#$j " . (isset($trace[$i]["file"]) ? cleanPath($trace[$i]["file"]) : "") . "(" . (isset($trace[$i]["line"]) ? $trace[$i]["line"] : "") . "): " . (isset($trace[$i]["class"]) ? $trace[$i]["class"] . (($trace[$i]["type"] === "dynamic" or $trace[$i]["type"] === "->") ? "->" : "::") : "") . $trace[$i]["function"] . "(" . Utils::printable(substr($params, 0, -2)) . ")"; } return $messages; } function cleanPath($path){ return rtrim(str_replace(["\\", ".php", "phar://", rtrim(str_replace(["\\", "phar://"], ["/", ""], \pocketmine\PATH), "/"), rtrim(str_replace(["\\", "phar://"], ["/", ""], \pocketmine\PLUGIN_PATH), "/")], ["/", "", "", "", ""], $path), "/"); } $errors = 0; if(php_sapi_name() !== "cli"){ $logger->critical("You must run PocketMine-MP using the CLI."); ++$errors; } if(!extension_loaded("sockets")){ $logger->critical("Unable to find the Socket extension."); ++$errors; } $pthreads_version = phpversion("pthreads"); if(substr_count($pthreads_version, ".") < 2){ $pthreads_version = "0.$pthreads_version"; } if(version_compare($pthreads_version, "3.1.5") < 0){ $logger->critical("pthreads >= 3.1.5 is required, while you have $pthreads_version."); ++$errors; } if(!extension_loaded("uopz")){ //$logger->notice("Couldn't find the uopz extension. Some functions may be limited"); } if(extension_loaded("pocketmine")){ if(version_compare(phpversion("pocketmine"), "0.0.1") < 0){ $logger->critical("You have the native PocketMine extension, but your version is lower than 0.0.1."); ++$errors; }elseif(version_compare(phpversion("pocketmine"), "0.0.4") > 0){ $logger->critical("You have the native PocketMine extension, but your version is higher than 0.0.4."); ++$errors; } } if(extension_loaded("xdebug")){ $logger->warning(" You are running PocketMine with xdebug enabled. This has a major impact on performance. "); } if(!extension_loaded("curl")){ $logger->critical("Unable to find the cURL extension."); ++$errors; } if(!extension_loaded("yaml")){ $logger->critical("Unable to find the YAML extension."); ++$errors; } if(!extension_loaded("sqlite3")){ $logger->critical("Unable to find the SQLite3 extension."); ++$errors; } if(!extension_loaded("zlib")){ $logger->critical("Unable to find the Zlib extension."); ++$errors; } if($errors > 0){ $logger->critical("Please update your PHP from itxtech.org/genisys/get/, or recompile PHP again."); $logger->shutdown(); $logger->join(); exit(1); //Exit with error } if(file_exists(\pocketmine\PATH . ".git/refs/heads/master")){ //Found Git information! define('pocketmine\GIT_COMMIT', strtolower(trim(file_get_contents(\pocketmine\PATH . ".git/refs/heads/master")))); }else{ define('pocketmine\GIT_COMMIT', "8f465763c590293e2003ce616832b6507edf5b1c"); } @define("ENDIANNESS", (pack("d", 1) === "\77\360\0\0\0\0\0\0" ? Binary::BIG_ENDIAN : Binary::LITTLE_ENDIAN)); @define("INT32_MASK", is_int(0xffffffff) ? 0xffffffff : -1); @ini_set("opcache.mmap_base", bin2hex(random_bytes(8))); //Fix OPCache address errors $lang = "unknown"; if(!file_exists(\pocketmine\DATA . "server.properties") and !isset($opts["no-wizard"])){ $inst = new Installer(); $lang = $inst->getDefaultLang(); } /*if(\Phar::running(true) === ""){ $logger->warning("Non-packaged PocketMine-MP installation detected, do not use on production."); }*/ ThreadManager::init(); new Server($autoloader, $logger, \pocketmine\PATH, \pocketmine\DATA, \pocketmine\PLUGIN_PATH, $lang); $logger->info("Stopping other threads"); foreach(ThreadManager::getInstance()->getAll() as $id => $thread){ $logger->debug("Stopping " . (new \ReflectionClass($thread))->getShortName() . " thread"); $thread->quit(); } $killer = new ServerKiller(8); $killer->start(); $logger->shutdown(); $logger->join(); echo "Server has stopped" . Terminal::$FORMAT_RESET . "\n"; exit(0); } ================================================ FILE: src/pocketmine/Server.php ================================================ isRunning === true; } /** * @return string * Returns a formatted string of how long the server has been running for */ public function getUptime(){ $time = microtime(true) - \pocketmine\START_TIME; $seconds = floor($time % 60); $minutes = null; $hours = null; $days = null; if($time >= 60){ $minutes = floor(($time % 3600) / 60); if($time >= 3600){ $hours = floor(($time % (3600 * 24)) / 3600); if($time >= 3600 * 24){ $days = floor($time / (3600 * 24)); } } } $uptime = ($minutes !== null ? ($hours !== null ? ($days !== null ? "$days " . $this->getLanguage()->translateString("%pocketmine.command.status.days") . " " : "") . "$hours " . $this->getLanguage()->translateString("%pocketmine.command.status.hours") . " " : "") . "$minutes " . $this->getLanguage()->translateString("%pocketmine.command.status.minutes") . " " : "") . "$seconds " . $this->getLanguage()->translateString("%pocketmine.command.status.seconds"); return $uptime; } /** * @return string */ public function getPocketMineVersion(){ return \pocketmine\VERSION; } public function getFormattedVersion($prefix = ""){ return (\pocketmine\VERSION !== ""? $prefix . \pocketmine\VERSION : ""); } /** * @return string */ public function getCodename(){ return \pocketmine\CODENAME; } /** * @return string */ public function getVersion(){ return \pocketmine\MINECRAFT_VERSION; } /** * @return string */ public function getApiVersion(){ return \pocketmine\API_VERSION; } /** * @return string */ public function getiTXApiVersion(){ return \pocketmine\GENISYS_API_VERSION; } /** * @return string */ public function getGeniApiVersion(){ return \pocketmine\GENISYS_API_VERSION; } /** * @return string */ public function getFilePath(){ return $this->filePath; } /** * @return string */ public function getDataPath(){ return $this->dataPath; } /** * @return string */ public function getPluginPath(){ return $this->pluginPath; } /** * @return int */ public function getMaxPlayers(){ return $this->maxPlayers; } /** * @return int */ public function getPort(){ return $this->getConfigInt("server-port", 19132); } /** * @return int */ public function getViewDistance(){ return max(56, $this->getProperty("chunk-sending.max-chunks", 256)); } /** * @return string */ public function getIp(){ return $this->getConfigString("server-ip", "0.0.0.0"); } public function getServerUniqueId(){ return $this->serverID; } /** * @return bool */ public function getAutoSave(){ return $this->autoSave; } /** * @param bool $value */ public function setAutoSave($value){ $this->autoSave = (bool) $value; foreach($this->getLevels() as $level){ $level->setAutoSave($this->autoSave); } } /** * @return string */ public function getLevelType(){ return $this->getConfigString("level-type", "DEFAULT"); } /** * @return bool */ public function getGenerateStructures(){ return $this->getConfigBoolean("generate-structures", true); } /** * @return int */ public function getGamemode(){ return $this->getConfigInt("gamemode", 0) & 0b11; } /** * @return bool */ public function getForceGamemode(){ return $this->getConfigBoolean("force-gamemode", false); } /** * Returns the gamemode text name * * @param int $mode * * @return string */ public static function getGamemodeString($mode){ switch((int) $mode){ case Player::SURVIVAL: return "%gameMode.survival"; case Player::CREATIVE: return "%gameMode.creative"; case Player::ADVENTURE: return "%gameMode.adventure"; case Player::SPECTATOR: return "%gameMode.spectator"; } return "UNKNOWN"; } /** * Parses a string and returns a gamemode integer, -1 if not found * * @param string $str * * @return int */ public static function getGamemodeFromString($str){ switch(strtolower(trim($str))){ case (string) Player::SURVIVAL: case "survival": case "s": return Player::SURVIVAL; case (string) Player::CREATIVE: case "creative": case "c": return Player::CREATIVE; case (string) Player::ADVENTURE: case "adventure": case "a": return Player::ADVENTURE; case (string) Player::SPECTATOR: case "spectator": case "view": case "v": return Player::SPECTATOR; } return -1; } /** * @param string $str * * @return int */ public static function getDifficultyFromString($str){ switch(strtolower(trim($str))){ case "0": case "peaceful": case "p": return 0; case "1": case "easy": case "e": return 1; case "2": case "normal": case "n": return 2; case "3": case "hard": case "h": return 3; } return -1; } /** * @return int */ public function getDifficulty(){ return $this->getConfigInt("difficulty", 1); } /** * @return bool */ public function hasWhitelist(){ return $this->getConfigBoolean("white-list", false); } /** * @return int */ public function getSpawnRadius(){ return $this->getConfigInt("spawn-protection", 16); } /** * @return bool */ public function getAllowFlight(){ return $this->getConfigBoolean("allow-flight", false); } /** * @return bool */ public function isHardcore(){ return $this->getConfigBoolean("hardcore", false); } /** * @return int */ public function getDefaultGamemode(){ return $this->getConfigInt("gamemode", 0) & 0b11; } /** * @return string */ public function getMotd(){ return $this->getConfigString("motd", "Minecraft: PE Server"); } /** * @return \ClassLoader */ public function getLoader(){ return $this->autoloader; } /** * @return MainLogger */ public function getLogger(){ return $this->logger; } /** * @return EntityMetadataStore */ public function getEntityMetadata(){ return $this->entityMetadata; } /** * @return PlayerMetadataStore */ public function getPlayerMetadata(){ return $this->playerMetadata; } /** * @return LevelMetadataStore */ public function getLevelMetadata(){ return $this->levelMetadata; } /** * @return PluginManager */ public function getPluginManager(){ return $this->pluginManager; } /** * @return CraftingManager */ public function getCraftingManager(){ return $this->craftingManager; } /** * @return ServerScheduler */ public function getScheduler(){ return $this->scheduler; } /** * @return int */ public function getTick(){ return $this->tickCounter; } /** * Returns the last server TPS measure * * @return float */ public function getTicksPerSecond(){ return round($this->maxTick, 2); } /** * Returns the last server TPS average measure * * @return float */ public function getTicksPerSecondAverage(){ return round(array_sum($this->tickAverage) / count($this->tickAverage), 2); } /** * Returns the TPS usage/load in % * * @return float */ public function getTickUsage(){ return round($this->maxUse * 100, 2); } /** * Returns the TPS usage/load average in % * * @return float */ public function getTickUsageAverage(){ return round((array_sum($this->useAverage) / count($this->useAverage)) * 100, 2); } /** * @return SimpleCommandMap */ public function getCommandMap(){ return $this->commandMap; } /** * @return Player[] */ public function getOnlinePlayers(){ return $this->playerList; } public function addRecipe(Recipe $recipe){ $this->craftingManager->registerRecipe($recipe); $this->generateRecipeList(); } public function shouldSavePlayerData() : bool{ return (bool) $this->getProperty("player.save-player-data", true); } /** * @param string $name * * @return OfflinePlayer|Player */ public function getOfflinePlayer($name){ $name = strtolower($name); $result = $this->getPlayerExact($name); if($result === null){ $result = new OfflinePlayer($this, $name); } return $result; } /** * @param string $name * * @return CompoundTag */ public function getOfflinePlayerData($name){ $name = strtolower($name); $path = $this->getDataPath() . "players/"; if($this->shouldSavePlayerData()){ if(file_exists($path . "$name.dat")){ try{ $nbt = new NBT(NBT::BIG_ENDIAN); $nbt->readCompressed(file_get_contents($path . "$name.dat")); return $nbt->getData(); }catch(\Throwable $e){ //zlib decode error / corrupt data rename($path . "$name.dat", $path . "$name.dat.bak"); $this->logger->notice($this->getLanguage()->translateString("pocketmine.data.playerCorrupted", [$name])); } }else{ $this->logger->notice($this->getLanguage()->translateString("pocketmine.data.playerNotFound", [$name])); } } $spawn = $this->getDefaultLevel()->getSafeSpawn(); $nbt = new CompoundTag("", [ new LongTag("firstPlayed", floor(microtime(true) * 1000)), new LongTag("lastPlayed", floor(microtime(true) * 1000)), new ListTag("Pos", [ new DoubleTag(0, $spawn->x), new DoubleTag(1, $spawn->y), new DoubleTag(2, $spawn->z) ]), new StringTag("Level", $this->getDefaultLevel()->getName()), //new StringTag("SpawnLevel", $this->getDefaultLevel()->getName()), //new IntTag("SpawnX", (int) $spawn->x), //new IntTag("SpawnY", (int) $spawn->y), //new IntTag("SpawnZ", (int) $spawn->z), //new ByteTag("SpawnForced", 1), //TODO new ListTag("Inventory", []), new ListTag("EnderChestInventory", []), new CompoundTag("Achievements", []), new IntTag("playerGameType", $this->getGamemode()), new ListTag("Motion", [ new DoubleTag(0, 0.0), new DoubleTag(1, 0.0), new DoubleTag(2, 0.0) ]), new ListTag("Rotation", [ new FloatTag(0, 0.0), new FloatTag(1, 0.0) ]), new FloatTag("FallDistance", 0.0), new ShortTag("Fire", 0), new ShortTag("Air", 300), new ByteTag("OnGround", 1), new ByteTag("Invulnerable", 0), new StringTag("NameTag", $name), new ShortTag("Health", 20), new ShortTag("MaxHealth", 20), ]); $nbt->Pos->setTagType(NBT::TAG_Double); $nbt->Inventory->setTagType(NBT::TAG_Compound); $nbt->EnderChestInventory->setTagType(NBT::TAG_Compound); $nbt->Motion->setTagType(NBT::TAG_Double); $nbt->Rotation->setTagType(NBT::TAG_Float); /*if(file_exists($path . "$name.yml")){ //Importing old PocketMine-MP files $data = new Config($path . "$name.yml", Config::YAML, []); $nbt["playerGameType"] = (int) $data->get("gamemode"); $nbt["Level"] = $data->get("position")["level"]; $nbt["Pos"][0] = $data->get("position")["x"]; $nbt["Pos"][1] = $data->get("position")["y"]; $nbt["Pos"][2] = $data->get("position")["z"]; $nbt["SpawnLevel"] = $data->get("spawn")["level"]; $nbt["SpawnX"] = (int) $data->get("spawn")["x"]; $nbt["SpawnY"] = (int) $data->get("spawn")["y"]; $nbt["SpawnZ"] = (int) $data->get("spawn")["z"]; $this->logger->notice($this->getLanguage()->translateString("pocketmine.data.playerOld", [$name])); foreach($data->get("inventory") as $slot => $item){ if(count($item) === 3){ $nbt->Inventory[$slot + 9] = new CompoundTag("", [ new ShortTag("id", $item[0]), new ShortTag("Damage", $item[1]), new ByteTag("Count", $item[2]), new ByteTag("Slot", $slot + 9), new ByteTag("TrueSlot", $slot + 9) ]); } } foreach($data->get("hotbar") as $slot => $itemSlot){ if(isset($nbt->Inventory[$itemSlot + 9])){ $item = $nbt->Inventory[$itemSlot + 9]; $nbt->Inventory[$slot] = new CompoundTag("", [ new ShortTag("id", $item["id"]), new ShortTag("Damage", $item["Damage"]), new ByteTag("Count", $item["Count"]), new ByteTag("Slot", $slot), new ByteTag("TrueSlot", $item["TrueSlot"]) ]); } } foreach($data->get("armor") as $slot => $item){ if(count($item) === 2){ $nbt->Inventory[$slot + 100] = new CompoundTag("", [ new ShortTag("id", $item[0]), new ShortTag("Damage", $item[1]), new ByteTag("Count", 1), new ByteTag("Slot", $slot + 100) ]); } } foreach($data->get("achievements") as $achievement => $status){ $nbt->Achievements[$achievement] = new ByteTag($achievement, $status == true ? 1 : 0); } unlink($path . "$name.yml"); }*/ $this->saveOfflinePlayerData($name, $nbt); return $nbt; } /** * @param string $name * @param CompoundTag $nbtTag * @param bool $async */ public function saveOfflinePlayerData($name, CompoundTag $nbtTag, $async = false){ if($this->shouldSavePlayerData()){ $nbt = new NBT(NBT::BIG_ENDIAN); try{ $nbt->setData($nbtTag); if($async){ $this->getScheduler()->scheduleAsyncTask(new FileWriteTask($this->getDataPath() . "players/" . strtolower($name) . ".dat", $nbt->writeCompressed())); }else{ file_put_contents($this->getDataPath() . "players/" . strtolower($name) . ".dat", $nbt->writeCompressed()); } }catch(\Throwable $e){ $this->logger->critical($this->getLanguage()->translateString("pocketmine.data.saveError", [$name, $e->getMessage()])); $this->logger->logException($e); } } } /** * @param string $name * * @return Player */ public function getPlayer(string $name){ $found = null; $name = strtolower($name); $delta = PHP_INT_MAX; foreach($this->getOnlinePlayers() as $player){ if(stripos($player->getName(), $name) === 0){ $curDelta = strlen($player->getName()) - strlen($name); if($curDelta < $delta){ $found = $player; $delta = $curDelta; } if($curDelta === 0){ break; } } } return $found; } /** * @param string $name * * @return Player */ public function getPlayerExact(string $name){ $name = strtolower($name); foreach($this->getOnlinePlayers() as $player){ if(strtolower($player->getName()) === $name){ return $player; } } return null; } /** * @param string $partialName * * @return Player[] */ public function matchPlayer($partialName){ $partialName = strtolower($partialName); $matchedPlayers = []; foreach($this->getOnlinePlayers() as $player){ if(strtolower($player->getName()) === $partialName){ $matchedPlayers = [$player]; break; }elseif(stripos($player->getName(), $partialName) !== false){ $matchedPlayers[] = $player; } } return $matchedPlayers; } /** * @param Player $player */ public function removePlayer(Player $player){ if(isset($this->identifiers[$hash = spl_object_hash($player)])){ $identifier = $this->identifiers[$hash]; unset($this->players[$identifier]); unset($this->identifiers[$hash]); return; } foreach($this->players as $identifier => $p){ if($player === $p){ unset($this->players[$identifier]); unset($this->identifiers[spl_object_hash($player)]); break; } } } /** * @return Level[] */ public function getLevels(){ return $this->levels; } /** * @return Level */ public function getDefaultLevel(){ return $this->levelDefault; } /** * Sets the default level to a different level * This won't change the level-name property, * it only affects the server on runtime * * @param Level $level */ public function setDefaultLevel($level){ if($level === null or ($this->isLevelLoaded($level->getFolderName()) and $level !== $this->levelDefault)){ $this->levelDefault = $level; } } /** * @param string $name * * @return bool */ public function isLevelLoaded($name){ return $this->getLevelByName($name) instanceof Level; } /** * @param int $levelId * * @return Level */ public function getLevel($levelId){ if(isset($this->levels[$levelId])){ return $this->levels[$levelId]; } return null; } /** * @param $name * * @return Level */ public function getLevelByName($name){ foreach($this->getLevels() as $level){ if($level->getFolderName() === $name){ return $level; } } return null; } /** * @param Level $level * @param bool $forceUnload * * @return bool */ public function unloadLevel(Level $level, $forceUnload = false){ if($level === $this->getDefaultLevel() and !$forceUnload){ throw new \InvalidStateException("The default level cannot be unloaded while running, please switch levels."); } if($level->unload($forceUnload) === true){ unset($this->levels[$level->getId()]); return true; } return false; } /** * Loads a level from the data directory * * @param string $name * * @return bool * * @throws LevelException */ public function loadLevel($name){ if(trim($name) === ""){ throw new LevelException("Invalid empty level name"); } if($this->isLevelLoaded($name)){ return true; }elseif(!$this->isLevelGenerated($name)){ $this->logger->notice($this->getLanguage()->translateString("pocketmine.level.notFound", [$name])); return false; } $path = $this->getDataPath() . "worlds/" . $name . "/"; $provider = LevelProviderManager::getProvider($path); if($provider === null){ $this->logger->error($this->getLanguage()->translateString("pocketmine.level.loadError", [$name, "Unknown provider"])); return false; } //$entities = new Config($path."entities.yml", Config::YAML); //if(file_exists($path . "tileEntities.yml")){ // @rename($path . "tileEntities.yml", $path . "tiles.yml"); //} try{ $level = new Level($this, $name, $path, $provider); }catch(\Throwable $e){ $this->logger->error($this->getLanguage()->translateString("pocketmine.level.loadError", [$name, $e->getMessage()])); if($this->logger instanceof MainLogger){ $this->logger->logException($e); } return false; } $this->levels[$level->getId()] = $level; $level->initLevel(); $this->getPluginManager()->callEvent(new LevelLoadEvent($level)); $level->setTickRate($this->baseTickRate); return true; } /** * Generates a new level if it does not exists * * @param string $name * @param int $seed * @param string $generator Class name that extends pocketmine\level\generator\Noise * @param array $options * * @return bool */ public function generateLevel($name, $seed = null, $generator = null, $options = []){ if(trim($name) === "" or $this->isLevelGenerated($name)){ return false; } $seed = $seed === null ? Binary::readInt(random_bytes(4)) : (int) $seed; if(!isset($options["preset"])){ $options["preset"] = $this->getConfigString("generator-settings", ""); } if(!($generator !== null and class_exists($generator, true) and is_subclass_of($generator, Generator::class))){ $generator = Generator::getGenerator($this->getLevelType()); } if(($provider = LevelProviderManager::getProviderByName($providerName = $this->getProperty("level-settings.default-format", "pmanvil"))) === null){ $provider = LevelProviderManager::getProviderByName($providerName = "pmanvil"); } try{ $path = $this->getDataPath() . "worlds/" . $name . "/"; /** @var \pocketmine\level\format\LevelProvider $provider */ $provider::generate($path, $name, $seed, $generator, $options); $level = new Level($this, $name, $path, $provider); $this->levels[$level->getId()] = $level; $level->initLevel(); $level->setTickRate($this->baseTickRate); }catch(\Throwable $e){ $this->logger->error($this->getLanguage()->translateString("pocketmine.level.generateError", [$name, $e->getMessage()])); if($this->logger instanceof MainLogger){ $this->logger->logException($e); } return false; } $this->getPluginManager()->callEvent(new LevelInitEvent($level)); $this->getPluginManager()->callEvent(new LevelLoadEvent($level)); $this->getLogger()->notice($this->getLanguage()->translateString("pocketmine.level.backgroundGeneration", [$name])); $centerX = $level->getSpawnLocation()->getX() >> 4; $centerZ = $level->getSpawnLocation()->getZ() >> 4; $order = []; for($X = -3; $X <= 3; ++$X){ for($Z = -3; $Z <= 3; ++$Z){ $distance = $X ** 2 + $Z ** 2; $chunkX = $X + $centerX; $chunkZ = $Z + $centerZ; $index = Level::chunkHash($chunkX, $chunkZ); $order[$index] = $distance; } } asort($order); foreach($order as $index => $distance){ Level::getXZ($index, $chunkX, $chunkZ); $level->populateChunk($chunkX, $chunkZ, true); } return true; } /** * @param string $name * * @return bool */ public function isLevelGenerated($name){ if(trim($name) === ""){ return false; } $path = $this->getDataPath() . "worlds/" . $name . "/"; if(!($this->getLevelByName($name) instanceof Level)){ if(LevelProviderManager::getProvider($path) === null){ return false; } /*if(file_exists($path)){ $level = new LevelImport($path); if($level->import() === false){ //Try importing a world return false; } }else{ return false; }*/ } return true; } /** * @param string $variable * @param string $defaultValue * * @return string */ public function getConfigString($variable, $defaultValue = ""){ $v = getopt("", ["$variable::"]); if(isset($v[$variable])){ return (string) $v[$variable]; } return $this->properties->exists($variable) ? $this->properties->get($variable) : $defaultValue; } /** * @param string $variable * @param mixed $defaultValue * * @return mixed */ public function getProperty($variable, $defaultValue = null){ if(!array_key_exists($variable, $this->propertyCache)){ $v = getopt("", ["$variable::"]); if(isset($v[$variable])){ $this->propertyCache[$variable] = $v[$variable]; }else{ $this->propertyCache[$variable] = $this->config->getNested($variable); } } return $this->propertyCache[$variable] === null ? $defaultValue : $this->propertyCache[$variable]; } /** * @param string $variable * @param string $value */ public function setConfigString($variable, $value){ $this->properties->set($variable, $value); } /** * @param string $variable * @param int $defaultValue * * @return int */ public function getConfigInt($variable, $defaultValue = 0){ $v = getopt("", ["$variable::"]); if(isset($v[$variable])){ return (int) $v[$variable]; } return $this->properties->exists($variable) ? (int) $this->properties->get($variable) : (int) $defaultValue; } /** * @param string $variable * @param int $value */ public function setConfigInt($variable, $value){ $this->properties->set($variable, (int) $value); } /** * @param string $variable * @param boolean $defaultValue * * @return boolean */ public function getConfigBoolean($variable, $defaultValue = false){ $v = getopt("", ["$variable::"]); if(isset($v[$variable])){ $value = $v[$variable]; }else{ $value = $this->properties->exists($variable) ? $this->properties->get($variable) : $defaultValue; } if(is_bool($value)){ return $value; } switch(strtolower($value)){ case "on": case "true": case "1": case "yes": return true; } return false; } /** * @param string $variable * @param bool $value */ public function setConfigBool($variable, $value){ $this->properties->set($variable, $value == true ? "1" : "0"); } /** * @param string $name * * @return PluginIdentifiableCommand */ public function getPluginCommand($name){ if(($command = $this->commandMap->getCommand($name)) instanceof PluginIdentifiableCommand){ return $command; }else{ return null; } } /** * @return BanList */ public function getNameBans(){ return $this->banByName; } /** * @return BanList */ public function getIPBans(){ return $this->banByIP; } public function getCIDBans(){ return $this->banByCID; } /** * @param string $name */ public function addOp($name){ $this->operators->set(strtolower($name), true); if(($player = $this->getPlayerExact($name)) !== null){ $player->recalculatePermissions(); } $this->operators->save(true); } /** * @param string $name */ public function removeOp($name){ foreach($this->operators->getAll() as $opName => $dummyValue){ if(strtolower($name) === strtolower($opName)){ $this->operators->remove($opName); } } if(($player = $this->getPlayerExact($name)) !== null){ $player->recalculatePermissions(); } $this->operators->save(); } /** * @param string $name */ public function addWhitelist($name){ $this->whitelist->set(strtolower($name), true); $this->whitelist->save(true); } /** * @param string $name */ public function removeWhitelist($name){ $this->whitelist->remove(strtolower($name)); $this->whitelist->save(); } /** * @param string $name * * @return bool */ public function isWhitelisted($name){ return !$this->hasWhitelist() or $this->whitelist->exists($name, true); } /** * @param string $name * * @return bool */ public function isOp($name){ return $this->operators->exists($name, true); } /** * @return Config */ public function getWhitelisted(){ return $this->whitelist; } /** * @return Config */ public function getOps(){ return $this->operators; } public function reloadWhitelist(){ $this->whitelist->reload(); } /** * @return string[] */ public function getCommandAliases(){ $section = $this->getProperty("aliases"); $result = []; if(is_array($section)){ foreach($section as $key => $value){ $commands = []; if(is_array($value)){ $commands = $value; }else{ $commands[] = $value; } $result[$key] = $commands; } } return $result; } public function getCrashPath(){ return $this->dataPath . "crashdumps/"; } /** * @return Server */ public static function getInstance() : Server{ return self::$instance; } public static function microSleep(int $microseconds){ Server::$sleeper->synchronized(function(int $ms){ Server::$sleeper->wait($ms); }, $microseconds); } public function about(){ $string = "§b ____ | __|_ _ | |__| | _ _(_)_ __ ___ | __| |_ _| | | | | '_ \ / _ \ | |__| | | | | |/\| | | | | | (_) | |____|_|\ \/ \__/\__/_|_| |_|\___ | _| / __| | |___/ |___/ §fA stable §e" . \pocketmine\MINECRAFT_VERSION . "§f fork of §3PocketMine-MP (pmmp)§f. §fModified by §bH§e4§3PM§f. Source code: §3https://github.com/H4PM/Elywing§f "; $this->getLogger()->info($string); } public function loadAdvancedConfig(){ $this->playerMsgType = $this->getAdvancedProperty("server.player-msg-type", self::PLAYER_MSG_TYPE_MESSAGE); $this->playerLoginMsg = $this->getAdvancedProperty("server.login-msg", "§3@player joined the game"); $this->playerLogoutMsg = $this->getAdvancedProperty("server.logout-msg", "§3@player left the game"); $this->weatherEnabled = $this->getAdvancedProperty("level.weather", true); $this->foodEnabled = $this->getAdvancedProperty("player.hunger", true); $this->expEnabled = $this->getAdvancedProperty("player.experience", true); $this->keepInventory = $this->getAdvancedProperty("player.keep-inventory", false); $this->keepExperience = $this->getAdvancedProperty("player.keep-experience", false); $this->netherEnabled = $this->getAdvancedProperty("nether.allow-nether", false); $this->netherName = $this->getAdvancedProperty("nether.level-name", "nether"); $this->weatherRandomDurationMin = $this->getAdvancedProperty("level.weather-random-duration-min", 6000); $this->weatherRandomDurationMax = $this->getAdvancedProperty("level.weather-random-duration-max", 12000); $this->lightningTime = $this->getAdvancedProperty("level.lightning-time", 200); $this->lightningFire = $this->getAdvancedProperty("level.lightning-fire", false); $this->allowSnowGolem = $this->getAdvancedProperty("server.allow-snow-golem", false); $this->allowIronGolem = $this->getAdvancedProperty("server.allow-iron-golem", false); $this->autoClearInv = $this->getAdvancedProperty("player.auto-clear-inventory", true); $this->dserverConfig = [ "enable" => $this->getAdvancedProperty("dserver.enable", false), "queryAutoUpdate" => $this->getAdvancedProperty("dserver.query-auto-update", false), "queryTickUpdate" => $this->getAdvancedProperty("dserver.query-tick-update", true), "motdMaxPlayers" => $this->getAdvancedProperty("dserver.motd-max-players", 0), "queryMaxPlayers" => $this->getAdvancedProperty("dserver.query-max-players", 0), "motdAllPlayers" => $this->getAdvancedProperty("dserver.motd-all-players", false), "queryAllPlayers" => $this->getAdvancedProperty("dserver.query-all-players", false), "motdPlayers" => $this->getAdvancedProperty("dserver.motd-players", false), "queryPlayers" => $this->getAdvancedProperty("dserver.query-players", false), "timer" => $this->getAdvancedProperty("dserver.time", 40), "retryTimes" => $this->getAdvancedProperty("dserver.retry-times", 3), "serverList" => explode(";", $this->getAdvancedProperty("dserver.server-list", "")) ]; $this->redstoneEnabled = $this->getAdvancedProperty("redstone.enable", false); $this->allowFrequencyPulse = $this->getAdvancedProperty("redstone.allow-frequency-pulse", false); $this->pulseFrequency = $this->getAdvancedProperty("redstone.pulse-frequency", 20); $this->getLogger()->setWrite(!$this->getAdvancedProperty("server.disable-log", false)); $this->antiFly = $this->getAdvancedProperty("server.anti-fly", true); $this->asyncChunkRequest = $this->getAdvancedProperty("server.async-chunk-request", true); $this->checkMovement = $this->getAdvancedProperty("server.check-movement", true); $this->limitedCreative = $this->getAdvancedProperty("server.limited-creative", true); $this->chunkRadius = $this->getAdvancedProperty("player.chunk-radius", -1); $this->destroyBlockParticle = $this->getAdvancedProperty("server.destroy-block-particle", true); $this->allowSplashPotion = $this->getAdvancedProperty("server.allow-splash-potion", true); $this->fireSpread = $this->getAdvancedProperty("level.fire-spread", false); $this->advancedCommandSelector = $this->getAdvancedProperty("server.advanced-command-selector", false); $this->anvilEnabled = $this->getAdvancedProperty("enchantment.enable-anvil", true); $this->enchantingTableEnabled = $this->getAdvancedProperty("enchantment.enable-enchanting-table", true); $this->countBookshelf = $this->getAdvancedProperty("enchantment.count-bookshelf", false); $this->allowInstabreak = $this->getAdvancedProperty("server.allow-instabreak", false); $this->allowInventoryCheats = $this->getAdvancedProperty("inventory.allow-cheats", false); } /** * @deprecated Use SynapsePM plugin instead * @return bool */ public function isSynapseEnabled() : bool { return $this->getSynapse() !== null; } /** * @return int * * Get DServer max players */ public function getDServerMaxPlayers(){ return ($this->dserverAllPlayers + $this->getMaxPlayers()); } /** * @return int * * Get DServer all online player count */ public function getDServerOnlinePlayers(){ return ($this->dserverPlayers + count($this->getOnlinePlayers())); } public function isDServerEnabled(){ return $this->dserverConfig["enable"]; } public function updateDServerInfo(){ $this->scheduler->scheduleAsyncTask(new DServerTask($this->dserverConfig["serverList"], $this->dserverConfig["retryTimes"])); } public function getBuild(){ return $this->version->getBuild(); } public function getGameVersion(){ return $this->version->getRelease(); } /** * @param \ClassLoader $autoloader * @param \ThreadedLogger $logger * @param string $filePath * @param string $dataPath * @param string $pluginPath * @param string $defaultLang */ public function __construct(\ClassLoader $autoloader, \ThreadedLogger $logger, $filePath, $dataPath, $pluginPath, $defaultLang = "unknown"){ self::$instance = $this; self::$sleeper = new \Threaded; $this->autoloader = $autoloader; $this->logger = $logger; $this->filePath = $filePath; try{ if(!file_exists($dataPath . "worlds/")){ mkdir($dataPath . "worlds/", 0777); } if(!file_exists($dataPath . "players/")){ mkdir($dataPath . "players/", 0777); } if(!file_exists($pluginPath)){ mkdir($pluginPath, 0777); } if(!file_exists($dataPath . "crashdumps/")){ mkdir($dataPath . "crashdumps/", 0777); } $this->dataPath = realpath($dataPath) . DIRECTORY_SEPARATOR; $this->pluginPath = realpath($pluginPath) . DIRECTORY_SEPARATOR; $this->console = new CommandReader($logger); $version = new VersionString($this->getPocketMineVersion()); $this->version = $version; $this->about(); $this->logger->info("Loading properties and configuration..."); if(!file_exists($this->dataPath . "pocketmine.yml")){ $content = file_get_contents($this->filePath . "src/pocketmine/resources/pocketmine.yml"); @file_put_contents($this->dataPath . "pocketmine.yml", $content); } $this->config = new Config($configPath = $this->dataPath . "pocketmine.yml", Config::YAML, []); $nowLang = $this->getProperty("settings.language", "eng"); //Crashes unsupported builds without the correct configuration if(strpos(\pocketmine\VERSION, "unsupported") !== false and getenv("GITLAB_CI") === false){ if($this->getProperty("settings.enable-testing", false) !== true){ throw new ServerException("This build is not intended for production use. You may set 'settings.enable-testing: true' under pocketmine.yml to allow use of non-production builds. Do so at your own risk and ONLY if you know what you are doing."); }else{ $this->logger->warning("You are using an unsupported build. Do not use this build in a production environment."); } } if($defaultLang != "unknown" and $nowLang != $defaultLang){ @file_put_contents($configPath, str_replace('language: "' . $nowLang . '"', 'language: "' . $defaultLang . '"', file_get_contents($configPath))); $this->config->reload(); unset($this->propertyCache["settings.language"]); } $lang = $this->getProperty("settings.language", BaseLang::FALLBACK_LANGUAGE); if(file_exists($this->filePath . "src/pocketmine/resources/genisys_$lang.yml")){ $content = file_get_contents($file = $this->filePath . "src/pocketmine/resources/genisys_$lang.yml"); }else{ $content = file_get_contents($file = $this->filePath . "src/pocketmine/resources/genisys_eng.yml"); } if(!file_exists($this->dataPath . "genisys.yml")){ @file_put_contents($this->dataPath . "genisys.yml", $content); } $internelConfig = new Config($file, Config::YAML, []); $this->advancedConfig = new Config($this->dataPath . "genisys.yml", Config::YAML, []); $cfgVer = $this->getAdvancedProperty("config.version", 0, $internelConfig); $advVer = $this->getAdvancedProperty("config.version", 0); $this->loadAdvancedConfig(); $this->properties = new Config($this->dataPath . "server.properties", Config::PROPERTIES, [ "motd" => "Minecraft: PE Server", "server-port" => 19132, "white-list" => false, "announce-player-achievements" => true, "spawn-protection" => 16, "max-players" => 20, "allow-flight" => false, "spawn-animals" => true, "spawn-mobs" => true, "gamemode" => 0, "force-gamemode" => false, "hardcore" => false, "pvp" => true, "difficulty" => 1, "generator-settings" => "", "level-name" => "world", "level-seed" => "", "level-type" => "DEFAULT", "enable-query" => true, "enable-rcon" => false, "rcon.password" => substr(base64_encode(random_bytes(20)), 3, 10), "auto-save" => true, "online-mode" => false, ]); $onlineMode = $this->getConfigBoolean("online-mode", false); if(!extension_loaded("openssl")){ $this->logger->warning("The OpenSSL extension is not loaded, and this is required for XBOX authentication to work. If you want to use Xbox Live auth, please update your PHP binaries at itxtech.org/genisys/get/, or recompile PHP with the OpenSSL extension."); $this->setConfigBool("online-mode", false); }elseif(!$onlineMode){ $this->logger->warning("SERVER IS RUNNING IN OFFLINE/INSECURE MODE!"); $this->logger->warning("The server will make no attempt to authenticate usernames. Beware."); $this->logger->warning("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."); $this->logger->warning("To change this, set \"online-mode\" to \"true\" in the server.properties file."); } $this->forceLanguage = $this->getProperty("settings.force-language", false); $this->baseLang = new BaseLang($this->getProperty("settings.language", BaseLang::FALLBACK_LANGUAGE)); $this->logger->info($this->getLanguage()->translateString("language.selected", [$this->getLanguage()->getName(), $this->getLanguage()->getLang()])); $this->memoryManager = new MemoryManager($this); if(($poolSize = $this->getProperty("settings.async-workers", "auto")) === "auto"){ $poolSize = ServerScheduler::$WORKERS; $processors = Utils::getCoreCount() - 2; if($processors > 0){ $poolSize = max(1, $processors); } } ServerScheduler::$WORKERS = $poolSize; if($this->getProperty("network.batch-threshold", 256) >= 0){ Network::$BATCH_THRESHOLD = (int) $this->getProperty("network.batch-threshold", 256); }else{ Network::$BATCH_THRESHOLD = -1; } $this->networkCompressionLevel = $this->getProperty("network.compression-level", 7); $this->networkCompressionAsync = $this->getProperty("network.async-compression", true); $this->autoTickRate = (bool) $this->getProperty("level-settings.auto-tick-rate", true); $this->autoTickRateLimit = (int) $this->getProperty("level-settings.auto-tick-rate-limit", 20); $this->alwaysTickPlayers = (int) $this->getProperty("level-settings.always-tick-players", false); $this->baseTickRate = (int) $this->getProperty("level-settings.base-tick-rate", 1); $this->scheduler = new ServerScheduler(); if($this->getConfigBoolean("enable-rcon", false) === true){ $this->rcon = new RCON($this, $this->getConfigString("rcon.password", ""), $this->getConfigInt("rcon.port", $this->getPort()), ($ip = $this->getIp()) != "" ? $ip : "0.0.0.0", $this->getConfigInt("rcon.threads", 1), $this->getConfigInt("rcon.clients-per-thread", 50)); } $this->entityMetadata = new EntityMetadataStore(); $this->playerMetadata = new PlayerMetadataStore(); $this->levelMetadata = new LevelMetadataStore(); $this->operators = new Config($this->dataPath . "ops.txt", Config::ENUM); $this->whitelist = new Config($this->dataPath . "white-list.txt", Config::ENUM); if(file_exists($this->dataPath . "banned.txt") and !file_exists($this->dataPath . "banned-players.txt")){ @rename($this->dataPath . "banned.txt", $this->dataPath . "banned-players.txt"); } @touch($this->dataPath . "banned-players.txt"); $this->banByName = new BanList($this->dataPath . "banned-players.txt"); $this->banByName->load(); @touch($this->dataPath . "banned-ips.txt"); $this->banByIP = new BanList($this->dataPath . "banned-ips.txt"); $this->banByIP->load(); @touch($this->dataPath . "banned-cids.txt"); $this->banByCID = new BanList($this->dataPath . "banned-cids.txt"); $this->banByCID->load(); $this->maxPlayers = $this->getConfigInt("max-players", 20); $this->setAutoSave($this->getConfigBoolean("auto-save", true)); if($this->getConfigBoolean("hardcore", false) === true and $this->getDifficulty() < 3){ $this->setConfigInt("difficulty", 3); } define("pocketmine\\DEBUG", (int) $this->getProperty("debug.level", 1)); if($this->logger instanceof MainLogger){ $this->logger->setLogDebug(\pocketmine\DEBUG > 1); } if(\pocketmine\DEBUG >= 0){ @cli_set_process_title($this->getName() . " " . $this->getPocketMineVersion()); } $this->logger->info($this->getLanguage()->translateString("pocketmine.server.networkStart", [$this->getIp() === "" ? "*" : $this->getIp(), $this->getPort()])); $this->serverID = Utils::getMachineUniqueId($this->getIp() . $this->getPort()); $this->getLogger()->debug("Server unique id: " . $this->getServerUniqueId()); $this->getLogger()->debug("Machine unique id: " . Utils::getMachineUniqueId()); $this->network = new Network($this); $this->network->setName($this->getMotd()); $this->logger->info($this->getLanguage()->translateString("pocketmine.server.license", [$this->getName()])); Timings::init(); $this->consoleSender = new ConsoleCommandSender(); $this->commandMap = new SimpleCommandMap($this); Entity::init(); Tile::init(); InventoryType::init(); Block::init(); Enchantment::init(); Item::init(); Biome::init(); Effect::init(); Attribute::init(); EnchantmentLevelTable::init(); Color::init(); $this->craftingManager = new CraftingManager(); $this->pluginManager = new PluginManager($this, $this->commandMap); $this->pluginManager->subscribeToPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this->consoleSender); $this->pluginManager->setUseTimings($this->getProperty("settings.enable-profiling", false)); $this->profilingTickRate = (float) $this->getProperty("settings.profile-report-trigger", 20); $this->pluginManager->registerInterface(PharPluginLoader::class); $this->pluginManager->registerInterface(ScriptPluginLoader::class); //set_exception_handler([$this, "exceptionHandler"]); register_shutdown_function([$this, "crashDump"]); $this->queryRegenerateTask = new QueryRegenerateEvent($this, 5); $this->network->registerInterface(new RakLibInterface($this)); $this->pluginManager->loadPlugins($this->pluginPath); $this->enablePlugins(PluginLoadOrder::STARTUP); LevelProviderManager::addProvider(Anvil::class); LevelProviderManager::addProvider(McRegion::class); LevelProviderManager::addProvider(PMAnvil::class); Generator::addGenerator(Flat::class, "flat"); Generator::addGenerator(Normal::class, "normal"); Generator::addGenerator(Normal::class, "default"); Generator::addGenerator(Nether::class, "hell"); Generator::addGenerator(Nether::class, "nether"); Generator::addGenerator(Void::class, "void"); Generator::addGenerator(Normal2::class, "normal2"); foreach((array) $this->getProperty("worlds", []) as $name => $worldSetting){ if($this->loadLevel($name) === false){ $seed = $this->getProperty("worlds.$name.seed", time()); $options = explode(":", $this->getProperty("worlds.$name.generator", Generator::getGenerator("default"))); $generator = Generator::getGenerator(array_shift($options)); if(count($options) > 0){ $options = [ "preset" => implode(":", $options), ]; }else{ $options = []; } $this->generateLevel($name, $seed, $generator, $options); } } if($this->getDefaultLevel() === null){ $default = $this->getConfigString("level-name", "world"); if(trim($default) == ""){ $this->getLogger()->warning("level-name cannot be null, using default"); $default = "world"; $this->setConfigString("level-name", "world"); } if($this->loadLevel($default) === false){ $seed = getopt("", ["level-seed::"])["level-seed"] ?? $this->properties->get("level-seed", time()); if(!is_numeric($seed) or bccomp($seed, "9223372036854775807") > 0){ $seed = Utils::javaStringHash($seed); }elseif(PHP_INT_SIZE === 8){ $seed = (int) $seed; } $this->generateLevel($default, $seed === 0 ? time() : $seed); } $this->setDefaultLevel($this->getLevelByName($default)); } $this->properties->save(true); if(!($this->getDefaultLevel() instanceof Level)){ $this->getLogger()->emergency($this->getLanguage()->translateString("pocketmine.level.defaultError")); $this->forceShutdown(); return; } if($this->netherEnabled){ if(!$this->loadLevel($this->netherName)){ //$this->logger->info("Level nether load".$this->netherName); $this->generateLevel($this->netherName, time(), Generator::getGenerator("nether")); } $this->netherLevel = $this->getLevelByName($this->netherName); } if($this->getProperty("ticks-per.autosave", 6000) > 0){ $this->autoSaveTicks = (int) $this->getProperty("ticks-per.autosave", 6000); } $this->enablePlugins(PluginLoadOrder::POSTWORLD); if($this->dserverConfig["enable"] and ($this->getAdvancedProperty("dserver.server-list", "") != "")) $this->scheduler->scheduleRepeatingTask(new CallbackTask([ $this, "updateDServerInfo" ]), $this->dserverConfig["timer"]); if($cfgVer > $advVer){ $this->logger->notice("Your genisys.yml needs update"); $this->logger->notice("Current Version: $advVer Latest Version: $cfgVer"); } $this->generateRecipeList(); $this->start(); }catch(\Throwable $e){ $this->exceptionHandler($e); } } /** * @deprecated Use SynapsePM plugin instead * @return Synapse|null */ public function getSynapse(){ $plugin = $this->pluginManager->getPlugin('SynapsePM'); if ($plugin === null or $plugin->isDisabled()) { return null; } return $plugin->getSynapse(); } /** * @param string $message * @param Player[]|null $recipients * * @return int */ public function broadcastMessage($message, $recipients = null) : int{ if(!is_array($recipients)){ return $this->broadcast($message, self::BROADCAST_CHANNEL_USERS); } /** @var Player[] $recipients */ foreach($recipients as $recipient){ $recipient->sendMessage($message); } return count($recipients); } /** * @param string $tip * @param Player[]|null $recipients * * @return int */ public function broadcastTip(string $tip, $recipients = null) : int{ if(!is_array($recipients)){ /** @var Player[] $recipients */ $recipients = []; foreach($this->pluginManager->getPermissionSubscriptions(self::BROADCAST_CHANNEL_USERS) as $permissible){ if($permissible instanceof Player and $permissible->hasPermission(self::BROADCAST_CHANNEL_USERS)){ $recipients[spl_object_hash($permissible)] = $permissible; // do not send messages directly, or some might be repeated } } } /** @var Player[] $recipients */ foreach($recipients as $recipient){ $recipient->sendTip($tip); } return count($recipients); } /** * @param string $popup * @param Player[]|null $recipients * * @return int */ public function broadcastPopup(string $popup, $recipients = null) : int{ if(!is_array($recipients)){ /** @var Player[] $recipients */ $recipients = []; foreach($this->pluginManager->getPermissionSubscriptions(self::BROADCAST_CHANNEL_USERS) as $permissible){ if($permissible instanceof Player and $permissible->hasPermission(self::BROADCAST_CHANNEL_USERS)){ $recipients[spl_object_hash($permissible)] = $permissible; // do not send messages directly, or some might be repeated } } } /** @var Player[] $recipients */ foreach($recipients as $recipient){ $recipient->sendPopup($popup); } return count($recipients); } /** * @param string $message * @param string $permissions * * @return int */ public function broadcast($message, string $permissions) : int{ /** @var CommandSender[] $recipients */ $recipients = []; foreach(explode(";", $permissions) as $permission){ foreach($this->pluginManager->getPermissionSubscriptions($permission) as $permissible){ if($permissible instanceof CommandSender and $permissible->hasPermission($permission)){ $recipients[spl_object_hash($permissible)] = $permissible; // do not send messages directly, or some might be repeated } } } foreach($recipients as $recipient){ $recipient->sendMessage($message); } return count($recipients); } /** * Broadcasts a Minecraft packet to a list of players * * @param Player[] $players * @param DataPacket $packet */ public function broadcastPacket(array $players, DataPacket $packet){ $packet->encode(); $packet->isEncoded = true; if(Network::$BATCH_THRESHOLD >= 0 and strlen($packet->buffer) >= Network::$BATCH_THRESHOLD){ $this->batchPackets($players, [$packet->buffer], false); return; } foreach($players as $player){ $player->dataPacket($packet); } if(isset($packet->__encapsulatedPacket)){ unset($packet->__encapsulatedPacket); } } /** * Broadcasts a list of packets in a batch to a list of players * * @param Player[] $players * @param DataPacket[]|string $packets * @param bool $forceSync */ public function batchPackets(array $players, array $packets, $forceSync = false){ Timings::$playerNetworkTimer->startTiming(); $str = ""; foreach($packets as $p){ if($p instanceof DataPacket){ if(!$p->isEncoded){ $p->encode(); } $str .= Binary::writeUnsignedVarInt(strlen($p->buffer)) . $p->buffer; }else{ $str .= Binary::writeUnsignedVarInt(strlen($p)) . $p; } } $targets = []; foreach($players as $p){ if($p->isConnected()){ $targets[] = $this->identifiers[spl_object_hash($p)]; } } if(!$forceSync and $this->networkCompressionAsync){ $task = new CompressBatchedTask($str, $targets, $this->networkCompressionLevel); $this->getScheduler()->scheduleAsyncTask($task); }else{ $this->broadcastPacketsCallback(zlib_encode($str, ZLIB_ENCODING_DEFLATE, $this->networkCompressionLevel), $targets); } Timings::$playerNetworkTimer->stopTiming(); } public function broadcastPacketsCallback($data, array $identifiers){ $pk = new BatchPacket(); $pk->payload = $data; $pk->encode(); $pk->isEncoded = true; foreach($identifiers as $i){ if(isset($this->players[$i])){ $this->players[$i]->dataPacket($pk); } } } /** * @param int $type */ public function enablePlugins(int $type){ foreach($this->pluginManager->getPlugins() as $plugin){ if(!$plugin->isEnabled() and $plugin->getDescription()->getOrder() === $type){ $this->enablePlugin($plugin); } } if($type === PluginLoadOrder::POSTWORLD){ $this->commandMap->registerServerAliases(); DefaultPermissions::registerCorePermissions(); } } /** * @param Plugin $plugin */ public function enablePlugin(Plugin $plugin){ $this->pluginManager->enablePlugin($plugin); } public function disablePlugins(){ $this->pluginManager->disablePlugins(); } public function checkConsole(){ Timings::$serverCommandTimer->startTiming(); if(($line = $this->console->getLine()) !== null){ $this->pluginManager->callEvent($ev = new ServerCommandEvent($this->consoleSender, $line)); if(!$ev->isCancelled()){ $this->dispatchCommand($ev->getSender(), $ev->getCommand()); } } Timings::$serverCommandTimer->stopTiming(); } /** * Executes a command from a CommandSender * * @param CommandSender $sender * @param string $commandLine * * @return bool * * @throws \Throwable */ public function dispatchCommand(CommandSender $sender, $commandLine){ if($this->commandMap->dispatch($sender, $commandLine)){ return true; } $sender->sendMessage(new TranslationContainer(TextFormat::GOLD . "%commands.generic.notFound")); return false; } public function reload(){ $this->logger->info("Saving levels..."); foreach($this->levels as $level){ $level->save(); } $this->pluginManager->disablePlugins(); $this->pluginManager->clearPlugins(); $this->commandMap->clearCommands(); $this->logger->info("Reloading properties..."); $this->properties->reload(); $this->advancedConfig->reload(); $this->loadAdvancedConfig(); $this->maxPlayers = $this->getConfigInt("max-players", 20); if($this->getConfigBoolean("hardcore", false) === true and $this->getDifficulty() < 3){ $this->setConfigInt("difficulty", 3); } $this->banByIP->load(); $this->banByName->load(); $this->banByCID->load(); $this->reloadWhitelist(); $this->operators->reload(); $this->memoryManager->doObjectCleanup(); foreach($this->getIPBans()->getEntries() as $entry){ $this->getNetwork()->blockAddress($entry->getName(), -1); } $this->pluginManager->registerInterface(PharPluginLoader::class); $this->pluginManager->registerInterface(ScriptPluginLoader::class); $this->pluginManager->loadPlugins($this->pluginPath); $this->enablePlugins(PluginLoadOrder::STARTUP); $this->enablePlugins(PluginLoadOrder::POSTWORLD); TimingsHandler::reload(); } /** * Shutdowns the server correctly * @param bool $restart * @param string $msg */ public function shutdown(bool $restart = false, string $msg = ""){ /*if($this->isRunning){ $killer = new ServerKiller(90); $killer->start(); $killer->kill(); }*/ $this->isRunning = false; if($msg != ""){ $this->propertyCache["settings.shutdown-message"] = $msg; } } public function forceShutdown(){ if($this->hasStopped){ return; } try{ if(!$this->isRunning()){ $this->sendUsage(SendUsageTask::TYPE_CLOSE); } $this->hasStopped = true; $this->shutdown(); if($this->rcon instanceof RCON){ $this->rcon->stop(); } if($this->getProperty("network.upnp-forwarding", false) === true){ $this->logger->info("[UPnP] Removing port forward..."); UPnP::RemovePortForward($this->getPort()); } $this->getLogger()->debug("Disabling all plugins"); $this->pluginManager->disablePlugins(); foreach($this->players as $player){ $player->close($player->getLeaveMessage(), $this->getProperty("settings.shutdown-message", "Server closed")); } $this->getLogger()->debug("Unloading all levels"); foreach($this->getLevels() as $level){ $this->unloadLevel($level, true); } $this->getLogger()->debug("Removing event handlers"); HandlerList::unregisterAll(); $this->getLogger()->debug("Stopping all tasks"); $this->scheduler->cancelAllTasks(); $this->scheduler->mainThreadHeartbeat(PHP_INT_MAX); $this->getLogger()->debug("Saving properties"); $this->properties->save(); $this->getLogger()->debug("Closing console"); $this->console->shutdown(); $this->console->notify(); $this->getLogger()->debug("Stopping network interfaces"); foreach($this->network->getInterfaces() as $interface){ $interface->shutdown(); $this->network->unregisterInterface($interface); } //$this->memoryManager->doObjectCleanup(); gc_collect_cycles(); }catch(\Throwable $e){ $this->logger->logException($e); $this->logger->emergency("Crashed while crashing, killing process"); @kill(getmypid()); } } public function getQueryInformation(){ return $this->queryRegenerateTask; } /** * Starts the PocketMine-MP server and starts processing ticks and packets */ public function start(){ if($this->getConfigBoolean("enable-query", true) === true){ $this->queryHandler = new QueryHandler(); } foreach($this->getIPBans()->getEntries() as $entry){ $this->network->blockAddress($entry->getName(), -1); } if($this->getProperty("settings.send-usage", true)){ $this->sendUsageTicker = 6000; $this->sendUsage(SendUsageTask::TYPE_OPEN); } if($this->getProperty("network.upnp-forwarding", false) == true){ $this->logger->info("[UPnP] Trying to port forward..."); UPnP::PortForward($this->getPort()); } $this->tickCounter = 0; if(function_exists("pcntl_signal")){ pcntl_signal(SIGTERM, [$this, "handleSignal"]); pcntl_signal(SIGINT, [$this, "handleSignal"]); pcntl_signal(SIGHUP, [$this, "handleSignal"]); $this->dispatchSignals = true; } $this->logger->info($this->getLanguage()->translateString("pocketmine.server.defaultGameMode", [self::getGamemodeString($this->getGamemode())])); $this->logger->info($this->getLanguage()->translateString("pocketmine.server.startFinished", [round(microtime(true) - \pocketmine\START_TIME, 3)])); $this->tickProcessor(); $this->forceShutdown(); gc_collect_cycles(); } public function handleSignal($signo){ if($signo === SIGTERM or $signo === SIGINT or $signo === SIGHUP){ $this->shutdown(); } } public function exceptionHandler(\Throwable $e, $trace = null){ if($e === null){ return; } global $lastError; if($trace === null){ $trace = $e->getTrace(); } $errstr = $e->getMessage(); $errfile = $e->getFile(); $errno = $e->getCode(); $errline = $e->getLine(); $type = ($errno === E_ERROR or $errno === E_USER_ERROR) ? \LogLevel::ERROR : (($errno === E_USER_WARNING or $errno === E_WARNING) ? \LogLevel::WARNING : \LogLevel::NOTICE); if(($pos = strpos($errstr, "\n")) !== false){ $errstr = substr($errstr, 0, $pos); } $errfile = cleanPath($errfile); if($this->logger instanceof MainLogger){ $this->logger->logException($e, $trace); } $lastError = [ "type" => $type, "message" => $errstr, "fullFile" => $e->getFile(), "file" => $errfile, "line" => $errline, "trace" => @getTrace(1, $trace) ]; global $lastExceptionError, $lastError; $lastExceptionError = $lastError; $this->crashDump(); } public function crashDump(){ if($this->isRunning === false){ return; } if($this->sendUsageTicker > 0){ $this->sendUsage(SendUsageTask::TYPE_CLOSE); } $this->hasStopped = false; ini_set("error_reporting", 0); ini_set("memory_limit", -1); //Fix error dump not dumped on memory problems $this->logger->emergency($this->getLanguage()->translateString("pocketmine.crash.create")); try{ $dump = new CrashDump($this); }catch(\Throwable $e){ $this->logger->critical($this->getLanguage()->translateString("pocketmine.crash.error", $e->getMessage())); return; } $this->logger->emergency($this->getLanguage()->translateString("pocketmine.crash.submit", [$dump->getPath()])); if($this->getProperty("auto-report.enabled", true) !== false){ $report = true; $plugin = $dump->getData()["plugin"]; if(is_string($plugin)){ $p = $this->pluginManager->getPlugin($plugin); if($p instanceof Plugin and !($p->getPluginLoader() instanceof PharPluginLoader)){ $report = false; } }elseif(\Phar::running(true) == ""){ $report = false; } if($dump->getData()["error"]["type"] === "E_PARSE" or $dump->getData()["error"]["type"] === "E_COMPILE_ERROR"){ $report = false; } if($report){ $reply = Utils::postURL("http://" . $this->getProperty("auto-report.host", "crash.pocketmine.net") . "/submit/api", [ "report" => "yes", "name" => $this->getName() . " " . $this->getPocketMineVersion(), "email" => "crash@pocketmine.net", "reportPaste" => base64_encode($dump->getEncodedData()) ]); if(($data = json_decode($reply)) !== false and isset($data->crashId)){ $reportId = $data->crashId; $reportUrl = $data->crashUrl; $this->logger->emergency($this->getLanguage()->translateString("pocketmine.crash.archive", [$reportUrl, $reportId])); } } } //$this->checkMemory(); //$dump .= "Memory Usage Tracking: \r\n" . chunk_split(base64_encode(gzdeflate(implode(";", $this->memoryStats), 9))) . "\r\n"; $this->forceShutdown(); $this->isRunning = false; @kill(getmypid()); exit(1); } public function __debugInfo(){ return []; } private function tickProcessor(){ $this->nextTick = microtime(true); while($this->isRunning){ $this->tick(); $next = $this->nextTick - 0.0001; if($next > microtime(true)){ @time_sleep_until($next); } } } public function onPlayerLogin(Player $player){ if($this->sendUsageTicker > 0){ $this->uniquePlayers[$player->getRawUniqueId()] = $player->getRawUniqueId(); } $this->sendFullPlayerListData($player); $this->sendRecipeList($player); } public function addPlayer($identifier, Player $player){ $this->players[$identifier] = $player; $this->identifiers[spl_object_hash($player)] = $identifier; } public function addOnlinePlayer(Player $player){ $this->playerList[$player->getRawUniqueId()] = $player; $this->updatePlayerListData($player->getUniqueId(), $player->getId(), $player->getDisplayName(), $player->getSkinId(), $player->getSkinData()); } public function removeOnlinePlayer(Player $player){ if(isset($this->playerList[$player->getRawUniqueId()])){ unset($this->playerList[$player->getRawUniqueId()]); $pk = new PlayerListPacket(); $pk->type = PlayerListPacket::TYPE_REMOVE; $pk->entries[] = [$player->getUniqueId()]; $this->broadcastPacket($this->playerList, $pk); } } public function updatePlayerListData(UUID $uuid, $entityId, $name, $skinId, $skinData, array $players = null){ $pk = new PlayerListPacket(); $pk->type = PlayerListPacket::TYPE_ADD; $pk->entries[] = [$uuid, $entityId, $name, $skinId, $skinData]; $this->broadcastPacket($players === null ? $this->playerList : $players, $pk); } public function removePlayerListData(UUID $uuid, array $players = null){ $pk = new PlayerListPacket(); $pk->type = PlayerListPacket::TYPE_REMOVE; $pk->entries[] = [$uuid]; $this->broadcastPacket($players === null ? $this->playerList : $players, $pk); } public function sendFullPlayerListData(Player $p){ $pk = new PlayerListPacket(); $pk->type = PlayerListPacket::TYPE_ADD; foreach($this->playerList as $player){ if($p === $player){ continue; //fixes duplicates } $pk->entries[] = [$player->getUniqueId(), $player->getId(), $player->getDisplayName(), $player->getSkinId(), $player->getSkinData()]; } $p->dataPacket($pk); } public function generateRecipeList(){ $pk = new CraftingDataPacket(); $pk->cleanRecipes = true; foreach($this->getCraftingManager()->getRecipes() as $recipe){ if($recipe instanceof ShapedRecipe){ $pk->addShapedRecipe($recipe); }elseif($recipe instanceof ShapelessRecipe){ $pk->addShapelessRecipe($recipe); } } foreach($this->getCraftingManager()->getFurnaceRecipes() as $recipe){ $pk->addFurnaceRecipe($recipe); } $pk->encode(); $pk->isEncoded = true; $this->recipeList = $pk; } public function sendRecipeList(Player $p){ $p->dataPacket($this->recipeList); } private function checkTickUpdates($currentTick, $tickTime){ foreach($this->players as $p){ if(!$p->loggedIn and ($tickTime - $p->creationTime) >= 10){ $p->close("", "Login timeout"); }elseif($this->alwaysTickPlayers){ $p->onUpdate($currentTick); } } //Do level ticks foreach($this->getLevels() as $level){ if($level->getTickRate() > $this->baseTickRate and --$level->tickRateCounter > 0){ continue; } try{ $levelTime = microtime(true); $level->doTick($currentTick); $tickMs = (microtime(true) - $levelTime) * 1000; $level->tickRateTime = $tickMs; if($this->autoTickRate){ if($tickMs < 50 and $level->getTickRate() > $this->baseTickRate){ $level->setTickRate($r = $level->getTickRate() - 1); if($r > $this->baseTickRate){ $level->tickRateCounter = $level->getTickRate(); } $this->getLogger()->debug("Raising level \"" . $level->getName() . "\" tick rate to " . $level->getTickRate() . " ticks"); }elseif($tickMs >= 50){ if($level->getTickRate() === $this->baseTickRate){ $level->setTickRate(max($this->baseTickRate + 1, min($this->autoTickRateLimit, floor($tickMs / 50)))); $this->getLogger()->debug("Level \"" . $level->getName() . "\" took " . round($tickMs, 2) . "ms, setting tick rate to " . $level->getTickRate() . " ticks"); }elseif(($tickMs / $level->getTickRate()) >= 50 and $level->getTickRate() < $this->autoTickRateLimit){ $level->setTickRate($level->getTickRate() + 1); $this->getLogger()->debug("Level \"" . $level->getName() . "\" took " . round($tickMs, 2) . "ms, setting tick rate to " . $level->getTickRate() . " ticks"); } $level->tickRateCounter = $level->getTickRate(); } } }catch(\Throwable $e){ $this->logger->critical($this->getLanguage()->translateString("pocketmine.level.tickError", [$level->getName(), $e->getMessage()])); if(\pocketmine\DEBUG > 1 and $this->logger instanceof MainLogger){ $this->logger->logException($e); } } } } public function doAutoSave(){ if($this->getAutoSave()){ Timings::$worldSaveTimer->startTiming(); foreach($this->players as $index => $player){ if($player->isOnline()){ $player->save(true); }elseif(!$player->isConnected()){ $this->removePlayer($player); } } foreach($this->getLevels() as $level){ $level->save(false); } Timings::$worldSaveTimer->stopTiming(); } } public function sendUsage($type = SendUsageTask::TYPE_STATUS){ $this->scheduler->scheduleAsyncTask(new SendUsageTask($this, $type, $this->uniquePlayers)); $this->uniquePlayers = []; } /** * @return BaseLang */ public function getLanguage(){ return $this->baseLang; } /** * @return bool */ public function isLanguageForced(){ return $this->forceLanguage; } /** * @return Network */ public function getNetwork(){ return $this->network; } /** * @return MemoryManager */ public function getMemoryManager(){ return $this->memoryManager; } private function titleTick(){ if(!Terminal::hasFormattingCodes()){ return; } $d = Utils::getRealMemoryUsage(); $u = Utils::getMemoryUsage(true); $usage = round(($u[0] / 1024) / 1024, 2) . "/" . round(($d[0] / 1024) / 1024, 2) . "/" . round(($u[1] / 1024) / 1024, 2) . "/" . round(($u[2] / 1024) / 1024, 2) . " MB @ " . Utils::getThreadCount() . " threads"; echo "\x1b]0;" . $this->getName() . $this->getFormattedVersion("-") . " | Online " . count($this->players) . "/" . $this->getMaxPlayers() . " | Memory " . $usage . " | U " . round($this->network->getUpload() / 1024, 2) . " D " . round($this->network->getDownload() / 1024, 2) . " kB/s | TPS " . $this->getTicksPerSecondAverage() . " | Load " . $this->getTickUsageAverage() . "%\x07"; $this->network->resetStatistics(); } /** * @param string $address * @param int $port * @param string $payload * * TODO: move this to Network */ public function handlePacket($address, $port, $payload){ try{ if(strlen($payload) > 2 and substr($payload, 0, 2) === "\xfe\xfd" and $this->queryHandler instanceof QueryHandler){ $this->queryHandler->handle($address, $port, $payload); } }catch(\Throwable $e){ if(\pocketmine\DEBUG > 1){ if($this->logger instanceof MainLogger){ $this->logger->logException($e); } } $this->getNetwork()->blockAddress($address, 600); } //TODO: add raw packet events } /** * @param $variable * @param null $defaultValue * @param Config|null $cfg * @return bool|mixed|null */ public function getAdvancedProperty($variable, $defaultValue = null, Config $cfg = null){ $vars = explode(".", $variable); $base = array_shift($vars); if($cfg == null) $cfg = $this->advancedConfig; if($cfg->exists($base)){ $base = $cfg->get($base); }else{ return $defaultValue; } while(count($vars) > 0){ $baseKey = array_shift($vars); if(is_array($base) and isset($base[$baseKey])){ $base = $base[$baseKey]; }else{ return $defaultValue; } } return $base; } public function updateQuery(){ try{ $this->getPluginManager()->callEvent($this->queryRegenerateTask = new QueryRegenerateEvent($this, 5)); if($this->queryHandler !== null){ $this->queryHandler->regenerateInfo(); } }catch(\Throwable $e){ $this->logger->logException($e); } } /** * Tries to execute a server tick */ private function tick(){ $tickTime = microtime(true); if(($tickTime - $this->nextTick) < -0.025){ //Allow half a tick of diff return false; } Timings::$serverTickTimer->startTiming(); ++$this->tickCounter; $this->checkConsole(); Timings::$connectionTimer->startTiming(); $this->network->processInterfaces(); if($this->rcon !== null){ $this->rcon->check(); } Timings::$connectionTimer->stopTiming(); Timings::$schedulerTimer->startTiming(); $this->scheduler->mainThreadHeartbeat($this->tickCounter); Timings::$schedulerTimer->stopTiming(); $this->checkTickUpdates($this->tickCounter, $tickTime); foreach($this->players as $player){ $player->checkNetwork(); } if(($this->tickCounter & 0b1111) === 0){ $this->titleTick(); $this->maxTick = 20; $this->maxUse = 0; if(($this->tickCounter & 0b111111111) === 0){ if(($this->dserverConfig["enable"] and $this->dserverConfig["queryTickUpdate"]) or !$this->dserverConfig["enable"]){ $this->updateQuery(); } } $this->getNetwork()->updateName(); } if($this->autoSave and ++$this->autoSaveTicker >= $this->autoSaveTicks){ $this->autoSaveTicker = 0; $this->doAutoSave(); } /*if($this->sendUsageTicker > 0 and --$this->sendUsageTicker === 0){ $this->sendUsageTicker = 6000; $this->sendUsage(SendUsageTask::TYPE_STATUS); }*/ if(($this->tickCounter % 100) === 0){ foreach($this->levels as $level){ $level->clearCache(); } if($this->getTicksPerSecondAverage() < 1){ $this->logger->warning($this->getLanguage()->translateString("pocketmine.server.tickOverload")); } } if($this->dispatchSignals and $this->tickCounter % 5 === 0){ pcntl_signal_dispatch(); } $this->getMemoryManager()->check(); Timings::$serverTickTimer->stopTiming(); $now = microtime(true); $tick = min(20, 1 / max(0.001, $now - $tickTime)); $use = min(1, ($now - $tickTime) / 0.05); //TimingsHandler::tick($tick <= $this->profilingTickRate); if($this->maxTick > $tick){ $this->maxTick = $tick; } if($this->maxUse < $use){ $this->maxUse = $use; } array_shift($this->tickAverage); $this->tickAverage[] = $tick; array_shift($this->useAverage); $this->useAverage[] = $use; if(($this->nextTick - $tickTime) < -1){ $this->nextTick = $tickTime; }else{ $this->nextTick += 0.05; } return true; } } ================================================ FILE: src/pocketmine/Thread.php ================================================ classLoader; } public function setClassLoader(\ClassLoader $loader = null){ if($loader === null){ $loader = Server::getInstance()->getLoader(); } $this->classLoader = $loader; } public function registerClassLoader(){ if(!interface_exists("ClassLoader", false)){ require(\pocketmine\PATH . "src/spl/ClassLoader.php"); require(\pocketmine\PATH . "src/spl/BaseClassLoader.php"); require(\pocketmine\PATH . "src/pocketmine/CompatibleClassLoader.php"); } if($this->classLoader !== null){ $this->classLoader->register(true); } } public function start(int $options = PTHREADS_INHERIT_ALL){ ThreadManager::getInstance()->add($this); if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated()){ if($this->getClassLoader() === null){ $this->setClassLoader(); } return parent::start($options); } return false; } /** * Stops the thread using the best way possible. Try to stop it yourself before calling this. */ public function quit(){ $this->isKilled = true; $this->notify(); if(!$this->isJoined()){ if(!$this->isTerminated()){ $this->join(); } } ThreadManager::getInstance()->remove($this); } public function getThreadName(){ return (new \ReflectionClass($this))->getShortName(); } } ================================================ FILE: src/pocketmine/ThreadManager.php ================================================ {spl_object_hash($thread)} = $thread; } } /** * @param Worker|Thread $thread */ public function remove($thread){ if($thread instanceof Thread or $thread instanceof Worker){ unset($this->{spl_object_hash($thread)}); } } /** * @return Worker[]|Thread[] */ public function getAll(){ $array = []; foreach($this as $key => $thread){ $array[$key] = $thread; } return $array; } } ================================================ FILE: src/pocketmine/Worker.php ================================================ classLoader; } public function setClassLoader(\ClassLoader $loader = null){ if($loader === null){ $loader = Server::getInstance()->getLoader(); } $this->classLoader = $loader; } public function registerClassLoader(){ if(!interface_exists("ClassLoader", false)){ require(\pocketmine\PATH . "src/spl/ClassLoader.php"); require(\pocketmine\PATH . "src/spl/BaseClassLoader.php"); require(\pocketmine\PATH . "src/pocketmine/CompatibleClassLoader.php"); } if($this->classLoader !== null){ $this->classLoader->register(true); } } public function start(int $options = PTHREADS_INHERIT_ALL){ ThreadManager::getInstance()->add($this); if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated()){ if($this->getClassLoader() === null){ $this->setClassLoader(); } return parent::start($options); } return false; } /** * Stops the thread using the best way possible. Try to stop it yourself before calling this. */ public function quit(){ $this->isKilled = true; $this->notify(); if($this->isRunning()){ $this->shutdown(); $this->notify(); $this->unstack(); }elseif(!$this->isJoined()){ if(!$this->isTerminated()){ $this->join(); } } ThreadManager::getInstance()->remove($this); } public function getThreadName(){ return (new \ReflectionClass($this))->getShortName(); } } ================================================ FILE: src/pocketmine/block/AcaciaDoor.php ================================================ meta = $meta; } public function getName(){ return "Acacia Door Block"; } public function canBeActivated() : bool { return true; } public function getHardness() { return 3; } public function getToolType(){ return Tool::TYPE_AXE; } public function getDrops(Item $item) : array { return [ [Item::ACACIA_DOOR, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/AcaciaDoorBlock.php ================================================ meta = $meta; } public function getName(){ return "Acacia Door Block"; } } ================================================ FILE: src/pocketmine/block/AcaciaWoodStairs.php ================================================ meta = $meta; } public function getName() { return "Activator Rail"; } } ================================================ FILE: src/pocketmine/block/ActiveRedstoneLamp.php ================================================ meta = $meta; } public function getName(){ return "Active Redstone Lamp"; } public function getHardness() { return 0.3; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getLightLevel(){ return 15; } public function getDrops(Item $item) : array { return [ [Item::INACTIVE_REDSTONE_LAMP, 0 ,1], ]; } public function isLightedByAround(){ return ($this->meta == 1); } protected function checkPower(array $ignore = []){ if($this->isLightedByAround()){ $sides = [Vector3::SIDE_EAST, Vector3::SIDE_WEST, Vector3::SIDE_SOUTH, Vector3::SIDE_NORTH, Vector3::SIDE_UP, Vector3::SIDE_DOWN]; foreach($sides as $side){ if(!in_array($side, $ignore)){ /** @var ActiveRedstoneLamp $block */ $block = $this->getSide($side); if($block->getId() == $this->id){ if(!$block->isLightedByAround()) return true; } } } } return false; } public function lightAround(){ $sides = [Vector3::SIDE_EAST, Vector3::SIDE_WEST, Vector3::SIDE_SOUTH, Vector3::SIDE_NORTH, Vector3::SIDE_UP, Vector3::SIDE_DOWN]; foreach($sides as $side){ /** @var InactiveRedstoneLamp $block */ $block = $this->getSide($side); if($block->getId() == self::INACTIVE_REDSTONE_LAMP){ $block->turnOn(); } } } protected function turnAroundOff(array $ignore = []){ if(!$this->isLightedByAround()){ $sides = [Vector3::SIDE_EAST, Vector3::SIDE_WEST, Vector3::SIDE_SOUTH, Vector3::SIDE_NORTH, Vector3::SIDE_UP, Vector3::SIDE_DOWN]; foreach($sides as $side){ if(!in_array($side, $ignore)){ /** @var ActiveRedstoneLamp $block */ $block = $this->getSide($side); if($block->getId() == $this->id){ if($block->isLightedByAround()){ if(!$block->checkPower([$this->getOppositeSide($side)])) $block->turnOff(); } } } } } } public function turnOn(){ /*if($this->isLightedByAround()){ $this->meta = 0; $this->getLevel()->setBlock($this, $this, true, false); $this->lightAround(); }*/ $this->meta = 0; $this->getLevel()->setBlock($this, $this, true, false); return true; } public function turnOff(){ $this->getLevel()->setBlock($this, new InactiveRedstoneLamp(), true, true); //$this->turnAroundOff(); return true; } } ================================================ FILE: src/pocketmine/block/Air.php ================================================ meta = $meta; } public function canBeActivated() : bool { return true; } public function getHardness() { return 5; } public function getResistance(){ return 6000; } public function getName(){ $names = [ self::NORMAL => "Anvil", self::SLIGHTLY_DAMAGED => "Slightly Damaged Anvil", self::VERY_DAMAGED => "Very Damaged Anvil", 12 => "Anvil" //just in case somebody uses /give to get an anvil with damage 12 or higher, to prevent crash ]; return $names[$this->meta & 0x0c]; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function onActivate(Item $item, Player $player = null){ if(!$this->getLevel()->getServer()->anvilEnabled){ return true; } if($player instanceof Player){ if($player->isCreative() and $player->getServer()->limitedCreative){ return true; } $player->addWindow(new AnvilInventory($this)); $player->craftingType = Player::CRAFTING_ANVIL; } return true; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $direction = ($player !== null? $player->getDirection(): 0) & 0x03; $this->meta = ($this->meta & 0x0c) | $direction; $this->getLevel()->setBlock($block, $this, true, true); } public function getDrops(Item $item) : array { if($item->isPickaxe() >= 1){ return [ [$this->id, $this->meta & 0x0c, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/Beacon.php ================================================ meta = $meta; } /*public function canBeActivated() : bool{ return true; }*/ public function getLightLevel(){ return 15; } public function getHardness(){ return 3; } public function getName(){ return "Beacon"; } /*public function onActivate(Item $item, Player $player = null){ if($player instanceof Player){ $player->addWindow(new BeaconInventory($this)); } return true; }*/ } ================================================ FILE: src/pocketmine/block/Bed.php ================================================ meta = $meta; } public function canBeActivated() : bool { return true; } public function getHardness() { return 0.2; } public function getName(){ return "Bed Block"; } protected function recalculateBoundingBox() { return new AxisAlignedBB( $this->x, $this->y, $this->z, $this->x + 1, $this->y + 0.5625, $this->z + 1 ); } public function onActivate(Item $item, Player $player = null){ if($this->getLevel()->getDimension() == Level::DIMENSION_NETHER){ $explosion = new Explosion($this, 6, $this); $explosion->explode(); return true; } $time = $this->getLevel()->getTime() % Level::TIME_FULL; $isNight = ($time >= Level::TIME_NIGHT and $time < Level::TIME_SUNRISE); if($player instanceof Player and !$isNight){ $player->sendMessage(TextFormat::GRAY . "You can only sleep at night"); return true; } $blockNorth = $this->getSide(2); //Gets the blocks around them $blockSouth = $this->getSide(3); $blockEast = $this->getSide(5); $blockWest = $this->getSide(4); if(($this->meta & 0x08) === 0x08){ //This is the Top part of bed $b = $this; }else{ //Bottom Part of Bed if($blockNorth->getId() === $this->id and ($blockNorth->meta & 0x08) === 0x08){ $b = $blockNorth; }elseif($blockSouth->getId() === $this->id and ($blockSouth->meta & 0x08) === 0x08){ $b = $blockSouth; }elseif($blockEast->getId() === $this->id and ($blockEast->meta & 0x08) === 0x08){ $b = $blockEast; }elseif($blockWest->getId() === $this->id and ($blockWest->meta & 0x08) === 0x08){ $b = $blockWest; }else{ if($player instanceof Player){ $player->sendMessage(TextFormat::GRAY . "This bed is incomplete"); } return true; } } if($player instanceof Player and $player->sleepOn($b) === false){ $player->sendMessage(TextFormat::GRAY . "This bed is occupied"); } return true; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(0); if($down->isTransparent() === false){ $faces = [ 0 => 3, 1 => 4, 2 => 2, 3 => 5, ]; $d = $player instanceof Player ? $player->getDirection() : 0; $next = $this->getSide($faces[(($d + 3) % 4)]); $downNext = $this->getSide(0); if($next->canBeReplaced() === true and $downNext->isTransparent() === false){ $meta = (($d + 3) % 4) & 0x03; $this->getLevel()->setBlock($block, Block::get($this->id, $meta), true, true); $this->getLevel()->setBlock($next, Block::get($this->id, $meta | 0x08), true, true); return true; } } return false; } public function onBreak(Item $item){ $sides = [ 0 => 3, 1 => 4, 2 => 2, 3 => 5, 8 => 2, 9 => 5, 10 => 3, 11 => 4, ]; if(($this->meta & 0x08) === 0x08){ //This is the Top part of bed $next = $this->getSide($sides[$this->meta]); if($next->getId() === $this->id and ($next->meta | 0x08) === $this->meta){ //Checks if the block ID and meta are right $this->getLevel()->setBlock($next, new Air(), true, true); } }else{ //Bottom Part of Bed $next = $this->getSide($sides[$this->meta]); if($next->getId() === $this->id and $next->meta === ($this->meta | 0x08)){ $this->getLevel()->setBlock($next, new Air(), true, true); } } $this->getLevel()->setBlock($this, new Air(), true, true); return true; } public function getDrops(Item $item) : array { return [ [Item::BED, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/Bedrock.php ================================================ meta = $meta; } public function getName(){ return "Beetroot Block"; } public function getDrops(Item $item) : array { $drops = []; if($this->meta >= 0x07){ $drops[] = [Item::BEETROOT, 0, 1]; $drops[] = [Item::BEETROOT_SEEDS, 0, mt_rand(0, 3)]; }else{ $drops[] = [Item::BEETROOT_SEEDS, 0, 1]; } return $drops; } } ================================================ FILE: src/pocketmine/block/BirchDoor.php ================================================ meta = $meta; } public function getName(){ return "Birch Door Block"; } public function canBeActivated() : bool { return true; } public function getHardness() { return 3; } public function getToolType(){ return Tool::TYPE_AXE; } public function getDrops(Item $item) : array { return [ [Item::BIRCH_DOOR, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/BirchDoorBlock.php ================================================ meta = $meta; } public function getName(){ return "Birch Door Block"; } } ================================================ FILE: src/pocketmine/block/BirchWoodStairs.php ================================================ $class){ if($class !== null){ /** @var Block $block */ $block = new $class(); for($data = 0; $data < 16; ++$data){ self::$fullList[($id << 4) | $data] = new $class($data); } self::$solid[$id] = $block->isSolid(); self::$transparent[$id] = $block->isTransparent(); self::$hardness[$id] = $block->getHardness(); self::$light[$id] = $block->getLightLevel(); if($block->isSolid()){ if($block->isTransparent()){ if($block instanceof Liquid or $block instanceof Ice){ self::$lightFilter[$id] = 2; }else{ self::$lightFilter[$id] = 1; } }elseif($block instanceof SolidLight){ self::$lightFilter[$id] = 1; }else{ self::$lightFilter[$id] = 15; } }else{ self::$lightFilter[$id] = 1; } }else{ self::$lightFilter[$id] = 1; for($data = 0; $data < 16; ++$data){ self::$fullList[($id << 4) | $data] = new Block($id, $data); } } } } } /** * @param int $id * @param int $meta * @param Position $pos * * @return Block */ public static function get($id, $meta = 0, Position $pos = null){ if($id > 0xff){ trigger_error("BlockID cannot be higher than 255, defaulting to 0", E_USER_NOTICE); $id = 0; } try{ $block = self::$list[$id]; if($block !== null){ $block = new $block($meta); }else{ $block = new Block($id, $meta); } }catch(\RuntimeException $e){ $block = new Block($id, $meta); } if($pos !== null){ $block->x = $pos->x; $block->y = $pos->y; $block->z = $pos->z; $block->level = $pos->level; } return $block; } /** * @param int $id * @param int $meta */ public function __construct($id, $meta = 0){ $this->id = (int) $id; $this->meta = (int) $meta; } /** * Places the Block, using block space and block target, and side. Returns if the block has been placed. * * @param Item $item * @param Block $block * @param Block $target * @param int $face * @param float $fx * @param float $fy * @param float $fz * @param Player $player = null * * @return bool */ public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ return $this->getLevel()->setBlock($this, $this, true, true); } /** * Returns if the item can be broken with an specific Item * * @param Item $item * * @return bool */ public function isBreakable(Item $item){ return true; } public function tickRate() : int{ return 10; } /** * Do the actions needed so the block is broken with the Item * * @param Item $item * * @return mixed */ public function onBreak(Item $item){ return $this->getLevel()->setBlock($this, new Air(), true, true); } /** * Fires a block update on the Block * * @param int $type * * @return void */ public function onUpdate($type){ } /** * Do actions when activated by Item. Returns if it has done anything * * @param Item $item * @param Player $player * * @return bool */ public function onActivate(Item $item, Player $player = null){ return false; } /** * @return int */ public function getHardness(){ return 10; } /** * @return int */ public function getResistance(){ return $this->getHardness() * 5; } /** * @return int */ public function getBurnChance() : int{ return 0; } /** * @return int */ public function getBurnAbility() : int{ return 0; } public function isTopFacingSurfaceSolid(){ if($this->isSolid()){ return true; }else{ if($this instanceof Stair and ($this->getDamage() &4) == 4){ return true; }elseif($this instanceof Slab and ($this->getDamage() & 8) == 8){ return true; }elseif($this instanceof SnowLayer and ($this->getDamage() & 7) == 7){ return true; } } return false; } public function canNeighborBurn(){ for($face = 0; $face < 5; $face++){ if($this->getSide($face)->getBurnChance() > 0){ return true; } } return false; } /** * @return int */ public function getToolType(){ return Tool::TYPE_NONE; } /** * @return float */ public function getFrictionFactor(){ return 0.6; } /** * @return int 0-15 */ public function getLightLevel(){ return 0; } /** * AKA: Block->isPlaceable * * @return bool */ public function canBePlaced(){ return true; } public function isPlaceable(){ return $this->canBePlaced(); } /** * AKA: Block->canBeReplaced() * * @return bool */ public function canBeReplaced(){ return false; } /** * @return bool */ public function isTransparent(){ return false; } public function isSolid(){ return true; } /** * AKA: Block->isFlowable * * @return bool */ public function canBeFlowedInto(){ return false; } /** * AKA: Block->isActivable * * @return bool */ public function canBeActivated() : bool{ return false; } public function activate(){ return false; } public function deactivate(){ return false; } public function isActivated(Block $from = null){ return false; } public function hasEntityCollision(){ return false; } public function canPassThrough(){ return false; } /** * @return string */ public function getName(){ return "Unknown"; } /** * @return int */ final public function getId(){ return $this->id; } public function addVelocityToEntity(Entity $entity, Vector3 $vector){ } /** * @return int */ final public function getDamage(){ return $this->meta; } /** * @param int $meta */ final public function setDamage($meta){ $this->meta = $meta & 0x0f; } /** * Sets the block position to a new Position object * * @param Position $v */ final public function position(Position $v){ $this->x = (int) $v->x; $this->y = (int) $v->y; $this->z = (int) $v->z; $this->level = $v->level; $this->boundingBox = null; } /** * Returns an array of Item objects to be dropped * * @param Item $item * * @return array */ public function getDrops(Item $item) : array{ if(!isset(self::$list[$this->getId()])){ //Unknown blocks return []; }else{ return [ [$this->getId(), $this->getDamage(), 1], ]; } } /** * Returns the seconds that this block takes to be broken using an specific Item * * @param Item $item * * @return float */ public function getBreakTime(Item $item){ $base = $this->getHardness() * 1.5; if($this->canBeBrokenWith($item)){ if($this->getToolType() === Tool::TYPE_SHEARS and $item->isShears()){ $base /= 15; }elseif( ($this->getToolType() === Tool::TYPE_PICKAXE and ($tier = $item->isPickaxe()) !== false) or ($this->getToolType() === Tool::TYPE_AXE and ($tier = $item->isAxe()) !== false) or ($this->getToolType() === Tool::TYPE_SHOVEL and ($tier = $item->isShovel()) !== false) ){ switch($tier){ case Tool::TIER_WOODEN: $base /= 2; break; case Tool::TIER_STONE: $base /= 4; break; case Tool::TIER_IRON: $base /= 6; break; case Tool::TIER_DIAMOND: $base /= 8; break; case Tool::TIER_GOLD: $base /= 12; break; } } }else{ $base *= 3.33; } if($item->isSword()){ $base *= 0.5; } return $base; } public function canBeBrokenWith(Item $item){ return $this->getHardness() !== -1; } /** * Returns the Block on the side $side, works like Vector3::side() * * @param int $side * @param int $step * * @return Block */ public function getSide($side, $step = 1){ if($this->isValid()){ return $this->getLevel()->getBlock(Vector3::getSide($side, $step)); } return Block::get(Item::AIR, 0, Position::fromObject(Vector3::getSide($side, $step))); } /** * @return string */ public function __toString(){ return "Block[" . $this->getName() . "] (" . $this->getId() . ":" . $this->getDamage() . ")"; } /** * Checks for collision against an AxisAlignedBB * * @param AxisAlignedBB $bb * * @return bool */ public function collidesWithBB(AxisAlignedBB $bb){ $bb2 = $this->getBoundingBox(); return $bb2 !== null and $bb->intersectsWith($bb2); } /** * @param Entity $entity */ public function onEntityCollide(Entity $entity){ } /** * @return AxisAlignedBB */ public function getBoundingBox(){ if($this->boundingBox === null){ $this->boundingBox = $this->recalculateBoundingBox(); } return $this->boundingBox; } /** * @return AxisAlignedBB */ protected function recalculateBoundingBox(){ return new AxisAlignedBB( $this->x, $this->y, $this->z, $this->x + 1, $this->y + 1, $this->z + 1 ); } /** * @param Vector3 $pos1 * @param Vector3 $pos2 * * @return MovingObjectPosition */ public function calculateIntercept(Vector3 $pos1, Vector3 $pos2){ $bb = $this->getBoundingBox(); if($bb === null){ return null; } $v1 = $pos1->getIntermediateWithXValue($pos2, $bb->minX); $v2 = $pos1->getIntermediateWithXValue($pos2, $bb->maxX); $v3 = $pos1->getIntermediateWithYValue($pos2, $bb->minY); $v4 = $pos1->getIntermediateWithYValue($pos2, $bb->maxY); $v5 = $pos1->getIntermediateWithZValue($pos2, $bb->minZ); $v6 = $pos1->getIntermediateWithZValue($pos2, $bb->maxZ); if($v1 !== null and !$bb->isVectorInYZ($v1)){ $v1 = null; } if($v2 !== null and !$bb->isVectorInYZ($v2)){ $v2 = null; } if($v3 !== null and !$bb->isVectorInXZ($v3)){ $v3 = null; } if($v4 !== null and !$bb->isVectorInXZ($v4)){ $v4 = null; } if($v5 !== null and !$bb->isVectorInXY($v5)){ $v5 = null; } if($v6 !== null and !$bb->isVectorInXY($v6)){ $v6 = null; } $vector = $v1; if($v2 !== null and ($vector === null or $pos1->distanceSquared($v2) < $pos1->distanceSquared($vector))){ $vector = $v2; } if($v3 !== null and ($vector === null or $pos1->distanceSquared($v3) < $pos1->distanceSquared($vector))){ $vector = $v3; } if($v4 !== null and ($vector === null or $pos1->distanceSquared($v4) < $pos1->distanceSquared($vector))){ $vector = $v4; } if($v5 !== null and ($vector === null or $pos1->distanceSquared($v5) < $pos1->distanceSquared($vector))){ $vector = $v5; } if($v6 !== null and ($vector === null or $pos1->distanceSquared($v6) < $pos1->distanceSquared($vector))){ $vector = $v6; } if($vector === null){ return null; } $f = -1; if($vector === $v1){ $f = 4; }elseif($vector === $v2){ $f = 5; }elseif($vector === $v3){ $f = 0; }elseif($vector === $v4){ $f = 1; }elseif($vector === $v5){ $f = 2; }elseif($vector === $v6){ $f = 3; } return MovingObjectPosition::fromBlock($this->x, $this->y, $this->z, $f, $vector->add($this->x, $this->y, $this->z)); } public function setMetadata($metadataKey, MetadataValue $metadataValue){ if($this->getLevel() instanceof Level){ $this->getLevel()->getBlockMetadata()->setMetadata($this, $metadataKey, $metadataValue); } } public function getMetadata($metadataKey){ if($this->getLevel() instanceof Level){ return $this->getLevel()->getBlockMetadata()->getMetadata($this, $metadataKey); } return null; } public function hasMetadata($metadataKey){ if($this->getLevel() instanceof Level){ $this->getLevel()->getBlockMetadata()->hasMetadata($this, $metadataKey); } } public function removeMetadata($metadataKey, Plugin $plugin){ if($this->getLevel() instanceof Level){ $this->getLevel()->getBlockMetadata()->removeMetadata($this, $metadataKey, $plugin); } } } ================================================ FILE: src/pocketmine/block/BlockIds.php ================================================ meta = $meta; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if($block->getSide(Vector3::SIDE_DOWN)->isTransparent() === false){ $this->getLevel()->setBlock($block, $this, true, true); $nbt = new CompoundTag("", [ new ListTag("Items", []), new StringTag("id", Tile::BREWING_STAND), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); if($item->hasCustomName()){ $nbt->CustomName = new StringTag("CustomName", $item->getCustomName()); } if($item->hasCustomBlockData()){ foreach($item->getCustomBlockData() as $key => $v){ $nbt->{$key} = $v; } } Tile::createTile(Tile::BREWING_STAND, $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; } return false; } public function canBeActivated() : bool { return true; } public function getHardness() { return 0.5; } public function getResistance(){ return 2.5; } public function getLightLevel(){ return 1; } public function getName(){ return "Brewing Stand"; } public function onActivate(Item $item, Player $player = null){ if($player instanceof Player){ //TODO lock if($player->isCreative() and $player->getServer()->limitedCreative){ return true; } $t = $this->getLevel()->getTile($this); //$brewingStand = false; if($t instanceof TileBrewingStand){ $brewingStand = $t; }else{ $nbt = new CompoundTag("", [ new ListTag("Items", []), new StringTag("id", Tile::BREWING_STAND), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); $brewingStand = Tile::createTile(Tile::BREWING_STAND, $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); } $player->addWindow($brewingStand->getInventory()); } return true; } public function getDrops(Item $item) : array { $drops = []; if($item->isPickaxe() >= Tool::TIER_WOODEN){ $drops[] = [Item::BREWING_STAND, 0, 1]; } return $drops; } } ================================================ FILE: src/pocketmine/block/BrickStairs.php ================================================ meta = $meta; } public function getHardness() { return 2; } public function getResistance(){ return 30; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getName(){ return "Brick Stairs"; } } ================================================ FILE: src/pocketmine/block/Bricks.php ================================================ isPickaxe() >= 1){ return [ [Item::BRICKS_BLOCK, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/BrownMushroom.php ================================================ meta = $meta; } public function getName(){ return "Brown Mushroom"; } public function getLightLevel(){ return 1; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent() === true){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } return false; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(0); if($down->isTransparent() === false){ $this->getLevel()->setBlock($block, $this, true, true); return true; } return false; } public function getBoundingBox(){ return null; } } ================================================ FILE: src/pocketmine/block/BrownMushroomBlock.php ================================================ meta = $meta; } public function canBeActivated() : bool { return true; } public function getName(){ return "Brown Mushroom Block"; } public function getHardness() { return 0.2; } public function getResistance(){ return 1; } public function getDrops(Item $item) : array { if($item->getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::BROWN_MUSHROOM_BLOCK, SELF::BROWN, 1], ]; }else{ return [ [Item::BROWN_MUSHROOM, 0, mt_rand(0, 2)], ]; } } } ================================================ FILE: src/pocketmine/block/BurningFurnace.php ================================================ 4, 1 => 2, 2 => 5, 3 => 3, ]; $this->meta = $faces[$player instanceof Player ? $player->getDirection() : 0]; $this->getLevel()->setBlock($block, $this, true, true); $nbt = new CompoundTag("", [ new ListTag("Items", []), new StringTag("id", Tile::FURNACE), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); if($item->hasCustomName()){ $nbt->CustomName = new StringTag("CustomName", $item->getCustomName()); } if($item->hasCustomBlockData()){ foreach($item->getCustomBlockData() as $key => $v){ $nbt->{$key} = $v; } } Tile::createTile("Furnace", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; } public function onBreak(Item $item){ $this->getLevel()->setBlock($this, new Air(), true, true); return true; } public function onActivate(Item $item, Player $player = null){ if($player instanceof Player){ if(!(($furnace = $this->getLevel()->getTile($this)) instanceof TileFurnace)){ $nbt = new CompoundTag("", [ new ListTag("Items", []), new StringTag("id", Tile::FURNACE), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); $furnace = Tile::createTile("Furnace", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); } if(isset($furnace->namedtag->Lock) and $furnace->namedtag->Lock instanceof StringTag){ if($furnace->namedtag->Lock->getValue() !== $item->getCustomName()){ return true; } } $player->addWindow($furnace->getInventory()); } return true; } public function getDrops(Item $item) : array{ $drops = []; if($item->isPickaxe() >= Tool::TIER_WOODEN){ $drops[] = [Item::FURNACE, 0, 1]; } return $drops; } } ================================================ FILE: src/pocketmine/block/Cactus.php ================================================ meta = $meta; } public function getHardness() { return 0.4; } public function hasEntityCollision(){ return true; } public function getName(){ return "Cactus"; } protected function recalculateBoundingBox() { return new AxisAlignedBB( $this->x + 0.0625, $this->y + 0.0625, $this->z + 0.0625, $this->x + 0.9375, $this->y + 0.9375, $this->z + 0.9375 ); } public function onEntityCollide(Entity $entity){ $ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_CONTACT, 1); if($entity->attack($ev->getFinalDamage(), $ev) === true){ $ev->useArmors(); } } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ $down = $this->getSide(0); if($down->getId() !== self::SAND and $down->getId() !== self::CACTUS){ $this->getLevel()->useBreakOn($this); }else{ for($side = 2; $side <= 5; ++$side){ $b = $this->getSide($side); if(!$b->canBeFlowedInto()){ $this->getLevel()->useBreakOn($this); } } } }elseif($type === Level::BLOCK_UPDATE_RANDOM){ if($this->getSide(0)->getId() !== self::CACTUS){ if($this->meta == 0x0F){ for($y = 1; $y < 3; ++$y){ $b = $this->getLevel()->getBlock(new Vector3($this->x, $this->y + $y, $this->z)); if($b->getId() === self::AIR){ Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($b, new Cactus())); if(!$ev->isCancelled()){ $this->getLevel()->setBlock($b, $ev->getNewState(), true); } } } $this->meta = 0; $this->getLevel()->setBlock($this, $this); }else{ ++$this->meta; $this->getLevel()->setBlock($this, $this); } } } return false; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(0); if($down->getId() === self::SAND or $down->getId() === self::CACTUS){ $block0 = $this->getSide(2); $block1 = $this->getSide(3); $block2 = $this->getSide(4); $block3 = $this->getSide(5); if($block0->isTransparent() === true and $block1->isTransparent() === true and $block2->isTransparent() === true and $block3->isTransparent() === true){ $this->getLevel()->setBlock($this, $this, true); return true; } } return false; } public function getDrops(Item $item) : array { return [ [$this->id, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/Cake.php ================================================ meta = $meta; } public function canBeActivated() : bool { return true; } public function getHardness() { return 0.5; } public function getName(){ return "Cake Block"; } protected function recalculateBoundingBox() { $f = (1 + $this->getDamage() * 2) / 16; return new AxisAlignedBB( $this->x + $f, $this->y, $this->z + 0.0625, $this->x + 1 - 0.0625, $this->y + 0.5, $this->z + 1 - 0.0625 ); } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(0); if($down->getId() !== self::AIR){ $this->getLevel()->setBlock($block, $this, true, true); return true; } return false; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->getId() === self::AIR){ //Replace with common break method $this->getLevel()->setBlock($this, new Air(), true); return Level::BLOCK_UPDATE_NORMAL; } } return false; } public function getDrops(Item $item) : array { return []; } public function canBeConsumed() : bool{ return true; } public function canBeConsumedBy(Entity $entity) : bool{ return $entity instanceof Player and ($entity->getFood() < $entity->getMaxFood()) and $this->canBeConsumed(); } public function getResidue(){ $new = clone $this; return $new; } public function getAdditionalEffects() : array{ return []; } public function getFoodRestore() : int{ return 2; } public function getSaturationRestore() : float{ return 0.4; } public function onActivate(Item $item, Player $player = null){ if($player instanceof Player and $player->getFood() < 20){ $player->getServer()->getPluginManager()->callEvent($ev = new EntityEatBlockEvent($player, $this)); if(!$ev->isCancelled()){ $player->setFood($player->getFood() + 2); ++$this->meta; if($this->meta >= 0x06){ $this->getLevel()->setBlock($this, new Air(), true); }else{ $this->getLevel()->setBlock($this, $this, true); } return true; } } return false; } } ================================================ FILE: src/pocketmine/block/Carpet.php ================================================ meta = $meta; } public function getHardness() { return 0.1; } public function isSolid(){ return true; } public function getName(){ static $names = [ 0 => "White Carpet", 1 => "Orange Carpet", 2 => "Magenta Carpet", 3 => "Light Blue Carpet", 4 => "Yellow Carpet", 5 => "Lime Carpet", 6 => "Pink Carpet", 7 => "Gray Carpet", 8 => "Light Gray Carpet", 9 => "Cyan Carpet", 10 => "Purple Carpet", 11 => "Blue Carpet", 12 => "Brown Carpet", 13 => "Green Carpet", 14 => "Red Carpet", 15 => "Black Carpet", ]; return $names[$this->meta & 0x0f]; } protected function recalculateBoundingBox() { return new AxisAlignedBB( $this->x, $this->y, $this->z, $this->x + 1, $this->y + 0.0625, $this->z + 1 ); } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(0); if($down->getId() !== self::AIR){ $this->getLevel()->setBlock($block, $this, true, true); return true; } return false; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->getId() === self::AIR){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } return false; } } ================================================ FILE: src/pocketmine/block/Carrot.php ================================================ meta = $meta; } public function getName(){ return "Carrot Block"; } public function getDrops(Item $item) : array { $drops = []; if($this->meta >= 0x07){ $fortunel = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortunel = $fortunel > 3 ? 3 : $fortunel; $drops[] = [Item::CARROT, 0, mt_rand(1, 4 + $fortunel)]; }else{ $drops[] = [Item::CARROT, 0, 1]; } return $drops; } } ================================================ FILE: src/pocketmine/block/Cauldron.php ================================================ meta = $meta; } public function getHardness(){ return 2; } public function getName(){ return "Cauldron"; } public function canBeActivated() : bool{ return true; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $nbt = new CompoundTag("", [ new StringTag("id", Tile::CAULDRON), new IntTag("x", $block->x), new IntTag("y", $block->y), new IntTag("z", $block->z), new ShortTag("PotionId", 0xffff), new ByteTag("SplashPotion", 0), new ListTag("Items", []) ]); if($item->hasCustomBlockData()){ foreach($item->getCustomBlockData() as $key => $v){ $nbt->{$key} = $v; } } $chunk = $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4); $tile = Tile::createTile("Cauldron", $chunk, $nbt);// $this->getLevel()->setBlock($block, $this, true, true); return true; } public function onBreak(Item $item){ $this->getLevel()->setBlock($this, new Air(), true); return true; } public function getDrops(Item $item) : array{ if($item->isPickaxe() >= 1){ return [ [Item::CAULDRON, 0, 1] ]; } return []; } public function update(){//umm... right update method...? $this->getLevel()->setBlock($this, Block::get($this->id, $this->meta + 1), true); $this->getLevel()->setBlock($this, $this, true);//Undo the damage value } public function isEmpty(){ return $this->meta === 0x00; } public function isFull(){ return $this->meta === 0x06; } public function onActivate(Item $item, Player $player = null){//@author iTX. rewrite @Dog194 $tile = $this->getLevel()->getTile($this); if(!($tile instanceof TileCauldron)){ return false; } switch($item->getId()){ case Item::BUCKET: if($item->getDamage() === 0){//empty bucket if(!$this->isFull() or $tile->isCustomColor() or $tile->hasPotion()){ break; } $bucket = clone $item; $bucket->setDamage(8);//water bucket Server::getInstance()->getPluginManager()->callEvent($ev = new PlayerBucketFillEvent($player, $this, 0, $item, $bucket)); if(!$ev->isCancelled()){ if($player->isSurvival()){ $player->getInventory()->setItemInHand($ev->getItem()); } $this->meta = 0;//empty $this->getLevel()->setBlock($this, $this, true); $tile->clearCustomColor(); $this->getLevel()->addSound(new SplashSound($this->add(0.5, 1, 0.5))); } }elseif($item->getDamage() === 8){//water bucket if($this->isFull() and !$tile->isCustomColor() and !$tile->hasPotion()){ break; } $bucket = clone $item; $bucket->setDamage(0);//empty bucket Server::getInstance()->getPluginManager()->callEvent($ev = new PlayerBucketEmptyEvent($player, $this, 0, $item, $bucket)); if(!$ev->isCancelled()){ if($player->isSurvival()){ $player->getInventory()->setItemInHand($ev->getItem()); } if($tile->hasPotion()){//if has potion $this->meta = 0;//empty $tile->setPotionId(0xffff);//reset potion $tile->setSplashPotion(false); $tile->clearCustomColor(); $this->getLevel()->setBlock($this, $this, true); $this->getLevel()->addSound(new ExplodeSound($this->add(0.5, 0, 0.5))); }else{ $this->meta = 6;//fill $tile->clearCustomColor(); $this->getLevel()->setBlock($this, $this, true); $this->getLevel()->addSound(new SplashSound($this->add(0.5, 1, 0.5))); } $this->update(); } } break; case Item::DYE: if($tile->hasPotion()) break; $color = Color::getDyeColor($item->getDamage()); if($tile->isCustomColor()){ $color = Color::averageColor($color, $tile->getCustomColor()); } if($player->isSurvival()){ $item->setCount($item->getCount() - 1); /*if($item->getCount() <= 0){ $player->getInventory()->setItemInHand(Item::get(Item::AIR)); }*/ } $tile->setCustomColor($color); $this->getLevel()->addSound(new SplashSound($this->add(0.5, 1, 0.5))); $this->update(); break; case Item::LEATHER_CAP: case Item::LEATHER_TUNIC: case Item::LEATHER_PANTS: case Item::LEATHER_BOOTS: if($this->isEmpty()) break; if($tile->isCustomColor()){ --$this->meta; $this->getLevel()->setBlock($this, $this, true); $newItem = clone $item; /** @var Armor $newItem */ $newItem->setCustomColor($tile->getCustomColor()); $player->getInventory()->setItemInHand($newItem); $this->getLevel()->addSound(new SplashSound($this->add(0.5, 1, 0.5))); if($this->isEmpty()){ $tile->clearCustomColor(); } }else{ --$this->meta; $this->getLevel()->setBlock($this, $this, true); $newItem = clone $item; /** @var Armor $newItem */ $newItem->clearCustomColor(); $player->getInventory()->setItemInHand($newItem); $this->getLevel()->addSound(new SplashSound($this->add(0.5, 1, 0.5))); } break; case Item::POTION: case Item::SPLASH_POTION: if(!$this->isEmpty() and (($tile->getPotionId() !== $item->getDamage() and $item->getDamage() !== Potion::WATER_BOTTLE) or ($item->getId() === Item::POTION and $tile->getSplashPotion()) or ($item->getId() === Item::SPLASH_POTION and !$tile->getSplashPotion()) and $item->getDamage() !== 0 or ($item->getDamage() === Potion::WATER_BOTTLE and $tile->hasPotion())) ){//long... $this->meta = 0x00; $this->getLevel()->setBlock($this, $this, true); $tile->setPotionId(0xffff);//reset $tile->setSplashPotion(false); $tile->clearCustomColor(); if($player->isSurvival()){ $player->getInventory()->setItemInHand(Item::get(Item::GLASS_BOTTLE)); } $this->getLevel()->addSound(new ExplodeSound($this->add(0.5, 0, 0.5))); }elseif($item->getDamage() === Potion::WATER_BOTTLE){//水瓶 喷溅型水瓶 $this->meta += 2; if($this->meta > 0x06) $this->meta = 0x06; $this->getLevel()->setBlock($this, $this, true); if($player->isSurvival()){ $player->getInventory()->setItemInHand(Item::get(Item::GLASS_BOTTLE)); } $tile->setPotionId(0xffff); $tile->setSplashPotion(false); $tile->clearCustomColor(); $this->getLevel()->addSound(new SplashSound($this->add(0.5, 1, 0.5))); }elseif(!$this->isFull()){ $this->meta += 2; if($this->meta > 0x06) $this->meta = 0x06; $tile->setPotionId($item->getDamage()); $tile->setSplashPotion($item->getId() === Item::SPLASH_POTION); $tile->clearCustomColor(); $this->getLevel()->setBlock($this, $this, true); if($player->isSurvival()){ $player->getInventory()->setItemInHand(Item::get(Item::GLASS_BOTTLE)); } $color = Potion::getColor($item->getDamage()); $this->getLevel()->addSound(new SpellSound($this->add(0.5, 1, 0.5), $color[0], $color[1], $color[2])); } break; case Item::GLASS_BOTTLE: $player->getServer()->getPluginManager()->callEvent($ev = new PlayerGlassBottleEvent($player, $this, $item)); if($ev->isCancelled()){ return false; } if($this->meta < 2) { break; } if($tile->hasPotion()){ $this->meta -= 2; if($tile->getSplashPotion() === true){ $result = Item::get(Item::SPLASH_POTION, $tile->getPotionId()); }else{ $result = Item::get(Item::POTION, $tile->getPotionId()); } if($this->isEmpty()){ $tile->setPotionId(0xffff);//reset $tile->setSplashPotion(false); $tile->clearCustomColor(); } $this->getLevel()->setBlock($this, $this, true); $this->addItem($item, $player, $result); $color = Potion::getColor($result->getDamage()); $this->getLevel()->addSound(new SpellSound($this->add(0.5, 1, 0.5), $color[0], $color[1], $color[2])); }else{ $this->meta -= 2; $this->getLevel()->setBlock($this, $this, true); if($player->isSurvival()){ $result = Item::get(Item::POTION, Potion::WATER_BOTTLE); $this->addItem($item, $player, $result); } $this->getLevel()->addSound(new GraySplashSound($this->add(0.5, 1, 0.5))); } break; } return true; } public function addItem(Item $item, Player $player, Item $result){ if($item->getCount() <= 1){ $player->getInventory()->setItemInHand($result); }else{ $item->setCount($item->getCount() - 1); if($player->getInventory()->canAddItem($result) === true){ $player->getInventory()->addItem($result); }else{ $motion = $player->getDirectionVector()->multiply(0.4); $position = clone $player->getPosition(); $player->getLevel()->dropItem($position->add(0 , 0.5, 0), $result , $motion, 40); } } } } ================================================ FILE: src/pocketmine/block/CauldronBlock.php ================================================ meta = $meta; } public function getName(){ return "Cauldron Block"; } } ================================================ FILE: src/pocketmine/block/Chest.php ================================================ meta = $meta; } public function canBeActivated() : bool { return true; } public function getHardness() { return 2.5; } public function getName(){ return "Chest"; } public function getToolType(){ return Tool::TYPE_AXE; } protected function recalculateBoundingBox() { return new AxisAlignedBB( $this->x + 0.0625, $this->y, $this->z + 0.0625, $this->x + 0.9375, $this->y + 0.9475, $this->z + 0.9375 ); } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $faces = [ 0 => 4, 1 => 2, 2 => 5, 3 => 3, ]; $chest = null; $this->meta = $faces[$player instanceof Player ? $player->getDirection() : 0]; for($side = 2; $side <= 5; ++$side){ if(($this->meta === 4 or $this->meta === 5) and ($side === 4 or $side === 5)){ continue; }elseif(($this->meta === 3 or $this->meta === 2) and ($side === 2 or $side === 3)){ continue; } $c = $this->getSide($side); if($c instanceof Chest and $c->getDamage() === $this->meta){ $tile = $this->getLevel()->getTile($c); if($tile instanceof TileChest and !$tile->isPaired()){ $chest = $tile; break; } } } $this->getLevel()->setBlock($block, $this, true, true); $nbt = new CompoundTag("", [ new ListTag("Items", []), new StringTag("id", Tile::CHEST), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); if($item->hasCustomName()){ $nbt->CustomName = new StringTag("CustomName", $item->getCustomName()); } if($item->hasCustomBlockData()){ foreach($item->getCustomBlockData() as $key => $v){ $nbt->{$key} = $v; } } $tile = Tile::createTile("Chest", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); if($chest instanceof TileChest and $tile instanceof TileChest){ $chest->pairWith($tile); $tile->pairWith($chest); } return true; } public function onBreak(Item $item){ $t = $this->getLevel()->getTile($this); if($t instanceof TileChest){ $t->unpair(); } $this->getLevel()->setBlock($this, new Air(), true, true); return true; } public function onActivate(Item $item, Player $player = null){ if($player instanceof Player){ $top = $this->getSide(1); if($top->isTransparent() !== true){ return true; } $t = $this->getLevel()->getTile($this); $chest = null; if($t instanceof TileChest){ $chest = $t; }else{ $nbt = new CompoundTag("", [ new ListTag("Items", []), new StringTag("id", Tile::CHEST), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); $chest = Tile::createTile("Chest", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); } if(isset($chest->namedtag->Lock) and $chest->namedtag->Lock instanceof StringTag){ if($chest->namedtag->Lock->getValue() !== $item->getCustomName()){ return true; } } if($player->isCreative() and $player->getServer()->limitedCreative){ return true; } $player->addWindow($chest->getInventory()); } return true; } public function getDrops(Item $item) : array { return [ [$this->id, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/ChorusPlant.php ================================================ meta = $meta; } public function getHardness() { return 0.4; } public function getToolType() { return Tool::TYPE_AXE; } public function getName() { return "Chorus Plant"; } public function getDrops(Item $item): array { $drops = []; if ($this->meta >= 0x07) { $fortunel = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortunel = $fortunel > 3 ? 3 : $fortunel; $drops[] = [Item::CHORUS_FRUIT, 0, 1]; } return $drops; } } ================================================ FILE: src/pocketmine/block/Clay.php ================================================ isPickaxe() >= 1){ return [ [Item::COAL_BLOCK, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/CoalOre.php ================================================ isPickaxe() >= 1){ if($item->getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::COAL_ORE, 0, 1], ]; }else{ $fortunel = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortunel = $fortunel > 3 ? 3 : $fortunel; $times = [1,1,2,3,4]; $time = $times[mt_rand(0, $fortunel + 1)]; return [ [Item::COAL, 0, $time], ]; } }else{ return []; } } } ================================================ FILE: src/pocketmine/block/Cobblestone.php ================================================ isPickaxe() >= 1){ return [ [Item::COBBLESTONE, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/CobblestoneStairs.php ================================================ meta = $meta; } public function getHardness() { return 2; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getName(){ return "Cobblestone Stairs"; } } ================================================ FILE: src/pocketmine/block/Cobweb.php ================================================ resetFallDistance(); } public function getDrops(Item $item) : array { if($item->isShears()){ return [ [Item::COBWEB, 0, 1], ]; }elseif($item->isSword() >= Tool::TIER_WOODEN){ if($item->getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::COBWEB, 0, 1], ]; }else{ return [ [Item::STRING, 0, 1], ]; } } return []; } } ================================================ FILE: src/pocketmine/block/CocoaBlock.php ================================================ meta = $meta; } public function getName() { return "Cocoa Block"; } public function getHardness() { return 0.2; } public function getResistance() { return 15; } public function canBeActivated() : bool { return true; } public function onActivate(Item $item, Player $player = null) { if ($item->getId() === Item::DYE and $item->getDamage() === 0x0F) { $block = clone $this; if ($block->meta > 7) { return false; } $block->meta += 4; Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($this, $block)); if (!$ev->isCancelled()) { $this->getLevel()->setBlock($this, $ev->getNewState(), true, true); } $item->count--; return true; } return false; } public function onUpdate($type) { if ($type === Level::BLOCK_UPDATE_NORMAL) { $faces = [3, 4, 2, 5, 3, 4, 2, 5, 3, 4, 2, 5]; if ($this->getSide($faces[$this->meta])->isTransparent() === true) { $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } elseif ($type === Level::BLOCK_UPDATE_RANDOM) { if (mt_rand(0, 45) === 1) { if ($this->meta <= 7) { $block = clone $this; $block->meta += 4; Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($this, $block)); if (!$ev->isCancelled()) { $this->getLevel()->setBlock($this, $ev->getNewState(), true, true); } else { return Level::BLOCK_UPDATE_RANDOM; } } } else { return Level::BLOCK_UPDATE_RANDOM; } } return false; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null) { if ($target->getId() === Block::WOOD and $target->getDamage() === 3) { if ($face !== 0 and $face !== 1) { $faces = [ 2 => 0, 3 => 2, 4 => 3, 5 => 1, ]; $this->meta = $faces[$face]; $this->getLevel()->setBlock($block, Block::get(Item::COCOA_BLOCK, $this->meta), true); return true; } } return false; } public function getDrops(Item $item) : array { $drops = []; if ($this->meta >= 8) { $drops[] = [Item::DYE, 3, 3]; } else { $drops[] = [Item::DYE, 3, 1]; } return $drops; } } ================================================ FILE: src/pocketmine/block/ComparatorBlock.php ================================================ meta = $meta; } public function getName(){ return "Comparator Block"; } } ================================================ FILE: src/pocketmine/block/Crops.php ================================================ getSide(0); if($down->getId() === self::FARMLAND){ $this->getLevel()->setBlock($block, $this, true, true); return true; } return false; } public function onActivate(Item $item, Player $player = null){ if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal $block = clone $this; $block->meta += mt_rand(2, 5); if($block->meta > 7){ $block->meta = 7; } Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($this, $block)); if(!$ev->isCancelled()){ $this->getLevel()->setBlock($this, $ev->getNewState(), true, true); } $item->count--; return true; } return false; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent() === true){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } }elseif($type === Level::BLOCK_UPDATE_RANDOM){ if(mt_rand(0, 2) == 1){ if($this->meta < 0x07){ $block = clone $this; ++$block->meta; Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($this, $block)); if(!$ev->isCancelled()){ $this->getLevel()->setBlock($this, $ev->getNewState(), true, true); }else{ return Level::BLOCK_UPDATE_RANDOM; } } }else{ return Level::BLOCK_UPDATE_RANDOM; } } return false; } } ================================================ FILE: src/pocketmine/block/Dandelion.php ================================================ getSide(0); if($down->getId() === 2 or $down->getId() === 3 or $down->getId() === 60){ $this->getLevel()->setBlock($block, $this, true, true); return true; } return false; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent() === true){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } return false; } } ================================================ FILE: src/pocketmine/block/DarkOakDoor.php ================================================ meta = $meta; } public function getName(){ return "Dark Oak Door Block"; } public function canBeActivated() : bool { return true; } public function getHardness() { return 3; } public function getToolType(){ return Tool::TYPE_AXE; } public function getDrops(Item $item) : array { return [ [Item::DARK_OAK_DOOR, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/DarkOakDoorBlock.php ================================================ meta = $meta; } public function getName(){ return "Dark Oak Door Block"; } } ================================================ FILE: src/pocketmine/block/DarkOakWoodStairs.php ================================================ boundingBox === null){ $this->boundingBox = $this->recalculateBoundingBox(); } return $this->boundingBox; } public function canBeFlowedInto(){ return false; } public function canBeActivated() : bool { return true; } /** * @return DLDetector */ protected function getTile(){ $t = $this->getLevel()->getTile($this); if($t instanceof DLDetector){ return $t; }else{ $nbt = new CompoundTag("", [ new StringTag("id", Tile::DAY_LIGHT_DETECTOR), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); return Tile::createTile(Tile::DAY_LIGHT_DETECTOR, $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); } } public function onActivate(Item $item, Player $player = null){ $this->getLevel()->setBlock($this, new DaylightDetectorInverted(), true, true); $this->getTile()->onUpdate(); return true; } public function isActivated(Block $from = null){ return $this->getTile()->isActivated(); } public function onBreak(Item $item){ $this->getLevel()->setBlock($this, new Air()); if($this->isActivated()) $this->deactivate(); } public function getHardness() { return 0.2; } public function getResistance(){ return 1; } public function getDrops(Item $item) : array { return [ [self::DAYLIGHT_SENSOR, 0, 1] ]; } } ================================================ FILE: src/pocketmine/block/DaylightDetectorInverted.php ================================================ getLevel()->setBlock($this, new DaylightDetector(), true, true); $this->getTile()->onUpdate(); return true; } } ================================================ FILE: src/pocketmine/block/DaylightSensor.php ================================================ meta = $meta; } public function getName(){ return "Daylight Sensor"; } } ================================================ FILE: src/pocketmine/block/DaylightSensorInverted.php ================================================ meta = $meta; } public function getName(){ return "Daylight Sensor Inverted"; } } ================================================ FILE: src/pocketmine/block/DeadBush.php ================================================ meta = $meta; } public function getName(){ return "Dead Bush"; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(0); if($down->getId() === Block::SAND or $down->getId() === Block::PODZOL or $down->getId() === Block::HARDENED_CLAY or $down->getId() === Block::STAINED_CLAY){ $this->getLevel()->setBlock($block, $this, true); return true; } return false; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent() === true){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } return false; } public function getDrops(Item $item) : array { if($item->isShears()){ return [ [Item::DEAD_BUSH, 0, 1], ]; }else{ return [ [Item::STICK, 0, mt_rand(0, 2)], ]; } } } ================================================ FILE: src/pocketmine/block/DetectorRail.php ================================================ meta = $meta; } public function getName() { return "Detector Rail"; } } ================================================ FILE: src/pocketmine/block/Diamond.php ================================================ isPickaxe() >= 4){ return [ [Item::DIAMOND_BLOCK, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/DiamondOre.php ================================================ isPickaxe() >= 4){ if($item->getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::DIAMOND_ORE, 0, 1], ]; }else{ $fortunel = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortunel = $fortunel > 3 ? 3 : $fortunel; $times = [1,1,2,3,4]; $time = $times[mt_rand(0, $fortunel + 1)]; return [ [Item::DIAMOND, 0, $time], ]; } }else{ return []; } } } ================================================ FILE: src/pocketmine/block/Dirt.php ================================================ isHoe()){ $item->useOn($this); $this->getLevel()->setBlock($this, Block::get(Item::FARMLAND, 0), true); return true; } return false; } } ================================================ FILE: src/pocketmine/block/Dispenser.php ================================================ meta = $meta; } public function canBeActivated() : bool { return true; } public function getHardness() { return 3.5; } public function getName(){ return "Dispenser"; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $dispenser = null; if($player instanceof Player){ $pitch = $player->getPitch(); if(abs($pitch) >= 45){ if($pitch < 0) $f = 4; else $f = 5; } else $f = $player->getDirection(); } else $f = 0; $faces = [ 3 => 3, 0 => 4, 2 => 5, 1 => 2, 4 => 0, 5 => 1 ]; $this->meta = $faces[$f]; $this->getLevel()->setBlock($block, $this, true, true); $nbt = new CompoundTag("", [ new ListTag("Items", []), new StringTag("id", Tile::DISPENSER), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); if($item->hasCustomName()){ $nbt->CustomName = new StringTag("CustomName", $item->getCustomName()); } if($item->hasCustomBlockData()){ foreach($item->getCustomBlockData() as $key => $v){ $nbt->{$key} = $v; } } Tile::createTile(Tile::DISPENSER, $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; } public function activate(){ $tile = $this->getLevel()->getTile($this); if($tile instanceof TileDispenser){ $tile->activate(); } } public function onActivate(Item $item, Player $player = null){ if($player instanceof Player){ $t = $this->getLevel()->getTile($this); $dispenser = null; if($t instanceof TileDispenser){ $dispenser = $t; }else{ $nbt = new CompoundTag("", [ new ListTag("Items", []), new StringTag("id", Tile::DISPENSER), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); $dispenser = Tile::createTile(Tile::DISPENSER, $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); } if($player->isCreative() and $player->getServer()->limitedCreative){ return true; } $player->addWindow($dispenser->getInventory()); } return true; } public function getDrops(Item $item) : array { return [ [$this->id, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/Door.php ================================================ getDamage(); $isUp = ($damage & 0x08) > 0; if($isUp){ $down = $this->getSide(Vector3::SIDE_DOWN)->getDamage(); $up = $damage; }else{ $down = $damage; $up = $this->getSide(Vector3::SIDE_UP)->getDamage(); } $isRight = ($up & 0x01) > 0; return $down & 0x07 | ($isUp ? 8 : 0) | ($isRight ? 0x10 : 0); } protected function recalculateBoundingBox() { $f = 0.1875; $damage = $this->getFullDamage(); $bb = new AxisAlignedBB( $this->x, $this->y, $this->z, $this->x + 1, $this->y + 2, $this->z + 1 ); $j = $damage & 0x03; $isOpen = (($damage & 0x04) > 0); $isRight = (($damage & 0x10) > 0); if($j === 0){ if($isOpen){ if(!$isRight){ $bb->setBounds( $this->x, $this->y, $this->z, $this->x + 1, $this->y + 1, $this->z + $f ); }else{ $bb->setBounds( $this->x, $this->y, $this->z + 1 - $f, $this->x + 1, $this->y + 1, $this->z + 1 ); } }else{ $bb->setBounds( $this->x, $this->y, $this->z, $this->x + $f, $this->y + 1, $this->z + 1 ); } }elseif($j === 1){ if($isOpen){ if(!$isRight){ $bb->setBounds( $this->x + 1 - $f, $this->y, $this->z, $this->x + 1, $this->y + 1, $this->z + 1 ); }else{ $bb->setBounds( $this->x, $this->y, $this->z, $this->x + $f, $this->y + 1, $this->z + 1 ); } }else{ $bb->setBounds( $this->x, $this->y, $this->z, $this->x + 1, $this->y + 1, $this->z + $f ); } }elseif($j === 2){ if($isOpen){ if(!$isRight){ $bb->setBounds( $this->x, $this->y, $this->z + 1 - $f, $this->x + 1, $this->y + 1, $this->z + 1 ); }else{ $bb->setBounds( $this->x, $this->y, $this->z, $this->x + 1, $this->y + 1, $this->z + $f ); } }else{ $bb->setBounds( $this->x + 1 - $f, $this->y, $this->z, $this->x + 1, $this->y + 1, $this->z + 1 ); } }elseif($j === 3){ if($isOpen){ if(!$isRight){ $bb->setBounds( $this->x, $this->y, $this->z, $this->x + $f, $this->y + 1, $this->z + 1 ); }else{ $bb->setBounds( $this->x + 1 - $f, $this->y, $this->z, $this->x + 1, $this->y + 1, $this->z + 1 ); } }else{ $bb->setBounds( $this->x, $this->y, $this->z + 1 - $f, $this->x + 1, $this->y + 1, $this->z + 1 ); } } return $bb; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(Vector3::SIDE_DOWN)->getId() === self::AIR and $this->getSide(Vector3::SIDE_UP) instanceof Door){ //Block underneath the door was broken $this->getLevel()->setBlock($this, new Air(), false, false); $this->getLevel()->setBlock($this->getSide(Vector3::SIDE_UP), new Air(), false); foreach($this->getDrops(Item::get(Item::DIAMOND_PICKAXE)) as $drop){ $this->getLevel()->dropItem($this, Item::get($drop[0], $drop[1], $drop[2])); } return Level::BLOCK_UPDATE_NORMAL; } } return false; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if($face === 1){ $blockUp = $this->getSide(Vector3::SIDE_UP); $blockDown = $this->getSide(Vector3::SIDE_DOWN); if($blockUp->canBeReplaced() === false or $blockDown->isTransparent() === true){ return false; } $direction = $player instanceof Player ? $player->getDirection() : 0; $face = [ 0 => 3, 1 => 4, 2 => 2, 3 => 5, ]; $next = $this->getSide($face[(($direction + 2) % 4)]); $next2 = $this->getSide($face[$direction]); $metaUp = 0x08; if($next->getId() === $this->getId() or ($next2->isTransparent() === false and $next->isTransparent() === true)){ //Door hinge $metaUp |= 0x01; } $this->setDamage($player->getDirection() & 0x03); $this->getLevel()->setBlock($block, $this, true, true); //Bottom $this->getLevel()->setBlock($blockUp, $b = Block::get($this->getId(), $metaUp), true); //Top return true; } return false; } public function onBreak(Item $item){ if(($this->getDamage() & 0x08) === 0x08){ $down = $this->getSide(Vector3::SIDE_DOWN); if($down->getId() === $this->getId()){ $this->getLevel()->setBlock($down, new Air(), true); } }else{ $up = $this->getSide(Vector3::SIDE_UP); if($up->getId() === $this->getId()){ $this->getLevel()->setBlock($up, new Air(), true); } } $this->getLevel()->setBlock($this, new Air(), true); return true; } public function isOpened(){ return (($this->getFullDamage() & 0x04) > 0); } public function onActivate(Item $item, Player $player = null){ if(($this->getDamage() & 0x08) === 0x08){ //Top $down = $this->getSide(Vector3::SIDE_DOWN); if($down->getId() === $this->getId()){ $meta = $down->getDamage() ^ 0x04; $this->getLevel()->setBlock($down, Block::get($this->getId(), $meta), true); $players = $this->getLevel()->getChunkPlayers($this->x >> 4, $this->z >> 4); if($player instanceof Player){ unset($players[$player->getLoaderId()]); } $this->level->addSound(new DoorSound($this)); return true; } return false; }else{ $this->meta ^= 0x04; $this->getLevel()->setBlock($this, $this, true); $players = $this->getLevel()->getChunkPlayers($this->x >> 4, $this->z >> 4); if($player instanceof Player){ unset($players[$player->getLoaderId()]); } $this->level->addSound(new DoorSound($this)); } return true; } } ================================================ FILE: src/pocketmine/block/DoublePlant.php ================================================ meta = $meta; } public function canBeReplaced(){ return true; } public function getName(){ static $names = [ 0 => "Sunflower", 1 => "Lilac", 2 => "Double Tallgrass", 3 => "Large Fern", 4 => "Rose Bush", 5 => "Peony" ]; return $names[$this->meta & 0x07]; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent() === true && !$this->getSide(0) instanceof DoublePlant){ //Replace with common break method $this->getLevel()->setBlock($this, new Air(), false, false); return Level::BLOCK_UPDATE_NORMAL; } } return false; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(0); $up = $this->getSide(1); if($down->getId() === self::GRASS or $down->getId() === self::DIRT){ $this->getLevel()->setBlock($block, $this, true); $this->getLevel()->setBlock($up, Block::get($this->id, $this->meta ^ 0x08), true); return true; } return false; } public function onBreak(Item $item){ $up = $this->getSide(1); $down = $this->getSide(0); if(($this->meta & 0x08) === 0x08){ // This is the Top part of flower if($up->getId() === $this->id and $up->meta !== 0x08){ // Checks if the block ID and meta are right $this->getLevel()->setBlock($up, new Air(), true, true); }elseif($down->getId() === $this->id and $down->meta !== 0x08){ $this->getLevel()->setBlock($down, new Air(), true, true); } }else{ // Bottom Part of flower if($up->getId() === $this->id and ($up->meta & 0x08) === 0x08){ $this->getLevel()->setBlock($up, new Air(), true, true); }elseif($down->getId() === $this->id and ($down->meta & 0x08) === 0x08){ $this->getLevel()->setBlock($down, new Air(), true, true); } } } public function getDrops(Item $item) : array{ if(($this->meta & 0x08) !== 0x08){ return [[Item::DOUBLE_PLANT, $this->meta, 1]]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/DoubleRedSandstoneSlab.php ================================================ isPickaxe() >= 1){ return [ [Item::RED_SANDSTONE_SLAB, $this->meta, 2], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/DoubleSlab.php ================================================ meta = $meta; } public function getHardness() { return 2; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getName(){ static $names = [ 0 => "Stone", 1 => "Sandstone", 2 => "Wooden", 3 => "Cobblestone", 4 => "Brick", 5 => "Stone Brick", 6 => "Quartz", 7 => "Nether Brick", ]; return "Double " . $names[$this->meta & 0x07] . " Slab"; } public function getDrops(Item $item) : array { if($item->isPickaxe() >= 1){ return [ [Item::SLAB, $this->meta & 0x07, 2], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/DoubleWoodSlab.php ================================================ meta = $meta; } public function getHardness() { return 2; } public function getToolType(){ return Tool::TYPE_AXE; } public function getName(){ static $names = [ 0 => "Oak", 1 => "Spruce", 2 => "Birch", 3 => "Jungle", 4 => "Acacia", 5 => "Dark Oak", 6 => "", 7 => "" ]; return "Double " . $names[$this->meta & 0x07] . " Wooden Slab"; } public function getDrops(Item $item) : array { return [ [Item::WOOD_SLAB, $this->meta & 0x07, 2], ]; } } ================================================ FILE: src/pocketmine/block/DragonEgg.php ================================================ meta = $meta; } public function canBeActivated() : bool { return true; } public function getHardness() { return 3.5; } public function getName(){ return "Dropper"; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $dispenser = null; if($player instanceof Player){ $pitch = $player->getPitch(); if(abs($pitch) >= 45){ if($pitch < 0) $f = 4; else $f = 5; } else $f = $player->getDirection(); } else $f = 0; $faces = [ 3 => 3, 0 => 4, 2 => 5, 1 => 2, 4 => 0, 5 => 1 ]; $this->meta = $faces[$f]; $this->getLevel()->setBlock($block, $this, true, true); $nbt = new CompoundTag("", [ new ListTag("Items", []), new StringTag("id", Tile::DROPPER), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); if($item->hasCustomName()){ $nbt->CustomName = new StringTag("CustomName", $item->getCustomName()); } if($item->hasCustomBlockData()){ foreach($item->getCustomBlockData() as $key => $v){ $nbt->{$key} = $v; } } Tile::createTile(Tile::DROPPER, $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; } public function activate(){ $tile = $this->getLevel()->getTile($this); if($tile instanceof TileDropper){ $tile->activate(); } } public function onActivate(Item $item, Player $player = null){ if($player instanceof Player){ $t = $this->getLevel()->getTile($this); $dropper = null; if($t instanceof TileDropper){ $dropper = $t; }else{ $nbt = new CompoundTag("", [ new ListTag("Items", []), new StringTag("id", Tile::DROPPER), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); $dropper = Tile::createTile(Tile::DROPPER, $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); } if($player->isCreative() and $player->getServer()->limitedCreative){ return true; } $player->addWindow($dropper->getInventory()); } return true; } public function getDrops(Item $item) : array { return [ [$this->id, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/ElectricalAppliance.php ================================================ isPickaxe() >= 4){ return [ [Item::EMERALD_BLOCK, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/EmeraldOre.php ================================================ isPickaxe() >= 4){ if($item->getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::EMERALD_ORE, 0, 1], ]; }else{ $fortunel = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortunel = $fortunel > 3 ? 3 : $fortunel; $times = [1,1,2,3,4]; $time = $times[mt_rand(0, $fortunel + 1)]; return [ [Item::EMERALD, 0, $time], ]; } }else{ return []; } } } ================================================ FILE: src/pocketmine/block/EnchantingTable.php ================================================ x, $this->y, $this->z, $this->x + 1, $this->y + 0.75, $this->z + 1 ); } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $this->getLevel()->setBlock($block, $this, true, true); $nbt = new CompoundTag("", [ new StringTag("id", Tile::ENCHANT_TABLE), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); if($item->hasCustomName()){ $nbt->CustomName = new StringTag("CustomName", $item->getCustomName()); } if($item->hasCustomBlockData()){ foreach($item->getCustomBlockData() as $key => $v){ $nbt->{$key} = $v; } } Tile::createTile(Tile::ENCHANT_TABLE, $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; } public function canBeActivated() : bool{ return true; } public function getHardness(){ return 5; } public function getResistance(){ return 6000; } public function getName(){ return "Enchanting Table"; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function onActivate(Item $item, Player $player = null){ if(!$this->getLevel()->getServer()->enchantingTableEnabled){ return true; } if($player instanceof Player){ if($player->isCreative() and $player->getServer()->limitedCreative){ return true; } $tile = $this->getLevel()->getTile($this); $enchantTable = null; if($tile instanceof EnchantTable){ $enchantTable = $tile; }else{ $this->getLevel()->setBlock($this, $this, true, true); $nbt = new CompoundTag("", [ new StringTag("id", Tile::ENCHANT_TABLE), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); if($item->hasCustomName()){ $nbt->CustomName = new StringTag("CustomName", $item->getCustomName()); } if($item->hasCustomBlockData()){ foreach($item->getCustomBlockData() as $key => $v){ $nbt->{$key} = $v; } } /** @var EnchantTable $enchantTable */ $enchantTable = Tile::createTile(Tile::ENCHANT_TABLE, $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); } $player->addWindow(new EnchantInventory($this)); $player->craftingType = Player::CRAFTING_ENCHANT; } return true; } public function getDrops(Item $item) : array{ if($item->isPickaxe() >= 1){ return [ [$this->id, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/EndPortalFrame.php ================================================ meta = $meta; } public function getLightLevel(){ return 1; } public function getName(){ return "End Portal Frame"; } public function getHardness() { return -1; } public function getResistance(){ return 18000000; } public function isBreakable(Item $item){ return false; } protected function recalculateBoundingBox() { return new AxisAlignedBB( $this->x, $this->y, $this->z, $this->x + 1, $this->y + (($this->getDamage() & 0x04) > 0 ? 1 : 0.8125), $this->z + 1 ); } } ================================================ FILE: src/pocketmine/block/EndRod.php ================================================ meta = $meta; } public function getLightLevel(){ return 14; } public function getName(){ return "End Rod"; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ $below = $this->getSide(0); $side = $this->getDamage(); $faces = [ 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 0 => 0, ]; } return false; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $below = $this->getSide(0); if($target->isTransparent() === false and $face !== 0){ $faces = [ 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, ]; $this->meta = $faces[$face]; $this->getLevel()->setBlock($block, $this, true, true); return true; } return false; } public function getDrops(Item $item) : array { return [ [$this->id, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/EndStone.php ================================================ meta = $meta; } public function getHardness() { return 0.8; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getDrops(Item $item) : array { if($item->isPickaxe() >= 1){ return [ [Item::END_STONE_BRICKS, $this->meta & 0x03, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/EnderChest.php ================================================ meta = $meta; } public function canBeActivated() : bool{ return true; } public function getHardness(){ return 22.5; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getName() : string{ return "Ender Chest"; } protected function recalculateBoundingBox(){ return new AxisAlignedBB( $this->x + 0.0625, $this->y, $this->z + 0.0625, $this->x + 0.9375, $this->y + 0.9475, $this->z + 0.9375 ); } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $faces = [ 0 => 4, 1 => 2, 2 => 5, 3 => 3, ]; $this->meta = $faces[$player instanceof Player ? $player->getDirection() : 0]; $this->getLevel()->setBlock($block, $this, true, true); $nbt = new CompoundTag("", [ new ListTag("Items", []), new StringTag("id", Tile::ENDER_CHEST), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); if($item->hasCustomName()){ $nbt->CustomName = new StringTag("CustomName", $item->getCustomName()); } $tile = Tile::createTile("EnderChest", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; } public function onBreak(Item $item){ $this->getLevel()->setBlock($this, new Air(), true, true); return true; } public function onActivate(Item $item, Player $player = null){ if($player instanceof Player){ $top = $this->getSide(1); if($top->isTransparent() !== true){ return true; } if(!($this->getLevel()->getTile($this) instanceof TileEnderChest)) { $nbt = new CompoundTag("", [ new StringTag("id", Tile::ENDER_CHEST), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); Tile::createTile("EnderChest", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); } if($player->isCreative() and $player->getServer()->limitedCreative){ return true; } $player->getEnderChestInventory()->openAt($this); } return true; } public function getDrops(Item $item) : array{ return [ [Item::OBSIDIAN, 0, 8], ]; } } ================================================ FILE: src/pocketmine/block/Fallable.php ================================================ getSide(Vector3::SIDE_DOWN); if($down->getId() === self::AIR or ($down instanceof Liquid)){ $fall = Entity::createEntity("FallingSand", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $this->x + 0.5), new DoubleTag("", $this->y), new DoubleTag("", $this->z + 0.5) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", 0), new DoubleTag("", 0), new DoubleTag("", 0) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", 0), new FloatTag("", 0) ]), "TileID" => new IntTag("TileID", $this->getId()), "Data" => new ByteTag("Data", $this->getDamage()), ])); $fall->spawnToAll(); } } } } ================================================ FILE: src/pocketmine/block/Farmland.php ================================================ meta = $meta; } public function getName(){ return "Farmland"; } public function getHardness() { return 0.6; } public function getToolType(){ return Tool::TYPE_SHOVEL; } protected function recalculateBoundingBox() { return new AxisAlignedBB( $this->x, $this->y, $this->z, $this->x + 1, $this->y + 0.9375, $this->z + 1 ); } public function getDrops(Item $item) : array { return [ [Item::DIRT, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/Fence.php ================================================ meta = $meta; } public function getHardness() { return 2; } public function getToolType(){ return Tool::TYPE_AXE; } public function getBurnChance() : int{ return 5; } public function getBurnAbility() : int{ return 20; } public function getName(){ static $names = [ 0 => "Oak Fence", 1 => "Spruce Fence", 2 => "Birch Fence", 3 => "Jungle Fence", 4 => "Acacia Fence", 5 => "Dark Oak Fence", "", "" ]; return $names[$this->meta & 0x07]; } protected function recalculateBoundingBox() { $north = $this->canConnect($this->getSide(Vector3::SIDE_NORTH)); $south = $this->canConnect($this->getSide(Vector3::SIDE_SOUTH)); $west = $this->canConnect($this->getSide(Vector3::SIDE_WEST)); $east = $this->canConnect($this->getSide(Vector3::SIDE_EAST)); $n = $north ? 0 : 0.375; $s = $south ? 1 : 0.625; $w = $west ? 0 : 0.375; $e = $east ? 1 : 0.625; return new AxisAlignedBB( $this->x + $w, $this->y, $this->z + $n, $this->x + $e, $this->y + 1.5, $this->z + $s ); } public function canConnect(Block $block){ return ($block instanceof Fence or $block instanceof FenceGate) ? true : $block->isSolid() and !$block->isTransparent(); } } ================================================ FILE: src/pocketmine/block/FenceGate.php ================================================ meta = $meta; } public function getName(){ return "Oak Fence Gate"; } public function getHardness() { return 2; } public function canBeActivated() : bool { return true; } public function getToolType(){ return Tool::TYPE_AXE; } protected function recalculateBoundingBox() { if(($this->getDamage() & 0x04) > 0){ return null; } $i = ($this->getDamage() & 0x03); if($i === 2 or $i === 0){ return new AxisAlignedBB( $this->x, $this->y, $this->z + 0.375, $this->x + 1, $this->y + 1.5, $this->z + 0.625 ); }else{ return new AxisAlignedBB( $this->x + 0.375, $this->y, $this->z, $this->x + 0.625, $this->y + 1.5, $this->z + 1 ); } } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $faces = [ 0 => 3, 1 => 0, 2 => 1, 3 => 2, ]; $this->meta = $faces[$player instanceof Player ? $player->getDirection() : 0] & 0x03; $this->getLevel()->setBlock($block, $this, true, true); return true; } public function isOpened(){ return (($this->getDamage() & 0x04) > 0); } public function getDrops(Item $item) : array { return [ [$this->id, 0, 1], ]; } public function onActivate(Item $item, Player $player = null){ $faces = [ 0 => 3, 1 => 0, 2 => 1, 3 => 2, ]; if($player !== null) $this->meta = ($faces[$player instanceof Player ? $player->getDirection() : 0] & 0x03) | ((~$this->meta) & 0x04); else $this->meta ^= 0x04; $this->getLevel()->setBlock($this, $this, true); $this->level->addSound(new DoorSound($this)); return true; } } ================================================ FILE: src/pocketmine/block/FenceGateAcacia.php ================================================ meta = $meta; if($this->temporalVector === null){ $this->temporalVector = new Vector3(0, 0, 0); } } public function hasEntityCollision(){ return true; } public function getName(){ return "Fire Block"; } public function getLightLevel(){ return 15; } public function isBreakable(Item $item){ return false; } public function canBeReplaced(){ return true; } public function onEntityCollide(Entity $entity){ $ProtectL = 0; if(!$entity->hasEffect(Effect::FIRE_RESISTANCE)){ $ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, 1); if($entity->attack($ev->getFinalDamage(), $ev) === true){ $ev->useArmors(); } $ProtectL = $ev->getFireProtectL(); } $ev = new EntityCombustByBlockEvent($this, $entity, 8, $ProtectL); if($entity instanceof Arrow){ $ev->setCancelled(); } Server::getInstance()->getPluginManager()->callEvent($ev); if(!$ev->isCancelled()){ $entity->setOnFire($ev->getDuration()); } } public function getDrops(Item $item) : array { return []; } public function onUpdate($type){ if($type == Level::BLOCK_UPDATE_NORMAL or $type == Level::BLOCK_UPDATE_RANDOM or $type == Level::BLOCK_UPDATE_SCHEDULED){ if(!$this->getSide(Vector3::SIDE_DOWN)->isTopFacingSurfaceSolid() and !$this->canNeighborBurn()){ $this->getLevel()->setBlock($this, new Air(), true); return Level::BLOCK_UPDATE_NORMAL; }elseif($type == Level::BLOCK_UPDATE_NORMAL or $type == Level::BLOCK_UPDATE_RANDOM){ $this->getLevel()->scheduleUpdate($this, $this->getTickRate() + mt_rand(0, 10)); }elseif($type == Level::BLOCK_UPDATE_SCHEDULED and $this->getLevel()->getServer()->fireSpread){ $forever = $this->getSide(Vector3::SIDE_DOWN)->getId() == Block::NETHERRACK; //TODO: END if(!$this->getSide(Vector3::SIDE_DOWN)->isTopFacingSurfaceSolid() and !$this->canNeighborBurn()){ $this->getLevel()->setBlock($this, new Air(), true); } if(!$forever and $this->getLevel()->getWeather()->isRainy() and ($this->getLevel()->canBlockSeeSky($this) or $this->getLevel()->canBlockSeeSky($this->getSide(Vector3::SIDE_EAST)) or $this->getLevel()->canBlockSeeSky($this->getSide(Vector3::SIDE_WEST)) or $this->getLevel()->canBlockSeeSky($this->getSide(Vector3::SIDE_SOUTH)) or $this->getLevel()->canBlockSeeSky($this->getSide(Vector3::SIDE_NORTH)) ) ){ $this->getLevel()->setBlock($this, new Air(), true); }else{ $meta = $this->meta; if($meta < 15){ $this->meta = $meta + mt_rand(0, 3); $this->getLevel()->setBlock($this, $this, true); } $this->getLevel()->scheduleUpdate($this, $this->getTickRate() + mt_rand(0, 10)); if(!$forever and !$this->canNeighborBurn()){ if(!$this->getSide(Vector3::SIDE_DOWN)->isTopFacingSurfaceSolid() or $meta > 3){ $this->getLevel()->setBlock($this, new Air(), true); } }elseif(!$forever && !($this->getSide(Vector3::SIDE_DOWN)->getBurnAbility() > 0) && $meta >= 15 && mt_rand(0, 4) == 0){ $this->getLevel()->setBlock($this, new Air(), true); }else{ $o = 0; //TODO: decrease the o if the rainfall values are high $this->tryToCatchBlockOnFire($this->getSide(Vector3::SIDE_EAST), 300 + $o, $meta); $this->tryToCatchBlockOnFire($this->getSide(Vector3::SIDE_WEST), 300 + $o, $meta); $this->tryToCatchBlockOnFire($this->getSide(Vector3::SIDE_DOWN), 250 + $o, $meta); $this->tryToCatchBlockOnFire($this->getSide(Vector3::SIDE_UP), 250 + $o, $meta); $this->tryToCatchBlockOnFire($this->getSide(Vector3::SIDE_SOUTH), 300 + $o, $meta); $this->tryToCatchBlockOnFire($this->getSide(Vector3::SIDE_NORTH), 300 + $o, $meta); for($x = ($this->x - 1); $x <= ($this->x + 1); ++$x){ for($z = ($this->z - 1); $z <= ($this->z + 1); ++$z){ for($y = ($this->y -1); $y <= ($this->y + 4); ++$y){ $k = 100; if($y > $this->y + 1){ $k += ($y - ($this->y + 1)) * 100; } $chance = $this->getChanceOfNeighborsEncouragingFire($this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $z))); if($chance > 0){ $t = ($chance + 40 + $this->getLevel()->getServer()->getDifficulty() * 7); //TODO: decrease t if the rainfall values are high if($t > 0 and mt_rand(0, $k) <= $t){ $damage = min(15, $meta + mt_rand(0, 5) / 4); $this->getLevel()->setBlock($this->temporalVector->setComponents($x, $y, $z), new Fire($damage), true); $this->getLevel()->scheduleUpdate($this->temporalVector, $this->getTickRate()); } } } } } } } } } return 0; } public function getTickRate() : int{ return 30; } /*public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ for($s = 0; $s <= 5; ++$s){ $side = $this->getSide($s); if($side->getId() !== self::AIR and !($side instanceof Liquid)){ return false; } } $this->getLevel()->setBlock($this, new Air(), true); return Level::BLOCK_UPDATE_NORMAL; }elseif($type === Level::BLOCK_UPDATE_RANDOM){ if($this->getSide(0)->getId() !== self::NETHERRACK){ $this->getLevel()->setBlock($this, new Air(), true); return Level::BLOCK_UPDATE_NORMAL; } } return false; }*/ private function tryToCatchBlockOnFire(Block $block, int $bound, int $damage){ $burnAbility = $block->getBurnAbility(); if(mt_rand(0, $bound) < $burnAbility){ if(mt_rand(0, $damage + 10) < 5){ $meta = max(15, $damage + mt_rand(0, 4) / 4); $this->getLevel()->getServer()->getPluginManager()->callEvent($ev = new BlockBurnEvent($block)); if(!$ev->isCancelled()){ $this->getLevel()->setBlock($block, $fire = new Fire($meta), true); $this->getLevel()->scheduleUpdate($block, $fire->getTickRate()); } }else{ $this->getLevel()->setBlock($this, new Air(), true); } if($block instanceof TNT){ $block->prime(); } } } private function getChanceOfNeighborsEncouragingFire(Block $block){ if($block->getId() !== self::AIR){ return 0; }else{ $chance = 0; for($i = 0; $i < 5; $i++){ $chance = max($chance, $block->getSide($i)->getBurnChance()); } return $chance; } } } ================================================ FILE: src/pocketmine/block/Flowable.php ================================================ meta = $meta; } public function getName(){ static $names = [ self::TYPE_POPPY => "Poppy", self::TYPE_BLUE_ORCHID => "Blue Orchid", self::TYPE_ALLIUM => "Allium", self::TYPE_AZURE_BLUET => "Azure Bluet", self::TYPE_RED_TULIP => "Red Tulip", self::TYPE_ORANGE_TULIP => "Orange Tulip", self::TYPE_WHITE_TULIP => "White Tulip", self::TYPE_PINK_TULIP => "Pink Tulip", self::TYPE_OXEYE_DAISY => "Oxeye Daisy", 9 => "Unknown", 10 => "Unknown", 11 => "Unknown", 12 => "Unknown", 13 => "Unknown", 14 => "Unknown", 15 => "Unknown" ]; return $names[$this->meta]; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(0); if($down->getId() === Block::GRASS or $down->getId() === Block::DIRT or $down->getId() === Block::FARMLAND){ $this->getLevel()->setBlock($block, $this, true); return true; } return false; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } return false; } } ================================================ FILE: src/pocketmine/block/FlowerPot.php ================================================ meta = $meta; } public function getName(){ return "Flower Pot Block"; } public function canBeActivated(): bool{ return true; } protected function recalculateBoundingBox(){ return new AxisAlignedBB( $this->x + 0.3125, $this->y, $this->z + 0.3125, $this->x + 0.6875, $this->y + 0.375, $this->z + 0.6875 ); } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if($this->getSide(Vector3::SIDE_DOWN)->isTransparent()){ return false; } $this->getLevel()->setBlock($block, $this, true, true); $nbt = new CompoundTag("", [ new StringTag("id", Tile::FLOWER_POT), new IntTag("x", $block->x), new IntTag("y", $block->y), new IntTag("z", $block->z), new ShortTag("item", 0), new IntTag("mData", 0), ]); if($item->hasCustomBlockData()){ foreach($item->getCustomBlockData() as $key => $v){ $nbt->{$key} = $v; } } Tile::createTile(Tile::FLOWER_POT, $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent() === true){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } return false; } public function onActivate(Item $item, Player $player = null){ $pot = $this->getLevel()->getTile($this); if(!($pot instanceof TileFlowerPot)){ return false; } if(!$pot->canAddItem($item)){ return true; } $this->setDamage(self::STATE_FULL); //specific damage value is unnecessary, it just needs to be non-zero to show an item. $this->getLevel()->setBlock($this, $this, true, false); $pot->setItem($item); if($player instanceof Player){ if($player->isSurvival()){ $item->setCount($item->getCount() - 1); $player->getInventory()->setItemInHand($item->getCount() > 0 ? $item : Item::get(Item::AIR)); } } return true; } public function getDrops(Item $item) : array{ $items = [[Item::FLOWER_POT, 0, 1]]; $tile = $this->getLevel()->getTile($this); if($tile instanceof TileFlowerPot){ if(($item = $tile->getItem())->getId() !== Item::AIR){ $items[] = [$item->getId(), $item->getDamage(), 1]; } } return $items; } } ================================================ FILE: src/pocketmine/block/Furnace.php ================================================ meta = $meta; } public function getName(){ return "Furnace"; } public function canBeActivated() : bool { return true; } public function getHardness() { return 3.5; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $faces = [ 0 => 4, 1 => 2, 2 => 5, 3 => 3, ]; $this->meta = $faces[$player instanceof Player ? $player->getDirection() : 0]; $this->getLevel()->setBlock($block, $this, true, true); $nbt = new CompoundTag("", [ new ListTag("Items", []), new StringTag("id", Tile::FURNACE), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); if($item->hasCustomName()){ $nbt->CustomName = new StringTag("CustomName", $item->getCustomName()); } if($item->hasCustomBlockData()){ foreach($item->getCustomBlockData() as $key => $v){ $nbt->{$key} = $v; } } Tile::createTile("Furnace", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; } public function onBreak(Item $item){ $this->getLevel()->setBlock($this, new Air(), true, true); return true; } public function onActivate(Item $item, Player $player = null){ if($player instanceof Player){ $t = $this->getLevel()->getTile($this); if($t instanceof FurnaceTile){ $furnace = $t; }else{ $nbt = new CompoundTag("", [ new ListTag("Items", []), new StringTag("id", Tile::FURNACE), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); $furnace = Tile::createTile("Furnace", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); } if(isset($furnace->namedtag->Lock) and $furnace->namedtag->Lock instanceof StringTag){ if($furnace->namedtag->Lock->getValue() !== $item->getCustomName()){ return true; } } if($player->isCreative() and $player->getServer()->limitedCreative){ return true; } $player->addWindow($furnace->getInventory()); } return true; } public function getDrops(Item $item) : array { $drops = []; if($item->isPickaxe() >= 1){ $drops[] = [Item::FURNACE, 0, 1]; } return $drops; } } ================================================ FILE: src/pocketmine/block/Glass.php ================================================ getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::GLASS, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/GlassPane.php ================================================ getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::GLASS_PANE, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/GlowingObsidian.php ================================================ meta = $meta; } public function getName(){ return "Glowing Obsidian"; } public function getLightLevel(){ return 12; } } ================================================ FILE: src/pocketmine/block/GlowingRedstoneOre.php ================================================ getLevel()->setBlock($this, Block::get(Item::REDSTONE_ORE, $this->meta), false, false); return Level::BLOCK_UPDATE_WEAK; } return false; } } ================================================ FILE: src/pocketmine/block/Glowstone.php ================================================ getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::GLOWSTONE_BLOCK, 0, 1], ]; }else{ $fortuneL = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortuneL = $fortuneL > 3 ? 3 : $fortuneL; $times = [1,1,2,3,4]; $time = $times[mt_rand(0, $fortuneL + 1)]; $num = mt_rand(2, 4) * $time; $num = $num > 4 ? 4 : $num; return [ [Item::GLOWSTONE_DUST, 0, $num], ]; } } } ================================================ FILE: src/pocketmine/block/Gold.php ================================================ isPickaxe() >= 4){ return [ [Item::GOLD_BLOCK, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/GoldOre.php ================================================ isPickaxe() >= 4){ return [ [Item::GOLD_ORE, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/Grass.php ================================================ getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::GRASS, 0, 1], ]; }else{ return [ [Item::DIRT, 0, 1], ]; } } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_RANDOM){ $block = $this->getLevel()->getBlock(new Vector3($this->x, $this->y, $this->z)); if($block->getSide(1)->getLightLevel() < 4){ Server::getInstance()->getPluginManager()->callEvent($ev = new BlockSpreadEvent($block, $this, new Dirt())); }elseif($block->getSide(1)->getLightLevel() >= 9){ for($l = 0; $l < 4; ++$l){ $x = mt_rand($this->x - 1, $this->x + 1); $y = mt_rand($this->y - 2, $this->y + 2); $z = mt_rand($this->z - 1, $this->z + 1); $block = $this->getLevel()->getBlock(new Vector3($x, $y, $z)); if($block->getId() === Block::DIRT && $block->getDamage() === 0x0F && $block->getSide(1)->getLightLevel() >= 4 && $block->z <= 2){ Server::getInstance()->getPluginManager()->callEvent($ev = new BlockSpreadEvent($block, $this, new Grass())); if(!$ev->isCancelled()){ $this->getLevel()->setBlock($block, $ev->getNewState()); } } } } } } public function onActivate(Item $item, Player $player = null){ if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){ $item->count--; TallGrassObject::growGrass($this->getLevel(), $this, new Random(mt_rand()), 8, 2); return true; }elseif($item->isHoe()){ $item->useOn($this); $this->getLevel()->setBlock($this, new Farmland()); return true; }elseif($item->isShovel() and $this->getSide(1)->getId() === Block::AIR){ $item->useOn($this); $this->getLevel()->setBlock($this, new GrassPath()); return true; } return false; } } ================================================ FILE: src/pocketmine/block/GrassPath.php ================================================ x, $this->y, $this->z, $this->x + 1, $this->y + 0.9375, $this->z + 1 ); } public function onUpdate($type){ if($type == Level::BLOCK_UPDATE_NORMAL){ $block = $this->getSide(self::SIDE_UP); if($block->getId() != self::AIR){ $this->getLevel()->setBlock($this, new Dirt(), true); } return Level::BLOCK_UPDATE_NORMAL; } return false; } public function getHardness() { return 0.6; } public function getDrops(Item $item) : array { if($item->getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::GRASS_PATH, 0, 1], ]; }else{ return [ [Item::DIRT, 0, 1], ]; } } } ================================================ FILE: src/pocketmine/block/Gravel.php ================================================ getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){//使用精准采集附魔 不掉落燧石 $drops[] = [Item::GRAVEL, 0, 1]; return $drops; } $fortunel = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortunel = $fortunel > 3 ? 3 : $fortunel; $rates = [10,7,4,1]; if(mt_rand(1, $rates[$fortunel]) === 1){//10% 14% 25% 100% $drops[] = [Item::FLINT, 0, 1]; } if(mt_rand(1, 10) !== 1){//90% $drops[] = [Item::GRAVEL, 0, 1]; } return $drops; } } ================================================ FILE: src/pocketmine/block/HardenedClay.php ================================================ meta = $meta; } public function getName(){ return "Hay Bale"; } public function getHardness() { return 0.5; } public function getBurnChance() : int{ return 60; } public function getBurnAbility() : int{ return 20; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $faces = [ 0 => 0, 1 => 0, 2 => 0b1000, 3 => 0b1000, 4 => 0b0100, 5 => 0b0100, ]; $this->meta = ($this->meta & 0x03) | $faces[$face]; $this->getLevel()->setBlock($block, $this, true, true); return true; } public function getDrops(Item $item) : array { return [ [$this->id, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/HeavyWeightedPressurePlate.php ================================================ meta = $meta; } public function canBeActivated(): bool{ return true; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getName(){ return "Hopper"; } public function getHardness(){ return 3; } public function onActivate(Item $item, Player $player = null){ if($player instanceof Player){ $t = $this->getLevel()->getTile($this); if($t instanceof TileHopper){ if($t->hasLock() and !$t->checkLock($item->getCustomName())){ $player->getServer()->getLogger()->debug($player->getName() . " attempted to open a locked hopper"); return true; } $player->addWindow($t->getInventory()); } } return true; } public function activate(){ //TODO: Hopper content freezing (requires basic redstone system upgrade) } public function getTarget(){ return $this->target; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $faces = [ 0 => 0, 1 => 0, 2 => 3, 3 => 2, 4 => 5, 5 => 4 ]; $this->meta = $faces[$face]; $this->getLevel()->setBlock($block, $this, true, true); $nbt = new CompoundTag("", [ new ListTag("Items", []), new StringTag("id", Tile::HOPPER), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); if($item->hasCustomName()){ $nbt->CustomName = new StringTag("CustomName", $item->getCustomName()); } if($item->hasCustomBlockData()){ foreach($item->getCustomBlockData() as $key => $v){ $nbt->{$key} = $v; } } $t = Tile::createTile(Tile::HOPPER, $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; } public function getDrops(Item $item) : array { if($item->isPickaxe() >= 1){ return [ [Item::HOPPER, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/HopperBlock.php ================================================ meta = $meta; } public function getName(){ return "Hopper Block"; } } ================================================ FILE: src/pocketmine/block/Ice.php ================================================ getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) === 0){ $this->getLevel()->setBlock($this, new Water(), true); } return true; } public function getDrops(Item $item) : array { if($item->getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::ICE, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/InactiveRedstoneLamp.php ================================================ getLevel()->setBlock($this, new ActiveRedstoneLamp(), true, true); /*}else{ $this->getLevel()->setBlock($this, new ActiveRedstoneLamp(), true, false); //$this->lightAround(); }*/ return true; } public function turnOff(){ return true; } } ================================================ FILE: src/pocketmine/block/InvisibleBedrock.php ================================================ isPickaxe() >= 3){ return [ [Item::IRON_BLOCK, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/IronBars.php ================================================ isPickaxe() >= 1){ return [ [Item::IRON_BARS, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/IronDoor.php ================================================ meta = $meta; } public function getName(){ return "Iron Door Block"; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getHardness() { return 5; } public function getDrops(Item $item) : array { if($item->isPickaxe() >= 1){ return [ [Item::IRON_DOOR, 0, 1], ]; }else{ return []; } } public function onActivate(Item $item, Player $player = null){ if($player instanceof Player) return true; else return parent::onActivate($item, $player); } } ================================================ FILE: src/pocketmine/block/IronOre.php ================================================ isPickaxe() >= 3){ return [ [Item::IRON_ORE, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/IronTrapdoor.php ================================================ meta = $meta; } public function getName(){ return "Item Frame"; } public function canBeActivated() : bool{ return true; } public function onActivate(Item $item, Player $player = null){ if(!(($tile = $this->level->getTile($this)) instanceof TileItemFrame)){ $nbt = new CompoundTag("", [ new StringTag("id", Tile::ITEM_FRAME), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z), new FloatTag("ItemDropChance", 1.0), new ByteTag("ItemRotation", 0) ]); $tile = Tile::createTile(Tile::ITEM_FRAME, $this->level->getChunk($this->x >> 4, $this->z >> 4), $nbt); } if($tile->hasItem()){ $tile->setItemRotation(($tile->getItemRotation() + 1) % 8); }else{ if($item->getCount() > 0){ $frameItem = clone $item; $frameItem->setCount(1); $item->setCount($item->getCount() - 1); $tile->setItem($frameItem); if($player instanceof Player and $player->isSurvival()){ $player->getInventory()->setItemInHand($item->getCount() <= 0 ? Item::get(Item::AIR) : $item); } } } return true; } public function onBreak(Item $item){ if(($tile = $this->level->getTile($this)) instanceof TileItemFrame){ //TODO: add events if(lcg_value() <= $tile->getItemDropChance() and $tile->getItem()->getId() !== Item::AIR){ $this->level->dropItem($tile->getBlock(), $tile->getItem()); } } return parent::onBreak($item); } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ $sides = [ 0 => 4, 1 => 5, 2 => 2, 3 => 3 ]; if(!$this->getSide($sides[$this->meta])->isSolid()){ $this->level->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } return false; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if($face === 0 or $face === 1){ return false; } $faces = [ 2 => 3, 3 => 2, 4 => 1, 5 => 0 ]; $this->meta = $faces[$face]; $this->level->setBlock($block, $this, true, true); $nbt = new CompoundTag("", [ new StringTag("id", Tile::ITEM_FRAME), new IntTag("x", $block->x), new IntTag("y", $block->y), new IntTag("z", $block->z), new FloatTag("ItemDropChance", 1.0), new ByteTag("ItemRotation", 0) ]); if($item->hasCustomBlockData()){ foreach($item->getCustomBlockData() as $key => $v){ $nbt->{$key} = $v; } } Tile::createTile(Tile::ITEM_FRAME, $this->level->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; } public function getDrops(Item $item) : array{ return [ [Item::ITEM_FRAME, 0, 1] ]; } } ================================================ FILE: src/pocketmine/block/ItemFrameBlock.php ================================================ meta = $meta; } public function getName(){ return "Item Frame Block"; } } ================================================ FILE: src/pocketmine/block/JungleDoor.php ================================================ meta = $meta; } public function getName(){ return "Jungle Door Block"; } public function canBeActivated() : bool { return true; } public function getHardness() { return 3; } public function getToolType(){ return Tool::TYPE_AXE; } public function getDrops(Item $item) : array { return [ [Item::JUNGLE_DOOR, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/JungleDoorBlock.php ================================================ meta = $meta; } public function getName(){ return "Jungle Door Block"; } } ================================================ FILE: src/pocketmine/block/JungleWoodStairs.php ================================================ meta = $meta; } public function getName(){ return "Ladder"; } public function hasEntityCollision(){ return true; } public function isSolid(){ return false; } public function getHardness() { return 0.4; } public function onEntityCollide(Entity $entity){ $entity->resetFallDistance(); $entity->onGround = true; } protected function recalculateBoundingBox() { $f = 0.1875; if($this->meta === 2){ return new AxisAlignedBB( $this->x, $this->y, $this->z + 1 - $f, $this->x + 1, $this->y + 1, $this->z + 1 ); }elseif($this->meta === 3){ return new AxisAlignedBB( $this->x, $this->y, $this->z, $this->x + 1, $this->y + 1, $this->z + $f ); }elseif($this->meta === 4){ return new AxisAlignedBB( $this->x + 1 - $f, $this->y, $this->z, $this->x + 1, $this->y + 1, $this->z + 1 ); }elseif($this->meta === 5){ return new AxisAlignedBB( $this->x, $this->y, $this->z, $this->x + $f, $this->y + 1, $this->z + 1 ); } return null; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if($target->isTransparent() === false){ $faces = [ 2 => 2, 3 => 3, 4 => 4, 5 => 5, ]; if(isset($faces[$face])){ $this->meta = $faces[$face]; $this->getLevel()->setBlock($block, $this, true, true); return true; } } return false; } public function onUpdate($type){ $faces = [ 2 => 3, 3 => 2, 4 => 5, 5 => 4, ]; /*if($this->getSide(0)->getId() === self::AIR){ //Replace with common break method Server::getInstance()->api->entity->drop($this, Item::get(LADDER, 0, 1)); $this->getLevel()->setBlock($this, new Air(), true, true, true); return Level::BLOCK_UPDATE_NORMAL; }*/ if($type === Level::BLOCK_UPDATE_NORMAL){ if(isset($faces[$this->meta])) { if ($this->getSide($faces[$this->meta])->getId() === self::AIR) { $this->getLevel()->useBreakOn($this); } return Level::BLOCK_UPDATE_NORMAL; } } return false; } public function getToolType(){ return Tool::TYPE_AXE; } public function getDrops(Item $item) : array { return [ [$this->id, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/Lapis.php ================================================ isPickaxe() >= 3){ return [ [Item::LAPIS_BLOCK, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/LapisOre.php ================================================ isPickaxe() >= 3){ if($item->getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::LAPIS_ORE, 0, 1], ]; }else{ $fortunel = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortunel = $fortunel > 3 ? 3 : $fortunel; $times = [1,1,2,3,4]; $time = $times[mt_rand(0, $fortunel + 1)]; return [ [Item::DYE, 4, mt_rand(4, 8) * $time], ]; } }else{ return []; } } } ================================================ FILE: src/pocketmine/block/Lava.php ================================================ meta = $meta; } public function getLightLevel(){ return 15; } public function getName(){ return "Lava"; } public function onEntityCollide(Entity $entity){ $entity->fallDistance *= 0.5; $ProtectL = 0; if(!$entity->hasEffect(Effect::FIRE_RESISTANCE)){ $ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_LAVA, 4); if($entity->attack($ev->getFinalDamage(), $ev) === true){ $ev->useArmors(); } $ProtectL = $ev->getFireProtectL(); } $ev = new EntityCombustByBlockEvent($this, $entity, 15, $ProtectL); Server::getInstance()->getPluginManager()->callEvent($ev); if(!$ev->isCancelled()){ $entity->setOnFire($ev->getDuration()); } $entity->resetFallDistance(); } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $ret = $this->getLevel()->setBlock($this, $this, true, false); $this->getLevel()->scheduleUpdate($this, $this->tickRate()); return $ret; } } ================================================ FILE: src/pocketmine/block/Leaves.php ================================================ meta = $meta; } public function getHardness() { return 0.2; } public function getToolType(){ return Tool::TYPE_SHEARS; } public function getBurnChance() : int{ return 30; } public function getBurnAbility() : int{ return 60; } public function getName(){ static $names = [ self::OAK => "Oak Leaves", self::SPRUCE => "Spruce Leaves", self::BIRCH => "Birch Leaves", self::JUNGLE => "Jungle Leaves", ]; return $names[$this->meta & 0x03]; } private function findLog(Block $pos, array $visited, $distance, &$check, $fromSide = null){ ++$check; $index = $pos->x . "." . $pos->y . "." . $pos->z; if(isset($visited[$index])){ return false; } if($pos->getId() === static::WOOD_TYPE){ return true; }elseif($pos->getId() === $this->id and $distance < 3){ $visited[$index] = true; $down = $pos->getSide(0)->getId(); if($down === static::WOOD_TYPE){ return true; } if($fromSide === null){ for($side = 2; $side <= 5; ++$side){ if($this->findLog($pos->getSide($side), $visited, $distance + 1, $check, $side) === true){ return true; } } }else{ //No more loops switch($fromSide){ case 2: if($this->findLog($pos->getSide(2), $visited, $distance + 1, $check, $fromSide) === true){ return true; }elseif($this->findLog($pos->getSide(4), $visited, $distance + 1, $check, $fromSide) === true){ return true; }elseif($this->findLog($pos->getSide(5), $visited, $distance + 1, $check, $fromSide) === true){ return true; } break; case 3: if($this->findLog($pos->getSide(3), $visited, $distance + 1, $check, $fromSide) === true){ return true; }elseif($this->findLog($pos->getSide(4), $visited, $distance + 1, $check, $fromSide) === true){ return true; }elseif($this->findLog($pos->getSide(5), $visited, $distance + 1, $check, $fromSide) === true){ return true; } break; case 4: if($this->findLog($pos->getSide(2), $visited, $distance + 1, $check, $fromSide) === true){ return true; }elseif($this->findLog($pos->getSide(3), $visited, $distance + 1, $check, $fromSide) === true){ return true; }elseif($this->findLog($pos->getSide(4), $visited, $distance + 1, $check, $fromSide) === true){ return true; } break; case 5: if($this->findLog($pos->getSide(2), $visited, $distance + 1, $check, $fromSide) === true){ return true; }elseif($this->findLog($pos->getSide(3), $visited, $distance + 1, $check, $fromSide) === true){ return true; }elseif($this->findLog($pos->getSide(5), $visited, $distance + 1, $check, $fromSide) === true){ return true; } break; } } } return false; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if(($this->meta & 0b00001100) === 0){ $this->meta |= 0x08; $this->getLevel()->setBlock($this, $this, false, true); } }elseif($type === Level::BLOCK_UPDATE_RANDOM){ if(($this->meta & 0b00001100) === 0x08){ $this->meta &= 0x03; $visited = []; $check = 0; Server::getInstance()->getPluginManager()->callEvent($ev = new LeavesDecayEvent($this)); if($ev->isCancelled() or $this->findLog($this, $visited, 0, $check) === true){ $this->getLevel()->setBlock($this, $this, false, false); }else{ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } } return false; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $this->meta |= 0x04; $this->getLevel()->setBlock($this, $this, true); } public function getDrops(Item $item) : array { $drops = []; if($item->isShears() or $item->getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ $drops[] = [$this->id, $this->meta & 0x03, 1]; }else{ $fortunel = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortunel = min(3, $fortunel); $rates = [20,16,12,10]; if(mt_rand(1, $rates[$fortunel]) === 1){ //Saplings $drops[] = [Item::SAPLING, $this->meta & 0x03, 1]; } $rates = [200,180,160,120]; if(($this->meta & 0x03) === self::OAK and mt_rand(1, $rates[$fortunel]) === 1){ //Apples $drops[] = [Item::APPLE, 0, 1]; } } return $drops; } } ================================================ FILE: src/pocketmine/block/Leaves2.php ================================================ meta = $meta; } public function getName(){ static $names = [ self::ACACIA => "Acacia Leaves", self::DARK_OAK => "Dark Oak Leaves", ]; return $names[$this->meta & 0x01]; } public function getDrops(Item $item) : array { $drops = []; if($item->isShears() or $item->getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ $drops[] = [$this->id, $this->meta & 0x01, 1]; }else{ $fortunel = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortunel = min(3, $fortunel); $rates = [20,16,12,10]; if(mt_rand(1, $rates[$fortunel]) === 1){ //Saplings $drops[] = [Item::SAPLING, ($this->meta & 0x01) | 0x04, 1]; } } return $drops; } } ================================================ FILE: src/pocketmine/block/Lever.php ================================================ meta = $meta; } public function canBeActivated() : bool { return true; } public function getName(){ return "Lever"; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ $side = $this->getDamage(); if($this->isActivated()) $side ^= 0x08; $faces = [ 5 => 0, 6 => 0, 3 => 2, 1 => 4, 4 => 3, 2 => 5, 0 => 1, 7 => 1, ]; $block = $this->getSide($faces[$side]); if($block->isTransparent()){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } return false; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if($target->isTransparent() === false){ $faces = [ 3 => 3, 2 => 4, 4 => 2, 5 => 1, ]; if($face === 0){ $to = $player instanceof Player ? $player->getDirection() : 0; $this->meta = ($to % 2 != 1 ? 0 : 7); }elseif($face === 1){ $to = $player instanceof Player ? $player->getDirection() : 0; $this->meta = ($to % 2 != 1 ? 6 : 5); }else{ $this->meta = $faces[$face]; } $this->getLevel()->setBlock($block, $this, true, false); return true; } return false; } public function activate(array $ignore = []){ parent::activate($ignore); $side = $this->meta; if($this->isActivated()) $side ^= 0x08; $faces = [ 5 => 0, 6 => 0, 3 => 2, 1 => 4, 4 => 3, 2 => 5, 0 => 1, 7 => 1, ]; $block = $this->getSide($faces[$side])->getSide(Vector3::SIDE_UP); if(!$this->equals($block)){ $this->activateBlock($block); } $this->checkTorchOn($this->getSide($faces[$side]),[$this->getOppositeSide($faces[$side])]); } public function deactivate(array $ignore = []){ parent::deactivate($ignore); $side = $this->meta; if($this->isActivated()) $side ^= 0x08; $faces = [ 5 => 0, 6 => 0, 3 => 2, 1 => 4, 4 => 3, 2 => 5, 0 => 1, 7 => 1, ]; $block = $this->getSide($faces[$side])->getSide(Vector3::SIDE_UP); if(!$this->equals($block)){ $this->deactivateBlock($block); } $this->checkTorchOff($this->getSide($faces[$side]),[$this->getOppositeSide($faces[$side])]); } public function onActivate(Item $item, Player $player = null){ $this->meta ^= 0x08; $this->getLevel()->setBlock($this, $this, true, false); if($this->isActivated()) $this->activate(); else $this->deactivate(); return true; } public function onBreak(Item $item){ if($this->isActivated()){ $this->meta ^= 0x08; $this->getLevel()->setBlock($this, $this, true, false); $this->deactivate(); } $this->getLevel()->setBlock($this, new Air(), true, false); } public function isActivated(Block $from = null){ return (($this->meta & 0x08) === 0x08); } public function getHardness() { return 0.5; } public function getResistance(){ return 2.5; } public function getDrops(Item $item) : array { return [ [$this->id, 0 ,1], ]; } } ================================================ FILE: src/pocketmine/block/LightWeightedPressurePlate.php ================================================ meta; if($d >= 8){ $d = 0; } return ($d + 1) / 9; } protected function getFlowDecay(Vector3 $pos){ if(!($pos instanceof Block)){ $pos = $this->getLevel()->getBlock($pos); } if($pos->getId() !== $this->getId()){ return -1; }else{ return $pos->getDamage(); } } protected function getEffectiveFlowDecay(Vector3 $pos){ if(!($pos instanceof Block)){ $pos = $this->getLevel()->getBlock($pos); } if($pos->getId() !== $this->getId()){ return -1; } $decay = $pos->getDamage(); if($decay >= 8){ $decay = 0; } return $decay; } public function getFlowVector(){ $vector = new Vector3(0, 0, 0); if($this->temporalVector === null){ $this->temporalVector = new Vector3(0, 0, 0); } $decay = $this->getEffectiveFlowDecay($this); for($j = 0; $j < 4; ++$j){ $x = $this->x; $y = $this->y; $z = $this->z; if($j === 0){ --$x; }elseif($j === 1){ ++$x; }elseif($j === 2){ --$z; }elseif($j === 3){ ++$z; } $sideBlock = $this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $z)); $blockDecay = $this->getEffectiveFlowDecay($sideBlock); if($blockDecay < 0){ if(!$sideBlock->canBeFlowedInto()){ continue; } $blockDecay = $this->getEffectiveFlowDecay($this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y - 1, $z))); if($blockDecay >= 0){ $realDecay = $blockDecay - ($decay - 8); $vector->x += ($sideBlock->x - $this->x) * $realDecay; $vector->y += ($sideBlock->y - $this->y) * $realDecay; $vector->z += ($sideBlock->z - $this->z) * $realDecay; } continue; }else{ $realDecay = $blockDecay - $decay; $vector->x += ($sideBlock->x - $this->x) * $realDecay; $vector->y += ($sideBlock->y - $this->y) * $realDecay; $vector->z += ($sideBlock->z - $this->z) * $realDecay; } } if($this->getDamage() >= 8){ $falling = false; if(!$this->getLevel()->getBlock($this->temporalVector->setComponents($this->x, $this->y, $this->z - 1))->canBeFlowedInto()){ $falling = true; }elseif(!$this->getLevel()->getBlock($this->temporalVector->setComponents($this->x, $this->y, $this->z + 1))->canBeFlowedInto()){ $falling = true; }elseif(!$this->getLevel()->getBlock($this->temporalVector->setComponents($this->x - 1, $this->y, $this->z))->canBeFlowedInto()){ $falling = true; }elseif(!$this->getLevel()->getBlock($this->temporalVector->setComponents($this->x + 1, $this->y, $this->z))->canBeFlowedInto()){ $falling = true; }elseif(!$this->getLevel()->getBlock($this->temporalVector->setComponents($this->x, $this->y + 1, $this->z - 1))->canBeFlowedInto()){ $falling = true; }elseif(!$this->getLevel()->getBlock($this->temporalVector->setComponents($this->x, $this->y + 1, $this->z + 1))->canBeFlowedInto()){ $falling = true; }elseif(!$this->getLevel()->getBlock($this->temporalVector->setComponents($this->x - 1, $this->y + 1, $this->z))->canBeFlowedInto()){ $falling = true; }elseif(!$this->getLevel()->getBlock($this->temporalVector->setComponents($this->x + 1, $this->y + 1, $this->z))->canBeFlowedInto()){ $falling = true; } if($falling){ $vector = $vector->normalize()->add(0, -6, 0); } } return $vector->normalize(); } public function addVelocityToEntity(Entity $entity, Vector3 $vector){ $flow = $this->getFlowVector(); $vector->x += $flow->x; $vector->y += $flow->y; $vector->z += $flow->z; } public function tickRate() : int{ if($this instanceof Water){ return 5; }elseif($this instanceof Lava){ return 30; } return 0; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ $this->checkForHarden(); $this->getLevel()->scheduleUpdate($this, $this->tickRate()); }elseif($type === Level::BLOCK_UPDATE_SCHEDULED){ if($this->temporalVector === null){ $this->temporalVector = new Vector3(0, 0, 0); } $decay = $this->getFlowDecay($this); $multiplier = $this instanceof Lava ? 2 : 1; $flag = true; if($decay > 0){ $smallestFlowDecay = -100; $this->adjacentSources = 0; $smallestFlowDecay = $this->getSmallestFlowDecay($this->level->getBlock($this->temporalVector->setComponents($this->x, $this->y, $this->z - 1)), $smallestFlowDecay); $smallestFlowDecay = $this->getSmallestFlowDecay($this->level->getBlock($this->temporalVector->setComponents($this->x, $this->y, $this->z + 1)), $smallestFlowDecay); $smallestFlowDecay = $this->getSmallestFlowDecay($this->level->getBlock($this->temporalVector->setComponents($this->x - 1, $this->y, $this->z)), $smallestFlowDecay); $smallestFlowDecay = $this->getSmallestFlowDecay($this->level->getBlock($this->temporalVector->setComponents($this->x + 1, $this->y, $this->z)), $smallestFlowDecay); $k = $smallestFlowDecay + $multiplier; if($k >= 8 or $smallestFlowDecay < 0){ $k = -1; } if(($topFlowDecay = $this->getFlowDecay($this->level->getBlock($this->level->getBlock($this->temporalVector->setComponents($this->x, $this->y + 1, $this->z))))) >= 0){ if($topFlowDecay >= 8){ $k = $topFlowDecay; }else{ $k = $topFlowDecay | 0x08; } } if($this->adjacentSources >= 2 and $this instanceof Water){ $bottomBlock = $this->level->getBlock($this->level->getBlock($this->temporalVector->setComponents($this->x, $this->y - 1, $this->z))); if($bottomBlock->isSolid()){ $k = 0; }elseif($bottomBlock instanceof Water and $bottomBlock->getDamage() === 0){ $k = 0; } } if($this instanceof Lava and $decay < 8 and $k < 8 and $k > 1 and mt_rand(0, 4) !== 0){ $k = $decay; $flag = false; } if($k !== $decay){ $decay = $k; if($decay < 0){ $this->getLevel()->setBlock($this, new Air(), true); }else{ $this->getLevel()->setBlock($this, Block::get($this->id, $decay), true); $this->getLevel()->scheduleUpdate($this, $this->tickRate()); } }elseif($flag){ //$this->getLevel()->scheduleUpdate($this, $this->tickRate()); //$this->updateFlow(); } }else{ //$this->updateFlow(); } $bottomBlock = $this->level->getBlock($this->temporalVector->setComponents($this->x, $this->y - 1, $this->z)); if($bottomBlock->canBeFlowedInto() or $bottomBlock instanceof Liquid){ if($this instanceof Lava and $bottomBlock instanceof Water){ $this->getLevel()->setBlock($bottomBlock, Block::get(Item::STONE), true); $this->triggerLavaMixEffects($bottomBlock); return; } if($decay >= 8){ //$this->getLevel()->setBlock($bottomBlock, Block::get($this->id, $decay), true); //$this->getLevel()->scheduleUpdate($bottomBlock, $this->tickRate()); $this->flowIntoBlock($bottomBlock, $decay); }else{ //$this->getLevel()->setBlock($bottomBlock, Block::get($this->id, $decay + 8), true); //$this->getLevel()->scheduleUpdate($bottomBlock, $this->tickRate()); $this->flowIntoBlock($bottomBlock, $decay | 0x08); } }elseif($decay >= 0 and ($decay === 0 or !$bottomBlock->canBeFlowedInto())){ $flags = $this->getOptimalFlowDirections(); $l = $decay + $multiplier; if($decay >= 8){ $l = 1; } if($l >= 8){ $this->checkForHarden(); return; } if($flags[0]){ $this->flowIntoBlock($this->level->getBlock($this->temporalVector->setComponents($this->x - 1, $this->y, $this->z)), $l); } if($flags[1]){ $this->flowIntoBlock($this->level->getBlock($this->temporalVector->setComponents($this->x + 1, $this->y, $this->z)), $l); } if($flags[2]){ $this->flowIntoBlock($this->level->getBlock($this->temporalVector->setComponents($this->x, $this->y, $this->z - 1)), $l); } if($flags[3]){ $this->flowIntoBlock($this->level->getBlock($this->temporalVector->setComponents($this->x, $this->y, $this->z + 1)), $l); } } $this->checkForHarden(); } } private function flowIntoBlock(Block $block, $newFlowDecay){ if($block->canBeFlowedInto()){ if($block instanceof Lava){ $this->triggerLavaMixEffects($block); }elseif($block->getId() > 0){ $this->getLevel()->useBreakOn($block); } $this->getLevel()->setBlock($block, Block::get($this->getId(), $newFlowDecay), true); $this->getLevel()->scheduleUpdate($block, $this->tickRate()); } } private function calculateFlowCost(Block $block, $accumulatedCost, $previousDirection){ $cost = 1000; for($j = 0; $j < 4; ++$j){ if( ($j === 0 and $previousDirection === 1) or ($j === 1 and $previousDirection === 0) or ($j === 2 and $previousDirection === 3) or ($j === 3 and $previousDirection === 2) ){ $x = $block->x; $y = $block->y; $z = $block->z; if($j === 0){ --$x; }elseif($j === 1){ ++$x; }elseif($j === 2){ --$z; }elseif($j === 3){ ++$z; } $blockSide = $this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $z)); if(!$blockSide->canBeFlowedInto() and !($blockSide instanceof Liquid)){ continue; }elseif($blockSide instanceof Liquid and $blockSide->getDamage() === 0){ continue; }elseif($this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y - 1, $z))->canBeFlowedInto()){ return $accumulatedCost; } if($accumulatedCost >= 4){ continue; } $realCost = $this->calculateFlowCost($blockSide, $accumulatedCost + 1, $j); if($realCost < $cost){ $cost = $realCost; } } } return $cost; } public function getHardness() { return 100; } private function getOptimalFlowDirections(){ if($this->temporalVector === null){ $this->temporalVector = new Vector3(0, 0, 0); } for($j = 0; $j < 4; ++$j){ $this->flowCost[$j] = 1000; $x = $this->x; $y = $this->y; $z = $this->z; if($j === 0){ --$x; }elseif($j === 1){ ++$x; }elseif($j === 2){ --$z; }elseif($j === 3){ ++$z; } $block = $this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $z)); if(!$block->canBeFlowedInto() and !($block instanceof Liquid)){ continue; }elseif($block instanceof Liquid and $block->getDamage() === 0){ continue; }elseif($this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y - 1, $z))->canBeFlowedInto()){ $this->flowCost[$j] = 0; }else{ $this->flowCost[$j] = $this->calculateFlowCost($block, 1, $j); } } $minCost = $this->flowCost[0]; for($i = 1; $i < 4; ++$i){ if($this->flowCost[$i] < $minCost){ $minCost = $this->flowCost[$i]; } } for($i = 0; $i < 4; ++$i){ $this->isOptimalFlowDirection[$i] = ($this->flowCost[$i] === $minCost); } return $this->isOptimalFlowDirection; } private function getSmallestFlowDecay(Vector3 $pos, $decay){ $blockDecay = $this->getFlowDecay($pos); if($blockDecay < 0){ return $decay; }elseif($blockDecay === 0){ ++$this->adjacentSources; }elseif($blockDecay >= 8){ $blockDecay = 0; } return ($decay >= 0 && $blockDecay >= $decay) ? $decay : $blockDecay; } private function checkForHarden(){ if($this instanceof Lava){ $colliding = false; for($side = 0; $side <= 5 and !$colliding; ++$side){ $colliding = $this->getSide($side) instanceof Water; } if($colliding){ if($this->getDamage() === 0){ $this->getLevel()->setBlock($this, Block::get(Item::OBSIDIAN), true); }elseif($this->getDamage() <= 4){ $this->getLevel()->setBlock($this, Block::get(Item::COBBLESTONE), true); } $this->triggerLavaMixEffects($this); } } } public function getBoundingBox(){ return null; } public function getDrops(Item $item) : array { return []; } /** * Creates fizzing sound and smoke. Used when lava flows over block or mixes with water. * * @param Vector3 $pos */ protected function triggerLavaMixEffects(Vector3 $pos){ $this->getLevel()->addSound(new FizzSound($pos->add(0.5, 0.5, 0.5), 2.5 + mt_rand(0, 1000) / 1000 * 0.8)); for($i = 0; $i < 8; ++$i){ $this->getLevel()->addParticle(new SmokeParticle($pos->add(mt_rand(0, 80) / 100, 0.5, mt_rand(0, 80) / 100))); } } } ================================================ FILE: src/pocketmine/block/LitPumpkin.php ================================================ meta = $meta; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if($player instanceof Player){ $this->meta = ((int) $player->getDirection() + 5) % 4; } $this->getLevel()->setBlock($block, $this, true, true); if($player != null) { $level = $this->getLevel(); if($player->getServer()->allowSnowGolem) { $block0 = $level->getBlock($block->add(0,-1,0)); $block1 = $level->getBlock($block->add(0,-2,0)); if($block0->getId() == Item::SNOW_BLOCK and $block1->getId() == Item::SNOW_BLOCK) { $level->setBlock($block, new Air()); $level->setBlock($block0, new Air()); $level->setBlock($block1, new Air()); $golem = new SnowGolem($player->getLevel()->getChunk($this->getX() >> 4, $this->getZ() >> 4), new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $this->x), new DoubleTag("", $this->y), new DoubleTag("", $this->z) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", 0), new DoubleTag("", 0), new DoubleTag("", 0) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", 0), new FloatTag("", 0) ]), ])); $golem->spawnToAll(); } } if($player->getServer()->allowIronGolem) { $block0 = $level->getBlock($block->add(0,-1,0)); $block1 = $level->getBlock($block->add(0,-2,0)); $block2 = $level->getBlock($block->add(-1,-1,0)); $block3 = $level->getBlock($block->add(1,-1,0)); $block4 = $level->getBlock($block->add(0,-1,-1)); $block5 = $level->getBlock($block->add(0,-1,1)); if($block0->getId() == Item::IRON_BLOCK and $block1->getId() == Item::IRON_BLOCK) { if($block2->getId() == Item::IRON_BLOCK and $block3->getId() == Item::IRON_BLOCK and $block4->getId() == Item::AIR and $block5->getId() == Item::AIR) { $level->setBlock($block2, new Air()); $level->setBlock($block3, new Air()); }elseif($block4->getId() == Item::IRON_BLOCK and $block5->getId() == Item::IRON_BLOCK and $block2->getId() == Item::AIR and $block3->getId() == Item::AIR){ $level->setBlock($block4, new Air()); $level->setBlock($block5, new Air()); }else return true; $level->setBlock($block, new Air()); $level->setBlock($block0, new Air()); $level->setBlock($block1, new Air()); $golem = new IronGolem($player->getLevel()->getChunk($this->getX() >> 4, $this->getZ() >> 4), new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $this->x), new DoubleTag("", $this->y), new DoubleTag("", $this->z) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", 0), new DoubleTag("", 0), new DoubleTag("", 0) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", 0), new FloatTag("", 0) ]), ])); $golem->spawnToAll(); } } } return true; } } ================================================ FILE: src/pocketmine/block/LitRedstoneLamp.php ================================================ meta = $meta; } public function getName(){ return "Lit Redstone Lamp"; } } ================================================ FILE: src/pocketmine/block/LitRedstoneTorch.php ================================================ meta = $meta; } public function getName(){ return "Lit Redstone Torch"; } } ================================================ FILE: src/pocketmine/block/Melon.php ================================================ getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::MELON_BLOCK, 0, 1], ]; }else{ $fortunel = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortunel = $fortunel > 2 ? 2 : $fortunel; //Note: for Melon level 2 is the same 3 So highest is 2 return [ [Item::MELON_SLICE, 0, mt_rand(3, 7 + $fortunel)], ]; } } } ================================================ FILE: src/pocketmine/block/MelonStem.php ================================================ meta = $meta; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent() === true){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } }elseif($type === Level::BLOCK_UPDATE_RANDOM){ if(mt_rand(0, 2) == 1){ if($this->meta < 0x07){ $block = clone $this; ++$block->meta; Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($this, $block)); if(!$ev->isCancelled()){ $this->getLevel()->setBlock($this, $ev->getNewState(), true); } return Level::BLOCK_UPDATE_RANDOM; }else{ for($side = 2; $side <= 5; ++$side){ $b = $this->getSide($side); if($b->getId() === self::MELON_BLOCK){ return Level::BLOCK_UPDATE_RANDOM; } } $side = $this->getSide(mt_rand(2, 5)); $d = $side->getSide(0); if($side->getId() === self::AIR and ($d->getId() === self::FARMLAND or $d->getId() === self::GRASS or $d->getId() === self::DIRT)){ Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($side, new Melon())); if(!$ev->isCancelled()){ $this->getLevel()->setBlock($side, $ev->getNewState(), true); } } } } return Level::BLOCK_UPDATE_RANDOM; } return false; } public function getDrops(Item $item) : array { return [ [Item::MELON_SEEDS, 0, mt_rand(0, 2)], ]; } } ================================================ FILE: src/pocketmine/block/MobHead.php ================================================ meta = $meta; } public function getHardness(){ return 1; } public function getName(){ return "Mob Head"; } protected function recalculateBoundingBox(){ return new AxisAlignedBB( $this->x + 0.25, $this->y, $this->z + 0.25, $this->x + 0.75, $this->y + 0.5, $this->z + 0.75 ); } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if($face !== 0){ $this->meta = $face; if($face === 1){ $rot = floor(($player->yaw * 16 / 360) + 0.5) & 0x0F; }else{ $rot = $face; } $this->getLevel()->setBlock($block, $this, true); $nbt = new CompoundTag("", [ new StringTag("id", Tile::SKULL), new ByteTag("SkullType", $item->getDamage()), new ByteTag("Rot", $rot), new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z) ]); if($item->hasCustomName()){ $nbt->CustomName = new StringTag("CustomName", $item->getCustomName()); } /** @var Spawnable $tile */ Tile::createTile("Skull", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; } return false; } public function onUpdate($type){ $faces = [ 1 => 0, 2 => 3, 3 => 2, 4 => 5, 5 => 4, ]; if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide($faces[$this->meta])->getId() === self::AIR){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } return parent::onUpdate($type); } public function getDrops(Item $item) : array{ if(($tile = $this->level->getTile($this)) instanceof SkullTile){ return [ [Item::MOB_HEAD, $tile->getType(), 1] ]; } return []; } } ================================================ FILE: src/pocketmine/block/MonsterEggBlock.php ================================================ meta = $meta; } public function getName(){ return "Monster Egg Block"; } } ================================================ FILE: src/pocketmine/block/MonsterSpawner.php ================================================ meta = $meta; } public function getHardness() { return 5; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getName(){ return "Monster Spawner"; } public function canBeActivated() : bool { return true; } public function onActivate(Item $item, Player $player = null){ if($this->getDamage() == 0){ if($item->getId() == Item::SPAWN_EGG){ $tile = $this->getLevel()->getTile($this); if($tile instanceof MobSpawner){ $this->meta = $item->getDamage(); //$this->getLevel()->setBlock($this, $this, true, false); $tile->setEntityId($this->meta); } return true; } } return false; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $this->getLevel()->setBlock($block, $this, true, true); $nbt = new CompoundTag("", [ new StringTag("id", Tile::MOB_SPAWNER), new IntTag("x", $block->x), new IntTag("y", $block->y), new IntTag("z", $block->z), new IntTag("EntityId", 0), ]); if($item->hasCustomBlockData()){ foreach($item->getCustomBlockData() as $key => $v){ $nbt->{$key} = $v; } } Tile::createTile(Tile::MOB_SPAWNER, $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; } public function getDrops(Item $item) : array { return []; } } ================================================ FILE: src/pocketmine/block/MossStone.php ================================================ meta = $meta; } public function getName(){ return "Moss Stone"; } public function getHardness() { return 2; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getDrops(Item $item) : array { if($item->isPickaxe() >= 1){ return [ [Item::MOSS_STONE, $this->meta, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/Mycelium.php ================================================ getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::MYCELIUM, 0, 1], ]; }else{ return [ [Item::DIRT, 0, 1], ]; } } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_RANDOM){ //TODO: light levels $x = mt_rand($this->x - 1, $this->x + 1); $y = mt_rand($this->y - 2, $this->y + 2); $z = mt_rand($this->z - 1, $this->z + 1); $block = $this->getLevel()->getBlock(new Vector3($x, $y, $z)); if($block->getId() === Block::DIRT){ if($block->getSide(1) instanceof Transparent){ Server::getInstance()->getPluginManager()->callEvent($ev = new BlockSpreadEvent($block, $this, new Mycelium())); if(!$ev->isCancelled()){ $this->getLevel()->setBlock($block, $ev->getNewState()); } } } } } } ================================================ FILE: src/pocketmine/block/NetherBrick.php ================================================ isPickaxe() >= 1){ return [ [Item::NETHER_BRICKS, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/NetherBrickFence.php ================================================ meta = $meta; } public function getHardness(){ return 2; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getName(){ return "Nether Brick Fence"; } public function canConnect(Block $block){ return ($block instanceof NetherBrickFence) or ($block->isSolid() and !$block->isTransparent()); } public function getDrops(Item $item) : array{ if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [$this->id, $this->meta, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/NetherBrickStairs.php ================================================ meta = $meta; } } ================================================ FILE: src/pocketmine/block/NetherQuartzOre.php ================================================ isPickaxe() >= Tool::TIER_WOODEN){ if($item->getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::NETHER_QUARTZ_ORE, 0, 1], ]; }else{ $fortunel = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortunel = $fortunel > 3 ? 3 : $fortunel; $times = [1,1,2,3,4]; $time = $times[mt_rand(0, $fortunel + 1)]; return [ [Item::NETHER_QUARTZ, 0, $time], ]; } }else{ return []; } } } ================================================ FILE: src/pocketmine/block/NetherReactor.php ================================================ meta = $meta; } public function getName(){ return "Nether Reactor"; } public function canBeActivated() : bool { return true; } } ================================================ FILE: src/pocketmine/block/NetherWart.php ================================================ meta = $meta; } public function getName(){ return "Nether Wart Block"; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(0); if($down->getId() === self::SOUL_SAND){ $this->getLevel()->setBlock($block, $this, true, true); return true; } return false; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent() === true){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } }elseif($type === Level::BLOCK_UPDATE_RANDOM){ if(mt_rand(0, 12) == 1){//only have 0-3 So maybe slowly if($this->meta < 0x03){//0x03 $block = clone $this; ++$block->meta; Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($this, $block)); if(!$ev->isCancelled()){ $this->getLevel()->setBlock($this, $ev->getNewState(), true, true); }else{ return Level::BLOCK_UPDATE_RANDOM; } } }else{ return Level::BLOCK_UPDATE_RANDOM; } } return false; } public function getDrops(Item $item) : array { $drops = []; if($this->meta >= 0x03){ $fortunel = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortunel = $fortunel > 3 ? 3 : $fortunel; $drops[] = [Item::NETHER_WART, 0, mt_rand(2, 4 + $fortunel)]; }else{ $drops[] = [Item::NETHER_WART, 0, 1]; } return $drops; } } ================================================ FILE: src/pocketmine/block/NetherWartBlock.php ================================================ meta = $meta; } public function getName(){ return "Nether Wart Block"; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(0); if($down->getId() === self::SOUL_SAND){ $this->getLevel()->setBlock($block, $this, true, true); return true; } return false; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent() === true){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } }elseif($type === Level::BLOCK_UPDATE_RANDOM){ if(mt_rand(0, 12) == 1){//only have 0-3 So maybe slowly if($this->meta < 0x03){//0x03 $block = clone $this; ++$block->meta; Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($this, $block)); if(!$ev->isCancelled()){ $this->getLevel()->setBlock($this, $ev->getNewState(), true, true); }else{ return Level::BLOCK_UPDATE_RANDOM; } } }else{ return Level::BLOCK_UPDATE_RANDOM; } } return false; } public function getDrops(Item $item) : array { $drops = []; if($this->meta >= 0x03){ $fortunel = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortunel = $fortunel > 3 ? 3 : $fortunel; $drops[] = [Item::NETHER_WART, 0, mt_rand(2, 4 + $fortunel)]; }else{ $drops[] = [Item::NETHER_WART, 0, 1]; } return $drops; } } ================================================ FILE: src/pocketmine/block/Netherrack.php ================================================ isPickaxe() >= Tool::TIER_WOODEN){ return [ [Item::NETHERRACK, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/Noteblock.php ================================================ meta = $meta; } public function getHardness(){ return 0.8; } public function getResistance(){ return 4; } public function getToolType(){ return Tool::TYPE_AXE; } public function canBeActivated() : bool{ return true; } public function getStrength(){ if($this->meta < 24) $this->meta++; else $this->meta = 0; $this->getLevel()->setBlock($this, $this); return $this->meta * 1; } public function getInstrument(){ $below = $this->getSide(Vector3::SIDE_DOWN); switch($below->getId()){ case Block::WOOD: case Block::WOOD2: case Block::WOODEN_PLANK: case Block::WOODEN_SLABS: case Block::DOUBLE_WOOD_SLABS: case Block::OAK_WOODEN_STAIRS: case Block::SPRUCE_WOODEN_STAIRS: case Block::BIRCH_WOODEN_STAIRS: case Block::JUNGLE_WOODEN_STAIRS: case Block::ACACIA_WOODEN_STAIRS: case Block::DARK_OAK_WOODEN_STAIRS: case Block::FENCE: case Block::FENCE_GATE: case Block::FENCE_GATE_SPRUCE: case Block::FENCE_GATE_BIRCH: case Block::FENCE_GATE_JUNGLE: case Block::FENCE_GATE_DARK_OAK: case Block::FENCE_GATE_ACACIA: case Block::SPRUCE_WOOD_STAIRS: case Block::BOOKSHELF: case Block::CHEST: case Block::CRAFTING_TABLE: case Block::SIGN_POST: case Block::WALL_SIGN: case Block::DOOR_BLOCK: case Block::NOTEBLOCK: return NoteblockSound::INSTRUMENT_BASS; case Block::SAND: case Block::SOUL_SAND: return NoteblockSound::INSTRUMENT_TABOUR; case Block::GLASS: case Block::GLASS_PANE: return NoteblockSound::INSTRUMENT_CLICK; case Block::STONE: case Block::COBBLESTONE: case Block::SANDSTONE: case Block::MOSS_STONE: case Block::BRICKS: case Block::STONE_BRICK: case Block::NETHER_BRICKS: case Block::QUARTZ_BLOCK: case Block::SLAB: case Block::COBBLESTONE_STAIRS: case Block::BRICK_STAIRS: case Block::STONE_BRICK_STAIRS: case Block::NETHER_BRICKS_STAIRS: case Block::SANDSTONE_STAIRS: case Block::QUARTZ_STAIRS: case Block::COBBLESTONE_WALL: case Block::NETHER_BRICK_FENCE: case Block::BEDROCK: case Block::GOLD_ORE: case Block::IRON_ORE: case Block::COAL_ORE: case Block::LAPIS_ORE: case Block::DIAMOND_ORE: case Block::REDSTONE_ORE: case Block::GLOWING_REDSTONE_ORE: case Block::EMERALD_ORE: case Block::FURNACE: case Block::BURNING_FURNACE: case Block::OBSIDIAN: case Block::MONSTER_SPAWNER: case Block::NETHERRACK: case Block::ENCHANTING_TABLE: case Block::END_STONE: case Block::STAINED_CLAY: case Block::COAL_BLOCK: return NoteblockSound::INSTRUMENT_BASS_DRUM; } return NoteblockSound::INSTRUMENT_PIANO; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $this->getLevel()->setBlock($this, $this, true, true); $nbt = new CompoundTag("", [ new StringTag("id", Tile::NOTEBLOCK), new IntTag("x", $block->x), new IntTag("y", $block->y), new IntTag("z", $block->z), new ByteTag("note", 0) ]); Tile::createTile(Tile::NOTEBLOCK, $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); return true; } public function onActivate(Item $item, Player $player = null){ $tile = $this->getLevel()->getTile($this); if($tile instanceof Music){ $instrument = $this->getInstrument(); $pitch = $tile->getNote() + 1; if($pitch < 0 or $pitch > 24){ $pitch = 0; } $tile->setNote($pitch); $this->getLevel()->addSound(new NoteblockSound($this, $instrument, $pitch)); $pk = new BlockEventPacket(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->case1 = $instrument; $pk->case2 = $pitch; $player->dataPacket($pk); $this->getLevel()->addChunkPacket($tile->chunk->getX(), $tile->chunk->getZ(), $pk); return true; } return false; } public function getName(){ return "Noteblock"; } } ================================================ FILE: src/pocketmine/block/Obsidian.php ================================================ temporalVector === null){ $this->temporalVector = new Vector3(0, 0, 0); } } public function getName(){ return "Obsidian"; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getHardness(){ return 35; } public function getDrops(Item $item) : array { if($item->isPickaxe() >= 5){ return [ [Item::OBSIDIAN, 0, 1], ]; }else{ return []; } } public function onBreak(Item $item) { parent::onBreak($item); if($this->getLevel()->getServer()->netherEnabled){ for($i = 0;$i <= 6;$i++){ if($this->getSide($i)->getId() == self::PORTAL){ break; } if($i == 6){ return; } } $block = $this->getSide($i); if($this->getLevel()->getBlock($this->temporalVector->setComponents($block->x - 1, $block->y, $block->z))->getId() == Block::PORTAL or $this->getLevel()->getBlock($this->temporalVector->setComponents($block->x + 1, $block->y, $block->z))->getId() == Block::PORTAL){ for($x = $block->x;$this->getLevel()->getBlock($this->temporalVector->setComponents($x, $block->y, $block->z))->getId() == Block::PORTAL;$x++){ for($y = $block->y;$this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $block->z))->getId() == Block::PORTAL;$y++){ $this->getLevel()->setBlock($this->temporalVector->setComponents($x, $y, $block->z), new Air()); } for($y = $block->y - 1;$this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $block->z))->getId() == Block::PORTAL;$y--){ $this->getLevel()->setBlock($this->temporalVector->setComponents($x, $y, $block->z), new Air()); } } for($x = $block->x - 1;$this->getLevel()->getBlock($this->temporalVector->setComponents($x, $block->y, $block->z))->getId() == Block::PORTAL;$x--){ for($y = $block->y;$this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $block->z))->getId() == Block::PORTAL;$y++){ $this->getLevel()->setBlock($this->temporalVector->setComponents($x, $y, $block->z), new Air()); } for($y = $block->y - 1;$this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $block->z))->getId() == Block::PORTAL;$y--){ $this->getLevel()->setBlock($this->temporalVector->setComponents($x, $y, $block->z), new Air()); } } }else{ for($z = $block->z;$this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $block->y, $z))->getId() == Block::PORTAL;$z++){ for($y = $block->y;$this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $y, $z))->getId() == Block::PORTAL;$y++){ $this->getLevel()->setBlock($this->temporalVector->setComponents($block->x, $y, $z), new Air()); } for($y = $block->y - 1;$this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $y, $z))->getId() == Block::PORTAL;$y--){ $this->getLevel()->setBlock($this->temporalVector->setComponents($block->x, $y, $z), new Air()); } } for($z = $block->z - 1;$this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $block->y, $z))->getId() == Block::PORTAL;$z--){ for($y = $block->y;$this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $y, $z))->getId() == Block::PORTAL;$y++){ $this->getLevel()->setBlock($this->temporalVector->setComponents($block->x, $y, $z), new Air()); } for($y = $block->y - 1;$this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $y, $z))->getId() == Block::PORTAL;$y--){ $this->getLevel()->setBlock($this->temporalVector->setComponents($block->x, $y, $z), new Air()); } } } } } } ================================================ FILE: src/pocketmine/block/PackedIce.php ================================================ getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::PACKED_ICE, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/Piston.php ================================================ meta = $meta; } public function getName(){ return "Piston"; } } ================================================ FILE: src/pocketmine/block/PistonHead.php ================================================ meta = $meta; } public function getName(){ return "Piston Head"; } } ================================================ FILE: src/pocketmine/block/Planks.php ================================================ meta = $meta; } public function getHardness() { return 2; } public function getToolType(){ return Tool::TYPE_AXE; } public function getBurnChance() : int{ return 5; } public function getBurnAbility() : int{ return 20; } public function getName(){ static $names = [ self::OAK => "Oak Wood Planks", self::SPRUCE => "Spruce Wood Planks", self::BIRCH => "Birch Wood Planks", self::JUNGLE => "Jungle Wood Planks", self::ACACIA => "Acacia Wood Planks", self::DARK_OAK => "Dark Oak Wood Planks", 6 => "Unknown Planks", 7 => "Unknown Planks" ]; return $names[$this->meta & 0x07]; } } ================================================ FILE: src/pocketmine/block/Podzol.php ================================================ getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::PODZOL, 0, 1], ]; }else{ return [ [Item::DIRT, 0, 1], ]; } } } ================================================ FILE: src/pocketmine/block/Portal.php ================================================ temporalVector === null){ $this->temporalVector = new Vector3(0, 0, 0); } } public function getName(){ return "Portal"; } public function getHardness() { return -1; } public function getResistance(){ return 0; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function canPassThrough(){ return true; } public function hasEntityCollision(){ return true; } public function onBreak(Item $item){ $block = $this; if($this->getLevel()->getBlock($this->temporalVector->setComponents($block->x - 1, $block->y, $block->z))->getId() == Block::PORTAL or $this->getLevel()->getBlock($this->temporalVector->setComponents($block->x + 1, $block->y, $block->z))->getId() == Block::PORTAL){//x方向 for($x = $block->x;$this->getLevel()->getBlock($this->temporalVector->setComponents($x, $block->y, $block->z))->getId() == Block::PORTAL;$x++){ for($y = $block->y;$this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $block->z))->getId() == Block::PORTAL;$y++){ $this->getLevel()->setBlock($this->temporalVector->setComponents($x, $y, $block->z), new Air()); } for($y = $block->y - 1;$this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $block->z))->getId() == Block::PORTAL;$y--){ $this->getLevel()->setBlock($this->temporalVector->setComponents($x, $y, $block->z), new Air()); } } for($x = $block->x - 1;$this->getLevel()->getBlock($this->temporalVector->setComponents($x, $block->y, $block->z))->getId() == Block::PORTAL;$x--){ for($y = $block->y;$this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $block->z))->getId() == Block::PORTAL;$y++){ $this->getLevel()->setBlock($this->temporalVector->setComponents($x, $y, $block->z), new Air()); } for($y = $block->y - 1;$this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $block->z))->getId() == Block::PORTAL;$y--){ $this->getLevel()->setBlock($this->temporalVector->setComponents($x, $y, $block->z), new Air()); } } }else{//z方向 for($z = $block->z;$this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $block->y, $z))->getId() == Block::PORTAL;$z++){ for($y = $block->y;$this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $y, $z))->getId() == Block::PORTAL;$y++){ $this->getLevel()->setBlock($this->temporalVector->setComponents($block->x, $y, $z), new Air()); } for($y = $block->y - 1;$this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $y, $z))->getId() == Block::PORTAL;$y--){ $this->getLevel()->setBlock($this->temporalVector->setComponents($block->x, $y, $z), new Air()); } } for($z = $block->z - 1;$this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $block->y, $z))->getId() == Block::PORTAL;$z--){ for($y = $block->y;$this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $y, $z))->getId() == Block::PORTAL;$y++){ $this->getLevel()->setBlock($this->temporalVector->setComponents($block->x, $y, $z), new Air()); } for($y = $block->y - 1;$this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $y, $z))->getId() == Block::PORTAL;$y--){ $this->getLevel()->setBlock($this->temporalVector->setComponents($block->x, $y, $z), new Air()); } } } parent::onBreak($item); } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if($player instanceof Player){ $this->meta = $player->getDirection() & 0x01; } $this->getLevel()->setBlock($block, $this, true, true); return true; } public function getDrops(Item $item) : array { return []; } } ================================================ FILE: src/pocketmine/block/PortalBlock.php ================================================ meta = $meta; } public function getName(){ return "Portal Block"; } } ================================================ FILE: src/pocketmine/block/Potato.php ================================================ meta = $meta; } public function getName(){ return "Potato Block"; } public function getDrops(Item $item) : array { $drops = []; if($this->meta >= 0x07){ $fortunel = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortunel = $fortunel > 3 ? 3 : $fortunel; $drops[] = [Item::POTATO, 0, mt_rand(1, 4 + $fortunel)]; }else{ $drops[] = [Item::POTATO, 0, 1]; } return $drops; } } ================================================ FILE: src/pocketmine/block/PoweredComparatorBlock.php ================================================ meta = $meta; } public function getName(){ return "Powered Comparator Block"; } } ================================================ FILE: src/pocketmine/block/PoweredRail.php ================================================ meta = $meta;//0,1,2,3,4,5 } public function getName(){ return "PoweredRail"; } protected function update(){ return true; } /** * @param Rail $block * @return bool */ public function canConnect(Rail $block){ if($this->distanceSquared($block) > 2){ return false; } /** @var Vector3 [] $blocks */ if(count($blocks = self::check($this)) == 2){ return false; } if(isset($blocks[0])){ $v3 = $blocks[0]->subtract($this); $v33 = $block->subtract($this); if(abs($v3->x) == abs($v33->z) and abs($v3->z) == abs($v33->x)){ return false; } } return $blocks; } public function isBlock(Block $block){ if($block instanceof AIR){ return false; } return $block; } public function connect(Rail $rail, $force = false){ if(!$force){ $connected = $this->canConnect($rail); if(!is_array($connected)){ return false; } /** @var Vector3 [] $connected */ $connected[] = $rail; switch(count($connected)){ case 1: $v3 = $connected[0]->subtract($this); $this->meta = (($v3->y != 1) ? ($v3->x == 0 ? 0 : 1) : ($v3->z == 0 ? ($v3->x / -2) + 2.5 : ($v3->z / 2) + 4.5)); break; case 2: $subtract = []; foreach($connected as $key => $value){ $subtract[$key] = $value->subtract($this); } if(abs($subtract[0]->x) == abs($subtract[1]->z) and abs($subtract[1]->x) == abs($subtract[0]->z)){ $v3 = $connected[0]->subtract($this)->add($connected[1]->subtract($this)); $this->meta = $v3->x == 1 ? ($v3->z == 1 ? 6 : 9) : ($v3->z == 1 ? 7 : 8); }elseif($subtract[0]->y == 1 or $subtract[1]->y == 1){ $v3 = $subtract[0]->y == 1 ? $subtract[0] : $subtract[1]; $this->meta = $v3->x == 0 ? ($v3->x == -1 ? 4 : 5) : ($v3->x == 1 ? 2 : 3); }else{ $this->meta = $subtract[0]->x == 0 ? 0 : 1; } break; default: break; } } $this->level->setBlock($this, Block::get($this->id, $this->meta), true, true); return true; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $downBlock = $this->getSide(Vector3::SIDE_DOWN); if($downBlock instanceof Rail or !$this->isBlock($downBlock)){//判断是否可以放置 return false; } $arrayXZ = [[1, 0], [0, 1], [-1, 0], [0, -1]]; $arrayY = [0, 1, -1]; /** @var Vector3 [] $connected */ $connected = []; foreach($arrayXZ as $key => $xz){ foreach($arrayY as $y){ $v3 = (new Vector3($xz[0], $y, $xz[1]))->add($this); $block = $this->level->getBlock($v3); if($block instanceof Rail){ if($block->connect($this)){ $connected[] = $v3; //感觉这里怪怪的 if($key <= 1){ $xz = $arrayXZ[$key + 1]; foreach($arrayY as $yy){ $v3 = (new Vector3($xz[0], $yy, $xz[1]))->add($this); $block = $this->level->getBlock($v3); if($block instanceof Rail){ if($block->connect($this)){ $connected[] = $v3; } } } } break; } } } if(count($connected) >= 1){ break; } } switch(count($connected)){ case 1: $v3 = $connected[0]->subtract($this); $this->meta = (($v3->y != 1) ? ($v3->x == 0 ? 0 : 1) : ($v3->z == 0 ? ($v3->x / -2) + 2.5 : ($v3->z / 2) + 4.5)); break; case 2: $subtract = []; foreach($connected as $key => $value){ $subtract[$key] = $value->subtract($this); } if(abs($subtract[0]->x) == abs($subtract[1]->z) and abs($subtract[1]->x) == abs($subtract[0]->z)){ $v3 = $connected[0]->subtract($this)->add($connected[1]->subtract($this)); $this->meta = $v3->x == 1 ? ($v3->z == 1 ? 6 : 9) : ($v3->z == 1 ? 7 : 8); }elseif($subtract[0]->y == 1 or $subtract[1]->y == 1){ $v3 = $subtract[0]->y == 1 ? $subtract[0] : $subtract[1]; $this->meta = $v3->x == 0 ? ($v3->x == -1 ? 4 : 5) : ($v3->x == 1 ? 2 : 3); }else{ $this->meta = $subtract[0]->x == 0 ? 0 : 1; } break; default: break; } $this->level->setBlock($this, Block::get($this->id, $this->meta), true, true); return true; } public function getHardness() { return 0.7; } public function canPassThrough(){ return true; } } ================================================ FILE: src/pocketmine/block/PoweredRepeater.php ================================================ meta = $meta; } public function getName(){ return "Powered Repeater"; } public function canBeActivated() : bool{ return true; } public function getStrength(){ return 15; } public function getDirection() : int{ $direction = 0; switch($this->meta % 4){ case 0: $direction = 3; break; case 1: $direction = 4; break; case 2: $direction = 2; break; case 3: $direction = 5; break; } return $direction; } public function getOppositeDirection() : int{ return $this->getOppositeSide($this->getDirection()); } public function getDelayLevel() : int{ return round(($this->meta - ($this->meta % 4)) / 4) + 1; } public function isActivated(Block $from = null){ if(!$from instanceof Block){ return false; }else{ if($this->y != $from->y){ return false; } if($from->equals($this->getSide($this->getOppositeDirection()))){ return true; } return false; } } public function activate(array $ignore = []){ if($this->canCalc()){ if($this->id != self::POWERED_REPEATER_BLOCK){ $this->id = self::POWERED_REPEATER_BLOCK; $this->getLevel()->setBlock($this, $this, true, false); } $this->getLevel()->setBlockTempData($this, self::ACTION_ACTIVATE); $this->getLevel()->scheduleUpdate($this, $this->getDelayLevel() * 2); } } public function deactivate(array $ignore = []){ if($this->canCalc()){ if($this->id != self::UNPOWERED_REPEATER_BLOCK){ $this->id = self::UNPOWERED_REPEATER_BLOCK; $this->getLevel()->setBlock($this, $this, true, false); } $this->getLevel()->setBlockTempData($this, self::ACTION_DEACTIVATE); $this->getLevel()->scheduleUpdate($this, $this->getDelayLevel() * 2); } } public function deactivateImmediately(){ $this->deactivateBlock($this->getSide($this->getOppositeDirection())); $this->deactivateBlock($this->getSide(Vector3::SIDE_DOWN, 2));//TODO: improve } public function onUpdate($type){ if($type == Level::BLOCK_UPDATE_SCHEDULED){ if($this->getLevel()->getBlockTempData($this) == self::ACTION_ACTIVATE){ $this->activateBlock($this->getSide($this->getOppositeDirection())); $this->activateBlock($this->getSide(Vector3::SIDE_DOWN, 2)); }elseif($this->getLevel()->getBlockTempData($this) == self::ACTION_DEACTIVATE){ $this->deactivateImmediately(); } $this->getLevel()->setBlockTempData($this); } return $type; } public function onActivate(Item $item, Player $player = null){ $meta = $this->meta + 4; if($meta > 15) $this->meta = $this->meta % 4; else $this->meta = $meta; $this->getLevel()->setBlock($this, $this, true, false); return true; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if($player instanceof Player){ $this->meta = ((int) $player->getDirection() + 5) % 4; } $this->getLevel()->setBlock($block, $this, true, false); if($this->checkPower($this)){ $this->activate(); } } public function onBreak(Item $item){ $this->deactivateImmediately(); $this->getLevel()->setBlock($this, new Air(), true, false); $this->getLevel()->setBlockTempData($this); } public function getDrops(Item $item) : array{ return [ [Item::REPEATER, 0, 1] ]; } } ================================================ FILE: src/pocketmine/block/PoweredRepeaterBlock.php ================================================ meta = $meta; } public function getName(){ return "Powered Repeater Block"; } } ================================================ FILE: src/pocketmine/block/PressurePlate.php ================================================ meta = $meta; } public function hasEntityCollision(){ return true; } public function onEntityCollide(Entity $entity){ if($this->getLevel()->getServer()->redstoneEnabled and $this->canActivate){ if(!$this->isActivated()){ $this->meta = 1; $this->getLevel()->setBlock($this, $this, true, false); $this->getLevel()->addSound(new GenericSound($this, 1000)); } if(!$this->isActivated() or ($this->isActivated() and ($this->getLevel()->getServer()->getTick() % 30) == 0)){ $this->activate(); } } } public function isActivated(Block $from = null){ return ($this->meta == 0) ? false : true; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ $below = $this->getSide(Vector3::SIDE_DOWN); if($below instanceof Transparent){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } /*if($type == Level::BLOCK_UPDATE_SCHEDULED){ if($this->isActivated()){ if(!$this->isCollided()){ $this->meta = 0; $this->getLevel()->setBlock($this, $this, true, false); $this->deactivate(); return Level::BLOCK_UPDATE_SCHEDULED; } } }*/ return true; } public function checkActivation(){ if($this->isActivated()){ if((($this->getLevel()->getServer()->getTick() - $this->activateTime)) >= 3){ $this->meta = 0; $this->getLevel()->setBlock($this, $this, true, false); $this->deactivate(); } } } /*public function isCollided(){ foreach($this->getLevel()->getEntities() as $p){ $blocks = $p->getBlocksAround(); if(isset($blocks[Level::blockHash($this->x, $this->y, $this->z)])) return true; } return false; }*/ public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $below = $this->getSide(Vector3::SIDE_DOWN); if($below instanceof Transparent) return; else $this->getLevel()->setBlock($block, $this, true, false); } public function onBreak(Item $item){ if($this->isActivated()){ $this->meta = 0; $this->deactivate(); } $this->canActivate = false; $this->getLevel()->setBlock($this, new Air(), true); } public function getHardness() { return 0.5; } public function getResistance(){ return 2.5; } } ================================================ FILE: src/pocketmine/block/Prismarine.php ================================================ meta = $meta; } public function getHardness(){ return 1.5; } public function getName(){ static $names = [ self::NORMAL => "Prismarine", self::DARK => "Dark Prismarine", self::BRICKS => "Prismarine Bricks", ]; return $names[$this->meta & 0x0f]; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getDrops(Item $item) : array{ if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [$this->id, $this->meta & 0x0f, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/Pumpkin.php ================================================ meta = ((int) $player->getDirection() + 5) % 4; } $this->getLevel()->setBlock($block, $this, true, true); if($player != null) { $level = $this->getLevel(); if($player->getServer()->allowSnowGolem) { $block0 = $level->getBlock($block->add(0,-1,0)); $block1 = $level->getBlock($block->add(0,-2,0)); if($block0->getId() == Item::SNOW_BLOCK and $block1->getId() == Item::SNOW_BLOCK) { $level->setBlock($block, new Air()); $level->setBlock($block0, new Air()); $level->setBlock($block1, new Air()); $golem = new SnowGolem($player->getLevel()->getChunk($this->getX() >> 4, $this->getZ() >> 4), new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $this->x), new DoubleTag("", $this->y), new DoubleTag("", $this->z) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", 0), new DoubleTag("", 0), new DoubleTag("", 0) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", 0), new FloatTag("", 0) ]), ])); $golem->spawnToAll(); } } if($player->getServer()->allowIronGolem) { $block0 = $level->getBlock($block->add(0,-1,0)); $block1 = $level->getBlock($block->add(0,-2,0)); $block2 = $level->getBlock($block->add(-1,-1,0)); $block3 = $level->getBlock($block->add(1,-1,0)); $block4 = $level->getBlock($block->add(0,-1,-1)); $block5 = $level->getBlock($block->add(0,-1,1)); if($block0->getId() == Item::IRON_BLOCK and $block1->getId() == Item::IRON_BLOCK) { if($block2->getId() == Item::IRON_BLOCK and $block3->getId() == Item::IRON_BLOCK and $block4->getId() == Item::AIR and $block5->getId() == Item::AIR) { $level->setBlock($block2, new Air()); $level->setBlock($block3, new Air()); }elseif($block4->getId() == Item::IRON_BLOCK and $block5->getId() == Item::IRON_BLOCK and $block2->getId() == Item::AIR and $block3->getId() == Item::AIR){ $level->setBlock($block4, new Air()); $level->setBlock($block5, new Air()); }else return true; $level->setBlock($block, new Air()); $level->setBlock($block0, new Air()); $level->setBlock($block1, new Air()); $golem = new IronGolem($player->getLevel()->getChunk($this->getX() >> 4, $this->getZ() >> 4), new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $this->x), new DoubleTag("", $this->y), new DoubleTag("", $this->z) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", 0), new DoubleTag("", 0), new DoubleTag("", 0) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", 0), new FloatTag("", 0) ]), ])); $golem->spawnToAll(); } } } return true; } } ================================================ FILE: src/pocketmine/block/PumpkinStem.php ================================================ meta = $meta; } public function getName(){ return "Pumpkin Stem"; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent()){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } }elseif($type === Level::BLOCK_UPDATE_RANDOM){ if(mt_rand(0, 2) == 1){ if($this->meta < 0x07){ $block = clone $this; ++$block->meta; Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($this, $block)); if(!$ev->isCancelled()){ $this->getLevel()->setBlock($this, $ev->getNewState(), true); } return Level::BLOCK_UPDATE_RANDOM; }else{ for($side = 2; $side <= 5; ++$side){ $b = $this->getSide($side); if($b->getId() === self::PUMPKIN){ return Level::BLOCK_UPDATE_RANDOM; } } $side = $this->getSide(mt_rand(2, 5)); $d = $side->getSide(0); if($side->getId() === self::AIR and ($d->getId() === self::FARMLAND or $d->getId() === self::GRASS or $d->getId() === self::DIRT)){ Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($side, new Pumpkin())); if(!$ev->isCancelled()){ $this->getLevel()->setBlock($side, $ev->getNewState(), true); } } } } return Level::BLOCK_UPDATE_RANDOM; } return false; } public function getDrops(Item $item) : array { return [ [Item::PUMPKIN_SEEDS, 0, mt_rand(0, 2)], ]; } } ================================================ FILE: src/pocketmine/block/PurpurBlock.php ================================================ meta = $meta; } public function getName(){ static $names = [ 0 => "Purpur Block", 2 => "Purpur Pillar", ]; return $names[$this->meta & 0x0f]??"Purpur Block"; } public function getHardness(){ return 1.5; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if($this->meta === 2){ //Quartz pillar block and chiselled quartz have different orientations $faces = [ 0 => 0, 1 => 0, 2 => 0b1000, 3 => 0b1000, 4 => 0b0100, 5 => 0b0100, ]; $this->meta = ($this->meta & 0x03) | $faces[$face]; } $this->getLevel()->setBlock($block, $this, true, true); return true; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getDrops(Item $item) : array { if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [$this->id, $this->meta & 0x0f, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/PurpurStairs.php ================================================ meta = $meta; } public function getHardness() { return 0.8; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getName(){ return "Purpur Stairs"; } } ================================================ FILE: src/pocketmine/block/Quartz.php ================================================ meta = $meta; } public function getHardness() { return 0.8; } public function getName(){ static $names = [ 0 => "Quartz Block", 1 => "Chiseled Quartz Block", 2 => "Quartz Pillar", 3 => "Quartz Block", ]; return $names[$this->meta & 0x03]; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if($this->meta === 1 or $this->meta === 2){ //Quartz pillar block and chiselled quartz have different orientations $faces = [ 0 => 0, 1 => 0, 2 => 0b1000, 3 => 0b1000, 4 => 0b0100, 5 => 0b0100, ]; $this->meta = ($this->meta & 0x03) | $faces[$face]; } $this->getLevel()->setBlock($block, $this, true, true); return true; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getDrops(Item $item) : array { if($item->isPickaxe() >= 1){ return [ [Item::QUARTZ_BLOCK, $this->meta & 0x03, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/QuartzStairs.php ================================================ meta = $meta; } public function getHardness() { return 0.8; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getName(){ return "Quartz Stairs"; } } ================================================ FILE: src/pocketmine/block/Rail.php ================================================ meta = $meta; } public function getName(){ return "Rail"; } protected function update(){ return true; } /** * @param Rail $block * @return bool */ public function canConnect(Rail $block){ if($this->distanceSquared($block) > 2){ return false; } /** @var Vector3 [] $blocks */ if(count($blocks = self::check($this)) == 2){ return false; } return $blocks; } public function isBlock(Block $block){ if($block instanceof AIR){ return false; } return $block; } public function connect(Rail $rail, $force = false){ if(!$force){ $connected = $this->canConnect($rail); if(!is_array($connected)){ return false; } /** @var Vector3 [] $connected */ $connected[] = $rail; switch(count($connected)){ case 1: $v3 = $connected[0]->subtract($this); $this->meta = (($v3->y != 1) ? ($v3->x == 0 ? 0 : 1) : ($v3->z == 0 ? ($v3->x / -2) + 2.5 : ($v3->z / 2) + 4.5)); break; case 2: $subtract = []; foreach($connected as $key => $value){ $subtract[$key] = $value->subtract($this); } if(abs($subtract[0]->x) == abs($subtract[1]->z) and abs($subtract[1]->x) == abs($subtract[0]->z)){ $v3 = $connected[0]->subtract($this)->add($connected[1]->subtract($this)); $this->meta = $v3->x == 1 ? ($v3->z == 1 ? 6 : 9) : ($v3->z == 1 ? 7 : 8); }elseif($subtract[0]->y == 1 or $subtract[1]->y == 1){ $v3 = $subtract[0]->y == 1 ? $subtract[0] : $subtract[1]; $this->meta = $v3->x == 0 ? ($v3->z == -1 ? 4 : 5) : ($v3->x == 1 ? 2 : 3); }else{ $this->meta = $subtract[0]->x == 0 ? 0 : 1; } break; default: break; } } $this->level->setBlock($this, Block::get($this->id, $this->meta), true, true); return true; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $downBlock = $this->getSide(Vector3::SIDE_DOWN); if($downBlock instanceof Rail or !$this->isBlock($downBlock)){//判断是否可以放置 return false; } $arrayXZ = [[1, 0], [0, 1], [-1, 0], [0, -1]]; $arrayY = [0, 1, -1]; /** @var Vector3 [] $connected */ $connected = []; foreach($arrayXZ as $xz){ $x = $xz[0]; $z = $xz[1]; foreach($arrayY as $y){ $v3 = (new Vector3($x, $y, $z))->add($this); $block = $this->level->getBlock($v3); if($block instanceof Rail){ if($block->connect($this)){ $connected[] = $v3; break; } } } if(count($connected) == 2){ break; } } switch(count($connected)){ case 1: $v3 = $connected[0]->subtract($this); $this->meta = (($v3->y != 1) ? ($v3->x == 0 ? 0 : 1) : ($v3->z == 0 ? ($v3->x / -2) + 2.5 : ($v3->z / 2) + 4.5)); break; case 2: $subtract = []; foreach($connected as $key => $value){ $subtract[$key] = $value->subtract($this); } if(abs($subtract[0]->x) == abs($subtract[1]->z) and abs($subtract[1]->x) == abs($subtract[0]->z)){ $v3 = $connected[0]->subtract($this)->add($connected[1]->subtract($this)); $this->meta = $v3->x == 1 ? ($v3->z == 1 ? 6 : 9) : ($v3->z == 1 ? 7 : 8); }elseif($subtract[0]->y == 1 or $subtract[1]->y == 1){ $v3 = $subtract[0]->y == 1 ? $subtract[0] : $subtract[1]; $this->meta = $v3->x == 0 ? ($v3->z == -1 ? 4 : 5) : ($v3->x == 1 ? 2 : 3); }else{ $this->meta = $subtract[0]->x == 0 ? 0 : 1; } break; default: break; } $this->level->setBlock($this, Block::get($this->id, $this->meta), true, true); return true; } /** * @param Rail $rail * @return array */ public static function check(Rail $rail){ $array = [ [[0, 1], [0, -1]], [[1, 0], [-1, 0]], [[1, 0], [-1, 0]], [[1, 0], [-1, 0]], [[0, 1], [0, -1]], [[0, 1], [0, -1]], [[1, 0], [0, 1]], [[0, 1], [-1, 0]], [[-1, 0], [0, -1]], [[0, -1], [1, 0]] ]; $arrayY = [0, 1, -1]; $blocks = $array[$rail->getDamage()]; $connected = []; foreach($arrayY as $y){ $v3 = new Vector3($rail->x + $blocks[0][0], $rail->y + $y, $rail->z + $blocks[0][1]); $id = $rail->getLevel()->getBlockIdAt($v3->x, $v3->y, $v3->z); $meta = $rail->getLevel()->getBlockDataAt($v3->x, $v3->y, $v3->z); if(in_array($id, [self::RAIL, self::ACTIVATOR_RAIL, self::DETECTOR_RAIL, self::POWERED_RAIL]) and in_array([$rail->x - $v3->x, $rail->z - $v3->z], $array[$meta])){ $connected[] = $v3; break; } } foreach($arrayY as $y){ $v3 = new Vector3($rail->x + $blocks[1][0], $rail->y + $y, $rail->z + $blocks[1][1]); $id = $rail->getLevel()->getBlockIdAt($v3->x, $v3->y, $v3->z); $meta = $rail->getLevel()->getBlockDataAt($v3->x, $v3->y, $v3->z); if(in_array($id, [self::RAIL, self::ACTIVATOR_RAIL, self::DETECTOR_RAIL, self::POWERED_RAIL]) and in_array([$rail->x - $v3->x, $rail->z - $v3->z], $array[$meta])){ $connected[] = $v3; break; } } return $connected; } public function getHardness() { return 0.7; } public function getResistance(){ return 3.5; } public function canPassThrough(){ return true; } } ================================================ FILE: src/pocketmine/block/RedMushroom.php ================================================ getSide(0)->isTransparent() === true){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } return false; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(0); if($down->isTransparent() === false){ $this->getLevel()->setBlock($block, $this, true, true); return true; } return false; } } ================================================ FILE: src/pocketmine/block/RedMushroomBlock.php ================================================ meta = $meta; } public function canBeActivated() : bool { return true; } public function getName(){ return "Red Mushroom Block"; } public function getHardness() { return 0.2; } public function getResistance(){ return 1; } public function getDrops(Item $item) : array { if($item->getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::RED_MUSHROOM_BLOCK, SELF::RED, 1], ]; }else{ return [ [Item::RED_MUSHROOM, 0, mt_rand(0, 2)], ]; } } } ================================================ FILE: src/pocketmine/block/RedSandstone.php ================================================ "Red Sandstone", 1 => "Chiseled Red Sandstone", 2 => "Smooth Red Sandstone", 3 => "", ]; return $names[$this->meta & 0x03]; } } ================================================ FILE: src/pocketmine/block/RedSandstoneSlab.php ================================================ getId() === self::RED_SANDSTONE_SLAB and ($target->getDamage() & 0x08) === 0x08){ $this->getLevel()->setBlock($target, Block::get(Item::DOUBLE_RED_SANDSTONE_SLAB, $this->meta), true); return true; }elseif($block->getId() === self::RED_SANDSTONE_SLAB){ $this->getLevel()->setBlock($block, Block::get(Item::DOUBLE_RED_SANDSTONE_SLAB, $this->meta), true); return true; }else{ $this->meta |= 0x08; } }elseif($face === 1){ if($target->getId() === self::RED_SANDSTONE_SLAB and ($target->getDamage() & 0x08) === 0){ $this->getLevel()->setBlock($target, Block::get(Item::DOUBLE_RED_SANDSTONE_SLAB, $this->meta), true); return true; }elseif($block->getId() === self::RED_SANDSTONE_SLAB){ $this->getLevel()->setBlock($block, Block::get(Item::DOUBLE_RED_SANDSTONE_SLAB, $this->meta), true); return true; } //TODO: check for collision }else{ if($block->getId() === self::RED_SANDSTONE_SLAB){ $this->getLevel()->setBlock($block, Block::get(Item::DOUBLE_RED_SANDSTONE_SLAB, $this->meta), true); }else{ if($fy > 0.5){ $this->meta |= 0x08; } } } if($block->getId() === self::RED_SANDSTONE_SLAB and ($target->getDamage() & 0x07) !== ($this->meta & 0x07)){ return false; } $this->getLevel()->setBlock($block, $this, true, true); return true; } } ================================================ FILE: src/pocketmine/block/RedSandstoneStairs.php ================================================ isPickaxe() >= 1){ return [ [Item::REDSTONE_BLOCK, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/RedstoneLamp.php ================================================ meta = $meta; } public function getName(){ return "Redstone Lamp"; } } ================================================ FILE: src/pocketmine/block/RedstoneOre.php ================================================ getLevel()->setBlock($this, Block::get(Item::GLOWING_REDSTONE_ORE, $this->meta)); return Level::BLOCK_UPDATE_WEAK; } return false; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getDrops(Item $item) : array { if($item->isPickaxe() >= Tool::TIER_IRON){ if($item->getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::REDSTONE_ORE, 0, 1], ]; }else{ $fortuneL = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortuneL = $fortuneL > 3 ? 3 : $fortuneL; return [ [Item::REDSTONE_DUST, 0, mt_rand(4, 5 + $fortuneL)], ]; } }else{ return []; } } } ================================================ FILE: src/pocketmine/block/RedstoneSource.php ================================================ maxStrength; } public function isActivated(Block $from = null){ return $this->activated; } public function canCalc(){ return $this->getLevel()->getServer()->redstoneEnabled; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $this->getLevel()->setBlock($this, $this, true); if($this->isActivated()){ $this->activate(); } } public function onBreak(Item $item){ $this->getLevel()->setBlock($this, new Air(), true); if($this->isActivated()){ $this->deactivate(); } } public function activateBlockWithoutWire(Block $block){ if(($block instanceof Door) or ($block instanceof Trapdoor) or ($block instanceof FenceGate)){ if(!$block->isOpened()) $block->onActivate(new Item(0)); } if($block->getId() == Block::TNT) $block->onActivate(new Item(Item::FLINT_AND_STEEL)); /** @var InactiveRedstoneLamp $block */ if($block->getId() == Block::INACTIVE_REDSTONE_LAMP) $block->turnOn(); /** @var Dropper|Dispenser $block */ if($block->getId() == Block::DROPPER or $block->getId() == Block::DISPENSER) $block->activate(); /** @var PoweredRepeater $block */ if($block->getId() == Block::UNPOWERED_REPEATER_BLOCK){ if($this->equals($block->getSide($block->getDirection()))) $block->activate(); } } public function activateBlock(Block $block){ $this->activateBlockWithoutWire($block); if($block->getId() == Block::REDSTONE_WIRE){ /** @var RedstoneWire $wire */ $wire = $block; $wire->calcSignal($this->maxStrength, RedstoneWire::ON); } } public function deactivateBlock(Block $block){ $this->deactivateBlockWithoutWire($block); if($block->getId() == Block::REDSTONE_WIRE){ /** @var RedstoneWire $wire */ $wire = $block; $wire->calcSignal(0, RedstoneWire::OFF); } } public function deactivateBlockWithoutWire(Block $block){ /** @var Door $block */ if(!$this->checkPower($block)){ if(($block instanceof Door) or ($block instanceof Trapdoor) or ($block instanceof FenceGate)){ if($block->isOpened()) $block->onActivate(new Item(0)); } /** @var ActiveRedstoneLamp $block */ if($block->getId() == Block::ACTIVE_REDSTONE_LAMP) $block->turnOff(); } /** @var PoweredRepeater $block */ if($block->getId() == Block::POWERED_REPEATER_BLOCK){ if($this->equals($block->getSide($block->getDirection()))) $block->deactivate(); } } public function activate(array $ignore = []){ if($this->canCalc()){ $this->activated = true; /** @var Door $block */ $sides = [Vector3::SIDE_EAST, Vector3::SIDE_WEST, Vector3::SIDE_SOUTH, Vector3::SIDE_NORTH, Vector3::SIDE_DOWN]; foreach($sides as $side){ if(!in_array($side, $ignore)){ $block = $this->getSide($side); $this->activateBlock($block); } } } } public function deactivate(array $ignore = []){ if($this->canCalc()){ $this->activated = false; /** @var Door $block */ $sides = [Vector3::SIDE_EAST, Vector3::SIDE_WEST, Vector3::SIDE_SOUTH, Vector3::SIDE_NORTH]; foreach($sides as $side){ if(!in_array($side, $ignore)){ $block = $this->getSide($side); $this->deactivateBlock($block); } } if(!in_array(Vector3::SIDE_DOWN, $ignore)){ $block = $this->getSide(Vector3::SIDE_DOWN); if(!$this->checkPower($block)){ /** @var $block ActiveRedstoneLamp */ if($block->getId() == Block::ACTIVE_REDSTONE_LAMP) $block->turnOff(); } $block = $this->getSide(Vector3::SIDE_DOWN, 2); $this->deactivateBlock($block); } } } public function checkPower(Block $block, array $ignore = [], $ignoreWire = false){ if($block instanceof PoweredRepeater){ if($block->getSide($block->getDirection())->isActivated($block)){ return true; } return false; } $sides = [ Vector3::SIDE_EAST, Vector3::SIDE_WEST, Vector3::SIDE_SOUTH, Vector3::SIDE_NORTH ]; foreach($sides as $side){ if(!in_array($side, $ignore)){ $pos = $block->getSide($side); if($pos instanceof RedstoneSource){ if($pos->isActivated($this)){ if(($ignoreWire and $pos->getId() != self::REDSTONE_WIRE) or (!$ignoreWire and $pos->getId() != self::REDSTONE_WIRE)) return true; if(!$ignoreWire and $pos->getId() == self::REDSTONE_WIRE){ /** @var RedstoneWire $pos */ $cb = $pos->getUnconnectedSide(); if(!$cb[0]) return false; if($this->equals($pos->getSide($cb[0]))) return true; } } } } } if($block->getId() == Block::ACTIVE_REDSTONE_LAMP and !in_array(Vector3::SIDE_UP, $ignore)){ $pos = $block->getSide(Vector3::SIDE_UP); if($pos instanceof RedstoneSource and $pos->getId() != self::REDSTONE_TORCH){ if($pos->isActivated($this)) return true; } } return false; } public function checkTorchOn(Block $pos, array $ignore = []){ $sides = [Vector3::SIDE_EAST, Vector3::SIDE_WEST, Vector3::SIDE_SOUTH, Vector3::SIDE_NORTH, Vector3::SIDE_UP]; foreach($sides as $side){ if(!in_array($side, $ignore)){ /** @var RedstoneTorch $block */ $block = $pos->getSide($side); if($block->getId() == self::REDSTONE_TORCH){ $faces = [ 1 => 4, 2 => 5, 3 => 2, 4 => 3, 5 => 0, 6 => 0, 0 => 0, ]; if($block->getSide($faces[$block->meta])->equals($pos)){ $ignoreBlock = $this->getSide($this->getOppositeSide($faces[$block->meta])); $block->turnOff(Level::blockHash($ignoreBlock->x, $ignoreBlock->y, $ignoreBlock->z)); } } } } } public function checkTorchOff(Block $pos, array $ignore = []){ $sides = [Vector3::SIDE_EAST, Vector3::SIDE_WEST, Vector3::SIDE_SOUTH, Vector3::SIDE_NORTH, Vector3::SIDE_UP]; foreach($sides as $side){ if(!in_array($side, $ignore)){ /** @var RedstoneTorch $block */ $block = $pos->getSide($side); if($block->getId() == self::UNLIT_REDSTONE_TORCH){ $faces = [ 1 => 4, 2 => 5, 3 => 2, 4 => 3, 5 => 0, 6 => 0, 0 => 0, ]; if($block->getSide($faces[$block->meta])->equals($pos)){ $ignoreBlock = $this->getSide($this->getOppositeSide($faces[$block->meta])); $block->turnOn(Level::blockHash($ignoreBlock->x, $ignoreBlock->y, $ignoreBlock->z)); } } } } } public function getStrength(){ if($this->isActivated()) return $this->maxStrength; return 0; } } ================================================ FILE: src/pocketmine/block/RedstoneTorch.php ================================================ meta = $meta; } public function getLightLevel(){ return 7; } public function getLastUpdateTime(){ return $this->getLevel()->getBlockTempData($this); } public function setLastUpdateTimeNow(){ $this->getLevel()->setBlockTempData($this, $this->getLevel()->getServer()->getTick()); } public function canCalcTurn(){ if(!parent::canCalc()) return false; if($this->getLevel()->getServer()->getTick() != $this->getLastUpdateTime()) return true; return ($this->canScheduleUpdate() ? Level::BLOCK_UPDATE_SCHEDULED : false); } public function canScheduleUpdate(){ return $this->getLevel()->getServer()->allowFrequencyPulse; } public function getFrequency(){ return $this->getLevel()->getServer()->pulseFrequency; } public function getName(){ return "Redstone Torch"; } public function turnOn($ignore = ""){ $result = $this->canCalcTurn(); $this->setLastUpdateTimeNow(); if($result === true){ $faces = [ 1 => 4, 2 => 5, 3 => 2, 4 => 3, 5 => 0, 6 => 0, 0 => 0, ]; $this->id = self::REDSTONE_TORCH; $this->getLevel()->setBlock($this, $this, true); $this->activateTorch([$faces[$this->meta]], [$ignore]); return true; }elseif($result === Level::BLOCK_UPDATE_SCHEDULED){ $this->ignore = $ignore; $this->getLevel()->scheduleUpdate($this, 20 * $this->getFrequency()); return true; } return false; } public function turnOff($ignore = ""){ $result = $this->canCalcTurn(); $this->setLastUpdateTimeNow(); if($result === true){ $faces = [ 1 => 4, 2 => 5, 3 => 2, 4 => 3, 5 => 0, 6 => 0, 0 => 0, ]; $this->id = self::UNLIT_REDSTONE_TORCH; $this->getLevel()->setBlock($this, $this, true); $this->deactivateTorch([$faces[$this->meta]], [$ignore]); return true; }elseif($result === Level::BLOCK_UPDATE_SCHEDULED){ $this->ignore = $ignore; $this->getLevel()->scheduleUpdate($this, 20 * $this->getFrequency()); return true; } return false; } public function activateTorch(array $ignore = [], $notCheck = []){ if($this->canCalc()){ $this->activated = true; /** @var Door $block */ $sides = [Vector3::SIDE_EAST, Vector3::SIDE_WEST, Vector3::SIDE_SOUTH, Vector3::SIDE_NORTH, Vector3::SIDE_UP, Vector3::SIDE_DOWN]; foreach($sides as $side){ if(!in_array($side, $ignore)){ $block = $this->getSide($side); if(!in_array($hash = Level::blockHash($block->x, $block->y, $block->z), $notCheck)){ $this->activateBlock($block); } } } //$this->lastUpdateTime = $this->getLevel()->getServer()->getTick(); } } public function activate(array $ignore = []){ $this->activateTorch($ignore); } public function deactivate(array $ignore = []){ $this->deactivateTorch($ignore); } public function deactivateTorch(array $ignore = [], array $notCheck = []){ if($this->canCalc()){ $this->activated = false; /** @var Door $block */ $sides = [Vector3::SIDE_EAST, Vector3::SIDE_WEST, Vector3::SIDE_SOUTH, Vector3::SIDE_NORTH]; foreach($sides as $side){ if(!in_array($side, $ignore)){ $block = $this->getSide($side); if(!in_array($hash = Level::blockHash($block->x, $block->y, $block->z), $notCheck)){ $this->deactivateBlock($block); } } } if(!in_array(Vector3::SIDE_DOWN, $ignore)){ $block = $this->getSide(Vector3::SIDE_DOWN); if(!in_array($hash = Level::blockHash($block->x, $block->y, $block->z), $notCheck)){ if(!$this->checkPower($block)){ /** @var $block ActiveRedstoneLamp */ if($block->getId() == Block::ACTIVE_REDSTONE_LAMP) $block->turnOff(); } $block = $this->getSide(Vector3::SIDE_DOWN, 2); $this->deactivateBlock($block); } } //$this->lastUpdateTime = $this->getLevel()->getServer()->getTick(); } } public function onUpdate($type){ $faces = [ 1 => 4, 2 => 5, 3 => 2, 4 => 3, 5 => 0, 6 => 0, 0 => 0, ]; if($type === Level::BLOCK_UPDATE_NORMAL){ $below = $this->getSide(0); $side = $this->getDamage(); if($this->getSide($faces[$side])->isTransparent() === true and !($side === 0 and ($below->getId() === self::FENCE or $below->getId() === self::COBBLE_WALL )) ){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } $this->activate([$faces[$side]]); } if($type == Level::BLOCK_UPDATE_SCHEDULED){ if($this->id == self::UNLIT_REDSTONE_TORCH) $this->turnOn($this->ignore); else $this->turnOff($this->ignore); return Level::BLOCK_UPDATE_SCHEDULED; } return false; } public function onBreak(Item $item){ $this->getLevel()->setBlock($this, new Air(), true, false); $faces = [ 1 => 4, 2 => 5, 3 => 2, 4 => 3, 5 => 0, 6 => 0, 0 => 0, ]; $this->deactivate([$faces[$this->meta]]); $this->getLevel()->setBlockTempData($this); } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $below = $this->getSide(0); if($target->isTransparent() === false and $face !== 0){ $faces = [ 1 => 5, 2 => 4, 3 => 3, 4 => 2, 5 => 1, ]; $this->meta = $faces[$face]; $this->getLevel()->setBlock($block, $this, true, true); return true; }elseif( $below->isTransparent() === false or $below->getId() === self::FENCE or $below->getId() === self::COBBLE_WALL or $below->getId() == Block::INACTIVE_REDSTONE_LAMP or $below->getId() == Block::ACTIVE_REDSTONE_LAMP ){ $this->meta = 0; $this->getLevel()->setBlock($block, $this, true, true); return true; } return false; } public function getDrops(Item $item) : array{ return [ [Item::LIT_REDSTONE_TORCH, 0, 1], ]; } public function isActivated(Block $from = null){ return true; } } ================================================ FILE: src/pocketmine/block/RedstoneWire.php ================================================ meta = $meta; } public function getName(){ return "Redstone Wire"; } public function getStrength(){ return $this->meta; } public function isActivated(Block $from = null){ return ($this->meta > 0); } public function getHighestStrengthAround(){ $strength = 0; $hasChecked = [ Vector3::SIDE_WEST => false, Vector3::SIDE_EAST => false, Vector3::SIDE_NORTH => false, Vector3::SIDE_SOUTH => false ]; //check blocks around foreach($hasChecked as $side => $bool){ /** @var RedstoneSource $block */ $block = $this->getSide($side); if($block instanceof RedstoneSource){ if(($block->getStrength() > $strength) and $block->isActivated($this)) $strength = $block->getStrength(); $hasChecked[$side] = true; } } //check blocks above $baseBlock = $this->add(0, 1, 0); foreach($hasChecked as $side => $bool){ if(!$bool){ $block = $this->getLevel()->getBlock($baseBlock->getSide($side)); if($block->getId() == $this->id){ if($block->getStrength() > $strength) $strength = $block->getStrength(); $hasChecked[$side] = true; } } } //check blocks below $baseBlock = $this->add(0, -1, 0); foreach($hasChecked as $side => $bool){ if(!$bool){ $block = $this->getLevel()->getBlock($baseBlock->getSide($side)); if($block->getId() == $this->id){ if($block->getStrength() > $strength) $strength = $block->getStrength(); $hasChecked[$side] = true; } } } unset($block); unset($hasChecked); return $strength; } public function getConnectedWires(){ $hasChecked = [ Vector3::SIDE_WEST => false, Vector3::SIDE_EAST => false, Vector3::SIDE_NORTH => false, Vector3::SIDE_SOUTH => false ]; //check blocks around foreach($hasChecked as $side => $bool){ $block = $this->getSide($side); if($block instanceof RedstoneSource and !$block instanceof PoweredRepeater){ $hasChecked[$side] = true; } if($block instanceof PoweredRepeater){ if($this->equals($block->getSide($block->getOppositeDirection()))){ $hasChecked[$side] = true; } } } //check blocks above $baseBlock = $this->add(0, 1, 0); foreach($hasChecked as $side => $bool){ if(!$bool){ $block = $this->getLevel()->getBlock($baseBlock->getSide($side)); if($block->getId() == $this->id){ $hasChecked[$side] = true; } } } //check blocks below $baseBlock = $this->add(0, -1, 0); foreach($hasChecked as $side => $bool){ if(!$bool){ $block = $this->getLevel()->getBlock($baseBlock->getSide($side)); if($block->getId() == $this->id){ $hasChecked[$side] = true; } } } unset($block); return $hasChecked; } public function getUnconnectedSide(){ $connected = []; $notConnected = []; foreach($this->getConnectedWires() as $key => $bool){ if($bool){ $connected[] = $key; }else $notConnected[] = $key; } if(count($connected) == 1){ return [$this->getOppositeSide($connected[0]), $connected]; }elseif(count($connected) == 3){ return [$notConnected[0], $connected]; }else return [false, $connected]; } public function activate(array $ignore = []){ if($this->canCalc()){ $block = $this->getSide(Vector3::SIDE_DOWN); /** @var ActiveRedstoneLamp $block */ if($block->getId() == Block::INACTIVE_REDSTONE_LAMP or $block->getId() == Block::ACTIVE_REDSTONE_LAMP) $block->turnOn(); $side = $this->getUnconnectedSide(); $sides = [Vector3::SIDE_WEST, Vector3::SIDE_EAST, Vector3::SIDE_SOUTH, Vector3::SIDE_NORTH]; foreach($sides as $s){ if(!in_array($s, $side[1])) { $block = $this->getSide(Vector3::SIDE_DOWN)->getSide($s); $this->activateBlock($block); } } if($side[0] == false) return; $block = $this->getSide($side[0]); $this->activateBlock($block); if(!$block->isTransparent()){ $sides = [Vector3::SIDE_WEST, Vector3::SIDE_EAST, Vector3::SIDE_SOUTH, Vector3::SIDE_NORTH, Vector3::SIDE_UP, Vector3::SIDE_DOWN]; foreach($sides as $s){ if($s != $this->getOppositeSide($side[0])){ $this->activateBlockWithoutWire($block->getSide($s)); } } } $this->checkTorchOn($block, [$this->getOppositeSide($side)]); unset($connected, $notConnected); } } public function deactivate(array $ignore = []){ if($this->canCalc()){ $block = $this->getSide(Vector3::SIDE_DOWN); if($block->getId() == Block::ACTIVE_REDSTONE_LAMP) { /** @var ActiveRedstoneLamp $block */ if(!$this->checkPower($block, [Vector3::SIDE_UP], true)) $block->turnOff(); } $side = $this->getUnconnectedSide(); $sides = [Vector3::SIDE_WEST, Vector3::SIDE_EAST, Vector3::SIDE_SOUTH, Vector3::SIDE_NORTH]; foreach($sides as $s){ if(!in_array($s, $side[1])) { $this->deactivateBlock($this->getSide(Vector3::SIDE_DOWN)->getSide($s)); } } if($side[0] == false) return; $block = $this->getSide($side[0]); $this->deactivateBlockWithoutWire($block); if(!$block->isTransparent()){ $sides = [Vector3::SIDE_WEST, Vector3::SIDE_EAST, Vector3::SIDE_SOUTH, Vector3::SIDE_NORTH, Vector3::SIDE_UP, Vector3::SIDE_DOWN]; foreach($sides as $s){ if($s != $this->getOppositeSide($side[0])){ $this->deactivateBlockWithoutWire($block->getSide($s)); } } } $this->checkTorchOff($block, [$this->getOppositeSide($side)]); unset($connected, $notConnected); } } public function getPowerSources(RedstoneWire $wire, array $powers = [], array $hasUpdated = [], $isStart = false){ if(!$isStart){ $wire->meta = 0; $wire->getLevel()->setBlock($wire, $wire, true, false); $wire->deactivate(); } $hasChecked = [ Vector3::SIDE_WEST => false, Vector3::SIDE_EAST => false, Vector3::SIDE_NORTH => false, Vector3::SIDE_SOUTH => false ]; $hash = Level::blockHash($wire->x, $wire->y, $wire->z); if(!isset($hasUpdated[$hash])) $hasUpdated[$hash] = true; else return [$powers, $hasUpdated]; //check blocks around foreach($hasChecked as $side => $bool){ /** @var RedstoneWire $block */ $block = $wire->getSide($side); if($block instanceof RedstoneSource){ if($block->isActivated($wire)){ if($block->getId() != $this->id){ $powers[] = $block; }else{ $result = $this->getPowerSources($block, $powers, $hasUpdated); $powers = $result[0]; $hasUpdated = $result[1]; } $hasChecked[$side] = true; } } } //check blocks above $baseBlock = $wire->add(0, 1, 0); foreach($hasChecked as $side => $bool){ if(!$bool){ $block = $this->getLevel()->getBlock($baseBlock->getSide($side)); if($block instanceof RedstoneSource){ if($block->isActivated($wire)){ if($block->getId() == $this->id){ $result = $this->getPowerSources($block, $powers, $hasUpdated); $powers = $result[0]; $hasUpdated = $result[1]; $hasChecked[$side] = true; } } } } } //check blocks below $baseBlock = $wire->add(0, -1, 0); foreach($hasChecked as $side => $bool){ if(!$bool){ $block = $this->getLevel()->getBlock($baseBlock->getSide($side)); if($block instanceof RedstoneSource){ if($block->isActivated($wire)){ if($block->getId() == $this->id){ $result = $this->getPowerSources($block, $powers, $hasUpdated); $powers = $result[0]; $hasUpdated = $result[1]; $hasChecked[$side] = true; } } } } } return [$powers, $hasUpdated]; } public function calcSignal($strength = 15, $type = self::ON, array $hasUpdated = []){ //This algorithm is provided by Stary and written by PeratX $hash = Level::blockHash($this->x, $this->y, $this->z); if(!in_array($hash, $hasUpdated)){ $hasUpdated[] = $hash; if($type == self::DESTROY or $type == self::OFF){ $this->meta = $strength; $this->getLevel()->setBlock($this, $this, true, false); if($type == self::DESTROY) $this->getLevel()->setBlock($this, new Air(), true, false); if($strength <= 0) $this->deactivate(); $powers = $this->getPowerSources($this, [], [], true); /** @var RedstoneSource $power */ foreach($powers[0] as $power){ $power->activate(); } }else{ if($strength <= 0) return $hasUpdated; if($type == self::PLACE) $strength = $this->getHighestStrengthAround() - 1; if($type == self::ON) $type = self::PLACE; if($this->getStrength() < $strength){ $this->meta = $strength; $this->getLevel()->setBlock($this, $this, true, false); $this->activate(); $hasChecked = [ Vector3::SIDE_WEST => false, Vector3::SIDE_EAST => false, Vector3::SIDE_NORTH => false, Vector3::SIDE_SOUTH => false ]; foreach($hasChecked as $side => $bool){ $needUpdate = $this->getSide($side); if(!in_array(Level::blockHash($needUpdate->x, $needUpdate->y, $needUpdate->z), $hasUpdated)){ $result = $this->updateNormalWire($needUpdate, $strength - 1, $type, $hasUpdated); if(count($result) != count($hasUpdated)){ $hasUpdated = $result; $hasChecked[$side] = true; } } } $baseBlock = $this->add(0, 1, 0); foreach($hasChecked as $side => $bool){ if(!$bool){ $needUpdate = $this->getLevel()->getBlock($baseBlock->getSide($side)); if(!in_array(Level::blockHash($needUpdate->x, $needUpdate->y, $needUpdate->z), $hasUpdated)){ $result = $this->updateNormalWire($needUpdate, $strength - 1, $type, $hasUpdated); if(count($result) != count($hasUpdated)){ $hasUpdated = $result; $hasChecked[$side] = true; } } } } $baseBlock = $this->add(0, -1, 0); foreach($hasChecked as $side => $bool){ if(!$bool){ $needUpdate = $this->getLevel()->getBlock($baseBlock->getSide($side)); if(!in_array(Level::blockHash($needUpdate->x, $needUpdate->y, $needUpdate->z), $hasUpdated)){ $result = $this->updateNormalWire($needUpdate, $strength - 1, $type, $hasUpdated); if(count($result) != count($hasUpdated)){ $hasUpdated = $result; $hasChecked[$side] = true; } } } } } } } return $hasUpdated; } public function updateNormalWire(Block $block, $strength, $type, array $hasUpdated){ /** @var RedstoneWire $block */ if($block->getId() == Block::REDSTONE_WIRE){ if($block->getStrength() < $strength){ return $block->calcSignal($strength, $type, $hasUpdated); } } return $hasUpdated; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ $down = $this->getSide(Vector3::SIDE_DOWN); if($down instanceof Transparent and $down->getId() != Block::INACTIVE_REDSTONE_LAMP and $down->getId() != Block::ACTIVE_REDSTONE_LAMP){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } return true; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(Vector3::SIDE_DOWN); if($down instanceof Transparent and $down->getId() != Block::INACTIVE_REDSTONE_LAMP and $down->getId() != Block::ACTIVE_REDSTONE_LAMP) return; else{ $this->getLevel()->setBlock($block, $this, true, false); $this->calcSignal(15, self::PLACE); } } public function onBreak(Item $item){ if($this->canCalc()) $this->calcSignal(0, self::DESTROY); else $this->getLevel()->setBlock($this, new Air()); } public function getDrops(Item $item) : array { return [ [Item::REDSTONE, 0, 1] ]; } } ================================================ FILE: src/pocketmine/block/RepeaterBlock.php ================================================ meta = $meta; } public function getName(){ return "Repeater Block"; } } ================================================ FILE: src/pocketmine/block/Sand.php ================================================ meta = $meta; } public function getHardness() { return 0.5; } public function getToolType(){ return Tool::TYPE_SHOVEL; } public function getName(){ if($this->meta === 0x01){ return "Red Sand"; } return "Sand"; } } ================================================ FILE: src/pocketmine/block/Sandstone.php ================================================ meta = $meta; } public function getHardness() { return 0.8; } public function getName(){ static $names = [ 0 => "Sandstone", 1 => "Chiseled Sandstone", 2 => "Smooth Sandstone", 3 => "", ]; return $names[$this->meta & 0x03]; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getDrops(Item $item) : array { if($item->isPickaxe() >= 1){ return [ [$this->id, $this->meta & 0x03, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/SandstoneStairs.php ================================================ meta = $meta; } public function getHardness() { return 0.8; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getName(){ return "Sandstone Stairs"; } } ================================================ FILE: src/pocketmine/block/Sapling.php ================================================ meta = $meta; } public function canBeActivated() : bool { return true; } public function getName(){ static $names = [ 0 => "Oak Sapling", 1 => "Spruce Sapling", 2 => "Birch Sapling", 3 => "Jungle Sapling", 4 => "Acacia Sapling", 5 => "Dark Oak Sapling", 6 => "", 7 => "", ]; return $names[$this->meta & 0x07]; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(0); if($down->getId() === self::GRASS or $down->getId() === self::DIRT or $down->getId() === self::FARMLAND or $down->getId() === self::PODZOL){ $this->getLevel()->setBlock($block, $this, true, true); return true; } return false; } public function onActivate(Item $item, Player $player = null){ if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal //TODO: change log type Tree::growTree($this->getLevel(), $this->x, $this->y, $this->z, new Random(mt_rand()), $this->meta & 0x07); if(($player->gamemode & 0x01) === 0){ $item->count--; } return true; } return false; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent() === true){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } }elseif($type === Level::BLOCK_UPDATE_RANDOM){ //Growth if(mt_rand(1, 7) === 1){ if(($this->meta & 0x08) === 0x08){ Tree::growTree($this->getLevel(), $this->x, $this->y, $this->z, new Random(mt_rand()), $this->meta & 0x07); }else{ $this->meta |= 0x08; $this->getLevel()->setBlock($this, $this, true); return Level::BLOCK_UPDATE_RANDOM; } }else{ return Level::BLOCK_UPDATE_RANDOM; } } return false; } public function getDrops(Item $item) : array { return [ [$this->id, $this->meta & 0x07, 1], ]; } } ================================================ FILE: src/pocketmine/block/SeaLantern.php ================================================ meta = $meta; } public function getName(){ return "Sea Lantern"; } public function getHardness(){ return 0.3; } public function getLightLevel(){ return 15; } public function getDrops(Item $item) : array{ return [ [Item::PRISMARINE_CRYSTALS, 0, 3], ]; } } ================================================ FILE: src/pocketmine/block/SignPost.php ================================================ meta = $meta; } public function getHardness(){ return 1; } public function isSolid(){ return false; } public function getName(){ return "Sign Post"; } public function getBoundingBox(){ return null; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if($face !== 0){ $faces = [ 2 => 2, 3 => 3, 4 => 4, 5 => 5, ]; if(!isset($faces[$face])){ $this->meta = floor((($player->yaw + 180) * 16 / 360) + 0.5) & 0x0F; $this->getLevel()->setBlock($block, Block::get(Item::SIGN_POST, $this->meta), true); return true; }else{ $this->meta = $faces[$face]; $this->getLevel()->setBlock($block, Block::get(Item::WALL_SIGN, $this->meta), true); return true; } } return false; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(Vector3::SIDE_DOWN)->getId() === Block::AIR){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } return false; } public function getDrops(Item $item) : array{ return [ [Item::SIGN, 0, 1], ]; } public function getToolType(){ return Tool::TYPE_AXE; } } ================================================ FILE: src/pocketmine/block/SkullBlock.php ================================================ meta = $meta; } public function getHardness() { return 1; } public function isHelmet(){ return true; } public function isSolid(){ return false; } public function getBoundingBox(){ return new AxisAlignedBB( $this->x - 0.75, $this->y - 0.5, $this->z - 0.75, $this->x + 0.75, $this->y + 0.5, $this->z + 0.75 ); } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(0); if($face !== 0 && $fy > 0.5 && $target->getId() !== self::SKULL_BLOCK && !$down instanceof SkullBlock){ $this->getLevel()->setBlock($block, Block::get(Block::SKULL_BLOCK, 0), true, true); if($face === 1){ $rot = new ByteTag("Rot", floor(($player->yaw * 16 / 360) + 0.5) & 0x0F); }else{ $rot = new ByteTag("Rot", 0); } $nbt = new CompoundTag("", [ new StringTag("id", Tile::SKULL), new IntTag("x", $block->x), new IntTag("y", $block->y), new IntTag("z", $block->z), new ByteTag("SkullType", $item->getDamage()), $rot ]); if($item->hasCustomBlockData()){ foreach($item->getCustomBlockData() as $key => $v){ $nbt->{$key} = $v; } } $chunk = $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4); $pot = Tile::createTile(Tile::SKULL, $chunk, $nbt); $this->getLevel()->setBlock($block, Block::get(Block::SKULL_BLOCK, $face), true, true); return true; } return false; } public function getResistance(){ return 5; } public function getName(){ static $names = [ 0 => "Skeleton Skull", 1 => "Wither Skeleton Skull", 2 => "Zombie Head", 3 => "Head", 4 => "Creeper Head" ]; return $names[$this->meta & 0x04]; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function onBreak(Item $item){ $this->getLevel()->setBlock($this, new Air(), true, true); return true; } public function getDrops(Item $item) : array { /** @var Skull $tile */ if($this->getLevel()!=null && (($tile = $this->getLevel()->getTile($this)) instanceof Skull)){ return [[Item::SKULL, $tile->getSkullType(), 1]]; }else return [[Item::SKULL, 0, 1]]; } } ================================================ FILE: src/pocketmine/block/Slab.php ================================================ meta = $meta; } public function getHardness() { return 2; } public function getName(){ static $names = [ self::STONE => "Stone", self::SANDSTONE => "Sandstone", self::WOODEN => "Wooden", self::COBBLESTONE => "Cobblestone", self::BRICK => "Brick", self::STONE_BRICK => "Stone Brick", self::QUARTZ => "Quartz", self::NETHER_BRICK => "Nether Brick", ]; return (($this->meta & 0x08) > 0 ? "Upper " : "") . $names[$this->meta & 0x07] . " Slab"; } public function getBurnChance() : int{ $type = $this->meta & 0x07; if($type == self::WOODEN){ return 5; } return 0; } public function getBurnAbility() : int{ $type = $this->meta & 0x07; if($type == self::WOODEN){ return 5; } return 0; } protected function recalculateBoundingBox() { if(($this->meta & 0x08) > 0){ return new AxisAlignedBB( $this->x, $this->y + 0.5, $this->z, $this->x + 1, $this->y + 1, $this->z + 1 ); }else{ return new AxisAlignedBB( $this->x, $this->y, $this->z, $this->x + 1, $this->y + 0.5, $this->z + 1 ); } } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $this->meta &= 0x07; if($face === 0){ if($target->getId() === self::SLAB and ($target->getDamage() & 0x08) === 0x08 and ($target->getDamage() & 0x07) === ($this->meta & 0x07)){ $this->getLevel()->setBlock($target, Block::get(Item::DOUBLE_SLAB, $this->meta), true); return true; }elseif($block->getId() === self::SLAB and ($block->getDamage() & 0x07) === ($this->meta & 0x07)){ $this->getLevel()->setBlock($block, Block::get(Item::DOUBLE_SLAB, $this->meta), true); return true; }else{ $this->meta |= 0x08; } }elseif($face === 1){ if($target->getId() === self::SLAB and ($target->getDamage() & 0x08) === 0 and ($target->getDamage() & 0x07) === ($this->meta & 0x07)){ $this->getLevel()->setBlock($target, Block::get(Item::DOUBLE_SLAB, $this->meta), true); return true; }elseif($block->getId() === self::SLAB and ($block->getDamage() & 0x07) === ($this->meta & 0x07)){ $this->getLevel()->setBlock($block, Block::get(Item::DOUBLE_SLAB, $this->meta), true); return true; } //TODO: check for collision }else{ if($block->getId() === self::SLAB){ if(($block->getDamage() & 0x07) === ($this->meta & 0x07)){ $this->getLevel()->setBlock($block, Block::get(Item::DOUBLE_SLAB, $this->meta), true); return true; } return false; }else{ if($fy > 0.5){ $this->meta |= 0x08; } } } if($block->getId() === self::SLAB and ($target->getDamage() & 0x07) !== ($this->meta & 0x07)){ return false; } $this->getLevel()->setBlock($block, $this, true, true); return true; } public function getDrops(Item $item) : array { if($item->isPickaxe() >= 1){ return [ [$this->id, $this->meta & 0x07, 1], ]; }else{ return []; } } public function getToolType(){ return Tool::TYPE_PICKAXE; } } ================================================ FILE: src/pocketmine/block/Slab2.php ================================================ meta = $meta; } public function getName(){ static $names = [ self::RED_SANDSTONE => "Red Sandstone", self::PURPUR_BLOCK => "Purpur Block", ]; return (($this->meta & 0x08) > 0 ? "Upper " : "") . $names[$this->meta & 0x07] . " Slab"; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if($face === 0){ if($target->getId() === self::SLAB2 and ($target->getDamage() & 0x08) === 0x08){ $this->getLevel()->setBlock($target, Block::get(Item::DOUBLE_SLAB2, $this->meta), true); return true; }elseif($block->getId() === self::SLAB2){ $this->getLevel()->setBlock($block, Block::get(Item::DOUBLE_SLAB2, $this->meta), true); return true; }else{ $this->meta |= 0x08; } }elseif($face === 1){ if($target->getId() === self::SLAB2 and ($target->getDamage() & 0x08) === 0){ $this->getLevel()->setBlock($target, Block::get(Item::DOUBLE_SLAB2, $this->meta), true); return true; }elseif($block->getId() === self::SLAB2){ $this->getLevel()->setBlock($block, Block::get(Item::DOUBLE_SLAB2, $this->meta), true); return true; } //TODO: check for collision }else{ if($block->getId() === self::SLAB2){ $this->getLevel()->setBlock($block, Block::get(Item::DOUBLE_SLAB2, $this->meta), true); }else{ if($fy > 0.5){ $this->meta |= 0x08; } } } if($block->getId() === self::SLAB2 and ($target->getDamage() & 0x07) !== ($this->meta & 0x07)){ return false; } $this->getLevel()->setBlock($block, $this, true, true); return true; } } ================================================ FILE: src/pocketmine/block/SlimeBlock.php ================================================ meta = $meta; } public function hasEntityCollision(){ return true; } public function getHardness() { return 0; } public function getName(){ return "Slime Block"; } } ================================================ FILE: src/pocketmine/block/Snow.php ================================================ meta = $meta; } public function getHardness() { return 0.2; } public function getName(){ return "Snow Block"; } public function getDrops(Item $item) : array { if($item->isShovel() !== false){ if($item->getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0){ return [ [Item::SNOW_BLOCK, 0, 1], ]; }else{ return [ [Item::SNOWBALL, 0, 4], ]; } }else{ return []; } } } ================================================ FILE: src/pocketmine/block/SnowLayer.php ================================================ meta = $meta; } public function getName(){ return "Snow Layer"; } public function canBeReplaced(){ return true; } public function getHardness() { return 0.1; } public function getToolType(){ return Tool::TYPE_SHOVEL; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(0); if($down->isSolid()){ $this->getLevel()->setBlock($block, $this, true); return true; } return false; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->getId() === self::AIR){ //Replace with common break method $this->getLevel()->setBlock($this, new Air(), true); return Level::BLOCK_UPDATE_NORMAL; } } return false; } public function getDrops(Item $item) : array { if($item->isShovel() !== false){ return [ [Item::SNOWBALL, 0, 1], ]; } return []; } } ================================================ FILE: src/pocketmine/block/Solid.php ================================================ x, $this->y, $this->z, $this->x + 1, $this->y + 1 - 0.125, $this->z + 1 ); } } ================================================ FILE: src/pocketmine/block/Sponge.php ================================================ meta = $meta; } public function getHardness() { return 0.6; } public function absorbWater($block = null){ if ($block == null) $block = $this; $range = $this->absorbRange / 2; for ($xx = -$range; $xx <= $range; $xx++){ for ($yy = -$range; $yy <= $range; $yy++){ for ($zz = -$range; $zz <= $range; $zz++){ $block = $this->getLevel()->getBlock(new Vector3($this->x + $xx, $this->y + $yy, $this->z + $zz)); if ($block->getId() === Block::WATER) $this->getLevel()->setBlock($block, Block::get(Block::AIR), true, true); } } } } public function onUpdate($type){ if ($this->meta == 0) { if($type === Level::BLOCK_UPDATE_NORMAL){ $blockAbove = $this->getSide(Vector3::SIDE_UP)->getId(); $blockBeneath = $this->getSide(Vector3::SIDE_DOWN)->getId(); $blockNorth = $this->getSide(Vector3::SIDE_NORTH)->getId(); $blockSouth = $this->getSide(Vector3::SIDE_SOUTH)->getId(); $blockEast = $this->getSide(Vector3::SIDE_EAST)->getId(); $blockWest = $this->getSide(Vector3::SIDE_WEST)->getId(); if($blockAbove === Block::WATER || $blockBeneath === Block::WATER || $blockNorth === Block::WATER || $blockSouth === Block::WATER || $blockEast === Block::WATER || $blockWest === Block::WATER){ $this->absorbWater($this); $this->getLevel()->setBlock($this, Block::get(Block::SPONGE, 1), true, true); return Level::BLOCK_UPDATE_NORMAL; } } } return false; } public function getName(){ static $names = [ 0 => "Sponge", 1 => "Wet Sponge", ]; return $names[$this->meta & 0x0f]; } public function getDrops(Item $item) : array { return [ [$this->id, $this->meta & 0x0f, 1], ]; } } ================================================ FILE: src/pocketmine/block/SpruceDoor.php ================================================ meta = $meta; } public function getName(){ return "Spruce Door Block"; } public function canBeActivated() : bool { return true; } public function getHardness() { return 3; } public function getToolType(){ return Tool::TYPE_AXE; } public function getDrops(Item $item) : array { return [ [Item::SPRUCE_DOOR, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/SpruceDoorBlock.php ================================================ meta = $meta; } public function getName(){ return "Spruce Door Block"; } } ================================================ FILE: src/pocketmine/block/SpruceWoodStairs.php ================================================ meta = $meta; } public function getHardness() { return 1.25; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getName() : string{ static $names = [ 0 => "White Stained Clay", 1 => "Orange Stained Clay", 2 => "Magenta Stained Clay", 3 => "Light Blue Stained Clay", 4 => "Yellow Stained Clay", 5 => "Lime Stained Clay", 6 => "Pink Stained Clay", 7 => "Gray Stained Clay", 8 => "Light Gray Stained Clay", 9 => "Cyan Stained Clay", 10 => "Purple Stained Clay", 11 => "Blue Stained Clay", 12 => "Brown Stained Clay", 13 => "Green Stained Clay", 14 => "Red Stained Clay", 15 => "Black Stained Clay", ]; return $names[$this->meta & 0x0f]; } } ================================================ FILE: src/pocketmine/block/Stair.php ================================================ getDamage(); $j = $damage & 0x03; $f = 0; $f1 = 0.5; $f2 = 0.5; $f3 = 1; if(($damage & 0x04) > 0){ $f = 0.5; $f1 = 1; $f2 = 0; $f3 = 0.5; } if($bb->intersectsWith($bb2 = AxisAlignedBB::getBoundingBoxFromPool( $this->x, $this->y + $f, $this->z, $this->x + 1, $this->y + $f1, $this->z + 1 ))){ $list[] = $bb2; } if($j === 0){ if($bb->intersectsWith($bb2 = AxisAlignedBB::getBoundingBoxFromPool( $this->x + 0.5, $this->y + $f2, $this->z, $this->x + 1, $this->y + $f3, $this->z + 1 ))){ $list[] = $bb2; } }elseif($j === 1){ if($bb->intersectsWith($bb2 = AxisAlignedBB::getBoundingBoxFromPool( $this->x, $this->y + $f2, $this->z, $this->x + 0.5, $this->y + $f3, $this->z + 1 ))){ $list[] = $bb2; } }elseif($j === 2){ if($bb->intersectsWith($bb2 = AxisAlignedBB::getBoundingBoxFromPool( $this->x, $this->y + $f2, $this->z + 0.5, $this->x + 1, $this->y + $f3, $this->z + 1 ))){ $list[] = $bb2; } }elseif($j === 3){ if($bb->intersectsWith($bb2 = AxisAlignedBB::getBoundingBoxFromPool( $this->x, $this->y + $f2, $this->z, $this->x + 1, $this->y + $f3, $this->z + 0.5 ))){ $list[] = $bb2; } } } */ protected function recalculateBoundingBox() { if(($this->getDamage() & 0x04) > 0){ return new AxisAlignedBB( $this->x, $this->y + 0.5, $this->z, $this->x + 1, $this->y + 1, $this->z + 1 ); }else{ return new AxisAlignedBB( $this->x, $this->y, $this->z, $this->x + 1, $this->y + 0.5, $this->z + 1 ); } } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $faces = [ 0 => 0, 1 => 2, 2 => 1, 3 => 3, ]; $this->meta = $faces[$player->getDirection()] & 0x03; if(($fy > 0.5 and $face !== 1) or $face === 0){ $this->meta |= 0x04; //Upside-down stairs } $this->getLevel()->setBlock($block, $this, true, true); return true; } public function getHardness() { return 2; } public function getResistance(){ return 15; } public function getDrops(Item $item) : array { if($item->isPickaxe() >= Tool::TIER_WOODEN){ return [ [$this->getId(), 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/StickyPiston.php ================================================ meta = $meta; } public function getName(){ return "Sticky Piston"; } } ================================================ FILE: src/pocketmine/block/StillLava.php ================================================ meta = $meta; } public function getHardness() { return 1.5; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getName(){ static $names = [ self::NORMAL => "Stone", self::GRANITE => "Granite", self::POLISHED_GRANITE => "Polished Granite", self::DIORITE => "Diorite", self::POLISHED_DIORITE => "Polished Diorite", self::ANDESITE => "Andesite", self::POLISHED_ANDESITE => "Polished Andesite", 7 => "Unknown Stone", ]; return $names[$this->meta & 0x07]; } public function getDrops(Item $item) : array { if($item->isPickaxe() >= Tool::TIER_WOODEN){ if($item->getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) > 0 and $this->getDamage() === 0){ return [ [Item::STONE, 0, 1], ]; } return [ [$this->getDamage() === 0 ? Item::COBBLESTONE : Item::STONE, $this->getDamage(), 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/StoneBrickStairs.php ================================================ meta = $meta; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getHardness() { return 1.5; } public function getName(){ return "Stone Brick Stairs"; } } ================================================ FILE: src/pocketmine/block/StoneBricks.php ================================================ meta = $meta; } public function getHardness() { return 1.5; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getName(){ static $names = [ 0 => "Stone Bricks", 1 => "Mossy Stone Bricks", 2 => "Cracked Stone Bricks", 3 => "Chiseled Stone Bricks", ]; return $names[$this->meta & 0x03]; } public function getDrops(Item $item) : array { if($item->isPickaxe() >= 1){ return [ [Item::STONE_BRICKS, $this->meta & 0x03, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/StoneButton.php ================================================ isActivated()){ $this->meta ^= 0x08; $this->getLevel()->setBlock($this, $this, true, false); $this->getLevel()->addSound(new ButtonClickSound($this)); $this->activate(); $this->getLevel()->scheduleUpdate($this, 40); } return true; } } ================================================ FILE: src/pocketmine/block/StonePressurePlate.php ================================================ meta = $meta; } public function isSolid(){ return false; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getHardness() { return 2; } public function getName(){ if($this->meta === 0x01){ return "Mossy Cobblestone Wall"; } return "Cobblestone Wall"; } protected function recalculateBoundingBox() { $north = $this->canConnect($this->getSide(Vector3::SIDE_NORTH)); $south = $this->canConnect($this->getSide(Vector3::SIDE_SOUTH)); $west = $this->canConnect($this->getSide(Vector3::SIDE_WEST)); $east = $this->canConnect($this->getSide(Vector3::SIDE_EAST)); $n = $north ? 0 : 0.25; $s = $south ? 1 : 0.75; $w = $west ? 0 : 0.25; $e = $east ? 1 : 0.75; if($north and $south and !$west and !$east){ $w = 0.3125; $e = 0.6875; }elseif(!$north and !$south and $west and $east){ $n = 0.3125; $s = 0.6875; } return new AxisAlignedBB( $this->x + $w, $this->y, $this->z + $n, $this->x + $e, $this->y + 1.5, $this->z + $s ); } public function canConnect(Block $block){ return ($block->getId() !== self::COBBLE_WALL and $block->getId() !== self::FENCE_GATE) ? $block->isSolid() and !$block->isTransparent() : true; } } ================================================ FILE: src/pocketmine/block/Stonecutter.php ================================================ meta = $meta; } public function getName(){ return "Stonecutter"; } public function getToolType(){ return Tool::TYPE_PICKAXE; } public function getDrops(Item $item) : array { if($item->isPickaxe() >= 1){ return [ [Item::STONECUTTER, 0, 1], ]; }else{ return []; } } } ================================================ FILE: src/pocketmine/block/Sugarcane.php ================================================ meta = $meta; } public function getName(){ return "Sugarcane"; } public function getDrops(Item $item) : array { return [ [Item::SUGARCANE, 0, 1], ]; } public function onActivate(Item $item, Player $player = null){ if($item->getId() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal if($this->getSide(0)->getId() !== self::SUGARCANE_BLOCK){ for($y = 1; $y < 3; ++$y){ $b = $this->getLevel()->getBlock(new Vector3($this->x, $this->y + $y, $this->z)); if($b->getId() === self::AIR){ Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($b, new Sugarcane())); if(!$ev->isCancelled()){ $this->getLevel()->setBlock($b, $ev->getNewState(), true); } break; } } $this->meta = 0; $this->getLevel()->setBlock($this, $this, true); } if(($player->gamemode & 0x01) === 0){ $item->count--; } return true; } return false; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ $down = $this->getSide(0); if($down->isTransparent() === true and $down->getId() !== self::SUGARCANE_BLOCK){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } }elseif($type === Level::BLOCK_UPDATE_RANDOM){ if($this->getSide(0)->getId() !== self::SUGARCANE_BLOCK){ if($this->meta === 0x0F){ for($y = 1; $y < 3; ++$y){ $b = $this->getLevel()->getBlock(new Vector3($this->x, $this->y + $y, $this->z)); if($b->getId() === self::AIR){ $this->getLevel()->setBlock($b, new Sugarcane(), true); break; } } $this->meta = 0; $this->getLevel()->setBlock($this, $this, true); }else{ ++$this->meta; $this->getLevel()->setBlock($this, $this, true); } return Level::BLOCK_UPDATE_RANDOM; } } return false; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(0); if($down->getId() === self::SUGARCANE_BLOCK){ $this->getLevel()->setBlock($block, new Sugarcane(), true); return true; }elseif($down->getId() === self::GRASS or $down->getId() === self::DIRT or $down->getId() === self::SAND){ $block0 = $down->getSide(2); $block1 = $down->getSide(3); $block2 = $down->getSide(4); $block3 = $down->getSide(5); if(($block0 instanceof Water) or ($block1 instanceof Water) or ($block2 instanceof Water) or ($block3 instanceof Water)){ $this->getLevel()->setBlock($block, new Sugarcane(), true); return true; } } return false; } } ================================================ FILE: src/pocketmine/block/TNT.php ================================================ meta = 1; if($player != null and $player->isCreative()){ $dropItem = false; }else{ $dropItem = true; } $mot = (new Random())->nextSignedFloat() * M_PI * 2; $tnt = Entity::createEntity("PrimedTNT", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $this->x + 0.5), new DoubleTag("", $this->y), new DoubleTag("", $this->z + 0.5) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", -sin($mot) * 0.02), new DoubleTag("", 0.2), new DoubleTag("", -cos($mot) * 0.02) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", 0), new FloatTag("", 0) ]), "Fuse" => new ByteTag("Fuse", 80) ]), $dropItem); $tnt->spawnToAll(); $this->level->addSound(new TNTPrimeSound($this)); } public function onUpdate($type){ if($type == Level::BLOCK_UPDATE_SCHEDULED){ $sides = [0, 1, 2, 3, 4, 5]; foreach($sides as $side){ $block = $this->getSide($side); if($block instanceof RedstoneSource and $block->isActivated($this)){ $this->prime(); $this->getLevel()->setBlock($this, new Air(), true); break; } } return Level::BLOCK_UPDATE_SCHEDULED; } return false; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $this->getLevel()->setBlock($this, $this, true, false); $this->getLevel()->scheduleUpdate($this, 40); } public function onActivate(Item $item, Player $player = null){ if($item->getId() === Item::FLINT_STEEL){ $this->prime($player); $this->getLevel()->setBlock($this, new Air(), true); $item->useOn($this); return true; } return false; } } ================================================ FILE: src/pocketmine/block/TallGrass.php ================================================ meta = $meta; } public function canBeReplaced(){ return true; } public function getName(){ static $names = [ 0 => "Dead Shrub", 1 => "Tall Grass", 2 => "Fern", 3 => "" ]; return $names[$this->meta & 0x03]; } public function getBurnChance() : int{ return 60; } public function getBurnAbility() : int{ return 100; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $down = $this->getSide(0); if($down->getId() === self::GRASS){ $this->getLevel()->setBlock($block, $this, true); return true; } return false; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if($this->getSide(0)->isTransparent() === true){ //Replace with common break method $this->getLevel()->setBlock($this, new Air(), false, false); return Level::BLOCK_UPDATE_NORMAL; } } return false; } public function getToolType() { return Tool::TYPE_SHEARS; } public function getDrops(Item $item) : array { if(mt_rand(0, 15) === 0){ return [ [Item::WHEAT_SEEDS, 0, 1] ]; } return []; } } ================================================ FILE: src/pocketmine/block/Thin.php ================================================ canConnect($this->getSide(2)); $flag1 = $this->canConnect($this->getSide(3)); $flag2 = $this->canConnect($this->getSide(4)); $flag3 = $this->canConnect($this->getSide(5)); if((!$flag2 or !$flag3) and ($flag2 or $flag3 or $flag or $flag1)){ if($flag2 and !$flag3){ $f = 0; }elseif(!$flag2 and $flag3){ $f1 = 1; } }else{ $f = 0; $f1 = 1; } if((!$flag or !$flag1) and ($flag2 or $flag3 or $flag or $flag1)){ if($flag and !$flag1){ $f2 = 0; }elseif(!$flag and $flag1){ $f3 = 1; } }else{ $f2 = 0; $f3 = 1; } return new AxisAlignedBB( $this->x + $f, $this->y, $this->z + $f2, $this->x + $f1, $this->y + 1, $this->z + $f3 ); } public function canConnect(Block $block){ return $block->isSolid() or $block->getId() === $this->getId() or $block->getId() === self::GLASS_PANE or $block->getId() === self::GLASS; } } ================================================ FILE: src/pocketmine/block/Torch.php ================================================ meta = $meta; } public function getLightLevel(){ return 15; } public function getName(){ return "Torch"; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ $below = $this->getSide(0); $side = $this->getDamage(); $faces = [ 1 => 4, 2 => 5, 3 => 2, 4 => 3, 5 => 0, 6 => 0, 0 => 0, ]; if($this->getSide($faces[$side])->isTransparent() === true and !($side === 0 and ($below->getId() === self::FENCE or $below->getId() === self::COBBLE_WALL or $below->getId() == Block::INACTIVE_REDSTONE_LAMP or $below->getId() == Block::ACTIVE_REDSTONE_LAMP) ) ){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } return false; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $below = $this->getSide(0); if($target->isTransparent() === false and $face !== 0){ $faces = [ 1 => 5, 2 => 4, 3 => 3, 4 => 2, 5 => 1, ]; $this->meta = $faces[$face]; $this->getLevel()->setBlock($block, $this, true, true); return true; }elseif( $below->isTransparent() === false or $below->getId() === self::FENCE or $below->getId() === self::COBBLE_WALL or $below->getId() == Block::INACTIVE_REDSTONE_LAMP or $below->getId() == Block::ACTIVE_REDSTONE_LAMP ){ $this->meta = 0; $this->getLevel()->setBlock($block, $this, true, true); return true; } return false; } public function getDrops(Item $item) : array { return [ [$this->id, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/Transparent.php ================================================ meta = $meta; } public function getName(){ return "Wooden Trapdoor"; } public function getHardness() { return 3; } public function getResistance(){ return 15; } public function canBeActivated() : bool { return true; } protected function recalculateBoundingBox() { $damage = $this->getDamage(); $f = 0.1875; if(($damage & 0x08) > 0){ $bb = new AxisAlignedBB( $this->x, $this->y + 1 - $f, $this->z, $this->x + 1, $this->y + 1, $this->z + 1 ); }else{ $bb = new AxisAlignedBB( $this->x, $this->y, $this->z, $this->x + 1, $this->y + $f, $this->z + 1 ); } if(($damage & 0x04) > 0){ if(($damage & 0x03) === 0){ $bb->setBounds( $this->x, $this->y, $this->z + 1 - $f, $this->x + 1, $this->y + 1, $this->z + 1 ); }elseif(($damage & 0x03) === 1){ $bb->setBounds( $this->x, $this->y, $this->z, $this->x + 1, $this->y + 1, $this->z + $f ); } if(($damage & 0x03) === 2){ $bb->setBounds( $this->x + 1 - $f, $this->y, $this->z, $this->x + 1, $this->y + 1, $this->z + 1 ); } if(($damage & 0x03) === 3){ $bb->setBounds( $this->x, $this->y, $this->z, $this->x + $f, $this->y + 1, $this->z + 1 ); } } return $bb; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if(($target->isTransparent() === false or $target->getId() === self::SLAB) and $face !== 0 and $face !== 1){ switch($face){ case 2: $this->meta |= 0b00000011; break; case 3: $this->meta |= 0b00000010; break; case 4: $this->meta |= 0b00000001; break; case 5: break; } if($fy > 0.5){ $this->meta |= 0b00000100; } $this->getLevel()->setBlock($block, $this, true, true); return true; } return false; } public function getDrops(Item $item) : array { return [ [$this->id, 0, 1], ]; } public function isOpened(){ return (($this->meta & 0b00001000) === 0); } public function onActivate(Item $item, Player $player = \null){ $this->meta ^= 0b00001000; $this->getLevel()->setBlock($this, $this, true); $this->level->addSound(new DoorSound($this)); return true; } public function getToolType(){ return Tool::TYPE_AXE; } } ================================================ FILE: src/pocketmine/block/TrappedChest.php ================================================ meta = $meta; } public function getBoundingBox(){ if($this->boundingBox === null){ $this->boundingBox = $this->recalculateBoundingBox(); } return $this->boundingBox; } public function isSolid(){ return true; } public function canBeFlowedInto(){ return false; } public function canBeActivated() : bool { return true; } public function getHardness() { return 2.5; } public function getResistance(){ return $this->getHardness() * 5; } public function getName(){ return "Trapped Chest"; } public function getToolType(){ return Tool::TYPE_AXE; } protected function recalculateBoundingBox() { return new AxisAlignedBB( $this->x + 0.0625, $this->y, $this->z + 0.0625, $this->x + 0.9375, $this->y + 0.9475, $this->z + 0.9375 ); } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $faces = [ 0 => 4, 1 => 2, 2 => 5, 3 => 3, ]; $chest = null; $this->meta = $faces[$player instanceof Player ? $player->getDirection() : 0]; for($side = 2; $side <= 5; ++$side){ if(($this->meta === 4 or $this->meta === 5) and ($side === 4 or $side === 5)){ continue; }elseif(($this->meta === 3 or $this->meta === 2) and ($side === 2 or $side === 3)){ continue; } $c = $this->getSide($side); if($c instanceof Chest and $c->getDamage() === $this->meta){ $tile = $this->getLevel()->getTile($c); if($tile instanceof TileChest and !$tile->isPaired()){ $chest = $tile; break; } } } $this->getLevel()->setBlock($block, $this, true, true); $nbt = new CompoundTag("", [ new ListTag("Items", []), new StringTag("id", Tile::CHEST), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); if($item->hasCustomName()){ $nbt->CustomName = new StringTag("CustomName", $item->getCustomName()); } if($item->hasCustomBlockData()){ foreach($item->getCustomBlockData() as $key => $v){ $nbt->{$key} = $v; } } $tile = Tile::createTile("Chest", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); if($chest instanceof TileChest and $tile instanceof TileChest){ $chest->pairWith($tile); $tile->pairWith($chest); } return true; } public function onBreak(Item $item){ $t = $this->getLevel()->getTile($this); if($t instanceof TileChest){ $t->unpair(); } $this->getLevel()->setBlock($this, new Air(), true, true); return true; } public function onActivate(Item $item, Player $player = null){ if($player instanceof Player){ $top = $this->getSide(1); if($top->isTransparent() !== true){ return true; } $t = $this->getLevel()->getTile($this); $chest = null; if($t instanceof TileChest){ $chest = $t; }else{ $nbt = new CompoundTag("", [ new ListTag("Items", []), new StringTag("id", Tile::CHEST), new IntTag("x", $this->x), new IntTag("y", $this->y), new IntTag("z", $this->z) ]); $nbt->Items->setTagType(NBT::TAG_Compound); $chest = Tile::createTile("Chest", $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt); } if(isset($chest->namedtag->Lock) and $chest->namedtag->Lock instanceof StringTag){ if($chest->namedtag->Lock->getValue() !== $item->getCustomName()){ return true; } } if($player->isCreative() and $player->getServer()->limitedCreative){ return true; } $player->addWindow($chest->getInventory()); } return true; } public function getDrops(Item $item) : array { return [ [$this->id, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/Tripwire.php ================================================ meta = $meta; } public function getName(){ return "Tripwire"; } public function getToolType(){ return Tool::TYPE_SHEARS; } public function getHardness(){ return 0; } public function getResistance(){ return 0; } } ================================================ FILE: src/pocketmine/block/TripwireHook.php ================================================ meta = $meta; } public function getName() :string { return "Tripwire Hook"; } public function getHardness() { return 0; } public function getResistance(){ return 0; } } ================================================ FILE: src/pocketmine/block/UnlitRedstoneTorch.php ================================================ getLevel()->setBlock($this, new Air(), true); } } ================================================ FILE: src/pocketmine/block/Vine.php ================================================ meta = $meta; } public function isSolid(){ return false; } public function getName(){ return "Vines"; } public function getHardness() { return 0.2; } public function canPassThrough(){ return true; } public function hasEntityCollision(){ return true; } public function onEntityCollide(Entity $entity){ $entity->resetFallDistance(); } protected function recalculateBoundingBox() { $f1 = 1; $f2 = 1; $f3 = 1; $f4 = 0; $f5 = 0; $f6 = 0; $flag = $this->meta > 0; if(($this->meta & 0x02) > 0){ $f4 = max($f4, 0.0625); $f1 = 0; $f2 = 0; $f5 = 1; $f3 = 0; $f6 = 1; $flag = true; } if(($this->meta & 0x08) > 0){ $f1 = min($f1, 0.9375); $f4 = 1; $f2 = 0; $f5 = 1; $f3 = 0; $f6 = 1; $flag = true; } if(($this->meta & 0x01) > 0){ $f3 = min($f3, 0.9375); $f6 = 1; $f1 = 0; $f4 = 1; $f2 = 0; $f5 = 1; $flag = true; } if(!$flag and $this->getSide(1)->isSolid()){ $f2 = min($f2, 0.9375); $f5 = 1; $f1 = 0; $f4 = 1; $f3 = 0; $f6 = 1; } return new AxisAlignedBB( $this->x + $f1, $this->y + $f2, $this->z + $f3, $this->x + $f4, $this->y + $f5, $this->z + $f6 ); } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if(!$target->isTransparent() and $target->isSolid()){ $faces = [ 0 => 0, 1 => 0, 2 => 1, 3 => 4, 4 => 8, 5 => 2, ]; if(isset($faces[$face])){ $this->meta = $faces[$face]; $this->getLevel()->setBlock($block, $this, true, true); return true; } } return false; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ /*if($this->getSide(0)->getId() === self::AIR){ //Replace with common break method Server::getInstance()->api->entity->drop($this, Item::get(LADDER, 0, 1)); $this->getLevel()->setBlock($this, new Air(), true, true, true); return Level::BLOCK_UPDATE_NORMAL; }*/ } return false; } public function getDrops(Item $item) : array { if($item->isShears()){ return [ [$this->id, 0, 1], ]; }else{ return []; } } public function getToolType(){ return Tool::TYPE_SHEARS; } } ================================================ FILE: src/pocketmine/block/WallSign.php ================================================ 3, 3 => 2, 4 => 5, 5 => 4, ]; if($type === Level::BLOCK_UPDATE_NORMAL){ if(isset($faces[$this->meta])) { if ($this->getSide($faces[$this->meta])->getId() === self::AIR) { $this->getLevel()->useBreakOn($this); } return Level::BLOCK_UPDATE_NORMAL; } } return false; } } ================================================ FILE: src/pocketmine/block/Water.php ================================================ meta = $meta; } public function getName(){ return "Water"; } public function onEntityCollide(Entity $entity){ $entity->resetFallDistance(); if($entity->fireTicks > 0){ $entity->extinguish(); } $entity->resetFallDistance(); } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $ret = $this->getLevel()->setBlock($this, $this, true, false); $this->getLevel()->scheduleUpdate($this, $this->tickRate()); return $ret; } } ================================================ FILE: src/pocketmine/block/WaterLily.php ================================================ meta = $meta; } public function isSolid(){ return false; } public function getName(){ return "Lily Pad"; } public function getHardness() { return 0; } public function getResistance(){ return 0; } public function canPassThrough(){ return true; } protected function recalculateBoundingBox() { return new AxisAlignedBB( $this->x, $this->y, $this->z, $this->x, $this->y + 0.0625, $this->z ); } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if($target instanceof Water){ $up = $target->getSide(Vector3::SIDE_UP); if($up->getId() === Block::AIR){ $this->getLevel()->setBlock($up, $this, true, true); return true; } } return false; } public function onUpdate($type){ if($type === Level::BLOCK_UPDATE_NORMAL){ if(!($this->getSide(0) instanceof Water)){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } return false; } public function getDrops(Item $item) : array { return [ [$this->id, 0, 1] ]; } } ================================================ FILE: src/pocketmine/block/WeightedPressurePlateHeavy.php ================================================ meta = $meta; } public function getName(){ return "Weighted Pressure Plate Heavy"; } } ================================================ FILE: src/pocketmine/block/WeightedPressurePlateLight.php ================================================ meta = $meta; } public function getName(){ return "Weighted Pressure Plate Light"; } } ================================================ FILE: src/pocketmine/block/Wheat.php ================================================ meta = $meta; } public function getName(){ return "Wheat Block"; } public function getDrops(Item $item) : array { $drops = []; if($this->meta >= 0x07){ $fortunel = $item->getEnchantmentLevel(Enchantment::TYPE_MINING_FORTUNE); $fortunel = $fortunel > 3 ? 3 : $fortunel; $drops[] = [Item::WHEAT, 0, 1]; $drops[] = [Item::WHEAT_SEEDS, 0, mt_rand(0, 3 + $fortunel)]; }else{ $drops[] = [Item::WHEAT_SEEDS, 0, 1]; } return $drops; } } ================================================ FILE: src/pocketmine/block/Wood.php ================================================ meta = $meta; } public function getHardness() { return 2; } public function getName(){ static $names = [ self::OAK => "Oak Wood", self::SPRUCE => "Spruce Wood", self::BIRCH => "Birch Wood", self::JUNGLE => "Jungle Wood", ]; return $names[$this->meta & 0x03]; } public function getBurnChance() : int{ return 5; } public function getBurnAbility() : int{ return 10; } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $faces = [ 0 => 0, 1 => 0, 2 => 0b1000, 3 => 0b1000, 4 => 0b0100, 5 => 0b0100, ]; $this->meta = ($this->meta & 0x03) | $faces[$face]; $this->getLevel()->setBlock($block, $this, true, true); return true; } public function getDrops(Item $item) : array { return [ [$this->id, $this->meta & 0x03, 1], ]; } public function getToolType(){ return Tool::TYPE_AXE; } } ================================================ FILE: src/pocketmine/block/Wood2.php ================================================ "Acacia Wood", 1 => "Dark Oak Wood", 2 => "Unknown", 3 => "Unknown" ]; return $names[$this->meta & 0x03]; } } ================================================ FILE: src/pocketmine/block/WoodDoor.php ================================================ meta = $meta; } public function getName(){ return "Wood Door Block"; } public function canBeActivated() : bool { return true; } public function getHardness() { return 3; } public function getToolType(){ return Tool::TYPE_AXE; } public function getDrops(Item $item) : array { return [ [Item::WOODEN_DOOR, 0, 1], ]; } } ================================================ FILE: src/pocketmine/block/WoodSlab.php ================================================ meta = $meta; } public function getHardness() { return 2; } public function getName(){ static $names = [ 0 => "Oak", 1 => "Spruce", 2 => "Birch", 3 => "Jungle", 4 => "Acacia", 5 => "Dark Oak", 6 => "", 7 => "" ]; return (($this->meta & 0x08) === 0x08 ? "Upper " : "") . $names[$this->meta & 0x07] . " Wooden Slab"; } protected function recalculateBoundingBox() { if(($this->meta & 0x08) > 0){ return new AxisAlignedBB( $this->x, $this->y + 0.5, $this->z, $this->x + 1, $this->y + 1, $this->z + 1 ); }else{ return new AxisAlignedBB( $this->x, $this->y, $this->z, $this->x + 1, $this->y + 0.5, $this->z + 1 ); } } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ $this->meta &= 0x07; if($face === 0){ if($target->getId() === self::WOOD_SLAB and ($target->getDamage() & 0x08) === 0x08 and ($target->getDamage() & 0x07) === ($this->meta & 0x07)){ $this->getLevel()->setBlock($target, Block::get(Item::DOUBLE_WOOD_SLAB, $this->meta), true); return true; }elseif($block->getId() === self::WOOD_SLAB and ($block->getDamage() & 0x07) === ($this->meta & 0x07)){ $this->getLevel()->setBlock($block, Block::get(Item::DOUBLE_WOOD_SLAB, $this->meta), true); return true; }else{ $this->meta |= 0x08; } }elseif($face === 1){ if($target->getId() === self::WOOD_SLAB and ($target->getDamage() & 0x08) === 0 and ($target->getDamage() & 0x07) === ($this->meta & 0x07)){ $this->getLevel()->setBlock($target, Block::get(Item::DOUBLE_WOOD_SLAB, $this->meta), true); return true; }elseif($block->getId() === self::WOOD_SLAB and ($block->getDamage() & 0x07) === ($this->meta & 0x07)){ $this->getLevel()->setBlock($block, Block::get(Item::DOUBLE_WOOD_SLAB, $this->meta), true); return true; } }else{ //TODO: collision if($block->getId() === self::WOOD_SLAB){ if(($block->getDamage() & 0x07) === ($this->meta & 0x07)){ $this->getLevel()->setBlock($block, Block::get(Item::DOUBLE_WOOD_SLAB, $this->meta), true); return true; } return false; }else{ if($fy > 0.5){ $this->meta |= 0x08; } } } if($block->getId() === self::WOOD_SLAB and ($target->getDamage() & 0x07) !== ($this->meta & 0x07)){ return false; } $this->getLevel()->setBlock($block, $this, true, true); return true; } public function getToolType(){ return Tool::TYPE_AXE; } public function getDrops(Item $item) : array { return [ [$this->id, $this->meta & 0x07, 1], ]; } } ================================================ FILE: src/pocketmine/block/WoodStairs.php ================================================ meta = $meta; } public function getName(){ return "Wood Stairs"; } public function getToolType(){ return Tool::TYPE_AXE; } public function getDrops(Item $item) : array { return [ [$this->id, 0, 1], ]; } public function getBurnChance() : int{ return 5; } public function getBurnAbility() : int{ return 20; } public function getHardness() { return 2; } } ================================================ FILE: src/pocketmine/block/WoodenButton.php ================================================ meta = $meta; } public function onUpdate($type){ if($type == Level::BLOCK_UPDATE_SCHEDULED){ if($this->isActivated()) { $this->meta ^= 0x08; $this->getLevel()->setBlock($this, $this, true, false); $this->getLevel()->addSound(new ButtonClickSound($this)); $this->deactivate(); } return Level::BLOCK_UPDATE_SCHEDULED; } if($type === Level::BLOCK_UPDATE_NORMAL){ $side = $this->getDamage(); if($this->isActivated()) $side ^= 0x08; $faces = [ 0 => 1, 1 => 0, 2 => 3, 3 => 2, 4 => 5, 5 => 4, ]; if($this->getSide($faces[$side]) instanceof Transparent){ $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } return false; } public function deactivate(array $ignore = []){ parent::deactivate($ignore = []); $faces = [ 0 => 1, 1 => 0, 2 => 3, 3 => 2, 4 => 5, 5 => 4, ]; $side = $this->meta; if($this->isActivated()) $side ^= 0x08; $block = $this->getSide($faces[$side])->getSide(Vector3::SIDE_UP); if(!$this->equals($block)){ $this->deactivateBlock($block); } if($side != 1){ $this->deactivateBlock($this->getSide($faces[$side], 2)); } $this->checkTorchOff($this->getSide($faces[$side]),[$this->getOppositeSide($faces[$side])]); } public function activate(array $ignore = []){ parent::activate($ignore = []); $faces = [ 0 => 1, 1 => 0, 2 => 3, 3 => 2, 4 => 5, 5 => 4, ]; $side = $this->meta; if($this->isActivated()) $side ^= 0x08; $block = $this->getSide($faces[$side])->getSide(Vector3::SIDE_UP); if(!$this->equals($block)){ $this->activateBlock($block); } if($side != 1){ $block = $this->getSide($faces[$side], 2); $this->activateBlock($block); } $this->checkTorchOn($this->getSide($faces[$side]),[$this->getOppositeSide($faces[$side])]); } public function getName(){ return "Wooden Button"; } public function getHardness() { return 0.5; } public function onBreak(Item $item){ if($this->isActivated()){ $this->meta ^= 0x08; $this->getLevel()->setBlock($this, $this, true, false); $this->deactivate(); } $this->getLevel()->setBlock($this, new Air(), true, false); } public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){ if($target->isTransparent() === false){ $this->meta = $face; $this->getLevel()->setBlock($block, $this, true, false); return true; } return false; } public function canBeActivated() : bool { return true; } public function isActivated(Block $from = null){ return (($this->meta & 0x08) === 0x08); } public function onActivate(Item $item, Player $player = null){ if(!$this->isActivated()){ $this->meta ^= 0x08; $this->getLevel()->setBlock($this, $this, true, false); $this->getLevel()->addSound(new ButtonClickSound($this)); $this->activate(); $this->getLevel()->scheduleUpdate($this, 30); } return true; } } ================================================ FILE: src/pocketmine/block/WoodenPressurePlate.php ================================================ meta = $meta; } public function getHardness(){ return 0.8; } public function getToolType(){ return Tool::TYPE_SHEARS; } public function getName(){ static $names = [ 0 => "White Wool", 1 => "Orange Wool", 2 => "Magenta Wool", 3 => "Light Blue Wool", 4 => "Yellow Wool", 5 => "Lime Wool", 6 => "Pink Wool", 7 => "Gray Wool", 8 => "Light Gray Wool", 9 => "Cyan Wool", 10 => "Purple Wool", 11 => "Blue Wool", 12 => "Brown Wool", 13 => "Green Wool", 14 => "Red Wool", 15 => "Black Wool", ]; return $names[$this->meta & 0x0f]; } public function getBurnChance() : int{ return 30; } public function getBurnAbility() : int{ return 60; } } ================================================ FILE: src/pocketmine/block/Workbench.php ================================================ meta = $meta; } public function canBeActivated() : bool { return true; } public function getHardness() { return 2.5; } public function getName(){ return "Crafting Table"; } public function getToolType(){ return Tool::TYPE_AXE; } public function onActivate(Item $item, Player $player = null){ if($player instanceof Player){ if($player->getServer()->limitedCreative and $player->isCreative()) return true; $player->craftingType = Player::CRAFTING_BIG; } return true; } public function getDrops(Item $item) : array { return [ [$this->id, 0, 1], ]; } } ================================================ FILE: src/pocketmine/command/Command.php ================================================ commandData = self::generateDefaultData(); $this->name = $this->nextLabel = $this->label = $name; $this->setDescription($description); $this->usageMessage = $usageMessage === null ? "/" . $name : $usageMessage; $this->setAliases($aliases); $this->timings = new TimingsHandler("** Command: " . $name); } /** * Returns an \stdClass containing command data * * @return \stdClass */ public function getDefaultCommandData() : \stdClass{ return $this->commandData; } /** * Generates modified command data for the specified player * for AvailableCommandsPacket. * * @param Player $player * * @return \stdClass|null */ public function generateCustomCommandData(Player $player){ //TODO: fix command permission filtering on join /*if(!$this->testPermission($player)){ return null; }*/ $customData = clone $this->commandData; $customData->aliases = $this->getAliases(); /*foreach($customData->overloads as &$overload){ if(($p = @$overload->pocketminePermission) !== null and !$player->hasPermission($p)){ unset($overload); } }*/ return $customData; } public function getOverloads(): \stdClass{ return $this->commandData->overloads; } /** * @param CommandSender $sender * @param string $commandLabel * @param string[] $args * * @return mixed */ public abstract function execute(CommandSender $sender, $commandLabel, array $args); /** * @return string */ public function getName() : string{ return $this->name; } /** * @return string */ public function getPermission(){ return $this->commandData->pocketminePermission ?? null; } /** * @param string|null $permission */ public function setPermission($permission){ if($permission !== null){ $this->commandData->pocketminePermission = $permission; }else{ unset($this->commandData->pocketminePermission); } } /** * @param CommandSender $target * * @return bool */ public function testPermission(CommandSender $target){ if($this->testPermissionSilent($target)){ return true; } if($this->permissionMessage === null){ $target->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission")); }elseif($this->permissionMessage !== ""){ $target->sendMessage(str_replace("", $this->getPermission(), $this->permissionMessage)); } return false; } /** * @param CommandSender $target * * @return bool */ public function testPermissionSilent(CommandSender $target){ if(($perm = $this->getPermission()) === null or $perm === ""){ return true; } foreach(explode(";", $perm) as $permission){ if($target->hasPermission($permission)){ return true; } } return false; } /** * @return string */ public function getLabel(){ return $this->label; } public function setLabel($name){ $this->nextLabel = $name; if(!$this->isRegistered()){ $this->timings = new TimingsHandler("** Command: " . $name); $this->label = $name; return true; } return false; } /** * Registers the command into a Command map * * @param CommandMap $commandMap * * @return bool */ public function register(CommandMap $commandMap){ if($this->allowChangesFrom($commandMap)){ $this->commandMap = $commandMap; return true; } return false; } /** * @param CommandMap $commandMap * * @return bool */ public function unregister(CommandMap $commandMap){ if($this->allowChangesFrom($commandMap)){ $this->commandMap = null; $this->activeAliases = $this->commandData->aliases; $this->label = $this->nextLabel; return true; } return false; } /** * @param CommandMap $commandMap * * @return bool */ private function allowChangesFrom(CommandMap $commandMap){ return $this->commandMap === null or $this->commandMap === $commandMap; } /** * @return bool */ public function isRegistered(){ return $this->commandMap !== null; } /** * @return string[] */ public function getAliases(){ return $this->activeAliases; } /** * @return string */ public function getPermissionMessage(){ return $this->permissionMessage; } /** * @return string */ public function getDescription(){ return $this->commandData->description; } /** * @return string */ public function getUsage(){ return $this->usageMessage; } /** * @param string[] $aliases */ public function setAliases(array $aliases){ $this->commandData->aliases = $aliases; if(!$this->isRegistered()){ $this->activeAliases = (array) $aliases; } } /** * @param string $description */ public function setDescription($description){ $this->commandData->description = $description; } /** * @param string $permissionMessage */ public function setPermissionMessage($permissionMessage){ $this->permissionMessage = $permissionMessage; } /** * @param string $usage */ public function setUsage($usage){ $this->usageMessage = $usage; } public static final function generateDefaultData() : \stdClass{ if(self::$defaultDataTemplate === null){ self::$defaultDataTemplate = json_decode(file_get_contents(Server::getInstance()->getFilePath() . "src/pocketmine/resources/command_default.json")); } return clone self::$defaultDataTemplate; } /** * @param CommandSender $source * @param string $message * @param bool $sendToSource */ public static function broadcastCommandMessage(CommandSender $source, $message, $sendToSource = true){ if($message instanceof TextContainer){ $m = clone $message; $result = "[".$source->getName().": ".($source->getServer()->getLanguage()->get($m->getText()) !== $m->getText() ? "%" : "") . $m->getText() ."]"; $users = $source->getServer()->getPluginManager()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_ADMINISTRATIVE); $colored = TextFormat::GRAY . TextFormat::ITALIC . $result; $m->setText($result); $result = clone $m; $m->setText($colored); $colored = clone $m; }else{ $users = $source->getServer()->getPluginManager()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_ADMINISTRATIVE); $result = new TranslationContainer("chat.type.admin", [$source->getName(), $message]); $colored = new TranslationContainer(TextFormat::GRAY . TextFormat::ITALIC . "%chat.type.admin", [$source->getName(), $message]); } if($sendToSource === true and !($source instanceof ConsoleCommandSender)){ $source->sendMessage($message); } foreach($users as $user){ if($user instanceof CommandSender){ if($user instanceof ConsoleCommandSender){ $user->sendMessage($result); }elseif($user !== $source){ $user->sendMessage($colored); } } } } /** * @return string */ public function __toString(){ return $this->name; } } ================================================ FILE: src/pocketmine/command/CommandExecutor.php ================================================ stdin = fopen("php://stdin", "r"); $opts = getopt("", ["disable-readline"]); if(extension_loaded("readline") && !isset($opts["disable-readline"]) && (!function_exists("posix_isatty") || posix_isatty($this->stdin))){ $this->readline = true; }else{ $this->readline = false; } $this->logger = $logger; $this->buffer = new \Threaded; $this->start(); } public function shutdown(){ $this->shutdown = true; } private function readline_callback($line){ if($line !== ""){ $this->buffer[] = $line; readline_add_history($line); } } private function readLine(){ if(!$this->readline){ $line = trim(fgets($this->stdin)); if($line !== ""){ $this->buffer[] = $line; } }else{ readline_callback_read_char(); } } /** * Reads a line from console, if available. Returns null if not available * * @return string|null */ public function getLine(){ if($this->buffer->count() !== 0){ return $this->buffer->shift(); } return null; } public function quit(){ $this->shutdown(); // Windows sucks if(Utils::getOS() !== "win"){ parent::quit(); } } public function run(){ if($this->readline){ readline_callback_handler_install("Elywing> ", [$this, "readline_callback"]); $this->logger->setConsoleCallback("readline_redisplay"); } while(!$this->shutdown){ $r = [$this->stdin]; $w = null; $e = null; if(stream_select($r, $w, $e, 0, 200000) > 0){ // PHP on Windows sucks if(feof($this->stdin)){ if(Utils::getOS() == "win"){ $this->stdin = fopen("php://stdin", "r"); if(!is_resource($this->stdin)){ break; } }else{ break; } } $this->readLine(); } } if($this->readline){ $this->logger->setConsoleCallback(null); readline_callback_handler_remove(); } } public function getThreadName(){ return "Console"; } } ================================================ FILE: src/pocketmine/command/CommandSender.php ================================================ perm = new PermissibleBase($this); } /** * @param \pocketmine\permission\Permission|string $name * * @return bool */ public function isPermissionSet($name){ return $this->perm->isPermissionSet($name); } /** * @param \pocketmine\permission\Permission|string $name * * @return bool */ public function hasPermission($name){ return $this->perm->hasPermission($name); } /** * @param Plugin $plugin * @param string $name * @param bool $value * * @return \pocketmine\permission\PermissionAttachment */ public function addAttachment(Plugin $plugin, $name = null, $value = null){ return $this->perm->addAttachment($plugin, $name, $value); } /** * @param PermissionAttachment $attachment * * @return void */ public function removeAttachment(PermissionAttachment $attachment){ $this->perm->removeAttachment($attachment); } public function recalculatePermissions(){ $this->perm->recalculatePermissions(); } /** * @return \pocketmine\permission\PermissionAttachmentInfo[] */ public function getEffectivePermissions(){ return $this->perm->getEffectivePermissions(); } /** * @return bool */ public function isPlayer(){ return false; } /** * @return \pocketmine\Server */ public function getServer(){ return Server::getInstance(); } /** * @param string $message */ public function sendMessage($message){ if($message instanceof TextContainer){ $message = $this->getServer()->getLanguage()->translate($message); }else{ $message = $this->getServer()->getLanguage()->translateString($message); } foreach(explode("\n", trim($message)) as $line){ MainLogger::getLogger()->info($line); } } /** * @return string */ public function getName() : string{ return "CONSOLE"; } /** * @return bool */ public function isOp(){ return true; } /** * @param bool $value */ public function setOp($value){ } } ================================================ FILE: src/pocketmine/command/FormattedCommandAlias.php ================================================ formatStrings = $formatStrings; } public function execute(CommandSender $sender, $commandLabel, array $args){ $commands = []; $result = false; foreach($this->formatStrings as $formatString){ try{ $commands[] = $this->buildCommand($formatString, $args); }catch(\Throwable $e){ if($e instanceof \InvalidArgumentException){ $sender->sendMessage(TextFormat::RED . $e->getMessage()); }else{ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.exception")); $logger = $sender->getServer()->getLogger(); if($logger instanceof MainLogger){ $logger->logException($e); } } return false; } } foreach($commands as $command){ $result |= Server::getInstance()->dispatchCommand($sender, $command); } return (bool) $result; } /** * @param string $formatString * @param array $args * * @return string * @throws \InvalidArgumentException */ private function buildCommand($formatString, array $args){ $index = strpos($formatString, '$'); while($index !== false){ $start = $index; if($index > 0 and $formatString{$start - 1} === "\\"){ $formatString = substr($formatString, 0, $start - 1) . substr($formatString, $start); $index = strpos($formatString, '$', $index); continue; } $required = false; if($formatString{$index + 1} == '$'){ $required = true; ++$index; } ++$index; $argStart = $index; while($index < strlen($formatString) and self::inRange(ord($formatString{$index}) - 48, 0, 9)){ ++$index; } if($argStart === $index){ throw new \InvalidArgumentException("Invalid replacement token"); } $position = intval(substr($formatString, $argStart, $index)); if($position === 0){ throw new \InvalidArgumentException("Invalid replacement token"); } --$position; $rest = false; if($index < strlen($formatString) and $formatString{$index} === "-"){ $rest = true; ++$index; } $end = $index; if($required and $position >= count($args)){ throw new \InvalidArgumentException("Missing required argument " . ($position + 1)); } $replacement = ""; if($rest and $position < count($args)){ for($i = $position; $i < count($args); ++$i){ if($i !== $position){ $replacement .= " "; } $replacement .= $args[$i]; } }elseif($position < count($args)){ $replacement .= $args[$position]; } $formatString = substr($formatString, 0, $start) . $replacement . substr($formatString, $end); $index = $start + strlen($replacement); $index = strpos($formatString, '$', $index); } return $formatString; } /** * @param int $i * @param int $j * @param int $k * * @return bool */ private static function inRange($i, $j, $k){ return $i >= $j and $i <= $k; } } ================================================ FILE: src/pocketmine/command/PluginCommand.php ================================================ owningPlugin = $owner; $this->executor = $owner; $this->usageMessage = ""; } public function execute(CommandSender $sender, $commandLabel, array $args){ if(!$this->owningPlugin->isEnabled()){ return false; } if(!$this->testPermission($sender)){ return false; } $success = $this->executor->onCommand($sender, $this, $commandLabel, $args); if(!$success and $this->usageMessage !== ""){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); } return $success; } public function getExecutor(){ return $this->executor; } /** * @param CommandExecutor $executor */ public function setExecutor(CommandExecutor $executor){ $this->executor = ($executor != null) ? $executor : $this->owningPlugin; } /** * @return Plugin */ public function getPlugin(){ return $this->owningPlugin; } } ================================================ FILE: src/pocketmine/command/PluginIdentifiableCommand.php ================================================ getServer()->getLanguage()->translate($message); }else{ $message = $this->getServer()->getLanguage()->translateString($message); } $this->messages .= trim($message, "\r\n") . "\n"; } public function getMessage(){ return $this->messages; } public function getName() : string{ return "Rcon"; } } ================================================ FILE: src/pocketmine/command/SimpleCommandMap.php ================================================ server = $server; /** @var bool[] */ $this->commandConfig = $this->server->getProperty("commands"); $this->setDefaultCommands(); } private function setDefaultCommands(){ $this->register("pocketmine", new WeatherCommand("weather")); $this->register("pocketmine", new BanCidCommand("bancid")); $this->register("pocketmine", new PardonCidCommand("pardoncid")); $this->register("pocketmine", new BanCidByNameCommand("bancidbyname")); $this->register("pocketmine", new BanIpByNameCommand("banipbyname")); $this->register("pocketmine", new VersionCommand("version")); $this->register("pocketmine", new PluginsCommand("plugins")); $this->register("pocketmine", new SeedCommand("seed")); $this->register("pocketmine", new HelpCommand("help"), null, true); $this->register("pocketmine", new StopCommand("stop"), null, true); $this->register("pocketmine", new TellCommand("tell")); $this->register("pocketmine", new DefaultGamemodeCommand("defaultgamemode")); $this->register("pocketmine", new BanCommand("ban")); $this->register("pocketmine", new BanIpCommand("ban-ip")); $this->register("pocketmine", new BanListCommand("banlist")); $this->register("pocketmine", new PardonCommand("pardon")); $this->register("pocketmine", new PardonIpCommand("pardon-ip")); $this->register("pocketmine", new SayCommand("say")); $this->register("pocketmine", new MeCommand("me")); $this->register("pocketmine", new ListCommand("list")); $this->register("pocketmine", new DifficultyCommand("difficulty")); $this->register("pocketmine", new KickCommand("kick")); $this->register("pocketmine", new OpCommand("op")); $this->register("pocketmine", new DeopCommand("deop")); $this->register("pocketmine", new WhitelistCommand("whitelist")); $this->register("pocketmine", new SaveOnCommand("save-on")); $this->register("pocketmine", new SaveOffCommand("save-off")); $this->register("pocketmine", new SaveCommand("save-all"), null, true); $this->register("pocketmine", new GiveCommand("give")); $this->register("pocketmine", new EffectCommand("effect")); $this->register("pocketmine", new EnchantCommand("enchant")); $this->register("pocketmine", new ParticleCommand("particle")); $this->register("pocketmine", new GamemodeCommand("gamemode")); $this->register("pocketmine", new KillCommand("kill")); $this->register("pocketmine", new SpawnpointCommand("spawnpoint")); $this->register("pocketmine", new SetWorldSpawnCommand("setworldspawn")); $this->register("pocketmine", new SummonCommand("summon")); $this->register("pocketmine", new TeleportCommand("tp")); $this->register("pocketmine", new TimeCommand("time")); $this->register("pocketmine", new TimingsCommand("timings")); $this->register("pocketmine", new TransferCommand("transferserver")); $this->register("pocketmine", new ReloadCommand("reload"), null, true); $this->register("pocketmine", new XpCommand("xp")); $this->register("pocketmine", new SetBlockCommand("setblock")); if($this->server->getProperty("debug.commands", false)){ $this->register("pocketmine", new StatusCommand("status"), null, true); $this->register("pocketmine", new GarbageCollectorCommand("gc"), null, true); $this->register("pocketmine", new DumpMemoryCommand("dumpmemory"), null, true); } } public function registerAll($fallbackPrefix, array $commands){ foreach($commands as $command){ $this->register($fallbackPrefix, $command); } } public function register($fallbackPrefix, Command $command, $label = null, $overrideConfig = false){ if($label === null){ $label = $command->getName(); } $label = strtolower(trim($label)); //Check if command was disabled in config and for override if(!(($this->commandConfig[$label] ?? $this->commandConfig["default"] ?? true) or $overrideConfig)){ return false; } $fallbackPrefix = strtolower(trim($fallbackPrefix)); $registered = $this->registerAlias($command, false, $fallbackPrefix, $label); $aliases = $command->getAliases(); foreach($aliases as $index => $alias){ if(!$this->registerAlias($command, true, $fallbackPrefix, $alias)){ unset($aliases[$index]); } } $command->setAliases($aliases); if(!$registered){ $command->setLabel($fallbackPrefix . ":" . $label); } $command->register($this); return $registered; } private function registerAlias(Command $command, $isAlias, $fallbackPrefix, $label){ $this->knownCommands[$fallbackPrefix . ":" . $label] = $command; if(($command instanceof VanillaCommand or $isAlias) and isset($this->knownCommands[$label])){ return false; } if(isset($this->knownCommands[$label]) and $this->knownCommands[$label]->getLabel() !== null and $this->knownCommands[$label]->getLabel() === $label){ return false; } if(!$isAlias){ $command->setLabel($label); } $this->knownCommands[$label] = $command; return true; } private function dispatchAdvanced(CommandSender $sender, Command $command, $label, array $args, $offset = 0){ if(isset($args[$offset])){ $argsTemp = $args; switch($args[$offset]){ case "@a": $p = $this->server->getOnlinePlayers(); if(count($p) <= 0){ $sender->sendMessage(TextFormat::RED . "No players online"); //TODO: add language }else{ foreach($p as $player){ $argsTemp[$offset] = $player->getName(); $this->dispatchAdvanced($sender, $command, $label, $argsTemp, $offset + 1); } } break; case "@r": $players = $this->server->getOnlinePlayers(); if(count($players) > 0){ $argsTemp[$offset] = $players[array_rand($players)]->getName(); $this->dispatchAdvanced($sender, $command, $label, $argsTemp, $offset + 1); } break; case "@p": if($sender instanceof Player){ $argsTemp[$offset] = $sender->getName(); $this->dispatchAdvanced($sender, $command, $label, $argsTemp, $offset + 1); }else{ $sender->sendMessage(TextFormat::RED . "You must be a player!"); //TODO: add language } break; default: $this->dispatchAdvanced($sender, $command, $label, $argsTemp, $offset + 1); } }else $command->execute($sender, $label, $args); } public function dispatch(CommandSender $sender, $commandLine){ $args = explode(" ", $commandLine); if(count($args) === 0){ return false; } $sentCommandLabel = strtolower(array_shift($args)); $target = $this->getCommand($sentCommandLabel); if($target === null){ return false; } $target->timings->startTiming(); try{ if($this->server->advancedCommandSelector){ $this->dispatchAdvanced($sender, $target, $sentCommandLabel, $args); }else{ $target->execute($sender, $sentCommandLabel, $args); } }catch(\Throwable $e){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.exception")); $this->server->getLogger()->critical($this->server->getLanguage()->translateString("pocketmine.command.exception", [$commandLine, (string) $target, $e->getMessage()])); $logger = $sender->getServer()->getLogger(); if($logger instanceof MainLogger){ $logger->logException($e); } } $target->timings->stopTiming(); return true; } public function clearCommands(){ foreach($this->knownCommands as $command){ $command->unregister($this); } $this->knownCommands = []; $this->setDefaultCommands(); } public function getCommand($name){ if(isset($this->knownCommands[$name])){ return $this->knownCommands[$name]; } return null; } /** * @return Command[] */ public function getCommands(){ return $this->knownCommands; } /** * @return void */ public function registerServerAliases(){ $values = $this->server->getCommandAliases(); foreach($values as $alias => $commandStrings){ if(strpos($alias, ":") !== false or strpos($alias, " ") !== false){ $this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.illegal", [$alias])); continue; } $targets = []; $bad = ""; foreach($commandStrings as $commandString){ $args = explode(" ", $commandString); $command = $this->getCommand($args[0]); if($command === null){ if(strlen($bad) > 0){ $bad .= ", "; } $bad .= $commandString; }else{ $targets[] = $commandString; } } if(strlen($bad) > 0){ $this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.notFound", [$alias, $bad])); continue; } //These registered commands have absolute priority if(count($targets) > 0){ $this->knownCommands[strtolower($alias)] = new FormattedCommandAlias(strtolower($alias), $targets); }else{ unset($this->knownCommands[strtolower($alias)]); } } } } ================================================ FILE: src/pocketmine/command/defaults/BanCidByNameCommand.php ================================================ setPermission("pocketmine.command.bancidbyname"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) === 0){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $name = array_shift($args); $reason = implode(" ", $args); if ($sender->getServer()->getPlayer($name) instanceof Player) $target = $sender->getServer()->getPlayer($name); else return false; $sender->getServer()->getCIDBans()->addBan($target->getClientId(), $reason, null, $sender->getName()); $target->kick($reason !== "" ? "Banned by admin. Reason:" . $reason : "Banned by admin."); Command::broadcastCommandMessage($sender, new TranslationContainer("%commands.bancidbyname.success", [$target !== null ? $target->getName() : $name])); return true; } } ================================================ FILE: src/pocketmine/command/defaults/BanCidCommand.php ================================================ setPermission("pocketmine.command.bancid"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) === 0){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $cid = array_shift($args); $reason = implode(" ", $args); $sender->getServer()->getCIDBans()->addBan($cid, $reason, null, $sender->getName()); $player = null; foreach($sender->getServer()->getOnlinePlayers() as $p){ if($p->getClientId() == $cid) { $p->kick($reason !== "" ? "Banned by admin. Reason:" . $reason : "Banned by admin."); $player = $p; break; } } Command::broadcastCommandMessage($sender, new TranslationContainer("%commands.bancid.success", [$player !== null ? $player->getName() : $cid])); return true; } } ================================================ FILE: src/pocketmine/command/defaults/BanCommand.php ================================================ setPermission("pocketmine.command.ban.player"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) === 0){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $name = array_shift($args); if(isset($args[0]) and isset($args[1])){ $reason = $args[0]; if($args[1] != null and is_numeric($args[1])){ $until = new \DateTime('@' . ($args[1] * 86400 + time())); }else{ $until = null; } $sender->getServer()->getNameBans()->addBan($name, $reason, $until, $sender->getName()); }else{ $sender->getServer()->getNameBans()->addBan($name, $reason = implode(" ", $args), null, $sender->getName()); } Command::broadcastCommandMessage($sender, new TranslationContainer("%commands.ban.success", [$player !== null ? $player->getName() : $name])); return true; } } ================================================ FILE: src/pocketmine/command/defaults/BanIpByNameCommand.php ================================================ setPermission("pocketmine.command.banipbyname"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return \true; } if(\count($args) === 0){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return \false; } $name = \array_shift($args); $reason = \implode(" ", $args); if ($sender->getServer()->getPlayer($name) instanceof Player) $target = $sender->getServer()->getPlayer($name); else return \false; $sender->getServer()->getIPBans()->addBan($target->getAddress(), $reason, \null, $sender->getName()); if(($player = $sender->getServer()->getPlayerExact($name)) instanceof Player){ $player->kick($reason !== "" ? "Banned by admin. Reason:" . $reason : "Banned by admin."); } Command::broadcastCommandMessage($sender, new TranslationContainer("%commands.banipbyname.success", [$player !== \null ? $player->getName() : $name])); return \true; } } ================================================ FILE: src/pocketmine/command/defaults/BanIpCommand.php ================================================ setPermission("pocketmine.command.ban.ip"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) === 0){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $value = array_shift($args); $reason = implode(" ", $args); if(preg_match("/^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])$/", $value)){ $this->processIPBan($value, $sender, $reason); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.banip.success", [$value])); }else{ if(($player = $sender->getServer()->getPlayer($value)) instanceof Player){ $this->processIPBan($player->getAddress(), $sender, $reason); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.banip.success.players", [$player->getAddress(), $player->getName()])); }else{ $sender->sendMessage(new TranslationContainer("commands.banip.invalid")); return false; } } return true; } private function processIPBan($ip, CommandSender $sender, $reason){ $sender->getServer()->getIPBans()->addBan($ip, $reason, null, $sender->getName()); foreach($sender->getServer()->getOnlinePlayers() as $player){ if($player->getAddress() === $ip){ $player->kick($reason !== "" ? $reason : "IP banned."); } } $sender->getServer()->getNetwork()->blockAddress($ip, -1); } } ================================================ FILE: src/pocketmine/command/defaults/BanListCommand.php ================================================ setPermission("pocketmine.command.ban.list"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } $args[0] = (isset($args[0]) ? strtolower($args[0]): ""); $title = ""; switch($args[0]){ case "ips": $list = $sender->getServer()->getIPBans(); $title = "commands.banlist.ips"; break; case "cids": $list = $list = $sender->getServer()->getCIDBans(); $title = "commands.banlist.cids"; break; case "players": $list = $sender->getServer()->getNameBans(); $title = "commands.banlist.players"; break; default: $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $message = ""; $list = $list->getEntries(); foreach($list as $entry){ $message .= $entry->getName() . ", "; } $sender->sendMessage(Server::getInstance()->getLanguage()->translateString($title, [count($list)])); $sender->sendMessage(\substr($message, 0, -2)); return true; } } ================================================ FILE: src/pocketmine/command/defaults/DefaultGamemodeCommand.php ================================================ setPermission("pocketmine.command.defaultgamemode"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) === 0){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $gameMode = Server::getGamemodeFromString($args[0]); if($gameMode !== -1){ $sender->getServer()->setConfigInt("gamemode", $gameMode); $sender->sendMessage(new TranslationContainer("commands.defaultgamemode.success", [Server::getGamemodeString($gameMode)])); }else{ $sender->sendMessage("You entered an unknown gamemode"); } return true; } } ================================================ FILE: src/pocketmine/command/defaults/DeopCommand.php ================================================ setPermission("pocketmine.command.op.take"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) === 0){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $name = array_shift($args); $player = $sender->getServer()->getOfflinePlayer($name); $player->setOp(false); if($player instanceof Player){ $player->sendMessage(TextFormat::GRAY . "You are no longer op!"); } Command::broadcastCommandMessage($sender, new TranslationContainer("commands.deop.success", [$player->getName()])); return true; } } ================================================ FILE: src/pocketmine/command/defaults/DifficultyCommand.php ================================================ setPermission("pocketmine.command.difficulty"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) !== 1){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $difficulty = Server::getDifficultyFromString($args[0]); if($sender->getServer()->isHardcore()){ $difficulty = 3; } if($difficulty !== -1){ $sender->getServer()->setConfigInt("difficulty", $difficulty); $pk = new SetDifficultyPacket(); $pk->difficulty = $sender->getServer()->getDifficulty(); Server::broadcastPacket($sender->getServer()->getOnlinePlayers(), $pk); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.difficulty.success", [$difficulty])); }else{ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } return true; } } ================================================ FILE: src/pocketmine/command/defaults/DumpMemoryCommand.php ================================================ setPermission("pocketmine.command.dumpmemory"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } Command::broadcastCommandMessage($sender, "Dumping server memory"); $sender->getServer()->getMemoryManager()->dumpServerMemory(isset($args[0]) ? $args[0] : $sender->getServer()->getDataPath() . "/memory_dumps/memoryDump_".date("D_M_j-H.i.s-T_Y", time()), 48, 80); return true; } } ================================================ FILE: src/pocketmine/command/defaults/EffectCommand.php ================================================ setPermission("pocketmine.command.effect;pocketmine.command.effect.other"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) < 2){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return true; } $player = $sender->getServer()->getPlayer($args[0]); if($player === null){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.player.notFound")); return true; } if($player->getName()!=$sender->getName() && !$sender->hasPermission("pocketmine.command.effect.other")){ $sender->sendMessage("You don't have permission to give effect to other player ."); return true; } if(strtolower($args[1]) === "clear"){ foreach($player->getEffects() as $effect){ $player->removeEffect($effect->getId()); } $sender->sendMessage(new TranslationContainer("commands.effect.success.removed.all", [$player->getDisplayName()])); return true; } $effect = Effect::getEffectByName($args[1]); if($effect === null){ $effect = Effect::getEffect((int) $args[1]); } if($effect === null){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.effect.notFound", [(string) $args[1]])); return true; } $duration = 300; $amplification = 0; if(count($args) >= 3){ $duration = (int) $args[2]; if(!($effect instanceof InstantEffect)){ $duration *= 20; } }elseif($effect instanceof InstantEffect){ $duration = 1; } if(count($args) >= 4){ $amplification = (int) $args[3]; } if(count($args) >= 5){ $v = strtolower($args[4]); if($v === "on" or $v === "true" or $v === "t" or $v === "1"){ $effect->setVisible(false); } } if($duration === 0){ if(!$player->hasEffect($effect->getId())){ if(count($player->getEffects()) === 0){ $sender->sendMessage(new TranslationContainer("commands.effect.failure.notActive.all", [$player->getDisplayName()])); }else{ $sender->sendMessage(new TranslationContainer("commands.effect.failure.notActive", [$effect->getName(), $player->getDisplayName()])); } return true; } if ($player->removeEffect($effect->getId())) { $sender->sendMessage(new TranslationContainer("commands.effect.success.removed", [$effect->getName(), $player->getDisplayName()])); } }else{ $effect->setDuration($duration)->setAmplifier($amplification); if ($player->addEffect($effect)) { self::broadcastCommandMessage($sender, new TranslationContainer("%commands.effect.success", [$effect->getName(), $effect->getId(), $effect->getAmplifier(), $player->getDisplayName(), $effect->getDuration() / 20])); } } return true; } } ================================================ FILE: src/pocketmine/command/defaults/EnchantCommand.php ================================================ setPermission("pocketmine.command.enchant"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) < 2){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return true; } $player = $sender->getServer()->getPlayer($args[0]); if($player === null){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.player.notFound")); return true; } $enchantId = $args[1]; $enchantLevel = isset($args[2]) ? (int) $args[2] : 1; $enchantment = Enchantment::getEnchantment($enchantId); if($enchantment->getId() === Enchantment::TYPE_INVALID){ $enchantment = Enchantment::getEnchantmentByName($enchantId); if($enchantment->getId() === Enchantment::TYPE_INVALID){ $sender->sendMessage(new TranslationContainer("commands.enchant.notFound", [$enchantment->getId()])); return true; } } $id = $enchantment->getId(); $maxLevel = Enchantment::getEnchantMaxLevel($id); if($enchantLevel > $maxLevel or $enchantLevel <= 0){ $sender->sendMessage(new TranslationContainer("commands.enchant.maxLevel", [$maxLevel])); return true; } $enchantment->setLevel($enchantLevel); $item = $player->getInventory()->getItemInHand(); if($item->getId() <= 0){ $sender->sendMessage(new TranslationContainer("commands.enchant.noItem")); return true; } if(Enchantment::getEnchantAbility($item) === 0){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.enchant.cantEnchant")); return true; } $item->addEnchantment($enchantment); $player->getInventory()->setItemInHand($item); self::broadcastCommandMessage($sender, new TranslationContainer("%commands.enchant.success")); return true; } } ================================================ FILE: src/pocketmine/command/defaults/GamemodeCommand.php ================================================ setPermission("pocketmine.command.gamemode"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) === 0){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $gameMode = (int) Server::getGamemodeFromString($args[0]); if($gameMode === -1){ $sender->sendMessage("Unknown game mode"); return true; } $target = $sender; if(isset($args[1])){ $target = $sender->getServer()->getPlayer($args[1]); if($target === null){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.player.notFound")); return true; } }elseif(!($sender instanceof Player)){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return true; } if($target->setGamemode($gameMode) == false){ $sender->sendMessage(TextFormat::RED . "Game mode change for " . $target->getName() . " failed!"); }else{ if($target === $sender){ Command::broadcastCommandMessage($sender, new TranslationContainer("commands.gamemode.success.self", [' ', ' ', Server::getGamemodeString($gameMode)])); }else{ $target->sendMessage(new TranslationContainer("gameMode.changed")); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.gamemode.success.other", [$target->getName(), Server::getGamemodeString($gameMode)])); } } return true; } } ================================================ FILE: src/pocketmine/command/defaults/GarbageCollectorCommand.php ================================================ setPermission("pocketmine.command.gc"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } $chunksCollected = 0; $entitiesCollected = 0; $tilesCollected = 0; $memory = memory_get_usage(); foreach($sender->getServer()->getLevels() as $level){ $diff = [count($level->getChunks()), count($level->getEntities()), count($level->getTiles())]; $level->doChunkGarbageCollection(); $level->unloadChunks(true); $chunksCollected += $diff[0] - count($level->getChunks()); $entitiesCollected += $diff[1] - count($level->getEntities()); $tilesCollected += $diff[2] - count($level->getTiles()); $level->clearCache(true); } $cyclesCollected = $sender->getServer()->getMemoryManager()->triggerGarbageCollector(); $sender->sendMessage(TextFormat::GREEN . "---- " . TextFormat::WHITE . "%pocketmine.command.gc.title" . TextFormat::GREEN . " ----"); $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.gc.chunks " . TextFormat::RED . \number_format($chunksCollected)); $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.gc.entities " . TextFormat::RED . \number_format($entitiesCollected)); $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.gc.tiles " . TextFormat::RED . \number_format($tilesCollected)); $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.gc.cycles " . TextFormat::RED . \number_format($cyclesCollected)); $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.gc.memory " . TextFormat::RED . \number_format(\round((($memory - \memory_get_usage()) / 1024) / 1024, 2))." MB"); return true; } } ================================================ FILE: src/pocketmine/command/defaults/GiveCommand.php ================================================ setPermission("pocketmine.command.give"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) < 2){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return true; } $player = $sender->getServer()->getPlayer($args[0]); $item = Item::fromString($args[1]); if(!isset($args[2])){ $item->setCount($item->getMaxStackSize()); }else{ $item->setCount((int) $args[2]); } if(isset($args[3])){ $tags = $exception = null; $data = implode(" ", array_slice($args, 3)); try{ $tags = NBT::parseJSON($data); }catch (\Throwable $ex){ $exception = $ex; } if(!($tags instanceof CompoundTag) or $exception !== null){ $sender->sendMessage(new TranslationContainer("commands.give.tagError", [$exception !== null ? $exception->getMessage() : "Invalid tag conversion"])); return true; } $item->setNamedTag($tags); } if($player instanceof Player){ if($item->getId() === 0){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.give.item.notFound", [$args[1]])); return true; } //TODO: overflow $player->getInventory()->addItem(clone $item); }else{ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.player.notFound")); return true; } Command::broadcastCommandMessage($sender, new TranslationContainer("%commands.give.success", [ $item->getName() . " (" . $item->getId() . ":" . $item->getDamage() . ")", (string) $item->getCount(), $player->getName() ])); return true; } } ================================================ FILE: src/pocketmine/command/defaults/HelpCommand.php ================================================ setPermission("pocketmine.command.help"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) === 0){ $command = ""; $pageNumber = 1; }elseif(is_numeric($args[count($args) - 1])){ $pageNumber = (int) array_pop($args); if($pageNumber <= 0){ $pageNumber = 1; } $command = implode(" ", $args); }else{ $command = implode(" ", $args); $pageNumber = 1; } if($sender instanceof ConsoleCommandSender){ $pageHeight = PHP_INT_MAX; }else{ $pageHeight = 7; } if($command === ""){ /** @var Command[][] $commands */ $commands = []; foreach($sender->getServer()->getCommandMap()->getCommands() as $command){ if($command->testPermissionSilent($sender)){ $commands[$command->getName()] = $command; } } ksort($commands, SORT_NATURAL | SORT_FLAG_CASE); $commands = array_chunk($commands, $pageHeight); $pageNumber = (int) min(count($commands), $pageNumber); if($pageNumber < 1){ $pageNumber = 1; } $sender->sendMessage(new TranslationContainer("commands.help.header", [$pageNumber, count($commands)])); if(isset($commands[$pageNumber - 1])){ foreach($commands[$pageNumber - 1] as $command){ $sender->sendMessage(TextFormat::DARK_GREEN . "/" . $command->getName() . ": " . TextFormat::WHITE . $command->getDescription()); } } return true; }else{ if(($cmd = $sender->getServer()->getCommandMap()->getCommand(strtolower($command))) instanceof Command){ if($cmd->testPermissionSilent($sender)){ $message = TextFormat::YELLOW . "--------- " . TextFormat::WHITE . " Help: /" . $cmd->getName() . TextFormat::YELLOW . " ---------\n"; $message .= TextFormat::GOLD . "Description: " . TextFormat::WHITE . $cmd->getDescription() . "\n"; $message .= TextFormat::GOLD . "Usage: " . TextFormat::WHITE . implode("\n" . TextFormat::WHITE, explode("\n", $cmd->getUsage())) . "\n"; $sender->sendMessage($message); return true; } } $sender->sendMessage(TextFormat::RED . "No help for " . strtolower($command)); return true; } } } ================================================ FILE: src/pocketmine/command/defaults/KickCommand.php ================================================ setPermission("pocketmine.command.kick"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) === 0){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $name = array_shift($args); $reason = trim(implode(" ", $args)); if(($player = $sender->getServer()->getPlayer($name)) instanceof Player){ $player->kick($reason); if(strlen($reason) >= 1){ Command::broadcastCommandMessage($sender, new TranslationContainer("commands.kick.success.reason", [$player->getName(), $reason])); }else{ Command::broadcastCommandMessage($sender, new TranslationContainer("commands.kick.success", [$player->getName()])); } }else{ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.player.notFound")); } return true; } } ================================================ FILE: src/pocketmine/command/defaults/KillCommand.php ================================================ setPermission("pocketmine.command.kill.self;pocketmine.command.kill.other"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) >= 2){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } if(count($args) === 1){ if(!$sender->hasPermission("pocketmine.command.kill.other")){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission")); return true; } $player = $sender->getServer()->getPlayer($args[0]); if($player instanceof Player){ $sender->getServer()->getPluginManager()->callEvent($ev = new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, 1000)); if($ev->isCancelled()){ return true; } $player->setLastDamageCause($ev); $player->setHealth(0); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.kill.successful", [$player->getName()])); }else{ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.player.notFound")); } return true; } if($sender instanceof Player){ if(!$sender->hasPermission("pocketmine.command.kill.self")){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission")); return true; } $sender->getServer()->getPluginManager()->callEvent($ev = new EntityDamageEvent($sender, EntityDamageEvent::CAUSE_SUICIDE, 1000)); if($ev->isCancelled()){ return true; } $sender->setLastDamageCause($ev); $sender->setHealth(0); $sender->sendMessage(new TranslationContainer("commands.kill.successful", [$sender->getName()])); }else{ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } return true; } } ================================================ FILE: src/pocketmine/command/defaults/ListCommand.php ================================================ setPermission("pocketmine.command.list"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } $online = ""; $onlineCount = 0; foreach($sender->getServer()->getOnlinePlayers() as $player){ if($player->isOnline() and (!($sender instanceof Player) or $sender->canSee($player))){ $online .= $player->getDisplayName() . ", "; ++$onlineCount; } } $sender->sendMessage(new TranslationContainer("commands.players.list", [$onlineCount, $sender->getServer()->getMaxPlayers()])); $sender->sendMessage(substr($online, 0, -2)); return true; } } ================================================ FILE: src/pocketmine/command/defaults/MeCommand.php ================================================ setPermission("pocketmine.command.me"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) === 0){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $sender->getServer()->broadcastMessage(new TranslationContainer("chat.type.emote", [$sender instanceof Player ? $sender->getDisplayName() : $sender->getName(), TextFormat::WHITE . implode(" ", $args)])); return true; } } ================================================ FILE: src/pocketmine/command/defaults/OpCommand.php ================================================ setPermission("pocketmine.command.op.give"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) === 0){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $name = array_shift($args); $player = $sender->getServer()->getOfflinePlayer($name); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.op.success", [$player->getName()])); if($player instanceof Player){ $player->sendMessage(TextFormat::GRAY . "You are now op!"); } $player->setOp(true); return true; } } ================================================ FILE: src/pocketmine/command/defaults/PardonCidCommand.php ================================================ setPermission("pocketmine.command.pardoncid"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) !== 1){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $sender->getServer()->getCIDBans()->remove($args[0]); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.unbancid.success", [$args[0]])); return true; } } ================================================ FILE: src/pocketmine/command/defaults/PardonCommand.php ================================================ setPermission("pocketmine.command.unban.player"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) !== 1){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $sender->getServer()->getNameBans()->remove($args[0]); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.unban.success", [$args[0]])); return true; } } ================================================ FILE: src/pocketmine/command/defaults/PardonIpCommand.php ================================================ setPermission("pocketmine.command.unban.ip"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) !== 1){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } if(preg_match("/^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])$/", $args[0])){ $sender->getServer()->getIPBans()->remove($args[0]); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.unbanip.success", [$args[0]])); $sender->getServer()->getNetwork()->unblockAddress($args[0]); }else{ $sender->sendMessage(new TranslationContainer("commands.unbanip.invalid")); } return true; } } ================================================ FILE: src/pocketmine/command/defaults/ParticleCommand.php ================================================ setPermission("pocketmine.command.particle"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) < 7){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return true; } if($sender instanceof Player){ $level = $sender->getLevel(); }else{ $level = $sender->getServer()->getDefaultLevel(); } $name = strtolower($args[0]); $pos = new Vector3((float) $args[1], (float) $args[2], (float) $args[3]); $xd = (float) $args[4]; $yd = (float) $args[5]; $zd = (float) $args[6]; $count = isset($args[7]) ? max(1, (int) $args[7]) : 1; $data = isset($args[8]) ? (int) $args[8] : null; $particle = $this->getParticle($name, $pos, $xd, $yd, $zd, $data); if($particle === null){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.particle.notFound", [$name])); return true; } $sender->sendMessage(new TranslationContainer("commands.particle.success", [$name, $count])); $random = new Random((int) (microtime(true) * 1000) + mt_rand()); for($i = 0; $i < $count; ++$i){ $particle->setComponents( $pos->x + $random->nextSignedFloat() * $xd, $pos->y + $random->nextSignedFloat() * $yd, $pos->z + $random->nextSignedFloat() * $zd ); $level->addParticle($particle); } return true; } /** * @param $name * @param Vector3 $pos * @param $xd * @param $yd * @param $zd * @param $data * @return null|DustParticle|ItemBreakParticle|TerrainParticle */ private function getParticle($name, Vector3 $pos, $xd, $yd, $zd, $data){ switch($name){ case "explode": return new ExplodeParticle($pos); case "hugeexplosion": return new HugeExplodeParticle($pos); case "hugeexplosionseed": return new HugeExplodeSeedParticle($pos); case "bubble": return new BubbleParticle($pos); case "splash": return new SplashParticle($pos); case "wake": case "water": return new WaterParticle($pos); case "crit": return new CriticalParticle($pos); case "smoke": return new SmokeParticle($pos, $data ?? 0); case "spell": return new EnchantParticle($pos); case "instantspell": return new InstantEnchantParticle($pos); case "dripwater": return new WaterDripParticle($pos); case "driplava": return new LavaDripParticle($pos); case "townaura": case "spore": return new SporeParticle($pos); case "portal": return new PortalParticle($pos); case "flame": return new FlameParticle($pos); case "lava": return new LavaParticle($pos); case "reddust": return new RedstoneParticle($pos, $data ?? 1); case "snowballpoof": return new ItemBreakParticle($pos, Item::get(Item::SNOWBALL)); case "slime": return new ItemBreakParticle($pos, Item::get(Item::SLIMEBALL)); case "itembreak": if($data !== null and $data !== 0){ return new ItemBreakParticle($pos, $data); } break; case "terrain": if($data !== null and $data !== 0){ return new TerrainParticle($pos, $data); } break; case "heart": return new HeartParticle($pos, $data ?? 0); case "ink": return new InkParticle($pos, $data ?? 0); case "droplet": return new RainSplashParticle($pos); case "enchantmenttable": return new EnchantmentTableParticle($pos); case "happyvillager": return new HappyVillagerParticle($pos); case "angryvillager": return new AngryVillagerParticle($pos); case "forcefield": return new BlockForceFieldParticle($pos, $data ?? 0); } if(substr($name, 0, 10) === "iconcrack_"){ $d = explode("_", $name); if(count($d) === 3){ return new ItemBreakParticle($pos, Item::get((int) $d[1], (int) $d[2])); } }elseif(substr($name, 0, 11) === "blockcrack_"){ $d = explode("_", $name); if(count($d) === 2){ return new TerrainParticle($pos, Block::get($d[1] & 0xff, $d[1] >> 12)); } }elseif(substr($name, 0, 10) === "blockdust_"){ $d = explode("_", $name); if(count($d) >= 4){ return new DustParticle($pos, $d[1] & 0xff, $d[2] & 0xff, $d[3] & 0xff, isset($d[4]) ? $d[4] & 0xff : 255); } } return null; } } ================================================ FILE: src/pocketmine/command/defaults/PluginsCommand.php ================================================ setPermission("pocketmine.command.plugins"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } $this->sendPluginList($sender); return true; } private function sendPluginList(CommandSender $sender){ $list = ""; foreach(($plugins = $sender->getServer()->getPluginManager()->getPlugins()) as $plugin){ if(strlen($list) > 0){ $list .= TextFormat::WHITE . ", "; } $list .= $plugin->isEnabled() ? TextFormat::GREEN : TextFormat::RED; $list .= $plugin->getDescription()->getFullName(); } $sender->sendMessage(new TranslationContainer("pocketmine.command.plugins.success", [count($plugins), $list])); } } ================================================ FILE: src/pocketmine/command/defaults/ReloadCommand.php ================================================ setPermission("pocketmine.command.reload"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } Command::broadcastCommandMessage($sender, new TranslationContainer(TextFormat::YELLOW . "%pocketmine.command.reload.reloading")); $sender->getServer()->reload(); Command::broadcastCommandMessage($sender, new TranslationContainer(TextFormat::YELLOW . "%pocketmine.command.reload.reloaded")); return true; } } ================================================ FILE: src/pocketmine/command/defaults/SaveCommand.php ================================================ setPermission("pocketmine.command.save.perform"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } Command::broadcastCommandMessage($sender, new TranslationContainer("commands.save.start")); foreach($sender->getServer()->getOnlinePlayers() as $player){ $player->save(); } foreach($sender->getServer()->getLevels() as $level){ $level->save(true); } Command::broadcastCommandMessage($sender, new TranslationContainer("commands.save.success")); return true; } } ================================================ FILE: src/pocketmine/command/defaults/SaveOffCommand.php ================================================ setPermission("pocketmine.command.save.disable"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } $sender->getServer()->setAutoSave(false); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.save.disabled")); return true; } } ================================================ FILE: src/pocketmine/command/defaults/SaveOnCommand.php ================================================ setPermission("pocketmine.command.save.enable"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } $sender->getServer()->setAutoSave(true); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.save.enabled")); return true; } } ================================================ FILE: src/pocketmine/command/defaults/SayCommand.php ================================================ setPermission("pocketmine.command.say"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) === 0){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $sender->getServer()->broadcastMessage(new TranslationContainer(TextFormat::LIGHT_PURPLE . "%chat.type.announcement", [$sender instanceof Player ? $sender->getDisplayName() : ($sender instanceof ConsoleCommandSender ? "Server" : $sender->getName()), TextFormat::LIGHT_PURPLE . implode(" ", $args)])); return true; } } ================================================ FILE: src/pocketmine/command/defaults/SeedCommand.php ================================================ setPermission("pocketmine.command.seed"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if($sender instanceof Player){ $seed = $sender->getLevel()->getSeed(); }else{ $seed = $sender->getServer()->getDefaultLevel()->getSeed(); } $sender->sendMessage(new TranslationContainer("commands.seed.success", [$seed])); return true; } } ================================================ FILE: src/pocketmine/command/defaults/SetBlockCommand.php ================================================ setPermission("pocketmine.command.setblock"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) < 4 or count($args) > 5){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $itemblock = Item::fromString($args[3]); if($itemblock instanceof ItemBlock){ $block = $itemblock->getBlock(); if(isset($args[4]) and is_numeric($args[4])) $block->setDamage((int)$args[4]); $x = $args[0]; $y = $args[1]; $z = $args[2]; if($x{0} === "~"){ if((is_numeric(trim($x, "~")) or trim($x, "~") === "") and ($sender instanceof Player)) $x = (int)round(trim($x, "~") + $sender->x); }elseif(is_numeric($x)){ $x = (int)round($x); }else{ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } if($y{0} === "~"){ if((is_numeric(trim($y, "~")) or trim($y, "~") === "") and ($sender instanceof Player)) $y = (int)round(trim($y, "~") + $sender->y); if($y < 0 or $y > 256) return false; }elseif(is_numeric($y)){ $y = (int)round($y); }else{ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } if($z{0} === "~"){ if((is_numeric(trim($z, "~")) or trim($z, "~") === "") and ($sender instanceof Player)) $z = (int)round(trim($z, "~") + $sender->z); }elseif(is_numeric($z)){ $z = (int)round($z); }else{ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } if(!(is_integer($x) and is_integer($y) and is_integer($z))){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $pos = new Vector3($x, $y, $z); if($pos instanceof Vector3){ $level = ($sender instanceof Player) ? $sender->getLevel() : $sender->getServer()->getDefaultLevel(); if($level->setBlock($pos, $block)){ $sender->sendMessage("Successfully set the block at ($x, $y, $z) to block $args[3]"); return true; }else{ $sender->sendMessage(TextFormat::RED . new TranslationContainer("commands.generic.exception", [])); return false; } } }else{ $sender->sendMessage(TextFormat::RED . new TranslationContainer("command.setblock.invalidBlock", [])); return false; } return true; } } ================================================ FILE: src/pocketmine/command/defaults/SetWorldSpawnCommand.php ================================================ setPermission("pocketmine.command.setworldspawn"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) === 0){ if($sender instanceof Player){ $level = $sender->getLevel(); $pos = (new Vector3($sender->x, $sender->y, $sender->z))->round(); }else{ $sender->sendMessage(TextFormat::RED . "You can only perform this command as a player"); return true; } }elseif(count($args) === 3){ $level = $sender->getServer()->getDefaultLevel(); $pos = new Vector3($this->getInteger($sender, $args[0]), $this->getInteger($sender, $args[1]), $this->getInteger($sender, $args[2])); }else{ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return true; } $level->setSpawnLocation($pos); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.setworldspawn.success", [round($pos->x, 2), round($pos->y, 2), round($pos->z, 2)])); return true; } } ================================================ FILE: src/pocketmine/command/defaults/SpawnpointCommand.php ================================================ setPermission("pocketmine.command.spawnpoint"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } $target = null; if(count($args) === 0){ if($sender instanceof Player){ $target = $sender; }else{ $sender->sendMessage(TextFormat::RED . "Please provide a player!"); return true; } }else{ $target = $sender->getServer()->getPlayer($args[0]); if($target === null){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.player.notFound")); return true; } } $level = $target->getLevel(); if(count($args) === 4){ if($level !== null){ $pos = $sender instanceof Player ? $sender->getPosition() : $level->getSpawnLocation(); $x = (int) $this->getRelativeDouble($pos->x, $sender, $args[1]); $y = $this->getRelativeDouble($pos->y, $sender, $args[2], 0, 128); $z = $this->getRelativeDouble($pos->z, $sender, $args[3]); $target->setSpawn(new Position($x, $y, $z, $level)); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.spawnpoint.success", [$target->getName(), round($x, 2), round($y, 2), round($z, 2)])); return true; } }elseif(count($args) <= 1){ if($sender instanceof Player){ $pos = new Position((int) $sender->x, (int) $sender->y, (int) $sender->z, $sender->getLevel()); $target->setSpawn($pos); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.spawnpoint.success", [$target->getName(), round($pos->x, 2), round($pos->y, 2), round($pos->z, 2)])); return true; }else{ $sender->sendMessage(TextFormat::RED . "Please provide a player!"); return true; } } $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return true; } } ================================================ FILE: src/pocketmine/command/defaults/StatusCommand.php ================================================ setPermission("pocketmine.command.status"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } $mUsage = Utils::getMemoryUsage(true); $rUsage = Utils::getRealMemoryUsage(); $server = $sender->getServer(); $onlineCount = 0; foreach($sender->getServer()->getOnlinePlayers() as $player){ if($player->isOnline() and (!($sender instanceof Player) or $sender->canSee($player))){ ++$onlineCount; } } $sender->sendMessage(TextFormat::GREEN . "---- " . TextFormat::WHITE . "%pocketmine.command.status.title" . TextFormat::GREEN . " ----"); $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.status.player" . TextFormat::GREEN ." ". $onlineCount . "/" . $sender->getServer()->getMaxPlayers()); $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.status.uptime " . TextFormat::RED . $sender->getServer()->getUptime()); $tpsColor = TextFormat::GREEN; if($server->getTicksPerSecondAverage() < 10){ $tpsColor = TextFormat::GOLD; }elseif($server->getTicksPerSecondAverage() < 1){ $tpsColor = TextFormat::RED; } $tpsColour = TextFormat::GREEN; if($server->getTicksPerSecond() < 10){ $tpsColour = TextFormat::GOLD; }elseif($server->getTicksPerSecond() < 1){ $tpsColour = TextFormat::RED; } $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.status.AverageTPS " . $tpsColor . $server->getTicksPerSecondAverage() . " (" . $server->getTickUsageAverage() . "%)"); $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.status.CurrentTPS " . $tpsColour . $server->getTicksPerSecond() . " (" . $server->getTickUsage() . "%)"); $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.status.Networkupload " . TextFormat::RED . \round($server->getNetwork()->getUpload() / 1024, 2) . " kB/s"); $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.status.Networkdownload " . TextFormat::RED . \round($server->getNetwork()->getDownload() / 1024, 2) . " kB/s"); $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.status.Threadcount " . TextFormat::RED . Utils::getThreadCount()); $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.status.Mainmemory " . TextFormat::RED . number_format(round(($mUsage[0] / 1024) / 1024, 2)) . " MB."); $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.status.Totalmemory " . TextFormat::RED . number_format(round(($mUsage[1] / 1024) / 1024, 2)) . " MB."); $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.status.Totalvirtualmemory " . TextFormat::RED . number_format(round(($mUsage[2] / 1024) / 1024, 2)) . " MB."); $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.status.Heapmemory " . TextFormat::RED . number_format(round(($rUsage[0] / 1024) / 1024, 2)) . " MB."); $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.status.Maxmemorysystem " . TextFormat::RED . number_format(round(($mUsage[2] / 1024) / 1024, 2)) . " MB."); if($server->getProperty("memory.global-limit") > 0){ $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.status.Maxmemorymanager " . TextFormat::RED . number_format(round($server->getProperty("memory.global-limit"), 2)) . " MB."); } foreach($server->getLevels() as $level){ $sender->sendMessage(TextFormat::GOLD . "%pocketmine.command.status.World \"" . $level->getFolderName() . "\"" . ($level->getFolderName() !== $level->getName() ? " (" . $level->getName() . ")" : "") . ": " . TextFormat::RED . number_format(count($level->getChunks())) . TextFormat::GREEN . " %pocketmine.command.status.chunks " . TextFormat::RED . number_format(count($level->getEntities())) . TextFormat::GREEN . " %pocketmine.command.status.entities " . TextFormat::RED . number_format(count($level->getTiles())) . TextFormat::GREEN . " %pocketmine.command.status.tiles " . "%pocketmine.command.status.Time " . (($level->getTickRate() > 1 or $level->getTickRateTime() > 40) ? TextFormat::RED : TextFormat::YELLOW) . round($level->getTickRateTime(), 2) . "%pocketmine.command.status.ms" . ($level->getTickRate() > 1 ? " (tick rate " . $level->getTickRate() . ")" : "") ); } return true; } } ================================================ FILE: src/pocketmine/command/defaults/StopCommand.php ================================================ setPermission("pocketmine.command.stop"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } $restart = false; if(isset($args[0])){ if($args[0] == 'force'){ $restart = true; array_shift($args); }else{ $restart = false; } } Command::broadcastCommandMessage($sender, new TranslationContainer("commands.stop.start")); $msg = implode(" ", $args); $sender->getServer()->shutdown($restart, $msg); return true; } } ================================================ FILE: src/pocketmine/command/defaults/SummonCommand.php ================================================ setPermission("pocketmine.command.summon"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) != 1 and count($args) != 4 and count($args) != 5){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return true; } $x = 0; $y = 0; $z = 0; if(count($args) == 4 or count($args) == 5){ //position is set //TODO:simpilify them to one piece of code //Code for setting $x if(is_numeric($args[1])){ //x is given directly $x = $args[1]; }elseif(strcmp($args[1], "~") >= 0){ //x is given with a "~" $offset_x = trim($args[1], "~"); if($sender instanceof Player){ //using in-game $x = is_numeric($offset_x) ? ($sender->x + $offset_x) : $sender->x; }else{ //using in console $sender->sendMessage(TextFormat::RED . "You must specify a position where the entity is spawned to when using in console"); return false; } }else{ //other circumstances $sender->sendMessage(TextFormat::RED . "Argument error"); return false; } //Code for setting $y if(is_numeric($args[2])){ //y is given directly $y = $args[2]; }elseif(strcmp($args[2], "~") >= 0){ //y is given with a "~" $offset_y = trim($args[2], "~"); if($sender instanceof Player){ //using in-game $y = is_numeric($offset_y) ? ($sender->y + $offset_y) : $sender->y; $y = min(128, max(0, $y)); }else{ //using in console $sender->sendMessage(TextFormat::RED . "You must specify a position where the entity is spawned to when using in console"); return false; } }else{ //other circumstances $sender->sendMessage(TextFormat::RED . "Argument error"); return false; } //Code for setting $z if(is_numeric($args[3])){ //z is given directly $z = $args[3]; }elseif(strcmp($args[3], "~") >= 0){ //z is given with a "~" $offset_z = trim($args[3], "~"); if($sender instanceof Player){ //using in-game $z = is_numeric($offset_z) ? ($sender->z + $offset_z) : $sender->z; }else{ //using in console $sender->sendMessage(TextFormat::RED . "You must specify a position where the entity is spawned to when using in console"); return false; } }else{ //other circumstances $sender->sendMessage(TextFormat::RED . "Argument error"); return false; } } //finish setting the location if(count($args) == 1){ if($sender instanceof Player){ $x = $sender->x; $y = $sender->y; $z = $sender->z; }else{ $sender->sendMessage(TextFormat::RED . "You must specify a position where the entity is spawned to when using in console"); return false; } } //finish setting the location $entity = null; $type = $args[0]; $level = ($sender instanceof Player) ? $sender->getLevel() : $sender->getServer()->getDefaultLevel(); $chunk = $level->getChunk($x >> 4, $z >> 4, true); $nbt = new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $x), new DoubleTag("", $y), new DoubleTag("", $z) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", 0), new DoubleTag("", 0), new DoubleTag("", 0) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", lcg_value() * 360), new FloatTag("", 0) ]), ]); if(count($args) == 5 and $args[4]{0} == "{"){//Tags are found $nbtExtra = NBT::parseJSON($args[4]); $nbt = NBT::combineCompoundTags($nbt, $nbtExtra, true); } $entity = Entity::createEntity($type, $chunk, $nbt); if($entity instanceof Entity){ $entity->spawnToAll(); $sender->sendMessage("Successfully spawned entity $type at ($x, $y, $z)"); return true; }else{ $sender->sendMessage(TextFormat::RED . "An error occurred when spawning the entity $type"); return false; } } } ================================================ FILE: src/pocketmine/command/defaults/TeleportCommand.php ================================================ setPermission("pocketmine.command.teleport"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) < 1 or count($args) > 6){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return true; } $target = null; $origin = $sender; if(count($args) === 1 or count($args) === 3){ if($sender instanceof Player){ $target = $sender; }else{ $sender->sendMessage(TextFormat::RED . "Please provide a player!"); return true; } if(count($args) === 1){ $target = $sender->getServer()->getPlayer($args[0]); if($target === null){ $sender->sendMessage(TextFormat::RED . "Can't find player " . $args[0]); return true; } } }else{ $target = $sender->getServer()->getPlayer($args[0]); if($target === null){ $sender->sendMessage(TextFormat::RED . "Can't find player " . $args[0]); return true; } if(count($args) === 2){ $origin = $target; $target = $sender->getServer()->getPlayer($args[1]); if($target === null){ $sender->sendMessage(TextFormat::RED . "Can't find player " . $args[1]); return true; } } } if(count($args) < 3){ $origin->teleport($target); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.tp.success", [$origin->getName(), $target->getName()])); return true; }elseif($target->getLevel() !== null){ if(count($args) === 4 or count($args) === 6){ $pos = 1; }else{ $pos = 0; } $x = $this->getRelativeDouble($target->x, $sender, $args[$pos++]); $y = $this->getRelativeDouble($target->y, $sender, $args[$pos++], 0, 256); $z = $this->getRelativeDouble($target->z, $sender, $args[$pos++]); $yaw = $target->getYaw(); $pitch = $target->getPitch(); if(count($args) === 6 or (count($args) === 5 and $pos === 3)){ $yaw = $args[$pos++]; $pitch = $args[$pos++]; } $target->teleport(new Vector3($x, $y, $z), $yaw, $pitch); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.tp.success.coordinates", [$target->getName(), round($x, 2), round($y, 2), round($z, 2)])); return true; } $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return true; } } ================================================ FILE: src/pocketmine/command/defaults/TellCommand.php ================================================ setPermission("pocketmine.command.tell"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) < 2){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $name = strtolower(array_shift($args)); $player = $sender->getServer()->getPlayer($name); if($player === $sender){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.message.sameTarget")); return true; } if($player instanceof Player){ $sender->sendMessage("[".$sender->getName()." -> " . $player->getDisplayName() . "] " . implode(" ", $args)); $player->sendMessage("[" . ($sender instanceof Player ? $sender->getDisplayName() : $sender->getName()) . " -> ".$player->getName()."] " . implode(" ", $args)); }else{ $sender->sendMessage(new TranslationContainer("commands.generic.player.notFound")); } return true; } } ================================================ FILE: src/pocketmine/command/defaults/TimeCommand.php ================================================ setPermission("pocketmine.command.time.add;pocketmine.command.time.set;pocketmine.command.time.start;pocketmine.command.time.stop"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(count($args) < 1){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } if($args[0] === "start"){ if(!$sender->hasPermission("pocketmine.command.time.start")){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission")); return true; } foreach($sender->getServer()->getLevels() as $level){ $level->checkTime(); $level->startTime(); $level->checkTime(); } Command::broadcastCommandMessage($sender, "Restarted the time"); return true; }elseif($args[0] === "stop"){ if(!$sender->hasPermission("pocketmine.command.time.stop")){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission")); return true; } foreach($sender->getServer()->getLevels() as $level){ $level->checkTime(); $level->stopTime(); $level->checkTime(); } Command::broadcastCommandMessage($sender, "Stopped the time"); return true; }elseif($args[0] === "query"){ if(!$sender->hasPermission("pocketmine.command.time.query")){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission")); return true; } if($sender instanceof Player){ $level = $sender->getLevel(); }else{ $level = $sender->getServer()->getDefaultLevel(); } $sender->sendMessage(new TranslationContainer("commands.time.query", [$level->getTime()])); return true; } if(count($args) < 2){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } if($args[0] === "set"){ if(!$sender->hasPermission("pocketmine.command.time.set")){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission")); return true; } if($args[1] === "day"){ $value = 0; }elseif($args[1] === "night"){ $value = Level::TIME_NIGHT; }else{ $value = $this->getInteger($sender, $args[1], 0); } foreach($sender->getServer()->getLevels() as $level){ $level->checkTime(); $level->setTime($value); $level->checkTime(); } Command::broadcastCommandMessage($sender, new TranslationContainer("commands.time.set", [$value])); }elseif($args[0] === "add"){ if(!$sender->hasPermission("pocketmine.command.time.add")){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission")); return true; } $value = $this->getInteger($sender, $args[1], 0); foreach($sender->getServer()->getLevels() as $level){ $level->checkTime(); $level->setTime($level->getTime() + $value); $level->checkTime(); } Command::broadcastCommandMessage($sender, new TranslationContainer("commands.time.added", [$value])); }else{ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); } return true; } } ================================================ FILE: src/pocketmine/command/defaults/TimingsCommand.php ================================================ setPermission("pocketmine.command.timings"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) !== 1){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return true; } $mode = strtolower($args[0]); if($mode === "on"){ $sender->getServer()->getPluginManager()->setUseTimings(true); TimingsHandler::reload(); $sender->sendMessage(new TranslationContainer("pocketmine.command.timings.enable")); return true; }elseif($mode === "off"){ $sender->getServer()->getPluginManager()->setUseTimings(false); $sender->sendMessage(new TranslationContainer("pocketmine.command.timings.disable")); return true; } if(!$sender->getServer()->getPluginManager()->useTimings()){ $sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsDisabled")); return true; } $paste = $mode === "paste"; if($mode === "reset"){ TimingsHandler::reload(); $sender->sendMessage(new TranslationContainer("pocketmine.command.timings.reset")); }elseif($mode === "merged" or $mode === "report" or $paste){ $sampleTime = microtime(true) - self::$timingStart; $index = 0; $timingFolder = $sender->getServer()->getDataPath() . "timings/"; if(!file_exists($timingFolder)){ mkdir($timingFolder, 0777); } $timings = $timingFolder . "timings.txt"; while(file_exists($timings)){ $timings = $timingFolder . "timings" . (++$index) . ".txt"; } $fileTimings = $paste ? fopen("php://temp", "r+b") : fopen($timings, "a+b"); TimingsHandler::printTimings($fileTimings); fwrite($fileTimings, "Sample time " . round($sampleTime * 1000000000) . " (" . $sampleTime . "s)" . PHP_EOL); if($paste){ fseek($fileTimings, 0); $data = [ "syntax" => "text", "poster" => $sender->getServer()->getName(), "content" => stream_get_contents($fileTimings) ]; $ch = curl_init("http://paste.ubuntu.com/"); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_FORBID_REUSE, 1); curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_AUTOREFERER, false); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, ["User-Agent: " . $this->getName() . " " . $sender->getServer()->getPocketMineVersion()]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $data = curl_exec($ch); curl_close($ch); if(preg_match('#^Location: http://paste\\.ubuntu\\.com/([0-9]{1,})/#m', $data, $matches) == 0){ $sender->sendMessage(new TranslationContainer("pocketmine.command.timings.pasteError")); return true; } $sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsUpload", ["http://paste.ubuntu.com/" . $matches[1] . "/"])); $sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsRead", ["http://timings.aikar.co/?url=" . $matches[1]])); fclose($fileTimings); }else{ fclose($fileTimings); $sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsWrite", [$timings])); } } return true; } } ================================================ FILE: src/pocketmine/command/defaults/TransferCommand.php ================================================ setPermission("pocketmine.command.transfer"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(!$sender instanceof Player){ $sender->sendMessage("Run command in-game"); return; } if(!isset($args[0]) || !isset($args[1])){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $sender->sendMessage("Transferring you to $args[0]".":".$args[1]); $pk = new TransferPacket(); $pk->address = $args[0]; $pk->port = $args[1]; $sender->dataPacket($pk); } } ================================================ FILE: src/pocketmine/command/defaults/VanillaCommand.php ================================================ $max){ $i = $max; } return $i; } protected function getRelativeDouble($original, CommandSender $sender, $input, $min = self::MIN_COORD, $max = self::MAX_COORD){ if($input{0} === "~"){ $value = $this->getDouble($sender, substr($input, 1)); return $original + $value; } return $this->getDouble($sender, $input, $min, $max); } protected function getDouble(CommandSender $sender, $value, $min = self::MIN_COORD, $max = self::MAX_COORD){ $i = (double) $value; if($i < $min){ $i = $min; }elseif($i > $max){ $i = $max; } return $i; } } ================================================ FILE: src/pocketmine/command/defaults/VersionCommand.php ================================================ setPermission("pocketmine.command.version"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return \true; } if(\count($args) === 0){ $sender->sendMessage(new TranslationContainer("pocketmine.server.info.extended.title")); $sender->sendMessage(new TranslationContainer("pocketmine.server.info.extended1", [ $sender->getServer()->getName(), $sender->getServer()->getFormattedVersion("-"), $sender->getServer()->getCodename() ])); $sender->sendMessage(new TranslationContainer("pocketmine.server.info.extended2", [ phpversion() ])); $sender->sendMessage(new TranslationContainer("pocketmine.server.info.extended3", [ $sender->getServer()->getApiVersion() ])); $sender->sendMessage(new TranslationContainer("pocketmine.server.info.extended4", [ $sender->getServer()->getVersion() ])); $sender->sendMessage(new TranslationContainer("pocketmine.server.info.extended5", [ Info::CURRENT_PROTOCOL ])); }else{ $pluginName = \implode(" ", $args); $exactPlugin = $sender->getServer()->getPluginManager()->getPlugin($pluginName); if($exactPlugin instanceof Plugin){ $this->describeToSender($exactPlugin, $sender); return \true; } $found = \false; $pluginName = \strtolower($pluginName); foreach($sender->getServer()->getPluginManager()->getPlugins() as $plugin){ if(\stripos($plugin->getName(), $pluginName) !== \false){ $this->describeToSender($plugin, $sender); $found = \true; } } if(!$found){ $sender->sendMessage(new TranslationContainer("pocketmine.command.version.noSuchPlugin")); } } return \true; } private function describeToSender(Plugin $plugin, CommandSender $sender){ $desc = $plugin->getDescription(); $sender->sendMessage(TextFormat::DARK_GREEN . $desc->getName() . TextFormat::WHITE . " version " . TextFormat::DARK_GREEN . $desc->getVersion()); if($desc->getDescription() != \null){ $sender->sendMessage($desc->getDescription()); } if($desc->getWebsite() != \null){ $sender->sendMessage("Website: " . $desc->getWebsite()); } if(\count($authors = $desc->getAuthors()) > 0){ if(\count($authors) === 1){ $sender->sendMessage("Author: " . \implode(", ", $authors)); }else{ $sender->sendMessage("Authors: " . \implode(", ", $authors)); } } } } ================================================ FILE: src/pocketmine/command/defaults/WeatherCommand.php ================================================ setPermission("pocketmine.command.weather"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) < 1){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } if($sender instanceof Player){ $wea = Weather::getWeatherFromString($args[0]); if(!isset($args[1])) $duration = mt_rand(min($sender->getServer()->weatherRandomDurationMin, $sender->getServer()->weatherRandomDurationMax), max($sender->getServer()->weatherRandomDurationMin, $sender->getServer()->weatherRandomDurationMax)); else $duration = (int) $args[1]; if($wea >= 0 and $wea <= 3){ $sender->getLevel()->getWeather()->setWeather($wea, $duration); $sender->sendMessage(new TranslationContainer("pocketmine.command.weather.changed", [$sender->getLevel()->getFolderName()])); return true; /*if(WeatherManager::isRegistered($sender->getLevel())){ $sender->getLevel()->getWeather()->setWeather($wea, $duration); $sender->sendMessage(new TranslationContainer("pocketmine.command.weather.changed", [$sender->getLevel()->getFolderName()])); return true; }else{ $sender->sendMessage(new TranslationContainer("pocketmine.command.weather.noregistered", [$sender->getLevel()->getFolderName()])); return false; }*/ }else{ $sender->sendMessage(TextFormat::RED . "%pocketmine.command.weather.invalid"); return false; } } if(count($args) < 2){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } $level = $sender->getServer()->getLevelByName($args[0]); if(!$level instanceof Level){ $sender->sendMessage(TextFormat::RED . "%pocketmine.command.weather.invalid.level"); return false; } $wea = Weather::getWeatherFromString($args[1]); if(!isset($args[1])) $duration = mt_rand(min($sender->getServer()->weatherRandomDurationMin, $sender->getServer()->weatherRandomDurationMax), max($sender->getServer()->weatherRandomDurationMin, $sender->getServer()->weatherRandomDurationMax)); else $duration = (int) $args[1]; if($wea >= 0 and $wea <= 3){ $level->getWeather()->setWeather($wea, $duration); $sender->sendMessage(new TranslationContainer("pocketmine.command.weather.changed", [$level->getFolderName()])); return true; /*if(WeatherManager::isRegistered($level)){ $level->getWeather()->setWeather($wea, $duration); $sender->sendMessage(new TranslationContainer("pocketmine.command.weather.changed", [$level->getFolderName()])); return true; }else{ $sender->sendMessage(new TranslationContainer("pocketmine.command.weather.noregistered", [$level->getFolderName()])); return false; }*/ }else{ $sender->sendMessage(TextFormat::RED . "%pocketmine.command.weather.invalid"); return false; } } } ================================================ FILE: src/pocketmine/command/defaults/WhitelistCommand.php ================================================ setPermission("pocketmine.command.whitelist.reload;pocketmine.command.whitelist.enable;pocketmine.command.whitelist.disable;pocketmine.command.whitelist.list;pocketmine.command.whitelist.add;pocketmine.command.whitelist.remove"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) === 0 or count($args) > 2){ $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return true; } if(count($args) === 1){ if($this->badPerm($sender, strtolower($args[0]))){ return false; } switch(strtolower($args[0])){ case "reload": $sender->getServer()->reloadWhitelist(); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.whitelist.reloaded")); return true; case "on": $sender->getServer()->setConfigBool("white-list", true); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.whitelist.enabled")); return true; case "off": $sender->getServer()->setConfigBool("white-list", false); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.whitelist.disabled")); return true; case "list": $result = ""; $count = 0; foreach($sender->getServer()->getWhitelisted()->getAll(true) as $player){ $result .= $player . ", "; ++$count; } $sender->sendMessage(new TranslationContainer("commands.whitelist.list", [$count, $count])); $sender->sendMessage(substr($result, 0, -2)); return true; case "add": $sender->sendMessage(new TranslationContainer("commands.generic.usage", ["%commands.whitelist.add.usage"])); return true; case "remove": $sender->sendMessage(new TranslationContainer("commands.generic.usage", ["%commands.whitelist.remove.usage"])); return true; } }elseif(count($args) === 2){ if($this->badPerm($sender, strtolower($args[0]))){ return false; } switch(strtolower($args[0])){ case "add": $sender->getServer()->getOfflinePlayer($args[1])->setWhitelisted(true); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.whitelist.add.success", [$args[1]])); return true; case "remove": $sender->getServer()->getOfflinePlayer($args[1])->setWhitelisted(false); Command::broadcastCommandMessage($sender, new TranslationContainer("commands.whitelist.remove.success", [$args[1]])); return true; } } return true; } private function badPerm(CommandSender $sender, $perm){ if(!$sender->hasPermission("pocketmine.command.whitelist.$perm")){ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission")); return true; } return false; } } ================================================ FILE: src/pocketmine/command/defaults/XpCommand.php ================================================ setPermission("pocketmine.command.xp"); } public function execute(CommandSender $sender, $currentAlias, array $args){ if(!$this->testPermission($sender)){ return true; } if(count($args) < 2){ if($sender instanceof ConsoleCommandSender){ $sender->sendMessage("You must specify a target player in the console"); return true; } $player = $sender; }else{ $player = $sender->getServer()->getPlayer($args[1]); } if($player instanceof Player){ $name = $player->getName(); if(count($args) < 1){ $player->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; } if(strcasecmp(substr($args[0], -1), "L") == 0){ //Set Experience Level(with "L" after args[0]) $level = (int) rtrim($args[0], "Ll"); if($level > 0){ $player->addXpLevel((int) $level); $sender->sendMessage(new TranslationContainer("%commands.xp.success.levels", [$level, $name])); $player->getLevel()->addSound(new ExpPickupSound($player, mt_rand(0, 1000))); //TODO: Find the level-up sound return true; }elseif($level < 0){ $player->takeXpLevel((int) -$level); $sender->sendMessage(new TranslationContainer("%commands.xp.success.negative.levels", [-$level, $name])); return true; } }else{ if(($xp = (int) $args[0]) > 0){ //Set Experience $player->addXp((int) $args[0]); $player->getLevel()->addSound(new ExpPickupSound($player, mt_rand(0, 1000))); $sender->sendMessage(new TranslationContainer("%commands.xp.success", [$name, $args[0]])); return true; }elseif($xp < 0){ //Stupid, but this lines up with vanilla behaviour, so... $sender->sendMessage(new TranslationContainer("%commands.xp.failure.withdrawXp")); return true; } } //This statement will only be reached if the command failed $sender->sendMessage(new TranslationContainer("commands.generic.usage", [$this->usageMessage])); return false; }else{ $sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.player.notFound")); return false; } return false; } } ================================================ FILE: src/pocketmine/entity/Ageable.php ================================================ getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_BABY); } } ================================================ FILE: src/pocketmine/entity/Arrow.php ================================================ isCritical = (bool) $critical; if(!isset($nbt->Potion)){ $nbt->Potion = new ShortTag("Potion", 0); } parent::__construct($chunk, $nbt, $shootingEntity); $this->potionId = $this->namedtag["Potion"]; } public function getPotionId() : int{ return $this->potionId; } public function onUpdate($currentTick){ if($this->closed){ return false; } $this->timings->startTiming(); $hasUpdate = parent::onUpdate($currentTick); if(!$this->hadCollision and $this->isCritical){ $this->level->addParticle(new CriticalParticle($this->add( $this->width / 2 + mt_rand(-100, 100) / 500, $this->height / 2 + mt_rand(-100, 100) / 500, $this->width / 2 + mt_rand(-100, 100) / 500))); }elseif($this->onGround){ $this->isCritical = false; } if($this->potionId != 0){ if(!$this->onGround or ($this->onGround and ($currentTick % 4) == 0)){ $color = Potion::getColor($this->potionId - 1); $this->level->addParticle(new MobSpellParticle($this->add( $this->width / 2 + mt_rand(-100, 100) / 500, $this->height / 2 + mt_rand(-100, 100) / 500, $this->width / 2 + mt_rand(-100, 100) / 500), $color[0], $color[1], $color[2])); } $hasUpdate = true; } if($this->age > 1200){ $this->kill(); $hasUpdate = true; } $this->timings->stopTiming(); return $hasUpdate; } public function isCritical(){ return $this->isCritical; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->type = Arrow::NETWORK_ID; $pk->eid = $this->getId(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Attachable.php ================================================ $maxValue or $defaultValue > $maxValue or $defaultValue < $minValue){ throw new \InvalidArgumentException("Invalid ranges: min value: $minValue, max value: $maxValue, $defaultValue: $defaultValue"); } return self::$attributes[(int) $id] = new Attribute($id, $name, $minValue, $maxValue, $defaultValue, $shouldSend); } /** * @param $id * * @return null|Attribute */ public static function getAttribute($id){ return isset(self::$attributes[$id]) ? clone self::$attributes[$id] : null; } /** * @param $name * * @return null|Attribute */ public static function getAttributeByName($name){ foreach(self::$attributes as $a){ if($a->getName() === $name){ return clone $a; } } return null; } private function __construct($id, $name, $minValue, $maxValue, $defaultValue, $shouldSend = true){ $this->id = (int) $id; $this->name = (string) $name; $this->minValue = (float) $minValue; $this->maxValue = (float) $maxValue; $this->defaultValue = (float) $defaultValue; $this->shouldSend = (bool) $shouldSend; $this->currentValue = $this->defaultValue; } public function getMinValue(){ return $this->minValue; } public function setMinValue($minValue){ if($minValue > $this->getMaxValue()){ throw new \InvalidArgumentException("Value $minValue is bigger than the maxValue!"); } if($this->minValue != $minValue){ $this->desynchronized = true; $this->minValue = $minValue; } return $this; } public function getMaxValue(){ return $this->maxValue; } public function setMaxValue($maxValue){ if($maxValue < $this->getMinValue()){ throw new \InvalidArgumentException("Value $maxValue is bigger than the minValue!"); } if($this->maxValue != $maxValue){ $this->desynchronized = true; $this->maxValue = $maxValue; } return $this; } public function getDefaultValue(){ return $this->defaultValue; } public function setDefaultValue($defaultValue){ if($defaultValue > $this->getMaxValue() or $defaultValue < $this->getMinValue()){ throw new \InvalidArgumentException("Value $defaultValue exceeds the range!"); } if($this->defaultValue !== $defaultValue){ $this->desynchronized = true; $this->defaultValue = $defaultValue; } return $this; } public function getValue(){ return $this->currentValue; } public function setValue($value, bool $fit = true, bool $shouldSend = false){ if($value > $this->getMaxValue() or $value < $this->getMinValue()){ if(!$fit){ Server::getInstance()->getLogger()->error("[Attribute / {$this->getName()}] Value $value exceeds the range!"); } $value = min(max($value, $this->getMinValue()), $this->getMaxValue()); } if($this->currentValue != $value){ $this->desynchronized = true; $this->currentValue = $value; } if($shouldSend){ $this->desynchronized = true; } return $this; } public function getName(){ return $this->name; } public function getId(){ return $this->id; } public function isSyncable(){ return $this->shouldSend; } public function isDesynchronized() : bool{ return $this->shouldSend and $this->desynchronized; } public function markSynchronized(bool $synced = true){ $this->desynchronized = !$synced; } } ================================================ FILE: src/pocketmine/entity/AttributeMap.php ================================================ attributes[$attribute->getId()] = $attribute; } /** * @param int $id * * @return Attribute|null */ public function getAttribute(int $id){ return $this->attributes[$id] ?? null; } public function getAll(): array{ return $this->attributes; } /** * @return Attribute[] */ public function needSend() : array{ return array_filter($this->attributes, function (Attribute $attribute){ return $attribute->isSyncable() and $attribute->isDesynchronized(); }); } public function offsetExists($offset){ return isset($this->attributes[$offset]); } public function offsetGet($offset){ return $this->attributes[$offset]->getValue(); } public function offsetSet($offset, $value){ $this->attributes[$offset]->setValue($value); } public function offsetUnset($offset){ throw new \RuntimeException("Could not unset an attribute from an attribute map"); } } ================================================ FILE: src/pocketmine/entity/Bat.php ================================================ setMaxHealth(6); parent::initEntity(); } public function __construct(Chunk $chunk, CompoundTag $nbt){ if(!isset($nbt->isResting)){ $nbt->isResting = new ByteTag("isResting", 0); } parent::__construct($chunk, $nbt); $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_RESTING, $this->isResting()); } public function isResting() : int{ return (int) $this->namedtag["isResting"]; } public function setResting(bool $resting){ $this->namedtag->isResting = new ByteTag("isResting", $resting ? 1 : 0); } public function onUpdate($currentTick){ if ($this->age > 20 * 60 * 10) { $this->kill(); } return parent::onUpdate($currentTick); } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = Bat::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Blaze.php ================================================ eid = $this->getId(); $pk->type = self::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } public function getDrops(){ $cause = $this->lastDamageCause; //Only drop when kill by player or dog(No add now.) if($cause instanceof EntityDamageByEntityEvent and $cause->getDamager() instanceof Player){ $lootingL = $cause->getDamager()->getItemInHand()->getEnchantmentLevel(Enchantment::TYPE_WEAPON_LOOTING); $drops = array(ItemItem::get(ItemItem::BLAZE_ROD, 0, mt_rand(0, 1 + $lootingL))); return $drops; } return []; } } ================================================ FILE: src/pocketmine/entity/Boat.php ================================================ WoodID)){ $nbt->WoodID = new IntTag("WoodID", 0); } parent::__construct($chunk, $nbt); $this->setDataProperty(self::DATA_VARIANT, self::DATA_TYPE_INT, $this->getWoodID()); } public function getWoodID() : int{ return (int) $this->namedtag["WoodID"]; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = Boat::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = 0; $pk->speedY = 0; $pk->speedZ = 0; $pk->yaw = 0; $pk->pitch = 0; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } public function attack($damage, EntityDamageEvent $source){ parent::attack($damage, $source); if(!$source->isCancelled()){ $pk = new EntityEventPacket(); $pk->eid = $this->id; $pk->event = EntityEventPacket::HURT_ANIMATION; foreach($this->getLevel()->getPlayers() as $player){ $player->dataPacket($pk); } } } public function onUpdate($currentTick){ if($this->closed){ return false; } $tickDiff = $currentTick - $this->lastUpdate; if($tickDiff <= 0 and !$this->justCreated){ return true; } $this->lastUpdate = $currentTick; $this->timings->startTiming(); $hasUpdate = $this->entityBaseTick($tickDiff); if(!$this->level->getBlock(new Vector3($this->x,$this->y,$this->z))->getBoundingBox()==null or $this->isInsideOfWater()){ $this->motionY = 0.1; }else{ $this->motionY = -0.08; } $this->move($this->motionX, $this->motionY, $this->motionZ); $this->updateMovement(); if($this->linkedEntity == null or $this->linkedType = 0){ if($this->age > 1500){ $this->close(); $hasUpdate = true; //$this->scheduleUpdate(); $this->age = 0; } $this->age++; }else $this->age = 0; $this->timings->stopTiming(); return $hasUpdate or !$this->onGround or abs($this->motionX) > 0.00001 or abs($this->motionY) > 0.00001 or abs($this->motionZ) > 0.00001; } public function getDrops(){ return [ ItemItem::get(ItemItem::BOAT, 0, 1) ]; } public function getSaveId(){ $class = new \ReflectionClass(static::class); return $class->getShortName(); } } ================================================ FILE: src/pocketmine/entity/CaveSpider.php ================================================ eid = $this->getId(); $pk->type = CaveSpider::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Chicken.php ================================================ eid = $this->getId(); $pk->type = Chicken::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } public function getDrops(){ $drops = []; if ($this->lastDamageCause instanceof EntityDamageByEntityEvent and $this->lastDamageCause->getEntity() instanceof Player) { switch (\mt_rand(0, 2)) { case 0: $drops[] = ItemItem::get(ItemItem::RAW_CHICKEN, 0, 1); break; case 1: $drops[] = ItemItem::get(ItemItem::FEATHER, 0, 1); break; case 2: $drops[] = ItemItem::get(ItemItem::FEATHER, 0, 2); break; } } return $drops; } } ================================================ FILE: src/pocketmine/entity/Colorable.php ================================================ eid = $this->getId(); $pk->type = Cow::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } public function getDrops(){ $lootingL = 0; $cause = $this->lastDamageCause; if($cause instanceof EntityDamageByEntityEvent and $cause->getDamager() instanceof Player){ $lootingL = $cause->getDamager()->getItemInHand()->getEnchantmentLevel(Enchantment::TYPE_WEAPON_LOOTING); } $drops = array(ItemItem::get(ItemItem::RAW_BEEF, 0, mt_rand(1, 3 + $lootingL))); $drops[] = ItemItem::get(ItemItem::LEATHER, 0, mt_rand(0, 2 + $lootingL)); //TODO: add judgement for Steak /*if ($this->lastDamageCause instanceof EntityDamageByEntityEvent and $this->lastDamageCause->getEntity() instanceof Player) { $drops[] = ItemItem::get(ItemItem::LEATHER, 0, mt_rand(0,2)); }*/ return $drops; } } ================================================ FILE: src/pocketmine/entity/Creature.php ================================================ attackingTick > 0){ $this->attackingTick--; } if(!$this->isAlive() and $this->hasSpawned){ ++$this->deadTicks; if($this->deadTicks >= 20){ $this->despawnFromAll(); } return true; } if($this->isAlive()){ $this->motionY -= $this->gravity; $this->move($this->motionX, $this->motionY, $this->motionZ); $friction = 1 - $this->drag; if($this->onGround and (abs($this->motionX) > 0.00001 or abs($this->motionZ) > 0.00001)){ $friction = $this->getLevel()->getBlock($this->temporalVector->setComponents((int) floor($this->x), (int) floor($this->y - 1), (int) floor($this->z) - 1))->getFrictionFactor() * $friction; } $this->motionX *= $friction; $this->motionY *= 1 - $this->drag; $this->motionZ *= $friction; if($this->onGround){ $this->motionY *= -0.5; } $this->updateMovement(); } } parent::entityBaseTick(); return parent::onUpdate($tick); } public function willMove($distance = 36){ foreach($this->getViewers() as $viewer){ if($this->distance($viewer->getLocation()) <= $distance) return true; } return false; } public function attack($damage, EntityDamageEvent $source){ parent::attack($damage, $source); if(!$source->isCancelled() and $source->getCause() == EntityDamageEvent::CAUSE_ENTITY_ATTACK){ $this->attackingTick = 20; } } /** * @param Level $level * @param Vector3 $v3 * @param bool $hate * @param bool $reason * @return bool|float|string * 判断某坐标是否可以行走 * 并给出原因 */ public function ifjump(Level $level, Vector3 $v3, $hate = false, $reason = false){ //boybook Y轴算法核心函数 $x = floor($v3->getX()); $y = floor($v3->getY()); $z = floor($v3->getZ()); //echo ($y." "); if($this->whatBlock($level, new Vector3($x, $y, $z)) == "air"){ //echo "前方空气 "; if($this->whatBlock($level, new Vector3($x, $y - 1, $z)) == "block" or new Vector3($x, $y - 1, $z) == "climb"){ //方块 //echo "考虑向前 "; if($this->whatBlock($level, new Vector3($x, $y + 1, $z)) == "block" or $this->whatBlock($level, new Vector3($x, $y + 1, $z)) == "half" or $this->whatBlock($level, new Vector3($x, $y + 1, $z)) == "high"){ //上方一格被堵住了 //echo "上方卡住 \n"; if($reason) return 'up!'; return false; //上方卡住 }else{ //echo "GO向前走 \n"; if($reason) return 'GO'; return $y; //向前走 } }elseif($this->whatBlock($level, new Vector3($x, $y - 1, $z)) == "water"){ //水 //echo "下水游泳 \n"; if($reason) return 'swim'; return $y - 1; //降低一格向前走(下水游泳) }elseif($this->whatBlock($level, new Vector3($x, $y - 1, $z)) == "half"){ //半砖 //echo "下到半砖 \n"; if($reason) return 'half'; return $y - 0.5; //向下跳0.5格 }elseif($this->whatBlock($level, new Vector3($x, $y - 1, $z)) == "lava"){ //岩浆 //echo "前方岩浆 \n"; if($reason) return 'lava'; return false; //前方岩浆 }elseif($this->whatBlock($level, new Vector3($x, $y - 1, $z)) == "air"){ //空气 //echo "考虑向下跳 "; if($this->whatBlock($level, new Vector3($x, $y - 2, $z)) == "block"){ //echo "GO向下跳 \n"; if($reason) return 'down'; return $y - 1; //向下跳 }else{ //前方悬崖 //echo "前方悬崖 \n"; if($reason) return 'fall'; if($hate === false){ return false; }else{ return $y - 1; //向下跳 } } } }elseif($this->whatBlock($level, new Vector3($x, $y, $z)) == "water"){ //水 //echo "正在水中"; if($this->whatBlock($level, new Vector3($x, $y + 1, $z)) == "water"){ //上面还是水 //echo "向上游 \n"; if($reason) return 'inwater'; return $y + 1; //向上游,防溺水 }elseif($this->whatBlock($level, new Vector3($x, $y + 1, $z)) == "block" or $this->whatBlock($level, new Vector3($x, $y + 1, $z)) == "half"){ //上方一格被堵住了 if($this->whatBlock($level, new Vector3($x, $y - 1, $z)) == "block" or $this->whatBlock($level, new Vector3($x, $y - 1, $z)) == "half"){ //下方一格被也堵住了 //echo "上下都被卡住 \n"; if($reason) return 'up!_down!'; return false; //上下都被卡住 }else{ //echo "向下游 \n"; if($reason) return 'up!'; return $y - 1; //向下游,防卡住 } }else{ //echo "游泳ing... \n"; if($reason) return 'swim...'; return $y; //向前游 } }elseif($this->whatBlock($level, new Vector3($x, $y, $z)) == "half"){ //半砖 //echo "前方半砖 \n"; if($this->whatBlock($level, new Vector3($x, $y + 1, $z)) == "block" or $this->whatBlock($level, new Vector3($x, $y + 1, $z)) == "half" or $this->whatBlock($level, new Vector3($x, $y + 1, $z)) == "high"){ //上方一格被堵住了 //return false; //上方卡住 }else{ if($reason) return 'halfGO'; return $y + 0.5; } }elseif($this->whatBlock($level, new Vector3($x, $y, $z)) == "lava"){ //岩浆 //echo "前方岩浆 \n"; if($reason) return 'lava'; return false; }elseif($this->whatBlock($level, new Vector3($x, $y, $z)) == "high"){ //1.5格高方块 //echo "前方栅栏 \n"; if($reason) return 'high'; return false; }elseif($this->whatBlock($level, new Vector3($x, $y, $z)) == "climb"){ //梯子 //echo "前方梯子 \n"; //return $y; if($reason) return 'climb'; if($hate){ return $y + 0.7; }else{ return $y + 0.5; } }else{ //考虑向上 //echo "考虑向上 "; if($this->whatBlock($level, new Vector3($x, $y + 1, $z)) != "air"){ //前方是面墙 //echo "前方是墙 \n"; if($reason) return 'wall'; return false; }else{ if($this->whatBlock($level, new Vector3($x, $y + 2, $z)) == "block" or $this->whatBlock($level, new Vector3($x, $y + 2, $z)) == "half" or $this->whatBlock($level, new Vector3($x, $y + 2, $z)) == "high"){ //上方两格被堵住了 //echo "2格处被堵 \n"; if($reason) return 'up2!'; return false; }else{ //echo "GO向上跳 \n"; if($reason) return 'upGO'; return $y + 1; //向上跳 } } } return false; } public function whatBlock(Level $level, $v3){ //boybook的y轴判断法 核心 什么方块? $id = $level->getBlockIdAt($v3->x, $v3->y, $v3->z); $damage = $level->getBlockDataAt($v3->x, $v3->y, $v3->z); switch($id){ case 0: case 6: case 27: case 30: case 31: case 37: case 38: case 39: case 40: case 50: case 51: case 63: case 66: case 68: case 78: case 111: case 141: case 142: case 171: case 175: case 244: case 323: //透明方块 return "air"; break; case 8: case 9: //水 return "water"; break; case 10: case 11: //岩浆 return "lava"; break; case 44: case 158: //半砖 if($damage >= 8){ return "block"; }else{ return "half"; } break; case 64: //门 //var_dump($damage." "); //TODO 不知如何判断门是否开启,因为以下条件永远满足 if(($damage & 0x08) === 0x08){ return "air"; }else{ return "block"; } break; case 85: case 107: case 139: //1.5格高的无法跳跃物 return "high"; break; case 65: case 106: //可攀爬物 return "climb"; break; default: //普通方块 return "block"; break; } } /** * @param $mx * @param $mz * @return float|int * 获取yaw角度 */ public function getMyYaw($mx, $mz){ //根据motion计算转向角度 //转向计算 if($mz == 0){ //斜率不存在 if($mx < 0){ $yaw = -90; }else{ $yaw = 90; } }else{ //存在斜率 if($mx >= 0 and $mz > 0){ //第一象限 $atan = atan($mx / $mz); $yaw = rad2deg($atan); }elseif($mx >= 0 and $mz < 0){ //第二象限 $atan = atan($mx / abs($mz)); $yaw = 180 - rad2deg($atan); }elseif($mx < 0 and $mz < 0){ //第三象限 $atan = atan($mx / $mz); $yaw = -(180 - rad2deg($atan)); }elseif($mx < 0 and $mz > 0){ //第四象限 $atan = atan(abs($mx) / $mz); $yaw = -(rad2deg($atan)); }else{ $yaw = 0; } } $yaw = -$yaw; return $yaw; } /** * @param Vector3 $from * @param Vector3 $to * @return float|int * 获取pitch角度 */ public function getMyPitch(Vector3 $from, Vector3 $to){ $distance = $from->distance($to); $height = $to->y - $from->y; if($height > 0){ return -rad2deg(asin($height / $distance)); }elseif($height < 0){ return rad2deg(asin(-$height / $distance)); }else{ return 0; } } } ================================================ FILE: src/pocketmine/entity/Creeper.php ================================================ namedtag->powered)){ $this->setPowered(false); } $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_POWERED, $this->isPowered()); } public function setPowered(bool $powered, Lightning $lightning = null){ if($lightning != null){ $powered = true; $cause = CreeperPowerEvent::CAUSE_LIGHTNING; }else $cause = $powered ? CreeperPowerEvent::CAUSE_SET_ON : CreeperPowerEvent::CAUSE_SET_OFF; $this->getLevel()->getServer()->getPluginManager()->callEvent($ev = new CreeperPowerEvent($this, $lightning, $cause)); if(!$ev->isCancelled()){ $this->namedtag->powered = new ByteTag("powered", $powered ? 1 : 0); $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_POWERED, $powered); } } public function isPowered() : bool{ return (bool) $this->namedtag["powered"]; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = Creeper::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Damageable.php ================================================ setMaxHealth(200); parent::initEntity(); } public function getName(){ return "Ender Dragon"; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = Dragon::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/DragonFireBall.php ================================================ closed){ return false; } $this->timings->startTiming(); $hasUpdate = parent::onUpdate($currentTick); if($this->age > 1200 or $this->isCollided){ $this->kill(); $hasUpdate = true; } $this->timings->stopTiming(); return $hasUpdate; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->type = DragonFireBall::NETWORK_ID; $pk->eid = $this->getId(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Effect.php ================================================ id = $id; $this->name = $name; $this->bad = (bool) $isBad; $this->setColor($r, $g, $b); } public function getName(){ return $this->name; } public function getId(){ return $this->id; } public function setDuration($ticks){ $this->duration = (($ticks > self::MAX_DURATION) ? self::MAX_DURATION : $ticks); return $this; } public function getDuration(){ return $this->duration; } public function isVisible(){ return $this->show; } public function setVisible($bool){ $this->show = (bool) $bool; return $this; } /** * @return int */ public function getAmplifier(){ return $this->amplifier; } /** * @param int $amplifier * * @return $this */ public function setAmplifier(int $amplifier){ $this->amplifier = $amplifier & 0xff; return $this; } public function isAmbient(){ return $this->ambient; } public function setAmbient($ambient = true){ $this->ambient = (bool) $ambient; return $this; } public function isBad(){ return $this->bad; } public function canTick(){ if($this->amplifier < 0) $this->amplifier = 0; switch($this->id){ case Effect::POISON: if(($interval = (25 >> $this->amplifier)) > 0){ return ($this->duration % $interval) === 0; } return true; case Effect::WITHER: if(($interval = (50 >> $this->amplifier)) > 0){ return ($this->duration % $interval) === 0; } return true; case Effect::REGENERATION: if(($interval = (40 >> $this->amplifier)) > 0){ return ($this->duration % $interval) === 0; } return true; case Effect::HUNGER: if($this->amplifier < 0){ // prevents hacking with amplifier -1 return false; } if(($interval = 20) > 0){ return ($this->duration % $interval) === 0; } return true; case Effect::HEALING: case Effect::HARMING: return true; case Effect::SATURATION: if(($interval = (20 >> $this->amplifier)) > 0){ return ($this->duration % $interval) === 0; } return true; } return false; } public function applyEffect(Entity $entity){ switch($this->id){ case Effect::POISON: if($entity->getHealth() > 1){ $ev = new EntityDamageEvent($entity, EntityDamageEvent::CAUSE_MAGIC, 1); $entity->attack($ev->getFinalDamage(), $ev); } break; case Effect::WITHER: $ev = new EntityDamageEvent($entity, EntityDamageEvent::CAUSE_MAGIC, 1); $entity->attack($ev->getFinalDamage(), $ev); break; case Effect::REGENERATION: if($entity->getHealth() < $entity->getMaxHealth()){ $ev = new EntityRegainHealthEvent($entity, 1, EntityRegainHealthEvent::CAUSE_MAGIC); $entity->heal($ev->getAmount(), $ev); } break; case Effect::HUNGER: if($entity instanceof Human){ $entity->exhaust(0.5 * $this->amplifier, PlayerExhaustEvent::CAUSE_POTION); } break; case Effect::HEALING: $level = $this->amplifier + 1; if(($entity->getHealth() + 4 * $level) <= $entity->getMaxHealth()) { $ev = new EntityRegainHealthEvent($entity, 4 * $level, EntityRegainHealthEvent::CAUSE_MAGIC); $entity->heal($ev->getAmount(), $ev); } else { $ev = new EntityRegainHealthEvent($entity, $entity->getMaxHealth() - $entity->getHealth(), EntityRegainHealthEvent::CAUSE_MAGIC); $entity->heal($ev->getAmount(), $ev); } break; case Effect::HARMING: $level = $this->amplifier + 1; if(($entity->getHealth() - 6 * $level) >= 0) { $ev = new EntityDamageEvent($entity, EntityDamageEvent::CAUSE_MAGIC, 6 * $level); $entity->attack($ev->getFinalDamage(), $ev); } else { $ev = new EntityDamageEvent($entity, EntityDamageEvent::CAUSE_MAGIC, $entity->getHealth()); $entity->attack($ev->getFinalDamage(), $ev); } break; case Effect::SATURATION: if($entity instanceof Player){ if($entity->getServer()->foodEnabled) { $entity->setFood($entity->getFood() + 1); } } break; } } public function getColor(){ return [$this->color >> 16, ($this->color >> 8) & 0xff, $this->color & 0xff]; } public function setColor($r, $g, $b){ $this->color = (($r & 0xff) << 16) + (($g & 0xff) << 8) + ($b & 0xff); } public function add(Entity $entity, $modify = false, Effect $oldEffect = null){ if($entity instanceof Player){ $pk = new MobEffectPacket(); $pk->eid = 0; $pk->effectId = $this->getId(); $pk->amplifier = $this->getAmplifier(); $pk->particles = $this->isVisible(); $pk->duration = $this->getDuration(); if($modify){ $pk->eventId = MobEffectPacket::EVENT_MODIFY; }else{ $pk->eventId = MobEffectPacket::EVENT_ADD; } $entity->dataPacket($pk); if($this->id === Effect::SPEED){ $attr = $entity->getAttributeMap()->getAttribute(Attribute::MOVEMENT_SPEED); if($modify and $oldEffect !== null){ $speed = $attr->getValue() / (1 + 0.2 * ($oldEffect->getAmplifier() + 1)); }else{ $speed = $attr->getValue(); } $speed *= (1 + 0.2 * ($this->amplifier + 1)); $attr->setValue($speed); }elseif($this->id === Effect::SLOWNESS){ $attr = $entity->getAttributeMap()->getAttribute(Attribute::MOVEMENT_SPEED); if($modify and $oldEffect !== null){ $speed = $attr->getValue() / (1 - 0.15 * ($oldEffect->getAmplifier() + 1)); }else{ $speed = $attr->getValue(); } $speed *= (1 - (0.15 * $this->amplifier + 1)); $attr->setValue($speed); } } if($this->id === Effect::INVISIBILITY){ $entity->setDataFlag(Entity::DATA_FLAGS, Entity::DATA_FLAG_INVISIBLE, true); $entity->setNameTagVisible(false); } } public function remove(Entity $entity){ if($entity instanceof Player){ $pk = new MobEffectPacket(); $pk->eid = 0; $pk->eventId = MobEffectPacket::EVENT_REMOVE; $pk->effectId = $this->getId(); $entity->dataPacket($pk); if($this->id === Effect::SPEED){ $attr = $entity->getAttributeMap()->getAttribute(Attribute::MOVEMENT_SPEED); $attr->setValue($attr->getValue() / (1 + 0.2 * ($this->amplifier + 1))); }elseif($this->id === Effect::SLOWNESS){ $attr = $entity->getAttributeMap()->getAttribute(Attribute::MOVEMENT_SPEED); $attr->setValue($attr->getValue() / (1 - 0.15 * ($this->amplifier + 1))); } } if($this->id === Effect::INVISIBILITY){ $entity->setDataFlag(Entity::DATA_FLAGS, Entity::DATA_FLAG_INVISIBLE, false); $entity->setNameTagVisible(true); } } } ================================================ FILE: src/pocketmine/entity/Egg.php ================================================ closed){ return false; } $this->timings->startTiming(); $hasUpdate = parent::onUpdate($currentTick); if($this->age > 1200 or $this->isCollided){ $this->kill(); $hasUpdate = true; //Chance to spawn chicken } $this->timings->stopTiming(); return $hasUpdate; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->type = Egg::NETWORK_ID; $pk->eid = $this->getId(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/EnderCrystal.php ================================================ eid = $this->getId(); $pk->type = EnderCrystal::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = 0; $pk->speedY = 0; $pk->speedZ = 0; $pk->yaw = 0; $pk->pitch = 0; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/EnderPearl.php ================================================ closed){ return false; } $this->timings->startTiming(); $hasUpdate = parent::onUpdate($currentTick); if($this->age > 1200 or $this->isCollided){ $this->kill(); $hasUpdate = true; } $this->timings->stopTiming(); return $hasUpdate; } /** @return Player */ public function getSpawner(){ return $this->player; } public function setSpawner(Player $player){ $this->player = $player; } public function close(){ if ($this->getSpawner() instanceof Player) { $this->getSpawner()->teleport($this); } parent::close(); } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->type = self::NETWORK_ID; $pk->eid = $this->getId(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Enderman.php ================================================ eid = $this->getId(); $pk->type = Enderman::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Entity.php ================================================ [self::DATA_TYPE_LONG, 0], self::DATA_AIR => [self::DATA_TYPE_SHORT, 400], self::DATA_MAX_AIR => [self::DATA_TYPE_SHORT, 400], self::DATA_NAMETAG => [self::DATA_TYPE_STRING, ""], self::DATA_LEAD_HOLDER_EID => [self::DATA_TYPE_LONG, -1], self::DATA_SCALE => [self::DATA_TYPE_FLOAT, 1], ]; public $passenger = null; public $vehicle = null; /** @var Chunk */ public $chunk; protected $lastDamageCause = null; /** @var Block[] */ private $blocksAround = []; public $lastX = null; public $lastY = null; public $lastZ = null; public $motionX; public $motionY; public $motionZ; /** @var Vector3 */ public $temporalVector; public $lastMotionX; public $lastMotionY; public $lastMotionZ; public $lastYaw; public $lastPitch; /** @var AxisAlignedBB */ public $boundingBox; public $onGround; public $inBlock = false; public $positionChanged; public $motionChanged; public $deadTicks = 0; protected $age = 0; public $height; public $eyeHeight = null; public $width; public $length; /** @var int */ private $health = 20; private $maxHealth = 20; protected $ySize = 0; protected $stepHeight = 0; public $keepMovement = false; public $fallDistance = 0; public $ticksLived = 0; public $lastUpdate; public $maxFireTicks; public $fireTicks = 0; public $namedtag; public $canCollide = true; protected $isStatic = false; public $isCollided = false; public $isCollidedHorizontally = false; public $isCollidedVertically = false; public $noDamageTicks; protected $justCreated; private $invulnerable; /** @var AttributeMap */ protected $attributeMap; protected $gravity; protected $drag; /** @var Server */ protected $server; public $closed = false; /** @var \pocketmine\event\TimingsHandler */ protected $timings; protected $isPlayer = false; /** @var Entity */ protected $linkedEntity = null; /** 0 no linked 1 linked other 2 be linked */ protected $linkedType = null; protected $riding = null; /** @var PressurePlate */ protected $activatedPressurePlates = []; public $dropExp = [0, 0]; public function __construct(Chunk $chunk, CompoundTag $nbt){ if($chunk === null or $chunk->getProvider() === null){ throw new ChunkException("Invalid garbage Chunk given to Entity"); } $this->timings = Timings::getEntityTimings($this); $this->isPlayer = $this instanceof Player; $this->temporalVector = new Vector3(); if($this->eyeHeight === null){ $this->eyeHeight = $this->height / 2 + 0.1; } $this->id = Entity::$entityCount++; $this->justCreated = true; $this->namedtag = $nbt; $this->chunk = $chunk; $this->setLevel($chunk->getProvider()->getLevel()); $this->server = $chunk->getProvider()->getLevel()->getServer(); $this->boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0); $this->setPositionAndRotation( $this->temporalVector->setComponents( $this->namedtag["Pos"][0], $this->namedtag["Pos"][1], $this->namedtag["Pos"][2] ), $this->namedtag->Rotation[0], $this->namedtag->Rotation[1] ); $this->setMotion($this->temporalVector->setComponents($this->namedtag["Motion"][0], $this->namedtag["Motion"][1], $this->namedtag["Motion"][2])); if(is_nan($this->x) or is_infinite($this->x) or is_nan($this->y) or is_infinite($this->y) or is_nan($this->z) or is_infinite($this->z)){ throw new \InvalidStateException("Invalid entity coordinates"); } if(!isset($this->namedtag->FallDistance)){ $this->namedtag->FallDistance = new FloatTag("FallDistance", 0); } $this->fallDistance = $this->namedtag["FallDistance"]; if(!isset($this->namedtag->Fire)){ $this->namedtag->Fire = new ShortTag("Fire", 0); } $this->fireTicks = $this->namedtag["Fire"]; if(!isset($this->namedtag->Air)){ $this->namedtag->Air = new ShortTag("Air", 300); } $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, $this->namedtag["Air"]); if(!isset($this->namedtag->OnGround)){ $this->namedtag->OnGround = new ByteTag("OnGround", 0); } $this->onGround = $this->namedtag["OnGround"] > 0 ? true : false; if(!isset($this->namedtag->Invulnerable)){ $this->namedtag->Invulnerable = new ByteTag("Invulnerable", 0); } $this->invulnerable = $this->namedtag["Invulnerable"] > 0 ? true : false; $this->attributeMap = new AttributeMap(); $this->chunk->addEntity($this); $this->level->addEntity($this); $this->initEntity(); $this->lastUpdate = $this->server->getTick(); $this->server->getPluginManager()->callEvent(new EntitySpawnEvent($this)); $this->scheduleUpdate(); } /** * @return int */ public function getDropExpMin() : int{ return $this->dropExp[0]; } /** * @return int */ public function getDropExpMax() : int{ return $this->dropExp[1]; } /** * @return string */ public function getNameTag(){ return $this->getDataProperty(self::DATA_NAMETAG); } /** * @return bool */ public function isNameTagVisible(){ return $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_CAN_SHOW_NAMETAG); } /** * @return bool */ public function isNameTagAlwaysVisible(){ return $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ALWAYS_SHOW_NAMETAG); } /** * @param string $name */ public function setNameTag($name){ $this->setDataProperty(self::DATA_NAMETAG, self::DATA_TYPE_STRING, $name); } /** * @param bool $value */ public function setNameTagVisible($value = true){ $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_CAN_SHOW_NAMETAG, $value); } /** * @param bool $value */ public function setNameTagAlwaysVisible($value = true){ $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ALWAYS_SHOW_NAMETAG, $value); } public function isSneaking(){ return $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_SNEAKING); } public function setSneaking($value = true){ $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_SNEAKING, (bool) $value); } public function isSprinting(){ return $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_SPRINTING); } public function setSprinting($value = true){ if($value !== $this->isSprinting()){ $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_SPRINTING, (bool) $value); $attr = $this->attributeMap->getAttribute(Attribute::MOVEMENT_SPEED); $attr->setValue($value ? ($attr->getValue() * 1.3) : ($attr->getValue() / 1.3)); } } public function isGliding(){ return $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_IDLING); } public function setGliding($value = true){ $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_FALL_FLYING, (bool) $value); $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_IDLING, (bool) $value); } public function isImmobile() : bool{ return $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_IMMOBILE); } public function setImmobile($value = true){ $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_IMMOBILE, $value); } /** * @return Effect[] */ public function getEffects(){ return $this->effects; } public function removeAllEffects(){ foreach($this->effects as $effect){ $this->removeEffect($effect->getId()); } } public function removeEffect($effectId){ Server::getInstance()->getPluginManager()->callEvent($ev = new EntityEffectRemoveEvent($this, $effectId)); if($ev->isCancelled()){ return false; } if(isset($this->effects[$effectId])){ $effect = $this->effects[$effectId]; unset($this->effects[$effectId]); $effect->remove($this); if($effectId === Effect::ABSORPTION and $this instanceof Human){ $this->setAbsorption(0); } $this->recalculateEffectColor(); return true; } } public function getEffect($effectId){ return isset($this->effects[$effectId]) ? $this->effects[$effectId] : null; } public function hasEffect($effectId){ return isset($this->effects[$effectId]); } public function addEffect(Effect $effect){ Server::getInstance()->getPluginManager()->callEvent($ev = new EntityEffectAddEvent($this, $effect)); if($ev->isCancelled()){ return false; } if($effect->getId() === Effect::HEALTH_BOOST){ $this->setHealth($this->getHealth() + 4 * ($effect->getAmplifier() + 1)); } if($effect->getId() === Effect::ABSORPTION and $this instanceof Human){ $this->setAbsorption(4 * ($effect->getAmplifier() + 1)); } if(isset($this->effects[$effect->getId()])){ $oldEffect = $this->effects[$effect->getId()]; if(($effect->getAmplifier() <= ($oldEffect->getAmplifier())) and $effect->getDuration() < $oldEffect->getDuration()){ return; } $effect->add($this, true, $oldEffect); }else{ $effect->add($this, false); } $this->effects[$effect->getId()] = $effect; $this->recalculateEffectColor(); return true; } protected function recalculateEffectColor(){ //TODO: add transparency values $color = [0, 0, 0]; //RGB $count = 0; $ambient = true; foreach($this->effects as $effect){ if($effect->isVisible()){ $c = $effect->getColor(); $color[0] += $c[0] * ($effect->getAmplifier() + 1); $color[1] += $c[1] * ($effect->getAmplifier() + 1); $color[2] += $c[2] * ($effect->getAmplifier() + 1); $count += $effect->getAmplifier() + 1; if(!$effect->isAmbient()){ $ambient = false; } } } if($count > 0){ $r = ($color[0] / $count) & 0xff; $g = ($color[1] / $count) & 0xff; $b = ($color[2] / $count) & 0xff; $this->setDataProperty(Entity::DATA_POTION_COLOR, Entity::DATA_TYPE_INT, ($r << 16) + ($g << 8) + $b); $this->setDataProperty(Entity::DATA_POTION_AMBIENT, Entity::DATA_TYPE_BYTE, $ambient ? 1 : 0); }else{ $this->setDataProperty(Entity::DATA_POTION_COLOR, Entity::DATA_TYPE_INT, 0); $this->setDataProperty(Entity::DATA_POTION_AMBIENT, Entity::DATA_TYPE_BYTE, 0); } } /** * @param int|string $type * @param Chunk $chunk * @param CompoundTag $nbt * @param $args * * @return Entity|Projectile */ public static function createEntity($type, Chunk $chunk, CompoundTag $nbt, ...$args){ if(isset(self::$knownEntities[$type])){ $class = self::$knownEntities[$type]; return new $class($chunk, $nbt, ...$args); } return null; } public static function registerEntity($className, $force = false){ $class = new \ReflectionClass($className); if(is_a($className, Entity::class, true) and !$class->isAbstract()){ if($className::NETWORK_ID !== -1){ self::$knownEntities[$className::NETWORK_ID] = $className; }elseif(!$force){ return false; } self::$knownEntities[$class->getShortName()] = $className; self::$shortNames[$className] = $class->getShortName(); return true; } return false; } /** * Returns the short save name * * @return string */ public function getSaveId(){ return self::$shortNames[static::class]; } public function saveNBT(){ if(!($this instanceof Player)){ $this->namedtag->id = new StringTag("id", $this->getSaveId()); if($this->getNameTag() !== ""){ $this->namedtag->CustomName = new StringTag("CustomName", $this->getNameTag()); $this->namedtag->CustomNameVisible = new StringTag("CustomNameVisible", $this->isNameTagVisible()); }else{ unset($this->namedtag->CustomName); unset($this->namedtag->CustomNameVisible); } } $this->namedtag->Pos = new ListTag("Pos", [ new DoubleTag(0, $this->x), new DoubleTag(1, $this->y), new DoubleTag(2, $this->z) ]); $this->namedtag->Motion = new ListTag("Motion", [ new DoubleTag(0, $this->motionX), new DoubleTag(1, $this->motionY), new DoubleTag(2, $this->motionZ) ]); $this->namedtag->Rotation = new ListTag("Rotation", [ new FloatTag(0, $this->yaw), new FloatTag(1, $this->pitch) ]); $this->namedtag->FallDistance = new FloatTag("FallDistance", $this->fallDistance); $this->namedtag->Fire = new ShortTag("Fire", $this->fireTicks); $this->namedtag->Air = new ShortTag("Air", $this->getDataProperty(self::DATA_AIR)); $this->namedtag->OnGround = new ByteTag("OnGround", $this->onGround == true ? 1 : 0); $this->namedtag->Invulnerable = new ByteTag("Invulnerable", $this->invulnerable == true ? 1 : 0); if(count($this->effects) > 0){ $effects = []; foreach($this->effects as $effect){ $effects[$effect->getId()] = new CompoundTag($effect->getId(), [ "Id" => new ByteTag("Id", $effect->getId()), "Amplifier" => new ByteTag("Amplifier", $effect->getAmplifier()), "Duration" => new IntTag("Duration", $effect->getDuration()), "Ambient" => new ByteTag("Ambient", 0), "ShowParticles" => new ByteTag("ShowParticles", $effect->isVisible() ? 1 : 0) ]); } $this->namedtag->ActiveEffects = new ListTag("ActiveEffects", $effects); }else{ unset($this->namedtag->ActiveEffects); } } protected function initEntity(){ if(!($this->namedtag instanceof CompoundTag)){ throw new \InvalidArgumentException("Expecting CompoundTag, received " . get_class($this->namedtag)); } if(isset($this->namedtag->CustomName)){ $this->setNameTag($this->namedtag["CustomName"]); if(isset($this->namedtag->CustomNameVisible)){ $this->setNameTagVisible($this->namedtag["CustomNameVisible"] > 0); } } $this->scheduleUpdate(); $this->addAttributes(); if(isset($this->namedtag->ActiveEffects)){ foreach($this->namedtag->ActiveEffects->getValue() as $e){ $amplifier = $e["Amplifier"] & 0xff; //0-255 only $effect = Effect::getEffect($e["Id"]); if($effect === null){ continue; } $effect->setAmplifier($amplifier)->setDuration($e["Duration"])->setVisible($e["ShowParticles"] > 0); $this->addEffect($effect); } } } protected function addAttributes(){ } /** * @return Player[] */ public function getViewers(){ return $this->hasSpawned; } /** * @param Player $player */ public function spawnTo(Player $player){ if(!isset($this->hasSpawned[$player->getLoaderId()]) and isset($player->usedChunks[Level::chunkHash($this->chunk->getX(), $this->chunk->getZ())])){ $this->hasSpawned[$player->getLoaderId()] = $player; } } public function sendPotionEffects(Player $player){ foreach($this->effects as $effect){ $pk = new MobEffectPacket(); $pk->eid = 0; $pk->effectId = $effect->getId(); $pk->amplifier = $effect->getAmplifier(); $pk->particles = $effect->isVisible(); $pk->duration = $effect->getDuration(); $pk->eventId = MobEffectPacket::EVENT_ADD; $player->dataPacket($pk); } } /** * @param Player[]|Player $player * @param array $data Properly formatted entity data, defaults to everything */ public function sendData($player, array $data = null){ if(!is_array($player)){ $player = [$player]; } $pk = new SetEntityDataPacket(); $pk->eid = $this->getId(); $pk->metadata = $data === null ? $this->dataProperties : $data; foreach($player as $p){ if($p === $this){ continue; } $p->dataPacket(clone $pk); } if($this instanceof Player){ $pk->eid = 0; $this->dataPacket($pk); } } /** * @param Player $player */ public function despawnFrom(Player $player){ if(isset($this->hasSpawned[$player->getLoaderId()])){ $pk = new RemoveEntityPacket(); $pk->eid = $this->getId(); $player->dataPacket($pk); unset($this->hasSpawned[$player->getLoaderId()]); } } /** * @param float $damage * @param EntityDamageEvent $source * * @return bool */ public function attack($damage, EntityDamageEvent $source){ if($this->hasEffect(Effect::FIRE_RESISTANCE) and ($source->getCause() === EntityDamageEvent::CAUSE_FIRE or $source->getCause() === EntityDamageEvent::CAUSE_FIRE_TICK or $source->getCause() === EntityDamageEvent::CAUSE_LAVA) ){ $source->setCancelled(); } $this->server->getPluginManager()->callEvent($source); if($source->isCancelled()){ return false; } $this->setLastDamageCause($source); if($this instanceof Human){ $damage = round($source->getFinalDamage()); if($this->getAbsorption() > 0){ $absorption = $this->getAbsorption() - $damage; $this->setAbsorption($absorption <= 0 ? 0 : $absorption); $this->setHealth($this->getHealth() + $absorption); }else{ $this->setHealth($this->getHealth() - $damage); } }else{ $this->setHealth($this->getHealth() - round($source->getFinalDamage())); } return true; } /** * @param float $amount * @param EntityRegainHealthEvent $source * */ public function heal($amount, EntityRegainHealthEvent $source){ $this->server->getPluginManager()->callEvent($source); if($source->isCancelled()){ return; } $this->setHealth($this->getHealth() + $source->getAmount()); } /** * @return int */ public function getHealth(){ return $this->health; } public function isAlive(){ return $this->health > 0; } /** * Sets the health of the Entity. This won't send any update to the players * * @param int $amount */ public function setHealth($amount){ $amount = (int) $amount; if($amount === $this->health){ return; } if($amount <= 0){ if($this->isAlive()){ $this->kill(); } }elseif($amount <= $this->getMaxHealth() or $amount < $this->health){ $this->health = (int) $amount; }else{ $this->health = $this->getMaxHealth(); } } /** * @param EntityDamageEvent $type */ public function setLastDamageCause(EntityDamageEvent $type){ $this->lastDamageCause = $type; } /** * @return EntityDamageEvent|null */ public function getLastDamageCause(){ return $this->lastDamageCause; } public function getAttributeMap(){ return $this->attributeMap; } /** * @return int */ public function getMaxHealth(){ return $this->maxHealth + ($this->hasEffect(Effect::HEALTH_BOOST) ? 4 * ($this->getEffect(Effect::HEALTH_BOOST)->getAmplifier() + 1) : 0); } /** * @param int $amount */ public function setMaxHealth($amount){ $this->maxHealth = (int) $amount; } public function canCollideWith(Entity $entity){ return !$this->justCreated and $entity !== $this; } protected function checkObstruction($x, $y, $z){ $i = Math::floorFloat($x); $j = Math::floorFloat($y); $k = Math::floorFloat($z); $diffX = $x - $i; $diffY = $y - $j; $diffZ = $z - $k; if(Block::$solid[$this->level->getBlockIdAt($i, $j, $k)]){ $flag = !Block::$solid[$this->level->getBlockIdAt($i - 1, $j, $k)]; $flag1 = !Block::$solid[$this->level->getBlockIdAt($i + 1, $j, $k)]; $flag2 = !Block::$solid[$this->level->getBlockIdAt($i, $j - 1, $k)]; $flag3 = !Block::$solid[$this->level->getBlockIdAt($i, $j + 1, $k)]; $flag4 = !Block::$solid[$this->level->getBlockIdAt($i, $j, $k - 1)]; $flag5 = !Block::$solid[$this->level->getBlockIdAt($i, $j, $k + 1)]; $direction = -1; $limit = 9999; if($flag){ $limit = $diffX; $direction = 0; } if($flag1 and 1 - $diffX < $limit){ $limit = 1 - $diffX; $direction = 1; } if($flag2 and $diffY < $limit){ $limit = $diffY; $direction = 2; } if($flag3 and 1 - $diffY < $limit){ $limit = 1 - $diffY; $direction = 3; } if($flag4 and $diffZ < $limit){ $limit = $diffZ; $direction = 4; } if($flag5 and 1 - $diffZ < $limit){ $direction = 5; } $force = lcg_value() * 0.2 + 0.1; if($direction === 0){ $this->motionX = -$force; return true; } if($direction === 1){ $this->motionX = $force; return true; } if($direction === 2){ $this->motionY = -$force; return true; } if($direction === 3){ $this->motionY = $force; return true; } if($direction === 4){ $this->motionZ = -$force; return true; } if($direction === 5){ $this->motionZ = $force; return true; } } return false; } public function entityBaseTick($tickDiff = 1){ Timings::$timerEntityBaseTick->startTiming(); //TODO: check vehicles $this->blocksAround = null; $this->justCreated = false; if(!$this->isAlive()){ $this->removeAllEffects(); $this->despawnFromAll(); if(!$this->isPlayer){ $this->close(); } Timings::$timerEntityBaseTick->stopTiming(); return false; } if(count($this->effects) > 0){ foreach($this->effects as $effect){ if($effect->canTick()){ $effect->applyEffect($this); } $effect->setDuration($effect->getDuration() - $tickDiff); if($effect->getDuration() <= 0){ $this->removeEffect($effect->getId()); } } } $hasUpdate = false; $this->checkBlockCollision(); if($this->y <= -16 and $this->isAlive()){ $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_VOID, 10); $this->attack($ev->getFinalDamage(), $ev); $hasUpdate = true; } if($this->fireTicks > 0){ if($this->isFireProof()){ if($this->fireTicks > 1){ $this->fireTicks = 1; }else{ $this->fireTicks -= 1; } }else{ if(!$this->hasEffect(Effect::FIRE_RESISTANCE) and (($this->fireTicks % 20) === 0 or $tickDiff > 20)){ $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_FIRE_TICK, 1); $this->attack($ev->getFinalDamage(), $ev); } $this->fireTicks -= $tickDiff; } if($this->fireTicks <= 0 && $this->fireTicks > -10){ $this->extinguish(); }else{ $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ONFIRE, true); $hasUpdate = true; } } if($this->noDamageTicks > 0){ $this->noDamageTicks -= $tickDiff; if($this->noDamageTicks < 0){ $this->noDamageTicks = 0; } } $this->age += $tickDiff; $this->ticksLived += $tickDiff; Timings::$timerEntityBaseTick->stopTiming(); return $hasUpdate; } protected function updateMovement(){ $diffPosition = ($this->x - $this->lastX) ** 2 + ($this->y - $this->lastY) ** 2 + ($this->z - $this->lastZ) ** 2; $diffRotation = ($this->yaw - $this->lastYaw) ** 2 + ($this->pitch - $this->lastPitch) ** 2; $diffMotion = ($this->motionX - $this->lastMotionX) ** 2 + ($this->motionY - $this->lastMotionY) ** 2 + ($this->motionZ - $this->lastMotionZ) ** 2; if($diffPosition > 0.04 or $diffRotation > 2.25 and ($diffMotion > 0.0001 and $this->getMotion()->lengthSquared() <= 0.00001)){ //0.2 ** 2, 1.5 ** 2 $this->lastX = $this->x; $this->lastY = $this->y; $this->lastZ = $this->z; $this->lastYaw = $this->yaw; $this->lastPitch = $this->pitch; $this->addMovement($this->x, $this->y + $this->getEyeHeight(), $this->z, $this->yaw, $this->pitch, $this->yaw); } if($diffMotion > 0.0025 or ($diffMotion > 0.0001 and $this->getMotion()->lengthSquared() <= 0.0001)){ //0.05 ** 2 $this->lastMotionX = $this->motionX; $this->lastMotionY = $this->motionY; $this->lastMotionZ = $this->motionZ; $this->level->addEntityMotion($this->chunk->getX(), $this->chunk->getZ(), $this->id, $this->motionX, $this->motionY, $this->motionZ); } } public function addMovement($x, $y, $z, $yaw, $pitch, $headYaw = null){ $this->level->addEntityMovement($this->chunk->getX(), $this->chunk->getZ(), $this->id, $x, $y, $z, $yaw, $pitch, $headYaw === null ? $yaw : $headYaw); } /** * @return Vector3 */ public function getDirectionVector(){ $y = -sin(deg2rad($this->pitch)); $xz = cos(deg2rad($this->pitch)); $x = -$xz * sin(deg2rad($this->yaw)); $z = $xz * cos(deg2rad($this->yaw)); return $this->temporalVector->setComponents($x, $y, $z)->normalize(); } public function getDirectionPlane(){ return (new Vector2(-cos(deg2rad($this->yaw) - M_PI_2), -sin(deg2rad($this->yaw) - M_PI_2)))->normalize(); } public function onUpdate($currentTick){ if($this->closed){ return false; } if(!$this->isAlive()){ ++$this->deadTicks; if($this->deadTicks >= 10){ $this->despawnFromAll(); if(!$this->isPlayer){ $this->close(); } } return $this->deadTicks < 10; } $tickDiff = $currentTick - $this->lastUpdate; if($tickDiff <= 0){ return false; } $this->lastUpdate = $currentTick; $this->timings->startTiming(); $hasUpdate = $this->entityBaseTick($tickDiff); $this->updateMovement(); $this->timings->stopTiming(); //if($this->isStatic()) return $hasUpdate; //return !($this instanceof Player); } public final function scheduleUpdate(){ $this->level->updateEntities[$this->id] = $this; } public function isOnFire(){ return $this->fireTicks > 0; } public function setOnFire($seconds){ $ticks = $seconds * 20; if($ticks > $this->fireTicks){ $this->fireTicks = $ticks; } } public function isFireProof() : bool{ return false; } public function getDirection(){ $rotation = ($this->yaw - 90) % 360; if($rotation < 0){ $rotation += 360.0; } if((0 <= $rotation and $rotation < 45) or (315 <= $rotation and $rotation < 360)){ return 2; //North }elseif(45 <= $rotation and $rotation < 135){ return 3; //East }elseif(135 <= $rotation and $rotation < 225){ return 0; //South }elseif(225 <= $rotation and $rotation < 315){ return 1; //West }else{ return null; } } public function extinguish(){ $this->fireTicks = 0; $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ONFIRE, false); } public function canTriggerWalking(){ return true; } public function resetFallDistance(){ $this->fallDistance = 0; } protected function updateFallState($distanceThisTick, $onGround){ if($onGround === true){ if($this->fallDistance > 0){ if($this instanceof Living){ $this->fall($this->fallDistance); } $this->resetFallDistance(); } }elseif($distanceThisTick < 0){ $this->fallDistance -= $distanceThisTick; } } public function getBoundingBox(){ return $this->boundingBox; } public function fall($fallDistance){ if($this instanceof Player and $this->isSpectator()){ return; } if($this->getLevel()->getServer()->destroyBlockParticle and $fallDistance > 3){ $this->getLevel()->addParticle(new DestroyBlockParticle($this, $this->getLevel()->getBlock($this->floor()->subtract(0, 1, 0)))); } if($this->isInsideOfWater()){ return; } $damage = floor($fallDistance - 3 - ($this->hasEffect(Effect::JUMP) ? $this->getEffect(Effect::JUMP)->getAmplifier() + 1 : 0)); //Get the block directly beneath the player's feet, check if it is a slime block if($this->getLevel()->getBlock($this->floor()->subtract(0, 1, 0)) instanceof SlimeBlock){ $damage = 0; } if($damage > 0){ $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_FALL, $damage); $this->attack($ev->getFinalDamage(), $ev); } } public function handleLavaMovement(){ //TODO } public function getEyeHeight(){ return $this->eyeHeight; } public function moveFlying(){ //TODO } public function onCollideWithPlayer(Human $entityPlayer){ } protected function switchLevel(Level $targetLevel){ if($this->closed){ return false; } if($this->isValid()){ $this->server->getPluginManager()->callEvent($ev = new EntityLevelChangeEvent($this, $this->level, $targetLevel)); if($ev->isCancelled()){ return false; } $this->level->removeEntity($this); if($this->chunk !== null){ $this->chunk->removeEntity($this); } $this->despawnFromAll(); } $this->setLevel($targetLevel); $this->level->addEntity($this); $this->chunk = null; return true; } public function getPosition(){ return new Position($this->x, $this->y, $this->z, $this->level); } public function getLocation(){ return new Location($this->x, $this->y, $this->z, $this->yaw, $this->pitch, $this->level); } public function isInsideOfPortal(){ $blocks = $this->getBlocksAround(); foreach($blocks as $block){ if($block instanceof Portal){ return true; } } return false; } public function isInsideOfWater(){ $block = $this->level->getBlock($this->temporalVector->setComponents(Math::floorFloat($this->x), Math::floorFloat($y = ($this->y + $this->getEyeHeight())), Math::floorFloat($this->z))); if($block instanceof Water){ $f = ($block->y + 1) - ($block->getFluidHeightPercent() - 0.1111111); return $y < $f; } return false; } public function isInsideOfSolid(){ $block = $this->level->getBlock($this->temporalVector->setComponents(Math::floorFloat($this->x), Math::floorFloat($y = ($this->y + $this->getEyeHeight())), Math::floorFloat($this->z))); $bb = $block->getBoundingBox(); if($bb !== null and $block->isSolid() and !$block->isTransparent() and $bb->intersectsWith($this->getBoundingBox())){ return true; } return false; } public function isInsideOfFire(){ foreach($this->getBlocksAround() as $block){ if($block instanceof Fire){ return true; } } return false; } public function fastMove($dx, $dy, $dz){ if($dx == 0 and $dz == 0 and $dy == 0){ return true; } Timings::$entityMoveTimer->startTiming(); /*$newBB = $this->boundingBox->getOffsetBoundingBox($dx, $dy, $dz); $list = $this->level->getCollisionCubes($this, $newBB, false); if(count($list) === 0){ $this->boundingBox = $newBB; }*/ $this->x = ($this->boundingBox->minX + $this->boundingBox->maxX) / 2; $this->y = $this->boundingBox->minY - $this->ySize; $this->z = ($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2; $this->checkChunks(); if(!$this->onGround or $dy != 0){ $bb = clone $this->boundingBox; $bb->minY -= 0.75; $this->onGround = false; if(!$this->level->getBlock(new Vector3($this->x, $this->y - 1, $this->z))->isTransparent()) $this->onGround = true; /* if(count($this->level->getCollisionBlocks($bb)) > 0){ $this->onGround = true; }*/ } $this->isCollided = $this->onGround; $this->updateFallState($dy, $this->onGround); Timings::$entityMoveTimer->stopTiming(); return true; } public function move($dx, $dy, $dz){ if($dx == 0 and $dz == 0 and $dy == 0){ return true; } if($this->keepMovement){ $this->boundingBox->offset($dx, $dy, $dz); $this->setPosition($this->temporalVector->setComponents(($this->boundingBox->minX + $this->boundingBox->maxX) / 2, $this->boundingBox->minY, ($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2)); $this->onGround = $this->isPlayer ? true : false; return true; }else{ Timings::$entityMoveTimer->startTiming(); $this->ySize *= 0.4; /* if($this->isColliding){ //With cobweb? $this->isColliding = false; $dx *= 0.25; $dy *= 0.05; $dz *= 0.25; $this->motionX = 0; $this->motionY = 0; $this->motionZ = 0; } */ $movX = $dx; $movY = $dy; $movZ = $dz; $axisalignedbb = clone $this->boundingBox; /*$sneakFlag = $this->onGround and $this instanceof Player; if($sneakFlag){ for($mov = 0.05; $dx != 0.0 and count($this->level->getCollisionCubes($this, $this->boundingBox->getOffsetBoundingBox($dx, -1, 0))) === 0; $movX = $dx){ if($dx < $mov and $dx >= -$mov){ $dx = 0; }elseif($dx > 0){ $dx -= $mov; }else{ $dx += $mov; } } for(; $dz != 0.0 and count($this->level->getCollisionCubes($this, $this->boundingBox->getOffsetBoundingBox(0, -1, $dz))) === 0; $movZ = $dz){ if($dz < $mov and $dz >= -$mov){ $dz = 0; }elseif($dz > 0){ $dz -= $mov; }else{ $dz += $mov; } } //TODO: big messy loop }*/ $list = $this->level->getCollisionCubes($this, $this->level->getTickRate() > 1 ? $this->boundingBox->getOffsetBoundingBox($dx, $dy, $dz) : $this->boundingBox->addCoord($dx, $dy, $dz), false); foreach($list as $bb){ $dy = $bb->calculateYOffset($this->boundingBox, $dy); } $this->boundingBox->offset(0, $dy, 0); $fallingFlag = ($this->onGround or ($dy != $movY and $movY < 0)); foreach($list as $bb){ $dx = $bb->calculateXOffset($this->boundingBox, $dx); } $this->boundingBox->offset($dx, 0, 0); foreach($list as $bb){ $dz = $bb->calculateZOffset($this->boundingBox, $dz); } $this->boundingBox->offset(0, 0, $dz); if($this->stepHeight > 0 and $fallingFlag and $this->ySize < 0.05 and ($movX != $dx or $movZ != $dz)){ $cx = $dx; $cy = $dy; $cz = $dz; $dx = $movX; $dy = $this->stepHeight; $dz = $movZ; $axisalignedbb1 = clone $this->boundingBox; $this->boundingBox->setBB($axisalignedbb); $list = $this->level->getCollisionCubes($this, $this->boundingBox->addCoord($dx, $dy, $dz), false); foreach($list as $bb){ $dy = $bb->calculateYOffset($this->boundingBox, $dy); } $this->boundingBox->offset(0, $dy, 0); foreach($list as $bb){ $dx = $bb->calculateXOffset($this->boundingBox, $dx); } $this->boundingBox->offset($dx, 0, 0); foreach($list as $bb){ $dz = $bb->calculateZOffset($this->boundingBox, $dz); } $this->boundingBox->offset(0, 0, $dz); if(($cx ** 2 + $cz ** 2) >= ($dx ** 2 + $dz ** 2)){ $dx = $cx; $dy = $cy; $dz = $cz; $this->boundingBox->setBB($axisalignedbb1); }else{ $this->ySize += 0.5; } } $this->x = ($this->boundingBox->minX + $this->boundingBox->maxX) / 2; $this->y = $this->boundingBox->minY - $this->ySize; $this->z = ($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2; $this->checkChunks(); $this->checkGroundState($movX, $movY, $movZ, $dx, $dy, $dz); $this->updateFallState($dy, $this->onGround); if($movX != $dx){ $this->motionX = 0; } if($movY != $dy){ $this->motionY = 0; } if($movZ != $dz){ $this->motionZ = 0; } //TODO: vehicle collision events (first we need to spawn them!) Timings::$entityMoveTimer->stopTiming(); return true; } } protected function checkGroundState($movX, $movY, $movZ, $dx, $dy, $dz){ $this->isCollidedVertically = $movY != $dy; $this->isCollidedHorizontally = ($movX != $dx or $movZ != $dz); $this->isCollided = ($this->isCollidedHorizontally or $this->isCollidedVertically); $this->onGround = ($movY != $dy and $movY < 0); } public function getBlocksAround(){ if($this->blocksAround === null){ $minX = Math::floorFloat($this->boundingBox->minX); $minY = Math::floorFloat($this->boundingBox->minY); $minZ = Math::floorFloat($this->boundingBox->minZ); $maxX = Math::ceilFloat($this->boundingBox->maxX); $maxY = Math::ceilFloat($this->boundingBox->maxY); $maxZ = Math::ceilFloat($this->boundingBox->maxZ); $this->blocksAround = []; for($z = $minZ; $z <= $maxZ; ++$z){ for($x = $minX; $x <= $maxX; ++$x){ for($y = $minY; $y <= $maxY; ++$y){ $block = $this->level->getBlock($this->temporalVector->setComponents($x, $y, $z)); if($block->hasEntityCollision()){ $this->blocksAround[Level::blockHash($block->x, $block->y, $block->z)] = $block; } } } } } return $this->blocksAround; } protected function checkBlockCollision(){ $vector = new Vector3(0, 0, 0); foreach($blocksaround = $this->getBlocksAround() as $block){ $block->onEntityCollide($this); if(!$this->isPlayer){ if($block instanceof PressurePlate){ $this->activatedPressurePlates[Level::blockHash($block->x, $block->y, $block->z)] = $block; } } $block->addVelocityToEntity($this, $vector); } if(!$this->isPlayer){ /** @var \pocketmine\block\PressurePlate $block * */ foreach($this->activatedPressurePlates as $key => $block){ if(!isset($blocksaround[$key])) $block->checkActivation(); } } if($vector->lengthSquared() > 0){ $vector = $vector->normalize(); $d = 0.014; $this->motionX += $vector->x * $d; $this->motionY += $vector->y * $d; $this->motionZ += $vector->z * $d; } } public function setPositionAndRotation(Vector3 $pos, $yaw, $pitch){ if($this->setPosition($pos) === true){ $this->setRotation($yaw, $pitch); return true; } return false; } public function setRotation($yaw, $pitch){ $this->yaw = $yaw; $this->pitch = $pitch; $this->scheduleUpdate(); } protected function checkChunks(){ if($this->chunk === null or ($this->chunk->getX() !== ($this->x >> 4) or $this->chunk->getZ() !== ($this->z >> 4))){ if($this->chunk !== null){ $this->chunk->removeEntity($this); } $this->chunk = $this->level->getChunk($this->x >> 4, $this->z >> 4, true); if(!$this->justCreated){ $newChunk = $this->level->getChunkPlayers($this->x >> 4, $this->z >> 4); foreach($this->hasSpawned as $player){ if(!isset($newChunk[$player->getLoaderId()])){ $this->despawnFrom($player); }else{ unset($newChunk[$player->getLoaderId()]); } } foreach($newChunk as $player){ $this->spawnTo($player); } } if($this->chunk === null){ return; } $this->chunk->addEntity($this); } } public function setLocation(Location $pos){ if($this->closed){ return false; } $this->setPositionAndRotation($pos, $pos->yaw, $pos->pitch); return true; } public function setPosition(Vector3 $pos){ if($this->closed){ return false; } if($pos instanceof Position and $pos->level !== null and $pos->level !== $this->level){ if($this->switchLevel($pos->getLevel()) === false){ return false; } } $this->x = $pos->x; $this->y = $pos->y; $this->z = $pos->z; $radius = $this->width / 2; $this->boundingBox->setBounds($pos->x - $radius, $pos->y, $pos->z - $radius, $pos->x + $radius, $pos->y + $this->height, $pos->z + $radius); $this->checkChunks(); return true; } public function getMotion(){ return new Vector3($this->motionX, $this->motionY, $this->motionZ); } public function setMotion(Vector3 $motion){ if(!$this->justCreated){ $this->server->getPluginManager()->callEvent($ev = new EntityMotionEvent($this, $motion)); if($ev->isCancelled()){ return false; } } $this->motionX = $motion->x; $this->motionY = $motion->y; $this->motionZ = $motion->z; if(!$this->justCreated){ $this->updateMovement(); } return true; } public function isOnGround(){ return $this->onGround === true; } public function kill(){ $this->health = 0; $this->removeAllEffects(); $this->scheduleUpdate(); if($this->getLevel()->getServer()->expEnabled){ $exp = mt_rand($this->getDropExpMin(), $this->getDropExpMax()); if($exp > 0) $this->getLevel()->spawnXPOrb($this, $exp); } } /** * @param Vector3|Position|Location $pos * @param float $yaw * @param float $pitch * * @return bool */ public function teleport(Vector3 $pos, $yaw = null, $pitch = null){ if($pos instanceof Location){ $yaw = $yaw === null ? $pos->yaw : $yaw; $pitch = $pitch === null ? $pos->pitch : $pitch; } $from = Position::fromObject($this, $this->level); $to = Position::fromObject($pos, $pos instanceof Position ? $pos->getLevel() : $this->level); $this->server->getPluginManager()->callEvent($ev = new EntityTeleportEvent($this, $from, $to)); if($ev->isCancelled()){ return false; } $this->ySize = 0; $pos = $ev->getTo(); $this->setMotion($this->temporalVector->setComponents(0, 0, 0)); if($this->setPositionAndRotation($pos, $yaw === null ? $this->yaw : $yaw, $pitch === null ? $this->pitch : $pitch) !== false){ $this->resetFallDistance(); $this->onGround = true; $this->lastX = $this->x; $this->lastY = $this->y; $this->lastZ = $this->z; $this->lastYaw = $this->yaw; $this->lastPitch = $this->pitch; $this->updateMovement(); return true; } return false; } public function getId(){ return $this->id; } public function respawnToAll(){ foreach($this->hasSpawned as $key => $player){ unset($this->hasSpawned[$key]); $this->spawnTo($player); } } public function spawnToAll(){ if($this->chunk === null or $this->closed){ return; } foreach($this->level->getChunkPlayers($this->chunk->getX(), $this->chunk->getZ()) as $player){ if($player->isOnline()){ $this->spawnTo($player); } } } public function despawnFromAll(){ foreach($this->hasSpawned as $player){ $this->despawnFrom($player); } } public function close(){ if(!$this->closed){ $this->server->getPluginManager()->callEvent(new EntityDespawnEvent($this)); $this->closed = true; $this->removeEffect(Effect::HEALTH_BOOST); $this->despawnFromAll(); if($this->linkedType != 0){ $this->linkedEntity->setLinked(0, $this); } if($this->chunk !== null){ $this->chunk->removeEntity($this); } if($this->level !== null){ $this->level->removeEntity($this); } } $this->activatedPressurePlates = []; if($this->attributeMap != null){ $this->attributeMap = null; } } /** * @param int $id * @param int $type * @param mixed $value * * @return bool */ public function setDataProperty($id, $type, $value){ if($this->getDataProperty($id) !== $value){ $this->dataProperties[$id] = [$type, $value]; $this->sendData($this->hasSpawned, [$id => $this->dataProperties[$id]]); return true; } return false; } public function linkEntity(Entity $entity){ return $this->setLinked(1, $entity); } public function sendLinkedData(){ if($this->linkedEntity instanceof Entity){ $this->setLinked($this->linkedType, $this->linkedEntity); } } public function setLinked($type = 0, Entity $entity){ if($type != 0 and $entity === null){ return false; } if($entity === $this){ return false; } switch($type){ case 0: if($this->linkedType == 0){ return true; } $this->linkedType = 0; $pk = new SetEntityLinkPacket(); $pk->from = $entity->getId(); $pk->to = $this->getId(); $pk->type = 3; $this->server->broadcastPacket($this->level->getPlayers(), $pk); if($this instanceof Player){ $pk = new SetEntityLinkPacket(); $pk->from = $entity->getId(); $pk->to = 0; $pk->type = 3; $this->dataPacket($pk); } if($this->linkedEntity->getLinkedType()){ $this->linkedEntity->setLinked(0, $this); } $this->linkedEntity = null; return true; case 1: if(!$entity->isAlive()){ return false; } $this->linkedEntity = $entity; $this->linkedType = 1; $entity->linkedEntity = $this; $entity->linkedType = 1; $pk = new SetEntityLinkPacket(); $pk->from = $entity->getId(); $pk->to = $this->getId(); $pk->type = 2; $this->server->broadcastPacket($this->level->getPlayers(), $pk); if($this instanceof Player){ $pk = new SetEntityLinkPacket(); $pk->from = $entity->getId(); $pk->to = 0; $pk->type = 2; $this->dataPacket($pk); } return true; case 2: if(!$entity->isAlive()){ return false; } if($entity->getLinkedEntity() !== $this){ return $entity->linkEntity($this); } $this->linkedEntity = $entity; $this->linkedType = 2; return true; default: return false; } } public function getLinkedEntity(){ return $this->linkedEntity; } public function getLinkedType(){ return $this->linkedType; } /** * @param int $id * * @return mixed */ public function getDataProperty($id){ return isset($this->dataProperties[$id]) ? $this->dataProperties[$id][1] : null; } /** * @param int $id * * @return int */ public function getDataPropertyType($id){ return isset($this->dataProperties[$id]) ? $this->dataProperties[$id][0] : null; } /** * @param $propertyId * @param $id * @param bool $value * @param int $type */ public function setDataFlag($propertyId, $id, $value = true, $type = self::DATA_TYPE_LONG){ if($this->getDataFlag($propertyId, $id) !== $value){ $flags = (int) $this->getDataProperty($propertyId); $flags ^= 1 << $id; $this->setDataProperty($propertyId, $type, $flags); } } /** * @param int $propertyId * @param int $id * * @return bool */ public function getDataFlag($propertyId, $id){ return (((int) $this->getDataProperty($propertyId)) & (1 << $id)) > 0; } public function __destruct(){ $this->close(); } public function setMetadata($metadataKey, MetadataValue $metadataValue){ $this->server->getEntityMetadata()->setMetadata($this, $metadataKey, $metadataValue); } public function getMetadata($metadataKey){ return $this->server->getEntityMetadata()->getMetadata($this, $metadataKey); } public function hasMetadata($metadataKey){ return $this->server->getEntityMetadata()->hasMetadata($this, $metadataKey); } public function removeMetadata($metadataKey, Plugin $plugin){ $this->server->getEntityMetadata()->removeMetadata($this, $metadataKey, $plugin); } public function __toString(){ return (new \ReflectionClass($this))->getShortName() . "(" . $this->getId() . ")"; } } ================================================ FILE: src/pocketmine/entity/Explosive.php ================================================ namedtag->TileID)){ $this->blockId = $this->namedtag["TileID"]; }elseif(isset($this->namedtag->Tile)){ $this->blockId = $this->namedtag["Tile"]; $this->namedtag["TileID"] = new IntTag("TileID", $this->blockId); } if(isset($this->namedtag->Data)){ $this->damage = $this->namedtag["Data"]; } if($this->blockId === 0){ $this->close(); return; } $this->setDataProperty(self::DATA_BLOCK_INFO, self::DATA_TYPE_INT, $this->getBlock() | ($this->getDamage() << 8)); } public function canCollideWith(Entity $entity){ return false; } public function attack($damage, EntityDamageEvent $source){ if($source->getCause() === EntityDamageEvent::CAUSE_VOID){ parent::attack($damage, $source); } } public function onUpdate($currentTick){ if($this->closed){ return false; } $this->timings->startTiming(); $tickDiff = $currentTick - $this->lastUpdate; if($tickDiff <= 0 and !$this->justCreated){ return true; } $this->lastUpdate = $currentTick; $height = $this->fallDistance; $hasUpdate = $this->entityBaseTick($tickDiff); if($this->isAlive()){ $pos = (new Vector3($this->x - 0.5, $this->y, $this->z - 0.5))->round(); if($this->ticksLived === 1){ $block = $this->level->getBlock($pos); if($block->getId() !== $this->blockId){ return true; } $this->level->setBlock($pos, Block::get(0), true); } $this->motionY -= $this->gravity; $this->move($this->motionX, $this->motionY, $this->motionZ); $friction = 1 - $this->drag; $this->motionX *= $friction; $this->motionY *= 1 - $this->drag; $this->motionZ *= $friction; $pos = (new Vector3($this->x - 0.5, $this->y, $this->z - 0.5))->round(); if($this->onGround){ $this->kill(); $block = $this->level->getBlock($pos); if($block->getId() > 0 and !$block->isSolid() and !($block instanceof Liquid)){ $this->getLevel()->dropItem($this, ItemItem::get($this->getBlock(), $this->getDamage(), 1)); }else{ if($block instanceof SnowLayer){ $oldDamage = $block->getDamage(); $this->server->getPluginManager()->callEvent($ev = new EntityBlockChangeEvent($this, $block, Block::get($this->getBlock(), $this->getDamage() + $oldDamage))); }else{ $this->server->getPluginManager()->callEvent($ev = new EntityBlockChangeEvent($this, $block, Block::get($this->getBlock(), $this->getDamage()))); } if(!$ev->isCancelled()){ $this->getLevel()->setBlock($pos, $ev->getTo(), true); if($ev->getTo() instanceof Anvil){ $sound = new AnvilFallSound($this); $this->getLevel()->addSound($sound); foreach($this->level->getNearbyEntities($this->boundingBox->grow(0.1, 0.1, 0.1), $this) as $entity){ $entity->scheduleUpdate(); if(!$entity->isAlive()){ continue; } if($entity instanceof Living){ $damage = ($height - 1) * 2; if($damage > 40) $damage = 40; $ev = new EntityDamageByEntityEvent($this, $entity, EntityDamageByEntityEvent::CAUSE_FALL, $damage, 0.1); $entity->attack($damage, $ev); } } } } } $hasUpdate = true; } $this->updateMovement(); } return $hasUpdate or !$this->onGround or abs($this->motionX) > 0.00001 or abs($this->motionY) > 0.00001 or abs($this->motionZ) > 0.00001; } public function getBlock(){ return $this->blockId; } public function getDamage(){ return $this->damage; } public function saveNBT(){ $this->namedtag->TileID = new IntTag("TileID", $this->blockId); $this->namedtag->Data = new ByteTag("Data", $this->damage); } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->type = FallingSand::NETWORK_ID; $pk->eid = $this->getId(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/FishingHook.php ================================================ namedtag->Data)){ $this->data = $this->namedtag["Data"]; } // $this->setDataProperty(FallingSand::DATA_BLOCK_INFO, self::DATA_TYPE_INT, $this->getData()); } public function __construct(Chunk $chunk, CompoundTag $nbt, Entity $shootingEntity = null){ parent::__construct($chunk, $nbt, $shootingEntity); } public function setData($id){ $this->data = $id; } public function getData(){ return $this->data; } public function onUpdate($currentTick){ if($this->closed){ return false; } $this->timings->startTiming(); $hasUpdate = parent::onUpdate($currentTick); if($this->isCollidedVertically && $this->isInsideOfWater()){ $this->motionX = 0; $this->motionY += 0.01; $this->motionZ = 0; $this->motionChanged = true; $hasUpdate = true; }elseif($this->isCollided && $this->keepMovement === true){ $this->motionX = 0; $this->motionY = 0; $this->motionZ = 0; $this->motionChanged = true; $this->keepMovement = false; $hasUpdate = true; } if($this->attractTimer === 0 && mt_rand(0, 100) <= 30){ // chance, that a fish bites $this->coughtTimer = mt_rand(5, 10) * 20; // random delay to catch fish $this->attractTimer = mt_rand(30, 100) * 20; // reset timer $this->attractFish(); if($this->shootingEntity instanceof Player) $this->shootingEntity->sendTip("A fish bites!"); }elseif($this->attractTimer > 0){ $this->attractTimer--; } if($this->coughtTimer > 0){ $this->coughtTimer--; $this->fishBites(); } $this->timings->stopTiming(); return $hasUpdate; } public function fishBites(){ if($this->shootingEntity instanceof Player){ $pk = new EntityEventPacket(); $pk->eid = $this->shootingEntity->getId();//$this or $this->shootingEntity $pk->event = EntityEventPacket::FISH_HOOK_HOOK; Server::getInstance()->broadcastPacket($this->shootingEntity->hasSpawned, $pk); } } public function attractFish(){ if($this->shootingEntity instanceof Player){ $pk = new EntityEventPacket(); $pk->eid = $this->shootingEntity->getId();//$this or $this->shootingEntity $pk->event = EntityEventPacket::FISH_HOOK_BUBBLE; Server::getInstance()->broadcastPacket($this->shootingEntity->hasSpawned, $pk); } } public function reelLine(){ $this->damageRod = false; if($this->shootingEntity instanceof Player && $this->coughtTimer > 0){ $fishes = [ItemItem::RAW_FISH, ItemItem::RAW_SALMON, ItemItem::CLOWN_FISH, ItemItem::PUFFER_FISH]; $fish = array_rand($fishes, 1); $item = ItemItem::get($fishes[$fish]); $this->getLevel()->getServer()->getPluginManager()->callEvent($ev = new PlayerFishEvent($this->shootingEntity, $item, $this)); if(!$ev->isCancelled()){ $this->shootingEntity->getInventory()->addItem($item); $this->shootingEntity->addExperience(mt_rand(1, 6)); $this->damageRod = true; } } if($this->shootingEntity instanceof Player){ $this->shootingEntity->unlinkHookFromPlayer(); } if(!$this->closed){ $this->kill(); $this->close(); } return $this->damageRod; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = FishingHook::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/FlyingAnimal.php ================================================ closed !== false){ return false; } if ($this->willMove(100)) { if(++$this->switchDirectionTicker === $this->switchDirectionTicks){ $this->switchDirectionTicker = 0; if(mt_rand(0, 100) < 50){ $this->flyDirection = null; } } $this->lastUpdate = $currentTick; $this->timings->startTiming(); if($this->isAlive()){ if($this->y > $this->highestY and $this->flyDirection !== null){ $this->flyDirection->y = -0.5; } $inAir = !$this->isInsideOfSolid() and !$this->isInsideOfWater(); if(!$inAir){ $this->flyDirection = null; } if($this->flyDirection instanceof Vector3){ //var_dump($this->flyDirection); $this->setMotion($this->flyDirection->multiply($this->flySpeed)); }else{ $this->flyDirection = $this->generateRandomDirection(); $this->flySpeed = mt_rand(50, 100) / 500; $this->setMotion($this->flyDirection); } //$expectedPos = new Vector3($this->x + $this->motionX, $this->y + $this->motionY, $this->z + $this->motionZ); //$motion = $this->flyDirection->multiply($this->flySpeed); $this->move($this->motionX, $this->motionY, $this->motionZ); $this->updateMovement(); //$this->getLevel()->addEntityMotion($this->chunk->getX(), $this->chunk->getZ(), $this->getId(), $motion->x, $motion->y, $motion->z); //echo "EID = {$this->getId()}, motionX = $this->motionX, motionY = $this->motionY, motionZ = $this->motionZ\n"; /* if($expectedPos->distanceSquared($this) > 0){ $this->flyDirection = $this->generateRandomDirection(); $this->flySpeed = mt_rand(50, 100) / 500; } $friction = 1 - $this->drag; $this->motionX *= $friction; $this->motionY *= 1 - $this->drag; $this->motionZ *= $friction; */ $f = sqrt(($this->motionX ** 2) + ($this->motionZ ** 2)); $this->yaw = (-atan2($this->motionX, $this->motionZ) * 180 / M_PI); $this->pitch = (-atan2($f, $this->motionY) * 180 / M_PI); if($this->onGround and $this->flyDirection instanceof Vector3){ $this->flyDirection->y *= -1; } } } parent::onUpdate($currentTick); //parent::entityBaseTick(); $this->timings->stopTiming(); return !$this->onGround or abs($this->motionX) > 0.00001 or abs($this->motionY) > 0.00001 or abs($this->motionZ) > 0.00001; } private function generateRandomDirection(){ return new Vector3(mt_rand(-1000, 1000) / 1000, mt_rand(-500, 500) / 1000, mt_rand(-1000, 1000) / 1000); } public function initEntity(){ parent::initEntity(); $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_BABY, false); } public function isBaby(){ return $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_BABY); } public function attack($damage, EntityDamageEvent $source){ if($source->isCancelled()){ return; } if ($source->getCause() == EntityDamageEvent::CAUSE_FALL) { $source->setCancelled(); return; } parent::attack($damage, $source); } } ================================================ FILE: src/pocketmine/entity/Ghast.php ================================================ setMaxHealth(10); parent::initEntity(); } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = Ghast::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Hanging.php ================================================ skin; } public function getSkinId(){ return $this->skinId; } /** * @return UUID|null */ public function getUniqueId(){ return $this->uuid; } /** * @return string */ public function getRawUniqueId(){ return $this->rawUUID; } /** * @param string $str * @param string $skinId */ public function setSkin($str, $skinId){ $this->skin = $str; $this->skinId = $skinId; } public function getFood() : float{ return $this->attributeMap->getAttribute(Attribute::HUNGER)->getValue(); } /** * WARNING: This method does not check if full and may throw an exception if out of bounds. * Use {@link Human::addFood()} for this purpose * * @param float $new * * @throws \InvalidArgumentException */ public function setFood(float $new){ $attr = $this->attributeMap->getAttribute(Attribute::HUNGER); $old = $attr->getValue(); $attr->setValue($new); // ranges: 18-20 (regen), 7-17 (none), 1-6 (no sprint), 0 (health depletion) foreach([17, 6, 0] as $bound){ if(($old > $bound) !== ($new > $bound)){ $reset = true; } } if(isset($reset)){ $this->foodTickTimer = 0; } } public function getMaxFood() : float{ return $this->attributeMap->getAttribute(Attribute::HUNGER)->getMaxValue(); } public function addFood(float $amount){ $attr = $this->attributeMap->getAttribute(Attribute::HUNGER); $amount += $attr->getValue(); $amount = max(min($amount, $attr->getMaxValue()), $attr->getMinValue()); $this->setFood($amount); } public function getSaturation() : float{ return $this->attributeMap->getAttribute(Attribute::SATURATION)->getValue(); } /** * WARNING: This method does not check if saturated and may throw an exception if out of bounds. * Use {@link Human::addSaturation()} for this purpose * * @param float $saturation * * @throws \InvalidArgumentException */ public function setSaturation(float $saturation){ $this->attributeMap->getAttribute(Attribute::SATURATION)->setValue($saturation); } public function addSaturation(float $amount){ $attr = $this->attributeMap->getAttribute(Attribute::SATURATION); $attr->setValue($attr->getValue() + $amount, true); } public function getExhaustion() : float{ return $this->attributeMap->getAttribute(Attribute::EXHAUSTION)->getValue(); } /** * WARNING: This method does not check if exhausted and does not consume saturation/food. * Use {@link Human::exhaust()} for this purpose. * * @param float $exhaustion */ public function setExhaustion(float $exhaustion){ $this->attributeMap->getAttribute(Attribute::EXHAUSTION)->setValue($exhaustion); } /** * Increases a human's exhaustion level. * * @param float $amount * @param int $cause * * @return float the amount of exhaustion level increased */ public function exhaust(float $amount, int $cause = PlayerExhaustEvent::CAUSE_CUSTOM) : float{ $this->server->getPluginManager()->callEvent($ev = new PlayerExhaustEvent($this, $amount, $cause)); if($ev->isCancelled()){ return 0.0; } $exhaustion = $this->getExhaustion(); $exhaustion += $ev->getAmount(); while($exhaustion >= 4.0){ $exhaustion -= 4.0; $saturation = $this->getSaturation(); if($saturation > 0){ $saturation = max(0, $saturation - 1.0); $this->setSaturation($saturation); }else{ $food = $this->getFood(); if($food > 0){ $food--; $this->setFood($food); } } } $this->setExhaustion($exhaustion); return $ev->getAmount(); } public function getXpLevel() : int{ return (int) $this->attributeMap->getAttribute(Attribute::EXPERIENCE_LEVEL)->getValue(); } public function setXpLevel(int $level) : bool{ $this->server->getPluginManager()->callEvent($ev = new PlayerExperienceChangeEvent($this, $level, $this->getXpProgress())); if(!$ev->isCancelled()){ $this->attributeMap->getAttribute(Attribute::EXPERIENCE_LEVEL)->setValue($ev->getExpLevel()); return true; } return false; } public function addXpLevel(int $level) : bool{ return $this->setXpLevel($this->getXpLevel() + $level); } public function takeXpLevel(int $level) : bool{ return $this->setXpLevel($this->getXpLevel() - $level); } public function getXpProgress() : float{ return $this->attributeMap->getAttribute(Attribute::EXPERIENCE)->getValue(); } public function setXpProgress(float $progress) : bool{ $this->attributeMap->getAttribute(Attribute::EXPERIENCE)->setValue($progress); return true; } public function getTotalXp() : int{ return $this->totalXp; } /** * Changes the total exp of a player * * @param int $xp * @param bool $syncLevel This will reset the level to be in sync with the total. Usually you don't want to do this, * because it'll mess up use of xp in anvils and enchanting tables. * * @return bool */ public function setTotalXp(int $xp, bool $syncLevel = false) : bool{ $xp &= 0x7fffffff; if($xp === $this->totalXp){ return false; } if(!$syncLevel){ $level = $this->getXpLevel(); $diff = $xp - $this->totalXp + $this->getFilledXp(); if($diff > 0){ //adding xp while($diff > ($v = self::getLevelXpRequirement($level))){ $diff -= $v; if(++$level >= 21863){ $diff = $v; //fill exp bar break; } } }else{ //taking xp while($diff < ($v = self::getLevelXpRequirement($level - 1))){ $diff += $v; if(--$level <= 0){ $diff = 0; break; } } } $progress = ($diff / $v); }else{ $values = self::getLevelFromXp($xp); $level = $values[0]; $progress = $values[1]; } $this->server->getPluginManager()->callEvent($ev = new PlayerExperienceChangeEvent($this, $level, $progress)); if(!$ev->isCancelled()){ $this->totalXp = $xp; $this->setXpLevel($ev->getExpLevel()); $this->setXpProgress($ev->getProgress()); return true; } return false; } public function addXp(int $xp, bool $syncLevel = false) : bool{ return $this->setTotalXp($this->totalXp + $xp, $syncLevel); } public function takeXp(int $xp, bool $syncLevel = false) : bool{ return $this->setTotalXp($this->totalXp - $xp, $syncLevel); } public function getRemainderXp() : int{ return self::getLevelXpRequirement($this->getXpLevel()) - $this->getFilledXp(); } public function getFilledXp() : int{ return self::getLevelXpRequirement($this->getXpLevel()) * $this->getXpProgress(); } public function recalculateXpProgress() : float{ return $this->setXpProgress($this->getRemainderXp() / self::getLevelXpRequirement($this->getXpLevel())); } public function getXpSeed() : int{ //TODO: use this for randomizing enchantments in enchanting tables return $this->xpSeed; } public function resetXpCooldown(){ $this->xpCooldown = microtime(true); } public function canPickupXp() : bool{ return microtime(true) - $this->xpCooldown > 0.5; } /** * Returns the total amount of exp required to reach the specified level. * * @param int $level * * @return int */ public static function getTotalXpRequirement(int $level) : int{ if($level <= 16){ return ($level ** 2) + (6 * $level); }elseif($level <= 31){ return (2.5 * ($level ** 2)) - (40.5 * $level) + 360; }elseif($level <= 21863){ return (4.5 * ($level ** 2)) - (162.5 * $level) + 2220; } return PHP_INT_MAX; //prevent float returns for invalid levels on 32-bit systems } /** * Returns the amount of exp required to complete the specified level. * * @param int $level * * @return int */ public static function getLevelXpRequirement(int $level) : int{ if($level <= 16){ return (2 * $level) + 7; }elseif($level <= 31){ return (5 * $level) - 38; }elseif($level <= 21863){ return (9 * $level) - 158; } return PHP_INT_MAX; } /** * Converts a quantity of exp into a level and a progress percentage * * @param int $xp * * @return int[] */ public static function getLevelFromXp(int $xp) : array{ $xp &= 0x7fffffff; /** These values are correct up to and including level 16 */ $a = 1; $b = 6; $c = -$xp; if($xp > self::getTotalXpRequirement(16)){ /** Modify the coefficients to fit the relevant equation */ if($xp <= self::getTotalXpRequirement(31)){ /** Levels 16-31 */ $a = 2.5; $b = -40.5; $c += 360; }else{ /** Level 32+ */ $a = 4.5; $b = -162.5; $c += 2220; } } $answer = max(Math::solveQuadratic($a, $b, $c)); //Use largest result value $level = floor($answer); $progress = $answer - $level; return [$level, $progress]; } public function getInventory(){ return $this->inventory; } public function getEnderChestInventory(){ return $this->enderChestInventory; } public function getFloatingInventory(){ return $this->floatingInventory; } public function getTransactionQueue(){ //Is creating the transaction queue ondemand a good idea? I think only if it's destroyed afterwards. hmm... if($this->transactionQueue === null){ //Potential for crashes here if a plugin attempts to use this, say for an NPC plugin or something... $this->transactionQueue = new SimpleTransactionQueue($this); } return $this->transactionQueue; } protected function initEntity(){ $this->setDataFlag(self::DATA_PLAYER_FLAGS, self::DATA_PLAYER_FLAG_SLEEP, false, self::DATA_TYPE_BYTE); $this->setDataProperty(self::DATA_PLAYER_BED_POSITION, self::DATA_TYPE_POS, [0, 0, 0]); $inventoryContents = ($this->namedtag->Inventory ?? null); $this->inventory = new PlayerInventory($this, $inventoryContents); $this->enderChestInventory = new EnderChestInventory($this, ($this->namedtag->EnderChestInventory ?? null)); //Virtual inventory for desktop GUI crafting and anti-cheat transaction processing $this->floatingInventory = new FloatingInventory($this); if($this instanceof Player){ $this->addWindow($this->inventory, 0); }else{ if(isset($this->namedtag->NameTag)){ $this->setNameTag($this->namedtag["NameTag"]); } if(isset($this->namedtag->Skin) and $this->namedtag->Skin instanceof CompoundTag){ $this->setSkin($this->namedtag->Skin["Data"], $this->namedtag->Skin["Name"]); } $this->uuid = UUID::fromData($this->getId(), $this->getSkinData(), $this->getNameTag()); } parent::initEntity(); if(!isset($this->namedtag->foodLevel) or !($this->namedtag->foodLevel instanceof IntTag)){ $this->namedtag->foodLevel = new IntTag("foodLevel", $this->getFood()); }else{ $this->setFood($this->namedtag["foodLevel"]); } if(!isset($this->namedtag->foodExhaustionLevel) or !($this->namedtag->foodExhaustionLevel instanceof IntTag)){ $this->namedtag->foodExhaustionLevel = new FloatTag("foodExhaustionLevel", $this->getExhaustion()); }else{ $this->setExhaustion($this->namedtag["foodExhaustionLevel"]); } if(!isset($this->namedtag->foodSaturationLevel) or !($this->namedtag->foodSaturationLevel instanceof IntTag)){ $this->namedtag->foodSaturationLevel = new FloatTag("foodSaturationLevel", $this->getSaturation()); }else{ $this->setSaturation($this->namedtag["foodSaturationLevel"]); } if(!isset($this->namedtag->foodTickTimer) or !($this->namedtag->foodTickTimer instanceof IntTag)){ $this->namedtag->foodTickTimer = new IntTag("foodTickTimer", $this->foodTickTimer); }else{ $this->foodTickTimer = $this->namedtag["foodTickTimer"]; } if(!isset($this->namedtag->XpLevel) or !($this->namedtag->XpLevel instanceof IntTag)){ $this->namedtag->XpLevel = new IntTag("XpLevel", 0); } $this->setXpLevel($this->namedtag["XpLevel"]); if(!isset($this->namedtag->XpP) or !($this->namedtag->XpP instanceof FloatTag)){ $this->namedtag->XpP = new FloatTag("XpP", 0); } $this->setXpProgress($this->namedtag["XpP"]); if(!isset($this->namedtag->XpTotal) or !($this->namedtag->XpTotal instanceof IntTag)){ $this->namedtag->XpTotal = new IntTag("XpTotal", 0); } $this->totalXp = $this->namedtag["XpTotal"]; if(!isset($this->namedtag->XpSeed) or !($this->namedtag->XpSeed instanceof IntTag)){ $this->namedtag->XpSeed = new IntTag("XpSeed", mt_rand(PHP_INT_MIN, PHP_INT_MAX)); } $this->xpSeed = $this->namedtag["XpSeed"]; } public function getAbsorption() : int{ return $this->attributeMap->getAttribute(Attribute::ABSORPTION)->getValue(); } public function setAbsorption(int $absorption){ $this->attributeMap->getAttribute(Attribute::ABSORPTION)->setValue($absorption); } protected function addAttributes(){ parent::addAttributes(); $this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::SATURATION)); $this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::EXHAUSTION)); $this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::HUNGER)); $this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::EXPERIENCE_LEVEL)); $this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::EXPERIENCE)); $this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::HEALTH)); $this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::MOVEMENT_SPEED)); $this->attributeMap->addAttribute(Attribute::getAttribute(Attribute::ABSORPTION)); } public function entityBaseTick($tickDiff = 1, $EnchantL = 0){ if($this->getInventory() instanceof PlayerInventory){ $EnchantL = $this->getInventory()->getHelmet()->getEnchantmentLevel(Enchantment::TYPE_WATER_BREATHING); } $hasUpdate = parent::entityBaseTick($tickDiff, $EnchantL); if($this->server->foodEnabled){ $food = $this->getFood(); $health = $this->getHealth(); if($food >= 18){ $this->foodTickTimer++; if($this->foodTickTimer >= 80 and $health < $this->getMaxHealth()){ $this->heal(1, new EntityRegainHealthEvent($this, 1, EntityRegainHealthEvent::CAUSE_SATURATION)); $this->exhaust(3.0, PlayerExhaustEvent::CAUSE_HEALTH_REGEN); $this->foodTickTimer = 0; } }elseif($food === 0){ $this->foodTickTimer++; if($this->foodTickTimer >= 80){ $diff = $this->server->getDifficulty(); $can = false; if($diff === 1){ $can = $health > 10; }elseif($diff === 2){ $can = $health > 1; }elseif($diff === 3){ $can = true; } if($can){ $this->attack(1, new EntityDamageEvent($this, EntityDamageEvent::CAUSE_STARVATION, 1)); } } } if($food <= 6){ if($this->isSprinting()){ $this->setSprinting(false); } } } return $hasUpdate; } public function getName(){ return $this->getNameTag(); } public function getDrops(){ $drops = []; if($this->inventory !== null){ foreach($this->inventory->getContents() as $item){ $drops[] = $item; } } return $drops; } public function saveNBT(){ parent::saveNBT(); $this->namedtag->Inventory = new ListTag("Inventory", []); $this->namedtag->Inventory->setTagType(NBT::TAG_Compound); if($this->inventory !== null){ //Hotbar for($slot = 0; $slot < $this->inventory->getHotbarSize(); ++$slot){ $inventorySlotIndex = $this->inventory->getHotbarSlotIndex($slot); $item = $this->inventory->getItem($inventorySlotIndex); $tag = $item->nbtSerialize($slot); $tag->TrueSlot = new ByteTag("TrueSlot", $inventorySlotIndex); $this->namedtag->Inventory[$slot] = $tag; } //Normal inventory $slotCount = $this->inventory->getSize() + $this->inventory->getHotbarSize(); for($slot = $this->inventory->getHotbarSize(); $slot < $slotCount; ++$slot){ $item = $this->inventory->getItem($slot - $this->inventory->getHotbarSize()); //As NBT, real inventory slots are slots 9-44, NOT 0-35 $this->namedtag->Inventory[$slot] = $item->nbtSerialize($slot); } //Armour for($slot = 100; $slot < 104; ++$slot){ $item = $this->inventory->getItem($this->inventory->getSize() + $slot - 100); if($item instanceof ItemItem and $item->getId() !== ItemItem::AIR){ $this->namedtag->Inventory[$slot] = $item->nbtSerialize($slot); } } } $this->namedtag->EnderChestInventory = new ListTag("EnderChestInventory", []); $this->namedtag->Inventory->setTagType(NBT::TAG_Compound); if($this->enderChestInventory !== null){ for($slot = 0; $slot < $this->enderChestInventory->getSize(); $slot++){ if(($item = $this->enderChestInventory->getItem($slot)) instanceof ItemItem){ $this->namedtag->EnderChestInventory[$slot] = $item->nbtSerialize($slot); } } } if(strlen($this->getSkinData()) > 0){ $this->namedtag->Skin = new CompoundTag("Skin", [ "Data" => new StringTag("Data", $this->getSkinData()), "Name" => new StringTag("Name", $this->getSkinId()) ]); } //Xp $this->namedtag->XpLevel = new IntTag("XpLevel", $this->getXpLevel()); $this->namedtag->XpTotal = new IntTag("XpTotal", $this->getTotalXp()); $this->namedtag->XpP = new FloatTag("XpP", $this->getXpProgress()); $this->namedtag->XpSeed = new IntTag("XpSeed", $this->getXpSeed()); //Food $this->namedtag->foodLevel = new IntTag("foodLevel", $this->getFood()); $this->namedtag->foodExhaustionLevel = new FloatTag("foodExhaustionLevel", $this->getExhaustion()); $this->namedtag->foodSaturationLevel = new FloatTag("foodSaturationLevel", $this->getSaturation()); $this->namedtag->foodTickTimer = new IntTag("foodTickTimer", $this->foodTickTimer); } public function spawnTo(Player $player){ if(strlen($this->skin) < 64 * 32 * 4){ $e = new \InvalidStateException((new \ReflectionClass($this))->getShortName() . " must have a valid skin set"); $this->server->getLogger()->logException($e); $this->close(); }elseif($player !== $this and !isset($this->hasSpawned[$player->getLoaderId()])){ $this->hasSpawned[$player->getLoaderId()] = $player; if(!($this instanceof Player)){ $this->server->updatePlayerListData($this->getUniqueId(), $this->getId(), $this->getName(), $this->skinId, $this->skin, [$player]); } $pk = new AddPlayerPacket(); $pk->uuid = $this->getUniqueId(); $pk->username = $this->getName(); $pk->eid = $this->getId(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->item = $this->getInventory()->getItemInHand(); $pk->metadata = $this->dataProperties; $player->dataPacket($pk); $this->sendLinkedData(); $this->inventory->sendArmorContents($player); if(!($this instanceof Player)){ $this->server->removePlayerListData($this->getUniqueId(), [$player]); } } } public function despawnFrom(Player $player){ if(isset($this->hasSpawned[$player->getLoaderId()])){ $pk = new RemoveEntityPacket(); $pk->eid = $this->getId(); $player->dataPacket($pk); unset($this->hasSpawned[$player->getLoaderId()]); } } public function close(){ if(!$this->closed){ if($this->getFloatingInventory() instanceof FloatingInventory){ foreach($this->getFloatingInventory()->getContents() as $craftingItem){ $this->level->dropItem($this, $craftingItem); } }else{ $this->server->getLogger()->debug("Attempted to drop a null crafting inventory\n"); } if(!($this instanceof Player) or $this->loggedIn){ foreach($this->inventory->getViewers() as $viewer){ $viewer->removeWindow($this->inventory); } } parent::close(); } } } ================================================ FILE: src/pocketmine/entity/Husk.php ================================================ eid = $this->getId(); $pk->type = Husk::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); Entity::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/InstantEffect.php ================================================ setMaxHealth(100); parent::initEntity(); } public function getName() { return "Iron Golem"; } public function spawnTo(Player $player) { $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = self::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } public function getDrops(){ //Not affected by Looting. $drops = array(ItemItem::get(ItemItem::IRON_INGOT, 0, mt_rand(3, 5))); $drops[] = ItemItem::get(ItemItem::POPPY, 0, mt_rand(0, 2)); return $drops; } } ================================================ FILE: src/pocketmine/entity/Item.php ================================================ setMaxHealth(5); $this->setHealth($this->namedtag["Health"]); if(isset($this->namedtag->Age)){ $this->age = $this->namedtag["Age"]; } if(isset($this->namedtag->PickupDelay)){ $this->pickupDelay = $this->namedtag["PickupDelay"]; } if(isset($this->namedtag->Owner)){ $this->owner = $this->namedtag["Owner"]; } if(isset($this->namedtag->Thrower)){ $this->thrower = $this->namedtag["Thrower"]; } if(!isset($this->namedtag->Item)){ $this->close(); return; } assert($this->namedtag->Item instanceof CompoundTag); $this->item = ItemItem::nbtDeserialize($this->namedtag->Item); $this->server->getPluginManager()->callEvent(new ItemSpawnEvent($this)); } public function attack($damage, EntityDamageEvent $source){ if( $source->getCause() === EntityDamageEvent::CAUSE_VOID or $source->getCause() === EntityDamageEvent::CAUSE_FIRE_TICK or $source->getCause() === EntityDamageEvent::CAUSE_ENTITY_EXPLOSION or $source->getCause() === EntityDamageEvent::CAUSE_BLOCK_EXPLOSION ){ parent::attack($damage, $source); } } public function onUpdate($currentTick){ if($this->closed){ return false; } $this->age++; $tickDiff = $currentTick - $this->lastUpdate; if($tickDiff <= 0 and !$this->justCreated){ return true; } $this->lastUpdate = $currentTick; $this->timings->startTiming(); $hasUpdate = $this->entityBaseTick($tickDiff); if($this->isAlive()){ if($this->pickupDelay > 0 and $this->pickupDelay < 32767){ //Infinite delay $this->pickupDelay -= $tickDiff; if($this->pickupDelay < 0){ $this->pickupDelay = 0; } } $this->motionY -= $this->gravity; if($this->checkObstruction($this->x, $this->y, $this->z)){ $hasUpdate = true; } $this->move($this->motionX, $this->motionY, $this->motionZ); $friction = 1 - $this->drag; if($this->onGround and (abs($this->motionX) > 0.00001 or abs($this->motionZ) > 0.00001)){ $friction = $this->getLevel()->getBlock($this->temporalVector->setComponents((int) floor($this->x), (int) floor($this->y - 1), (int) floor($this->z) - 1))->getFrictionFactor() * $friction; } $this->motionX *= $friction; $this->motionY *= 1 - $this->drag; $this->motionZ *= $friction; if($this->onGround){ $this->motionY *= -0.5; } if($currentTick % 5 ==0) $this->updateMovement(); if($this->age > 2000){ $this->server->getPluginManager()->callEvent($ev = new ItemDespawnEvent($this)); if($ev->isCancelled()){ $this->age = 0; }else{ $this->kill(); $hasUpdate = true; } } } $this->timings->stopTiming(); return $hasUpdate or !$this->onGround or abs($this->motionX) > 0.00001 or abs($this->motionY) > 0.00001 or abs($this->motionZ) > 0.00001; } public function saveNBT(){ parent::saveNBT(); $this->namedtag->Item = $this->item->nbtSerialize(-1, "Item"); $this->namedtag->Health = new ShortTag("Health", $this->getHealth()); $this->namedtag->Age = new ShortTag("Age", $this->age); $this->namedtag->PickupDelay = new ShortTag("PickupDelay", $this->pickupDelay); if($this->owner !== null){ $this->namedtag->Owner = new StringTag("Owner", $this->owner); } if($this->thrower !== null){ $this->namedtag->Thrower = new StringTag("Thrower", $this->thrower); } } /** * @return ItemItem */ public function getItem(){ return $this->item; } public function canCollideWith(Entity $entity){ return false; } /** * @return int */ public function getPickupDelay(){ return $this->pickupDelay; } /** * @param int $delay */ public function setPickupDelay($delay){ $this->pickupDelay = $delay; } /** * @return string */ public function getOwner(){ return $this->owner; } /** * @param string $owner */ public function setOwner($owner){ $this->owner = $owner; } /** * @return string */ public function getThrower(){ return $this->thrower; } /** * @param string $thrower */ public function setThrower($thrower){ $this->thrower = $thrower; } public function spawnTo(Player $player){ $pk = new AddItemEntityPacket(); $pk->eid = $this->getId(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->item = $this->getItem(); $player->dataPacket($pk); $this->sendData($player); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/LavaSlime.php ================================================ eid = $this->getId(); $pk->type = LavaSlime::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Lightning.php ================================================ setMaxHealth(2); $this->setHealth(2); } public function onUpdate($tick){ parent::onUpdate($tick); if($this->age > 20){ $this->kill(); $this->close(); } return true; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = self::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); $pk = new ExplodePacket(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->radius = 10; $pk->records = []; $player->dataPacket($pk); parent::spawnTo($player); } public function spawnToAll(){ parent::spawnToAll(); if($this->getLevel()->getServer()->lightningFire){ $fire = ItemItem::get(ItemItem::FIRE)->getBlock(); $oldBlock = $this->getLevel()->getBlock($this); if($oldBlock instanceof Liquid){ }elseif($oldBlock->isSolid()){ $v3 = new Vector3($this->x, $this->y + 1, $this->z); }else{ $v3 = new Vector3($this->x, $this->y, $this->z); } if(isset($v3)) $this->getLevel()->setBlock($v3, $fire); foreach($this->level->getNearbyEntities($this->boundingBox->grow(4, 3, 4), $this) as $entity){ if($entity instanceof Player){ $damage = mt_rand(8, 20); $ev = new EntityDamageByEntityEvent($this, $entity, EntityDamageByEntityEvent::CAUSE_LIGHTNING, $damage); if($entity->attack($ev->getFinalDamage(), $ev) === true){ $ev->useArmors(); } $entity->setOnFire(mt_rand(3, 8)); } if($entity instanceof Creeper){ $entity->setPowered(true, $this); } } } } } ================================================ FILE: src/pocketmine/entity/Living.php ================================================ namedtag->HealF)){ $this->namedtag->Health = new ShortTag("Health", (int) $this->namedtag["HealF"]); unset($this->namedtag->HealF); } if(!isset($this->namedtag->Health) or !($this->namedtag->Health instanceof ShortTag)){ $this->namedtag->Health = new ShortTag("Health", $this->getMaxHealth()); } if($this->namedtag["Health"] <= 0) $this->setHealth(20); else $this->setHealth($this->namedtag["Health"]); } public function setHealth($amount){ $wasAlive = $this->isAlive(); parent::setHealth($amount); if($this->isAlive() and !$wasAlive){ $pk = new EntityEventPacket(); $pk->eid = $this->getId(); $pk->event = EntityEventPacket::RESPAWN; $this->server->broadcastPacket($this->hasSpawned, $pk); } } public function saveNBT(){ parent::saveNBT(); $this->namedtag->Health = new ShortTag("Health", $this->getHealth()); } public abstract function getName(); public function hasLineOfSight(Entity $entity){ //TODO: head height return true; //return $this->getLevel()->rayTraceBlocks(Vector3::createVector($this->x, $this->y + $this->height, $this->z), Vector3::createVector($entity->x, $entity->y + $entity->height, $entity->z)) === null; } public function heal($amount, EntityRegainHealthEvent $source){ parent::heal($amount, $source); if($source->isCancelled()){ return; } $this->attackTime = 0; } public function attack($damage, EntityDamageEvent $source){ if($this->attackTime > 0 or $this->noDamageTicks > 0){ $lastCause = $this->getLastDamageCause(); if($lastCause !== null and $lastCause->getDamage() >= $damage){ $source->setCancelled(); } } parent::attack($damage, $source); if($source->isCancelled()){ return; } if($source instanceof EntityDamageByEntityEvent){ $e = $source->getDamager(); if($source instanceof EntityDamageByChildEntityEvent){ $e = $source->getChild(); } if($e->isOnFire() > 0 and !($e instanceof Player)){ $this->setOnFire(2 * $this->server->getDifficulty()); } $deltaX = $this->x - $e->x; $deltaZ = $this->z - $e->z; $this->knockBack($e, $damage, $deltaX, $deltaZ, $source->getKnockBack()); if($e instanceof Husk){ $this->addEffect(Effect::getEffect(Effect::HUNGER)->setDuration(7 * 20 * $this->server->getDifficulty())); } } $pk = new EntityEventPacket(); $pk->eid = $this->getId(); $pk->event = $this->getHealth() <= 0 ? EntityEventPacket::DEATH_ANIMATION : EntityEventPacket::HURT_ANIMATION; //Ouch! $this->server->broadcastPacket($this->hasSpawned, $pk); $this->attackTime = 10; //0.5 seconds cooldown } public function knockBack(Entity $attacker, $damage, $x, $z, $base = 0.4){ $f = sqrt($x * $x + $z * $z); if($f <= 0){ return; } $f = 1 / $f; $motion = new Vector3($this->motionX, $this->motionY, $this->motionZ); $motion->x /= 2; $motion->y /= 2; $motion->z /= 2; $motion->x += $x * $f * $base; $motion->y += $base; $motion->z += $z * $f * $base; if($motion->y > $base){ $motion->y = $base; } $this->setMotion($motion); } public function kill(){ if(!$this->isAlive()){ return; } parent::kill(); $this->server->getPluginManager()->callEvent($ev = new EntityDeathEvent($this, $this->getDrops())); foreach($ev->getDrops() as $item){ $this->getLevel()->dropItem($this, $item); } } public function entityBaseTick($tickDiff = 1, $EnchantL = 0){ Timings::$timerLivingEntityBaseTick->startTiming(); $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_BREATHING, !$this->isInsideOfWater()); $hasUpdate = parent::entityBaseTick($tickDiff); if($this->isAlive()){ if($this->isInsideOfSolid()){ $hasUpdate = true; $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_SUFFOCATION, 1); $this->attack($ev->getFinalDamage(), $ev); } if(!$this->hasEffect(Effect::WATER_BREATHING) and $this->isInsideOfWater()){ if($this instanceof WaterAnimal){ $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, 400); }else{ $hasUpdate = true; $airTicks = $this->getDataProperty(self::DATA_AIR) - $tickDiff * (4 - $EnchantL); if($airTicks <= -80){ $airTicks = 0; $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_DROWNING, 2); $this->attack($ev->getFinalDamage(), $ev); } $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, $airTicks); } }else{ if($this instanceof WaterAnimal){ $hasUpdate = true; $airTicks = $this->getDataProperty(self::DATA_AIR) - $tickDiff * (4 - $EnchantL); if($airTicks <= -80){ $airTicks = 0; $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_SUFFOCATION, 2); $this->attack($ev->getFinalDamage(), $ev); } $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, $airTicks); }else{ $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, 400); } } } if($this->attackTime > 0){ $this->attackTime -= $tickDiff; } Timings::$timerLivingEntityBaseTick->stopTiming(); return $hasUpdate; } /** * @return ItemItem[] */ public function getDrops(){ return []; } /** * @param int $maxDistance * @param int $maxLength * @param array $transparent * * @return Block[] */ public function getLineOfSight($maxDistance, $maxLength = 0, array $transparent = []){ if($maxDistance > 120){ $maxDistance = 120; } if(count($transparent) === 0){ $transparent = null; } $blocks = []; $nextIndex = 0; $itr = new BlockIterator($this->level, $this->getPosition(), $this->getDirectionVector(), $this->getEyeHeight(), $maxDistance); while($itr->valid()){ $itr->next(); $block = $itr->current(); $blocks[$nextIndex++] = $block; if($maxLength !== 0 and count($blocks) > $maxLength){ array_shift($blocks); --$nextIndex; } $id = $block->getId(); if($transparent === null){ if($id !== 0){ break; } }else{ if(!isset($transparent[$id])){ break; } } } return $blocks; } /** * @param int $maxDistance * @param array $transparent * * @return Block */ public function getTargetBlock($maxDistance, array $transparent = []){ try{ $block = $this->getLineOfSight($maxDistance, 1, $transparent)[0]; if($block instanceof Block){ return $block; } }catch (\ArrayOutOfBoundsException $e){ } return null; } } ================================================ FILE: src/pocketmine/entity/Minecart.php ================================================ setMaxHealth(1); $this->setHealth($this->getMaxHealth()); $this->moveVector[Entity::NORTH] = new Vector3(-1, 0, 0); $this->moveVector[Entity::SOUTH] = new Vector3(1, 0, 0); $this->moveVector[Entity::EAST] = new Vector3(0, 0, -1); $this->moveVector[Entity::WEST] = new Vector3(0, 0, 1); parent::initEntity(); } public function getName(){ return "Minecart"; } public function getType() : int{ return self::TYPE_NORMAL; } public function onUpdate($currentTick){ if($this->closed !== false){ return false; } $tickDiff = $currentTick - $this->lastUpdate; if($tickDiff <= 1){ return false; } $this->lastUpdate = $currentTick; $this->timings->startTiming(); $hasUpdate = false; //parent::onUpdate($currentTick); if($this->isAlive()){ $p = $this->getLinkedEntity(); if($p instanceof Player){ if($this->state === Minecart::STATE_INITIAL){ $this->checkIfOnRail(); }elseif($this->state === Minecart::STATE_ON_RAIL){ $hasUpdate = $this->forwardOnRail($p); $this->updateMovement(); } } } $this->timings->stopTiming (); return $hasUpdate or ! $this->onGround or abs ( $this->motionX ) > 0.00001 or abs ( $this->motionY ) > 0.00001 or abs ( $this->motionZ ) > 0.00001; } /** * Check if minecart is currently on a rail and if so center the cart. */ private function checkIfOnRail() { for ($y = -1; $y !== 2 and $this->state === Minecart::STATE_INITIAL; $y++) { $positionToCheck = $this->temporalVector->setComponents($this->x, $this->y + $y, $this->z); $block = $this->level->getBlock($positionToCheck); if($this->isRail($block)){ $minecartPosition = $positionToCheck->floor()->add(0.5, 0, 0.5); $this->setPosition($minecartPosition); // Move minecart to center of rail $this->state = Minecart::STATE_ON_RAIL; } } if($this->state !== Minecart::STATE_ON_RAIL){ $this->state = Minecart::STATE_OFF_RAIL; } } private function isRail($rail) { return ($rail !== null and in_array($rail->getId(), [Block::RAIL, Block::ACTIVATOR_RAIL, Block::DETECTOR_RAIL, Block::POWERED_RAIL])); } private function getCurrentRail() { $block = $this->getLevel()->getBlock($this); if($this->isRail($block)){ return $block; } // Rail could be one block below descending down $down = $this->temporalVector->setComponents($this->x, $this->y - 1, $this->z); $block = $this->getLevel()->getBlock($down); if($this->isRail($block)){ return $block; } return null; } /** * Attempt to move forward on rail given the direction the cart is already moving, or if not moving based * on the direction the player is looking. * @param Player $player Player riding the minecart. * @return boolean True if minecart moved, false otherwise. */ private function forwardOnRail(Player $player) { if($this->direction === -1){ $candidateDirection = $player->getDirection(); }else{ $candidateDirection = $this->direction; } $rail = $this->getCurrentRail(); if ($rail !== null) { $railType = $rail->getDamage (); $nextDirection = $this->getDirectionToMove($railType, $candidateDirection); if ($nextDirection !== -1) { $this->direction = $nextDirection; $moved = $this->checkForVertical($railType, $nextDirection); if(!$moved){ return $this->moveIfRail(); }else{ return true; } }else{ $this->direction = -1; // Was not able to determine direction to move, so wait for player to look in valid direction } }else{ // Not able to find rail $this->state = Minecart::STATE_INITIAL; } return false; } /** * Determine the direction the minecart should move based on the candidate direction (current direction * minecart is moving, or the direction the player is looking) and the type of rail that the minecart is * on. * @param RailType $railType Type of rail the minecart is on. * @param Direction $candidateDirection Direction minecart already moving, or direction player looking. * @return Direction The direction the minecart should move. */ private function getDirectionToMove($railType, $candidateDirection) { switch($railType){ case Rail::STRAIGHT_NORTH_SOUTH: case Rail::SLOPED_ASCENDING_NORTH: case Rail::SLOPED_ASCENDING_SOUTH: switch($candidateDirection){ case Entity::NORTH: case Entity::SOUTH: return $candidateDirection; } break; case Rail::STRAIGHT_EAST_WEST: case Rail::SLOPED_ASCENDING_EAST: case Rail::SLOPED_ASCENDING_WEST: switch($candidateDirection){ case Entity::WEST: case Entity::EAST: return $candidateDirection; } break; case Rail::CURVED_SOUTH_EAST: switch($candidateDirection){ case Entity::SOUTH: case Entity::EAST: return $candidateDirection; case Entity::NORTH: return $this->checkForTurn($candidateDirection, Entity::EAST); case Entity::WEST: return $this->checkForTurn($candidateDirection, Entity::SOUTH); } break; case Rail::CURVED_SOUTH_WEST: switch($candidateDirection){ case Entity::SOUTH: case Entity::WEST: return $candidateDirection; case Entity::NORTH: return $this->checkForTurn($candidateDirection, Entity::WEST); case Entity::EAST: return $this->checkForTurn($candidateDirection, Entity::SOUTH); } break; case Rail::CURVED_NORTH_WEST: switch ($candidateDirection) { case Entity::NORTH: case Entity::WEST: return $candidateDirection; case Entity::SOUTH: return $this->checkForTurn($candidateDirection, Entity::WEST); case Entity::EAST: return $this->checkForTurn($candidateDirection, Entity::NORTH); } break; case Rail::CURVED_NORTH_EAST: switch ($candidateDirection) { case Entity::NORTH: case Entity::EAST: return $candidateDirection; case Entity::SOUTH: return $this->checkForTurn($candidateDirection, Entity::EAST); case Entity::WEST: return $this->checkForTurn($candidateDirection, Entity::NORTH); } break; } return -1; } /** * Need to alter direction on curves halfway through the turn and reset the minecart to be in the middle of * the rail again so as not to collide with nearby blocks. * @param Direction $currentDirection Direction minecart currently moving * @param Direction $newDirection Direction minecart should turn once has hit the halfway point. * @return Direction Either the current direction or the new direction depending on haw far across the rail the * minecart is. */ private function checkForTurn($currentDirection, $newDirection) { switch($currentDirection) { case Entity::NORTH: $diff = $this->x - $this->getFloorX(); if ($diff !== 0 and $diff <= .5) { $dx = ($this->getFloorX() + .5) - $this->x; $this->move($dx, 0, 0); return $newDirection; } break; case Entity::SOUTH: $diff = $this->x - $this->getFloorX(); if ($diff !== 0 and $diff >= .5) { $dx = ($this->getFloorX() + .5) - $this->x; $this->move($dx, 0, 0); return $newDirection; } break; case Entity::EAST: $diff = $this->z - $this->getFloorZ(); if ($diff !== 0 and $diff <= .5) { $dz = ($this->getFloorZ() + .5) - $this->z; $this->move(0, 0, $dz); return $newDirection; } break; case Entity::WEST: $diff = $this->z - $this->getFloorZ(); if ($diff !== 0 and $diff >= .5) { $dz = $dz = ($this->getFloorZ() + .5) - $this->z; $this->move(0, 0, $dz); return $newDirection; } break; } return $currentDirection; } private function checkForVertical($railType, $currentDirection) { switch ($railType) { case Rail::SLOPED_ASCENDING_NORTH: switch($currentDirection){ case Entity::NORTH: // Headed north up $diff = $this->x - $this->getFloorX(); if ($diff !== 0 and $diff <= .5) { $dx = ($this->getFloorX() - .1) - $this->x; $this->move($dx, 1, 0); return true; } break; case ENTITY::SOUTH: // Headed south down $diff = $this->x - $this->getFloorX(); if ($diff !== 0 and $diff >= .5) { $dx = ($this->getFloorX() + 1 ) - $this->x; $this->move($dx, -1, 0); return true; } break; } break; case Rail::SLOPED_ASCENDING_SOUTH: switch($currentDirection){ case Entity::SOUTH: // Headed south up $diff = $this->x - $this->getFloorX(); if ($diff !== 0 and $diff >= .5) { $dx = ($this->getFloorX() + 1 ) - $this->x; $this->move($dx, 1, 0); return true; } break; case Entity::NORTH: // Headed north down $diff = $this->x - $this->getFloorX(); if ($diff !== 0 and $diff <= .5) { $dx = ($this->getFloorX() - .1) - $this->x; $this->move($dx, -1, 0); return true; } break; } break; case Rail::SLOPED_ASCENDING_EAST: switch($currentDirection){ case Entity::EAST: // Headed east up $diff = $this->z - $this->getFloorZ(); if ($diff !== 0 and $diff <= .5) { $dz = ($this->getFloorZ() - .1) - $this->z; $this->move(0, 1, $dz); return true; } break; case Entity::WEST: // Headed west down $diff = $this->z - $this->getFloorZ(); if ($diff !== 0 and $diff >= .5) { $dz = ($this->getFloorZ() + 1) - $this->z; $this->move(0, -1, $dz); return true; } break; } break; case Rail::SLOPED_ASCENDING_WEST: switch($currentDirection){ case Entity::WEST: // Headed west up $diff = $this->z - $this->getFloorZ(); if ($diff !== 0 and $diff >= .5) { $dz = ($this->getFloorZ() + 1) - $this->z; $this->move(0, 1, $dz); return true; } break; case Entity::EAST: // Headed east down $diff = $this->z - $this->getFloorZ(); if ($diff !== 0 and $diff <= .5) { $dz = ($this->getFloorZ() - .1) - $this->z; $this->move(0, -1, $dz); return true; } break; } break; } return false; } /** * Move the minecart as long as it will still be moving on to another piece of rail. * @return boolean True if the minecart moved. */ private function moveIfRail(){ $nextMoveVector = $this->moveVector[$this->direction]; $nextMoveVector = $nextMoveVector->multiply($this->moveSpeed); $newVector = $this->add($nextMoveVector->x, $nextMoveVector->y, $nextMoveVector->z); $possibleRail = $this->getCurrentRail(); if(in_array($possibleRail->getId(), [Block::RAIL, Block::ACTIVATOR_RAIL, Block::DETECTOR_RAIL, Block::POWERED_RAIL])) { $this->moveUsingVector($newVector); return true; } return false; } /** * Invoke the normal move code, but first need to convert the desired position vector into the * delta values from the current position. * @param Vector3 $desiredPosition */ private function moveUsingVector(Vector3 $desiredPosition){ $dx = $desiredPosition->x - $this->x; $dy = $desiredPosition->y - $this->y; $dz = $desiredPosition->z - $this->z; $this->move($dx, $dy, $dz); } /** * @return Rail */ public function getNearestRail(){ $minX = Math::floorFloat($this->boundingBox->minX); $minY = Math::floorFloat($this->boundingBox->minY); $minZ = Math::floorFloat($this->boundingBox->minZ); $maxX = Math::ceilFloat($this->boundingBox->maxX); $maxY = Math::ceilFloat($this->boundingBox->maxY); $maxZ = Math::ceilFloat($this->boundingBox->maxZ); $rails = []; for($z = $minZ; $z <= $maxZ; ++$z){ for($x = $minX; $x <= $maxX; ++$x){ for($y = $minY; $y <= $maxY; ++$y){ $block = $this->level->getBlock($this->temporalVector->setComponents($x, $y, $z)); if(in_array($block->getId(), [Block::RAIL, Block::ACTIVATOR_RAIL, Block::DETECTOR_RAIL, Block::POWERED_RAIL])) $rails[] = $block; } } } $minDistance = PHP_INT_MAX; $nearestRail = null; foreach($rails as $rail){ $dis = $this->distance($rail); if($dis < $minDistance){ $nearestRail = $rail; $minDistance = $dis; } } return $nearestRail; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = Minecart::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = 0; $pk->speedY = 0; $pk->speedZ = 0; $pk->yaw = 0; $pk->pitch = 0; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } /*public function attack($damage, EntityDamageEvent $source){ parent::attack($damage, $source); if(!$source->isCancelled()){ $pk = new EntityEventPacket(); $pk->eid = $this->id; $pk->event = EntityEventPacket::HURT_ANIMATION; foreach($this->getLevel()->getPlayers() as $player){ $player->dataPacket($pk); } } } public function getSaveId(){ $class = new \ReflectionClass(static::class); return $class->getShortName(); }*/ } ================================================ FILE: src/pocketmine/entity/MinecartChest.php ================================================ eid = $this->getId(); $pk->type = MinecartChest::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = 0; $pk->speedY = 0; $pk->speedZ = 0; $pk->yaw = 0; $pk->pitch = 0; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); Entity::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/MinecartHopper.php ================================================ eid = $this->getId(); $pk->type = MinecartHopper::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = 0; $pk->speedY = 0; $pk->speedZ = 0; $pk->yaw = 0; $pk->pitch = 0; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); Entity::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/MinecartTNT.php ================================================ eid = $this->getId(); $pk->type = MinecartTNT::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = 0; $pk->speedY = 0; $pk->speedZ = 0; $pk->yaw = 0; $pk->pitch = 0; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); Entity::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Monster.php ================================================ eid = $this->getId(); $pk->type = Mooshroom::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } public function getDrops(){ $lootingL = 0; $cause = $this->lastDamageCause; if($cause instanceof EntityDamageByEntityEvent and $cause->getDamager() instanceof Player){ $lootingL = $cause->getDamager()->getItemInHand()->getEnchantmentLevel(Enchantment::TYPE_WEAPON_LOOTING); } $drops = array(ItemItem::get(ItemItem::RAW_BEEF, 0, mt_rand(1, 3 + $lootingL))); $drops[] = ItemItem::get(ItemItem::LEATHER, 0, mt_rand(0, 2 + $lootingL)); return $drops; } } ================================================ FILE: src/pocketmine/entity/NPC.php ================================================ CatType)){ $nbt->CatType = new ByteTag("CatType", mt_rand(0, 3)); } parent::__construct($chunk, $nbt); $this->setDataProperty(self::DATA_CAT_TYPE, self::DATA_TYPE_BYTE, $this->getCatType()); } public function setCatType(int $type){ $this->namedtag->CatType = new ByteTag("CatType", $type); } public function getCatType() : int{ return (int) $this->namedtag["CatType"]; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = self::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Painting.php ================================================ setMaxHealth(1); parent::initEntity(); if(isset($this->namedtag->Motive)){ $this->motive = $this->namedtag["Motive"]; }else $this->close(); } public function attack($damage, EntityDamageEvent $source){ parent::attack($damage, $source); if($source->isCancelled()) return false; $this->level->addParticle(new DestroyBlockParticle($this->add(0.5), Block::get(Block::LADDER))); $this->kill(); return true; } public function spawnTo(Player $player){ $pk = new AddPaintingPacket(); $pk->eid = $this->getId(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->direction = $this->getDirection(); $pk->title = $this->motive; $player->dataPacket($pk); parent::spawnTo($player); } protected function updateMovement(){ //Nothing to update, paintings cannot move. } public function getDrops(){ return [ItemItem::get(ItemItem::PAINTING, 0, 1)]; } } ================================================ FILE: src/pocketmine/entity/Pig.php ================================================ eid = $this->getId(); $pk->type = Pig::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } public function getDrops(){ $lootingL = 0; $cause = $this->lastDamageCause; if($cause instanceof EntityDamageByEntityEvent and $cause->getDamager() instanceof Player){ $lootingL = $cause->getDamager()->getItemInHand()->getEnchantmentLevel(Enchantment::TYPE_WEAPON_LOOTING); } $drops = array(ItemItem::get(ItemItem::RAW_PORKCHOP, 0, mt_rand(1, 3 + $lootingL))); return $drops; } } ================================================ FILE: src/pocketmine/entity/PigZombie.php ================================================ eid = $this->getId(); $pk->type = PigZombie::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); $pk = new MobEquipmentPacket(); $pk->eid = $this->getId(); $pk->item = new ItemItem(283); $pk->slot = 0; $pk->selectedSlot = 0; $player->dataPacket($pk); } public function getDrops(){ $cause = $this->lastDamageCause; $drops = []; if($cause instanceof EntityDamageByEntityEvent and $cause->getDamager() instanceof Player){ $lootingL = $cause->getDamager()->getItemInHand()->getEnchantmentLevel(Enchantment::TYPE_WEAPON_LOOTING); if(mt_rand(1, 200) <= (5 + 2 * $lootingL)){ $drops[] = ItemItem::get(ItemItem::GOLD_INGOT, 0, 1); } $drops[] = ItemItem::get(ItemItem::GOLD_NUGGET, 0, mt_rand(0, 1 + $lootingL)); $drops[] = ItemItem::get(ItemItem::ROTTEN_FLESH, 0, mt_rand(0, 1 + $lootingL)); } return $drops; } } ================================================ FILE: src/pocketmine/entity/PolarBear.php ================================================ setMaxHealth(30); return "Polar Bear"; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = PolarBear::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } public function getDrops(){ $cause = $this->lastDamageCause; $drops = []; if($cause instanceof EntityDamageByEntityEvent and $cause->getDamager() instanceof Player){ $drops = []; if (mt_rand(1, 4) === 1) { $drops[] = ItemItem::get(ItemItem::RAW_SALMON, 0, mt_rand(0, 2));//yes.. 0,2 } else { $drops[] = ItemItem::get(ItemItem::RAW_FISH, 0, mt_rand(0, 2));//yes.. 0,2 } } return $drops; } } ================================================ FILE: src/pocketmine/entity/PrimedTNT.php ================================================ dropItem = $dropItem; } public function attack($damage, EntityDamageEvent $source){ if($source->getCause() === EntityDamageEvent::CAUSE_VOID){ parent::attack($damage, $source); } } protected function initEntity(){ parent::initEntity(); if(isset($this->namedtag->Fuse)){ $this->fuse = $this->namedtag["Fuse"]; }else{ $this->fuse = 80; } } public function canCollideWith(Entity $entity){ return false; } public function saveNBT(){ parent::saveNBT(); $this->namedtag->Fuse = new ByteTag("Fuse", $this->fuse); } public function onUpdate($currentTick){ if($this->closed){ return false; } $this->timings->startTiming(); $tickDiff = $currentTick - $this->lastUpdate; if($tickDiff <= 0 and !$this->justCreated){ return true; } $this->lastUpdate = $currentTick; $hasUpdate = $this->entityBaseTick($tickDiff); if($this->isAlive()){ $this->motionY -= $this->gravity; $this->move($this->motionX, $this->motionY, $this->motionZ); $friction = 1 - $this->drag; $this->motionX *= $friction; $this->motionY *= $friction; $this->motionZ *= $friction; $this->updateMovement(); if($this->onGround){ $this->motionY *= -0.5; $this->motionX *= 0.7; $this->motionZ *= 0.7; } $this->fuse -= $tickDiff; if($this->fuse <= 0){ $this->kill(); $this->explode(); } } return $hasUpdate or $this->fuse >= 0 or abs($this->motionX) > 0.00001 or abs($this->motionY) > 0.00001 or abs($this->motionZ) > 0.00001; } public function explode(){ $this->server->getPluginManager()->callEvent($ev = new ExplosionPrimeEvent($this, 4, $this->dropItem)); if(!$ev->isCancelled()){ $explosion = new Explosion($this, $ev->getForce(), $this, $ev->dropItem()); if($ev->isBlockBreaking()){ $explosion->explodeA(); } $explosion->explodeB(); } } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->type = PrimedTNT::NETWORK_ID; $pk->eid = $this->getId(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Projectile.php ================================================ shootingEntity = $shootingEntity; if($shootingEntity !== null){ $this->setDataProperty(self::DATA_SHOOTER_ID, self::DATA_TYPE_LONG, $shootingEntity->getId()); } parent::__construct($chunk, $nbt); } public function attack($damage, EntityDamageEvent $source){ if($source->getCause() === EntityDamageEvent::CAUSE_VOID){ parent::attack($damage, $source); } } protected function initEntity(){ parent::initEntity(); $this->setMaxHealth(1); $this->setHealth(1); if(isset($this->namedtag->Age)){ $this->age = $this->namedtag["Age"]; } } public function canCollideWith(Entity $entity){ return $entity instanceof Living and !$this->onGround; } public function saveNBT(){ parent::saveNBT(); $this->namedtag->Age = new ShortTag("Age", $this->age); } public function onUpdate($currentTick){ if($this->closed){ return false; } $tickDiff = $currentTick - $this->lastUpdate; if($tickDiff <= 0 and !$this->justCreated){ return true; } $this->lastUpdate = $currentTick; $hasUpdate = $this->entityBaseTick($tickDiff); if($this->isAlive()){ $movingObjectPosition = null; if(!$this->isCollided){ $this->motionY -= $this->gravity; } $moveVector = new Vector3($this->x + $this->motionX, $this->y + $this->motionY, $this->z + $this->motionZ); $list = $this->getLevel()->getCollidingEntities($this->boundingBox->addCoord($this->motionX, $this->motionY, $this->motionZ)->expand(1, 1, 1), $this); $nearDistance = PHP_INT_MAX; $nearEntity = null; foreach($list as $entity){ if(/*!$entity->canCollideWith($this) or */ ($entity === $this->shootingEntity and $this->ticksLived < 5) ){ continue; } $axisalignedbb = $entity->boundingBox->grow(0.3, 0.3, 0.3); $ob = $axisalignedbb->calculateIntercept($this, $moveVector); if($ob === null){ continue; } $distance = $this->distanceSquared($ob->hitVector); if($distance < $nearDistance){ $nearDistance = $distance; $nearEntity = $entity; } } if($nearEntity !== null){ $movingObjectPosition = MovingObjectPosition::fromEntity($nearEntity); } if($movingObjectPosition !== null){ if($movingObjectPosition->entityHit !== null){ $this->server->getPluginManager()->callEvent(new ProjectileHitEvent($this)); $motion = sqrt($this->motionX ** 2 + $this->motionY ** 2 + $this->motionZ ** 2); $damage = ceil($motion * $this->damage); if($this instanceof Arrow and $this->isCritical()){ $damage += mt_rand(0, (int) ($damage / 2) + 1); } if($this->shootingEntity === null){ $ev = new EntityDamageByEntityEvent($this, $movingObjectPosition->entityHit, EntityDamageEvent::CAUSE_PROJECTILE, $damage); }else{ $ev = new EntityDamageByChildEntityEvent($this->shootingEntity, $this, $movingObjectPosition->entityHit, EntityDamageEvent::CAUSE_PROJECTILE, $damage); } if($movingObjectPosition->entityHit->attack($ev->getFinalDamage(), $ev) === true){ if($this instanceof Arrow and $this->getPotionId() != 0){ foreach(Potion::getEffectsById($this->getPotionId() - 1) as $effect){ $movingObjectPosition->entityHit->addEffect($effect->setDuration($effect->getDuration() / 8)); } } $ev->useArmors(); } $this->hadCollision = true; if($this->fireTicks > 0){ $ev = new EntityCombustByEntityEvent($this, $movingObjectPosition->entityHit, 5); $this->server->getPluginManager()->callEvent($ev); if(!$ev->isCancelled()){ $movingObjectPosition->entityHit->setOnFire($ev->getDuration()); } } $this->kill(); return true; } } $this->move($this->motionX, $this->motionY, $this->motionZ); if($this->isCollided and !$this->hadCollision){ $this->hadCollision = true; $this->motionX = 0; $this->motionY = 0; $this->motionZ = 0; $this->server->getPluginManager()->callEvent(new ProjectileHitEvent($this)); }elseif(!$this->isCollided and $this->hadCollision){ $this->hadCollision = false; } if(!$this->onGround or abs($this->motionX) > 0.00001 or abs($this->motionY) > 0.00001 or abs($this->motionZ) > 0.00001){ $f = sqrt(($this->motionX ** 2) + ($this->motionZ ** 2)); $this->yaw = (atan2($this->motionX, $this->motionZ) * 180 / M_PI); $this->pitch = (atan2($this->motionY, $f) * 180 / M_PI); $hasUpdate = true; } $this->updateMovement(); } return $hasUpdate; } } ================================================ FILE: src/pocketmine/entity/ProjectileSource.php ================================================ setMaxHealth(3); parent::initEntity(); } public function __construct(Chunk $chunk, CompoundTag $nbt){ if(!isset($nbt->RabbitType)){ $nbt->RabbitType = new ByteTag("RabbitType", $this->getRandomRabbitType()); } parent::__construct($chunk, $nbt); $this->setDataProperty(self::DATA_RABBIT_TYPE, self::DATA_TYPE_BYTE, $this->getRabbitType()); } public function getRandomRabbitType() : int{ $arr = [0, 1, 2, 3, 4, 5, 99]; return $arr[mt_rand(0, count($arr) - 1)]; } public function setRabbitType(int $type){ $this->namedtag->RabbitType = new ByteTag("RabbitType", $type); } public function getRabbitType() : int{ return (int) $this->namedtag["RabbitType"]; } public function getName(){ return "Rabbit"; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = Rabbit::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } public function getDrops(){ $lootingL = 0; $cause = $this->lastDamageCause; if($cause instanceof EntityDamageByEntityEvent and $cause->getDamager() instanceof Player){ $lootingL = $cause->getDamager()->getItemInHand()->getEnchantmentLevel(Enchantment::TYPE_WEAPON_LOOTING); } $drops = [ItemItem::get(ItemItem::RABBIT_HIDE, 0, mt_rand(0, 1))]; if($this->getLastDamageCause() === EntityDamageEvent::CAUSE_FIRE){ $drops[] = ItemItem::get(ItemItem::COOKED_RABBIT, 0, mt_rand(0, 1)); }else{ $drops[] = ItemItem::get(ItemItem::RAW_RABBIT, 0, mt_rand(0, 1)); } //Rare drop if(mt_rand(1, 200) <= (5 + 2 * $lootingL)){ $drops[] = ItemItem::get(ItemItem::RABBIT_FOOT, 0, 1); } return $drops; } } ================================================ FILE: src/pocketmine/entity/Rideable.php ================================================ Color)){ $nbt->Color = new ByteTag("Color", self::getRandomColor()); } parent::__construct($chunk, $nbt); $this->setDataProperty(self::DATA_COLOR_INFO, self::DATA_TYPE_BYTE, $this->getColor()); } public static function getRandomColor() : int{ $rand = ""; $rand .= str_repeat(Wool::WHITE . " ", 20); $rand .= str_repeat(Wool::ORANGE . " ", 5); $rand .= str_repeat(Wool::MAGENTA . " ", 5); $rand .= str_repeat(Wool::LIGHT_BLUE . " ", 5); $rand .= str_repeat(Wool::YELLOW . " ", 5); $rand .= str_repeat(Wool::GRAY . " ", 10); $rand .= str_repeat(Wool::LIGHT_GRAY . " ", 10); $rand .= str_repeat(Wool::CYAN . " ", 5); $rand .= str_repeat(Wool::PURPLE . " ", 5); $rand .= str_repeat(Wool::BLUE . " ", 5); $rand .= str_repeat(Wool::BROWN . " ", 5); $rand .= str_repeat(Wool::GREEN . " ", 5); $rand .= str_repeat(Wool::RED . " ", 5); $rand .= str_repeat(Wool::BLACK . " ", 10); $arr = explode(" ", $rand); return intval($arr[mt_rand(0, count($arr) - 1)]); } public function getColor() : int{ return (int) $this->namedtag["Color"]; } public function setColor(int $color){ $this->namedtag->Color = new ByteTag("Color", $color); } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = Sheep::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } public function getDrops(){ $drops = [ ItemItem::get(ItemItem::WOOL, $this->getColor(), 1) ]; return $drops; } } ================================================ FILE: src/pocketmine/entity/Shulker.php ================================================ eid = $this->getId(); $pk->type = Shulker::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } public function getDrops(){ $drops = []; if ($this->lastDamageCause instanceof EntityDamageByEntityEvent and $this->lastDamageCause->getEntity() instanceof Player) { if (mt_rand(0, 1) === 1) $drops[] = ItemItem::get(ItemItem::SHULKER_SHELL, 0, 1); } return $drops; } } ================================================ FILE: src/pocketmine/entity/ShulkerBullet.php ================================================ closed){ return false; } $this->timings->startTiming(); $hasUpdate = parent::onUpdate($currentTick); if($this->age > 1200 or $this->isCollided){ $this->kill(); $hasUpdate = true; } $this->timings->stopTiming(); return $hasUpdate; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->type = ShulkerBullet::NETWORK_ID; $pk->eid = $this->getId(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Silverfish.php ================================================ eid = $this->getId(); $pk->type = Silverfish::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Skeleton.php ================================================ eid = $this->getId(); $pk->type = Skeleton::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); $pk = new MobEquipmentPacket(); $pk->eid = $this->getId(); $pk->item = new ItemItem(ItemItem::BOW); $pk->slot = 0; $pk->selectedSlot = 0; $player->dataPacket($pk); } } ================================================ FILE: src/pocketmine/entity/Slime.php ================================================ eid = $this->getId(); $pk->type = Slime::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } public function getDrops(){ $drops = array(ItemItem::get(ItemItem::SLIMEBALL, 0, 1)); if ($this->lastDamageCause instanceof EntityDamageByEntityEvent and $this->lastDamageCause->getEntity() instanceof Player) { if (\mt_rand(0, 199) < 5) { switch (\mt_rand(0, 2)) { case 0: $drops[] = ItemItem::get(ItemItem::IRON_INGOT, 0, 1); break; case 1: $drops[] = ItemItem::get(ItemItem::CARROT, 0, 1); break; case 2: $drops[] = ItemItem::get(ItemItem::POTATO, 0, 1); break; } } } return $drops; } } ================================================ FILE: src/pocketmine/entity/SnowGolem.php ================================================ setMaxHealth(4); parent::initEntity(); } public function getName() { return "Snow Golem"; } public function spawnTo(Player $player) { $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = self::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Snowball.php ================================================ closed){ return false; } $this->timings->startTiming(); $hasUpdate = parent::onUpdate($currentTick); if($this->age > 1200 or $this->isCollided){ $this->kill(); $hasUpdate = true; } $this->timings->stopTiming(); return $hasUpdate; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->type = Snowball::NETWORK_ID; $pk->eid = $this->getId(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Spider.php ================================================ eid = $this->getId(); $pk->type = Spider::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } public function getDrops(){ $drops = array(ItemItem::get(ItemItem::STRING, 0, 1)); if ($this->lastDamageCause instanceof EntityDamageByEntityEvent and $this->lastDamageCause->getEntity() instanceof Player) { if (mt_rand(0, 199) < 5) { switch (mt_rand(0, 2)) { case 0: $drops[] = ItemItem::get(ItemItem::IRON_INGOT, 0, 1); break; case 1: $drops[] = ItemItem::get(ItemItem::CARROT, 0, 1); break; case 2: $drops[] = ItemItem::get(ItemItem::POTATO, 0, 1); break; } } } return $drops; } } ================================================ FILE: src/pocketmine/entity/Squid.php ================================================ setMaxHealth(5); } public function getName(){ return "Squid"; } public function attack($damage, EntityDamageEvent $source){ parent::attack($damage, $source); if($source->isCancelled()){ return; } if($source instanceof EntityDamageByEntityEvent){ $this->swimSpeed = mt_rand(150, 350) / 2000; $e = $source->getDamager(); $this->swimDirection = (new Vector3($this->x - $e->x, $this->y - $e->y, $this->z - $e->z))->normalize(); $pk = new EntityEventPacket(); $pk->eid = $this->getId(); $pk->event = EntityEventPacket::SQUID_INK_CLOUD; $this->server->broadcastPacket($this->hasSpawned, $pk); } } private function generateRandomDirection(){ return new Vector3(mt_rand(-1000, 1000) / 1000, mt_rand(-500, 500) / 1000, mt_rand(-1000, 1000) / 1000); } public function onUpdate($currentTick){ if($this->closed !== false){ return false; } if(++$this->switchDirectionTicker === 100){ $this->switchDirectionTicker = 0; if(mt_rand(0, 100) < 50){ $this->swimDirection = null; } } $this->lastUpdate = $currentTick; $this->timings->startTiming(); $hasUpdate = parent::onUpdate($currentTick); if($this->isAlive()){ if($this->y > 62 and $this->swimDirection !== null){ $this->swimDirection->y = -0.5; } $inWater = $this->isInsideOfWater(); if(!$inWater){ $this->motionY -= $this->gravity; $this->swimDirection = null; }elseif($this->swimDirection !== null){ if($this->motionX ** 2 + $this->motionY ** 2 + $this->motionZ ** 2 <= $this->swimDirection->lengthSquared()){ $this->motionX = $this->swimDirection->x * $this->swimSpeed; $this->motionY = $this->swimDirection->y * $this->swimSpeed; $this->motionZ = $this->swimDirection->z * $this->swimSpeed; } }else{ $this->swimDirection = $this->generateRandomDirection(); $this->swimSpeed = mt_rand(50, 100) / 2000; } $expectedPos = new Vector3($this->x + $this->motionX, $this->y + $this->motionY, $this->z + $this->motionZ); $this->move($this->motionX, $this->motionY, $this->motionZ); if($expectedPos->distanceSquared($this) > 0){ $this->swimDirection = $this->generateRandomDirection(); $this->swimSpeed = mt_rand(50, 100) / 2000; } $friction = 1 - $this->drag; $this->motionX *= $friction; $this->motionY *= 1 - $this->drag; $this->motionZ *= $friction; $f = sqrt(($this->motionX ** 2) + ($this->motionZ ** 2)); $this->yaw = (-atan2($this->motionX, $this->motionZ) * 180 / M_PI); $this->pitch = (-atan2($f, $this->motionY) * 180 / M_PI); if($this->onGround){ $this->motionY *= -0.5; } } $this->timings->stopTiming(); return $hasUpdate or !$this->onGround or abs($this->motionX) > 0.00001 or abs($this->motionY) > 0.00001 or abs($this->motionZ) > 0.00001; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = Squid::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } public function getDrops(){ $lootingL = 0; $cause = $this->lastDamageCause; if($cause instanceof EntityDamageByEntityEvent and $cause->getDamager() instanceof Player){ $lootingL = $cause->getDamager()->getItemInHand()->getEnchantmentLevel(Enchantment::TYPE_WEAPON_LOOTING); } return [ ItemItem::get(ItemItem::DYE, 0, mt_rand(1, 3 + $lootingL)) ]; } } ================================================ FILE: src/pocketmine/entity/Stray.php ================================================ eid = $this->getId(); $pk->type = Stray::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); Entity::spawnTo($player); $pk = new MobEquipmentPacket(); $pk->eid = $this->getId(); $pk->item = new ItemItem(ItemItem::BOW); $pk->slot = 0; $pk->selectedSlot = 0; $player->dataPacket($pk); } } ================================================ FILE: src/pocketmine/entity/Tameable.php ================================================ closed){ return false; } $this->timings->startTiming(); $hasUpdate = parent::onUpdate($currentTick); $this->age++; if($this->age > 1200 or $this->isCollided){ $this->kill(); $this->close(); $this->getLevel()->addParticle(new SpellParticle($this, 46, 82, 153)); if($this->getLevel()->getServer()->expEnabled){ $this->getLevel()->spawnXPOrb($this->add(0, -0.2, 0), mt_rand(1, 4)); $this->getLevel()->spawnXPOrb($this->add(-0.1, -0.2, 0), mt_rand(1, 4)); $this->getLevel()->spawnXPOrb($this->add(0, -0.2, -0.1), mt_rand(1, 4)); } $hasUpdate = true; } $this->timings->stopTiming(); return $hasUpdate; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->type = ThrownExpBottle::NETWORK_ID; $pk->eid = $this->getId(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/ThrownPotion.php ================================================ PotionId)){ $nbt->PotionId = new ShortTag("PotionId", Potion::AWKWARD); } parent::__construct($chunk, $nbt, $shootingEntity); unset($this->dataProperties[self::DATA_SHOOTER_ID]); $this->setDataProperty(self::DATA_POTION_ID, self::DATA_TYPE_SHORT, $this->getPotionId()); } public function getPotionId() : int{ return (int) $this->namedtag["PotionId"]; } public function kill(){ if($this->isAlive()) { $color = Potion::getColor($this->getPotionId()); $this->getLevel()->addParticle(new SpellParticle($this, $color[0], $color[1], $color[2])); $players = $this->getViewers(); foreach($players as $p) { if($p->distance($this) <= 6) { foreach(Potion::getEffectsById($this->getPotionId()) as $effect) { $p->addEffect($effect); } } } parent::kill(); } } public function onUpdate($currentTick){ if($this->closed){ return false; } $this->timings->startTiming(); $hasUpdate = parent::onUpdate($currentTick); $this->age++; if($this->age > 1200 or $this->isCollided){ $this->kill(); $this->close(); $hasUpdate = true; } $this->timings->stopTiming(); return $hasUpdate; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->type = ThrownPotion::NETWORK_ID; $pk->eid = $this->getId(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Vehicle.php ================================================ Profession)){ $nbt->Profession = new ByteTag("Profession", mt_rand(0, 4)); } parent::__construct($chunk, $nbt); $this->setDataProperty(self::DATA_PROFESSION_ID, self::DATA_TYPE_BYTE, $this->getProfession()); } protected function initEntity(){ parent::initEntity(); if(!isset($this->namedtag->Profession)){ $this->setProfession(self::PROFESSION_FARMER); } } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = Villager::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } /** * Sets the villager profession * * @param int $profession */ public function setProfession(int $profession){ $this->namedtag->Profession = new ByteTag("Profession", $profession); } public function getProfession() : int{ $pro = (int) $this->namedtag["Profession"]; return min(4, max(0, $pro)); } public function isBaby(){ return $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_BABY); } } ================================================ FILE: src/pocketmine/entity/WaterAnimal.php ================================================ getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_BABY); } } ================================================ FILE: src/pocketmine/entity/Witch.php ================================================ setMaxHealth(26); parent::initEntity(); } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = Witch::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } public function getDrops(){ //TODO return []; } } ================================================ FILE: src/pocketmine/entity/WitherSkeleton.php ================================================ eid = $this->getId(); $pk->type = WitherSkeleton::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); $pk = new MobEquipmentPacket(); $pk->eid = $this->getId(); $pk->item = new ItemItem(ItemItem::STONE_SWORD); $pk->slot = 0; $pk->selectedSlot = 0; $player->dataPacket($pk); } } ================================================ FILE: src/pocketmine/entity/Wolf.php ================================================ eid = $this->getId(); $pk->type = Wolf::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/XPOrb.php ================================================ namedtag->Experience)){ $this->experience = $this->namedtag["Experience"]; }else $this->close(); } public function onUpdate($currentTick){ if($this->closed){ return false; } $tickDiff = $currentTick - $this->lastUpdate; $this->lastUpdate = $currentTick; $this->timings->startTiming(); $hasUpdate = $this->entityBaseTick($tickDiff); $this->age++; if($this->age > 1200){ $this->kill(); $this->close(); $hasUpdate = true; } $minDistance = PHP_INT_MAX; $target = null; foreach($this->getViewers() as $p){ if(!$p->isSpectator() and $p->isAlive()){ if(($dist = $p->distance($this)) < $minDistance and $dist < $this->range){ $target = $p; $minDistance = $dist; } } } if($target !== null){ $moveSpeed = 0.7; $motX = ($target->getX() - $this->x) / 8; $motY = ($target->getY() + $target->getEyeHeight() - $this->y) / 8; $motZ = ($target->getZ() - $this->z) / 8; $motSqrt = sqrt($motX * $motX + $motY * $motY + $motZ * $motZ); $motC = 1 - $motSqrt; if($motC > 0){ $motC *= $motC; $this->motionX = $motX / $motSqrt * $motC * $moveSpeed; $this->motionY = $motY / $motSqrt * $motC * $moveSpeed; $this->motionZ = $motZ / $motSqrt * $motC * $moveSpeed; } $this->motionY -= $this->gravity; if($this->checkObstruction($this->x, $this->y, $this->z)){ $hasUpdate = true; } if($this->isInsideOfSolid()){ $this->setPosition($target); } if($minDistance <= 1.3){ if($this->getLevel()->getServer()->expEnabled and $target->canPickupXp()){ $this->getLevel()->getServer()->getPluginManager()->callEvent($ev = new PlayerPickupExpOrbEvent($target, $this->getExperience())); if(!$ev->isCancelled()){ $this->kill(); $this->close(); if($this->getExperience() > 0){ $this->level->addSound(new ExpPickupSound($target, mt_rand(0, 1000))); $target->addXp($this->getExperience()); $target->resetXpCooldown(); } } } } } $this->move($this->motionX, $this->motionY, $this->motionZ); $this->updateMovement(); $this->timings->stopTiming(); return $hasUpdate or !$this->onGround or abs($this->motionX) > 0.00001 or abs($this->motionY) > 0.00001 or abs($this->motionZ) > 0.00001; } public function canCollideWith(Entity $entity){ return false; } public function setExperience($exp){ $this->experience = $exp; } public function getExperience(){ return $this->experience; } public function spawnTo(Player $player){ $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_NO_AI, true); $pk = new AddEntityPacket(); $pk->type = XPOrb::NETWORK_ID; $pk->eid = $this->getId(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/entity/Zombie.php ================================================ setMaxHealth(20); parent::initEntity(); } public function attack($damage, EntityDamageEvent $source){ parent::attack($damage, $source); if($source instanceof EntityDamageByEntityEvent){ $e = $source->getDamager(); $deltaX = $this->x - $e->x; $deltaZ = $this->z - $e->z; $this->knockBack($e, $damage, $deltaX / 100, $deltaZ / 100, $source->getKnockBack()); } } private function generateRandomDirection(){ return new Vector3(mt_rand(-1000, 1000) / 1000, 0, mt_rand(-1000, 1000) / 1000); } /* * 返回一个一位小数 */ private function toFloat($num){ while(((abs($num) > 1) or (abs($num) < 0.1)) and (abs($num) > 0)) { if(abs($num) > 1) $num /= 10; if(abs($num) < 0.1) $num *= 10; } return $num; } private function generateDirection(Vector3 $pos){ return new Vector3($this->toFloat($pos->x - $this->x), 0, $this->toFloat($pos->z - $this->z)); } private function getNearestPlayer(){ $dis = PHP_INT_MAX; $player = false; foreach($this->getViewers() as $p){ if($p->distance($this) < $dis){ $dis = $p->distance($this); $player = $p; } } return (($dis <= $this->hate_r) ? $p : false); } private function getVelY(){ $expectedPos = (new Vector3($this->x + $this->moveDirection->x * $this->moveSpeed, $this->y + $this->motionY, $this->z + $this->moveDirection->z * $this->moveSpeed))->round(); $block0 = $this->getLevel()->getBlock($expectedPos); $block1 = $this->getLevel()->getBlock($expectedPos->add(0, 1, 0)); if($block1->getId() != 0) return 1.2; return 0; } public function onUpdate($currentTick){ if($this->closed !== false){ return false; } return parent::onUpdate($currentTick); //Abort AI? $this->lastUpdate = $currentTick; $this->timings->startTiming(); $hasUpdate = parent::onUpdate($currentTick); if($this->isAlive()){ /* Don't use time directly * Instead, get remainder of current time divided by 24,000 * This tells us the time of day, which is what we really need */ $timeOfDay = abs($this->getLevel()->getTime() % 24000); if(0 < $timeOfDay and $timeOfDay < 13000) $this->setOnFire(2); //僵尸起火 $p = $this->getNearestPlayer();//找到最近的可以被仇恨的玩家 if(!$p) { $this->hated = false; if(++$this->moveTicker >= 100) { $this->moveDirection = $this->generateRandomDirection(); $this->moveTicker = 0; } }else{ $this->hated = $p; if($p->distance($this) <= $this->fire_r) $p->setOnFire(2); //点燃玩家 if(!$this->tempTicking){ if(++$this->hateTicker >= 10 or $this->moveDirection == null) { //每0.5秒获取僵尸前进的新方向 $this->moveDirection = $this->generateDirection($p); $this->hateTicker = 0; } } } if($this->tempTicking) { //帮助僵尸寻找新的方向走出困境 if(++$this->tempTicker >= 20) { $this->tempTicking = false; $this->tempTicker = 0; } } if($this->hated instanceof Player){ //攻击玩家 if($this->hated->distance($this) < $this->attack_r) $this->hated->attack(2, new EntityDamageByEntityEvent($this, $this->hated, EntityDamageEvent::CAUSE_ENTITY_ATTACK, 2)); } if($this->moveDirection != null){ if($this->motionX ** 2 + $this->motionZ ** 2 <= $this->moveDirection->lengthSquared()){ $motionY = $this->getVelY(); //僵尸运动计算 if($motionY >= 0){ $this->motionX = $this->moveDirection->x * $this->moveSpeed; $this->motionZ = $this->moveDirection->z * $this->moveSpeed; $this->motionY = $motionY; }else{ $this->moveDirection = $this->generateRandomDirection(); //生成随机运动方向 $this->moveTicker = 0; $this->tempTicking = true; } } }else{ $this->moveDirection = $this->generateRandomDirection(); $this->moveTicker = 0; } //var_dump($this->moveDirection,$this->motionX,$this->motionZ); $expectedPos = new Vector3($this->x + $this->motionX, $this->y + $this->motionY, $this->z + $this->motionZ); if($this->motionY == 0) $this->motionY -= $this->gravity; //重力计算 $this->move($this->motionX, $this->motionY, $this->motionZ); if($expectedPos->distanceSquared($this) > 0){ $this->moveDirection = $this->generateRandomDirection(); } $friction = 1 - $this->drag; $this->motionX *= $friction; //$this->motionY *= 1 - $this->drag; $this->motionZ *= $friction; $f = sqrt(($this->motionX ** 2) + ($this->motionZ ** 2)); $this->yaw = (-atan2($this->motionX, $this->motionZ) * 180 / M_PI); //视角计算 //$this->pitch = (-atan2($f, $this->motionY) * 180 / M_PI); $this->updateMovement(); } $this->timings->stopTiming(); return $hasUpdate or !$this->onGround or abs($this->motionX) > 0.00001 or abs($this->motionY) > 0.00001 or abs($this->motionZ) > 0.00001; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); $pk->type = Zombie::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } public function getDrops(){ $lootingL = 0; $cause = $this->lastDamageCause; $drops = []; if($cause instanceof EntityDamageByEntityEvent and $cause->getDamager() instanceof Player){ $lootingL = $cause->getDamager()->getItemInHand()->getEnchantmentLevel(Enchantment::TYPE_WEAPON_LOOTING); if(mt_rand(0, 199) < (5 + 2 * $lootingL)){ switch(mt_rand(0, 3)){ case 0: $drops[] = ItemItem::get(ItemItem::IRON_INGOT, 0, 1); break; case 1: $drops[] = ItemItem::get(ItemItem::CARROT, 0, 1); break; case 2: $drops[] = ItemItem::get(ItemItem::POTATO, 0, 1); break; } } $count = mt_rand(0, 2 + $lootingL); if($count > 0){ $drops[] = ItemItem::get(ItemItem::ROTTEN_FLESH, 0, $count); } } return $drops; } } ================================================ FILE: src/pocketmine/entity/ZombieVillager.php ================================================ setMaxHealth(20); parent::initEntity(); } public function getName(){ return "Zombie Villager"; } public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->type = ZombieVillager::NETWORK_ID; $pk->eid = $this->getId(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->speedX = $this->motionX; $pk->speedY = $this->motionY; $pk->speedZ = $this->motionZ; $pk->metadata = $this->dataProperties; $player->dataPacket($pk); parent::spawnTo($player); } } ================================================ FILE: src/pocketmine/event/Cancellable.php ================================================ eventName === null ? get_class($this) : $this->eventName; } /** * @return bool * * @throws \BadMethodCallException */ public function isCancelled(){ if(!($this instanceof Cancellable)){ throw new \BadMethodCallException("Event is not Cancellable"); } /** @var Event $this */ return $this->isCancelled === true; } /** * @param bool $value * * @return bool * * @throws \BadMethodCallException */ public function setCancelled($value = true){ if(!($this instanceof Cancellable)){ throw new \BadMethodCallException("Event is not Cancellable"); } /** @var Event $this */ $this->isCancelled = (bool) $value; return (bool) $value; } /** * @return HandlerList */ public function getHandlers(){ if(static::$handlerList === null){ static::$handlerList = new HandlerList(); } return static::$handlerList; } } ================================================ FILE: src/pocketmine/event/EventPriority.php ================================================ LOW -> NORMAL -> HIGH -> HIGHEST -> MONITOR * * MONITOR events should not change the event outcome or contents */ abstract class EventPriority{ /** * Event call is of very low importance and should be ran first, to allow * other plugins to further customise the outcome */ const LOWEST = 5; /** * Event call is of low importance */ const LOW = 4; /** * Event call is neither important or unimportant, and may be ran normally */ const NORMAL = 3; /** * Event call is of high importance */ const HIGH = 2; /** * Event call is critical and must have the final say in what happens * to the event */ const HIGHEST = 1; /** * Event is listened to purely for monitoring the outcome of an event. * * No modifications to the event should be made under this priority */ const MONITOR = 0; } ================================================ FILE: src/pocketmine/event/HandlerList.php ================================================ bake(); } } /** * Unregisters all the listeners * If a Plugin or Listener is passed, all the listeners with that object will be removed * * @param Plugin|Listener|null $object */ public static function unregisterAll($object = null){ if($object instanceof Listener or $object instanceof Plugin){ foreach(self::$allLists as $h){ $h->unregister($object); } }else{ foreach(self::$allLists as $h){ foreach($h->handlerSlots as $key => $list){ $h->handlerSlots[$key] = []; } $h->handlers = null; } } } public function __construct(){ $this->handlerSlots = [ EventPriority::LOWEST => [], EventPriority::LOW => [], EventPriority::NORMAL => [], EventPriority::HIGH => [], EventPriority::HIGHEST => [], EventPriority::MONITOR => [] ]; self::$allLists[] = $this; } /** * @param RegisteredListener $listener * * @throws \Throwable */ public function register(RegisteredListener $listener){ if($listener->getPriority() < EventPriority::MONITOR or $listener->getPriority() > EventPriority::LOWEST){ return; } if(isset($this->handlerSlots[$listener->getPriority()][spl_object_hash($listener)])){ throw new \InvalidStateException("This listener is already registered to priority " . $listener->getPriority()); } $this->handlers = null; $this->handlerSlots[$listener->getPriority()][spl_object_hash($listener)] = $listener; } /** * @param RegisteredListener[] $listeners */ public function registerAll(array $listeners){ foreach($listeners as $listener){ $this->register($listener); } } /** * @param RegisteredListener|Listener|Plugin $object */ public function unregister($object){ if($object instanceof Plugin or $object instanceof Listener){ $changed = false; foreach($this->handlerSlots as $priority => $list){ foreach($list as $hash => $listener){ if(($object instanceof Plugin and $listener->getPlugin() === $object) or ($object instanceof Listener and $listener->getListener() === $object) ){ unset($this->handlerSlots[$priority][$hash]); $changed = true; } } } if($changed === true){ $this->handlers = null; } }elseif($object instanceof RegisteredListener){ if(isset($this->handlerSlots[$object->getPriority()][spl_object_hash($object)])){ unset($this->handlerSlots[$object->getPriority()][spl_object_hash($object)]); $this->handlers = null; } } } public function bake(){ if($this->handlers !== null){ return; } $entries = []; foreach($this->handlerSlots as $list){ foreach($list as $hash => $listener){ $entries[$hash] = $listener; } } $this->handlers = $entries; } /** * @param null|Plugin $plugin * * @return RegisteredListener[] */ public function getRegisteredListeners($plugin = null){ if($plugin !== null){ $listeners = []; foreach($this->getRegisteredListeners(null) as $hash => $listener){ if($listener->getPlugin() === $plugin){ $listeners[$hash] = $plugin; } } return $listeners; }else{ while(($handlers = $this->handlers) === null){ $this->bake(); } return $handlers; } } /** * @return HandlerList[] */ public static function getHandlerLists(){ return self::$allLists; } } ================================================ FILE: src/pocketmine/event/LevelTimings.php ================================================ getFolderName() . " - "; $this->mobSpawn = new TimingsHandler("** " . $name . "mobSpawn"); $this->doChunkUnload = new TimingsHandler("** " . $name . "doChunkUnload"); $this->doTickPending = new TimingsHandler("** " . $name . "doTickPending"); $this->doTickTiles = new TimingsHandler("** " . $name . "doTickTiles"); $this->doVillages = new TimingsHandler("** " . $name . "doVillages"); $this->doChunkMap = new TimingsHandler("** " . $name . "doChunkMap"); $this->doSounds = new TimingsHandler("** " . $name . "doSounds"); $this->doChunkGC = new TimingsHandler("** " . $name . "doChunkGC"); $this->doPortalForcer = new TimingsHandler("** " . $name . "doPortalForcer"); $this->entityTick = new TimingsHandler("** " . $name . "entityTick"); $this->tileEntityTick = new TimingsHandler("** " . $name . "tileEntityTick"); $this->tileEntityPending = new TimingsHandler("** " . $name . "tileEntityPending"); $this->syncChunkSendTimer = new TimingsHandler("** " . $name . "syncChunkSend"); $this->syncChunkSendPrepareTimer = new TimingsHandler("** " . $name . "syncChunkSendPrepare"); $this->syncChunkLoadTimer = new TimingsHandler("** " . $name . "syncChunkLoad"); $this->syncChunkLoadDataTimer = new TimingsHandler("** " . $name . "syncChunkLoad - Data"); $this->syncChunkLoadStructuresTimer = new TimingsHandler("** " . $name . "syncChunkLoad - Structures"); $this->syncChunkLoadEntitiesTimer = new TimingsHandler("** " . $name . "syncChunkLoad - Entities"); $this->syncChunkLoadTileEntitiesTimer = new TimingsHandler("** " . $name . "syncChunkLoad - TileEntities"); $this->syncChunkLoadTileTicksTimer = new TimingsHandler("** " . $name . "syncChunkLoad - TileTicks"); $this->syncChunkLoadPostTimer = new TimingsHandler("** " . $name . "syncChunkLoad - Post"); $this->tracker = new TimingsHandler($name . "tracker"); $this->doTick = new TimingsHandler($name . "doTick"); $this->tickEntities = new TimingsHandler($name . "tickEntities"); } } ================================================ FILE: src/pocketmine/event/Listener.php ================================================ text = $text; } public function setText($text){ $this->text = $text; } /** * @return string */ public function getText(){ return $this->text; } /** * @return string */ public function __toString(){ return $this->getText(); } } ================================================ FILE: src/pocketmine/event/Timings.php ================================================ getTask(); if($ftask instanceof PluginTask and $ftask->getOwner() !== null){ $plugin = $ftask->getOwner()->getDescription()->getFullName(); }elseif($task->timingName !== null){ $plugin = "Scheduler"; }else{ $plugin = "Unknown"; } $taskname = $task->getTaskName(); $name = "Task: " . $plugin . " Runnable: " . $taskname; if($period > 0){ $name .= "(interval:" . $period . ")"; }else{ $name .= "(Single)"; } if(!isset(self::$pluginTaskTimingMap[$name])){ self::$pluginTaskTimingMap[$name] = new TimingsHandler($name, self::$schedulerSyncTimer); } return self::$pluginTaskTimingMap[$name]; } /** * @param Entity $entity * * @return TimingsHandler */ public static function getEntityTimings(Entity $entity){ $entityType = (new \ReflectionClass($entity))->getShortName(); if(!isset(self::$entityTypeTimingMap[$entityType])){ if($entity instanceof Player){ self::$entityTypeTimingMap[$entityType] = new TimingsHandler("** tickEntity - EntityPlayer", self::$tickEntityTimer); }else{ self::$entityTypeTimingMap[$entityType] = new TimingsHandler("** tickEntity - " . $entityType, self::$tickEntityTimer); } } return self::$entityTypeTimingMap[$entityType]; } /** * @param Tile $tile * * @return TimingsHandler */ public static function getTileEntityTimings(Tile $tile){ $tileType = (new \ReflectionClass($tile))->getShortName(); if(!isset(self::$tileEntityTypeTimingMap[$tileType])){ self::$tileEntityTypeTimingMap[$tileType] = new TimingsHandler("** tickTileEntity - " . $tileType, self::$tickTileEntityTimer); } return self::$tileEntityTypeTimingMap[$tileType]; } /** * @param DataPacket $pk * * @return TimingsHandler */ public static function getReceiveDataPacketTimings(DataPacket $pk){ if(!isset(self::$packetReceiveTimingMap[$pk::NETWORK_ID])){ $pkName = (new \ReflectionClass($pk))->getShortName(); self::$packetReceiveTimingMap[$pk::NETWORK_ID] = new TimingsHandler("** receivePacket - " . $pkName . " [0x" . dechex($pk::NETWORK_ID) . "]", self::$playerNetworkReceiveTimer); } return self::$packetReceiveTimingMap[$pk::NETWORK_ID]; } /** * @param DataPacket $pk * * @return TimingsHandler */ public static function getSendDataPacketTimings(DataPacket $pk){ if(!isset(self::$packetSendTimingMap[$pk::NETWORK_ID])){ $pkName = (new \ReflectionClass($pk))->getShortName(); self::$packetSendTimingMap[$pk::NETWORK_ID] = new TimingsHandler("** sendPacket - " . $pkName . " [0x" . dechex($pk::NETWORK_ID) . "]", self::$playerNetworkTimer); } return self::$packetSendTimingMap[$pk::NETWORK_ID]; } } ================================================ FILE: src/pocketmine/event/TimingsHandler.php ================================================ name = $name; if($parent !== null){ $this->parent = $parent; } self::$HANDLERS[spl_object_hash($this)] = $this; } public static function printTimings($fp){ fwrite($fp, "Minecraft" . PHP_EOL); foreach(self::$HANDLERS as $timings){ $time = $timings->totalTime; $count = $timings->count; if($count === 0){ continue; } $avg = $time / $count; fwrite($fp, " " . $timings->name . " Time: " . round($time * 1000000000) . " Count: " . $count . " Avg: " . round($avg * 1000000000) . " Violations: " . $timings->violations . PHP_EOL); } fwrite($fp, "# Version " . Server::getInstance()->getVersion() . PHP_EOL); fwrite($fp, "# " . Server::getInstance()->getName() . " " . Server::getInstance()->getPocketMineVersion() . PHP_EOL); $entities = 0; $livingEntities = 0; foreach(Server::getInstance()->getLevels() as $level){ $entities += count($level->getEntities()); foreach($level->getEntities() as $e){ if($e instanceof Living){ ++$livingEntities; } } } fwrite($fp, "# Entities " . $entities . PHP_EOL); fwrite($fp, "# LivingEntities " . $livingEntities . PHP_EOL); } public static function reload(){ if(Server::getInstance()->getPluginManager()->useTimings()){ foreach(self::$HANDLERS as $timings){ $timings->reset(); } TimingsCommand::$timingStart = microtime(true); } } public static function tick($measure = true){ if(PluginManager::$useTimings){ if($measure){ foreach(self::$HANDLERS as $timings){ if($timings->curTickTotal > 0.05){ $timings->violations += round($timings->curTickTotal / 0.05); } $timings->curTickTotal = 0; $timings->curCount = 0; $timings->timingDepth = 0; } }else{ foreach(self::$HANDLERS as $timings){ $timings->totalTime -= $timings->curTickTotal; $timings->count -= $timings->curCount; $timings->curTickTotal = 0; $timings->curCount = 0; $timings->timingDepth = 0; } } } } public function startTiming(){ if(PluginManager::$useTimings and ++$this->timingDepth === 1){ $this->start = microtime(true); if($this->parent !== null and ++$this->parent->timingDepth === 1){ $this->parent->start = $this->start; } } } public function stopTiming(){ if(PluginManager::$useTimings){ if(--$this->timingDepth !== 0 or $this->start === 0){ return; } $diff = microtime(true) - $this->start; $this->totalTime += $diff; $this->curTickTotal += $diff; ++$this->curCount; ++$this->count; $this->start = 0; if($this->parent !== null){ $this->parent->stopTiming(); } } } public function reset(){ $this->count = 0; $this->curCount = 0; $this->violations = 0; $this->curTickTotal = 0; $this->totalTime = 0; $this->start = 0; $this->timingDepth = 0; } public function remove(){ unset(self::$HANDLERS[spl_object_hash($this)]); } } ================================================ FILE: src/pocketmine/event/TranslationContainer.php ================================================ setParameters($params); } /** * @return string[] */ public function getParameters(){ return $this->params; } /** * @param int $i * * @return string */ public function getParameter($i){ return isset($this->params[$i]) ? $this->params[$i] : null; } /** * @param int $i * @param string $str */ public function setParameter($i, $str){ if($i < 0 or $i > count($this->params)){ //Intended, allow to set the last throw new \InvalidArgumentException("Invalid index $i, have " . count($this->params)); } $this->params[(int) $i] = $str; } /** * @param string[] $params */ public function setParameters(array $params){ $i = 0; foreach($params as $str){ $this->params[$i] = (string) $str; ++$i; } } } ================================================ FILE: src/pocketmine/event/block/BlockBreakEvent.php ================================================ block = $block; $this->item = $item; $this->player = $player; $this->instaBreak = (bool)$instaBreak; $drops = $player->isSurvival() ? $block->getDrops($item) : []; if($drops != null && is_numeric($drops[0])) $this->blockDrops[] = Item::get($drops[0], $drops[1], $drops[2]); else foreach($drops as $i){ $this->blockDrops[] = Item::get($i[0], $i[1], $i[2]); } } public function getPlayer(){ return $this->player; } public function getItem(){ return $this->item; } public function getInstaBreak(){ return $this->instaBreak; } /** * @return Item[] */ public function getDrops(){ return $this->blockDrops; } /** * @param Item[] $drops */ public function setDrops(array $drops){ $this->blockDrops = $drops; } /** * @param boolean $instaBreak */ public function setInstaBreak($instaBreak){ $this->instaBreak = (bool)$instaBreak; } } ================================================ FILE: src/pocketmine/event/block/BlockBurnEvent.php ================================================ block = $block; } /** * @return Block */ public function getBlock(){ return $this->block; } } ================================================ FILE: src/pocketmine/event/block/BlockFormEvent.php ================================================ newState = $newState; } /** * @return Block */ public function getNewState(){ return $this->newState; } } ================================================ FILE: src/pocketmine/event/block/BlockPlaceEvent.php ================================================ block = $blockPlace; $this->blockReplace = $blockReplace; $this->blockAgainst = $blockAgainst; $this->item = $item; $this->player = $player; } public function getPlayer(){ return $this->player; } /** * Gets the item in hand * * @return mixed */ public function getItem(){ return $this->item; } public function getBlockReplaced(){ return $this->blockReplace; } public function getBlockAgainst(){ return $this->blockAgainst; } } ================================================ FILE: src/pocketmine/event/block/BlockSpreadEvent.php ================================================ source = $source; } /** * @return Block */ public function getSource(){ return $this->source; } } ================================================ FILE: src/pocketmine/event/block/BlockUpdateEvent.php ================================================ player = $player; $this->block = $block; $this->itemFrame = $itemFrame; $this->item = $item; } public function getPlayer(){ return $this->player; } public function getItemFrame(){ return $this->itemFrame; } public function getItem(){ return $this->item; } } ================================================ FILE: src/pocketmine/event/block/LeavesDecayEvent.php ================================================ player = $thePlayer; $this->lines = $theLines; } /** * @return Player */ public function getPlayer(){ return $this->player; } /** * @return string[] */ public function getLines(){ return $this->lines; } /** * @param int $index 0-3 * * @return string */ public function getLine($index){ return $this->lines[$index]; } /** * @param int $index 0-3 * @param string $line */ public function setLine($index, $line){ $this->lines[$index] = $line; } } ================================================ FILE: src/pocketmine/event/entity/CreeperPowerEvent.php ================================================ entity = $creeper; $this->lightning = $lightning; $this->cause = $cause; } public function getLightning(){ return $this->lightning; } public function getCause(){ return $this->cause; } } ================================================ FILE: src/pocketmine/event/entity/EntityArmorChangeEvent.php ================================================ entity = $entity; $this->oldItem = $oldItem; $this->newItem = $newItem; $this->slot = (int) $slot; } public function getSlot(){ return $this->slot; } public function getNewItem(){ return $this->newItem; } public function setNewItem(Item $item){ $this->newItem = $item; } public function getOldItem(){ return $this->oldItem; } } ================================================ FILE: src/pocketmine/event/entity/EntityBlockChangeEvent.php ================================================ entity = $entity; $this->from = $from; $this->to = $to; } /** * @return Block */ public function getBlock(){ return $this->from; } /** * @return Block */ public function getTo(){ return $this->to; } } ================================================ FILE: src/pocketmine/event/entity/EntityCombustByBlockEvent.php ================================================ combuster = $combuster; } /** * @return Block */ public function getCombuster(){ return $this->combuster; } } ================================================ FILE: src/pocketmine/event/entity/EntityCombustByEntityEvent.php ================================================ combuster = $combuster; } /** * @return Entity */ public function getCombuster(){ return $this->combuster; } } ================================================ FILE: src/pocketmine/event/entity/EntityCombustEvent.php ================================================ entity = $combustee; $this->duration = $duration; $this->ProtectLevel = $ProtectLevel; } public function getDuration(){ if($this->ProtectLevel !== 0){ return round($this->duration * (1 - 0.15 * $this->ProtectLevel)); }else{ return $this->duration; } } public function setDuration($duration){ $this->duration = (int) $duration; } public function setProtectLevel($ProtectLevel){ $this->ProtectLevel = (int) $ProtectLevel; } } ================================================ FILE: src/pocketmine/event/entity/EntityDamageByBlockEvent.php ================================================ damager = $damager; parent::__construct($entity, $cause, $damage); } /** * @return Block */ public function getDamager(){ return $this->damager; } } ================================================ FILE: src/pocketmine/event/entity/EntityDamageByChildEntityEvent.php ================================================ childEntity = $childEntity; parent::__construct($damager, $entity, $cause, $damage); } /** * @return Entity */ public function getChild(){ return $this->childEntity; } } ================================================ FILE: src/pocketmine/event/entity/EntityDamageByEntityEvent.php ================================================ damager = $damager; $this->knockBack = $knockBack; parent::__construct($entity, $cause, $damage); $this->addAttackerModifiers($damager); } protected function addAttackerModifiers(Entity $damager){ if($damager->hasEffect(Effect::STRENGTH)){ $this->setRateDamage(1 + 0.3 * ($damager->getEffect(Effect::STRENGTH)->getAmplifier() + 1), self::MODIFIER_STRENGTH); } if($damager->hasEffect(Effect::WEAKNESS)){ $eff_level = 1 - 0.2 * ($damager->getEffect(Effect::WEAKNESS)->getAmplifier() + 1); if($eff_level < 0){ $eff_level = 0; } $this->setRateDamage($eff_level, self::MODIFIER_WEAKNESS); } } /** * @return Entity */ public function getDamager(){ return $this->damager; } /** * @return float */ public function getKnockBack(){ return $this->knockBack; } /** * @param float $knockBack */ public function setKnockBack($knockBack){ $this->knockBack = $knockBack; } } ================================================ FILE: src/pocketmine/event/entity/EntityDamageEvent.php ================================================ entity = $entity; $this->cause = $cause; if(is_array($damage)){ $this->modifiers = $damage; }else{ $this->modifiers = [ self::MODIFIER_BASE => $damage ]; } $this->originals = $this->modifiers; if(!isset($this->modifiers[self::MODIFIER_BASE])){ throw new \InvalidArgumentException("BASE Damage modifier missing"); } //For DAMAGE_RESISTANCE if($cause !== self::CAUSE_VOID and $cause !== self::CAUSE_SUICIDE){ if($entity->hasEffect(Effect::DAMAGE_RESISTANCE)){ $RES_level = 1 - 0.20 * ($entity->getEffect(Effect::DAMAGE_RESISTANCE)->getAmplifier() + 1); if($RES_level < 0){ $RES_level = 0; } $this->setRateDamage($RES_level, self::MODIFIER_RESISTANCE); } } //TODO: add zombie if($entity instanceof Player and $entity->getInventory() instanceof PlayerInventory){ switch($cause){ case self::CAUSE_CONTACT: case self::CAUSE_ENTITY_ATTACK: case self::CAUSE_PROJECTILE: case self::CAUSE_FIRE: case self::CAUSE_LAVA: case self::CAUSE_BLOCK_EXPLOSION: case self::CAUSE_ENTITY_EXPLOSION: case self::CAUSE_LIGHTNING: $points = 0; foreach($entity->getInventory()->getArmorContents() as $index => $i){ if($i->isArmor()){ $points += $i->getArmorValue(); $this->usedArmors[$index] = 1; } } if($points !== 0){ $this->setRateDamage(1 - 0.04 * $points, self::MODIFIER_ARMOR); } //For Protection $spe_Prote = null; switch($cause){ case self::CAUSE_ENTITY_EXPLOSION: case self::CAUSE_BLOCK_EXPLOSION: $spe_Prote = Enchantment::TYPE_ARMOR_EXPLOSION_PROTECTION; break; case self::CAUSE_FIRE: case self::CAUSE_LAVA: $spe_Prote = Enchantment::TYPE_ARMOR_FIRE_PROTECTION; break; case self::CAUSE_PROJECTILE: $spe_Prote = Enchantment::TYPE_ARMOR_PROJECTILE_PROTECTION; break; default; break; } foreach($this->usedArmors as $index => $cost){ $i = $entity->getInventory()->getArmorItem($index); if($i->isArmor()){ $this->EPF += $i->getEnchantmentLevel(Enchantment::TYPE_ARMOR_PROTECTION); $this->fireProtectL = max($this->fireProtectL, $i->getEnchantmentLevel(Enchantment::TYPE_ARMOR_FIRE_PROTECTION)); if($i->getEnchantmentLevel(Enchantment::TYPE_ARMOR_THORNS) > 0){ $this->thornsLevel[$index] = $i->getEnchantmentLevel(Enchantment::TYPE_ARMOR_THORNS); } if($spe_Prote !== null){ $this->EPF += 2 * $i->getEnchantmentLevel($spe_Prote); } } } break; case self::CAUSE_FALL: //Feather Falling $i = $entity->getInventory()->getBoots(); if($i->isArmor()){ $this->EPF += $i->getEnchantmentLevel(Enchantment::TYPE_ARMOR_PROTECTION); $this->EPF += 3 * $i->getEnchantmentLevel(Enchantment::TYPE_ARMOR_FALL_PROTECTION); } break; case self::CAUSE_FIRE_TICK: case self::CAUSE_SUFFOCATION: case self::CAUSE_DROWNING: case self::CAUSE_VOID: case self::CAUSE_SUICIDE: case self::CAUSE_MAGIC: case self::CAUSE_CUSTOM: case self::CAUSE_STARVATION: break; default: break; } if($this->EPF !== 0){ $this->EPF = min(20, ceil($this->EPF * mt_rand(50, 100) / 100)); $this->setRateDamage(1 - 0.04 * $this->EPF, self::MODIFIER_PROTECTION); } } } /** * @return int */ public function getCause(){ return $this->cause; } /** * @param int $type * * @return int */ public function getOriginalDamage($type = self::MODIFIER_BASE){ if(isset($this->originals[$type])){ return $this->originals[$type]; } return 0; } /** * @param int $type * * @return int */ public function getDamage($type = self::MODIFIER_BASE){ if(isset($this->modifiers[$type])){ return $this->modifiers[$type]; } return 0; } /** * @param float $damage * @param int $type * * @throws \UnexpectedValueException */ public function setDamage($damage, $type = self::MODIFIER_BASE){ $this->modifiers[$type] = $damage; } /** * @param int $type * * @return float 1 - the percentage */ public function getRateDamage($type = self::MODIFIER_BASE){ if(isset($this->rateModifiers[$type])){ return $this->rateModifiers[$type]; } return 1; } /** * @param float $damage * @param int $type * * Notice:If you want to add/reduce the damage without reducing by Armor or effect. set a new Damage using setDamage * Notice:If you want to add/reduce the damage within reducing by Armor of effect. Plz change the MODIFIER_BASE * Notice:If you want to add/reduce the damage by multiplying. Plz use this function. */ public function setRateDamage($damage, $type = self::MODIFIER_BASE){ $this->rateModifiers[$type] = $damage; } /** * @param int $type * * @return bool */ public function isApplicable($type){ return isset($this->modifiers[$type]); } /** * @return int */ public function getFinalDamage(){ $damage = $this->modifiers[self::MODIFIER_BASE]; foreach($this->rateModifiers as $type => $d){ $damage *= $d; } foreach($this->modifiers as $type => $d){ if($type !== self::MODIFIER_BASE){ $damage += $d; } } return $damage; } /** * @return Item $usedArmors * notice: $usedArmors $index->$cost * $index: the $index of ArmorInventory * $cost: the num of durability cost */ public function getUsedArmors(){ return $this->usedArmors; } /** * @return Int $fireProtectL */ public function getFireProtectL(){ return $this->fireProtectL; } /** * @return bool */ public function useArmors(){ if($this->entity instanceof Player){ if($this->entity->isSurvival() and $this->entity->isAlive()){ foreach($this->usedArmors as $index => $cost){ $i = $this->entity->getInventory()->getArmorItem($index); if($i->isArmor()){ /** @var Armor $i */ $i->useOn($i, $cost); $this->entity->getInventory()->setArmorItem($index, $i); } } } return true; } return false; } public function createThornsDamage(){ if($this->thornsLevel !== []){ $this->thornsArmor = array_rand($this->thornsLevel); $thornsL = $this->thornsLevel[$this->thornsArmor]; if(mt_rand(1, 100) < $thornsL * 15){ $this->thornsDamage = mt_rand(1, 4); } } } public function getThornsDamage(){ return $this->thornsDamage; } /** * @return bool should be used after getThornsDamage() */ public function setThornsArmorUse(){ if($this->thornsArmor === null){ return false; }else{ $this->usedArmors[$this->thornsArmor] = 3; return true; } } } ================================================ FILE: src/pocketmine/event/entity/EntityDeathEvent.php ================================================ entity = $entity; $this->drops = $drops; } /** * @return Living */ public function getEntity(){ return $this->entity; } /** * @return \pocketmine\item\Item[] */ public function getDrops(){ return $this->drops; } /** * @param Item[] $drops */ public function setDrops(array $drops){ $this->drops = $drops; } } ================================================ FILE: src/pocketmine/event/entity/EntityDespawnEvent.php ================================================ entity = $entity; $this->entityType = $entity::NETWORK_ID; } /** * @return int */ public function getType(){ return $this->entityType; } /** * @return bool */ public function isCreature(){ return $this->entity instanceof Creature; } /** * @return bool */ public function isHuman(){ return $this->entity instanceof Human; } /** * @return bool */ public function isProjectile(){ return $this->entity instanceof Projectile; } /** * @return bool */ public function isVehicle(){ return $this->entity instanceof Vehicle; } /** * @return bool */ public function isItem(){ return $this->entity instanceof Item; } } ================================================ FILE: src/pocketmine/event/entity/EntityDrinkPotionEvent.php ================================================ entity = $entity; $this->potion = $potion; $this->effects = $potion->getEffects(); } public function getEffects(){ return $this->effects; } public function getPotion(){ return $this->potion; } } ================================================ FILE: src/pocketmine/event/entity/EntityEatBlockEvent.php ================================================ entity = $entity; $this->foodSource = $foodSource; $this->foodRestore = $foodSource->getFoodRestore(); $this->saturationRestore = $foodSource->getSaturationRestore(); $this->residue = $foodSource->getResidue(); $this->additionalEffects = $foodSource->getAdditionalEffects(); } public function getFoodSource(){ return $this->foodSource; } public function getFoodRestore() : int{ return $this->foodRestore; } public function setFoodRestore(int $foodRestore){ $this->foodRestore = $foodRestore; } public function getSaturationRestore() : float{ return $this->saturationRestore; } public function setSaturationRestore(float $saturationRestore){ $this->saturationRestore = $saturationRestore; } public function getResidue(){ return $this->residue; } public function setResidue($residue){ $this->residue = $residue; } /** * @return Effect[] */ public function getAdditionalEffects(){ return $this->additionalEffects; } /** * @param Effect[] $additionalEffects * * @throws \TypeError */ public function setAdditionalEffects(array $additionalEffects){ foreach($additionalEffects as $effect){ if(!($effect instanceof Effect)){ throw new \TypeError("Argument 1 passed to EntityEatEvent::setAdditionalEffects() must be an Effect array"); } } $this->additionalEffects = $additionalEffects; } } ================================================ FILE: src/pocketmine/event/entity/EntityEatItemEvent.php ================================================ entity = $entity; $this->effect = $effect; } /** * @return Effect */ public function getEffect(){ return $this->effect; } } ================================================ FILE: src/pocketmine/event/entity/EntityEffectRemoveEvent.php ================================================ entity = $entity; $this->effect = $effect; } /** * @return Effect */ public function getEffect(){ return $this->effect; } } ================================================ FILE: src/pocketmine/event/entity/EntityEvent.php ================================================ entity; } } ================================================ FILE: src/pocketmine/event/entity/EntityExplodeEvent.php ================================================ entity = $entity; $this->position = $position; $this->blocks = $blocks; $this->yield = $yield; } /** * @return Position */ public function getPosition(){ return $this->position; } /** * @return Block[] */ public function getBlockList(){ return $this->blocks; } /** * @param Block[] $blocks */ public function setBlockList(array $blocks){ $this->blocks = $blocks; } /** * @return float */ public function getYield(){ return $this->yield; } /** * @param float $yield */ public function setYield($yield){ $this->yield = $yield; } } ================================================ FILE: src/pocketmine/event/entity/EntityGenerateEvent.php ================================================ position = $pos; $this->entityType = $entityType; $this->cause = $cause; } /** * @return Position */ public function getPosition(){ return $this->position; } public function setPosition(Position $pos){ $this->position = $pos; } /** * @return int */ public function getType() : int{ return $this->entityType; } /** * @return int */ public function getCause() : int{ return $this->cause; } } ================================================ FILE: src/pocketmine/event/entity/EntityInventoryChangeEvent.php ================================================ entity = $entity; $this->oldItem = $oldItem; $this->newItem = $newItem; $this->slot = (int) $slot; } public function getSlot(){ return $this->slot; } public function getNewItem(){ return $this->newItem; } public function setNewItem(Item $item){ $this->newItem = $item; } public function getOldItem(){ return $this->oldItem; } } ================================================ FILE: src/pocketmine/event/entity/EntityLevelChangeEvent.php ================================================ entity = $entity; $this->originLevel = $originLevel; $this->targetLevel = $targetLevel; } public function getOrigin(){ return $this->originLevel; } public function getTarget(){ return $this->targetLevel; } } ================================================ FILE: src/pocketmine/event/entity/EntityMotionEvent.php ================================================ entity = $entity; $this->mot = $mot; } /** * @return Vector3 */ public function getVector(){ return $this->mot; } } ================================================ FILE: src/pocketmine/event/entity/EntityRegainHealthEvent.php ================================================ entity = $entity; $this->amount = $amount; $this->reason = (int) $regainReason; } /** * @return float */ public function getAmount(){ return $this->amount; } /** * @param float $amount */ public function setAmount($amount){ $this->amount = $amount; } public function getRegainReason(){ return $this->reason; } } ================================================ FILE: src/pocketmine/event/entity/EntityShootBowEvent.php ================================================ entity = $shooter; $this->bow = $bow; $this->projectile = $projectile; $this->force = $force; } /** * @return Living */ public function getEntity(){ return $this->entity; } /** * @return Item */ public function getBow(){ return $this->bow; } /** * @return Entity|Projectile */ public function getProjectile(){ return $this->projectile; } /** * @param Entity $projectile */ public function setProjectile(Entity $projectile){ if($projectile !== $this->projectile){ if(count($this->projectile->getViewers()) === 0){ $this->projectile->kill(); $this->projectile->close(); } $this->projectile = $projectile; } } /** * @return float */ public function getForce(){ return $this->force; } /** * @param float $force */ public function setForce($force){ $this->force = $force; } } ================================================ FILE: src/pocketmine/event/entity/EntitySpawnEvent.php ================================================ entity = $entity; $this->entityType = $entity::NETWORK_ID; } /** * @return \pocketmine\level\Position */ public function getPosition(){ return $this->entity->getPosition(); } /** * @return int */ public function getType(){ return $this->entityType; } /** * @return bool */ public function isCreature(){ return $this->entity instanceof Creature; } /** * @return bool */ public function isHuman(){ return $this->entity instanceof Human; } /** * @return bool */ public function isProjectile(){ return $this->entity instanceof Projectile; } /** * @return bool */ public function isVehicle(){ return $this->entity instanceof Vehicle; } /** * @return bool */ public function isItem(){ return $this->entity instanceof Item; } } ================================================ FILE: src/pocketmine/event/entity/EntityTeleportEvent.php ================================================ entity = $entity; $this->from = $from; $this->to = $to; } /** * @return Position */ public function getFrom(){ return $this->from; } /** * @param Position $from */ public function setFrom(Position $from){ $this->from = $from; } /** * @return Position */ public function getTo(){ return $this->to; } /** * @param Position $to */ public function setTo(Position $to){ $this->to = $to; } } ================================================ FILE: src/pocketmine/event/entity/ExplosionPrimeEvent.php ================================================ entity = $entity; $this->force = $force; $this->blockBreaking = true; $this->dropItem = $dropItem; } public function setDropItem(bool $dropItem){ $this->dropItem = $dropItem; } public function dropItem() : bool{ return $this->dropItem; } /** * @return float */ public function getForce(){ return $this->force; } public function setForce($force){ $this->force = (float) $force; } /** * @return bool */ public function isBlockBreaking(){ return $this->blockBreaking; } /** * @param bool $affectsBlocks */ public function setBlockBreaking($affectsBlocks){ $this->blockBreaking = (bool) $affectsBlocks; } } ================================================ FILE: src/pocketmine/event/entity/ItemDespawnEvent.php ================================================ entity = $item; } /** * @return Item */ public function getEntity(){ return $this->entity; } } ================================================ FILE: src/pocketmine/event/entity/ItemSpawnEvent.php ================================================ entity = $item; } /** * @return Item */ public function getEntity(){ return $this->entity; } } ================================================ FILE: src/pocketmine/event/entity/ProjectileHitEvent.php ================================================ entity = $entity; } /** * @return Projectile */ public function getEntity(){ return $this->entity; } } ================================================ FILE: src/pocketmine/event/entity/ProjectileLaunchEvent.php ================================================ entity = $entity; } /** * @return Projectile */ public function getEntity(){ return $this->entity; } } ================================================ FILE: src/pocketmine/event/inventory/CraftItemEvent.php ================================================ player = $player; $this->input = $input; $this->recipe = $recipe; } /** * @return Item[] */ public function getInput(){ $items = []; foreach($items as $i => $item){ $items[$i] = clone $item; } return $items; } /** * @return Recipe */ public function getRecipe(){ return $this->recipe; } /** * @return \pocketmine\Player */ public function getPlayer(){ return $this->player; } } ================================================ FILE: src/pocketmine/event/inventory/FurnaceBurnEvent.php ================================================ getBlock()); $this->fuel = $fuel; $this->burnTime = (int) $burnTime; $this->furnace = $furnace; } /** * @return Furnace */ public function getFurnace(){ return $this->furnace; } /** * @return Item */ public function getFuel(){ return $this->fuel; } /** * @return int */ public function getBurnTime(){ return $this->burnTime; } /** * @param int $burnTime */ public function setBurnTime($burnTime){ $this->burnTime = (int) $burnTime; } /** * @return bool */ public function isBurning(){ return $this->burning; } /** * @param bool $burning */ public function setBurning($burning){ $this->burning = (bool) $burning; } } ================================================ FILE: src/pocketmine/event/inventory/FurnaceSmeltEvent.php ================================================ getBlock()); $this->source = clone $source; $this->source->setCount(1); $this->result = $result; $this->furnace = $furnace; } /** * @return Furnace */ public function getFurnace(){ return $this->furnace; } /** * @return Item */ public function getSource(){ return $this->source; } /** * @return Item */ public function getResult(){ return $this->result; } /** * @param Item $result */ public function setResult(Item $result){ $this->result = $result; } } ================================================ FILE: src/pocketmine/event/inventory/InventoryCloseEvent.php ================================================ who = $who; parent::__construct($inventory); } /** * @return Player */ public function getPlayer(){ return $this->who; } } ================================================ FILE: src/pocketmine/event/inventory/InventoryEvent.php ================================================ inventory = $inventory; } /** * @return Inventory */ public function getInventory(){ return $this->inventory; } /** * @return \pocketmine\entity\Human[] */ public function getViewers(){ return $this->inventory->getViewers(); } } ================================================ FILE: src/pocketmine/event/inventory/InventoryOpenEvent.php ================================================ who = $who; parent::__construct($inventory); } /** * @return Player */ public function getPlayer(){ return $this->who; } } ================================================ FILE: src/pocketmine/event/inventory/InventoryPickupArrowEvent.php ================================================ arrow = $arrow; parent::__construct($inventory); } /** * @return Arrow */ public function getArrow(){ return $this->arrow; } } ================================================ FILE: src/pocketmine/event/inventory/InventoryPickupItemEvent.php ================================================ item = $item; parent::__construct($inventory); } /** * @return Item */ public function getItem(){ return $this->item; } } ================================================ FILE: src/pocketmine/event/inventory/InventoryTransactionEvent.php ================================================ transactionQueue = $transactionQueue; } /** * @deprecated * @return TransactionQueue */ public function getTransaction(){ return $this->transactionQueue; } /** * @return TransactionQueue */ public function getQueue(){ return $this->transactionQueue; } } ================================================ FILE: src/pocketmine/event/level/ChunkEvent.php ================================================ getProvider()->getLevel()); $this->chunk = $chunk; } /** * @return Chunk */ public function getChunk(){ return $this->chunk; } } ================================================ FILE: src/pocketmine/event/level/ChunkLoadEvent.php ================================================ newChunk = (bool) $newChunk; } /** * @return bool */ public function isNewChunk(){ return $this->newChunk; } } ================================================ FILE: src/pocketmine/event/level/ChunkPopulateEvent.php ================================================ level = $level; } /** * @return \pocketmine\level\Level */ public function getLevel(){ return $this->level; } } ================================================ FILE: src/pocketmine/event/level/LevelInitEvent.php ================================================ previousSpawn = $previousSpawn; } /** * @return Position */ public function getPreviousSpawn(){ return $this->previousSpawn; } } ================================================ FILE: src/pocketmine/event/level/WeatherChangeEvent.php ================================================ weather = $weather; $this->duration = $duration; } public function getWeather() : int{ return $this->weather; } public function setWeather(int $weather = Weather::SUNNY){ $this->weather = $weather; } public function getDuration() : int{ return $this->duration; } public function setDuration(int $duration){ $this->duration = $duration; } } ================================================ FILE: src/pocketmine/event/player/PlayerAchievementAwardedEvent.php ================================================ player = $player; $this->achievement = $achievementId; } public function getAchievement(){ return $this->achievement; } } ================================================ FILE: src/pocketmine/event/player/PlayerAnimationEvent.php ================================================ player = $player; $this->animationType = $animation; } /** * @return int */ public function getAnimationType(){ return $this->animationType; } } ================================================ FILE: src/pocketmine/event/player/PlayerBedEnterEvent.php ================================================ player = $player; $this->bed = $bed; } public function getBed(){ return $this->bed; } } ================================================ FILE: src/pocketmine/event/player/PlayerBedLeaveEvent.php ================================================ player = $player; $this->bed = $bed; } public function getBed(){ return $this->bed; } } ================================================ FILE: src/pocketmine/event/player/PlayerBucketEmptyEvent.php ================================================ player = $who; $this->blockClicked = $blockClicked; $this->blockFace = (int) $blockFace; $this->item = $itemInHand; $this->bucket = $bucket; } /** * Returns the bucket used in this event * * @return Item */ public function getBucket(){ return $this->bucket; } /** * Returns the item in hand after the event * * @return Item */ public function getItem(){ return $this->item; } /** * @param Item $item */ public function setItem(Item $item){ $this->item = $item; } /** * @return Block */ public function getBlockClicked(){ return $this->blockClicked; } /** * @return int */ public function getBlockFace(){ return $this->blockFace; } } ================================================ FILE: src/pocketmine/event/player/PlayerBucketFillEvent.php ================================================ player = $player; $this->message = $message; $this->format = $format; if($recipients === null){ $this->recipients = Server::getInstance()->getPluginManager()->getPermissionSubscriptions(Server::BROADCAST_CHANNEL_USERS); }else{ $this->recipients = $recipients; } } public function getMessage(){ return $this->message; } public function setMessage($message){ $this->message = $message; } /** * Changes the player that is sending the message * * @param Player $player */ public function setPlayer(Player $player){ $this->player = $player; } public function getFormat(){ return $this->format; } public function setFormat($format){ $this->format = $format; } public function getRecipients(){ return $this->recipients; } public function setRecipients(array $recipients){ $this->recipients = $recipients; } } ================================================ FILE: src/pocketmine/event/player/PlayerCommandPreprocessEvent.php ================================================ player = $player; $this->message = $message; } /** * @return string */ public function getMessage(){ return $this->message; } /** * @param string $message */ public function setMessage($message){ $this->message = $message; } /** * @param Player $player */ public function setPlayer(Player $player){ $this->player = $player; } } ================================================ FILE: src/pocketmine/event/player/PlayerCreationEvent.php ================================================ interface = $interface; $this->clientId = $clientId; $this->address = $address; $this->port = $port; if(!is_a($baseClass, Player::class, true)){ throw new \RuntimeException("Base class $baseClass must extend " . Player::class); } $this->baseClass = $baseClass; if(!is_a($playerClass, Player::class, true)){ throw new \RuntimeException("Class $playerClass must extend " . Player::class); } $this->playerClass = $playerClass; } /** * @return SourceInterface */ public function getInterface(){ return $this->interface; } /** * @return string */ public function getAddress(){ return $this->address; } /** * @return int */ public function getPort(){ return $this->port; } /** * @return mixed */ public function getClientId(){ return $this->clientId; } /** * @return Player::class */ public function getBaseClass(){ return $this->baseClass; } /** * @param Player::class $class */ public function setBaseClass($class){ if(!is_a($class, $this->baseClass, true)){ throw new \RuntimeException("Base class $class must extend " . $this->baseClass); } $this->baseClass = $class; } /** * @return Player::class */ public function getPlayerClass(){ return $this->playerClass; } /** * @param Player::class $class */ public function setPlayerClass($class){ if(!is_a($class, $this->baseClass, true)){ throw new \RuntimeException("Class $class must extend " . $this->baseClass); } $this->playerClass = $class; } } ================================================ FILE: src/pocketmine/event/player/PlayerDeathEvent.php ================================================ deathMessage = $deathMessage; } /** * @return Player */ public function getEntity(){ return $this->entity; } /** * @return Player */ public function getPlayer(){ return $this->entity; } /** * @return TextContainer|string */ public function getDeathMessage(){ return $this->deathMessage; } /** * @param string|TextContainer $deathMessage */ public function setDeathMessage($deathMessage){ $this->deathMessage = $deathMessage; } public function getKeepInventory() : bool{ return $this->keepInventory; } public function setKeepInventory(bool $keepInventory){ $this->keepInventory = $keepInventory; } public function getKeepExperience() : bool{ return $this->keepExperience; } public function setKeepExperience(bool $keepExperience){ $this->keepExperience = $keepExperience; } } ================================================ FILE: src/pocketmine/event/player/PlayerDropItemEvent.php ================================================ player = $player; $this->drop = $drop; } /** * @return Item */ public function getItem(){ return $this->drop; } } ================================================ FILE: src/pocketmine/event/player/PlayerEvent.php ================================================ player; } } ================================================ FILE: src/pocketmine/event/player/PlayerExhaustEvent.php ================================================ player = $human; $this->amount = $amount; } /** * @return Human|Player */ public function getPlayer(){ return $this->player; } public function getAmount() : float{ return $this->amount; } public function setAmount(float $amount){ $this->amount = $amount; } } ================================================ FILE: src/pocketmine/event/player/PlayerExperienceChangeEvent.php ================================================ progress = $progress; $this->expLevel = $expLevel; $this->player = $player; } /** * @deprecated This is redundant, and will be removed in the future. */ public function getAction(){ return self::SET_EXPERIENCE; } public function getExpLevel(){ return $this->expLevel; } public function setExpLevel($level){ $this->expLevel = $level; } public function getProgress(): float{ return $this->progress; } public function setProgress(float $progress){ $this->progress = $progress; //errors will be handled internally anyway } public function getExp(){ return Human::getLevelXpRequirement($this->level) * $this->progress; } public function setExp($exp){ $this->progress = $exp / Human::getLevelXpRequirement($this->level); } } ================================================ FILE: src/pocketmine/event/player/PlayerFishEvent.php ================================================ player = $player; $this->item = $item; $this->hook = $fishingHook; } /** * @return Item */ public function getItem(){ return clone $this->item; } public function getHook(){ return $this->hook; } } ================================================ FILE: src/pocketmine/event/player/PlayerGameModeChangeEvent.php ================================================ player = $player; $this->gamemode = (int) $newGamemode; } public function getNewGamemode(){ return $this->gamemode; } } ================================================ FILE: src/pocketmine/event/player/PlayerGlassBottleEvent.php ================================================ player = $Player; $this->target = $target; $this->item = $itemInHand; } /** * @return Item */ public function getItem(){ return $this->item; } /** * @param Item $item */ public function setItem(Item $item){ $this->item = $item; } /** * @return Block */ public function getBlock(){ return $this->target; } } ================================================ FILE: src/pocketmine/event/player/PlayerHungerChangeEvent.php ================================================ data = $data; $this->player = $player; } public function getData(){ return $this->data; } public function setData($data){ $this->data = $data; } } ================================================ FILE: src/pocketmine/event/player/PlayerInteractEvent.php ================================================ blockTouched = $block; $this->touchVector = new Vector3(0, 0, 0); }else{ $this->touchVector = $block; $this->blockTouched = Block::get(0, 0, new Position(0, 0, 0, $player->level)); } $this->player = $player; $this->item = $item; $this->blockFace = (int) $face; $this->action = (int) $action; } /** * @return int */ public function getAction(){ return $this->action; } /** * @return Item */ public function getItem(){ return $this->item; } /** * @return Block */ public function getBlock(){ return $this->blockTouched; } /** * @return Vector3 */ public function getTouchVector(){ return $this->touchVector; } /** * @return int */ public function getFace(){ return $this->blockFace; } } ================================================ FILE: src/pocketmine/event/player/PlayerItemConsumeEvent.php ================================================ player = $player; $this->item = $item; } /** * @return Item */ public function getItem(){ return clone $this->item; } } ================================================ FILE: src/pocketmine/event/player/PlayerItemHeldEvent.php ================================================ player = $player; $this->item = $item; $this->inventorySlot = (int) $inventorySlot; $this->slot = (int) $slot; } public function getSlot(){ return $this->slot; } public function getInventorySlot(){ return $this->inventorySlot; } public function getItem(){ return $this->item; } } ================================================ FILE: src/pocketmine/event/player/PlayerJoinEvent.php ================================================ player = $player; $this->joinMessage = $joinMessage; } /** * @param string|TextContainer $joinMessage */ public function setJoinMessage($joinMessage){ $this->joinMessage = $joinMessage; } /** * @return string|TextContainer */ public function getJoinMessage(){ return $this->joinMessage; } } ================================================ FILE: src/pocketmine/event/player/PlayerKickEvent.php ================================================ player = $player; $this->quitMessage = $quitMessage; $this->reason = $reason; } public function getReason(){ return $this->reason; } public function setQuitMessage($quitMessage){ $this->quitMessage = $quitMessage; } public function getQuitMessage(){ return $this->quitMessage; } } ================================================ FILE: src/pocketmine/event/player/PlayerLoginEvent.php ================================================ player = $player; $this->kickMessage = $kickMessage; } public function setKickMessage($kickMessage){ $this->kickMessage = $kickMessage; } public function getKickMessage(){ return $this->kickMessage; } } ================================================ FILE: src/pocketmine/event/player/PlayerMoveEvent.php ================================================ player = $player; $this->from = $from; $this->to = $to; } public function getFrom(){ return $this->from; } public function setFrom(Location $from){ $this->from = $from; } public function getTo(){ return $this->to; } public function setTo(Location $to){ $this->to = $to; } } ================================================ FILE: src/pocketmine/event/player/PlayerPickupExpOrbEvent.php ================================================ player = $p; $this->amount = $amount; } public function getAmount() : int{ return $this->amount; } public function setAmount(int $amount){ $this->amount = $amount; } } ================================================ FILE: src/pocketmine/event/player/PlayerPreLoginEvent.php ================================================ player = $player; $this->kickMessage = $kickMessage; } public function setKickMessage($kickMessage){ $this->kickMessage = $kickMessage; } public function getKickMessage(){ return $this->kickMessage; } } ================================================ FILE: src/pocketmine/event/player/PlayerQuitEvent.php ================================================ player = $player; $this->quitMessage = $quitMessage; $this->autoSave = $autoSave; } public function setQuitMessage($quitMessage){ $this->quitMessage = $quitMessage; } public function getQuitMessage(){ return $this->quitMessage; } public function getAutoSave(){ return $this->autoSave; } public function setAutoSave($value = true){ $this->autoSave = (bool) $value; } } ================================================ FILE: src/pocketmine/event/player/PlayerRespawnEvent.php ================================================ player = $player; $this->position = $position; } /** * @return Position */ public function getRespawnPosition(){ return $this->position; } /** * @param Position $position */ public function setRespawnPosition(Position $position){ $this->position = $position; } } ================================================ FILE: src/pocketmine/event/player/PlayerTextPreSendEvent.php ================================================ player = $player; $this->message = $message; $this->type = $type; } public function getMessage(){ return $this->message; } public function setMessage($message){ $this->message = $message; } public function getType(){ return $this->type; } } ================================================ FILE: src/pocketmine/event/player/PlayerToggleFlightEvent.php ================================================ player = $player; $this->isFlying = (bool) $isFlying; } public function isFlying(){ return $this->isFlying; } } ================================================ FILE: src/pocketmine/event/player/PlayerToggleGlideEvent.php ================================================ player = $player; $this->isGliding = (bool) $isGliding; } public function isGliding(){ return $this->isGliding; } } ================================================ FILE: src/pocketmine/event/player/PlayerToggleSneakEvent.php ================================================ player = $player; $this->isSneaking = (bool) $isSneaking; } public function isSneaking(){ return $this->isSneaking; } } ================================================ FILE: src/pocketmine/event/player/PlayerToggleSprintEvent.php ================================================ player = $player; $this->isSprinting = (bool) $isSprinting; } public function isSprinting(){ return $this->isSprinting; } } ================================================ FILE: src/pocketmine/event/player/PlayerUseFishingRodEvent.php ================================================ player = $player; $this->action = $action; } public function getAction() : int{ return $this->action; } } ================================================ FILE: src/pocketmine/event/plugin/PluginDisableEvent.php ================================================ plugin = $plugin; } /** * @return Plugin */ public function getPlugin(){ return $this->plugin; } } ================================================ FILE: src/pocketmine/event/server/DataPacketReceiveEvent.php ================================================ packet = $packet; $this->player = $player; } public function getPacket(){ return $this->packet; } public function getPlayer(){ return $this->player; } } ================================================ FILE: src/pocketmine/event/server/DataPacketSendEvent.php ================================================ packet = $packet; $this->player = $player; } public function getPacket(){ return $this->packet; } public function getPlayer(){ return $this->player; } } ================================================ FILE: src/pocketmine/event/server/LowMemoryEvent.php ================================================ memory = $memory; $this->memoryLimit = $memoryLimit; $this->global = (bool) $isGlobal; $this->triggerCount = (int) $triggerCount; } /** * Returns the memory usage at the time of the event call (in bytes) * * @return int */ public function getMemory(){ return $this->memory; } /** * Returns the memory limit defined (in bytes) * * @return int */ public function getMemoryLimit(){ return $this->memory; } /** * Returns the times this event has been called in the current low-memory state * * @return int */ public function getTriggerCount(){ return $this->triggerCount; } /** * @return bool */ public function isGlobal(){ return $this->global; } /** * Amount of memory already freed * * @return int */ public function getMemoryFreed(){ return $this->getMemory() - ($this->isGlobal() ? Utils::getMemoryUsage(true)[1] : Utils::getMemoryUsage(true)[0]); } } ================================================ FILE: src/pocketmine/event/server/QueryRegenerateEvent.php ================================================ timeout = $timeout; $this->serverName = $server->getMotd(); $this->listPlugins = $server->getProperty("settings.query-plugins", true); $this->plugins = $server->getPluginManager()->getPlugins(); $this->players = []; foreach($server->getOnlinePlayers() as $player){ if($player->isOnline()){ $this->players[] = $player; } } if($server->isDServerEnabled() and $server->dserverConfig["queryMaxPlayers"]) $pc = $server->dserverConfig["queryMaxPlayers"]; elseif($server->isDServerEnabled() and $server->dserverConfig["queryAllPlayers"]) $pc = $server->getDServerMaxPlayers(); else $pc = $server->getMaxPlayers(); if($server->isDServerEnabled() and $server->dserverConfig["queryPlayers"]) $poc = $server->getDServerOnlinePlayers(); else $poc = count($this->players); $this->gametype = ($server->getGamemode() & 0x01) === 0 ? "SMP" : "CMP"; $this->version = $server->getVersion(); $this->server_engine = $server->getName() . " " . $server->getPocketMineVersion(); $this->map = $server->getDefaultLevel() === null ? "unknown" : $server->getDefaultLevel()->getName(); $this->numPlayers = $poc; $this->maxPlayers = $pc; $this->whitelist = $server->hasWhitelist() ? "on" : "off"; $this->port = $server->getPort(); $this->ip = $server->getIp(); } /** * Gets the min. timeout for Query Regeneration * * @return int */ public function getTimeout(){ return $this->timeout; } public function setTimeout($timeout){ $this->timeout = $timeout; } public function getServerName(){ return $this->serverName; } public function setServerName($serverName){ $this->serverName = $serverName; } public function canListPlugins(){ return $this->listPlugins; } public function setListPlugins($value){ $this->listPlugins = (bool) $value; } /** * @return \pocketmine\plugin\Plugin[] */ public function getPlugins(){ return $this->plugins; } /** * @param \pocketmine\plugin\Plugin[] $plugins */ public function setPlugins(array $plugins){ $this->plugins = $plugins; } /** * @return \pocketmine\Player[] */ public function getPlayerList(){ return $this->players; } /** * @param \pocketmine\Player[] $players */ public function setPlayerList(array $players){ $this->players = $players; } public function getPlayerCount(){ return $this->numPlayers; } public function setPlayerCount($count){ $this->numPlayers = (int) $count; } public function getMaxPlayerCount(){ return $this->maxPlayers; } public function setMaxPlayerCount($count){ $this->maxPlayers = (int) $count; } public function getWorld(){ return $this->map; } public function setWorld($world){ $this->map = (string) $world; } /** * Returns the extra Query data in key => value form * * @return array */ public function getExtraData(){ return $this->extraData; } public function setExtraData(array $extraData){ $this->extraData = $extraData; } public function getLongQuery(){ $query = ""; $plist = $this->server_engine; if(count($this->plugins) > 0 and $this->listPlugins){ $plist .= ":"; foreach($this->plugins as $p){ $d = $p->getDescription(); $plist .= " " . str_replace([";", ":", " "], ["", "", "_"], $d->getName()) . " " . str_replace([";", ":", " "], ["", "", "_"], $d->getVersion()) . ";"; } $plist = substr($plist, 0, -1); } $KVdata = [ "splitnum" => chr(128), "hostname" => $this->serverName, "gametype" => $this->gametype, "game_id" => self::GAME_ID, "version" => $this->version, "server_engine" => $this->server_engine, "plugins" => $plist, "map" => $this->map, "numplayers" => $this->numPlayers, "maxplayers" => $this->maxPlayers, "whitelist" => $this->whitelist, "hostip" => $this->ip, "hostport" => $this->port ]; foreach($KVdata as $key => $value){ $query .= $key . "\x00" . $value . "\x00"; } foreach($this->extraData as $key => $value){ $query .= $key . "\x00" . $value . "\x00"; } $query .= "\x00\x01player_\x00\x00"; foreach($this->players as $player){ $query .= $player->getName() . "\x00"; } $query .= "\x00"; return $query; } public function getShortQuery(){ return $this->serverName . "\x00" . $this->gametype . "\x00" . $this->map . "\x00" . $this->numPlayers . "\x00" . $this->maxPlayers . "\x00" . Binary::writeLShort($this->port) . $this->ip . "\x00"; } } ================================================ FILE: src/pocketmine/event/server/RemoteServerCommandEvent.php ================================================ sender = $sender; $this->command = $command; } /** * @return CommandSender */ public function getSender(){ return $this->sender; } /** * @return string */ public function getCommand(){ return $this->command; } /** * @param string $command */ public function setCommand($command){ $this->command = $command; } } ================================================ FILE: src/pocketmine/event/server/ServerEvent.php ================================================ holder; } public function getResultSlotIndex(){ return self::RESULT; } public function onRename(Player $player, Item $resultItem) : bool{ if(!$resultItem->deepEquals($this->getItem(self::TARGET), true, false, true)){ //Item does not match target item. Everything must match except the tags. return false; } if($player->getXpLevel() < $resultItem->getRepairCost()){ //Not enough exp return false; } $player->takeXpLevel($resultItem->getRepairCost()); $this->clearAll(); if(!$player->getServer()->allowInventoryCheats and !$player->isCreative()){ if(!$player->getFloatingInventory()->canAddItem($resultItem)){ return false; } $player->getFloatingInventory()->addItem($resultItem); } return true; } public function processSlotChange(Transaction $transaction): bool{ if($transaction->getSlot() === $this->getResultSlotIndex()){ return false; } return true; } public function onSlotChange($index, $before, $send){ //Do not send anvil slot updates to anyone. This will cause a client crash. } public function onClose(Player $who){ parent::onClose($who); $this->getHolder()->getLevel()->dropItem($this->getHolder()->add(0.5, 0.5, 0.5), $this->getItem(0)); $this->getHolder()->getLevel()->dropItem($this->getHolder()->add(0.5, 0.5, 0.5), $this->getItem(1)); $this->clear(0); $this->clear(1); $this->clear(2); } } ================================================ FILE: src/pocketmine/inventory/BaseInventory.php ================================================ holder = $holder; $this->type = $type; if($overrideSize !== null){ $this->size = (int) $overrideSize; }else{ $this->size = $this->type->getDefaultSize(); } if($overrideTitle !== null){ $this->title = $overrideTitle; }else{ $this->title = $this->type->getDefaultTitle(); } $this->name = $this->type->getDefaultTitle(); $this->setContents($items); } public function __destruct(){ $this->holder = null; $this->slots = []; } public function getSize(){ return $this->size; } public function getHotbarSize(){ return 0; } public function setSize($size){ $this->size = (int) $size; } public function getMaxStackSize(){ return $this->maxStackSize; } public function getName() : string{ return $this->name; } public function getTitle(){ return $this->title; } public function getItem($index){ return isset($this->slots[$index]) ? clone $this->slots[$index] : Item::get(Item::AIR, 0, 0); } public function getContents(){ return $this->slots; } /** * @param Item[] $items */ public function setContents(array $items, $send = true){ if(count($items) > $this->size){ $items = array_slice($items, 0, $this->size, true); } for($i = 0; $i < $this->size; ++$i){ if(!isset($items[$i])){ if(isset($this->slots[$i])){ $this->clear($i, $send); } }else{ if (!$this->setItem($i, $items[$i], $send)){ $this->clear($i, $send); } } } } public function setItem($index, Item $item, $send = true){ $item = clone $item; if($index < 0 or $index >= $this->size){ return false; }elseif($item->getId() === 0 or $item->getCount() <= 0){ return $this->clear($index, $send); } $holder = $this->getHolder(); if($holder instanceof Entity){ Server::getInstance()->getPluginManager()->callEvent($ev = new EntityInventoryChangeEvent($holder, $this->getItem($index), $item, $index)); if($ev->isCancelled()){ $this->sendSlot($index, $this->getViewers()); return false; } $item = $ev->getNewItem(); } $old = $this->getItem($index); $this->slots[$index] = clone $item; $this->onSlotChange($index, $old, $send); return true; } public function contains(Item $item){ $count = max(1, $item->getCount()); $checkDamage = !$item->hasAnyDamageValue(); $checkTags = $item->getCompoundTag(); foreach($this->getContents() as $i){ if($item->equals($i, $checkDamage, $checkTags)){ $count -= $i->getCount(); if($count <= 0){ return true; } } } return false; } public function slotContains($slot, Item $item, $matchCount = false){ if($matchCount){ return $this->getItem($slot)->deepEquals($item, true, true, true); }else{ return $this->getItem($slot)->deepEquals($item) and $this->getItem($slot)->getCount() >= $item->getCount(); } } public function all(Item $item){ $slots = []; $checkDamage = !$item->hasAnyDamageValue() ; $checkTags = $item->getCompoundTag(); foreach($this->getContents() as $index => $i){ if($item->equals($i, $checkDamage, $checkTags)){ $slots[$index] = $i; } } return $slots; } public function remove(Item $item, $send = true){ $checkDamage = !$item->hasAnyDamageValue() ; $checkTags = $item->getCompoundTag(); $checkCount = $item->getCount() === null ? false : true; foreach($this->getContents() as $index => $i){ if($item->equals($i, $checkDamage, $checkTags, $checkCount)){ $this->clear($index, $send); break; } } } public function first(Item $item){ $count = max(1, $item->getCount()); $checkDamage = !$item->hasAnyDamageValue(); $checkTags = $item->getCompoundTag(); foreach($this->getContents() as $index => $i){ if($item->equals($i, $checkDamage, $checkTags) and $i->getCount() >= $count){ return $index; } } return -1; } public function firstEmpty(){ for($i = 0; $i < $this->size; ++$i){ if($this->getItem($i)->getId() === Item::AIR){ return $i; } } return -1; } public function firstOccupied(){ for($i = 0; $i < $this->size; $i++){ if(($item = $this->getItem($i))->getId() !== Item::AIR and $item->getCount() > 0){ return $i; } } return -1; } public function canAddItem(Item $item){ $item = clone $item; $checkDamage = $item->hasAnyDamageValue() ; $checkTags = $item->getCompoundTag(); for($i = 0; $i < $this->getSize(); ++$i){ $slot = $this->getItem($i); if($item->equals($slot, $checkDamage, $checkTags)){ if(($diff = $slot->getMaxStackSize() - $slot->getCount()) > 0){ $item->setCount($item->getCount() - $diff); } }elseif($slot->getId() === Item::AIR){ $item->setCount($item->getCount() - $this->getMaxStackSize()); } if($item->getCount() <= 0){ return true; } } return false; } public function addItem(...$slots){ /** @var Item[] $itemSlots */ /** @var Item[] $slots */ $itemSlots = []; foreach($slots as $slot){ if(!($slot instanceof Item)){ throw new \InvalidArgumentException("Expected Item, got ".gettype($slot)); } if($slot->getId() !== 0 and $slot->getCount() > 0){ $itemSlots[] = clone $slot; } } $emptySlots = []; for($i = 0; $i < $this->getSize(); ++$i){ $item = $this->getItem($i); if($item->getId() === Item::AIR or $item->getCount() <= 0){ $emptySlots[] = $i; } foreach($itemSlots as $index => $slot){ if($slot->equals($item) and $item->getCount() < $item->getMaxStackSize()){ $amount = min($item->getMaxStackSize() - $item->getCount(), $slot->getCount(), $this->getMaxStackSize()); if($amount > 0){ $slot->setCount($slot->getCount() - $amount); $item->setCount($item->getCount() + $amount); $this->setItem($i, $item); if($slot->getCount() <= 0){ unset($itemSlots[$index]); } } } } if(count($itemSlots) === 0){ break; } } if(count($itemSlots) > 0 and count($emptySlots) > 0){ foreach($emptySlots as $slotIndex){ //This loop only gets the first item, then goes to the next empty slot foreach($itemSlots as $index => $slot){ $amount = min($slot->getMaxStackSize(), $slot->getCount(), $this->getMaxStackSize()); $slot->setCount($slot->getCount() - $amount); $item = clone $slot; $item->setCount($amount); $this->setItem($slotIndex, $item); if($slot->getCount() <= 0){ unset($itemSlots[$index]); } break; } } } return $itemSlots; } public function removeItem(...$slots){ /** @var Item[] $itemSlots */ /** @var Item[] $slots */ $itemSlots = []; foreach($slots as $slot){ if(!($slot instanceof Item)){ throw new \InvalidArgumentException("Expected Item[], got ".gettype($slot)); } if($slot->getId() !== 0 and $slot->getCount() > 0){ $itemSlots[] = clone $slot; } } for($i = 0; $i < $this->getSize(); ++$i){ $item = $this->getItem($i); if($item->getId() === Item::AIR or $item->getCount() <= 0){ continue; } foreach($itemSlots as $index => $slot){ if($slot->equals($item, $slot->hasAnyDamageValue(), $slot->getCompoundTag())){ $amount = min($item->getCount(), $slot->getCount()); $slot->setCount($slot->getCount() - $amount); $item->setCount($item->getCount() - $amount); $this->setItem($i, $item); if($slot->getCount() <= 0){ unset($itemSlots[$index]); } } } if(count($itemSlots) === 0){ break; } } return $itemSlots; } public function clear($index, $send = true){ if(isset($this->slots[$index])){ $item = Item::get(Item::AIR, 0, 0); $old = $this->slots[$index]; $holder = $this->getHolder(); if($holder instanceof Entity){ Server::getInstance()->getPluginManager()->callEvent($ev = new EntityInventoryChangeEvent($holder, $old, $item, $index)); if($ev->isCancelled()){ $this->sendSlot($index, $this->getViewers()); return false; } $item = $ev->getNewItem(); } if($item->getId() !== Item::AIR){ $this->slots[$index] = clone $item; }else{ unset($this->slots[$index]); } $this->onSlotChange($index, $old, $send); } return true; } public function clearAll($send = true){ foreach($this->getContents() as $index => $i){ $this->clear($index, $send); } } /** * @return Player[] */ public function getViewers(){ return $this->viewers; } public function getHolder(){ return $this->holder; } public function setMaxStackSize($size){ $this->maxStackSize = (int) $size; } public function open(Player $who){ $who->getServer()->getPluginManager()->callEvent($ev = new InventoryOpenEvent($this, $who)); if($ev->isCancelled()){ return false; } $this->onOpen($who); return true; } public function close(Player $who){ $this->onClose($who); } public function onOpen(Player $who){ $this->viewers[spl_object_hash($who)] = $who; } public function onClose(Player $who){ unset($this->viewers[spl_object_hash($who)]); } public function onSlotChange($index, $before, $send){ if($send){ $this->sendSlot($index, $this->getViewers()); } } public function processSlotChange(Transaction $transaction): bool{ return true; } /** * @param Player|Player[] $target */ public function sendContents($target){ if($target instanceof Player){ $target = [$target]; } $pk = new ContainerSetContentPacket(); $pk->slots = []; for($i = 0; $i < $this->getSize(); ++$i){ $pk->slots[$i] = $this->getItem($i); } foreach($target as $player){ if(($id = $player->getWindowId($this)) === -1 or $player->spawned !== true){ $this->close($player); continue; } $pk->windowid = $id; $player->dataPacket($pk); } } /** * @param int $index * @param Player|Player[] $target */ public function sendSlot($index, $target){ if($target instanceof Player){ $target = [$target]; } $pk = new ContainerSetSlotPacket(); $pk->slot = $index; $pk->item = clone $this->getItem($index); foreach($target as $player){ if(($id = $player->getWindowId($this)) === -1){ $this->close($player); continue; } $pk->windowid = $id; $player->dataPacket($pk); } } public function getType(){ return $this->type; } } ================================================ FILE: src/pocketmine/inventory/BaseTransaction.php ================================================ inventory = $inventory; $this->slot = (int) $slot; $this->targetItem = clone $targetItem; $this->creationTime = microtime(true); $this->transactionType = $transactionType; $this->achievements = $achievements; } public function getCreationTime(){ return $this->creationTime; } public function getInventory(){ return $this->inventory; } public function getSlot(){ return $this->slot; } public function getTargetItem(){ return clone $this->targetItem; } public function setTargetItem(Item $item){ $this->targetItem = clone $item; } public function getFailures(){ return $this->failures; } public function addFailure(){ $this->failures++; } public function succeeded(){ return $this->wasSuccessful; } public function setSuccess($value = true){ $this->wasSuccessful = $value; } public function getTransactionType(){ return $this->transactionType; } public function getAchievements(){ return $this->achievements; } public function hasAchievements(){ return count($this->achievements) !== 0; } public function addAchievement(string $achievementName){ $this->achievements[] = $achievementName; } /** * @param Player $source * * Sends a slot update to inventory viewers * For successful transactions, update non-source viewers (source does not need updating) * For failed transactions, update the source (non-source viewers will see nothing anyway) */ public function sendSlotUpdate(Player $source){ if($this->getInventory() instanceof TemporaryInventory){ return; } $targets = []; if($this->wasSuccessful){ $targets = $this->getInventory()->getViewers(); unset($targets[spl_object_hash($source)]); }else{ $targets = [$source]; } $this->inventory->sendSlot($this->slot, $targets); } /** * Returns the change in inventory resulting from this transaction * @return Item[ * "in" => items added to the inventory * "out" => items removed from the inventory * ] */ public function getChange(){ $sourceItem = $this->getInventory()->getItem($this->slot); if($sourceItem->deepEquals($this->targetItem, true, true, true)){ //This should never happen, somehow a change happened where nothing changed return null; }elseif($sourceItem->deepEquals($this->targetItem)){ //Same item, change of count $item = clone $sourceItem; $countDiff = $this->targetItem->getCount() - $sourceItem->getCount(); $item->setCount(abs($countDiff)); if($countDiff < 0){ //Count decreased return ["in" => null, "out" => $item]; }elseif($countDiff > 0){ //Count increased return [ "in" => $item, "out" => null]; }else{ //Should be impossible (identical items and no count change) //This should be caught by the first condition even if it was possible return null; } }elseif($sourceItem->getId() !== Item::AIR and $this->targetItem->getId() === Item::AIR){ //Slot emptied (item removed) return ["in" => null, "out" => clone $sourceItem]; }elseif($sourceItem->getId() === Item::AIR and $this->targetItem->getId() !== Item::AIR){ //Slot filled (item added) return ["in" => $this->getTargetItem(), "out" => null]; }else{ //Some other slot change - an item swap (tool damage changes will be ignored as they are processed server-side before any change is sent by the client return ["in" => $this->getTargetItem(), "out" => clone $sourceItem]; } } /** * @param Player $source * @return bool * * Handles transaction execution. Returns whether transaction was successful or not. */ public function execute(Player $source): bool{ if($this->getInventory()->processSlotChange($this)){ //This means that the transaction should be handled the normal way if(!$source->getServer()->allowInventoryCheats and !$source->isCreative()){ $change = $this->getChange(); if($change === null){ //No changes to make, ignore this transaction return true; } /* Verify that we have the required items */ if($change["out"] instanceof Item){ if(!$this->getInventory()->slotContains($this->getSlot(), $change["out"])){ return false; } } if($change["in"] instanceof Item){ if(!$source->getFloatingInventory()->contains($change["in"])){ return false; } } /* All checks passed, make changes to floating inventory * This will not be reached unless all requirements are met */ if($change["out"] instanceof Item){ $source->getFloatingInventory()->addItem($change["out"]); } if($change["in"] instanceof Item){ $source->getFloatingInventory()->removeItem($change["in"]); } } $this->getInventory()->setItem($this->getSlot(), $this->getTargetItem(), false); } /* Process transaction achievements, like getting iron from a furnace */ foreach($this->achievements as $achievement){ $source->awardAchievement($achievement); } return true; } } ================================================ FILE: src/pocketmine/inventory/BigShapedRecipe.php ================================================ holder; } public function setIngredient(Item $item){ $this->setItem(0, $item); } /** * @return Item */ public function getIngredient(){ return $this->getItem(0); } public function onSlotChange($index, $before, $send){ parent::onSlotChange($index, $before, $send); $this->getHolder()->scheduleUpdate(); $this->getHolder()->updateSurface(); } } ================================================ FILE: src/pocketmine/inventory/BrewingRecipe.php ================================================ output = clone $result; $this->ingredient = clone $ingredient; $this->potion = clone $potion; } public function getPotion(){ return clone $this->potion; } public function getId(){ return $this->id; } public function setId(UUID $id){ if($this->id !== null){ throw new \InvalidStateException("Id is already set"); } $this->id = $id; } /** * @param Item $item */ public function setInput(Item $item){ $this->ingredient = clone $item; } /** * @return Item */ public function getInput(){ return clone $this->ingredient; } /** * @return Item */ public function getResult(){ return clone $this->output; } public function registerToCraftingManager(){ Server::getInstance()->getCraftingManager()->registerBrewingRecipe($this); } } ================================================ FILE: src/pocketmine/inventory/ChestInventory.php ================================================ holder; } public function getContents($withAir = false){ if($withAir){ $contents = []; for($i = 0; $i < $this->getSize(); ++$i){ $contents[$i] = $this->getItem($i); } return $contents; } return parent::getContents(); } public function onOpen(Player $who){ parent::onOpen($who); if(count($this->getViewers()) === 1){ $pk = new BlockEventPacket(); $pk->x = $this->getHolder()->getX(); $pk->y = $this->getHolder()->getY(); $pk->z = $this->getHolder()->getZ(); $pk->case1 = 1; $pk->case2 = 2; if(($level = $this->getHolder()->getLevel()) instanceof Level){ $level->addChunkPacket($this->getHolder()->getX() >> 4, $this->getHolder()->getZ() >> 4, $pk); } } if($this->getHolder()->getLevel() instanceof Level){ /** @var TrappedChest $block */ $block = $this->getHolder()->getBlock(); if($block instanceof TrappedChest){ if(!$block->isActivated()){ $block->activate(); } } } } public function onClose(Player $who){ if($this->getHolder()->getLevel() instanceof Level){ /** @var TrappedChest $block */ $block = $this->getHolder()->getBlock(); if($block instanceof TrappedChest){ if($block->isActivated()){ $block->deactivate(); } } } if(count($this->getViewers()) === 1){ $pk = new BlockEventPacket(); $pk->x = $this->getHolder()->getX(); $pk->y = $this->getHolder()->getY(); $pk->z = $this->getHolder()->getZ(); $pk->case1 = 1; $pk->case2 = 0; if(($level = $this->getHolder()->getLevel()) instanceof Level){ $level->addChunkPacket($this->getHolder()->getX() >> 4, $this->getHolder()->getZ() >> 4, $pk); } } parent::onClose($who); } } ================================================ FILE: src/pocketmine/inventory/ContainerInventory.php ================================================ windowid = $who->getWindowId($this); $pk->type = $this->getType()->getNetworkType(); $pk->slots = $this->getSize(); $holder = $this->getHolder(); if($holder instanceof Vector3){ $pk->x = $holder->getX(); $pk->y = $holder->getY(); $pk->z = $holder->getZ(); }else{ $pk->x = $pk->y = $pk->z = 0; } $who->dataPacket($pk); $this->sendContents($who); } public function onClose(Player $who){ $pk = new ContainerClosePacket(); $pk->windowid = $who->getWindowId($this); $who->dataPacket($pk); parent::onClose($who); } } ================================================ FILE: src/pocketmine/inventory/CraftingInventory.php ================================================ getDefaultTitle() !== "Crafting"){ throw new \InvalidStateException("Invalid Inventory type, expected CRAFTING or WORKBENCH"); } $this->resultInventory = $resultInventory; parent::__construct($holder, $inventoryType); } /** * @return Inventory */ public function getResultInventory(){ return $this->resultInventory; } public function getSize(){ return $this->getResultInventory()->getSize() + parent::getSize(); } } ================================================ FILE: src/pocketmine/inventory/CraftingManager.php ================================================ registerBrewingStand(); // load recipes from src/pocketmine/resources/recipes.json $recipes = new Config(Server::getInstance()->getFilePath() . "src/pocketmine/resources/recipes.json", Config::JSON, []); foreach($recipes->getAll() as $recipe){ switch($recipe["type"]){ case 0: // TODO: handle multiple result items if(count($recipe["output"]) === 1){ $first = $recipe["output"][0]; $result = new ShapelessRecipe(Item::get($first["id"], $first["damage"], $first["count"], $first["nbt"])); foreach($recipe["input"] as $ingredient){ $result->addIngredient(Item::get($ingredient["id"], $ingredient["damage"], $ingredient["count"], $first["nbt"])); } $this->registerRecipe($result); } break; case 1: // TODO: handle multiple result items if(count($recipe["output"]) === 1){ $first = $recipe["output"][0]; $result = new ShapedRecipe(Item::get($first["id"], $first["damage"], $first["count"], $first["nbt"]), $recipe["height"], $recipe["width"]); $shape = array_chunk($recipe["input"], $recipe["width"]); foreach($shape as $y => $row){ foreach($row as $x => $ingredient){ $result->addIngredient($x, $y, Item::get($ingredient["id"], ($ingredient["damage"] < 0 ? -1 : $ingredient["damage"]), $ingredient["count"], $ingredient["nbt"])); } } $this->registerRecipe($result); } break; case 2: case 3: $result = $recipe["output"]; $resultItem = Item::get($result["id"], $result["damage"], $result["count"], $result["nbt"]); $this->registerRecipe(new FurnaceRecipe($resultItem, Item::get($recipe["inputId"], $recipe["inputDamage"] ?? -1, 1))); break; default: break; } } } protected function registerBrewingStand(){ //Potion //WATER_BOTTLE $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::AWKWARD, 1), Item::get(Item::NETHER_WART, 0, 1), Item::get(Item::POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::THICK, 1), Item::get(Item::GLOWSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::MUNDANE_EXTENDED, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::WEAKNESS, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::MUNDANE, 1), Item::get(Item::GHAST_TEAR, 0, 1), Item::get(Item::POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::MUNDANE, 1), Item::get(Item::GLISTERING_MELON, 0, 1), Item::get(Item::POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::MUNDANE, 1), Item::get(Item::BLAZE_POWDER, 0, 1), Item::get(Item::POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::MUNDANE, 1), Item::get(Item::MAGMA_CREAM, 0, 1), Item::get(Item::POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::MUNDANE, 1), Item::get(Item::SUGAR, 0, 1), Item::get(Item::POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::MUNDANE, 1), Item::get(Item::SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::MUNDANE, 1), Item::get(Item::RABBIT_FOOT, 0, 1), Item::get(Item::POTION, Potion::WATER_BOTTLE, 1))); //To WEAKNESS $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::WEAKNESS, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::MUNDANE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::WEAKNESS, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::THICK, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::WEAKNESS_T, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::MUNDANE_EXTENDED, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::WEAKNESS_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::WEAKNESS, 1))); //GHAST_TEAR and BLAZE_POWDER $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::REGENERATION, 1), Item::get(Item::GHAST_TEAR, 0, 1), Item::get(Item::POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::REGENERATION_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::REGENERATION, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::REGENERATION_TWO, 1), Item::get(Item::GLOWSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::REGENERATION, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::STRENGTH, 1), Item::get(Item::BLAZE_POWDER, 0, 1), Item::get(Item::POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::STRENGTH_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::STRENGTH, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::STRENGTH_TWO, 1), Item::get(Item::GLOWSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::STRENGTH, 1))); //SPIDER_EYE GLISTERING_MELON and PUFFERFISH $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::POISON, 1), Item::get(Item::SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::POISON_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::POISON, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::POISON_TWO, 1), Item::get(Item::GLOWSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::POISON, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::HEALING, 1), Item::get(Item::GLISTERING_MELON, 0, 1), Item::get(Item::POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::HEALING_TWO, 1), Item::get(Item::GLOWSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::HEALING, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::WATER_BREATHING, 1), Item::get(Item::PUFFER_FISH, 0, 1), Item::get(Item::POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::WATER_BREATHING_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::WATER_BREATHING, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::HARMING, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::WATER_BREATHING, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::HARMING, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::HEALING, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::HARMING, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::POISON, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::HARMING_TWO, 1), Item::get(Item::GLOWSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::HARMING, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::HARMING_TWO, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::HEALING_TWO, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::HARMING_TWO, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::POISON_T, 1))); //SUGAR MAGMA_CREAM and RABBIT_FOOT $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::SWIFTNESS, 1), Item::get(Item::SUGAR, 0, 1), Item::get(Item::POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::SWIFTNESS_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::SWIFTNESS, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::SWIFTNESS_TWO, 1), Item::get(Item::GLOWSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::SWIFTNESS, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::FIRE_RESISTANCE, 1), Item::get(Item::MAGMA_CREAM, 0, 1), Item::get(Item::POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::FIRE_RESISTANCE_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::FIRE_RESISTANCE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::LEAPING, 1), Item::get(Item::RABBIT_FOOT, 0, 1), Item::get(Item::POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::LEAPING_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::LEAPING, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::LEAPING_TWO, 1), Item::get(Item::GLOWSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::LEAPING, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::SLOWNESS, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::FIRE_RESISTANCE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::SLOWNESS, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::SWIFTNESS, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::SLOWNESS, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::LEAPING, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::SLOWNESS_T, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::FIRE_RESISTANCE_T, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::SLOWNESS_T, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::LEAPING_T, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::SLOWNESS_T, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::SWIFTNESS_T, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::SLOWNESS_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::SLOWNESS, 1))); //GOLDEN_CARROT $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::NIGHT_VISION, 1), Item::get(Item::GOLDEN_CARROT, 0, 1), Item::get(Item::POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::NIGHT_VISION_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::NIGHT_VISION, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::INVISIBILITY, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::NIGHT_VISION, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::INVISIBILITY_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::POTION, Potion::INVISIBILITY, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::POTION, Potion::INVISIBILITY_T, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::POTION, Potion::NIGHT_VISION_T, 1))); //===================================================================分隔符======================================================================= //SPLASH_POTION //WATER_BOTTLE $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::AWKWARD, 1), Item::get(Item::NETHER_WART, 0, 1), Item::get(Item::SPLASH_POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::THICK, 1), Item::get(Item::GLOWSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::MUNDANE_EXTENDED, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::WEAKNESS, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::MUNDANE, 1), Item::get(Item::GHAST_TEAR, 0, 1), Item::get(Item::SPLASH_POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::MUNDANE, 1), Item::get(Item::GLISTERING_MELON, 0, 1), Item::get(Item::SPLASH_POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::MUNDANE, 1), Item::get(Item::BLAZE_POWDER, 0, 1), Item::get(Item::SPLASH_POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::MUNDANE, 1), Item::get(Item::MAGMA_CREAM, 0, 1), Item::get(Item::SPLASH_POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::MUNDANE, 1), Item::get(Item::SUGAR, 0, 1), Item::get(Item::SPLASH_POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::MUNDANE, 1), Item::get(Item::SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::WATER_BOTTLE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::MUNDANE, 1), Item::get(Item::RABBIT_FOOT, 0, 1), Item::get(Item::SPLASH_POTION, Potion::WATER_BOTTLE, 1))); //To WEAKNESS $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::WEAKNESS, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::MUNDANE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::WEAKNESS, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::THICK, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::WEAKNESS_T, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::MUNDANE_EXTENDED, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::WEAKNESS_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::WEAKNESS, 1))); //GHAST_TEAR and BLAZE_POWDER $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::REGENERATION, 1), Item::get(Item::GHAST_TEAR, 0, 1), Item::get(Item::SPLASH_POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::REGENERATION_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::REGENERATION, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::REGENERATION_TWO, 1), Item::get(Item::GLOWSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::REGENERATION, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::STRENGTH, 1), Item::get(Item::BLAZE_POWDER, 0, 1), Item::get(Item::SPLASH_POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::STRENGTH_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::STRENGTH, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::STRENGTH_TWO, 1), Item::get(Item::GLOWSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::STRENGTH, 1))); //SPIDER_EYE GLISTERING_MELON and PUFFERFISH $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::POISON, 1), Item::get(Item::SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::POISON_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::POISON, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::POISON_TWO, 1), Item::get(Item::GLOWSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::POISON, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::HEALING, 1), Item::get(Item::GLISTERING_MELON, 0, 1), Item::get(Item::SPLASH_POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::HEALING_TWO, 1), Item::get(Item::GLOWSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::HEALING, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::WATER_BREATHING, 1), Item::get(Item::PUFFER_FISH, 0, 1), Item::get(Item::SPLASH_POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::WATER_BREATHING_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::WATER_BREATHING, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::HARMING, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::WATER_BREATHING, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::HARMING, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::HEALING, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::HARMING, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::POISON, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::HARMING_TWO, 1), Item::get(Item::GLOWSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::HARMING, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::HARMING_TWO, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::HEALING_TWO, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::HARMING_TWO, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::POISON_T, 1))); //SUGAR MAGMA_CREAM and RABBIT_FOOT $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::SWIFTNESS, 1), Item::get(Item::SUGAR, 0, 1), Item::get(Item::SPLASH_POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::SWIFTNESS_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::SWIFTNESS, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::SWIFTNESS_TWO, 1), Item::get(Item::GLOWSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::SWIFTNESS, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::FIRE_RESISTANCE, 1), Item::get(Item::MAGMA_CREAM, 0, 1), Item::get(Item::SPLASH_POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::FIRE_RESISTANCE_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::FIRE_RESISTANCE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::LEAPING, 1), Item::get(Item::RABBIT_FOOT, 0, 1), Item::get(Item::SPLASH_POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::LEAPING_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::LEAPING, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::LEAPING_TWO, 1), Item::get(Item::GLOWSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::LEAPING, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::SLOWNESS, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::FIRE_RESISTANCE, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::SLOWNESS, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::SWIFTNESS, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::SLOWNESS, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::LEAPING, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::SLOWNESS_T, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::FIRE_RESISTANCE_T, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::SLOWNESS_T, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::LEAPING_T, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::SLOWNESS_T, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::SWIFTNESS_T, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::SLOWNESS_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::SLOWNESS, 1))); //GOLDEN_CARROT $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::NIGHT_VISION, 1), Item::get(Item::GOLDEN_CARROT, 0, 1), Item::get(Item::SPLASH_POTION, Potion::AWKWARD, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::NIGHT_VISION_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::NIGHT_VISION, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::INVISIBILITY, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::NIGHT_VISION, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::INVISIBILITY_T, 1), Item::get(Item::REDSTONE_DUST, 0, 1), Item::get(Item::SPLASH_POTION, Potion::INVISIBILITY, 1))); $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, Potion::INVISIBILITY_T, 1), Item::get(Item::FERMENTED_SPIDER_EYE, 0, 1), Item::get(Item::SPLASH_POTION, Potion::NIGHT_VISION_T, 1))); //===================================================================分隔符======================================================================= //普通药水升级成喷溅 foreach (Potion::POTIONS as $potion => $effect){ $this->registerBrewingRecipe(new BrewingRecipe(Item::get(Item::SPLASH_POTION, $potion, 1), Item::get(Item::GUNPOWDER, 0, 1), Item::get(Item::POTION, $potion, 1))); } } public function sort(Item $i1, Item $i2){ if($i1->getId() > $i2->getId()){ return 1; }elseif($i1->getId() < $i2->getId()){ return -1; }elseif($i1->getDamage() > $i2->getDamage()){ return 1; }elseif($i1->getDamage() < $i2->getDamage()){ return -1; }elseif($i1->getCount() > $i2->getCount()){ return 1; }elseif($i1->getCount() < $i2->getCount()){ return -1; }else{ return 0; } } /** * @param UUID $id * @return Recipe */ public function getRecipe(UUID $id){ $index = $id->toBinary(); return $this->recipes[$index] ?? null; } /** * @return Recipe[] */ public function getRecipes(){ return $this->recipes; } public function getRecipesByResult(Item $item){ return @array_values($this->recipeLookup[$item->getId() . ":" . $item->getDamage()]) ?? []; } /** * @return FurnaceRecipe[] */ public function getFurnaceRecipes(){ return $this->furnaceRecipes; } /** * @param Item $input * * @return FurnaceRecipe */ public function matchFurnaceRecipe(Item $input){ if(isset($this->furnaceRecipes[$input->getId() . ":" . $input->getDamage()])){ return $this->furnaceRecipes[$input->getId() . ":" . $input->getDamage()]; }elseif(isset($this->furnaceRecipes[$input->getId() . ":?"])){ return $this->furnaceRecipes[$input->getId() . ":?"]; } return null; } /** * @param Item $input * @param Item $potion * * @return BrewingRecipe */ public function matchBrewingRecipe(Item $input, Item $potion){ $subscript = $input->getId() . ":" . ($input->getDamage() === null ? "0" : $input->getDamage()) . ":" . $potion->getId() . ":" .($potion->getDamage() === null ? "0" : $potion->getDamage()); if(isset($this->brewingRecipes[$subscript])){ return $this->brewingRecipes[$subscript]; } return null; } /** * @param ShapedRecipe $recipe */ public function registerShapedRecipe(ShapedRecipe $recipe){ $result = $recipe->getResult(); $this->recipes[$recipe->getId()->toBinary()] = $recipe; $ingredients = $recipe->getIngredientMap(); $hash = ""; foreach($ingredients as $v){ foreach($v as $item){ if($item !== null){ /** @var Item $item */ $hash .= $item->getId() . ":" . ($item->hasAnyDamageValue() ? "?" : $item->getDamage()) . "x" . $item->getCount() . ","; } } $hash .= ";"; } $this->recipeLookup[$result->getId() . ":" . $result->getDamage()][$hash] = $recipe; } /** * @param ShapelessRecipe $recipe */ public function registerShapelessRecipe(ShapelessRecipe $recipe){ $result = $recipe->getResult(); $this->recipes[$recipe->getId()->toBinary()] = $recipe; $hash = ""; $ingredients = $recipe->getIngredientList(); usort($ingredients, [$this, "sort"]); foreach($ingredients as $item){ $hash .= $item->getId() . ":" . ($item->hasAnyDamageValue() ? "?" : $item->getDamage()) . "x" . $item->getCount() . ","; } $this->recipeLookup[$result->getId() . ":" . $result->getDamage()][$hash] = $recipe; } /** * @param FurnaceRecipe $recipe */ public function registerFurnaceRecipe(FurnaceRecipe $recipe){ $input = $recipe->getInput(); $this->furnaceRecipes[$input->getId() . ":" . ($input->hasAnyDamageValue() ? "?" : $input->getDamage())] = $recipe; } /** * @param BrewingRecipe $recipe */ public function registerBrewingRecipe(BrewingRecipe $recipe){ $input = $recipe->getInput(); $potion = $recipe->getPotion(); $this->brewingRecipes[$input->getId() . ":" . ($input->getDamage() === null ? "0" : $input->getDamage()) . ":" . $potion->getId() . ":" .($potion->getDamage() === null ? "0" : $potion->getDamage())] = $recipe; } /** * @param ShapelessRecipe $recipe * @return bool */ public function matchRecipe(ShapelessRecipe $recipe){ if(!isset($this->recipeLookup[$idx = $recipe->getResult()->getId() . ":" . $recipe->getResult()->getDamage()])){ return false; } $hash = ""; $ingredients = $recipe->getIngredientList(); usort($ingredients, [$this, "sort"]); foreach($ingredients as $item){ $hash .= $item->getId() . ":" . ($item->hasAnyDamageValue() ? "?" : $item->getDamage()) . "x" . $item->getCount() . ","; } if(isset($this->recipeLookup[$idx][$hash])){ return true; } $hasRecipe = null; foreach($this->recipeLookup[$idx] as $recipe){ if($recipe instanceof ShapelessRecipe){ if($recipe->getIngredientCount() !== count($ingredients)){ continue; } $checkInput = $recipe->getIngredientList(); foreach($ingredients as $item){ $amount = $item->getCount(); foreach($checkInput as $k => $checkItem){ if($checkItem->equals($item, $checkItem->getDamage() === null ? false : true, $checkItem->getCompoundTag() === null ? false : true)){ $remove = min($checkItem->getCount(), $amount); $checkItem->setCount($checkItem->getCount() - $remove); if($checkItem->getCount() === 0){ unset($checkInput[$k]); } $amount -= $remove; if($amount === 0){ break; } } } } if(count($checkInput) === 0){ $hasRecipe = $recipe; break; } } if($hasRecipe instanceof Recipe){ break; } } return $hasRecipe !== null; } /** * @param Recipe $recipe */ public function registerRecipe(Recipe $recipe){ $recipe->setId(UUID::fromData(++self::$RECIPE_COUNT, $recipe->getResult()->getId(), $recipe->getResult()->getDamage(), $recipe->getResult()->getCount(), $recipe->getResult()->getCompoundTag())); if($recipe instanceof ShapedRecipe){ $this->registerShapedRecipe($recipe); }elseif($recipe instanceof ShapelessRecipe){ $this->registerShapelessRecipe($recipe); }elseif($recipe instanceof FurnaceRecipe){ $this->registerFurnaceRecipe($recipe); } } } ================================================ FILE: src/pocketmine/inventory/CustomInventory.php ================================================ holder; } } ================================================ FILE: src/pocketmine/inventory/DoubleChestInventory.php ================================================ left = $left->getRealInventory(); $this->right = $right->getRealInventory(); $items = array_merge($this->left->getContents(true), $this->right->getContents(true)); BaseInventory::__construct($this, InventoryType::get(InventoryType::DOUBLE_CHEST), $items); } public function getInventory(){ return $this; } public function getHolder(){ return $this->left->getHolder(); } public function getItem($index){ return $index < $this->left->getSize() ? $this->left->getItem($index) : $this->right->getItem($index - $this->right->getSize()); } public function setItem($index, Item $item){ return $index < $this->left->getSize() ? $this->left->setItem($index, $item) : $this->right->setItem($index - $this->right->getSize(), $item); } public function clear($index){ return $index < $this->left->getSize() ? $this->left->clear($index) : $this->right->clear($index - $this->right->getSize()); } public function getContents(){ $contents = []; for($i = 0; $i < $this->getSize(); ++$i){ $contents[$i] = $this->getItem($i); } return $contents; } /** * @param Item[] $items */ public function setContents(array $items){ if(count($items) > $this->size){ $items = array_slice($items, 0, $this->size, true); } for($i = 0; $i < $this->size; ++$i){ if(!isset($items[$i])){ if ($i < $this->left->size){ if(isset($this->left->slots[$i])){ $this->clear($i); } }elseif(isset($this->right->slots[$i - $this->left->size])){ $this->clear($i); } }elseif(!$this->setItem($i, $items[$i])){ $this->clear($i); } } } public function onOpen(Player $who){ parent::onOpen($who); if(count($this->getViewers()) === 1){ $pk = new BlockEventPacket(); $pk->x = $this->right->getHolder()->getX(); $pk->y = $this->right->getHolder()->getY(); $pk->z = $this->right->getHolder()->getZ(); $pk->case1 = 1; $pk->case2 = 2; if(($level = $this->right->getHolder()->getLevel()) instanceof Level){ $level->addChunkPacket($this->right->getHolder()->getX() >> 4, $this->right->getHolder()->getZ() >> 4, $pk); } } } public function onClose(Player $who){ if(count($this->getViewers()) === 1){ $pk = new BlockEventPacket(); $pk->x = $this->right->getHolder()->getX(); $pk->y = $this->right->getHolder()->getY(); $pk->z = $this->right->getHolder()->getZ(); $pk->case1 = 1; $pk->case2 = 0; if(($level = $this->right->getHolder()->getLevel()) instanceof Level){ $level->addChunkPacket($this->right->getHolder()->getX() >> 4, $this->right->getHolder()->getZ() >> 4, $pk); } } parent::onClose($who); } /** * @return ChestInventory */ public function getLeftSide(){ return $this->left; } /** * @return ChestInventory */ public function getRightSide(){ return $this->right; } } ================================================ FILE: src/pocketmine/inventory/DropItemTransaction.php ================================================ targetItem = $droppedItem; } public function setSourceItem(Item $item){ //Nothing to update } public function getInventory(){ return null; } public function getSlot(){ return null; } public function sendSlotUpdate(Player $source){ //Nothing to update } public function getChange(){ return ["in" => $this->getTargetItem(), "out" => null]; } public function execute(Player $source): bool{ $droppedItem = $this->getTargetItem(); if(!$source->getServer()->allowInventoryCheats and !$source->isCreative()){ if(!$source->getFloatingInventory()->contains($droppedItem)){ return false; } $source->getFloatingInventory()->removeItem($droppedItem); } $source->dropItem($droppedItem); return true; } } ================================================ FILE: src/pocketmine/inventory/DropperInventory.php ================================================ holder; } } ================================================ FILE: src/pocketmine/inventory/EnchantInventory.php ================================================ holder; } public function getResultSlotIndex(){ return -1; //enchanting tables don't have result slots, they modify the item in the target slot instead } public function onOpen(Player $who){ parent::onOpen($who); if($this->levels == null){ $this->bookshelfAmount = $this->countBookshelf(); if($this->bookshelfAmount < 0){ $this->bookshelfAmount = 0; } if($this->bookshelfAmount > 15){ $this->bookshelfAmount = 15; } $base = mt_rand(1, 8) + ($this->bookshelfAmount / 2) + mt_rand(0, $this->bookshelfAmount); $this->levels = [ 0 => max($base / 3, 1), 1 => (($base * 2) / 3 + 1), 2 => max($base, $this->bookshelfAmount * 2) ]; } } private function randomFloat($min = 0, $max = 1){ return $min + mt_rand() / mt_getrandmax() * ($max - $min); } public function onSlotChange($index, $before, $send){ parent::onSlotChange($index, $before, $send); if($index === 0){ $item = $this->getItem(0); if($item->getId() === Item::AIR){ $this->entries = null; }elseif($before->getId() == Item::AIR and !$item->hasEnchantments()){ //before enchant if($this->entries === null){ $enchantAbility = Enchantment::getEnchantAbility($item); $this->entries = []; for($i = 0; $i < 3; $i++){ $result = []; $level = $this->levels[$i]; $k = $level + mt_rand(0, round(round($enchantAbility / 4) * 2)) + 1; $bonus = ($this->randomFloat() + $this->randomFloat() - 1) * 0.15 + 1; $modifiedLevel = ($k * (1 + $bonus) + 0.5); $possible = EnchantmentLevelTable::getPossibleEnchantments($item, $modifiedLevel); $weights = []; $total = 0; for($j = 0; $j < count($possible); $j++){ $id = $possible[$j]->getId(); $weight = Enchantment::getEnchantWeight($id); $weights[$j] = $weight; $total += $weight; } $v = mt_rand(1, $total + 1); $sum = 0; for($key = 0; $key < count($weights); ++$key){ $sum += $weights[$key]; if($sum >= $v){ $key++; break; } } $key--; if(!isset($possible[$key])) return; $enchantment = $possible[$key]; $result[] = $enchantment; unset($possible[$key]); //Extra enchantment while(count($possible) > 0){ $modifiedLevel = round($modifiedLevel / 2); $v = mt_rand(0, 51); if($v <= ($modifiedLevel + 1)){ $possible = $this->removeConflictEnchantment($enchantment, $possible); $weights = []; $total = 0; for($j = 0; $j < count($possible); $j++){ $id = $possible[$j]->getId(); $weight = Enchantment::getEnchantWeight($id); $weights[$j] = $weight; $total += $weight; } $v = mt_rand(1, $total + 1); $sum = 0; for($key = 0; $key < count($weights); ++$key){ $sum += $weights[$key]; if($sum >= $v){ $key++; break; } } $key--; $enchantment = $possible[$key]; $result[] = $enchantment; unset($possible[$key]); }else{ break; } } $this->entries[$i] = new EnchantmentEntry($result, $level, Enchantment::getRandomName()); } $this->sendEnchantmentList(); } } } } public function onClose(Player $who){ parent::onClose($who); $level = $this->getHolder()->getLevel(); for($i = 0; $i < 2; ++$i){ if($level instanceof Level) $level->dropItem($this->getHolder()->add(0.5, 0.5, 0.5), $this->getItem($i)); $this->clear($i); } if(count($this->getViewers()) === 0){ $this->levels = null; $this->entries = null; $this->bookshelfAmount = 0; } } /** * @param Enchantment[] $ent1 * @param Enchantment[] $ent2 * * @return bool */ public function checkEnts(array $ent1, array $ent2){ foreach($ent1 as $enchantment){ $hasResult = false; foreach($ent2 as $enchantment1){ if($enchantment->equals($enchantment1)){ $hasResult = true; continue; } } if(!$hasResult){ return false; } } return true; } public function onEnchant(Player $who, Item $before, Item $after){ $result = ($before->getId() === Item::BOOK) ? new EnchantedBook() : $before; if(!$before->hasEnchantments() and $after->hasEnchantments() and $after->getId() == $result->getId() and $this->levels != null and $this->entries != null ){ $enchantments = $after->getEnchantments(); for($i = 0; $i < 3; $i++){ if($this->checkEnts($enchantments, $this->entries[$i]->getEnchantments())){ $lapis = $this->getItem(1); $level = $who->getXpLevel(); $cost = $this->entries[$i]->getCost(); if($lapis->getId() == Item::DYE and $lapis->getDamage() == Dye::BLUE and $lapis->getCount() > $i and $level >= $cost){ foreach($enchantments as $enchantment){ $result->addEnchantment($enchantment); } $this->setItem(0, $result); $lapis->setCount($lapis->getCount() - $i - 1); $this->setItem(1, $lapis); $who->takeXpLevel($i + 1); break; } } } } } public function countBookshelf() : int{ if($this->getHolder()->getLevel()->getServer()->countBookshelf){ $count = 0; $pos = $this->getHolder(); $offsets = [[2, 0], [-2, 0], [0, 2], [0, -2], [2, 1], [2, -1], [-2, 1], [-2, 1], [1, 2], [-1, 2], [1, -2], [-1, -2]]; for($i = 0; $i < 3; $i++){ foreach($offsets as $offset){ if($pos->getLevel()->getBlockIdAt($pos->x + $offset[0], $pos->y + $i, $pos->z + $offset[1]) == Block::BOOKSHELF){ $count++; } if($count >= 15){ break 2; } } } return $count; }else{ return mt_rand(0, 15); } } public function sendEnchantmentList(){ $pk = new CraftingDataPacket(); if($this->entries !== null and $this->levels !== null){ $list = new EnchantmentList(count($this->entries)); for($i = 0; $i < count($this->entries); $i++){ $list->setSlot($i, $this->entries[$i]); } $pk->addEnchantList($list); } Server::getInstance()->broadcastPacket($this->getViewers(), $pk); } /** * @param Enchantment $enchantment * @param Enchantment[] $enchantments * @return Enchantment[] */ public function removeConflictEnchantment(Enchantment $enchantment, array $enchantments){ if(count($enchantments) > 0){ foreach($enchantments as $e){ $id = $e->getId(); if($id == $enchantment->getId()){ unset($enchantments[$id]); continue; } if($id >= 0 and $id <= 4 and $enchantment->getId() >= 0 and $enchantment->getId() <= 4){ //Protection unset($enchantments[$id]); continue; } if($id >= 9 and $id <= 14 and $enchantment->getId() >= 9 and $enchantment->getId() <= 14){ //Weapon unset($enchantments[$id]); continue; } if(($id === Enchantment::TYPE_MINING_SILK_TOUCH and $enchantment->getId() === Enchantment::TYPE_MINING_FORTUNE) or ($id === Enchantment::TYPE_MINING_FORTUNE and $enchantment->getId() === Enchantment::TYPE_MINING_SILK_TOUCH)){ //Protection unset($enchantments[$id]); continue; } } } $result = []; if(count($enchantments) > 0){ foreach($enchantments as $enchantment){ $result[] = $enchantment; } } return $result; } } ================================================ FILE: src/pocketmine/inventory/EnderChestInventory.php ================================================ owner = $owner; parent::__construct(new FakeBlockMenu($this, $owner), InventoryType::get(InventoryType::ENDER_CHEST)); if($contents !== null){ if($contents instanceof ListTag){ foreach($contents as $item){ $this->setItem($item["Slot"], Item::nbtDeserialize($item)); } }else{ throw new \InvalidArgumentException("Expecting ListTag, received " . gettype($contents)); } } } public function getOwner(){ return $this->owner; } public function openAt(Position $pos){ $this->getHolder()->setComponents($pos->x, $pos->y, $pos->z); $this->getHolder()->setLevel($pos->getLevel()); $this->owner->addWindow($this); } public function getHolder(){ return $this->holder; } public function onOpen(Player $who){ parent::onOpen($who); if(count($this->getViewers()) === 1){ $pk = new BlockEventPacket(); $pk->x = $this->getHolder()->getX(); $pk->y = $this->getHolder()->getY(); $pk->z = $this->getHolder()->getZ(); $pk->case1 = 1; $pk->case2 = 2; if(($level = $this->getHolder()->getLevel()) instanceof Level){ $level->addChunkPacket($this->getHolder()->getX() >> 4, $this->getHolder()->getZ() >> 4, $pk); } } } public function onClose(Player $who){ if(count($this->getViewers()) === 1){ $pk = new BlockEventPacket(); $pk->x = $this->getHolder()->getX(); $pk->y = $this->getHolder()->getY(); $pk->z = $this->getHolder()->getZ(); $pk->case1 = 1; $pk->case2 = 0; if(($level = $this->getHolder()->getLevel()) instanceof Level){ $level->addChunkPacket($this->getHolder()->getX() >> 4, $this->getHolder()->getZ() >> 4, $pk); } } parent::onClose($who); } } ================================================ FILE: src/pocketmine/inventory/FakeBlockMenu.php ================================================ inventory = $inventory; parent::__construct($pos->x, $pos->y, $pos->z, $pos->level); } public function getInventory(){ return $this->inventory; } } ================================================ FILE: src/pocketmine/inventory/FloatingInventory.php ================================================ 1600, Item::COAL_BLOCK => 16000, Item::TRUNK => 300, //Item::BROWN_MUSHROOM_BLOCK => 300, //Item::RED_MUSHROOM_BLOCK => 300, Item::WOODEN_PLANKS => 300, Item::SAPLING => 100, Item::WOODEN_AXE => 200, Item::WOODEN_PICKAXE => 200, Item::WOODEN_SWORD => 200, Item::WOODEN_SHOVEL => 200, Item::WOODEN_HOE => 200, Item::WOODEN_PRESSURE_PLATE => 300, Item::STICK => 100, Item::FENCE => 300, Item::FENCE_GATE => 300, Item::FENCE_GATE_SPRUCE => 300, Item::FENCE_GATE_BIRCH => 300, Item::FENCE_GATE_JUNGLE => 300, Item::FENCE_GATE_ACACIA => 300, Item::FENCE_GATE_DARK_OAK => 300, Item::WOODEN_STAIRS => 300, Item::SPRUCE_WOOD_STAIRS => 300, Item::BIRCH_WOOD_STAIRS => 300, Item::JUNGLE_WOOD_STAIRS => 300, Item::TRAPDOOR => 300, Item::WORKBENCH => 300, Item::NOTEBLOCK => 300, Item::BOOKSHELF => 300, Item::CHEST => 300, Item::TRAPPED_CHEST => 300, Item::DAYLIGHT_SENSOR => 300, Item::BUCKET => 20000, Item::BLAZE_ROD => 2400, ]; } ================================================ FILE: src/pocketmine/inventory/FurnaceInventory.php ================================================ holder; } /** * @return Item */ public function getResult(){ return $this->getItem(self::RESULT); } /** * @return Item */ public function getFuel(){ return $this->getItem(self::FUEL); } /** * @return Item */ public function getSmelting(){ return $this->getItem(self::SMELTING); } /** * @param Item $item * * @return bool */ public function setResult(Item $item){ return $this->setItem(self::RESULT, $item); } /** * @param Item $item * * @return bool */ public function setFuel(Item $item){ return $this->setItem(self::FUEL, $item); } /** * @param Item $item * * @return bool */ public function setSmelting(Item $item){ return $this->setItem(self::SMELTING, $item); } public function onSlotChange($index, $before, $send){ parent::onSlotChange($index, $before, $send); $this->getHolder()->scheduleUpdate(); } } ================================================ FILE: src/pocketmine/inventory/FurnaceRecipe.php ================================================ output = clone $result; $this->ingredient = clone $ingredient; } public function getId(){ return $this->id; } public function setId(UUID $id){ if($this->id !== null){ throw new \InvalidStateException("Id is already set"); } $this->id = $id; } /** * @param Item $item */ public function setInput(Item $item){ $this->ingredient = clone $item; } /** * @return Item */ public function getInput(){ return clone $this->ingredient; } /** * @return Item */ public function getResult(){ return clone $this->output; } public function registerToCraftingManager(){ Server::getInstance()->getCraftingManager()->registerFurnaceRecipe($this); } } ================================================ FILE: src/pocketmine/inventory/HopperInventory.php ================================================ holder; } } ================================================ FILE: src/pocketmine/inventory/Inventory.php ================================================ 0){ return; } static::$default[static::CHEST] = new InventoryType(27, "Chest", 0); static::$default[static::DOUBLE_CHEST] = new InventoryType(27 + 27, "Double Chest", 0); static::$default[static::PLAYER] = new InventoryType(36 + 4, "Player", 0); //36 CONTAINER, 4 ARMOR static::$default[static::FURNACE] = new InventoryType(3, "Furnace", 2); static::$default[static::CRAFTING] = new InventoryType(5, "Crafting", 1); //4 CRAFTING slots, 1 RESULT static::$default[static::WORKBENCH] = new InventoryType(10, "Crafting", 1); //9 CRAFTING slots, 1 RESULT static::$default[static::ENCHANT_TABLE] = new InventoryType(2, "Enchant", 3); //1 INPUT/OUTPUT, 1 LAPIS static::$default[static::BREWING_STAND] = new InventoryType(4, "Brewing", 4); //1 INPUT, 3 POTION static::$default[static::ANVIL] = new InventoryType(3, "Anvil", 5); //2 INPUT, 1 OUTPUT static::$default[static::DISPENSER] = new InventoryType(9, "Dispenser", 6); //9 CONTAINER static::$default[static::DROPPER] = new InventoryType(9, "Dropper", 7); //9 CONTAINER static::$default[static::HOPPER] = new InventoryType(5, "Hopper", 8); //5 CONTAINER static::$default[static::ENDER_CHEST] = new InventoryType(27, "EnderChest", 0); static::$default[static::PLAYER_FLOATING] = new InventoryType(36, "Floating", null); //Mirror all slots of main inventory (needed for large item pickups) } /** * @param int $defaultSize * @param string $defaultTitle * @param int $typeId */ private function __construct($defaultSize, $defaultTitle, $typeId = 0){ $this->size = $defaultSize; $this->title = $defaultTitle; $this->typeId = $typeId; } /** * @return int */ public function getDefaultSize(){ return $this->size; } /** * @return string */ public function getDefaultTitle(){ return $this->title; } /** * @return int */ public function getNetworkType(){ return $this->typeId; } } ================================================ FILE: src/pocketmine/inventory/MultiRecipe.php ================================================ uuid = $uuid; } } ================================================ FILE: src/pocketmine/inventory/PlayerInventory.php ================================================ hotbar = range(0, $this->getHotbarSize() - 1, 1); parent::__construct($player, InventoryType::get(InventoryType::PLAYER)); if($contents !== null){ if($contents instanceof ListTag){ //Saved data to be loaded into the inventory foreach($contents as $item){ if($item["Slot"] >= 0 and $item["Slot"] < $this->getHotbarSize()){ //Hotbar if(isset($item["TrueSlot"])){ //Valid slot was found, change the linkage to this slot if(0 <= $item["TrueSlot"] and $item["TrueSlot"] < $this->getSize()){ $this->hotbar[$item["Slot"]] = $item["TrueSlot"]; }elseif($item["TrueSlot"] < 0){ //Link to an empty slot (empty hand) $this->hotbar[$item["Slot"]] = -1; } } /* If TrueSlot is not set, leave the slot index as its default which was filled in above * This only overwrites slot indexes for valid links */ }elseif($item["Slot"] >= 100 and $item["Slot"] < 104){ //Armor $this->setItem($this->getSize() + $item["Slot"] - 100, Item::nbtDeserialize($item), false); }else{ $this->setItem($item["Slot"] - $this->getHotbarSize(), Item::nbtDeserialize($item), false); } } }else{ throw new \InvalidArgumentException("Expecting ListTag, received ".gettype($contents)); } } } public function getSize(){ return parent::getSize() - 4; //Remove armor slots } public function setSize($size){ parent::setSize($size + 4); $this->sendContents($this->getViewers()); } /** * @param int $index * * @return int * * Returns the index of the inventory slot linked to the specified hotbar slot */ public function getHotbarSlotIndex($index){ return ($index >= 0 and $index < $this->getHotbarSize()) ? $this->hotbar[$index] : -1; } /** * @deprecated * * Changes the linkage of the specified hotbar slot. This should never be done unless it is requested by the client. */ public function setHotbarSlotIndex($index, $slot){ if($this->getHolder()->getServer()->getProperty("settings.deprecated-verbose") !== false){ trigger_error("Do not attempt to change hotbar links in plugins!", E_USER_DEPRECATED); } } /** * @return int * * Returns the index of the inventory slot the player is currently holding */ public function getHeldItemIndex(){ return $this->itemInHandIndex; } /** * @param int $hotbarSlotIndex * @param bool $sendToHolder * @param int $slotMapping * * Sets which hotbar slot the player is currently holding. * Allows slot remapping as specified by a MobEquipmentPacket. DO NOT CHANGE SLOT MAPPING IN PLUGINS! * This new implementation is fully compatible with older APIs. * NOTE: Slot mapping is the raw slot index sent by MCPE, which will be between 9 and 44. */ public function setHeldItemIndex($hotbarSlotIndex, $sendToHolder = true, $slotMapping = null){ if($slotMapping !== null){ //Get the index of the slot in the actual inventory $slotMapping -= $this->getHotbarSize(); } if(0 <= $hotbarSlotIndex and $hotbarSlotIndex < $this->getHotbarSize()){ $this->itemInHandIndex = $hotbarSlotIndex; if($slotMapping !== null){ /* Handle a hotbar slot mapping change. This allows PE to select different inventory slots. * This is the only time slot mapping should ever be changed. */ if($slotMapping < 0 or $slotMapping >= $this->getSize()){ //Mapping was not in range of the inventory, set it to -1 //This happens if the client selected a blank slot (sends 255) $slotMapping = -1; } $item = $this->getItem($slotMapping); if($this->getHolder() instanceof Player){ Server::getInstance()->getPluginManager()->callEvent($ev = new PlayerItemHeldEvent($this->getHolder(), $item, $slotMapping, $hotbarSlotIndex)); if($ev->isCancelled()){ $this->sendHeldItem($this->getHolder()); $this->sendContents($this->getHolder()); return; } } if(($key = array_search($slotMapping, $this->hotbar)) !== false and $slotMapping !== -1){ /* Do not do slot swaps if the slot was null * Chosen slot is already linked to a hotbar slot, swap the two slots around. * This will already have been done on the client-side so no changes need to be sent. */ $this->hotbar[$key] = $this->hotbar[$this->itemInHandIndex]; } $this->hotbar[$this->itemInHandIndex] = $slotMapping; } $this->sendHeldItem($this->getHolder()->getViewers()); if($sendToHolder){ $this->sendHeldItem($this->getHolder()); } } } /** * @return Item * * Returns the item the player is currently holding */ public function getItemInHand(){ $item = $this->getItem($this->getHeldItemSlot()); if($item instanceof Item){ return $item; }else{ return Item::get(Item::AIR, 0, 0); } } /** * @param Item $item * * @return bool * * Sets the item in the inventory slot the player is currently holding. */ public function setItemInHand(Item $item){ return $this->setItem($this->getHeldItemSlot(), $item); } /** * @return int[] * * Returns an array of hotbar indices */ public function getHotbar(){ return $this->hotbar; } /** * @return int * * Returns the inventory slot index of the currently equipped slot */ public function getHeldItemSlot(){ return $this->getHotbarSlotIndex($this->itemInHandIndex); } /** * @deprecated * @param int $slot */ public function setHeldItemSlot($slot){ } /** * @param Player|Player[] $target */ public function sendHeldItem($target){ $item = $this->getItemInHand(); $pk = new MobEquipmentPacket(); $pk->eid = ($target === $this->getHolder() ? 0 : $this->getHolder()->getId()); $pk->item = $item; $pk->slot = $this->getHeldItemSlot(); $pk->selectedSlot = $this->getHeldItemIndex(); if(!is_array($target)){ $target->dataPacket($pk); if($target === $this->getHolder()){ $this->sendSlot($this->getHeldItemSlot(), $target); } }else{ $this->getHolder()->getLevel()->getServer()->broadcastPacket($target, $pk); foreach($target as $player){ if($player === $this->getHolder()){ $this->sendSlot($this->getHeldItemSlot(), $player); break; } } } } public function onSlotChange($index, $before, $send){ if($send){ $holder = $this->getHolder(); if(!$holder instanceof Player or !$holder->spawned){ return; } parent::onSlotChange($index, $before, $send); } if($index === $this->itemInHandIndex){ $this->sendHeldItem($this->getHolder()->getViewers()); if($send){ $this->sendHeldItem($this->getHolder()); } }elseif($index >= $this->getSize()){ //Armour equipment $this->sendArmorSlot($index, $this->getViewers()); $this->sendArmorSlot($index, $this->getHolder()->getViewers()); } } public function getHotbarSize(){ return 9; } public function getArmorItem($index){ return $this->getItem($this->getSize() + $index); } public function setArmorItem($index, Item $item){ return $this->setItem($this->getSize() + $index, $item); } public function damageArmor($index, $cost){ $this->slots[$this->getSize() + $index]->useOn($this->slots[$this->getSize() + $index], $cost); if($this->slots[$this->getSize() + $index]->getDamage() >= $this->slots[$this->getSize() + $index]->getMaxDurability()){ $this->setItem($this->getSize() + $index, Item::get(Item::AIR, 0, 0)); } $this->sendArmorContents($this->getViewers()); } public function getHelmet(){ return $this->getItem($this->getSize()); } public function getChestplate(){ return $this->getItem($this->getSize() + 1); } public function getLeggings(){ return $this->getItem($this->getSize() + 2); } public function getBoots(){ return $this->getItem($this->getSize() + 3); } public function setHelmet(Item $helmet){ return $this->setItem($this->getSize(), $helmet); } public function setChestplate(Item $chestplate){ return $this->setItem($this->getSize() + 1, $chestplate); } public function setLeggings(Item $leggings){ return $this->setItem($this->getSize() + 2, $leggings); } public function setBoots(Item $boots){ return $this->setItem($this->getSize() + 3, $boots); } public function setItem($index, Item $item, $send = true){ if($index < 0 or $index >= $this->size){ return false; }elseif($item->getId() === 0 or $item->getCount() <= 0){ return $this->clear($index, $send); } if($index >= $this->getSize()){ //Armor change Server::getInstance()->getPluginManager()->callEvent($ev = new EntityArmorChangeEvent($this->getHolder(), $this->getItem($index), $item, $index)); if($ev->isCancelled() and $this->getHolder() instanceof Human){ $this->sendArmorSlot($index, $this->getViewers()); return false; } $item = $ev->getNewItem(); }else{ Server::getInstance()->getPluginManager()->callEvent($ev = new EntityInventoryChangeEvent($this->getHolder(), $this->getItem($index), $item, $index)); if($ev->isCancelled()){ $this->sendSlot($index, $this->getViewers()); return false; } $item = $ev->getNewItem(); } $old = $this->getItem($index); $this->slots[$index] = clone $item; $this->onSlotChange($index, $old, $send); return true; } public function clear($index, $send = true){ if(isset($this->slots[$index])){ $item = Item::get(Item::AIR, 0, 0); $old = $this->slots[$index]; if($index >= $this->getSize() and $index < $this->size){ //Armor change Server::getInstance()->getPluginManager()->callEvent($ev = new EntityArmorChangeEvent($this->getHolder(), $old, $item, $index)); if($ev->isCancelled()){ if($index >= $this->size){ $this->sendArmorSlot($index, $this->getViewers()); }else{ $this->sendSlot($index, $this->getViewers()); } return false; } $item = $ev->getNewItem(); }else{ Server::getInstance()->getPluginManager()->callEvent($ev = new EntityInventoryChangeEvent($this->getHolder(), $old, $item, $index)); if($ev->isCancelled()){ if($index >= $this->size){ $this->sendArmorSlot($index, $this->getViewers()); }else{ $this->sendSlot($index, $this->getViewers()); } return false; } $item = $ev->getNewItem(); } if($item->getId() !== Item::AIR){ $this->slots[$index] = clone $item; }else{ unset($this->slots[$index]); } $this->onSlotChange($index, $old, $send); } return true; } /** * @return Item[] */ public function getArmorContents(){ $armor = []; for($i = 0; $i < 4; ++$i){ $armor[$i] = $this->getItem($this->getSize() + $i); } return $armor; } public function clearAll(){ $limit = $this->getSize() + 4; for($index = 0; $index < $limit; ++$index){ $this->clear($index, false); } $this->hotbar = range(0, $this->getHotbarSize() - 1, 1); $this->sendContents($this->getViewers()); } /** * @param Player|Player[] $target */ public function sendArmorContents($target){ if($target instanceof Player){ $target = [$target]; } $armor = $this->getArmorContents(); $pk = new MobArmorEquipmentPacket(); $pk->eid = $this->getHolder()->getId(); $pk->slots = $armor; $pk->encode(); $pk->isEncoded = true; foreach($target as $player){ if($player === $this->getHolder()){ $pk2 = new ContainerSetContentPacket(); $pk2->windowid = ContainerSetContentPacket::SPECIAL_ARMOR; $pk2->slots = $armor; $player->dataPacket($pk2); }else{ $player->dataPacket($pk); } } } /** * @param Item[] $items */ public function setArmorContents(array $items){ for($i = 0; $i < 4; ++$i){ if(!isset($items[$i]) or !($items[$i] instanceof Item)){ $items[$i] = Item::get(Item::AIR, 0, 0); } if($items[$i]->getId() === Item::AIR){ $this->clear($this->getSize() + $i); }else{ $this->setItem($this->getSize() + $i, $items[$i]); } } } /** * @param int $index * @param Player|Player[] $target */ public function sendArmorSlot($index, $target){ if($target instanceof Player){ $target = [$target]; } $armor = $this->getArmorContents(); $pk = new MobArmorEquipmentPacket(); $pk->eid = $this->getHolder()->getId(); $pk->slots = $armor; $pk->encode(); $pk->isEncoded = true; foreach($target as $player){ if($player === $this->getHolder()){ /** @var Player $player */ $pk2 = new ContainerSetSlotPacket(); $pk2->windowid = ContainerSetContentPacket::SPECIAL_ARMOR; $pk2->slot = $index - $this->getSize(); $pk2->item = $this->getItem($index); $player->dataPacket($pk2); }else{ $player->dataPacket($pk); } } } /** * @param Player|Player[] $target */ public function sendContents($target){ if($target instanceof Player){ $target = [$target]; } $pk = new ContainerSetContentPacket(); $pk->slots = []; for($i = 0; $i < $this->getSize(); ++$i){ //Do not send armor by error here $pk->slots[$i] = $this->getItem($i); } //Because PE is stupid and shows 9 less slots than you send it, give it 9 dummy slots so it shows all the REAL slots. for($i = $this->getSize(); $i < $this->getSize() + $this->getHotbarSize(); ++$i){ $pk->slots[$i] = Item::get(Item::AIR, 0, 0); } foreach($target as $player){ $pk->hotbar = []; if($player === $this->getHolder()){ for($i = 0; $i < $this->getHotbarSize(); ++$i){ $index = $this->getHotbarSlotIndex($i); $pk->hotbar[$i] = $index <= -1 ? -1 : $index + $this->getHotbarSize(); } } if(($id = $player->getWindowId($this)) === -1 or $player->spawned !== true){ $this->close($player); continue; } $pk->windowid = $id; $player->dataPacket(clone $pk); } } /** * @param int $index * @param Player|Player[] $target */ public function sendSlot($index, $target){ if($target instanceof Player){ $target = [$target]; } $pk = new ContainerSetSlotPacket(); $pk->slot = $index; $pk->item = clone $this->getItem($index); foreach($target as $player){ if($player === $this->getHolder()){ /** @var Player $player */ $pk->windowid = 0; $player->dataPacket(clone $pk); }else{ if(($id = $player->getWindowId($this)) === -1){ $this->close($player); continue; } $pk->windowid = $id; $player->dataPacket(clone $pk); } } } /** * @return Human|Player */ public function getHolder(){ return parent::getHolder(); } } ================================================ FILE: src/pocketmine/inventory/Recipe.php ================================================ 3){ throw new \InvalidStateException("Crafting rows should be 1, 2, 3 wide, not $width"); } $this->ingredients[] = array_fill(0, $width, null); } $this->output = clone $result; } public function getWidth(){ return count($this->ingredients[0]); } public function getHeight(){ return count($this->ingredients); } public function getResult(){ return $this->output; } public function getId(){ return $this->id; } public function setId(UUID $id){ if($this->id !== null){ throw new \InvalidStateException("Id is already set"); } $this->id = $id; } public function addIngredient($x, $y, Item $item){ $this->ingredients[$y][$x] = clone $item; return $this; } /** * @param string $key * @param Item $item * * @return $this * @throws \Exception */ public function setIngredient($key, Item $item){ if(!array_key_exists($key, $this->shape)){ throw new \Exception("Symbol does not appear in the shape: " . $key); } $item->setCount(1); $this->fixRecipe($key, $item); return $this; } protected function fixRecipe($key, $item){ foreach($this->shapeItems[$key] as $entry){ $this->ingredients[$entry->y][$entry->x] = clone $item; } } /** * @return Item[][] */ public function getIngredientMap(){ $ingredients = []; foreach($this->ingredients as $y => $row){ $ingredients[$y] = []; foreach($row as $x => $ingredient){ if($ingredient !== null){ $ingredients[$y][$x] = clone $ingredient; }else{ $ingredients[$y][$x] = Item::get(Item::AIR); } } } return $ingredients; } /** * @return Item[] */ public function getIngredientList(){ $ingredients = []; for ($x = 0; $x < 3; ++$x){ for ($y = 0; $y < 3; ++$y){ if (!empty($this->ingredients[$x][$y])){ if ($this->ingredients[$x][$y]->getId() !== Item::AIR){ $ingredients[] = clone $this->ingredients[$x][$y]; } } } } return $ingredients; } /** * @param $x * @param $y * @return null|Item */ public function getIngredient($x, $y){ return isset($this->ingredients[$y][$x]) ? $this->ingredients[$y][$x] : Item::get(Item::AIR); } /** * @return string[] */ public function getShape(){ return $this->shape; } public function registerToCraftingManager(){ Server::getInstance()->getCraftingManager()->registerShapedRecipe($this); } } ================================================ FILE: src/pocketmine/inventory/ShapedRecipeFromJson.php ================================================ 3){ throw new \InvalidStateException("Crafting rows should be 1, 2, 3 wide, not $width"); } $this->ingredients[] = array_fill(0, $width, null); } $this->output = clone $result; } public function getWidth(){ return count($this->ingredients[0]); } public function getHeight(){ return count($this->ingredients); } public function getResult(){ return $this->output; } public function getId(){ return $this->id; } public function setId(UUID $id){ if($this->id !== null){ throw new \InvalidStateException("Id is already set"); } $this->id = $id; } public function addIngredient($x, $y, Item $item){ $this->ingredients[$y][$x] = clone $item; return $this; } /** * @param string $key * @param Item $item * * @return $this * @throws \Exception */ public function setIngredient($key, Item $item){ if(!array_key_exists($key, $this->shape)){ throw new \Exception("Symbol does not appear in the shape: " . $key); } $this->fixRecipe($key, $item); return $this; } protected function fixRecipe($key, $item){ foreach($this->shapeItems[$key] as $entry){ $this->ingredients[$entry->y][$entry->x] = clone $item; } } /** * @return Item[][] */ public function getIngredientMap(){ $ingredients = []; foreach($this->ingredients as $y => $row){ $ingredients[$y] = []; foreach($row as $x => $ingredient){ if($ingredient !== null){ $ingredients[$y][$x] = clone $ingredient; }else{ $ingredients[$y][$x] = Item::get(Item::AIR); } } } return $ingredients; } /** * @param $x * @param $y * @return null|Item */ public function getIngredient($x, $y){ return isset($this->ingredients[$y][$x]) ? $this->ingredients[$y][$x] : Item::get(Item::AIR); } /** * @return string[] */ public function getShape(){ return $this->shape; } public function registerToCraftingManager(){ Server::getInstance()->getCraftingManager()->registerShapedRecipe($this); } } ================================================ FILE: src/pocketmine/inventory/ShapelessRecipe.php ================================================ output = clone $result; } public function getId(){ return $this->id; } public function setId(UUID $id){ if($this->id !== null){ throw new \InvalidStateException("Id is already set"); } $this->id = $id; } public function getResult(){ return clone $this->output; } /** * @param Item $item * * @returns ShapelessRecipe * * @throws \InvalidArgumentException */ public function addIngredient(Item $item){ if(count($this->ingredients) >= 9){ throw new \InvalidArgumentException("Shapeless recipes cannot have more than 9 ingredients"); } $it = clone $item; $it->setCount(1); while($item->getCount() > 0){ $this->ingredients[] = clone $it; $item->setCount($item->getCount() - 1); } return $this; } /** * @param Item $item * * @return $this */ public function removeIngredient(Item $item){ foreach($this->ingredients as $index => $ingredient){ if($item->getCount() <= 0){ break; } if($ingredient->equals($item, !$item->hasAnyDamageValue(), $item->hasCompoundTag())){ unset($this->ingredients[$index]); $item->setCount($item->getCount() - 1); } } return $this; } /** * @return Item[] */ public function getIngredientList(){ $ingredients = []; foreach($this->ingredients as $ingredient){ $ingredients[] = clone $ingredient; } return $ingredients; } /** * @return int */ public function getIngredientCount(){ $count = 0; foreach($this->ingredients as $ingredient){ $count += $ingredient->getCount(); } return $count; } public function registerToCraftingManager(){ Server::getInstance()->getCraftingManager()->registerShapelessRecipe($this); } } ================================================ FILE: src/pocketmine/inventory/SimpleTransactionQueue.php ================================================ player = $player; $this->transactionQueue = new \SplQueue(); $this->transactionsToRetry = new \SplQueue(); } /** * @return Player */ public function getPlayer(){ return $this->player; } public function getInventories(){ return $this->inventories; } public function getTransactions(){ return $this->transactionQueue; } public function getTransactionCount(){ return $this->transactionCount; } public function addTransaction(Transaction $transaction){ $this->transactionQueue->enqueue($transaction); if($transaction->getInventory() instanceof Inventory){ /** For dropping items, the target inventory is open air, a.k.a. null. */ $this->inventories[spl_object_hash($transaction)] = $transaction->getInventory(); } $this->lastUpdate = microtime(true); $this->transactionCount += 1; } public function execute(){ /** @var Transaction[] */ $failed = []; while(!$this->transactionsToRetry->isEmpty()){ //Some failed transactions are waiting from the previous execution to be retried $this->transactionQueue->enqueue($this->transactionsToRetry->dequeue()); } if(!$this->transactionQueue->isEmpty()){ $this->player->getServer()->getPluginManager()->callEvent($ev = new InventoryTransactionEvent($this)); }else{ return; } while(!$this->transactionQueue->isEmpty()){ $transaction = $this->transactionQueue->dequeue(); if($ev->isCancelled()){ $this->transactionCount -= 1; $transaction->sendSlotUpdate($this->player); //Send update back to client for cancelled transaction unset($this->inventories[spl_object_hash($transaction)]); continue; }elseif(!$transaction->execute($this->player)){ $transaction->addFailure(); if($transaction->getFailures() >= self::DEFAULT_ALLOWED_RETRIES){ /* Transaction failed completely after several retries, hold onto it to send a slot update */ $this->transactionCount -= 1; $failed[] = $transaction; }else{ /* Add the transaction to the back of the queue to be retried on the next tick */ $this->transactionsToRetry->enqueue($transaction); } continue; } $this->transactionCount -= 1; $transaction->setSuccess(); $transaction->sendSlotUpdate($this->player); unset($this->inventories[spl_object_hash($transaction)]); } foreach($failed as $f){ $f->sendSlotUpdate($this->player); unset($this->inventories[spl_object_hash($f)]); } } } ================================================ FILE: src/pocketmine/inventory/TemporaryInventory.php ================================================ getContents() as $slot => $item){ if($slot === $this->getResultSlotIndex()){ //Do not drop the item in the result slot - it is a virtual item and does not actually exist. continue; } $who->dropItem($item); } $this->clearAll(); } } ================================================ FILE: src/pocketmine/inventory/Transaction.php ================================================ block = Block::get(Item::ACACIA_DOOR_BLOCK); parent::__construct(self::ACACIA_DOOR, 0, $count, "Acacia Door"); } } ================================================ FILE: src/pocketmine/item/Apple.php ================================================ isUnbreakable()){ return true; } $unbreakings = [ 0 => 100, 1 => 80, 2 => 73, 3 => 70 ]; $unbreakingl = $this->getEnchantmentLevel(Enchantment::TYPE_MINING_DURABILITY); if(mt_rand(1, 100) > $unbreakings[$unbreakingl]){ return true; } $this->setDamage($this->getDamage() + $cost); if($this->getDamage() >= $this->getMaxDurability()){ $this->setCount(0); } return true; } public function isUnbreakable(){ $tag = $this->getNamedTagEntry("Unbreakable"); return $tag !== null and $tag->getValue() > 0; } public function setCustomColor(Color $color){ if(($hasTag = $this->hasCompoundTag())){ $tag = $this->getNamedTag(); }else{ $tag = new CompoundTag("", []); } $tag->customColor = new IntTag("customColor", $color->getColorCode()); $this->setCompoundTag($tag); } public function getCustomColor(){ if(!$this->hasCompoundTag()) return null; $tag = $this->getNamedTag(); if(isset($tag->customColor)){ return $tag["customColor"]; } return null; } public function clearCustomColor(){ if(!$this->hasCompoundTag()) return; $tag = $this->getNamedTag(); if(isset($tag->customColor)){ unset($tag->customColor); } $this->setCompoundTag($tag); } public function getArmorTier(){ return false; } public function getArmorType(){ return false; } public function getMaxDurability(){ return false; } public function getArmorValue(){ return false; } public function isHelmet(){ return false; } public function isChestplate(){ return false; } public function isLeggings(){ return false; } public function isBoots(){ return false; } } ================================================ FILE: src/pocketmine/item/Arrow.php ================================================ block = Block::get(Item::BED_BLOCK); parent::__construct(self::BED, 0, $count, "Bed"); } public function getMaxStackSize() : int{ return 1; } } ================================================ FILE: src/pocketmine/item/Beetroot.php ================================================ block = Block::get(Item::BEETROOT_BLOCK); parent::__construct(self::BEETROOT_SEEDS, 0, $count, "Beetroot Seeds"); } } ================================================ FILE: src/pocketmine/item/BeetrootSoup.php ================================================ block = Block::get(Item::BIRCH_DOOR_BLOCK); parent::__construct(self::BIRCH_DOOR, 0, $count, "Birch Door"); } } ================================================ FILE: src/pocketmine/item/BlazePowder.php ================================================ getSide($face); $boat = new BoatEntity($player->getLevel()->getChunk($realPos->getX() >> 4, $realPos->getZ() >> 4), new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $realPos->getX() + 0.5), new DoubleTag("", $realPos->getY()), new DoubleTag("", $realPos->getZ() + 0.5) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", 0), new DoubleTag("", 0), new DoubleTag("", 0) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", 0), new FloatTag("", 0) ]), "WoodID" => new IntTag("WoodID", $this->getDamage()) ])); $boat->spawnToAll(); if($player->isSurvival()) { $item = $player->getInventory()->getItemInHand(); $count = $item->getCount(); if(--$count <= 0){ $player->getInventory()->setItemInHand(Item::get(Item::AIR)); return true; } $item->setCount($count); $player->getInventory()->setItemInHand($item); } return true; } } ================================================ FILE: src/pocketmine/item/Bone.php ================================================ block = Block::get(Block::BREWING_STAND_BLOCK); parent::__construct(self::BREWING_STAND, $meta, $count, "Brewing Stand"); } } ================================================ FILE: src/pocketmine/item/Brick.php ================================================ meta); if($targetBlock instanceof Air){ if($target instanceof Liquid and $target->getDamage() === 0){ $result = clone $this; $id = $target->getId(); if($id == self::STILL_WATER){ $id = self::WATER; } if($id == self::STILL_LAVA){ $id = self::LAVA; } $result->setDamage($id); $player->getServer()->getPluginManager()->callEvent($ev = new PlayerBucketFillEvent($player, $block, $face, $this, $result)); if(!$ev->isCancelled()){ $player->getLevel()->setBlock($target, new Air(), true, true); if($player->isSurvival()){ $player->getInventory()->setItemInHand($ev->getItem()); } return true; }else{ $player->getInventory()->sendContents($player); } } }elseif($targetBlock instanceof Liquid){ $result = clone $this; $result->setDamage(0); $player->getServer()->getPluginManager()->callEvent($ev = new PlayerBucketEmptyEvent($player, $block, $face, $this, $result)); if(!$ev->isCancelled()){ //Only disallow water placement in the Nether, allow other liquids to be placed //In vanilla, water buckets are emptied when used in the Nether, but no water placed. if(!($player->getLevel()->getDimension() === Level::DIMENSION_NETHER and $targetBlock->getID() === self::WATER)){ $player->getLevel()->setBlock($block, $targetBlock, true, true); } if($player->isSurvival()){ $player->getInventory()->setItemInHand($ev->getItem()); } return true; }else{ $player->getInventory()->sendContents($player); } } return false; } } ================================================ FILE: src/pocketmine/item/Cake.php ================================================ block = Block::get(Item::CAKE_BLOCK); parent::__construct(self::CAKE, 0, $count, "Cake"); } public function getMaxStackSize() : int{ return 1; } } ================================================ FILE: src/pocketmine/item/Camera.php ================================================ block = Block::get(Item::CARROT_BLOCK); parent::__construct(self::CARROT, 0, $count, "Carrot"); } public function getFoodRestore() : int{ return 3; } public function getSaturationRestore() : float{ return 4.8; } } ================================================ FILE: src/pocketmine/item/Cauldron.php ================================================ block = Block::get(Block::CAULDRON_BLOCK); parent::__construct(self::CAULDRON, $meta, $count, "Cauldron"); } public function getMaxStackSize() : int{ return 1; } } ================================================ FILE: src/pocketmine/item/ChainBoots.php ================================================ meta === 1){ $this->name = "Charcoal"; } } } ================================================ FILE: src/pocketmine/item/Compass.php ================================================ meta === self::FISH_SALMON ? 6 : 5; } public function getSaturationRestore() : float{ return $this->meta === self::FISH_SALMON ? 9.6 : 6; } } ================================================ FILE: src/pocketmine/item/CookedMutton.php ================================================ block = Block::get(Item::DARK_OAK_DOOR_BLOCK); parent::__construct(self::DARK_OAK_DOOR, 0, $count, "Dark Oak Door"); } } ================================================ FILE: src/pocketmine/item/Diamond.php ================================================ block = Block::get(Item::COCOA_BLOCK); parent::__construct(self::DYE, 3, $count, "Cocoa Beans"); } else { parent::__construct(self::DYE, $meta, $count, $this->getNameByMeta($meta)); } } public function getNameByMeta(int $meta) : string{ switch($meta){ case self::BLACK: return "Ink Sac"; case self::RED: return "Rose Red"; case self::GREEN: return "Cactus Green"; case self::BROWN: return "Cocoa Beans"; case self::BLUE: return "Lapis Lazuli"; case self::PURPLE: return "Purple Dye"; case self::CYAN: return "Cyan Dye"; case self::SILVER: return "Light Gray Dye"; case self::GRAY: return "Gray Dye"; case self::PINK: return "Pink Dye"; case self::LIME: return "Lime Dye"; case self::YELLOW: return "Dandelion Yellow"; case self::LIGHT_BLUE: return "Light Blue Dye"; case self::MAGENTA: return "Magenta Dye"; case self::ORANGE: return "Orange Dye"; case self::WHITE: return "Bone Meal"; default: return "Dye"; } } } ================================================ FILE: src/pocketmine/item/Egg.php ================================================ canBeConsumed(); } public function getFoodRestore() : int{ return 4; } public function getSaturationRestore() : float{ return 9.6; } public function getAdditionalEffects() : array{ return [ Effect::getEffect(Effect::REGENERATION)->setDuration(600)->setAmplifier(4), Effect::getEffect(Effect::ABSORPTION)->setDuration(2400)->setAmplifier(3), Effect::getEffect(Effect::DAMAGE_RESISTANCE)->setDuration(6000)->setAmplifier(0), Effect::getEffect(Effect::FIRE_RESISTANCE)->setDuration(6000)->setAmplifier(0), ]; } } ================================================ FILE: src/pocketmine/item/EnchantingBottle.php ================================================ meta === self::FISH_SALMON){ $name = "Raw Salmon"; }elseif($this->meta === self::FISH_CLOWNFISH){ $name = "Clownfish"; }elseif($this->meta === self::FISH_PUFFERFISH){ $name = "Pufferfish"; } parent::__construct(self::RAW_FISH, $meta, $count, $name); } public function getFoodRestore() : int{ if($this->meta === self::FISH_FISH){ return 2; }elseif($this->meta === self::FISH_SALMON){ return 2; }elseif($this->meta === self::FISH_CLOWNFISH){ return 1; }elseif($this->meta === self::FISH_PUFFERFISH){ return 1.2; } return 0; } public function getSaturationRestore() : float{ if($this->meta === self::FISH_FISH){ return 0.4; }elseif($this->meta === self::FISH_SALMON){ return 0.4; }elseif($this->meta === self::FISH_CLOWNFISH){ return 0.2; }elseif($this->meta === self::FISH_PUFFERFISH){ return 0.2; } return 0; } public function getAdditionalEffects() : array{ return $this->meta === self::FISH_PUFFERFISH ? [ Effect::getEffect(Effect::HUNGER)->setDuration(300)->setAmplifier(2), Effect::getEffect(Effect::NAUSEA)->setDuration(300)->setAmplifier(1), Effect::getEffect(Effect::POISON)->setDuration(1200)->setAmplifier(3), ] : []; } } ================================================ FILE: src/pocketmine/item/FishingRod.php ================================================ temporalVector === null){ $this->temporalVector = new Vector3(0, 0, 0); } } public function canBeActivated() : bool{ return true; } public function onActivate(Level $level, Player $player, Block $block, Block $target, $face, $fx, $fy, $fz){ if($target->getId() === Block::OBSIDIAN and $player->getServer()->netherEnabled){//黑曜石 4*5最小 23*23最大 //$level->setBlock($block, new Fire(), true); $tx = $target->getX(); $ty = $target->getY(); $tz = $target->getZ(); //x方向 $x_max = $tx;//x最大值 $x_min = $tx;//x最小值 for($x = $tx + 1; $level->getBlock($this->temporalVector->setComponents($x, $ty, $tz))->getId() == Block::OBSIDIAN; $x++){ $x_max++; } for($x = $tx - 1; $level->getBlock($this->temporalVector->setComponents($x, $ty, $tz))->getId() == Block::OBSIDIAN; $x--){ $x_min--; } $count_x = $x_max - $x_min + 1;//x方向方块 if($count_x >= 4 and $count_x <= 23){//4 23 $x_max_y = $ty;//x最大值时的y最大值 $x_min_y = $ty;//x最小值时的y最大值 for($y = $ty; $level->getBlock($this->temporalVector->setComponents($x_max, $y, $tz))->getId() == Block::OBSIDIAN; $y++){ $x_max_y++; } for($y = $ty; $level->getBlock($this->temporalVector->setComponents($x_min, $y, $tz))->getId() == Block::OBSIDIAN; $y++){ $x_min_y++; } $y_max = min($x_max_y, $x_min_y) - 1;//y最大值 $count_y = $y_max - $ty + 2;//方向方块 //Server::getInstance()->broadcastMessage("$y_max $x_max_y $x_min_y $x_max $x_min"); if($count_y >= 5 and $count_y <= 23){//5 23 $count_up = 0;//上面 for($ux = $x_min; ($level->getBlock($this->temporalVector->setComponents($ux, $y_max, $tz))->getId() == Block::OBSIDIAN and $ux <= $x_max); $ux++){ $count_up++; } //Server::getInstance()->broadcastMessage("$count_up $count_x"); if($count_up == $count_x){ for($px = $x_min + 1; $px < $x_max; $px++){ for($py = $ty + 1; $py < $y_max; $py++){ $level->setBlock($this->temporalVector->setComponents($px, $py, $tz), new Portal()); } } if($player->isSurvival()){ $this->useOn($block, 2); $player->getInventory()->setItemInHand($this); } return true; } } } //z方向 $z_max = $tz;//z最大值 $z_min = $tz;//z最小值 $count_z = 0;//z方向方块 for($z = $tz + 1; $level->getBlock($this->temporalVector->setComponents($tx, $ty, $z))->getId() == Block::OBSIDIAN; $z++){ $z_max++; } for($z = $tz - 1; $level->getBlock($this->temporalVector->setComponents($tx, $ty, $z))->getId() == Block::OBSIDIAN; $z--){ $z_min--; } $count_z = $z_max - $z_min + 1; if($count_z >= 4 and $count_z <= 23){//4 23 $z_max_y = $ty;//z最大值时的y最大值 $z_min_y = $ty;//z最小值时的y最大值 for($y = $ty; $level->getBlock($this->temporalVector->setComponents($tx, $y, $z_max))->getId() == Block::OBSIDIAN; $y++){ $z_max_y++; } for($y = $ty; $level->getBlock($this->temporalVector->setComponents($tx, $y, $z_min))->getId() == Block::OBSIDIAN; $y++){ $z_min_y++; } $y_max = min($z_max_y, $z_min_y) - 1;//y最大值 $count_y = $y_max - $ty + 2;//方向方块 if($count_y >= 5 and $count_y <= 23){//5 23 $count_up = 0;//上面 for($uz = $z_min; ($level->getBlock($this->temporalVector->setComponents($tx, $y_max, $uz))->getId() == Block::OBSIDIAN and $uz <= $z_max); $uz++){ $count_up++; } //Server::getInstance()->broadcastMessage("$count_up $count_z"); if($count_up == $count_z){ for($pz = $z_min + 1; $pz < $z_max; $pz++){ for($py = $ty + 1; $py < $y_max; $py++){ $level->setBlock($this->temporalVector->setComponents($tx, $py, $pz), new Portal()); } } if($player->isSurvival()){ $this->useOn($block, 2); $player->getInventory()->setItemInHand($this); } return true; } } } //return true; } if($block->getId() === self::AIR and ($target instanceof Solid)){ $level->setBlock($block, new Fire(), true); /** @var Fire $block */ $block = $level->getBlock($block); if($block->getSide(Vector3::SIDE_DOWN)->isTopFacingSurfaceSolid() or $block->canNeighborBurn()){ $level->scheduleUpdate($block, $block->getTickRate() + mt_rand(0, 10)); // return true; } if($player->isSurvival()){ $this->useOn($block, 2);//耐久跟报废分别写在 tool 跟 level 了 $player->getInventory()->setItemInHand($this); } return true; } return false; } } ================================================ FILE: src/pocketmine/item/FlowerPot.php ================================================ block = Block::get(Item::FLOWER_POT_BLOCK); parent::__construct(self::FLOWER_POT, 0, $count, "Flower Pot"); } public function getMaxStackSize() : int{ return 64; } } ================================================ FILE: src/pocketmine/item/Food.php ================================================ getFood() < $entity->getMaxFood()) and $this->canBeConsumed(); } public function getResidue(){ if($this->getCount() === 1){ return Item::get(0); }else{ $new = clone $this; $new->count--; return $new; } } public function getAdditionalEffects() : array{ return []; } public function onConsume(Entity $human){ $pk = new EntityEventPacket(); $pk->eid = $human->getId(); $pk->event = EntityEventPacket::USE_ITEM; if($human instanceof Player){ $human->dataPacket($pk); } $human->getServer()->broadcastPacket($human->getViewers(), $pk); Server::getInstance()->getPluginManager()->callEvent($ev = new EntityEatItemEvent($human, $this)); if(!$ev->isCancelled()){ $human->addSaturation($ev->getSaturationRestore()); $human->addFood($ev->getFoodRestore()); foreach($ev->getAdditionalEffects() as $effect){ $human->addEffect($effect); } $human->getInventory()->setItemInHand($ev->getResidue()); } } } ================================================ FILE: src/pocketmine/item/FoodSource.php ================================================ isSurvival() !== true){ return false; } if($target->getId() === Block::STILL_WATER or $target->getId() === Block::WATER){ $player->getServer()->getPluginManager()->callEvent($ev = new PlayerGlassBottleEvent($player, $target, $this)); if($ev->isCancelled()){ return false; }else{ if($this->count <= 1){ $player->getInventory()->setItemInHand(Item::get(Item::POTION, 0, 1)); return true; }else{ $this->count--; $player->getInventory()->setItemInHand($this); } if($player->getInventory()->canAddItem(Item::get(Item::POTION, 0, 1)) === true){ $player->getInventory()->AddItem(Item::get(Item::POTION, 0, 1)); }else{ $motion = $player->getDirectionVector()->multiply(0.4); $position = clone $player->getPosition(); $player->getLevel()->dropItem($position->add(0 , 0.5, 0), Item::get(Item::POTION, 0, 1) , $motion, 40); } return true; } } return false; } } ================================================ FILE: src/pocketmine/item/GlisteringMelon.php ================================================ canBeConsumed(); } public function getFoodRestore() : int{ return 4; } public function getSaturationRestore() : float{ return 9.6; } public function getAdditionalEffects() : array{ return [ Effect::getEffect(Effect::REGENERATION)->setDuration(100)->setAmplifier(1), Effect::getEffect(Effect::ABSORPTION)->setDuration(2400)->setAmplifier(0) ]; } } ================================================ FILE: src/pocketmine/item/GoldenCarrot.php ================================================ block = Block::get(Block::HOPPER_BLOCK); parent::__construct(self::HOPPER, 0, $count, "Hopper"); } } ================================================ FILE: src/pocketmine/item/IronAxe.php ================================================ block = Block::get(Item::IRON_DOOR_BLOCK); parent::__construct(self::IRON_DOOR, 0, $count, "Iron Door"); } } ================================================ FILE: src/pocketmine/item/IronHelmet.php ================================================ read($tag); return self::$cachedParser->getData(); } private static function writeCompoundTag(CompoundTag $tag) : string{ if(self::$cachedParser === null){ self::$cachedParser = new NBT(NBT::LITTLE_ENDIAN); } self::$cachedParser->setData($tag); return self::$cachedParser->write(); } /** @var \SplFixedArray */ public static $list = null; protected $block; protected $id; protected $meta; private $tags = ""; private $cachedNBT = null; public $count; protected $durability = 0; protected $name; public function canBeActivated() :bool{ return false; } public static function init($readFromJson = false){ if(self::$list === null){ //TODO: Sort this mess into some kind of order self::$list = new \SplFixedArray(65536); self::$list[self::SUGARCANE] = Sugarcane::class; self::$list[self::ENDER_PEARL] = EnderPearl::class; //self::$list[self::EYE_OF_ENDER] = EyeOfEnder::class; //self::$list[self::DRAGONS_BREATH] = DragonsBreath::class; //self::$list[self::SHULKER_SHELL] = ShulkerShell::class; //self::$list[self::POPPED_CHORUS_FRUIT] = PoppedChorusFruit::class; self::$list[self::WHEAT_SEEDS] = WheatSeeds::class; self::$list[self::PUMPKIN_SEEDS] = PumpkinSeeds::class; self::$list[self::MELON_SEEDS] = MelonSeeds::class; self::$list[self::MUSHROOM_STEW] = MushroomStew::class; self::$list[self::RABBIT_STEW] = RabbitStew::class; self::$list[self::BEETROOT_SOUP] = BeetrootSoup::class; self::$list[self::BEETROOT_SEEDS] = BeetrootSeeds::class; self::$list[self::SIGN] = Sign::class; self::$list[self::WOODEN_DOOR] = WoodenDoor::class; self::$list[self::SPRUCE_DOOR] = SpruceDoor::class; self::$list[self::BIRCH_DOOR] = BirchDoor::class; self::$list[self::JUNGLE_DOOR] = JungleDoor::class; self::$list[self::ACACIA_DOOR] = AcaciaDoor::class; self::$list[self::DARK_OAK_DOOR] = DarkOakDoor::class; self::$list[self::BUCKET] = Bucket::class; self::$list[self::IRON_DOOR] = IronDoor::class; self::$list[self::CAKE] = Cake::class; self::$list[self::BED] = Bed::class; self::$list[self::PAINTING] = Painting::class; self::$list[self::COAL] = Coal::class; self::$list[self::APPLE] = Apple::class; self::$list[self::SPAWN_EGG] = SpawnEgg::class; self::$list[self::DIAMOND] = Diamond::class; self::$list[self::STICK] = Stick::class; self::$list[self::SNOWBALL] = Snowball::class; self::$list[self::BOWL] = Bowl::class; self::$list[self::FEATHER] = Feather::class; self::$list[self::BRICK] = Brick::class; self::$list[self::LEATHER_CAP] = LeatherCap::class; self::$list[self::LEATHER_TUNIC] = LeatherTunic::class; self::$list[self::LEATHER_PANTS] = LeatherPants::class; self::$list[self::LEATHER_BOOTS] = LeatherBoots::class; self::$list[self::CHAIN_HELMET] = ChainHelmet::class; self::$list[self::CHAIN_CHESTPLATE] = ChainChestplate::class; self::$list[self::CHAIN_LEGGINGS] = ChainLeggings::class; self::$list[self::CHAIN_BOOTS] = ChainBoots::class; self::$list[self::IRON_HELMET] = IronHelmet::class; self::$list[self::IRON_CHESTPLATE] = IronChestplate::class; self::$list[self::IRON_LEGGINGS] = IronLeggings::class; self::$list[self::IRON_BOOTS] = IronBoots::class; self::$list[self::GOLD_HELMET] = GoldHelmet::class; self::$list[self::GOLD_CHESTPLATE] = GoldChestplate::class; self::$list[self::GOLD_LEGGINGS] = GoldLeggings::class; self::$list[self::GOLD_BOOTS] = GoldBoots::class; self::$list[self::DIAMOND_HELMET] = DiamondHelmet::class; self::$list[self::DIAMOND_CHESTPLATE] = DiamondChestplate::class; self::$list[self::DIAMOND_LEGGINGS] = DiamondLeggings::class; self::$list[self::DIAMOND_BOOTS] = DiamondBoots::class; self::$list[self::IRON_SWORD] = IronSword::class; self::$list[self::IRON_INGOT] = IronIngot::class; self::$list[self::GOLD_INGOT] = GoldIngot::class; self::$list[self::IRON_SHOVEL] = IronShovel::class; self::$list[self::IRON_PICKAXE] = IronPickaxe::class; self::$list[self::IRON_AXE] = IronAxe::class; self::$list[self::IRON_HOE] = IronHoe::class; self::$list[self::DIAMOND_SWORD] = DiamondSword::class; self::$list[self::DIAMOND_SHOVEL] = DiamondShovel::class; self::$list[self::DIAMOND_PICKAXE] = DiamondPickaxe::class; self::$list[self::DIAMOND_AXE] = DiamondAxe::class; self::$list[self::DIAMOND_HOE] = DiamondHoe::class; self::$list[self::GOLD_SWORD] = GoldSword::class; self::$list[self::GOLD_SHOVEL] = GoldShovel::class; self::$list[self::GOLD_PICKAXE] = GoldPickaxe::class; self::$list[self::GOLD_AXE] = GoldAxe::class; self::$list[self::GOLD_HOE] = GoldHoe::class; self::$list[self::STONE_SWORD] = StoneSword::class; self::$list[self::STONE_SHOVEL] = StoneShovel::class; self::$list[self::STONE_PICKAXE] = StonePickaxe::class; self::$list[self::STONE_AXE] = StoneAxe::class; self::$list[self::STONE_HOE] = StoneHoe::class; self::$list[self::WOODEN_SWORD] = WoodenSword::class; self::$list[self::WOODEN_SHOVEL] = WoodenShovel::class; self::$list[self::WOODEN_PICKAXE] = WoodenPickaxe::class; self::$list[self::WOODEN_AXE] = WoodenAxe::class; self::$list[self::WOODEN_HOE] = WoodenHoe::class; self::$list[self::FLINT_STEEL] = FlintSteel::class; self::$list[self::SHEARS] = Shears::class; self::$list[self::BOW] = Bow::class; self::$list[self::RAW_FISH] = Fish::class; self::$list[self::COOKED_FISH] = CookedFish::class; self::$list[self::NETHER_QUARTZ] = NetherQuartz::class; self::$list[self::POTION] = Potion::class; self::$list[self::GLASS_BOTTLE] = GlassBottle::class; self::$list[self::SPLASH_POTION] = SplashPotion::class; self::$list[self::ENCHANTING_BOTTLE] = EnchantingBottle::class; self::$list[self::BOAT] = Boat::class; self::$list[self::MINECART] = Minecart::class; self::$list[self::ARROW] = Arrow::class; self::$list[self::STRING] = ItemString::class; self::$list[self::GUNPOWDER] = Gunpowder::class; self::$list[self::WHEAT] = Wheat::class; self::$list[self::BREAD] = Bread::class; self::$list[self::FLINT] = Flint::class; self::$list[self::FLINT] = Flint::class; self::$list[self::RAW_PORKCHOP] = RawPorkchop::class; self::$list[self::COOKED_PORKCHOP] = CookedPorkchop::class; self::$list[self::GOLDEN_APPLE] = GoldenApple::class; self::$list[self::MINECART] = Minecart::class; self::$list[self::REDSTONE] = Redstone::class; self::$list[self::LEATHER] = Leather::class; self::$list[self::CLAY] = Clay::class; self::$list[self::PAPER] = Paper::class; self::$list[self::BOOK] = Book::class; self::$list[self::SLIMEBALL] = Slimeball::class; self::$list[self::EGG] = Egg::class; self::$list[self::COMPASS] = Compass::class; self::$list[self::CLOCK] = Clock::class; self::$list[self::GLOWSTONE_DUST] = GlowstoneDust::class; self::$list[self::DYE] = Dye::class; self::$list[self::BONE] = Bone::class; self::$list[self::SUGAR] = Sugar::class; self::$list[self::COOKIE] = Cookie::class; self::$list[self::MELON] = Melon::class; self::$list[self::RAW_BEEF] = RawBeef::class; self::$list[self::STEAK] = Steak::class; self::$list[self::RAW_CHICKEN] = RawChicken::class; self::$list[self::COOKED_CHICKEN] = CookedChicken::class; self::$list[self::GOLD_NUGGET] = GoldNugget::class; self::$list[self::EMERALD] = Emerald::class; self::$list[self::ITEM_FRAME] = ItemFrame::class; self::$list[self::FLOWER_POT] = FlowerPot::class; self::$list[self::CARROT] = Carrot::class; self::$list[self::POTATO] = Potato::class; self::$list[self::BAKED_POTATO] = BakedPotato::class; self::$list[self::PUMPKIN_PIE] = PumpkinPie::class; self::$list[self::NETHER_BRICK] = NetherBrick::class; self::$list[self::QUARTZ] = Quartz::class; self::$list[self::BREWING_STAND] = BrewingStand::class; self::$list[self::CAMERA] = Camera::class; self::$list[self::BEETROOT] = Beetroot::class; self::$list[self::SKULL] = Skull::class; self::$list[self::RAW_RABBIT] = RawRabbit::class; self::$list[self::COOKED_RABBIT] = CookedRabbit::class; self::$list[self::GOLDEN_CARROT] = GoldenCarrot::class; self::$list[self::NETHER_WART] = NetherWart::class; self::$list[self::SPIDER_EYE] = SpiderEye::class; self::$list[self::FERMENTED_SPIDER_EYE] = FermentedSpiderEye::class; self::$list[self::BLAZE_POWDER] = BlazePowder::class; self::$list[self::MAGMA_CREAM] = MagmaCream::class; self::$list[self::GLISTERING_MELON] = GlisteringMelon::class; self::$list[self::ENCHANTED_BOOK] = EnchantedBook::class; self::$list[self::REPEATER] = Repeater::class; self::$list[self::CAULDRON] = Cauldron::class; self::$list[self::ROTTEN_FLESH] = RottenFlesh::class; self::$list[self::ENCHANTED_GOLDEN_APPLE] = EnchantedGoldenApple::class; self::$list[self::RAW_MUTTON] = RawMutton::class; self::$list[self::COOKED_MUTTON] = CookedMutton::class; self::$list[self::HOPPER] = Hopper::class; self::$list[self::ELYTRA] = Elytra::class; self::$list[self::NETHER_STAR] = NetherStar::class; self::$list[self::CHORUS_FRUIT] = ChorusFruit::class; self::$list[self::PRISMARINE_CRYSTALS] = PrismarineCrystals::class; self::$list[self::PRISMARINE_SHARD] = PrismarineShard::class; for($i = 0; $i < 256; ++$i){ if(Block::$list[$i] !== null){ self::$list[$i] = Block::$list[$i]; } } } self::initCreativeItems(); } private static $creative = []; private static function initCreativeItems(){ self::clearCreativeItems(); $creativeItems = new Config(Server::getInstance()->getFilePath() . "src/pocketmine/resources/creativeitems.json", Config::JSON, []); foreach($creativeItems->getAll() as $data){ $item = Item::get($data["id"], $data["damage"], $data["count"], $data["nbt"]); if($item->getName() === "Unknown"){ continue; } self::addCreativeItem($item); } } public static function clearCreativeItems(){ Item::$creative = []; } public static function getCreativeItems() : array{ return Item::$creative; } public static function addCreativeItem(Item $item){ Item::$creative[] = clone $item; } public static function removeCreativeItem(Item $item){ $index = self::getCreativeItemIndex($item); if($index !== -1){ unset(Item::$creative[$index]); } } public static function isCreativeItem(Item $item) : bool{ foreach(Item::$creative as $i => $d){ if($item->equals($d, !$item->isTool())){ return true; } } return false; } /** * @param $index * @return Item */ public static function getCreativeItem(int $index){ return isset(Item::$creative[$index]) ? Item::$creative[$index] : null; } public static function getCreativeItemIndex(Item $item) : int{ foreach(Item::$creative as $i => $d){ if($item->equals($d, !$item->isTool())){ return $i; } } return -1; } public static function get(int $id, int $meta = 0, int $count = 1, string $tags = "") : Item{ try{ $class = self::$list[$id]; if($class === null){ return (new Item($id, $meta, $count))->setCompoundTag($tags); }elseif($id < 256){ return (new ItemBlock(new $class($meta), $meta, $count))->setCompoundTag($tags); }else{ return (new $class($meta, $count))->setCompoundTag($tags); } }catch(\RuntimeException $e){ return (new Item($id, $meta, $count))->setCompoundTag($tags); } } /** * @param string $str * @param bool $multiple * @return Item[]|Item */ public static function fromString(string $str, bool $multiple = false){ if($multiple === true){ $blocks = []; foreach(explode(",", $str) as $b){ $blocks[] = self::fromString($b, false); } return $blocks; }else{ $b = explode(":", str_replace([" ", "minecraft:"], ["_", ""], trim($str))); if(!isset($b[1])){ $meta = 0; }else{ $meta = $b[1] & 0xFFFF; } if(defined(Item::class . "::" . strtoupper($b[0]))){ $item = self::get(constant(Item::class . "::" . strtoupper($b[0])), $meta); if($item->getId() === self::AIR and strtoupper($b[0]) !== "AIR"){ $item = self::get($b[0] & 0xFFFF, $meta); } }else{ $item = self::get($b[0] & 0xFFFF, $meta); } return $item; } } public function __construct(int $id, int $meta = 0, int $count = 1, string $name = "Unknown"){ $this->id = $id & 0xffff; $this->meta = $meta !== -1 ? $meta & 0xffff : -1; $this->count = $count; $this->name = $name; if(!isset($this->block) and $this->id <= 0xff and isset(Block::$list[$this->id])){ $this->block = Block::get($this->id, $this->meta); $this->name = $this->block->getName(); } } public function setCompoundTag($tags){ if($tags instanceof CompoundTag){ $this->setNamedTag($tags); }else{ $this->tags = (string) $tags; $this->cachedNBT = null; } return $this; } /** * @return string */ public function getCompoundTag() : string{ return $this->tags; } public function hasCompoundTag() : bool{ return $this->tags !== ""; } public function hasCustomBlockData() : bool{ if(!$this->hasCompoundTag()){ return false; } $tag = $this->getNamedTag(); if(isset($tag->BlockEntityTag) and $tag->BlockEntityTag instanceof CompoundTag){ return true; } return false; } public function clearCustomBlockData(){ if(!$this->hasCompoundTag()){ return $this; } $tag = $this->getNamedTag(); if(isset($tag->BlockEntityTag) and $tag->BlockEntityTag instanceof CompoundTag){ unset($tag->display->BlockEntityTag); $this->setNamedTag($tag); } return $this; } public function setCustomBlockData(CompoundTag $compound){ $tags = clone $compound; $tags->setName("BlockEntityTag"); if(!$this->hasCompoundTag()){ $tag = new CompoundTag("", []); }else{ $tag = $this->getNamedTag(); } $tag->BlockEntityTag = $tags; $this->setNamedTag($tag); return $this; } public function getCustomBlockData(){ if(!$this->hasCompoundTag()){ return null; } $tag = $this->getNamedTag(); if(isset($tag->BlockEntityTag) and $tag->BlockEntityTag instanceof CompoundTag){ return $tag->BlockEntityTag; } return null; } public function hasEnchantments() : bool{ if(!$this->hasCompoundTag()){ return false; } $tag = $this->getNamedTag(); if(isset($tag->ench)){ $tag = $tag->ench; if($tag instanceof ListTag){ return true; } } return false; } /** * @param $id * @return Enchantment|null */ public function getEnchantment(int $id){ if(!$this->hasEnchantments()){ return null; } foreach($this->getNamedTag()->ench as $entry){ if($entry["id"] === $id){ $e = Enchantment::getEnchantment($entry["id"]); $e->setLevel($entry["lvl"]); return $e; } } return null; } /** * @param int $id * @param int $level * @param bool $compareLevel * @return bool */ public function hasEnchantment(int $id, int $level = 1, bool $compareLevel = false) : bool{ if($this->hasEnchantments()){ foreach($this->getEnchantments() as $enchantment){ if($enchantment->getId() == $id){ if($compareLevel){ if($enchantment->getLevel() == $level){ return true; } }else{ return true; } } } } return false; } /** * @param $id * @return Int level|0(for null) */ public function getEnchantmentLevel(int $id){ if(!$this->hasEnchantments()){ return 0; } foreach($this->getNamedTag()->ench as $entry){ if($entry["id"] === $id){ $e = Enchantment::getEnchantment($entry["id"]); $e->setLevel($entry["lvl"]); $E_level = $e->getLevel() > Enchantment::getEnchantMaxLevel($id) ? Enchantment::getEnchantMaxLevel($id) : $e->getLevel(); return $E_level; } } return 0; } /** * @param Enchantment $ench */ public function addEnchantment(Enchantment $ench){ if(!$this->hasCompoundTag()){ $tag = new CompoundTag("", []); }else{ $tag = $this->getNamedTag(); } if(!isset($tag->ench)){ $tag->ench = new ListTag("ench", []); $tag->ench->setTagType(NBT::TAG_Compound); } $found = false; foreach($tag->ench as $k => $entry){ if($entry["id"] === $ench->getId()){ $tag->ench->{$k} = new CompoundTag("", [ "id" => new ShortTag("id", $ench->getId()), "lvl" => new ShortTag("lvl", $ench->getLevel()) ]); $found = true; break; } } if(!$found){ $count = 0; foreach($tag->ench as $key => $value){ if(is_numeric($key)){ $count++; } } $tag->ench->{$count + 1} = new CompoundTag("", [ "id" => new ShortTag("id", $ench->getId()), "lvl" => new ShortTag("lvl", $ench->getLevel()) ]); } $this->setNamedTag($tag); } /** * @return Enchantment[] */ public function getEnchantments() : array{ if(!$this->hasEnchantments()){ return []; } $enchantments = []; foreach($this->getNamedTag()->ench as $entry){ $e = Enchantment::getEnchantment($entry["id"]); $e->setLevel($entry["lvl"]); $enchantments[] = $e; } return $enchantments; } public function hasRepairCost() : bool{ if(!$this->hasCompoundTag()){ return false; } $tag = $this->getNamedTag(); if(isset($tag->RepairCost)){ $tag = $tag->RepairCost; if($tag instanceof IntTag){ return true; } } return false; } public function getRepairCost() : int{ if(!$this->hasCompoundTag()){ return 1; } $tag = $this->getNamedTag(); if(isset($tag->display)){ $tag = $tag->RepairCost; if($tag instanceof IntTag){ return $tag->getValue(); } } return 1; } public function setRepairCost(int $cost){ if($cost === 1){ $this->clearRepairCost(); } if(!($hadCompoundTag = $this->hasCompoundTag())){ $tag = new CompoundTag("", []); }else{ $tag = $this->getNamedTag(); } $tag->RepairCost = new IntTag("RepairCost", $cost); if(!$hadCompoundTag){ $this->setCompoundTag($tag); } return $this; } public function clearRepairCost(){ if(!$this->hasCompoundTag()){ return $this; } $tag = $this->getNamedTag(); if(isset($tag->RepairCost) and $tag->RepairCost instanceof IntTag){ unset($tag->RepairCost); $this->setNamedTag($tag); } return $this; } public function hasCustomName() : bool{ if(!$this->hasCompoundTag()){ return false; } $tag = $this->getNamedTag(); if(isset($tag->display)){ $tag = $tag->display; if($tag instanceof CompoundTag and isset($tag->Name) and $tag->Name instanceof StringTag){ return true; } } return false; } public function getCustomName() : string{ if(!$this->hasCompoundTag()){ return ""; } $tag = $this->getNamedTag(); if(isset($tag->display)){ $tag = $tag->display; if($tag instanceof CompoundTag and isset($tag->Name) and $tag->Name instanceof StringTag){ return $tag->Name->getValue(); } } return ""; } public function setCustomName(string $name){ if($name === ""){ $this->clearCustomName(); } if(!($hadCompoundTag = $this->hasCompoundTag())){ $tag = new CompoundTag("", []); }else{ $tag = $this->getNamedTag(); } if(isset($tag->display) and $tag->display instanceof CompoundTag){ $tag->display->Name = new StringTag("Name", $name); }else{ $tag->display = new CompoundTag("display", [ "Name" => new StringTag("Name", $name) ]); } if(!$hadCompoundTag){ $this->setCompoundTag($tag); } return $this; } public function clearCustomName(){ if(!$this->hasCompoundTag()){ return $this; } $tag = $this->getNamedTag(); if(isset($tag->display) and $tag->display instanceof CompoundTag){ unset($tag->display->Name); if($tag->display->getCount() === 0){ unset($tag->display); } $this->setNamedTag($tag); } return $this; } public function getNamedTagEntry($name){ $tag = $this->getNamedTag(); if($tag !== null){ return isset($tag->{$name}) ? $tag->{$name} : null; } return null; } public function getNamedTag(){ if(!$this->hasCompoundTag()){ return null; }elseif($this->cachedNBT !== null){ return $this->cachedNBT; } return $this->cachedNBT = self::parseCompoundTag($this->tags); } public function setNamedTag(CompoundTag $tag){ if($tag->getCount() === 0){ return $this->clearNamedTag(); } $this->cachedNBT = $tag; $this->tags = self::writeCompoundTag($tag); return $this; } public function clearNamedTag(){ return $this->setCompoundTag(""); } public function getCount() : int{ return $this->count; } public function setCount(int $count){ $this->count = $count; } final public function getName() : string{ return $this->hasCustomName() ? $this->getCustomName() : $this->name; } final public function canBePlaced() : bool{ return $this->block !== null and $this->block->canBePlaced(); } final public function isPlaceable() : bool{ return $this->canBePlaced(); } public function canBeConsumed() : bool{ return false; } public function canBeConsumedBy(Entity $entity) : bool{ return $this->canBeConsumed(); } public function onConsume(Entity $entity){ } public function getBlock() : Block{ if($this->block instanceof Block){ return clone $this->block; }else{ return Block::get(self::AIR); } } final public function getId() : int{ return $this->id; } final public function getDamage() : int{ return $this->meta; } public function setDamage(int $meta){ $this->meta = $meta !== -1 ? $meta & 0xFFFF : -1; } public function hasAnyDamageValue() : bool{ return $this->meta === -1; } public function getMaxStackSize() : int{ return 64; } final public function getFuelTime(){ if(!isset(Fuel::$duration[$this->id])){ return null; } if($this->id !== self::BUCKET or $this->meta === 10){ return Fuel::$duration[$this->id]; } return null; } /** * @param Entity|Block $object * * @return bool */ public function useOn($object){ return false; } /** * @return bool */ public function isTool(){ return false; } /** * @return int|bool */ public function getMaxDurability(){ return false; } public function isPickaxe(){ return false; } public function isAxe(){ return false; } public function isSword(){ return false; } public function isShovel(){ return false; } public function isHoe(){ return false; } public function isShears(){ return false; } public function isArmor(){ return false; } public function getArmorValue(){ return false; } public function isBoots(){ return false; } public function isHelmet(){ return false; } public function isLeggings(){ return false; } public function isChestplate(){ return false; } public function getAttackDamage(){ return 1; } public function getModifyAttackDamage(Entity $target){ $rec = $this->getAttackDamage(); $sharpL = $this->getEnchantmentLevel(Enchantment::TYPE_WEAPON_SHARPNESS); if($sharpL > 0){ $rec += 0.5 * ($sharpL + 1); } if($target instanceof Skeleton or $target instanceof Zombie or $target instanceof Witch or $target instanceof PigZombie){ //SMITE wither skeletons $rec += 2.5 * $this->getEnchantmentLevel(Enchantment::TYPE_WEAPON_SMITE); }elseif($target instanceof Spider or $target instanceof CaveSpider or $target instanceof Silverfish){ //Bane of Arthropods wither skeletons $rec += 2.5 * $this->getEnchantmentLevel(Enchantment::TYPE_WEAPON_ARTHROPODS); } return $rec; } public function getDestroySpeed(Block $block, Player $player){ return 1; } public function onActivate(Level $level, Player $player, Block $block, Block $target, $face, $fx, $fy, $fz){ return false; } public final function equals(Item $item, bool $checkDamage = true, bool $checkCompound = true, bool $checkCount = false) : bool{ return $this->id === $item->getId() and ($checkCount === false or $this->getCount() === $item->getCount()) and($checkDamage === false or $this->getDamage() === $item->getDamage()) and ($checkCompound === false or $this->getCompoundTag() === $item->getCompoundTag()); } public final function deepEquals(Item $item, bool $checkDamage = true, bool $checkCompound = true, bool $checkCount = false) : bool{ if($this->equals($item, $checkDamage, $checkCompound, $checkCount)){ return true; }elseif($item->hasCompoundTag() and $this->hasCompoundTag()){ return NBT::matchTree($this->getNamedTag(), $item->getNamedTag()); } return false; } final public function __toString() : string{ return "Item " . $this->name . " (" . $this->id . ":" . ($this->meta === null ? "?" : $this->meta) . ")x" . $this->count . ($this->hasCompoundTag() ? " tags:0x" . bin2hex($this->getCompoundTag()) : ""); } final public function jsonSerialize(){ return [ "id" => $this->id, "damage" => $this->meta, "count" => $this->count, //TODO: separate items and stacks "nbt" => $this->tags ]; } /** * Serializes the item to an NBT CompoundTag * * @param int $slot optional, the inventory slot of the item * @param string $tagName the name to assign to the CompoundTag object * * @return CompoundTag */ public function nbtSerialize(int $slot = -1, string $tagName = "") : CompoundTag{ $tag = new CompoundTag($tagName, [ "id" => new ShortTag("id", $this->id), "Count" => new ByteTag("Count", $this->count ?? -1), "Damage" => new ShortTag("Damage", $this->meta), ]); if($this->hasCompoundTag()){ $tag->tag = clone $this->getNamedTag(); $tag->tag->setName("tag"); } if($slot !== -1){ $tag->Slot = new ByteTag("Slot", $slot); } return $tag; } /** * Deserializes an Item from an NBT CompoundTag * * @param CompoundTag $tag * * @return Item */ public static function nbtDeserialize(CompoundTag $tag) : Item{ if(!isset($tag->id) or !isset($tag->Count)){ return Item::get(0); } if($tag->id instanceof ShortTag){ $item = Item::get($tag->id->getValue(), !isset($tag->Damage) ? 0 : $tag->Damage->getValue(), $tag->Count->getValue()); }elseif($tag->id instanceof StringTag){ //PC item save format $item = Item::fromString($tag->id->getValue()); $item->setDamage(!isset($tag->Damage) ? 0 : $tag->Damage->getValue()); $item->setCount($tag->Count->getValue()); }else{ throw new \InvalidArgumentException("Item CompoundTag ID must be an instance of StringTag or ShortTag, " . get_class($tag->id) . " given"); } if(isset($tag->tag) and $tag->tag instanceof CompoundTag){ $item->setNamedTag($tag->tag); } return $item; } } ================================================ FILE: src/pocketmine/item/ItemBlock.php ================================================ block = $block; parent::__construct($block->getId(), $block->getDamage(), $count, $block->getName()); } public function setDamage(int $meta){ $this->meta = $meta !== -1 ? $meta & 0xf : -1; $this->block->setDamage($this->meta !== -1 ? $this->meta : 0); } public function __clone(){ $this->block = clone $this->block; } public function getBlock() : Block{ return $this->block; } } ================================================ FILE: src/pocketmine/item/ItemFrame.php ================================================ block = Block::get(Item::ITEM_FRAME_BLOCK); parent::__construct(self::ITEM_FRAME, 0, $count, "Item Frame"); } } ================================================ FILE: src/pocketmine/item/ItemIds.php ================================================ block = Block::get(Item::JUNGLE_DOOR_BLOCK); parent::__construct(self::JUNGLE_DOOR, 0, $count, "Jungle Door"); } } ================================================ FILE: src/pocketmine/item/Leather.php ================================================ block = Block::get(Item::MELON_STEM); parent::__construct(self::MELON_SEEDS, 0, $count, "Melon Seeds"); } } ================================================ FILE: src/pocketmine/item/Minecart.php ================================================ getLevel()->getChunk($block->getX() >> 4, $block->getZ() >> 4), new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $block->getX()), new DoubleTag("", $block->getY() + 0.8), new DoubleTag("", $block->getZ()) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", 0), new DoubleTag("", 0), new DoubleTag("", 0) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", 0), new FloatTag("", 0) ]), ])); $minecart->spawnToAll(); if($player->isSurvival()){ $item = $player->getInventory()->getItemInHand(); $count = $item->getCount(); if(--$count <= 0){ $player->getInventory()->setItemInHand(Item::get(Item::AIR)); return true; } $item->setCount($count); $player->getInventory()->setItemInHand($item); } return true; } } ================================================ FILE: src/pocketmine/item/MobHead.php ================================================ "Skeleton Head", self::WITHER_SKELETON => "Wither Skeleton Head", self::STEVE => "Steve Head", self::CREEPER => "Creeper Head", ]; public function __construct($meta = 0, $count = 1){ $this->block = Block::get(Block::MOB_HEAD_BLOCK); parent::__construct(self::MOB_HEAD, $meta, $count, self::$names[$meta]); } } ================================================ FILE: src/pocketmine/item/MushroomStew.php ================================================ block = Block::get(Item::NETHER_WART_BLOCK); parent::__construct(self::NETHER_WART, $meta, $count, "Nether Wart"); } } ================================================ FILE: src/pocketmine/item/Painting.php ================================================ isTransparent() === false and $face > 1 and $block->isSolid() === false){ $faces = [ 2 => 1, 3 => 3, 4 => 0, 5 => 2, ]; $motives = [ // Motive Width Height ["Kebab", 1, 1], ["Aztec", 1, 1], ["Alban", 1, 1], ["Aztec2", 1, 1], ["Bomb", 1, 1], ["Plant", 1, 1], ["Wasteland", 1, 1], ["Wanderer", 1, 2], ["Graham", 1, 2], ["Pool", 2, 1], ["Courbet", 2, 1], ["Sunset", 2, 1], ["Sea", 2, 1], ["Creebet", 2, 1], ["Match", 2, 2], ["Bust", 2, 2], ["Stage", 2, 2], ["Void", 2, 2], ["SkullAndRoses", 2, 2], //array("Wither", 2, 2), ["Fighters", 4, 2], ["Skeleton", 4, 3], ["DonkeyKong", 4, 3], ["Pointer", 4, 4], ["Pigscene", 4, 4], ["Flaming Skull", 4, 4], ]; $right = [4, 5, 3, 2]; $validMotives = []; foreach($motives as $motive){ $valid = true; for($x = 0; $x < $motive[1] && $valid; $x++){ for($z = 0; $z < $motive[2] && $valid; $z++){ if($target->getSide($right[$face - 2], $x)->isTransparent() || $target->getSide(Vector3::SIDE_UP, $z)->isTransparent() || $block->getSide($right[$face - 2], $x)->isSolid() || $block->getSide(Vector3::SIDE_UP, $z)->isSolid() ){ $valid = false; } } } if($valid){ $validMotives[] = $motive; } } $motive = $motives[mt_rand(0, count($validMotives) - 1)]; $data = [ "x" => $target->x, "y" => $target->y, "z" => $target->z, "yaw" => $faces[$face] * 90, "Motive" => $motive[0], ]; $nbt = new CompoundTag("", [ "Motive" => new StringTag("Motive", $data["Motive"]), "Pos" => new ListTag("Pos", [ new DoubleTag("", $data["x"]), new DoubleTag("", $data["y"]), new DoubleTag("", $data["z"]) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", 0), new DoubleTag("", 0), new DoubleTag("", 0) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", $data["yaw"]), new FloatTag("", 0) ]), ]); $painting = new PaintingEntity($player->getLevel()->getChunk($block->getX() >> 4, $block->getZ() >> 4), $nbt); $painting->spawnToAll(); if($player->isSurvival()){ $item = $player->getInventory()->getItemInHand(); $count = $item->getCount(); if(--$count <= 0){ $player->getInventory()->setItemInHand(Item::get(Item::AIR)); return true; } $item->setCount($count); $player->getInventory()->setItemInHand($item); } //TODO //$e = $server->api->entity->add($level, ENTITY_OBJECT, OBJECT_PAINTING, $data); //$e->spawnToAll(); /*if(($player->gamemode & 0x01) === 0x00){ $player->removeItem(Item::get($this->getId(), $this->getDamage(), 1)); }*/ return true; } return false; } } ================================================ FILE: src/pocketmine/item/Paper.php ================================================ block = Block::get(Item::POTATO_BLOCK); parent::__construct(self::POTATO, 0, $count, "Potato"); } } ================================================ FILE: src/pocketmine/item/Potion.php ================================================ [matching effect, duration in ticks, amplifier] //Use false if no effects. const POTIONS = [ self::WATER_BOTTLE => false, self::MUNDANE => false, self::MUNDANE_EXTENDED => false, self::THICK => false, self::AWKWARD => false, self::NIGHT_VISION => [Effect::NIGHT_VISION, (180 * 20), 0], self::NIGHT_VISION_T => [Effect::NIGHT_VISION, (480 * 20), 0], self::INVISIBILITY => [Effect::INVISIBILITY, (180 * 20), 0], self::INVISIBILITY_T => [Effect::INVISIBILITY, (480 * 20), 0], self::LEAPING => [Effect::JUMP, (180 * 20), 0], self::LEAPING_T => [Effect::JUMP, (480 * 20), 0], self::LEAPING_TWO => [Effect::JUMP, (90 * 20), 1], self::FIRE_RESISTANCE => [Effect::FIRE_RESISTANCE, (180 * 20), 0], self::FIRE_RESISTANCE_T => [Effect::FIRE_RESISTANCE, (480 * 20), 0], self::SWIFTNESS => [Effect::SPEED, (180 * 20), 0], self::SWIFTNESS_T => [Effect::SPEED, (480 * 20), 0], self::SWIFTNESS_TWO => [Effect::SPEED, (90 * 20), 1], self::SLOWNESS => [Effect::SLOWNESS, (90 * 20), 0], self::SLOWNESS_T => [Effect::SLOWNESS, (240 * 20), 0], self::WATER_BREATHING => [Effect::WATER_BREATHING, (180 * 20), 0], self::WATER_BREATHING_T => [Effect::WATER_BREATHING, (480 * 20), 0], self::HEALING => [Effect::HEALING, (1), 0], self::HEALING_TWO => [Effect::HEALING, (1), 1], self::HARMING => [Effect::HARMING, (1), 0], self::HARMING_TWO => [Effect::HARMING, (1), 1], self::POISON => [Effect::POISON, (45 * 20), 0], self::POISON_T => [Effect::POISON, (120 * 20), 0], self::POISON_TWO => [Effect::POISON, (22 * 20), 1], self::REGENERATION => [Effect::REGENERATION, (45 * 20), 0], self::REGENERATION_T => [Effect::REGENERATION, (120 * 20), 0], self::REGENERATION_TWO => [Effect::REGENERATION, (22 * 20), 1], self::STRENGTH => [Effect::STRENGTH, (180 * 20), 0], self::STRENGTH_T => [Effect::STRENGTH, (480 * 20), 0], self::STRENGTH_TWO => [Effect::STRENGTH, (90 * 20), 1], self::WEAKNESS => [Effect::WEAKNESS, (90 * 20), 0], self::WEAKNESS_T => [Effect::WEAKNESS, (240 * 20), 0] ]; public function __construct($meta = 0, $count = 1){ parent::__construct(self::POTION, $meta, $count, self::getNameByMeta($meta)); } public static function getColor(int $meta){ $effect = Effect::getEffect(self::getEffectId($meta)); if($effect !== null){ return $effect->getColor(); } return [0, 0, 0]; } public function getMaxStackSize() : int{ return 1; } public function canBeConsumed() : bool{ return $this->meta > 0; } public function canBeConsumedBy(Entity $entity) : bool{ return $entity instanceof Human; } public function getEffects(): array{ return self::getEffectsById($this->meta); } /** * @param int $id * @return Effect[] */ public static function getEffectsById(int $id) : array{ if(count(self::POTIONS[$id] ?? []) === 3){ return [Effect::getEffect(self::POTIONS[$id][0])->setDuration(self::POTIONS[$id][1])->setAmplifier(self::POTIONS[$id][2])]; } return []; } public function onConsume(Entity $human){ $pk = new EntityEventPacket(); $pk->eid = $human->getId(); $pk->event = EntityEventPacket::USE_ITEM; if($human instanceof Player){ $human->dataPacket($pk); } Server::getInstance()->broadcastPacket($human->getViewers(), $pk); Server::getInstance()->getPluginManager()->callEvent($ev = new EntityDrinkPotionEvent($human, $this)); if(!$ev->isCancelled()){ foreach($ev->getEffects() as $effect){ $human->addEffect($effect); } //Don't set the held item to glass bottle if we're in creative if($human instanceof Player){ if($human->getGamemode() === 1){ return; } } $human->getInventory()->setItemInHand(Item::get(self::GLASS_BOTTLE)); } } public static function getEffectId(int $meta) : int{ switch($meta){ case self::INVISIBILITY: case self::INVISIBILITY_T: return Effect::INVISIBILITY; case self::LEAPING: case self::LEAPING_T: case self::LEAPING_TWO: return Effect::JUMP; case self::FIRE_RESISTANCE: case self::FIRE_RESISTANCE_T: return Effect::FIRE_RESISTANCE; case self::SWIFTNESS: case self::SWIFTNESS_T: case self::SWIFTNESS_TWO: return Effect::SPEED; case self::SLOWNESS: case self::SLOWNESS_T: return Effect::SLOWNESS; case self::WATER_BREATHING: case self::WATER_BREATHING_T: return Effect::WATER_BREATHING; case self::HARMING: case self::HARMING_TWO: return Effect::HARMING; case self::POISON: case self::POISON_T: case self::POISON_TWO: return Effect::POISON; case self::HEALING: case self::HEALING_TWO: return Effect::HEALING; case self::NIGHT_VISION: case self::NIGHT_VISION_T: return Effect::NIGHT_VISION; case self::REGENERATION: case self::REGENERATION_T: case self::REGENERATION_TWO: return Effect::REGENERATION; default: return 0; } } public static function getNameByMeta(int $meta) : string{ switch($meta){ case self::WATER_BOTTLE: return "Water Bottle"; case self::MUNDANE: case self::MUNDANE_EXTENDED: return "Mundane Potion"; case self::THICK: return "Thick Potion"; case self::AWKWARD: return "Awkward Potion"; case self::INVISIBILITY: case self::INVISIBILITY_T: return "Potion of Invisibility"; case self::LEAPING: case self::LEAPING_T: return "Potion of Leaping"; case self::LEAPING_TWO: return "Potion of Leaping II"; case self::FIRE_RESISTANCE: case self::FIRE_RESISTANCE_T: return "Potion of Fire Resistance"; case self::SWIFTNESS: case self::SWIFTNESS_T: return "Potion of Swiftness"; case self::SWIFTNESS_TWO: return "Potion of Swiftness II"; case self::SLOWNESS: case self::SLOWNESS_T: return "Potion of Slowness"; case self::WATER_BREATHING: case self::WATER_BREATHING_T: return "Potion of Water Breathing"; case self::HARMING: return "Potion of Harming"; case self::HARMING_TWO: return "Potion of Harming II"; case self::POISON: case self::POISON_T: return "Potion of Poison"; case self::POISON_TWO: return "Potion of Poison II"; case self::HEALING: return "Potion of Healing"; case self::HEALING_TWO: return "Potion of Healing II"; case self::NIGHT_VISION: case self::NIGHT_VISION_T: return "Potion of Night Vision"; case self::STRENGTH: case self::STRENGTH_T: return "Potion of Strength"; case self::STRENGTH_TWO: return "Potion of Strength II"; case self::REGENERATION: case self::REGENERATION_T: return "Potion of Regeneration"; case self::REGENERATION_TWO: return "Potion of Regeneration II"; case self::WEAKNESS: case self::WEAKNESS_T: return "Potion of Weakness"; default: return "Potion"; } } } ================================================ FILE: src/pocketmine/item/PrismarineCrystals.php ================================================ block = Block::get(Item::PUMPKIN_STEM); parent::__construct(self::PUMPKIN_SEEDS, 0, $count, "Pumpkin Seeds"); } } ================================================ FILE: src/pocketmine/item/Quartz.php ================================================ = 70){ return [Effect::getEffect(Effect::HUNGER)->setDuration(30 * 20)]; }else{ return []; } } } ================================================ FILE: src/pocketmine/item/RawMutton.php ================================================ block = Block::get(Item::REDSTONE_WIRE); parent::__construct(self::REDSTONE, 0, $count, "Redstone"); } } ================================================ FILE: src/pocketmine/item/Repeater.php ================================================ block = Block::get(Block::UNPOWERED_REPEATER_BLOCK); parent::__construct(self::REPEATER, $meta, $count, "Repeater"); } } ================================================ FILE: src/pocketmine/item/RottenFlesh.php ================================================ = 20){ return [Effect::getEffect(Effect::HUNGER)->setDuration(30 * 20)]; }else{ return []; } } } ================================================ FILE: src/pocketmine/item/Shears.php ================================================ block = Block::get(Item::SIGN_POST); parent::__construct(self::SIGN, 0, $count, "Sign"); } public function getMaxStackSize() : int { return 16; } } ================================================ FILE: src/pocketmine/item/Skull.php ================================================ block = Block::get(Block::SKULL_BLOCK); parent::__construct(self::SKULL, $meta, $count, "Skull"); } public function getMaxStackSize() : int { return 64; } } ================================================ FILE: src/pocketmine/item/Slimeball.php ================================================ getId() == Block::MONSTER_SPAWNER){ return true; }else{ $entity = null; $chunk = $level->getChunk($block->getX() >> 4, $block->getZ() >> 4); if(!($chunk instanceof Chunk)){ return false; } $nbt = new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $block->getX() + 0.5), new DoubleTag("", $block->getY()), new DoubleTag("", $block->getZ() + 0.5) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", 0), new DoubleTag("", 0), new DoubleTag("", 0) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", lcg_value() * 360), new FloatTag("", 0) ]), ]); if($this->hasCustomName()){ $nbt->CustomName = new StringTag("CustomName", $this->getCustomName()); } $entity = Entity::createEntity($this->meta, $chunk, $nbt); if($entity instanceof Entity){ if($player->isSurvival()){ --$this->count; } $entity->spawnToAll(); return true; } } return false; } } ================================================ FILE: src/pocketmine/item/SpiderEye.php ================================================ setDuration(80)]; } } ================================================ FILE: src/pocketmine/item/SplashPotion.php ================================================ getNameByMeta($meta)); } public function getMaxStackSize() : int{ return 1; } public function getNameByMeta(int $meta){ return "Splash ".Potion::getNameByMeta($meta); } } ================================================ FILE: src/pocketmine/item/SpruceDoor.php ================================================ block = Block::get(Item::SPRUCE_DOOR_BLOCK); parent::__construct(self::SPRUCE_DOOR, 0, $count, "Spruce Door"); } } ================================================ FILE: src/pocketmine/item/Steak.php ================================================ block = Block::get(Item::SUGARCANE_BLOCK); parent::__construct(self::SUGARCANE, 0, $count, "Sugar Cane"); } } ================================================ FILE: src/pocketmine/item/Tool.php ================================================ isUnbreakable()){ return true; } $unbreakingl = $this->getEnchantmentLevel(Enchantment::TYPE_MINING_DURABILITY); $unbreakingl = $unbreakingl > 3 ? 3 : $unbreakingl; if (mt_rand(1, $unbreakingl + 1) !== 1) { return true; } if ($type === 1) { if ($object instanceof Entity) { if ($this->isHoe() !== false or $this->isSword() !== false) { //Hoe and Sword $this->meta++; return true; } elseif ($this->isPickaxe() !== false or $this->isAxe() !== false or $this->isShovel() !== false) { //Pickaxe Axe and Shovel $this->meta += 2; return true; } return true;//Other tool do not lost durability white hitting } elseif ($object instanceof Block) { if ($this->isShears() !== false) { if ($object->getToolType() === Tool::TYPE_SHEARS) {//This should be checked in each block $this->meta++; } return true; } elseif ($object->getHardness() > 0) {//Sword Pickaxe Axe and Shovel if ($this->isSword() !== false) { $this->meta += 2; return true; } elseif ($this->isPickaxe() !== false or $this->isAxe() !== false or $this->isShovel() !== false) { $this->meta += 1; return true; } } } } elseif ($type === 2) {//For Touch. only trigger when OnActivate return true if ($this->isHoe() !== false or $this->id === self::FLINT_STEEL or $this->isShovel() !== false) { $this->meta++; return true; } } return true; } /** * TODO: Move this to each item * * @return int|bool */ public function getMaxDurability(){ $levels = [ Tool::TIER_GOLD => 33, Tool::TIER_WOODEN => 60, Tool::TIER_STONE => 132, Tool::TIER_IRON => 251, Tool::TIER_DIAMOND => 1562, self::FLINT_STEEL => 65, self::SHEARS => 239, self::BOW => 385, ]; if(($type = $this->isPickaxe()) === false){ if(($type = $this->isAxe()) === false){ if(($type = $this->isSword()) === false){ if(($type = $this->isShovel()) === false){ if(($type = $this->isHoe()) === false){ $type = $this->id; } } } } } return $levels[$type]; } public function isUnbreakable(){ $tag = $this->getNamedTagEntry("Unbreakable"); return $tag !== null and $tag->getValue() > 0; } public function isPickaxe(){ return false; } public function isAxe(){ return false; } public function isSword(){ return false; } public function isShovel(){ return false; } public function isHoe(){ return false; } public function isShears(){ return ($this->id === self::SHEARS); } public function isTool(){ return ($this->id === self::FLINT_STEEL or $this->id === self::SHEARS or $this->id === self::BOW or $this->isPickaxe() !== false or $this->isAxe() !== false or $this->isShovel() !== false or $this->isSword() !== false or $this->isHoe() !== false); } } ================================================ FILE: src/pocketmine/item/Wheat.php ================================================ block = Block::get(Item::WHEAT_BLOCK); parent::__construct(self::WHEAT_SEEDS, 0, $count, "Wheat Seeds"); } } ================================================ FILE: src/pocketmine/item/WoodenAxe.php ================================================ block = Block::get(Item::WOODEN_DOOR_BLOCK); parent::__construct(self::WOODEN_DOOR, 0, $count, "Wooden Door"); } } ================================================ FILE: src/pocketmine/item/WoodenHoe.php ================================================ getId()){ case Item::BOOK: case Item::BOW: case Item::FISHING_ROD: return 4; } if($item->isArmor()){ if($item instanceof ChainBoots or $item instanceof ChainChestplate or $item instanceof ChainHelmet or $item instanceof ChainLeggings) return 12; if($item instanceof IronBoots or $item instanceof IronChestplate or $item instanceof IronHelmet or $item instanceof IronLeggings) return 9; if($item instanceof DiamondBoots or $item instanceof DiamondChestplate or $item instanceof DiamondHelmet or $item instanceof DiamondLeggings) return 10; if($item instanceof LeatherBoots or $item instanceof LeatherTunic or $item instanceof LeatherCap or $item instanceof LeatherPants) return 15; if($item instanceof GoldBoots or $item instanceof GoldChestplate or $item instanceof GoldHelmet or $item instanceof GoldLeggings) return 25; } if($item->isTool()){ if($item instanceof WoodenAxe or $item instanceof WoodenHoe or $item instanceof WoodenPickaxe or $item instanceof WoodenShovel or $item instanceof WoodenSword) return 15; if($item instanceof StoneAxe or $item instanceof StoneHoe or $item instanceof StonePickaxe or $item instanceof StoneShovel or $item instanceof StoneSword) return 5; if($item instanceof DiamondAxe or $item instanceof DiamondHoe or $item instanceof DiamondPickaxe or $item instanceof DiamondShovel or $item instanceof DiamondSword) return 10; if($item instanceof IronAxe or $item instanceof IronHoe or $item instanceof IronPickaxe or $item instanceof IronShovel or $item instanceof IronSword) return 14; if($item instanceof GoldAxe or $item instanceof GoldHoe or $item instanceof GoldPickaxe or $item instanceof GoldShovel or $item instanceof GoldSword) return 22; } return 0; } public static function getEnchantWeight(int $enchantmentId){ switch($enchantmentId){ case self::TYPE_ARMOR_PROTECTION: return 10; case self::TYPE_ARMOR_FIRE_PROTECTION: return 5; case self::TYPE_ARMOR_FALL_PROTECTION: return 2; case self::TYPE_ARMOR_EXPLOSION_PROTECTION: return 5; case self::TYPE_WATER_BREATHING: return 2; case self::TYPE_WATER_AFFINITY: return 2; case self::TYPE_WEAPON_SHARPNESS: return 10; case self::TYPE_WEAPON_SMITE: return 5; case self::TYPE_WEAPON_ARTHROPODS: return 5; case self::TYPE_WEAPON_KNOCKBACK: return 5; case self::TYPE_WEAPON_FIRE_ASPECT: return 2; case self::TYPE_WEAPON_LOOTING: return 2; case self::TYPE_MINING_EFFICIENCY: return 10; case self::TYPE_MINING_SILK_TOUCH: return 1; case self::TYPE_MINING_DURABILITY: return 5; case self::TYPE_MINING_FORTUNE: return 2; case self::TYPE_BOW_POWER: return 10; case self::TYPE_BOW_KNOCKBACK: return 2; case self::TYPE_BOW_FLAME: return 2; case self::TYPE_BOW_INFINITY: return 1; } return 0; } public static function getEnchantMaxLevel(int $enchantmentId){ switch($enchantmentId){ case self::TYPE_ARMOR_PROTECTION: case self::TYPE_ARMOR_FIRE_PROTECTION: case self::TYPE_ARMOR_FALL_PROTECTION: case self::TYPE_ARMOR_EXPLOSION_PROTECTION: case self::TYPE_ARMOR_PROJECTILE_PROTECTION: return 4; case self::TYPE_ARMOR_THORNS: return 3; case self::TYPE_WATER_BREATHING: case self::TYPE_WATER_SPEED: return 3; case self::TYPE_WATER_AFFINITY: return 1; case self::TYPE_WEAPON_SHARPNESS: case self::TYPE_WEAPON_SMITE: case self::TYPE_WEAPON_ARTHROPODS: return 5; case self::TYPE_WEAPON_KNOCKBACK: case self::TYPE_WEAPON_FIRE_ASPECT: return 2; case self::TYPE_WEAPON_LOOTING: return 3; case self::TYPE_MINING_EFFICIENCY: return 5; case self::TYPE_MINING_SILK_TOUCH: return 1; case self::TYPE_MINING_DURABILITY: case self::TYPE_MINING_FORTUNE: return 3; case self::TYPE_BOW_POWER: return 5; case self::TYPE_BOW_KNOCKBACK: return 2; case self::TYPE_BOW_FLAME: case self::TYPE_BOW_INFINITY: return 1; case self::TYPE_FISHING_FORTUNE: case self::TYPE_FISHING_LURE: return 3; } return 999; } private $id; private $level = 1; private $name; private $rarity; private $activationType; private $slot; private function __construct($id, $name, $rarity, $activationType, $slot){ $this->id = (int) $id; $this->name = (string) $name; $this->rarity = (int) $rarity; $this->activationType = (int) $activationType; $this->slot = (int) $slot; } public function getId(){ return $this->id; } public function getName() : string{ return $this->name; } public function getRarity(){ return $this->rarity; } public function getActivationType(){ return $this->activationType; } public function getSlot(){ return $this->slot; } public function hasSlot($slot){ return ($this->slot & $slot) > 0; } public function getLevel(){ return $this->level; } public function setLevel(int $level){ $this->level = $level; return $this; } public function equals(Enchantment $ent){ if($ent->getId() == $this->getId() and $ent->getLevel() == $this->getLevel() and $ent->getActivationType() == $this->getActivationType() and $ent->getRarity() == $this->getRarity()){ return true; } return false; } public static function getRandomName(){ $count = mt_rand(3, 6); $set = []; while(count($set) < $count){ $set[] = self::$words[mt_rand(0, count(self::$words) - 1)]; } return implode(" ", $set); } } ================================================ FILE: src/pocketmine/item/enchantment/EnchantmentEntry.php ================================================ enchantments = $enchantments; $this->cost = (int) $cost; $this->randomName = $randomName; } public function getEnchantments(){ return $this->enchantments; } public function getCost(){ return $this->cost; } public function getRandomName(){ return $this->randomName; } } ================================================ FILE: src/pocketmine/item/enchantment/EnchantmentLevelTable.php ================================================ [ new Range(1, 21), new Range(12, 32), new Range(23, 43), new Range(34, 54) ], Enchantment::TYPE_ARMOR_FIRE_PROTECTION => [ new Range(10, 22), new Range(18, 30), new Range(26, 38), new Range(34, 46)], Enchantment::TYPE_ARMOR_FALL_PROTECTION => [ new Range(5, 12), new Range(11, 21), new Range(17, 27), new Range(23, 33) ], Enchantment::TYPE_ARMOR_EXPLOSION_PROTECTION => [ new Range(5, 17), new Range(13, 25), new Range(21, 33), new Range(29, 41) ], Enchantment::TYPE_ARMOR_PROJECTILE_PROTECTION => [ new Range(3, 18), new Range(9, 24), new Range(15, 30), new Range(21, 36) ], Enchantment::TYPE_WATER_BREATHING => [ new Range(10, 40), new Range(20, 50), new Range(30, 60) ], Enchantment::TYPE_WATER_AFFINITY => [ new Range(10, 41) ], Enchantment::TYPE_ARMOR_THORNS => [ new Range(10, 60), new Range(30, 80), new Range(50, 100) ], //Weapon Enchantment::TYPE_WEAPON_SHARPNESS => [ new Range(1, 21), new Range(12, 32), new Range(23, 43), new Range(34, 54), new Range(45, 65) ], Enchantment::TYPE_WEAPON_SMITE => [ new Range(5, 25), new Range(13, 33), new Range(21, 41), new Range(29, 49), new Range(37, 57) ], Enchantment::TYPE_WEAPON_ARTHROPODS => [ new Range(5, 25), new Range(13, 33), new Range(21, 41), new Range(29, 49), new Range(37, 57) ], Enchantment::TYPE_WEAPON_KNOCKBACK => [ new Range(5, 55), new Range(25, 75) ], Enchantment::TYPE_WEAPON_FIRE_ASPECT => [ new Range(10, 60), new Range(30, 80) ], Enchantment::TYPE_WEAPON_LOOTING => [ new Range(15, 65), new Range(24, 74), new Range(33, 83) ], //Bow Enchantment::TYPE_BOW_POWER => [ new Range(1, 16), new Range(11, 26), new Range(21, 36), new Range(31, 46), new Range(41, 56) ], Enchantment::TYPE_BOW_KNOCKBACK => [ new Range(12, 37), new Range(32, 57) ], Enchantment::TYPE_BOW_FLAME => [ new Range(20, 50) ], Enchantment::TYPE_BOW_INFINITY => [ new Range(20, 50) ], //Mining Enchantment::TYPE_MINING_EFFICIENCY => [ new Range(1, 51), new Range(11, 61), new Range(21, 71), new Range(31, 81), new Range(41, 91) ], Enchantment::TYPE_MINING_SILK_TOUCH => [ new Range(15, 65) ], Enchantment::TYPE_MINING_DURABILITY => [ new Range(5, 55), new Range(13, 63), new Range(21, 71) ], Enchantment::TYPE_MINING_FORTUNE => [ new Range(15, 55), new Range(24, 74), new Range(33, 83) ], //Fishing Enchantment::TYPE_FISHING_FORTUNE => [ new Range(15, 65), new Range(24, 74), new Range(33, 83) ], Enchantment::TYPE_FISHING_LURE => [ new Range(15, 65), new Range(24, 74), new Range(33, 83) ] ]; } /** * @param Item $item * @param int $modifiedLevel * @return Enchantment[] */ public static function getPossibleEnchantments(Item $item, int $modifiedLevel){ $result = []; $enchantmentIds = []; if($item->getId() == Item::BOOK){ $enchantmentIds = array_keys(self::$map); }elseif($item->isArmor()){ $enchantmentIds[] = Enchantment::TYPE_ARMOR_PROTECTION; $enchantmentIds[] = Enchantment::TYPE_ARMOR_FIRE_PROTECTION; $enchantmentIds[] = Enchantment::TYPE_ARMOR_EXPLOSION_PROTECTION; $enchantmentIds[] = Enchantment::TYPE_ARMOR_PROJECTILE_PROTECTION; $enchantmentIds[] = Enchantment::TYPE_ARMOR_THORNS; if($item->isBoots()){ $enchantmentIds[] = Enchantment::TYPE_ARMOR_FALL_PROTECTION; } if($item->isHelmet()){ $enchantmentIds[] = Enchantment::TYPE_WATER_BREATHING; $enchantmentIds[] = Enchantment::TYPE_WATER_AFFINITY; } }elseif($item->isSword()){ $enchantmentIds[] = Enchantment::TYPE_WEAPON_SHARPNESS; $enchantmentIds[] = Enchantment::TYPE_WEAPON_SMITE; $enchantmentIds[] = Enchantment::TYPE_WEAPON_ARTHROPODS; $enchantmentIds[] = Enchantment::TYPE_WEAPON_KNOCKBACK; $enchantmentIds[] = Enchantment::TYPE_WEAPON_FIRE_ASPECT; $enchantmentIds[] = Enchantment::TYPE_WEAPON_LOOTING; }elseif($item->isTool()){ $enchantmentIds[] = Enchantment::TYPE_MINING_EFFICIENCY; $enchantmentIds[] = Enchantment::TYPE_MINING_SILK_TOUCH; $enchantmentIds[] = Enchantment::TYPE_MINING_FORTUNE; }elseif($item->getId() == Item::BOW){ $enchantmentIds[] = Enchantment::TYPE_BOW_POWER; $enchantmentIds[] = Enchantment::TYPE_BOW_KNOCKBACK; $enchantmentIds[] = Enchantment::TYPE_BOW_FLAME; $enchantmentIds[] = Enchantment::TYPE_BOW_INFINITY; }elseif($item->getId() == Item::FISHING_ROD){ $enchantmentIds[] = Enchantment::TYPE_FISHING_FORTUNE; $enchantmentIds[] = Enchantment::TYPE_FISHING_LURE; } if($item->isTool() || $item->isArmor()){ $enchantmentIds[] = Enchantment::TYPE_MINING_DURABILITY; } foreach($enchantmentIds as $enchantmentId) { $enchantment = Enchantment::getEnchantment($enchantmentId); $ranges = self::$map[$enchantmentId]; $i = 0; /** @var Range $range */ foreach($ranges as $range) { $i++; if($range->isInRange($modifiedLevel)){ $result[] = $enchantment->setLevel($i); } } } return $result; } } ================================================ FILE: src/pocketmine/item/enchantment/EnchantmentList.php ================================================ enchantments = new \SplFixedArray($size); } /** * @param $slot * @param EnchantmentEntry $entry */ public function setSlot($slot, EnchantmentEntry $entry){ $this->enchantments[$slot] = $entry; } /** * @param $slot * @return EnchantmentEntry */ public function getSlot($slot){ return $this->enchantments[$slot]; } public function getSize(){ return $this->enchantments->getSize(); } } ================================================ FILE: src/pocketmine/lang/BaseLang.php ================================================ langName = strtolower($lang); if($path === null){ $path = \pocketmine\PATH . "src/pocketmine/lang/locale/"; } $this->loadLang($path . $this->langName . ".ini", $this->lang); $this->loadLang($path . $fallback . ".ini", $this->fallbackLang); } public function getName() : string{ return $this->get("language.name"); } public function getLang(){ return $this->langName; } protected function loadLang($path, array &$d){ if(file_exists($path) and strlen($content = file_get_contents($path)) > 0){ foreach(explode("\n", $content) as $line){ $line = trim($line); if($line === "" or $line{0} === "#"){ continue; } $t = explode("=", $line); if(count($t) < 2){ continue; } $key = trim(array_shift($t)); $value = trim(implode("=", $t)); if($value === ""){ continue; } $d[$key] = $value; } } } /** * @param string $str * @param string[] $params * * @return string */ public function translateString($str, array $params = [], $onlyPrefix = null){ $baseText = $this->get($str); $baseText = $this->parseTranslation(($baseText !== null and ($onlyPrefix === null or strpos($str, $onlyPrefix) === 0)) ? $baseText : $str, $onlyPrefix); foreach($params as $i => $p){ $baseText = str_replace("{%$i}", $this->parseTranslation((string) $p), $baseText, $onlyPrefix); } return str_replace("%0", "", $baseText); //fixes a client bug where %0 in translation will cause freeze } public function translate(TextContainer $c){ if($c instanceof TranslationContainer){ $baseText = $this->internalGet($c->getText()); $baseText = $this->parseTranslation($baseText !== null ? $baseText : $c->getText()); foreach($c->getParameters() as $i => $p){ $baseText = str_replace("{%$i}", $this->parseTranslation($p), $baseText); } }else{ $baseText = $this->parseTranslation($c->getText()); } return $baseText; } public function internalGet($id){ if(isset($this->lang[$id])){ return $this->lang[$id]; }elseif(isset($this->fallbackLang[$id])){ return $this->fallbackLang[$id]; } return null; } public function get($id){ if(isset($this->lang[$id])){ return $this->lang[$id]; }elseif(isset($this->fallbackLang[$id])){ return $this->fallbackLang[$id]; } return $id; } protected function parseTranslation($text, $onlyPrefix = null){ $newString = ""; $replaceString = null; $len = strlen($text); for($i = 0; $i < $len; ++$i){ $c = $text{$i}; if($replaceString !== null){ $ord = ord($c); if( ($ord >= 0x30 and $ord <= 0x39) // 0-9 or ($ord >= 0x41 and $ord <= 0x5a) // A-Z or ($ord >= 0x61 and $ord <= 0x7a) or // a-z $c === "." or $c === "-" ){ $replaceString .= $c; }else{ if(($t = $this->internalGet(substr($replaceString, 1))) !== null and ($onlyPrefix === null or strpos($replaceString, $onlyPrefix) === 1)){ $newString .= $t; }else{ $newString .= $replaceString; } $replaceString = null; if($c === "%"){ $replaceString = $c; }else{ $newString .= $c; } } }elseif($c === "%"){ $replaceString = $c; }else{ $newString .= $c; } } if($replaceString !== null){ if(($t = $this->internalGet(substr($replaceString, 1))) !== null and ($onlyPrefix === null or strpos($replaceString, $onlyPrefix) === 1)){ $newString .= $t; }else{ $newString .= $replaceString; } } return $newString; } } ================================================ FILE: src/pocketmine/lang/Installer/chs.ini ================================================ language_has_been_selected = 您现在选择了简体中文. skip_installer = 您想跳过安装向导吗? welcome_to_pocketmine = 欢迎来到Elywing!\n在开始使用您的新服务器之前,您需要接受以下协议\nElywing使用了LGPL协议,\n你可以在这个文件夹中找到LICENCE文件。 accept_license = 您接受协议内容吗? you_have_to_accept_the_license = 您要接受LGPL协议才可继续使用Elywing setting_up_server_now = 你现在要开始设置您的服务器了。 default_values_info = 如果您希望使用默认设置,请直接按下回车键。 server_properties = 您以后可以在server.properties中修改设置. name_your_server = 给你的服务器起个名字吧: port_warning = 如果这是您第一次设置服务器,尽量不要改变端口。 server_port = 设置你的服务器端口: invalid_port = 服务器端口不正确。 online_mode_info = 联机模式下, 服务器将强制玩家需要 Xbox 登录\n当联机时, 建议让服务器开启此功能 online_mode = 您想启用联机模式? ram_warning = 设置Elywing可用的最大内存. server_ram = 分配给服务器的内存(RAM)(MB): gamemode_info = 选择模式: (0)生存模式 或 (1)创造模式 default_gamemode = 默认游戏模式 max_players = 最多在线人数 spawn_protection_info = 出生点保护可以在出生点范围内保护所有方块不被改变。 spawn_protection = 启用出生点保护嘛? level_name = 为您的服务器世界设置一个名称: level_type = 设置您服务器的世界类型: invalid_level_type = 无效的世界类型 announce_player_achievements = 当玩家获得成就时是否公布? op_info = OP是服务器的管理员, 可以执行比普通玩家更多的命令. op_who = OP的用户名是什么? op_warning = 你可以执行\"/op <用户名>\"来添加OP. whitelist_info = 白名单可以只允许在其列表内的玩家加入. whitelist_enable = 您想启用白名单吗? whitelist_warning = 你可以用"/whitelist add <用户名>"把别人加入白名单. query_warning1 = Query可用来获取您服务器数据和登录的玩家. query_warning2 = 如果您禁止了它, 您将不能使用服务器列表. query_disable = 您希望禁用Query请求吗? rcon_info = RCON可用来远程连接到服务器控制台(需要密码). rcon_enable = 您希望启用RCON吗? rcon_password = RCON密码 (您也以后更改它) : usage_info = 匿名数据让我们可以获得全球的PocketMine-MP和它的插件的统计信息. 您可以在 stats.pocketmine.net 查看统计信息. usage_disable = 您希望禁用匿名数据吗? ip_get = 获得你的外部IP和内部IP ip_warning = 您的外部IP是 {{EXTERNAL_IP}} . 您可能需要端口转发到您的内网IP {{INTERNAL_IP}} . ip_confirm = 请确认,如没有问题请按下\"回车\"键 you_have_finished = 您已经成功完成了服务器设置向导. pocketmine_will_start = 现在,尽情使用吧~. 输入 \"/help\" 来看所有可用的命令. pocketmine_plugins = 请查看插件源来添加新的功能, 迷你游戏或者对服务器的高级保护. ================================================ FILE: src/pocketmine/lang/Installer/deu.ini ================================================ language_has_been_selected = Deutsch wurde erfolgreich als Sprache ausgewählt. skip_installer = Möchtest du den Einrichtungsassistenten überspringen? welcome_to_pocketmine = Willkommen bei Elywing!\nBevor es mit der Einrichtung deines neuen Servers losgehen kann, musst du die Lizenz akzeptieren.\nElywing ist unter der GPL Lizenz Version 3 und neuer lizenziert, welche du in der Datei LICENCE nachlesen kannst. accept_license = Akzeptierst du die Lizenz? you_have_to_accept_the_license = Um Elywing weiterverwenden zu können, musst du der Lizenz zustimmen. setting_up_server_now = Du bist nun dabei, deinen Server einzurichten. default_values_info = Drücke einfach Enter, um die Standardwerte zu übernehmen. server_properties = Du kannst sie auch später in der Datei server.properties ändern. name_your_server = Gib deinem Server einen Namen. port_warning = Ändere nicht den Wert für den Standart-Port, falls dies dein erster Server ist. server_port = Server-Port invalid_port = Ungültiger Server-Port online_mode_info = Im Online-Modus wird der Spieler gezwungen, sich mit seinem XBOX-Account zu authentifizieren.\nEs wird die Aktivierung des Online-Modus empfohlen, falls der Server für externe Spieler geöffnet ist. online_mode = Möchtest du den Online-Modus aktivieren? ram_warning = Der RAM ist die maximale Menge an Arbeitsspeicher, welchen Elywing verwenden darf. Es wird ein Wert von 128-256 MB empfohlen. server_ram = Server RAM in MB gamemode_info = Wähle zwischen Kreativmodus (1) oder Überlebensmodus (0) default_gamemode = Standard Spielmodus max_players = Max. Online-Spieler spawn_protection_info = Der Spawnschutz (spawn protection) verhindert das Setzen oder Abbauen von Blöcken im Spawn-Bereich, außer für OPs. spawn_protection = Spawnschutz aktivieren? level_name = Bestimme den Namen der Welt level_type = Lege fest, welchen Welttyp der Server verwenden soll invalid_level_type = Ungültiger Welttyp announce_player_achievements = Sollen die Spieler benachrichtigt werden, wenn ein Erfolg erzielt wird? op_info = Ein OP ist ein Spieler, der Admin ist. OPs können mehr Befehle ausführen als normale Spieler. op_who = OP Spielername (z.B. dein Spielername) op_warning = Du kannst später weitere OP Spieler hinzufügen, indem du eingibst /op whitelist_info = Mit Aktivierung der Whitelist dürfen nur noch Spieler joinen, die darin enthalten sind. whitelist_enable = Möchtest du die Whitelist aktivieren? whitelist_warning = Du musst die Spieler dann noch zur Whitelist hinzufügen query_warning1 = Query ist ein Protokoll, das von verschiedenen Tools verwendet wird, um Informationen zu deinem Servers und die darauf angemeldeten Spieler abzufragen. query_warning2 = Wenn du es deaktivierst, kannst du keine Serverlisten nutzen. query_disable = Möchtest du Query deaktivieren? rcon_info = RCON ist ein Protokoll, über welches man sich aus der Ferne mit einem Passwort zur Serverkonsole verbinden kann. rcon_enable = Möchtest du RCON aktivieren? rcon_password = RCON Passwort (du kannst es später ändern) usage_info = Die anonymen Nutzungsdaten ermöglichen uns, eine globale Statistik für Pocketmine-MP und dessen Plugins zu generieren. Du kannst sie dir auf stats.pocketmine.net ansehen. usage_disable = Möchtest du die anonymen Nutzungsdaten deaktivieren? ip_get = Deine interne und externe IP-Adresse wird ermittelt ip_warning = Deine externe IP ist {{EXTERNAL_IP}}. Möglicherweise muss eine Port-Weiterleitung zu deiner internen IP {{INTERNAL_IP}} eingerichtet werden. ip_confirm = Stelle sicher, dass du das geprüft hast. Falls du eine Weiterleitung benötigst, aber nicht einrichtest, wird kein externer Spieler joinen können. [Drücke Enter] you_have_finished = Du hast den Einrichtungsassistenten nun erfolgreich abgeschlossen. pocketmine_will_start = Elywing wird jetzt gestartet. Gib /help ein, um dir eine Liste verfügbarer Befehle anzeigen zu lassen. pocketmine_plugins = Prüfe auch das Plugin-Repository im Web, um deinem Server weitere Features, Minigames oder erweiterten Schutz hinzuzufügen. ================================================ FILE: src/pocketmine/lang/Installer/eng.ini ================================================ language_has_been_selected = English has been correctly selected. skip_installer = Do you want to skip the set-up wizard? welcome_to_pocketmine = Welcome to Elywing!\nBefore starting setting up your new server you have to accept the license.\nT is licensed under the LGPL License,\nthat you can read opening the LICENSE file on this folder. accept_license = Do you accept the License? you_have_to_accept_the_license = You have to accept the LGPL license to continue using Elywing setting_up_server_now = You are going to set up your server now. default_values_info = If you don't want to change the default value, just press Enter. server_properties = You can edit them later on the server.properties file. name_your_server = Give a name to your server port_warning = Do not change the default port value if this is your first server. server_port = Server port invalid_port = Invalid server port online_mode_info = In online mode, the server will force the user to login via XBOX.\nEnabling online mode is recommended if the server is opened to external players. online_mode = Do you want to enable online mode? gamemode_info = Choose between Creative (1), Survival (0), or Spectator (3) default_gamemode = Default gamemode max_players = Max. online players spawn_protection_info = The spawn protection disallows placing/breaking blocks in the spawn zone except for OPs spawn_protection = Enable spawn protection? level_name = Set the name of the level level_type = Set the server's level type invalid_level_type = Invalid level type announce_player_achievements = Announce when a player gets an achievement? op_info = An OP is the player admin of the server. OPs can run more commands than normal players op_who = OP player name (example, your game name) op_warning = You will be able to add an OP user later using /op whitelist_info = The white-list only allows players in it to join. whitelist_enable = Do you want to enable the white-list? whitelist_warning = You will have to add the players to the white-list query_warning1 = Query is a protocol used by different tools to get information of your server and players logged in. query_warning2 = If you disable it, you won't be able to use server lists. query_disable = Do you want to disable Query? rcon_info = RCON is a protocol to remote connect with the server console using a password. rcon_enable = Do you want to enable RCON? rcon_password = RCON password (you can change it later) usage_info = The anonymous usage data allows us to calculate global statistics for PocketMine-MP and its plugins. You can view them on stats.pocketmine.net usage_disable = Do you want to disable the anonymous usage? ip_get = Getting your external IP and internal IP ip_warning = Your external IP is {{EXTERNAL_IP}}. You may have to port-forward to your internal IP {{INTERNAL_IP}} ip_confirm = Be sure to check it, if you have to forward and you skip that, no external players will be able to join. [Press Enter] you_have_finished = You have finished the set-up wizard correctly pocketmine_will_start = Elywing will now start. Type /help to view the list of available commands. ================================================ FILE: src/pocketmine/lang/Installer/fra.ini ================================================ language_has_been_selected = Français a été correctement sélectionné. skip_installer = Voulez-vous passer l'assistant d'installation? welcome_to_pocketmine = Bienvenue sur Elywing!\nAvant de commencer à paramétrer votre nouveau serveur, vous devez accepter la license d'utilisation.\nElywing est sous licence LGPL,\nvous pouvez la lire en ouvrant le fichier LICENSE dans ce dossier. accept_license = Acceptez-vous la Licence? you_have_to_accept_the_license = Vous devez accepter la licence LGPL pour continuer à utiliser Elywing setting_up_server_now = Vous êtes maintenant prêt à paramétrer votre serveur. default_values_info = Si vous ne voulez pas changer la valeur par défaut, appuyez sur entrée. server_properties = Vous pouvez éditer cela plus tard dans le fichier server.properties. name_your_server = Donnez un nom à votre serveur port_warning = Ne changez pas la valeur par défaut du port si c'est votre premier serveur. server_port = Port du serveur invalid_port = Port du serveur invalide ram_warning = La RAM est au maximum de sa capacité par rapport à la mémoire utilisée par PocketMine-MP. Une valeur de 128-256 MB est recommandée server_ram = RAM du serveur en MB gamemode_info = Choisir entre Créatif (1) ou Survie (0) default_gamemode = Mode de jeu par défaut max_players = Joueurs max. en ligne spawn_protection_info = La protection de spawn désactive le placement/cassement de blocs dans la zone de spawn excepté pour les OPs spawn_protection = Activer la protection de spawn? level_name = Donnez un nom à votre niveau level_type = Définissez le type de niveau du serveur invalid_level_type = Type de niveau invalide announce_player_achievements = Annoncer les succés dans le salon de discussion? op_info = Un OP est un administrateur du serveur. Les OPs peuvent exécuter plus de commandes que les joueurs normaux op_who = Nom de joueur OP (exemple, votre nom de jeu) op_warning = Vous serez en mesure d'ajouter un OP plus tard en utilisant /op whitelist_info = La white-list autorise seulement les joueurs présents sur celle-ci. whitelist_enable = Voulez-vous activer la white-list? whitelist_warning = Vous devrez ajouter les joueurs à la white-list query_warning1 = Query est un protocole utilisé par différents outils pour avoir des informations sur votre serveur et les joueurs connectés. query_warning2 = Si vous le désactivez, vous ne pourrez pas utiliser les listes du serveur. query_disable = Voulez-vous désactiver Query? rcon_info = RCON est un protocole pour se connecter à distance à la console du serveur par un mot de passe. rcon_enable = Voulez-vous activer RCON? rcon_password = Mot de passe RCON (vous pouvez le changer plus tard) usage_info = Les données d'utilisation anonyme vous permettent de calculer les statistiques globales de PocketMine-MP et ses plugins. Vous pouvez les voir sur stats.pocketmine.net usage_disable = Voulez-vous désactiver l'utilisation anonyme? ip_get = Obtention de votre IP externe et IP interne ip_warning = Votre IP externe est {{EXTERNAL_IP}}. Vous pourriez avoir à transmettre au port votre IP interne {{INTERNAL_IP}} ip_confirm = Vérifiez-le bien, si vous avancez et sautez cette étape, les joueurs extérieurs ne pourront pas rejoindre votre serveur. [Tapez Enter] you_have_finished = Vous avez correctement terminé l'assistant d'installation pocketmine_will_start = Elywing va maintenant démarrer. Tapez /help pour voir la liste des commandes disponibles. pocketmine_plugins = Vérification du répertoire de plugin pour ajouter des nouvelles caractéristiques, mini-jeux, ou une protection avancée de votre serveur ================================================ FILE: src/pocketmine/lang/Installer/ita.ini ================================================ language_has_been_selected = Hai selezionato correttamente l'italiano. skip_installer = Vuoi saltare la procedura guidata di installazione? welcome_to_pocketmine = Benvenuto in Elywing!\nPrima di iniziare la configurazione del nuovo server e' necessario accettare la licenza.\nElywing e' rilasciato sotto la licenza LGPL,\nche si puo' leggere aprendo il file di licenza su questa cartella. accept_license = Accettate la licenza? you_have_to_accept_the_license = Bisogna accettare la licenza LGPL per continuare ad utilizzare Elywing setting_up_server_now = Si sta per configurare il server ora. default_values_info = Se non si desidera modificare il valore di default, basta premere Invio. server_properties = E' possibile modificarli in seguito aprendo il file server.properties. name_your_server = Nome del server port_warning = Non modificare il valore della porta di default se questo e' il vostro primo server. server_port = Porta del server invalid_port = Porta del server invalida online_mode_info = In modalita' online, il server costringera' l'utente a effettuare il login tramite XBOX. \nL'attivazione della modalita' online e' consigliato se il server e' aperto ai giocatori esterni. online_mode = Vuoi abilitare la modalita' online? ram_warning = La RAM e' la quantita' massima di memoria che Elywing utilizzera'. Si consiglia un valore di 128-256 MB server_ram = Server RAM in MB gamemode_info = Scegliere tra Creativa (1) o sopravvivenza (0) default_gamemode = Modalita' di gioco predefinito max_players = Max. giocatori online spawn_protection_info = La protezione di spawn non consente il piazzamento di blocchi e la loro rottura nella zona di spawn ad eccezione degli operatori spawn_protection = Attiva protezione spawn? level_name = Impostare il nome del livello level_type = Impostare il tipo di livello di server invalid_level_type = Tipo di livello non valido announce_player_achievements = Annuncia quando un giocatore ottiene un obbiettivo? op_info = Un operatore e' il giocatore di amministrazione del server. Gli operatori possono eseguire piu' comandi di giocatori normali op_who = Nome delgli operatori (esempio, il tuo nome) op_warning = Sarete in grado di aggiungere un operatore in seguito utilizzando / op whitelist_info = Il white-list consente solo a certi giocatori di unirsi. whitelist_enable = Desideri attivare la white-list? whitelist_warning = Dovrai aggiungere i giocatori alla whitelist query_warning1 = Query e' un protocollo utilizzato da diversi strumenti per ottenere informazioni del server e giocatori loggati. query_warning2 = Se si disattiva, non sarai in grado di utilizzare gli elenchi di server. query_disable = Vuoi disattivare Query? rcon_info = RCON e' un protocollo per la connessione remota con la console del server utilizzando una password. rcon_enable = Vuoi abilitare RCON? rcon_password = password RCON (si puo' cambiare in un secondo momento) usage_info = I dati di utilizzo anonimi ci permette di calcolare le statistiche globali per PocketMine-MP e dei suoi plug-in. e' possibile visualizzarle su stats.pocketmine.net usage_disable = Vuoi per disattivare l'uso di dati anonimo? ip_get = Ottenendo il vostro IP esterno e IP interno ip_warning = Il tuo IP esterno e' {{EXTERNAL_IP}}. Potrebbe essere necessario port-forward per il tuo IP interno {{INTERNAL_IP}} ip_confirm = Assicuratevi di controllarlo, se devi fare il port-forward e non lo fai, nessun giocatore esterno sara' in grado di entrare. [Premere Invio] you_have_finished = e' terminata correttamente la procedura guidata di installazione pocketmine_will_start = Elywing ora si avviera'. Digita /help per visualizzare l'elenco dei comandi disponibili. pocketmine_plugins = Controllare il Repository Plugin per aggiungere nuove funzionalita', minigiochi, o protezione avanzata per il vostro server ================================================ FILE: src/pocketmine/lang/Installer/jpn.ini ================================================ language_has_been_selected = 日本語に設定されました skip_installer = セットアップウィザードをスキップしますか? welcome_to_pocketmine = Elywingをインストールして頂きありがとうございます!サーバのセットアップを開始するにはライセンスに同意する必要があります。ElywingはLGPLライセンスに基づいて認可されており、これについてはこのフォルダ内のLICENSEファイルから確認することができます。 accept_license = ライセンスに同意しますか? you_have_to_accept_the_license = Elywingを使用するにはLGPLライセンスに同意する必要があります setting_up_server_now = サーバのセットアップを開始します default_values_info = 設定を変更しない場合は、Enterキーを押してください。 server_properties = それらの設定は後からでもserver.propertiesファイルから変更できます name_your_server = あなたのサーバに名前を付けてください port_warning = これが初めてのサーバの場合は、ポート番号をデフォルトから変更しないでください server_port = サーバポート invalid_port = 無効なサーバポートです online_mode_info = オンラインモードでは、ユーザーがXBOXを経由してログインすることを強制します。\nサーバーが外部に開放されている場合はオンラインモードを有効にすることをおすすめします。 online_mode = オンラインモードを有効にしますか? ram_warning = RAMはElywingが使用し得るメモリの最大値を示しています。128-256MBの範囲内で指定することを推奨します。 server_ram = RAMの単位はMBです gamemode_info = クリエイティブモード(1)、またはサバイバルモード(0)を選択してください default_gamemode = デフォルトのゲームモード max_players = 最大プレイヤー数 spawn_protection_info = スポーンプロテクションは、OPでないプレイヤーによるスポーン地点付近でのブロックの設置/破壊を制限します spawn_protection = スポーンプロテクションを有効にしますか? level_name = ワールド名を指定してください level_type = サーバーのワールドタイプを指定してください invalid_level_type = 無効なレベルタイプです announce_player_achievements = プレイヤーが実績を獲得したときにアナウンスしますか? op_info = OPとはそのサーバの管理権限を指します。OPを持ったプレイヤーは他のプレイヤーよりも多くのコマンドを使用できます。 op_who = OPプレイヤー名(例: あなたのゲーム内での名前) op_warning = 後から/op <プレイヤー名>コマンドを実行してOPプレイヤーを追加することもできます whitelist_info = ホワイトリストはこのサーバに入ることのできるプレイヤーを制限します。 whitelist_enable = ホワイトリストを有効にしますか? whitelist_warning = プレイヤーをホワイトリストに追加してください query_warning1 = クエリは他のツールによりあなたのサーバやプレイヤーの情報を取得するためのプロトコルです。 query_warning2 = それを無効にした場合、サーバリストを使用できなくなる可能性があります。 query_disable = クエリを無効にしますか? rcon_info = RCONはパスワードを用いてサーバコンソールからリモート接続するためのプロトコルです。 rcon_enable = RCONを有効にしますか? rcon_password = RCONパスワード(後から変更できます) usage_info = 世界中で使われているPocketMine-MPやそのプラグインの統計を算出するために匿名の使用データが送信されます。統計はこちらから確認できます。stats.pocketmine.net usage_disable = 匿名の使用データの送信を拒否しますか? ip_get = グローバルIPとプライベートIPの取得 ip_warning = あなたのグローバルIPは{{EXTERNAL_IP}}です。プライベートIP{{INTERNAL_IP}}をポート解放してください。 ip_confirm = 必ずポート解放ができているか確認してください。ポートが解放できていなかった場合、他のプレイヤーがサーバに入れなくなる恐れがあります。[Enterキーを押してください] you_have_finished = セットアップは正しく終了しました pocketmine_will_start = Elywingを起動します。/helpと入力すれば使用可能なコマンド一覧を表示できます。 pocketmine_plugins = 拡張機能や管理システム、ミニゲームなどを追加できるプラグインリポジトリも確認してみてください ================================================ FILE: src/pocketmine/lang/Installer/kor.ini ================================================ language_has_been_selected = 한국어가 언어로 선택되었습니다. skip_installer = 설치 마법사를 건너뛰겠습니까? welcome_to_pocketmine = Elywing에 오신 것을 환영합니다!\n서버 설치를 시작하기 전, 약관에 동의해야 합니다. \nElywing는 GNU 약소 일반 공중 사용 허가서(LGPL) 하에 배포되고 있습니다. \n이 폴더에서 약관을 읽을 수 있습니다. accept_license = 약관에 동의하십니까? you_have_to_accept_the_license = GNU 약소 일반 공중 사용 허가서(LGPL)에 동의하셔야 Elywing를 사용할 수 있습니다. setting_up_server_now = 서버 설정을 시작합니다. default_values_info = 기본값을 수정하고 싶지 않으면, 엔터를 누르시기 바랍니다. server_properties = 이 설정들은 server.properties 파일에서 세부적으로 변경이 가능합니다. name_your_server = 당신의 서버 이름을 입력하시기 바랍니다. port_warning = 만약 당신이 서버를 처음 설정한다면 포트 값을 변경하지 마세요. server_port = 서버 포트 invalid_port = 서버 포트가 잘못 입력되었습니다. online_mode_info = 온라인 모드에서는 서버가 사용자를 XBOX를 사용해서만 로그인하도록 합니다.\n외부 서버를 여는 경우 온라인 모드를 활성화 하는 것이 좋습니다. online_mode = 온라인 모드를 활성화 하시겠습니까? ram_warning = RAM 값은 Elywing에 할당할 메모리의 크기입니다. 128~256MB를 권장합니다. server_ram = 서버의 램(RAM) (MB) gamemode_info = 크리에이티브 (1) 또는 서바이벌 (0) 게임모드 중 하나를 고르세요. default_gamemode = 기본 게임 모드 max_players = 최대 동시접속 인원 수 spawn_protection_info = 스폰 보호는 OP를 제외한 유저들이 스폰 지역 근처에서 블럭을 놓거나 부수는 것을 방지합니다. spawn_protection = 스폰 지역 보호를 사용하겠습니까? level_name = 레벨의 이름을 설정하시기 바랍니다. level_type = 레벨의 유형을 설정하시기 바랍니다. invalid_level_type = 레벨 유형이 잘못 입력되었습니다. announce_player_achievements = 플레이어가 도전 과제를 달성했을때 알리시겠습니까? op_info = OP는 서버 관리자를 뜻합니다. OP는 일반 플레이어보다 훨씬 많은 명령어들을 사용할 수 있습니다 op_who = OP 권한을 줄 플레이어 이름(예: 당신의 닉네임) op_warning = 또는 이후에 /op <플레이어 이름> 을 입력해 그 유저에게 OP 권한을 줄 수도 있습니다 whitelist_info = 화이트리스트를 사용하면 허용된 플레이어들만 서버에 접속할 수 있습니다. whitelist_enable = 화이트리스트를 사용하겠습니까? whitelist_warning = 당신은 접속을 허용할 플레이어들의 이름을 적어야 합니다 query_warning1 = 쿼리 (Query)는 당신의 서버와 현재 접속한 플레이어들의 정보를 알 수 있게 해주는 여러가지 기능이 담긴 프로토콜입니다. query_warning2 = 쿼리를 사용하지 않으면, 당신은 서버 리스트를 사용할 수 없게 됩니다. query_disable = 쿼리를 사용하지 않겠습니까? rcon_info = RCON은 비밀 번호를 사용하여 서버 명령창에 원격으로 접속할 수 있는 프로토콜입니다. rcon_enable = RCON을 사용하시겠습니까? rcon_password = RCON 비밀번호 설정 (나중에 server.properties 에서 변경이 가능합니다.) usage_info = 익명 사용 통계 전송을 허용하면 PocketMine-MP가 세계의 서버 상황과 플러그인들을 통계화하는 데 사용할 수 있습니다. 당신은 stats.pocketmine.net에서 이를 확인할 수 있습니다 usage_disable = 익명 사용 통계 전송을 사용하지 않으시겠습니까? ip_get = 내/외부 IP 주소 얻기 ip_warning = 당신의 외부 IP는 {{EXTERNAL_IP}}입니다. 당신의 내부 IP {{INTERNAL_IP}}로 포트포워딩이 필요할 수 있습니다 ip_confirm = 신중하게 확인하세요. 만약 포트포워딩이 필요하지만 하지 않을 경우 외부에서 플레이어들이 접속할 수 없게 됩니다. [계속 하시려면 엔터를 누르세요] you_have_finished = Elywing의 설치가 모두 완료되었습니다. pocketmine_will_start = Elywing 서버를 구동합니다. /help로 사용 가능한 모든 명령어의 목록을 보시기 바랍니다. pocketmine_plugins = 플러그인 저장소에서 새로운 기능을 추가하세요. 새로운 기능, 미니게임을 추가하거나 고급 기능으로 당신의 서버를 보호할 수 있습니다 ================================================ FILE: src/pocketmine/lang/Installer/rus.ini ================================================ language_has_been_selected = Был выбран русский язык. skip_installer = Вы хотите пропустить мастер настройки? welcome_to_pocketmine = Добро пожаловать в Elywing!\nПеред началом установки нового сервера, вы должны согласиться с лицензией. \nElywing лицензировано на условиях LGPL лицензии, \nс которой вы можете ознакомиться, открыв файл LICENSE в этой папке. accept_license = Принимаете ли вы лицензию? you_have_to_accept_the_license = Вы должны принять LGPL лицензию, чтобы продолжить использование Elywing setting_up_server_now = Теперь вы можете настроить сервер. default_values_info = Если вы не хотите изменять стандартное значение, просто нажмите Enter. server_properties = Вы можете редактировать их позже в файле server.properties. name_your_server = Дайте имя вашему серверу port_warning = Не изменяйте значение порта по умолчанию, если это ваш первый сервер. server_port = Порт сервера invalid_port = Неверный порт сервера online_mode_info=online-mode позволяет проверять игрока на аутентификацию в XBOX Live online_mode=Включить online-mode? ram_warning = RAM - максимальный объем памяти, котооый будет использовать Elywing, рекомендуемое значение 128-256 МБ server_ram = Оперативная память сервера в МБ gamemode_info = Выберите между Креативом-(1), или Выживанием (0) default_gamemode = Режим игры по умолчанию max_players = Максимум онлайн игроков spawn_protection_info = Защита спавна запрещает размещение/разрушение блоков на спавне, за исключением операторов spawn_protection = Включить защиту спавна? level_name = Дайте имя миру вашего сервера level_type = Выберите тип мира invalid_level_type = Неверный тип мира announce_player_achievements = Объявлять достижение игрока когда он его получил? op_info = Оператор - Администратор сервера, который может использовать больше команд, чем обычный игрок op_who = Имя оператора (К примеру ваш ник в игре) op_warning = Вы можете добавить оператора позже используя команду /op whitelist_info = White-list позволяет присоединиться только игрокам в этом списке. whitelist_enable = Вы хотите включить white-list? whitelist_warning = Вам придётся добавить игроков в white-list query_warning1 = Query это протокол, используемый разными инструментами, чтобы получить информацию о вашем сервере и о зашедших игроках. query_warning2 = Если вы отключите его, вы не сможете использовать списки серверов. query_disable = Вы хотите отключить Query? rcon_info = RCON - это протокол для удаленного управления сервером через консоль, с использованием пароля. rcon_enable = Вы хотите включить RCON? rcon_password = RCON пароль (вы сможете изменить его позже) usage_info = Сбор анонимных данных о использовании поможет нам проанализировать глобальную статистику для PocketMine-MP и плагинов. Статистика доступна по адресу stats.pocketmine.net usage_disable = Вы хотите отключить сбор анонимных данных о использовании? ip_get = Получение вашего внешнего и внутреннего IP адреса ip_warning = Ваш внешний IP адрес: {{EXTERNAL_IP}}. Возможно вам необходимо открыть порты к вашему внутреннему IP адресу: {{INTERNAL_IP}} ip_confirm = Убедитесь в том, что вы открыли порты, в противном случае игроки вне вашей локальной сети не смогут подключится. [Нажмите Enter] you_have_finished = Вы закончили работу с мастером установки pocketmine_will_start = Elywing сейчас запустится. Введите /help для просмотра доступных вам команд. pocketmine_plugins = Посетите репозиторий плагинов для получения новых функций, мини-игр, или повышения защиты вашего сервера ================================================ FILE: src/pocketmine/lang/Installer/ukr.ini ================================================ language_has_been_selected = Було вибрано українську мову. skip_installer = Бажаєте пропустити майстер налаштування? welcome_to_pocketmine = Вітаємо в Elywing!\nПеред початком встановлення нового серверу, ви маєте погодитися з умовами ліцензії. \nElywing ліцензовано на умовах LGPL. \nДетально ви можете ознайомитися з нею у файлі LICENSE в цій теці. accept_license = Ви приймаєте умови ліцензії? you_have_to_accept_the_license = Ви маєте погодитися з умовами LGPL ліцензії для подальшого користування Elywing setting_up_server_now = Тепер ви можете налаштувати сервер. default_values_info = Для вибору значень за замовчуванням просто натискайте Enter. server_properties = Пізніше ви зможете їх відредагувати у файлі server.properties. name_your_server = Вкажіть назву вашого серверу port_warning = Не змінюйте номер порту за замовчуванням, якщо ви налаштовуєте сервер вперше. server_port = Порт серверу invalid_port = Неправильний порт серверу online_mode_info=online-mode дозволяє перевіряти у гравця наявність аутентифікації в XBOX Live online_mode=Ввімкнути online-mode? ram_warning = RAM - максимальний об’єм пам’яті, який буде використовувати Elywing, рекомендоване значення - 128-256 МБ server_ram = Оперативна пам’ять серверу в МБ gamemode_info = Вкажіть 1 для Творчого режиму на сервері або 0 для Виживання default_gamemode = Режим гри за замовчуванням max_players = Максимальна кількість гравців spawn_protection_info = Захист місця появи гравців забороняє розміщення або руйнування блоків на місті появи гравця, за виключенням операторів spawn_protection = Ввімкнути захист спавну? level_name = Вкажіть назву світу level_type = Вкажіть тип світу invalid_level_type = Неправильний тип світу announce_player_achievements = Демонструвати досягнення гравців? op_info = Оператор - це адміністратор серверу, що може використовувати більше команд, ніж звичайний гравець op_who = Нік оператора (Наприклад, ваш нік в грі) op_warning = Ви зможете додати оператора пізніше за допомогою команди /op <гравець> whitelist_info = Білий список дозволяє заходити тільки тим гравцям, що знаходяться в ньому. whitelist_enable = Бажаєте ввімкнути білий список? whitelist_warning = Необхідно додати гравців до білого списку query_warning1 = QUERY - це протокол, що використовується різними інструментами для отримання інформації про сервер та про гравців серверу. query_warning2 = Якщо ви його вимкнете, то не зможете користуватися моніторингами серверів. query_disable = Ви хочете вимкнути QUERY? rcon_info = RCON - це протокол, який реалізує віддалене керування сервером. Захищене паролем. rcon_enable = Бажаєте ввімкнути RCON? rcon_password = RCON пароль (ви зможете змінити його пізніше) usage_info = Збір анонімних даних про користування допоможе нам проаналізувати глобальну статистику для PocketMine-MP и плагінів. Статистика доступна за адресою stats.pocketmine.net usage_disable = Бажаєте вимкнути збір анонімних даних про користування? ip_get = Отримання вашого внутрішньої і зовнішньої IP адрес ip_warning = Ваша зовнішня IP адреса: {{EXTERNAL_IP}}. Можливо, вам необхідно відкрити порти до вашої внутрішньої адреси: {{INTERNAL_IP}} ip_confirm = Переконайтеся в тому, що ви відкрили порти, інакше гравці поза локальною мережею не зможуть приєднатися. [Натисніть Enter] you_have_finished = Ви завершили роботу з майстром встановлення pocketmine_will_start = Elywing зараз запуститься. Введіть /help для перегляду доступних команд. pocketmine_plugins = Відвідайте сховище плагінів для отримання нових функцій, міні ігр або для підвищення захисту вашого серверу ================================================ FILE: src/pocketmine/lang/Installer/zho.ini ================================================ language_has_been_selected=您選擇了繁體中文 skip_installer=您想跳過安裝精靈嗎? welcome_to_pocketmine=歡迎來到Elywing!\n在開始使用您的新伺服器前您需要接受以下協議\nElywing使用了LGPL協議\n你可以在這個資料夾中找到LICENCE文件 accept_license=您接受協議內容嗎? you_have_to_accept_the_license=您必須接受LGPL協議來繼續使用Elywing setting_up_server_now=您現在要開始設定您的伺服器了 default_values_info=如果您希望使用預設值請直接按下Enter鍵 server_properties=您以後可以在server.properties中修改設定 name_your_server=命名您的伺服器: port_warning=如果這是您第一次設定服伺服器,請儘量不要更改接口 server_port=伺服器接口: invalid_port=不正確的伺服器接口 online_mode_info = 多人連線模式下, 伺服器將強制玩家需要 Xbox 登入\n當多人模式時, 建議讓伺服器開啟此功能 online_mode = 您想啟用多人模式? ram_warning=RAM是Elywing可用的最大記憶體。推薦範圍:128~256 MB server_ram=分配給服務器的記憶體(MB): gamemode_info=選擇模式:(0) 生存模式 或 (1) 創造模式 default_gamemode=預設遊戲模式 max_players=最大在線人數 spawn_protection_info=出生點保護可以在出生點範圍內保護所有方塊不被放置與破壞 spawn_protection=啟用出生點保護? level_name = 命名您的伺服器世界: level_type = 設置您的伺服器世界類型: invalid_level_type = 無效的世界類型 announce_player_achievements = 當玩家獲得成就時是否公告? op_info=OP是一個伺服器的管理員, 可以執行比普通玩家更多的指令 op_who=請輸入OP的玩家名稱 op_warning=你可以執行\"/op <玩家名稱>\"來新增OP whitelist_info=白名單可以只允許在其列表中的玩家加入 whitelist_enable=您想啟用白名單嗎? whitelist_warning=你可以用"/whitelist add <玩家名稱>"把別人加入白名單 query_warning1=Query請求是一個用於不同程式的協議用來獲取您伺服器數據和登入的玩家 query_warning2=如果您停用了它,您將不能使用伺服器列表 query_disable=您希望停用Query請求嗎? rcon_info=RCON是一個用來遠程連接到伺服器控制台的協議(需要密碼) rcon_enable=您希望啟用RCON嗎? rcon_password=RCON密碼 (您以後能更改它): usage_info=匿名數據讓我們可以獲得全球的PocketMine-MP和它的插件的統計訊息,您可以在 stats.pocketmine.net 查看統計訊息 usage_disable=您希望停用匿名數據嗎? ip_get=取得你的外網IP和內網IP ip_warning=您的外網IP是 {{EXTERNAL_IP}},您可能需要接口轉發到您的內網IP {{INTERNAL_IP}} ip_confirm=請確認您檢查了它,如果您直接進入下一步並跳過這一步,沒有外部的玩家可以加入 [按\"Enter\"鍵] you_have_finished=您已經成功完成了伺服器安裝精靈 pocketmine_will_start=Elywing現在開始運行,輸入 \"/help\" 來看所有可用的指令 pocketmine_plugins=請查看插件源來新增新的功能,迷你遊戲或者對伺服器的高級保護 ================================================ FILE: src/pocketmine/lang/locale/ces.ini ================================================ language.name=Čeština language.selected=Vybráno {%0} ({%1}) jako základní jazyk multiplayer.player.joined={%0} se připojil do hry multiplayer.player.left={%0} se odpojil ze hry chat.type.text={%0} : {%1} chat.type.achievement={%0} právě získal ocenění {%1} disconnectionScreen.outdatedClient=Zastaralá verze! disconnectionScreen.outdatedServer=Zastaralý server! disconnectionScreen.serverFull=Server je plný! disconnectionScreen.noReason=Odpojen ze serveru disconnectionScreen.invalidSkin=Neplatný skin! disconnectionScreen.invalidName=Nevhodný herní nick! death.fell.accident.generic={%0} spadl z vysokého místa death.attack.inFire={%0} shořel v ohni death.attack.onFire={%0} uhořel k smrti death.attack.lava={%0} se pokusil plavat v lávě death.attack.inWall={%0} se udusil ve zdi death.attack.drown={%0} se utopil death.attack.cactus={%0} byl upíchán k smrti death.attack.generic={%0} zemřel death.attack.explosion={%0} vybouchnul death.attack.explosion.player={%0} byl odbouchnut hráčem {%1} death.attack.magic={%0} byl zabit magickým efektem death.attack.wither={%0} zemřel na útok Withera death.attack.mob= {%0} byl zabit {%1} death.attack.player= {%0} byl zabit {%1} death.attack.player.item={%0} byl zabit hráčem {%1} používajícím {%2} death.attack.arrow={%0} byl zastřelen {%1} death.attack.arrow.item={%0} byl zastřelen hráčem {%1} používajícím {%2} death.attack.fall={%0} spadl z moc velké výšky death.attack.outOfWorld={%0} vypadl ze světa gameMode.survival=§l§f"Mód Survival" gameMode.creative=§l§f"Mód Creative" gameMode.adventure=§l§f"Mód Adventure" gameMode.spectator=§l§f"Mód Spetractor" gameMode.changed=Tvůj herní mód byl úspěšně změněn potion.moveSpeed=Rychlost potion.moveSlowdown=zpomalení potion.digSpeed=Rychle kopání potion.digSlowDown=Pomalé kopání potion.damageBoost=Síla potion.heal=Rychlé uzdravení potion.harm=Rychlé zranění potion.jump=vysoké skoky potion.confusion=nevolnost potion.regeneration=Regenerace potion.resistance=Odpor potion.fireResistance=Ohnivzdornost potion.waterBreathing=Dýchání pod vodou potion.invisibility=Neviditelnost potion.blindness=Hloupost potion.nightVision=Noční vidění potion.hunger=Hlad potion.weakness=Slabost potion.poison=Jed potion.wither=Wither potion.healthBoost=Zvýšení života potion.absorption=Absorbace potion.saturation=Saturace commands.generic.exception=Při pokusu o provedení tohoto příkazu došlo k neznámé chybě commands.generic.permission=§l§cNemas opravneni pouzit tento prikaz commands.generic.notFound=§l§cNeznamy příkaz. Pouzijte /help pro ziskani seznamu prikazu commands.generic.player.notFound=Tento hráč nemůže být nalezen commands.generic.usage=Vyuziti:{%0} commands.time.added=Pridáno {%0} do časů commands.time.set=Čas nastaven na {%0} commands.time.query=Čas je {%0} commands.me.usage=/me commands.give.item.notFound=Neexistuje žádný item s tímto ménem {%0} commands.give.success=Given {%0} * {%1} to {%2} commands.give.tagError=Rozbor data tagu neůspesný: {%0} commands.effect.usage=/effect [seconds] [amplifier] [hideParticles] NEBO /effect clear commands.effect.notFound=Efekt s ID {%0} neexistuje commands.effect.success=Daný {%0} (ID {%1}) * {%2} na {%3} pro {%4} sekund commands.effect.success.removed=Vzal {%0} z {%1} commands.effect.success.removed.all=Vzal všechny efekty od {%0} commands.effect.failure.notActive=Nemůžu vzít {%0} od {%1} protože efekt nemá commands.effect.failure.notActive.all=Nemůžu získat efekt od {%0} protože žádný nemá commands.enchant.noItem=Terc nedrzi item commands.enchant.notFound=Tady není zadný enchant s ID {%0} commands.enchant.success=Enchant byl uspesný commands.enchant.usage=/enchant [level] commands.particle.success=Zapínám efekt {%0} {%1} commands.particle.notFound=Neznámé méno efektu {%0} commands.players.usage=/seznam commands.players.list={%0}/{%1} Hráčů online: commands.kill.successful=Zabit {%0} commands.banlist.ips=Celkem zabanovaných IP adres: %d commands.banlist.players=Je zabanováno {%0} Hráčů: commands.banlist.usage=/banlist [ips|players] commands.defaultgamemode.usage=/defaultgamemode commands.defaultgamemode.success=Základní herní mód je teď {%0} commands.op.success=Povýšen {%0} commands.op.usage=/op commands.deop.success=§l§cDe-opnut {%0} commands.deop.usage=/deop commands.say.usage=/say commands.seed.usage=/seed commands.seed.success=Seed:{%0} commands.ban.success=Zabanován hráč {%0} commands.ban.usage=/ban [Důvod ...] commands.unban.success=Odbanován hráč {%0} commands.unban.usage=/pardon commands.banip.invalid=Zadal jsi špatnou IP adresu nebo tento hráč není online commands.banip.success=Zabanována IP adresa {%0} commands.banip.success.players=Zabanovaná IP adresa {%0} byla přepsána na{%1} commands.banip.usage=/ban-ip [Důvod...] commands.unbanip.invalid=Zadal jsi špatnou ip adresu commands.unbanip.success=Odbanovali jste adresu ip {%0} commands.unbanip.usage=/pardon-ip
commands.save.usage=-ulozit-vse commands.save-on.usage=/zapnout-ukladani commands.save-off.usage=/vypnout-ulozeni commands.save.enabled=Automatické ukládání světa zapnuto commands.save.disabled=Automatické ukládání světa vypnuto commands.save.start=Ukládám... commands.save.success=Svět uložen commands.stop.usage=/stop commands.stop.start=Zastavuji server commands.kick.success={%0} byl odpojen ze hry commands.kick.success.reason={%0} byl kicknut ze serveru z duvodu: {%1} commands.kick.usage=/odpojit [důvod...] commands.tp.success={%0} se portnul k {%1} commands.tp.success.coordinates={%0} Se portnul na souřadnice {%1}, {%2}, {%3} commands.tp.usage=/tp [cílový hráč] OR /tp [cílový hráč] [ ] commands.whitelist.list=Nacházejí se zde {%0} (z {%1} viděných) whitelist hráčů: commands.whitelist.enabled=Zapnut whitelist commands.whitelist.disabled=Vypnut whitelist commands.whitelist.reloaded=Whitelist znova načten commands.whitelist.add.success={%0} byl přidán na whitelist commands.whitelist.add.usage=/whitelist přidat commands.whitelist.remove.success={%0} byl odebrán z whitelistu commands.whitelist.remove.usage=/whitelist odebrat commands.whitelist.usage=/whitelist commands.gamemode.success.self=Změnit svůj herní mód na {%2} commands.gamemode.success.other=Změněn herní mód hráče {%1} na {%2} commands.gamemode.usage=/gamemode [player] commands.help.header=--- Ukazuji pomoc - strana {%0} z {%1} (/help ) --- commands.help.usage=§l/help [strana|jméno příkazu] commands.message.usage=/tell commands.message.sameTarget=Nemůžeš poslat soukromou zprávu sám sobě! commands.difficulty.usage=/difficulty commands.difficulty.success=Herní obtížnost nastavena na {%0} commands.spawnpoint.usage=/spawnpoint [player] [ ] commands.spawnpoint.success={%0}ův spawnpoint byl nastaven na ({%1}, {%2}, {%3}) commands.setworldspawn.usage=/setworldspawn [] commands.setworldspawn.success=Spawn světa byl nastaven na souřadnice ({%0}, {%1}, {%2}) pocketmine.data.playerNotFound=Data hráče {%0} nenalezena, vytvářím nový profil pocketmine.data.playerCorrupted=Špatná data hráče {%0}, vytvářím nový profil pocketmine.data.playerOld=Pro hráče {%0} nalezena stará data, vylepšuji profil pocketmine.data.saveError=Nemůžu nastavit hráče {%0}; {%1} pocketmine.level.notFound=Svět {%0} nebyl nalezen pocketmine.level.loadError=Nemůžu načíst svět {%0}; {%1} pocketmine.level.generationError=Nemůžu vytvořit svět {%0}; {%1} pocketmine.level.tickError=Nemůžu najít svět {%0}; {%1} pocketmine.level.chunkUnloadError=Chyba při načítáni chunku {%0} pocketmine.level.backgroundGeneration=Spawn pro svět {%0} bude vytvořen v pozadí pocketmine.level.defaultError=Žádný základní level nebyl načten pocketmine.level.preparing=Připravuji svět {%0} pocketmine.level.unloading=Odnacitavam svět {%0} pocketmine.server.start=Startuji Minecraft: PE server na verzi {%0} pocketmine.server.networkError=[Network] Zastavil interface {%0} protože {%1} pocketmine.server.networkStart=Startuji server na {%0}:{%1} pocketmine.server.info=Tento server běží na {%0} verzi {%1}"{%2}" (API {%3}) pocketmine.server.info.extended=Tento server používá {%0} {%1}「{%2}」implantována API {%3} pro Minecraft:PE {%4} (protokol {%5}) pocketmine.server.license={%0} je distribuován pod LGPL Licencí pocketmine.server.tickOverload=Pozor! Server je pretižen! pocketmine.server.startFinished=Hotovo ({%0}s)! Pro pomoc napiš "help" nebo "?" pocketmine.server.defaultGameMode=Základní herní mód je:{%0} pocketmine.server.query.start=Zapínám GS4 status poslouchač pocketmine.server.query.info=Nastavuji query port na {%0} pocketmine.server.query.running=Query běží na {%0}:{%1} pocketmine.command.alias.illegal=Nemůžu registrovat alias {%0} protože obsahuje nepoužitelné písmena pocketmine.command.alias.notFound=Nemůžu registrovat alias {%0} protože obsahuje příkaz který neexistuje:{%1} pocketmine.command.exception=Neošetřené použití příkazu '{%0}' in {%1}: {%2} pocketmine.command.plugins.description=Dá list všech pluginů na serveru pocketmine.command.plugins.success=Pluginy ({%0}):{%1} pocketmine.command.plugins.usage=/plugins pocketmine.command.reload.description=Přenačte kofiguraci serveru a jeho pluginy pocketmine.command.reload.usage=/reload pocketmine.command.reload.reloading=Znovu načítám data serveru.... pocketmine.command.reload.reloaded=Načtení serveru proběhlo úspěšně pocketmine.command.status.description=Vrátí zpátky výkon serveru. pocketmine.command.status.usage=/status pocketmine.command.gc.description=Fires garbage collection tasks pocketmine.command.gc.usage=/gc pocketmine.command.timings.description=Nahrává načasování aby viděl stav serveru. pocketmine.command.timings.usage=/timings pocketmine.command.timings.enable=Zapnuto časování & reset pocketmine.command.timings.disable=Vypnuto časování pocketmine.command.timings.timingsDisabled=Prosím zapněte nahrávání zadáním: /timings on pocketmine.command.timings.reset=Nastaven restart pocketmine.command.timings.pasteError=Při vkládání zprávy nastala chyba pocketmine.command.timings.timingsUpload=Nahrávání nahrána na {%0} pocketmine.command.timings.timingsRead=Můžete si přečíst výsledky na {%0} pocketmine.command.timings.timingsWrite=Nahrávání napsána do {%0} pocketmine.command.version.description=Dá vám informace o všech používaných pluginech pocketmine.command.version.usage=/version [plugin name] pocketmine.command.version.noSuchPlugin=Tento server nepoužívá žádný plugin s tímto jménem. Použij /plugins. pocketmine.command.give.description=Daruje specifikovanému hráči určený počet vybraných itemů pocketmine.command.give.usage=/give [amount] [tags...] pocketmine.command.kill.description=Zabití pocketmine.command.kill.usage=/kill [player] pocketmine.command.particle.description=Přidá prvek do světa pocketmine.command.particle.usage=/particle [count] [data] pocketmine.command.time.description=Nastaví čas sveta pocketmine.command.time.usage=/time NEBO /time pocketmine.command.ban.player.description=Zakáže hráči přístup na server pocketmine.command.ban.ip.description=Zakáže specifické IP adrese používat tento server pocketmine.command.banlist.description=Zobrazí všechny zabanované hráče pocketmine.command.defaultgamemode.description=Nastaví základní herní mód pocketmine.command.deop.description=Nastaví hráči op pocketmine.command.difficulty.description=Nastaví obtížnost hry pocketmine.command.enchant.description=Pridá enchant na itemy pocketmine.command.effect.description=Přidá/Odebere hráči op pocketmine.command.gamemode.description=Změní hráči herní mód pocketmine.command.help.description=Ukáže nápovědu pocketmine.command.kick.description=Odebere ban hráči pocketmine.command.list.description=Seznam všech připojených hráčů pocketmine.command.me.description=Udělá popsanou akci v chatu pocketmine.command.op.description=Dá danému hráči status operátora pocketmine.command.unban.player.description=Zruší ban danému hráči pocketmine.command.unban.ip.description=Zruší ban dané IP adrese pocketmine.command.save.description=Uloží data serveru na disk pocketmine.command.saveoff.description=Vypne automatické ukládání serveru pocketmine.command.saveon.description=Zapnuto automatické ukládání serveru pocketmine.command.say.description=Napíše zprávu jako odesílatel pocketmine.command.seed.description=Zobrazí seed světa pocketmine.command.setworldspawn.description=Nastaví spawn světa. Pokud nejsou použity žádné souřadnice, budou použity souřadnice hráče. pocketmine.command.spawnpoint.description=Nastaví spawn hráči pocketmine.command.stop.description=zastaví server pocketmine.command.tp.description=Teleportuje daného hráče (nebo vás) k jinému hráči nebo na jiné souřadnice pocketmine.command.tell.description=Pošle soukromou zprávu vybranému hráči pocketmine.command.whitelist.description=Spravuje list hráčů s povolením navštívit tento server pocketmine.crash.create=Neopravitelná chyba shodila server. Vytvářím report o chybe pocketmine.crash.error=Nemůžu vytvořit správu o chybě: {%0} pocketmine.crash.submit=Prosím nahrajte "{%0}" do archivu chyb a pošli link na stránku ohlašování chyb. Dej co nejvíc informací co můžeš. pocketmine.crash.archive=Zpráva o chybě byla nahrána do archivu chyb. Můžete se na ni podívat na {%0} nebo použít ID#{%1}. pocketmine.debug.enable=LevelDB podpora zapnuta pocketmine.player.invalidMove={%0} se hýbe špatně! pocketmine.player.logIn={%0}[/{%1}:{%2}] se připojil do hry s entitou id {%3} v ({%4},{%5},{%6},{%7}) pocketmine.player.logOut={%0}[/{%1}:{%2}] se odpojil kůli {%3} pocketmine.player.invalidEntity={%0} umřel na útok neznámého moba pocketmine.plugin.load=Načítání {%0} pocketmine.plugin.enable=Povolení {%0} pocketmine.plugin.disable=Zakázání {%0} pocketmine.plugin.restrictedName=Omezené méno pocketmine.plugin.incompatibleAPI=Nekompatibilní API pocketmine.plugin.unknownDependency=Neznámá závislost pocketmine.plugin.circularDependency=oběžná závislost detekována pocketmine.plugin.genericLoadError=Nemůžu načíst plugin '{%0}' pocketmine.plugin.spacesDiscouraged=Plugin '{%0}' používá mezery ve svém jménu, to nejde pocketmine.plugin.loadError=Nemůžu načíst plugin '{%0}':{%1} pocketmine.plugin.duplicateError=Nemůžu načíst plugin '{%0}':plugin existuje pocketmine.plugin.fileError=Nemůžu načíst '{%0}' ve složce '{%1}':{%2} pocketmine.plugin.commandError=Nemůžu načíst příkaz {%0} pro plugin {%1} pocketmine.plugin.aliasError=Nemůžu načíst alias {%0} pro plugin {%1} pocketmine.plugin.deprecatedEvent=Plugin '{%0}' registroval poslouchač pro '{%1}' na metodě '{%2}', ale event. není praktikován. pocketmine.plugin.eventError="Nemůžu předělat event '{%0}' na'{%1}':{%2}na {%3}" ================================================ FILE: src/pocketmine/lang/locale/chs.ini ================================================ # Language file compatible with Minecraft: Pocket Edition identifiers= #= # A message doesn't need to be there to be shown correctly on the client.= # Only messages shown in PocketMine itself need to be here= language.name=中文(简体) language.selected=设定 {%0} ({%1}) 为基本语言 multiplayer.player.joined={%0} 加入了游戏 multiplayer.player.left={%0} 离开了游戏 chat.type.text={%0} : {%1} chat.type.emote=* {%0} {%1} chat.type.announcement=[{%0}] {%1} chat.type.admin=[{%0}: {%1}] chat.type.achievement={%0} 刚刚获得了成就 {%1} disconnectionScreen.notAuthenticated=需要 Xbox 登录 disconnectionScreen.outdatedClient=客户端版本过旧! disconnectionScreen.outdatedServer=服务器版本过旧! disconnectionScreen.serverFull=服务器人数已满! disconnectionScreen.noReason=与服务器联机中断 disconnectionScreen.invalidSkin=无效的皮肤! disconnectionScreen.invalidName=用户名不符合规范! death.fell.accident.generic={%0} 发动了信仰之跃! death.attack.inFire={%0} 在火焰中升天 death.attack.onFire={%0} 被烧死了 death.attack.lava={%0} 试图在岩浆里游泳 death.attack.inWall={%0} 在墙内窒息而死 death.attack.drown={%0} 淹死了 death.attack.cactus={%0} 被仙人掌刺死了 death.attack.generic={%0} 无疾而终 death.attack.explosion={%0} 被炸飞了 death.attack.explosion.player={%0} 被 {%1} 炸死了 death.attack.magic={%0} 被魔法杀死了 death.attack.wither={%0} 凋零至死了 death.attack.mob={%0} 被 {%1} 杀死了 death.attack.player={%0} 被 {%1} 杀死了 death.attack.player.item={%0} 被 {%1} 用 {%2} 杀死了 death.attack.arrow={%0} 被 {%1} 射杀了 death.attack.arrow.item={%0} 被 {%1} 用 {%2} 射杀了 death.attack.fall={%0} 发动了信仰之跃! death.attack.outOfWorld={%0} 掉出了这个世界 gameMode.survival=生存模式 gameMode.creative=创造模式 gameMode.adventure=冒险模式 gameMode.spectator=旁观者模式 gameMode.changed=您的游戏模式已更新 potion.moveSpeed=速度 potion.moveSlowdown=缓慢 potion.digSpeed=急迫 potion.digSlowDown=挖掘疲劳 potion.damageBoost=力量 potion.heal=瞬间治疗 potion.harm=瞬间伤害 potion.jump=跳跃提升 potion.confusion=反胃 potion.regeneration=生命恢复 potion.resistance=抗性提升 potion.fireResistance=防火 potion.waterBreathing=水下呼吸 potion.invisibility=隐身 potion.blindness=失明 potion.nightVision=夜视 potion.hunger=饥饿 potion.weakness=虚弱 potion.poison=中毒 potion.wither=凋零 potion.healthBoost=生命提升 potion.absorption=伤害吸收 potion.saturation=饱和 commands.generic.exception=出现问题,请通知管理员修复。 commands.generic.permission=您没有权限使用此指令 commands.generic.notFound=未知的指令。请尝试用 /help 来显示指令列表。 commands.generic.player.notFound=找不到该玩家 commands.generic.usage=用法:{%0} commands.generic.level=地图名 commands.generic.seed=种子 commands.generic.name=名称 commands.generic.generator=生成器名称 commands.generic.opt.missing=指令缺少参数,请确认后重新输入。 commands.generic.runingame=请在游戏中使用该命令。 commands.time.added=时间增加了 {%0} commands.time.set=时间设定为 {%0} commands.time.query=现在时间是 {%0} commands.me.usage=/me commands.give.item.notFound=ID为 {%0} 的物品并不存在 commands.give.success=将 {%0} * {%1} 给 {%2} commands.give.tagError=数据格式不正确: {%0} commands.effect.usage=/effect <玩家名称> <效果> [秒数] [倍数] [隐藏粒子] 或 /effect <玩家名称> clear commands.effect.notFound=然而ID为 {%0} 的特殊效果并不存在 commands.effect.success=对 {%3} 加上了 {%4} 秒的 {%0} (ID {%1}) * {%2} commands.effect.success.removed=从 {%1} 身上移除了 {%0} commands.effect.success.removed.all=已解除 {%0} 身上所有特殊状态 commands.effect.failure.notActive=无法从 {%1} 身上移除 {%0},因为其身上无此效果 commands.effect.failure.notActive.all=无法移除效果,因为 {%0} 身上没有任何效果 commands.enchant.noItem=目标没有手持一样物品 commands.enchant.notFound=没有一个附魔ID为 {%0} commands.enchant.success=附魔完成 commands.enchant.cantEnchant=这件物品不能被附魔! commands.enchant.usage=/enchant <玩家名称> <附魔ID> [物品等级] commands.particle.success=正在应用 {%0} 效果 {%1} 次 commands.particle.notFound=未知的效果名称 {%0} commands.players.usage=/list commands.players.list=There are {%0}/{%1} players online: commands.kill.successful=已删除 {%0} commands.banlist.ips=共有 {%0} 个被封锁的 IP 地址: commands.banlist.players=共有 {%0} 个被封锁的玩家: commands.banlist.cids=共有 {%0} 禁止的 CIDs: commands.banlist.usage=/banlist [ips|players|cids] commands.defaultgamemode.usage=/defaultgamemode <模式> commands.defaultgamemode.success=服务器的默认游戏模式为 {%0} commands.op.success={%0} 获得管理员 commands.op.usage=/op <玩家名称> commands.deop.success={%0} 移除管理员 commands.deop.usage=/deop <玩家名称> commands.say.usage=/say <讯息...> commands.seed.usage=/seed commands.seed.success=种子码:{%0} commands.bancidbyname.success=封锁玩家 {%0} 的CID commands.bancidbyname.usage=/bancidbyname commands.bancid.success=封锁CID: {%0} commands.bancid.usage=/bancid commands.unbancid.usage=/pardoncid commands.ban.success=封锁玩家 {%0} commands.ban.usage=/ban <玩家名称> [原因...] [时间(天)] commands.unban.success=解锁玩家 {%0} commands.unban.usage=/pardon <玩家名称> commands.banip.invalid=您输入了一个无效的 IP 地址、玩家名称或不在在线 commands.banip.success=封锁 IP 地址 {%0} 。 commands.banip.success.players=封锁 IP 地址 {%0} 来自 {%1} commands.banip.usage=/ban-ip [原因 ...] commands.unbanip.invalid=您输入了一个无效的 IP 地址 commands.unbanip.success=解除封锁 IP 地址 {%0} commands.unbanip.usage=/pardon-ip commands.banipbyname.success=封锁玩家 {%0} 的IP commands.banipbyname.usage=/banipbyname commands.save.usage=/save-all commands.save-on.usage=/save-on commands.save-off.usage=/save-off commands.save.enabled=开启地图自动存储功能 commands.save.disabled=关闭地图自动存储功能 commands.save.start=正在储存... commands.save.success=世界储存完毕。 command.setblock.usage=/setblock <方块名称> [方块损害值] command.setblock.invalidBlock=无效的方块名称/ID commands.stop.usage=/stop commands.stop.start=服务器停止中 commands.kick.success={%0} 从游戏中被踢出 commands.kick.success.reason={%0} 从游戏中被踢出:{%1} commands.kick.usage=/kick <玩家名称> [原因...] commands.tp.success=传送 {%0} 至 {%1} commands.tp.success.coordinates=传送 {%0} 至 {%1},{%2},{%3} commands.tp.usage=/tp [玩家名称] <目标玩家> 或是 /tp [玩家名称] [ ] commands.whitelist.list=有 {%0} 人(全部 {%1 人) 为白名单玩家: commands.whitelist.enabled=开启白名单 commands.whitelist.disabled=关闭白名单 commands.whitelist.reloaded=重置白名单 commands.whitelist.add.success=新增 {%0} 至白名单 commands.whitelist.add.usage=/whitelist add <玩家名称> commands.whitelist.remove.success=从白名单删除 {%0} commands.whitelist.remove.usage=/whitelist remove <玩家名称> commands.whitelist.usage=/whitelist commands.gamemode.success.self=设定自己的游戏模式为 {%3} commands.gamemode.success.other=设定 {%1} 的游戏模式为 {%2} commands.gamemode.usage=/gamemode <模式> [玩家名称] commands.help.header=--- 查看帮助列表第 {%0} 页共 {%1} 页 (/help ) --- commands.help.usage=/help [页数|指令名称] commands.message.usage=/tell <玩家名称> <讯息...> commands.message.sameTarget=您不能传送讯息给自己! commands.xp.usage=/xp <经验值或等级+L> <玩家名称> commands.difficulty.usage=/difficulty <难度> commands.difficulty.success=设定游戏难度为 {%0} commands.spawnpoint.usage=/spawnpoint [玩家名称] [ ] commands.spawnpoint.success=设定 {%0} 的重生点为 ({%1},{%2},{%3}) commands.setworldspawn.usage=/setworldspawn [ ] commands.setworldspawn.success=设定世界重生点为 ({%0},{%1},{%2}) commands.summon.usage=/summon [实体名] [ ] [数据标签] # -------------------- PocketMine language files, only for console -------------------- pocketmine.data.playerNotFound=无法找到玩家数据 "{%0}",创建新的配置文件 pocketmine.data.playerCorrupted=发现损坏的数据 "{%0}",创建新的配置文件 pocketmine.data.playerOld=发现旧的玩家数据 "{%0}",更新配置文件 pocketmine.data.saveError=无法储存 "{%0}" 的玩家数据:{%1} pocketmine.level.notFound=无法找到 "{%0}" 地图 pocketmine.level.loadError=无法读取地图 "{%0}":{%1} pocketmine.level.generationError=无法产生地图 "{%0}":{%1} pocketmine.level.tickError=计算地图“{%0}”时出现错误∶{%1} pocketmine.level.chunkUnloadError=移除一个区块时发生错误:{%0} pocketmine.level.backgroundGeneration=正在于背景生成世界 “{%0}“ 的地形 pocketmine.level.defaultError=没有读取预设的地图 pocketmine.level.preparing=准备地图中... "{%0}" pocketmine.level.unloading=正在移除地图 "{%0}" pocketmine.server.start=正在启动支持 Minecraft:PE {%0} 版本的服务器 pocketmine.server.networkError=[网络] 停止接口 {%0} 由于 {%1} pocketmine.server.networkStart=正在启动服务器在 {%0}:{%1} pocketmine.server.info=此服务器正在运作 {%0} {%1} 版本 "{%2}" (API {%3}) pocketmine.server.info.extended=此服务器正在运作 {%0} {%1} “{%2}” 执行 API 版本 {%3} 支持 Minecraft:PE {%4} (协议版本 {%5}) pocketmine.server.info.extended1=本服务器正运行 {%0}{%1} (代号 "{%2}") pocketmine.server.info.extended2=PHP 版本: {%0} pocketmine.server.info.extended3=API: {%0} (iTX API 版本 {%1}) pocketmine.server.info.extended4=客户端: Minecraft PE {%0} (protocol 版本 pocketmine.server.license={%0} 根据 LGPL 许可发布 pocketmine.server.tickOverload=注意!服务器有超载的可能 pocketmine.server.startFinished=Done ({%0}s)!如需帮助,请输入 "help" 或 "?" pocketmine.server.defaultGameMode=预设的游戏类型:{%0} pocketmine.server.query.start=启动 GS4 状态监听器 pocketmine.server.query.info=设定 query 接口到 {%0} pocketmine.server.query.running=Query 运作在 {%0}:{%1} pocketmine.command.alias.illegal=不能注册别名 {%0},因为它包含非法字符 pocketmine.command.alias.notFound=未能登记别称 {%0} ,因为它包含不存在的指令: {%1} pocketmine.command.exception=于 {%1} 执行指令 “{%0}“ 时,出现了未被处理的错误: {%2} pocketmine.command.plugins.description=获取在服务器上运行的插件列表 pocketmine.command.plugins.success=插件 ({%0}):{%1} pocketmine.command.plugins.usage=/plugins pocketmine.command.reload.description=重新读取服务器设定和插件 pocketmine.command.reload.usage=/reload pocketmine.command.reload.reloading=重新读取服务器... pocketmine.command.reload.reloaded=重新读取完成 pocketmine.command.status.description=重新读取服务器的性能。 pocketmine.command.status.usage=/status pocketmine.command.status.title=服务器状态 pocketmine.command.status.player=服务器人数: pocketmine.command.status.days=天 pocketmine.command.status.hours=小时 pocketmine.command.status.minutes=分 pocketmine.command.status.seconds=秒 pocketmine.command.status.uptime=运行时间: pocketmine.command.status.AverageTPS=平均TPS: pocketmine.command.status.CurrentTPS=瞬时TPS: pocketmine.command.status.Networkupload=网络上传: pocketmine.command.status.Networkdownload=网络下载: pocketmine.command.status.Threadcount=线程总数: pocketmine.command.status.Mainmemory=线程总数: pocketmine.command.status.Totalmemory=总内存: pocketmine.command.status.Totalvirtualmemory=总虚拟内存: pocketmine.command.status.Heapmemory=堆栈内存: pocketmine.command.status.Maxmemorysystem=系统最大内存: pocketmine.command.status.Maxmemorymanager=核心全局最大内存: pocketmine.command.status.World=世界 pocketmine.command.status.chunks=区块, pocketmine.command.status.entities=实体, pocketmine.command.status.tiles=tiles. pocketmine.command.status.Time=时间 pocketmine.command.status.ms=毫秒 pocketmine.command.gc.description=回收垃圾 pocketmine.command.gc.usage=/gc pocketmine.command.gc.title=垃圾回收结果 pocketmine.command.gc.chunks=区块: pocketmine.command.gc.entities=实体: pocketmine.command.gc.tiles=方块: pocketmine.command.gc.cycles=循环: pocketmine.command.gc.memory=内存释放: pocketmine.command.timings.description=纪录计时数据,以检视服务器的性能。 pocketmine.command.timings.usage=/timings pocketmine.command.timings.enable=启用定时和重启 pocketmine.command.timings.disable=停用定时 pocketmine.command.timings.timingsDisabled=启用定时工具透过 /timings on pocketmine.command.timings.reset=定时重启 pocketmine.command.timings.pasteError=已记录在事件记录文件中 pocketmine.command.timings.timingsUpload=计时数据已被上载至 {%0} pocketmine.command.timings.timingsRead=你可以在 {%0} 阅读计时结果 pocketmine.command.timings.timingsWrite=计时数据已被储存至 {%0} pocketmine.command.version.description=检视此服务器 (及其使用的插件) 的版本 pocketmine.command.version.usage=/version [插件名称] pocketmine.command.version.noSuchPlugin=该服务器没有运行任何叫这个名称的插件。使用 /plugins 来获得插件列表。 pocketmine.command.give.description=给指定玩家一定数量的物品 pocketmine.command.give.usage=/give <玩家名称> <项目[:损毁程度]> [数量] pocketmine.command.kill.description=自杀或杀死其他玩家 pocketmine.command.kill.usage=/kill [玩家名称] pocketmine.command.particle.description=加入粒子效果至世界 pocketmine.command.particle.usage=/particle <玩家名称> [数量] [数据值] pocketmine.command.time.description=更改每个世界的时间 pocketmine.command.time.usage=/time <数值> 或 /time pocketmine.command.bancidbyname.description=禁止指定玩家的设备 ID pocketmine.command.bancid.description=禁止指定的设备 ID pocketmine.command.banipbyname.description=禁止指定玩家的 IP pocketmine.command.ban.player.description=禁止指定的玩家使用此服务器 pocketmine.command.ban.ip.description=禁止指定的 IP 地址使用此服务器 pocketmine.command.banlist.description=查看来自该服务器禁止的所有玩家 pocketmine.command.defaultgamemode.description=设定默认的游戏模式 pocketmine.command.deop.description=移除指定玩家的管理员权限 pocketmine.command.difficulty.description=设定游戏的难易度 pocketmine.command.enchant.description=把物件附魔 pocketmine.command.effect.description=增加/减少玩家身上的效果 pocketmine.command.gamemode.description=改变玩家到一个特定的游戏模式 pocketmine.command.help.description=显示帮助列表 pocketmine.command.kick.description=从服务器中删除指定玩家 pocketmine.command.list.description=显示在线玩家列表 pocketmine.command.me.description=于聊天中作出指定的动作 pocketmine.command.op.description=赋予指定玩家管理员权限 pocketmine.command.unban.cid.description=允许指定 CID 使用此服务器 pocketmine.command.unban.player.description=允许指定玩家使用此服务器 pocketmine.command.unban.ip.description=允许指定 IP 地址使用此服务器 pocketmine.command.save.description=储存服务器到磁盘上 pocketmine.command.saveoff.description=停用自动储存服务器 pocketmine.command.saveon.description=启用自动储存服务器 pocketmine.command.say.description=以发送指令者身份广播指定的讯息 pocketmine.command.seed.description=显示世界种子码 pocketmine.command.setworldspawn.description=设定一个世界重生点。未指定坐标,将使用玩家的坐标。 pocketmine.command.spawnpoint.description=设定玩家重生点 pocketmine.command.stop.description=关闭服务器 pocketmine.command.tp.description=传送指定玩家(或是自己)到另一位玩家或坐标 pocketmine.command.tell.description=传送私讯给指定玩家 pocketmine.command.xp.description=给指定玩家增加经验值或等级 pocketmine.command.summon.description=召唤指定的实体于玩家位置或指定位置 ocketmine.command.fill.description=填充了指定方块 pocketmine.command.setblock.description=将一个方块更改为另一个方块 pocketmine.command.weather.description=设置指定地图的天气 pocketmine.command.weather.usage=/weather <世界名 天气值|天气值> pocketmine.command.weather.changed=成功设置世界{%0}的天气! pocketmine.command.weather.noregistered=世界{%0}没有注册到天气管理器! pocketmine.command.weather.invalid=无效的天气!请输入天气类型 0,1,2,3 pocketmine.command.weather.wrong=缺少参数! pocketmine.command.weather.invalid.level=错误的地图名 pocketmine.command.whitelist.description=管理员允许使用此服务器的玩家列表 pocketmine.crash.create=一个未知的错误发生了,使服务器崩溃。正在储存错误报告。 pocketmine.crash.error=未能储存错误报告∶{%0} pocketmine.crash.submit=请上载档案“{%0}”至在线崩溃储存库。请尽量提供更多数据。 pocketmine.crash.archive=错误报告已经上传到在线崩溃储存库。你可以在{%0} 查看到它或使用ID #{%1}。 pocketmine.debug.enable=启用 LevelDB 支援 pocketmine.player.invalidMove={%0} 行动可疑! pocketmine.player.logIn={%0}[/{%1}:{%2}] [ClientID: {%3}] logged in with entity id {%4} at ({%5}, {%6}, {%7}, {%8}) pocketmine.player.logOut={%0}[/{%1}:{%2}] logged out due to {%3} pocketmine.player.transferred={%0}[/{%1}:{%2}] 被传送到了 {%3} pocketmine.player.invalidEntity={%0} 尝试攻击一个无效的实体 pocketmine.plugin.load=读取中... {%0} pocketmine.plugin.enable=开启中... {%0} pocketmine.plugin.disable=关闭中... {%0} pocketmine.plugin.restrictedName=受限的名称 pocketmine.plugin.incompatibleAPI=不兼容的API版本 pocketmine.plugin.unknownDependency=本插件无法单独使用 pocketmine.plugin.circularDependency=检测出循环依赖 pocketmine.plugin.genericLoadError=无法读取插件 '{%0}' pocketmine.plugin.spacesDiscouraged=插件 '{%0}' 在名称中使用了空格,不建议这样做 pocketmine.plugin.loadError=无法读取插件 '{%0}':{%1} pocketmine.plugin.duplicateError=无法读取插件 '{%0}':已有相同插件 pocketmine.plugin.fileError=无法读取在 '{%1}' 文件夹中的 '{%0}':{%2} pocketmine.plugin.commandError=无法读取 {%1} 插件的 {%0} 指令 pocketmine.plugin.aliasError=无法读取 {%1} 插件的 {%0} 别名 pocketmine.plugin.deprecatedEvent=插件 '{%0}' 已经使用 '{%2}' 方法注册了一个在 '{%1}' 的监听器,但是该事件已过时。 pocketmine.plugin.eventError="无法处理事件 '{%0}' 至 '{%1}':{%2} 在 {%3} 上" ================================================ FILE: src/pocketmine/lang/locale/deu.ini ================================================ # Language file compatible with Minecraft: Pocket Edition identifiers # # A message doesn't need to be there to be shown correctly on the client. # Only messages shown in PocketMine itself need to be here language.name=Deutsch language.selected={%0} ({%1}) als Basissprache ausgewählt multiplayer.player.joined={%0} hat das Spiel betreten multiplayer.player.left={%0} hat das Spiel verlassen chat.type.text={%0} : {%1} chat.type.emote=* {%0} {%1} chat.type.announcement=[{%0}] {%1} chat.type.admin=[{%0}: {%1}] chat.type.achievement={%0} hat gerade den Erfolg {%1} erzielt disconnectionScreen.notAuthenticated=Xbox Anmeldung notwendig disconnectionScreen.outdatedClient=Veralteter Client! disconnectionScreen.outdatedServer=Veralteter Server! disconnectionScreen.serverFull=Server ist voll! disconnectionScreen.noReason=Verbindung zum Server getrennt disconnectionScreen.invalidSkin=Ungültiger Skin! disconnectionScreen.invalidName=Ungültiger Name! death.fell.accident.generic={%0} fiel aus zu großer Höhe death.attack.inFire={%0} ging in Flammen auf death.attack.onFire={%0} verbrannte death.attack.lava={%0} versuchte in Lava zu schwimmen death.attack.inWall={%0} wurde lebendig begraben death.attack.drown={%0} ertrank death.attack.cactus={%0} wurde zu Tode gestochen death.attack.generic={%0} starb death.attack.explosion={%0} wurde in die Luft gesprengt death.attack.explosion.player={%0} wurde durch {%1} in die Luft gesprengt death.attack.magic={%0} wurde durch Magie getötet death.attack.wither={%0} verdorrte death.attack.mob={%0} wurde von {%1} erschlagen death.attack.player={%0} wurde von {%1} erschlagen death.attack.player.item={%0} wurde von {%1} mit {%2} erschlagen death.attack.arrow={%0} wurde von {%1} erschossen death.attack.arrow.item={%0} wurde von {%1} mit {%2} erschossen death.attack.fall={%0} fiel der Schwerkraft zum Opfer death.attack.outOfWorld={%0} fiel aus der Welt gameMode.survival=Überlebensmodus gameMode.creative=Kreativmodus gameMode.adventure=Abenteuermodus gameMode.spectator=Zuschauermodus gameMode.changed=Dein Spielmodus wurde aktualisiert potion.moveSpeed=Schnelligkeit potion.moveSlowdown=Langsamkeit potion.digSpeed=Eile potion.digSlowDown=Langsames Abbauen potion.damageBoost=Stärke potion.heal=Direktheilung potion.harm=Direktschaden potion.jump=Sprungkraft potion.confusion=Übelkeit potion.regeneration=Regeneration potion.resistance=Resistenz potion.fireResistance=Feuerresistenz potion.waterBreathing=Unterwasser-Atmung potion.invisibility=Unsichtbarkeit potion.blindness=Blindheit potion.nightVision=Nachtsicht potion.hunger=Hunger potion.weakness=Schwäche potion.poison=Gift potion.wither=Dürre potion.healthBoost=Lebenserweiterung potion.absorption=Absorption potion.saturation=Sättigung commands.generic.exception=Ein unbekannter Fehler trat auf, während versucht wurde, diesen Befehl auszufüren commands.generic.permission=Du hast keine Berechtigung, diesen Befehl auszuführen commands.generic.notFound=Unbekannter Befehl. Versuche /help für eine Liste von Befehlen commands.generic.player.notFound=Dieser Spieler kann nicht gefunden werden commands.generic.usage=Aufruf: {%0} commands.generic.level=Weltname commands.generic.seed=Startwert commands.generic.name=Name commands.generic.generator=Generator commands.generic.opt.missing=Notwendige Eigenschaft fehlt, bitte überprüfen und neu eingeben. commands.generic.runingame=Dieser Befehl kann nur im Spiel ausgeführt werden. commands.time.added={%0} zur Zeit hinzugefügt commands.time.set=Setzte die Zeit auf {%0} commands.time.query=Zeit: {%0} commands.me.usage=/me commands.give.item.notFound=Es gibt kein Item mit dem Namen {%0} commands.give.success={%0} * {%1} an {%2} gegeben commands.give.tagError=Parsen des Datatags fehlgeschlagen: {%0} commands.effect.usage=/effect [Sekunden] [Verstärker] [versteckePartikel] ODER /effect clear commands.effect.notFound=Es gibt keinen Mob-Effekt mit der ID {%0} commands.effect.success={%0} (ID {%1}) * {%2} für {%4} Sekunden an {%3} gegeben commands.effect.success.removed={%0} von {%1} genommen commands.effect.success.removed.all=Alle Effekte von {%0} genommen commands.effect.failure.notActive=Konnte {%0} nicht von {%1} nehmen, da der Effekt nicht vorhanden war commands.effect.failure.notActive.all=Konnte keinen Effekt von {%0} nehmen, da kein Effekt vorhanden war commands.enchant.noItem=Dieser Spieler hält keinen Gegenstand commands.enchant.notFound=Es gibt keine Verzauberung mit der ID {%0} commands.enchant.success=Verzauberung erfolgreich commands.enchant.cantEnchant=Die gewählte Verzauberung kann diesem Gegenstand nicht hinzugefügt werden. commands.enchant.usage=/enchant [Level] commands.particle.success=Erzeuge {%1}-fach den Partikeleffekt {%0} commands.particle.notFound=Unbekannter Partikeleffekt {%0} commands.players.usage=/list commands.players.list=Es sind {%0}/{%1} Spieler online: commands.kill.successful={%0} wurde getötet commands.banlist.ips=Es sind insgesamt {%0} IP-Adressen gebannt: commands.banlist.players=Es sind insgesamt {%0} Spieler gebannt: commands.banlist.cids=Es sind insgesamt {%0} CIDs gebannt: commands.banlist.usage=/banlist [ips|players|cids] commands.defaultgamemode.usage=/defaultgamemode commands.defaultgamemode.success=Der Standart-Spielmodus der Welt ist jetzt {%0} commands.op.success={%0} wurde zum Operator ernannt commands.op.usage=/op commands.deop.success={%0} ist jetzt kein Operator mehr commands.deop.usage=/deop commands.say.usage=/say commands.seed.usage=/seed commands.seed.success=Startwert: {%0} commands.bancidbyname.success=CID des Spielers {%0} wurde gebannt commands.bancidbyname.usage=/bancidbyname commands.bancid.success=CID {%0} wurde gebannt commands.bancid.usage=/bancid commands.unbancid.usage=/pardoncid commands.unbancid.success=CID {%0} wurde entbannt commands.ban.success=Spieler {%0} wurde gebannt commands.ban.usage=/ban [Grund ...] [Zeit(Tag)] commands.unban.success=Spieler {%0} wurde entbannt commands.unban.usage=/pardon commands.banip.invalid=Du hast eine ungültige IP-Adresse angegeben oder einen Spieler, der nicht online ist commands.banip.success=IP-Adresse {%0} wurde gebannt commands.banip.success.players=IP-Adresse {%0}, die zu {%1} gehört, wurde gebannt commands.banip.usage=/ban-ip [Grund ...] commands.unbanip.invalid=Du hast eine ungültige IP-Adresse angegeben commands.unbanip.success=IP-Adresse {%0} wurde entbannt commands.unbanip.usage=/pardon-ip commands.banipbyname.success=IP-Adresse des Spielers {%0} wurde gebannt commands.banipbyname.usage=/banipbyname commands.save.usage=/save-all commands.save-on.usage=/save-on commands.save-off.usage=/save-off commands.save.enabled=Automatische Welt-Speicherung aktiviert commands.save.disabled=Automatische Welt-Speicherung deaktiviert commands.save.start=Speichern... commands.save.success=Welt wurde gespeichert commands.setblock.usage=/setblock [Schaden] command.setblock.invalidBlock=Blockname/ID ist ungültig commands.stop.usage=/stop commands.stop.start=Server wird beendet commands.kick.success={%0} wurde aus dem Spiel geworfen commands.kick.success.reason={%0} wurde aus dem Spielgeworfen: '{%1}' commands.kick.usage=/kick [Grund ...] commands.tp.success={%0} wurde zu {%1} teleportiert commands.tp.success.coordinates={%0} wurde zu {%1}, {%2}, {%3} teleportiert commands.tp.usage=/tp [Spieler] ODER /tp [Spieler] [ ] commands.whitelist.list=Es sind insgesamt {%0} (von {%1} hier gewesenen) Spieler in der Whitelist: commands.whitelist.enabled=Whitelist aktiviert commands.whitelist.disabled=Whitelist deaktiviert commands.whitelist.reloaded=Whitelist neu eingelesen commands.whitelist.add.success={%0} zur Whitelist hinzugefügt commands.whitelist.add.usage=/whitelist add commands.whitelist.remove.success={%0} von der Whitelist entfernt commands.whitelist.remove.usage=/whitelist remove commands.whitelist.usage=/whitelist commands.gamemode.success.self=Eigenen Spielmodus auf {%2} gesetzt commands.gamemode.success.other={%1}'s Spielmodus auf {%2} gesetzt commands.gamemode.usage=/gamemode [Spieler] commands.help.header=--- Zeige Hilfe-Seite {%0} von {%1} (/help ) --- commands.help.usage=/help [Seite|Befehl] commands.message.usage=/tell commands.message.sameTarget=Du kannst keine private Nachricht an dich selbst schicken! commands.difficulty.usage=/difficulty commands.difficulty.success=Schwierigkeitsgrad auf {%0} gesetzt commands.spawnpoint.usage=/spawnpoint [Spieler] [ ] commands.spawnpoint.success={%0}'s Spawnpunkt wurde auf ({%1}, {%2}, {%3}) gesetzt commands.setworldspawn.usage=/setworldspawn [ ] commands.setworldspawn.success=Der Welt-Spawn wurde auf ({%0}, {%1}, {%2}) gesetzt commands.summon.usage=/summon [Entity] [ ] [NBTTag] commands.xp.failure.withdrawXp=Einem Spieler können keine negativen Erfahrungspunkte gegeben werden commands.xp.success={%0} Erfahrungspunkte an Spieler {%1} gegeben commands.xp.success.levels={%0} Level Erfahrung an {%1} gegeben commands.xp.success.negative.levels={%0} Level Erfahrung von {%1} genommen commands.xp.usage=/xp [Spieler] ODER /xp L [Spieler] # -------------------- PocketMine language files, only for console -------------------- pocketmine.data.playerNotFound=Keine Spielerdaten für "{%0}" gefunden, erstelle neues Profil pocketmine.data.playerCorrupted=Defekte Daten für Spieler "{%0}" gefunden, erstelle neues Profil pocketmine.data.playerOld=Alte Spielerdaten für "{%0}" gefunden, aktualisiere Profil pocketmine.data.saveError=Konnte Spieler "{%0}" nicht speichern: {%1} pocketmine.level.notFound=Welt "{%0}" nicht gefunden pocketmine.level.loadError=Welt "{%0}" konnte nicht geladen werden: {%1} pocketmine.level.generationError=Welt "{%0}" konnte nicht generiert werden: {%1} pocketmine.level.tickError=Welt "{%0}" konnte nicht getickt werden: {%1} pocketmine.level.chunkUnloadError=Fehler beim Entladen eines Chunks: {%0} pocketmine.level.backgroundGeneration=Spawn-Landschaft wird für die Welt "{%0}" im Hintergrund generiert pocketmine.level.defaultError=Es wurde keine Standard-Welt geladen pocketmine.level.preparing=Welt "{%0}" wird vorbereitet pocketmine.level.unloading=Welt "{%0}" wird entladen pocketmine.server.start=Starte Minecraft: PE server version {%0} pocketmine.server.networkError=[Netzwerk] Schnittstelle {%0} wurde wegen {%1} beendet pocketmine.server.networkStart=Öffne Server auf {%0}:{%1} pocketmine.server.info=Dieser Server läuft mit Version {%0}{%1} "{%2}" (API {%3}) pocketmine.server.info.extended.title=-----Server Information----- pocketmine.server.info.extended1=Dieser Server läuft mit Version {%0}{%1} (Codename "{%2}") pocketmine.server.info.extended2=PHP Version: {%0} pocketmine.server.info.extended3=API: {%0} (iTX API Version {%1}) pocketmine.server.info.extended4=Zielclient: Minecraft PE {%0} (Protokollversion {%1}) pocketmine.server.license={%0} wurde unter der GPL Lizenz Version 3 und neuer herausgegeben pocketmine.server.tickOverload=Server kann nicht mehr am Laufen gehalten werden! Ist der Server überladen? pocketmine.server.startFinished=Fertig ({%0}s)! Für Hilfe kannst du "help" oder "?" eingeben pocketmine.server.defaultGameMode=Standard-Spielmodus: {%0} pocketmine.server.query.start=Starte GS4 Status-Listener pocketmine.server.query.info=Setze Query-Port auf {%0} pocketmine.server.query.running=Query läuft auf {%0}:{%1} pocketmine.command.alias.illegal=Alias {%0} konnte nicht registriert werden, da er unzulässige Zeichen enthält pocketmine.command.alias.notFound=Alias {%0} konnte nicht registriert werden, da er Befehle enthält, die nicht existieren: {%1} pocketmine.command.exception=Unbehandelte Ausnahme bei Ausführung des Befehls '{%0}' in {%1}: {%2} pocketmine.command.plugins.description=Zeigt eine Liste aller auf dem Server laufenden Plugins pocketmine.command.plugins.success=Plugins ({%0}): {%1} pocketmine.command.plugins.usage=/plugins pocketmine.command.reload.description=Lädt die Serverkonfiguration und Plugins neu ein pocketmine.command.reload.usage=/reload pocketmine.command.reload.reloading=Server wird neu geladen... pocketmine.command.reload.reloaded=Neuladen des Servers abgeschlossen pocketmine.command.status.description=Zeigt die aktuelle Serverauslastung an. pocketmine.command.status.usage=/status pocketmine.command.status.title=Server-Status pocketmine.command.status.player=Spieleranzahl: pocketmine.command.status.days=Tage pocketmine.command.status.hours=Stunden pocketmine.command.status.minutes=Minuten pocketmine.command.status.seconds=Sekunden pocketmine.command.status.uptime=Laufzeit: pocketmine.command.status.AverageTPS=Durchschnittliche TPS: pocketmine.command.status.CurrentTPS=Aktuelle TPS: pocketmine.command.status.Networkupload=Netzwerk Upload: pocketmine.command.status.Networkdownload=Netzwerk Download: pocketmine.command.status.Threadcount=Anzahl der Threads: pocketmine.command.status.Mainmemory=Speicher des Main-Threads: pocketmine.command.status.Totalmemory=Speicher gesamt: pocketmine.command.status.Totalvirtualmemory=Virtueller Speicher gesamt: pocketmine.command.status.Heapmemory=Heap-Speicher: pocketmine.command.status.Maxmemorysystem=Maximaler Speicher (System): pocketmine.command.status.Maxmemorymanager=Maximaler Speicher (Manager): pocketmine.command.status.World=Welt pocketmine.command.status.chunks=Chunks, pocketmine.command.status.entities=Entities, pocketmine.command.status.tiles=Tiles. pocketmine.command.status.Time=Zeit pocketmine.command.status.ms=ms pocketmine.command.gc.description=Startet die Tasks zur automatischen Speicherbereinigung (garbage collection) pocketmine.command.gc.usage=/gc pocketmine.command.gc.title=Sammelbericht pocketmine.command.gc.chunks=Chunks: pocketmine.command.gc.entities=Entities: pocketmine.command.gc.tiles=Tiles: pocketmine.command.gc.cycles=Zyklen: pocketmine.command.gc.memory=Freigegebener Speicher: pocketmine.command.timings.description=Nimmt Zeitmessungen auf, um die Severauslastung zu ermitteln. pocketmine.command.timings.usage=/timings pocketmine.command.timings.enable=Timings & Reset aktiviert pocketmine.command.timings.disable=Timings deaktiviert pocketmine.command.timings.timingsDisabled=Bitte aktiviere die Timings, indem du '/timings on' eingibst pocketmine.command.timings.reset=Timings zurückgesetzt pocketmine.command.timings.pasteError=Während der Übernahme des Berichts trat ein Fehler auf pocketmine.command.timings.timingsUpload=Timings wurden hochgeladen zu {%0} pocketmine.command.timings.timingsRead=Du kannst dir die Ergebnisse hier anschauen {%0} pocketmine.command.timings.timingsWrite=Timings wurden gespeichert in {%0} pocketmine.command.version.description=Ermittelt die Version des Servers inklusive jedem verwendeten Plugin. pocketmine.command.version.usage=/version [Plugin-Name] pocketmine.command.version.noSuchPlugin=Auf diesem Server läuft kein Plugin mit diesem Namen. Mit /plugins kannst du dir eine Liste aller Plugins anzeigen lassen. pocketmine.command.give.description=Gibt dem angegebenen Spieler eine bestimmte Anzahl an Gegenständen (Items) pocketmine.command.give.usage=/give [Anzahl] [NBT-Daten...] pocketmine.command.kill.description=Tötet dich selbst oder andere Spieler pocketmine.command.kill.usage=/kill [Spieler] pocketmine.command.particle.description=Fügt einer Welt Partikel hinzu pocketmine.command.particle.usage=/particle [Anzahl] [Wert] pocketmine.command.time.description=Ändert in allen Welten die Zeit pocketmine.command.time.usage=/time ODER /time pocketmine.command.bancidbyname.description=Verhindet, dass sich die durch den Spielernamen ermittelte CID mit dem Server verbinden kann pocketmine.command.bancid.description=Verhindert, dass sich die angegebene CID mit dem Server verbinden kann pocketmine.command.banipbyname.description=Verhindet, dass sich die durch den Spielernamen ermittelte IP-Adresse mit dem Server verbinden kann pocketmine.command.ban.player.description=Verhindert, dass sich der angegebene Spieler mit dem Server verbinden kann pocketmine.command.ban.ip.description=Verhindert, dass sich die angegebene IP-Adresse mit dem Server verbinden kann pocketmine.command.banlist.description=Zeigt alle Spieler an, die von diesem Server gebannt wurden pocketmine.command.defaultgamemode.description=Legt den Standard-Spielmodus fest pocketmine.command.deop.description=Nimmt dem angegebenen Spieler den Operator-Status pocketmine.command.difficulty.description=Legt den Schwierigkeitsgrad des Spiels fest pocketmine.command.enchant.description=Fügt Gegenständen Verzauberungen hinzu pocketmine.command.effect.description=Fügt Spielern Effekte hinzu oder entfernt sie pocketmine.command.gamemode.description=Ändert den Spiemodus eines Spielers wie angegeben pocketmine.command.help.description=Zeigt das Hilfe-Menü an pocketmine.command.kick.description=Trennt den angegebenen Spieler vom Server pocketmine.command.list.description=Zeigt alle Spieler an, die gerade online sind pocketmine.command.me.description=Stellt die angegebene Aktion im Chat dar pocketmine.command.op.description=Gibt dem angegebenen Spieler den Operator-Status pocketmine.command.unban.cid.description=Erlaubt der angegebenen CID, sich wieder mit dem Server zu verbinden pocketmine.command.unban.player.description=Erlaubt dem angegebenen Spieler, sich wieder mit dem Server zu verbinden pocketmine.command.unban.ip.description=Erlaubt der angegebenen IP-Adresse, sich wieder mit dem Server zu verbinden pocketmine.command.save.description=Speichert den Server im Dateisystem pocketmine.command.saveoff.description=Deaktiviert das automatische Speichern des Servers pocketmine.command.saveon.description=Aktiviert das automatische Speichern des Servers pocketmine.command.say.description=Sendet die eingegebene Nachricht im Namen des Absenders an alle Spieler pocketmine.command.seed.description=Zeigt den Startwert (seed) der Welt pocketmine.command.setworldspawn.description=Legt den Spawnpunkt einer Welt fest. Werden keine Koordinaten angegeben, werden die des Spielers verwendet. pocketmine.command.spawnpoint.description=Legt den Spawnpunkt eines Spielers fest. pocketmine.command.stop.description=Beendet den Server pocketmine.command.tp.description=Teleportiert den angegebenen Spieler (oder dich selbst) zu einem anderen Spieler oder Koordinaten pocketmine.command.tell.description=Sendet dem angegebenen Spieler eine private Nachricht pocketmine.command.xp.description=Fügt dem angegebenen Spieler Erfahrungspunkte oder -level hinzu pocketmine.command.summon.description=Läßt ein Wesen am Ort des Spielers oder einem angegebenen Ort erscheinen pocketmine.command.fill.description=Füllt einen bestimmten Auswahlbereich mit Blöcken pocketmine.command.setblock.description=Ersetzt einen Block durch einen anderen Block pocketmine.command.weather.description=Legt das Wetter für eine Welt fest pocketmine.command.weather.usage=/weather pocketmine.command.weather.changed=Das Wetter in der Welt {%0} wurde erfolgreich gewechselt! pocketmine.command.weather.noregistered=Die Welt {%0} wurde nicht beim WetterManager registriert. pocketmine.command.weather.invalid=Ungültiges Wetter.(0,1,2,3) pocketmine.command.weather.wrong=Falsche Parameter. pocketmine.command.weather.invalid.level=Ungültiger Weltname. pocketmine.command.whitelist.description=Verwaltet die Liste der Spieler, denen es erlaubt ist, den Server zu benutzen pocketmine.crash.create=Es ist ein nicht behebbarer Fehler aufgetreten und der Server ist abgestürzt. Erstelle einen Crash-Dump... pocketmine.crash.error=Crash-Dump konnte nicht erstellt werden: {%0} pocketmine.crash.submit=Bitte lade die Datei "{%0}" in das Crash-Archiv hoch und übermittel den Link auf der Bug-Reporting Seite. Gib so viele Informationen an wie du kannst. pocketmine.crash.archive=Der Crash-Dump wurde automatisch in das Crash-Archiv übertragen. Du kannst in unter {%0} einsehen oder die ID #{%1} verwenden. pocketmine.debug.enable=LevelDB Unterstützung aktiviert pocketmine.player.invalidMove={%0} bewegte sich falsch! pocketmine.player.logIn={%0}[/{%1}:{%2}] [ClientID: {%3}] loggte sich mit der Entity-ID {%4} bei ({%5}, {%6}, {%7}, {%8}) ein pocketmine.player.logOut={%0}[/{%1}:{%2}] loggte sich wegen {%3} aus pocketmine.player.transferred={%0}[/{%1}:{%2}] wurde transferiert zu {%3} pocketmine.player.invalidEntity={%0} versuchte, eine ungültige Entity zu attackieren pocketmine.plugin.load=Lade {%0} pocketmine.plugin.enable=Aktiviere {%0} pocketmine.plugin.disable=Deaktiviere {%0} pocketmine.plugin.restrictedName=Verbotener Name pocketmine.plugin.incompatibleAPI=Inkompatible API-Version pocketmine.plugin.unknownDependency=Unbekannte Abhängigkeit pocketmine.plugin.circularDependency=Zirkuläre Abhängigkeit erkannt pocketmine.plugin.genericLoadError=Plugin '{%0}' konnte nicht geladen werden pocketmine.plugin.spacesDiscouraged=Plugin '{%0}' verwendet Leerzeichen im Namen, was nicht erlaubt ist pocketmine.plugin.loadError=Plugin '{%0}' konnte nicht geladen werden: {%1} pocketmine.plugin.duplicateError=Plugin '{%0}' konnte nicht geladen werden: Plugin existiert bereits pocketmine.plugin.fileError=Konnte '{%0}' im Ordner '{%1}' nicht laden: {%2} pocketmine.plugin.commandError=Der Befehl {%0} konnte für das Plugin {%1} nicht geladen werden pocketmine.plugin.aliasError=Alias {%0} konnte für Plugin {%1} nicht geladen werden pocketmine.plugin.deprecatedEvent=Das Plugin '{%0}' hat einen Listener für '{%1}' auf die Methode '{%2}' registriert, aber das Event ist veraltet. pocketmine.plugin.eventError="Das Event '{%0}' konnte nicht an '{%1}' weitergegeben werden: {%2} in {%3}" ================================================ FILE: src/pocketmine/lang/locale/eng.ini ================================================ # Language file compatible with Minecraft: Pocket Edition identifiers # # A message doesn't need to be there to be shown correctly on the client. # Only messages shown in PocketMine itself need to be here language.name=English language.selected=Selected {%0} ({%1}) as the base language multiplayer.player.joined={%0} joined the game multiplayer.player.left={%0} left the game chat.type.text={%0} : {%1} chat.type.emote=* {%0} {%1} chat.type.announcement=[{%0}] {%1} chat.type.admin=[{%0}: {%1}] chat.type.achievement={%0} has just earned the achievement {%1} disconnectionScreen.notAuthenticated=Xbox login required disconnectionScreen.outdatedClient=Outdated client! disconnectionScreen.outdatedServer=Outdated server! disconnectionScreen.serverFull=Server is full! disconnectionScreen.noReason=Disconnected from server disconnectionScreen.invalidSkin=Invalid skin! disconnectionScreen.invalidName=Invalid name! death.fell.accident.generic={%0} fell from a high place death.attack.inFire={%0} went up in flames death.attack.onFire={%0} burned to death death.attack.lava={%0} tried to swim in lava death.attack.inWall={%0} suffocated in a wall death.attack.drown={%0} drowned death.attack.cactus={%0} was pricked to death death.attack.generic={%0} died death.attack.explosion={%0} blew up death.attack.explosion.player={%0} was blown up by {%1} death.attack.magic={%0} was killed by magic death.attack.wither={%0} withered away death.attack.mob={%0} was slain by {%1} death.attack.player={%0} was slain by {%1} death.attack.player.item={%0} was slain by {%1} using {%2} death.attack.arrow={%0} was shot by {%1} death.attack.arrow.item={%0} was shot by {%1} using {%2} death.attack.fall={%0} hit the ground too hard death.attack.outOfWorld={%0} fell out of the world gameMode.survival=Survival Mode gameMode.creative=Creative Mode gameMode.adventure=Adventure Mode gameMode.spectator=Spectator Mode gameMode.changed=Your game mode has been updated potion.moveSpeed=Speed potion.moveSlowdown=Slowness potion.digSpeed=Haste potion.digSlowDown=Mining Fatigue potion.damageBoost=Strength potion.heal=Instant Health potion.harm=Instant Damage potion.jump=Jump Boost potion.confusion=Nausea potion.regeneration=Regeneration potion.resistance=Resistance potion.fireResistance=Fire Resistance potion.waterBreathing=Water Breathing potion.invisibility=Invisibility potion.blindness=Blindness potion.nightVision=Night Vision potion.hunger=Hunger potion.weakness=Weakness potion.poison=Poison potion.wither=Wither potion.healthBoost=Health Boost potion.absorption=Absorption potion.saturation=Saturation commands.generic.exception=An unknown error occurred while attempting to perform this command commands.generic.permission=You do not have permission to use this command commands.generic.notFound=Unknown command. Try /help for a list of commands commands.generic.player.notFound=That player cannot be found commands.generic.usage=Usage: {%0} commands.generic.level=level-name commands.generic.seed=seed-name commands.generic.name=name commands.generic.generator=generator-name commands.generic.opt.missing=Missing required properties, please confirm and re-enter. commands.generic.runingame=Please run this command in-game. commands.time.added=Added {%0} to the time commands.time.set=Set the time to {%0} commands.time.query=Time is {%0} commands.give.item.notFound=There is no such item with name {%0} commands.give.success=Given {%0} * {%1} to {%2} commands.give.tagError=Data tag parsing failed: {%0} commands.effect.notFound=There is no such mob effect with ID {%0} commands.effect.success=Given {%0} (ID {%1}) * {%2} to {%3} for {%4} seconds commands.effect.success.removed=Took {%0} from {%1} commands.effect.success.removed.all=Took all effects from {%0} commands.effect.failure.notActive=Couldn't take {%0} from {%1} as they do not have the effect commands.effect.failure.notActive.all=Couldn't take any effects from {%0} as they do not have any commands.enchant.maxLevel=Level Range of that enchantment is 1 - {%0} commands.enchant.noItem=The target is not holding an item commands.enchant.notFound=There is no such enchantment with ID {%0} commands.enchant.success=Enchanting succeeded commands.enchant.cantEnchant=The selected enchantment can't be added to the target item commands.particle.success=Playing effect {%0} for {%1} times commands.particle.notFound=Unknown effect name {%0} commands.players.list=There are {%0}/{%1} players online: commands.kill.successful=Killed {%0} commands.banlist.ips=There are {%0} total banned IP addresses: commands.banlist.players=There are {%0} total banned players: commands.banlist.cids=There are {%0} total banned CIDs: commands.defaultgamemode.success=The world's default game mode is now {%0} commands.op.success=Opped {%0} commands.deop.success=De-opped {%0} commands.seed.success=Seed: {%0} commands.bancidbyname.success=Banned player {%0}'s CID commands.bancid.success=Banned CID {%0} commands.unbancid.success=Unbanned CID {%0} commands.ban.success=Banned player {%0} commands.unban.success=Unbanned player {%0} commands.banip.invalid=You have entered an invalid IP address or a player that is not online commands.banip.success=Banned IP address {%0} commands.banip.success.players=Banned IP address {%0} belonging to {%1} commands.unbanip.invalid=You have entered an invalid IP address commands.unbanip.success=Unbanned IP address {%0} commands.banipbyname.success=Banned player {%0}'s IP commands.save.enabled=Turned on world auto-saving commands.save.disabled=Turned off world auto-saving commands.save.start=Saving... commands.save.success=Saved the world command.setblock.invalidBlock=Invalid block name/ID commands.stop.start=Stopping the server commands.kick.success=Kicked {%0} from the game commands.kick.success.reason=Kicked {%0} from the game: '{%1}' commands.tp.success=Teleported {%0} to {%1} commands.tp.success.coordinates=Teleported {%0} to {%1}, {%2}, {%3} commands.whitelist.list=There are {%0} (out of {%1} seen) whitelisted players: commands.whitelist.enabled=Turned on the whitelist commands.whitelist.disabled=Turned off the whitelist commands.whitelist.reloaded=Reloaded the whitelist commands.whitelist.add.success=Added {%0} to the whitelist commands.whitelist.add.usage=/whitelist add commands.whitelist.remove.success=Removed {%0} from the whitelist commands.whitelist.remove.usage=/whitelist remove commands.gamemode.success.self=Set own game mode to {%2} commands.gamemode.success.other=Set {%1}'s game mode to {%2} commands.gamemode.usage=/gamemode [player] commands.help.header=--- Showing help page {%0} of {%1} (/help ) --- commands.message.sameTarget=You can't send a private message to yourself! commands.difficulty.success=Set game difficulty to {%0} commands.spawnpoint.success=Set {%0}'s spawn point to ({%1}, {%2}, {%3}) commands.setworldspawn.success=Set the world spawn point to ({%0}, {%1}, {%2}) commands.xp.failure.withdrawXp=Cannot give player negative experience points commands.xp.success=Given {%0} experience to {%1} commands.xp.success.levels=Given {%0} levels to {%1} commands.xp.success.negative.levels=Taken {%0} levels from {%1} # -------------------- PocketMine language files, only for console -------------------- pocketmine.data.playerNotFound=Player data not found for "{%0}", creating new profile pocketmine.data.playerCorrupted=Corrupted data found for "{%0}", creating new profile pocketmine.data.playerOld=Old Player data found for "{%0}", upgrading profile pocketmine.data.saveError=Could not save player "{%0}": {%1} pocketmine.level.notFound=Level "{%0}" not found pocketmine.level.loadError=Could not load level "{%0}": {%1} pocketmine.level.generationError=Could not generate level "{%0}": {%1} pocketmine.level.tickError=Could not tick level "{%0}": {%1} pocketmine.level.chunkUnloadError=Error while unloading a chunk: {%0} pocketmine.level.backgroundGeneration=Spawn terrain for level "{%0}" is being generated in the background pocketmine.level.defaultError=No default level has been loaded pocketmine.level.preparing=Preparing level "{%0}" pocketmine.level.unloading=Unloading level "{%0}" pocketmine.server.start=Starting Minecraft: PE server version {%0} pocketmine.server.networkError=[Network] Stopped interface {%0} due to {%1} pocketmine.server.networkStart=Opening server on {%0}:{%1} pocketmine.server.info=This server is running {%0}{%1} "{%2}" (API {%3}) pocketmine.server.info.extended.title=-----Server information----- pocketmine.server.info.extended1=This server is running {%0}{%1} {%2} pocketmine.server.info.extended2=PHP version: {%0} pocketmine.server.info.extended3=API: {%0} pocketmine.server.info.extended4=Target client: Minecraft PE {%0} pocketmine.server.info.extended5=Protocol version: {%0} pocketmine.server.license={%0} is distributed under the GPL License version 3 and later pocketmine.server.tickOverload=Can't keep up! Is the server overloaded? pocketmine.server.startFinished=Done ({%0}s)! For help, type "help" or "?" pocketmine.server.defaultGameMode=Default game type: {%0} pocketmine.server.query.start=Starting GS4 status listener pocketmine.server.query.info=Setting query port to {%0} pocketmine.server.query.running=Query running on {%0}:{%1} pocketmine.command.alias.illegal=Could not register alias {%0} because it contains illegal characters pocketmine.command.alias.notFound=Could not register alias {%0} because it contains commands that do not exist: {%1} pocketmine.command.exception=Unhandled exception executing command '{%0}' in {%1}: {%2} pocketmine.command.plugins.description=Gets a list of plugins running on the server pocketmine.command.plugins.success=Plugins ({%0}): {%1} pocketmine.command.plugins.usage=/plugins pocketmine.command.reload.description=Reloads the server configuration and plugins pocketmine.command.reload.usage=/reload pocketmine.command.reload.reloading=Reloading server... pocketmine.command.reload.reloaded=Reload complete. pocketmine.command.lvdat.description=Change properties of a map. pocketmine.command.lvdat.changed=has changed {%1} of level "{%0}", some change need to reboot your server. pocketmine.command.lvdat.fixname=fixname successfully for level "{%0}", some change need to reboot your server. pocketmine.command.lvdat.nofound=level "{%0}" no found or load failed. pocketmine.command.lvdat.preset=Generator setting (preset) pocketmine.command.status.description=Reads back the server's performance. pocketmine.command.status.usage=/status pocketmine.command.status.title=Server status pocketmine.command.status.player=Player count: pocketmine.command.status.days=days pocketmine.command.status.hours=hours pocketmine.command.status.minutes=minutes pocketmine.command.status.seconds=seconds pocketmine.command.status.uptime=Uptime: pocketmine.command.status.AverageTPS=Average TPS: pocketmine.command.status.CurrentTPS=Current TPS: pocketmine.command.status.Networkupload=Network upload: pocketmine.command.status.Networkdownload=Network download: pocketmine.command.status.Threadcount=Thread count: pocketmine.command.status.Mainmemory=Main thread memory: pocketmine.command.status.Totalmemory=Total memory: pocketmine.command.status.Totalvirtualmemory=Total virtual memory: pocketmine.command.status.Heapmemory=Heap memory: pocketmine.command.status.Maxmemorysystem=Maximum memory (system): pocketmine.command.status.Maxmemorymanager=Maximum memory (manager): pocketmine.command.status.World=World pocketmine.command.status.chunks=chunks, pocketmine.command.status.entities=entities, pocketmine.command.status.tiles=tiles. pocketmine.command.status.Time=Time pocketmine.command.status.ms=ms pocketmine.command.gc.description=Fires garbage collection tasks pocketmine.command.gc.usage=/gc pocketmine.command.gc.title=Collection Report pocketmine.command.gc.chunks=Chunks: pocketmine.command.gc.entities=Entities: pocketmine.command.gc.tiles=Tiles: pocketmine.command.gc.cycles=Cycles: pocketmine.command.gc.memory=Release memory: pocketmine.command.biome.description=Change the biome of the area.(To change snow or rain) pocketmine.command.biome.posset=Set position {%3} at ({%1},{%2}) in level {%0} pocketmine.command.biome.get=The ID of biome you are in is {%0}. Color: {%1},{%2},{%3} pocketmine.command.biome.wrongLev=Cannot set position in different level. pocketmine.command.biome.wrongBio=Wrong ID of biome. e.g. 1 (Plains), 2 (Desert),13 (Ice Mountains),6 (Swampland) pocketmine.command.biome.wrongCol=Wrong Color. e.g. 146,188,89 .Use "/biome get" to get other color. pocketmine.command.biome.noPos=Please use "/biome pos1|pos2" to select the area first. pocketmine.command.biome.set=Set the selected area's biome to {%0} pocketmine.command.biome.color=Set the grass colour to {%0},{%1},{%2} pocketmine.command.timings.description=Records timings to see performance of the server. pocketmine.command.timings.usage=/timings pocketmine.command.timings.enable=Enabled Timings & Reset pocketmine.command.timings.disable=Disabled Timings pocketmine.command.timings.timingsDisabled=Please enable timings by typing /timings on pocketmine.command.timings.reset=Timings reset pocketmine.command.timings.pasteError=An error happened while pasting the report pocketmine.command.timings.timingsUpload=Timings uploaded to {%0} pocketmine.command.timings.timingsRead=You can read the results at {%0} pocketmine.command.timings.timingsWrite=Timings written to {%0} pocketmine.command.version.description=Gets the version of this server including any plugins in use pocketmine.command.version.usage=/version [plugin name] pocketmine.command.version.noSuchPlugin=This server is not running any plugin by that name. Use /plugins to get a list of plugins. pocketmine.command.give.description=Gives the specified player a certain amount of items pocketmine.command.give.usage=/give [amount] [tags...] pocketmine.command.kill.description=Commit suicide or kill other players pocketmine.command.kill.usage=/kill [player] pocketmine.command.particle.description=Adds particles to a world pocketmine.command.particle.usage=/particle [count] [data] pocketmine.command.time.description=Changes the time on each world pocketmine.command.time.usage=/time OR /time pocketmine.command.bancidbyname.description=Prevents the specified CID by name from using this server pocketmine.command.bancidbyname.usage=/bancidbyname pocketmine.command.bancid.description=Prevents the specified CID from using this server pocketmine.command.bancid.usage=/bancid pocketmine.command.banipbyname.description=Prevents the specified IP address by name from using this server pocketmine.command.banipbyname.usage=/banipbyname pocketmine.command.ban.player.description=Prevents the specified player from using this server pocketmine.command.ban.player.ban.usage=/ban [reason ...] [time(day)] pocketmine.command.banip.description=Prevents the specified IP address from using this server pocketmine.command.banip.usage=/ban-ip [reason ...] pocketmine.command.banlist.description=View all players banned from this server pocketmine.command.banlist.usage=/banlist [ips|players|cids] pocketmine.command.defaultgamemode.description=Set the default gamemode pocketmine.command.defaultgamemode.usage=/defaultgamemode pocketmine.command.deop.description=Takes the specified player's operator status pocketmine.command.op.usage=/op pocketmine.command.deop.usage=/deop pocketmine.command.difficulty.description=Sets the game difficulty pocketmine.command.difficulty.usage=/difficulty pocketmine.command.enchant.description=Adds enchantments on items pocketmine.command.enchant.usage=/enchant [level] pocketmine.command.effect.description=Adds/Removes effects on players pocketmine.command.effect.usage=/effect [seconds] [amplifier] [hideParticles] OR /effect clear pocketmine.command.gamemode.description=Changes the player to a specific game mode pocketmine.command.gamemode.usage=/gamemode [player] pocketmine.command.help.description=Shows the help menu pocketmine.command.help.usage=/help [page|command name] pocketmine.command.kick.description=Removes the specified player from the server pocketmine.command.kick.usage=/kick [reason ...] pocketmine.command.list.description=Lists all online players pocketmine.command.players.usage=/list pocketmine.command.me.description=Performs the specified action in chat pocketmine.command.me.usage=/me pocketmine.command.op.description=Gives the specified player operator status pocketmine.command.unban.cid.description=Allows the specified CID to use this server pocketmine.command.unban.cid.usage=/pardoncid pocketmine.command.unban.player.description=Allows the specified player to use this server pocketmine.command.unban.player.usage=/pardon pocketmine.command.unban.ip.description=Allows the specified IP address to use this server pocketmine.command.unban.ip.usage=/pardon-ip
pocketmine.command.save.description=Saves the server to disk pocketmine.command.save.usage=/save-all pocketmine.command.saveoff.description=Disables server autosaving pocketmine.command.saveoff.usage=/save-off pocketmine.command.saveon.description=Enables server autosaving pocketmine.command.saveon.usage=/save-on pocketmine.command.say.description=Broadcasts the given message as the sender pocketmine.command.say.usage=/say pocketmine.command.seed.description=Shows the world seed pocketmine.command.seed.usage=/seed pocketmine.command.setworldspawn.description=Sets a worlds's spawn point. If no coordinates are specified, the player's coordinates will be used. pocketmine.command.setworldspawn.usage=/setworldspawn [ ] pocketmine.command.spawnpoint.description=Sets a player's spawn point pocketmine.command.spawnpoint.usage=/spawnpoint [player] [ ] pocketmine.command.stop.description=Stops the server pocketmine.command.stop.usage=/stop pocketmine.command.tp.description=Teleports the given player (or yourself) to another player or coordinates pocketmine.command.tp.usage=/tp [target player] OR /tp [target player] [ ] pocketmine.command.tell.description=Sends a private message to the given player pocketmine.command.tell.usage=/tell pocketmine.command.xp.description=Add experience or experience level to the given player pocketmine.command.xp.usage=/xp [player] OR /xp L [player] pocketmine.command.summon.description=Summons a entity at the player's location or a specific location pocketmine.command.summon.usage=/summon [entity] [ ] [NBTTag] pocketmine.command.fill.description=fills a specific selection with blocks pocketmine.command.setblock.description=Changes a block to another block pocketmine.command.setblock.usage=/setblock [damage] pocketmine.command.weather.description=Set weather for level pocketmine.command.weather.usage=/weather pocketmine.command.weather.changed=Weather changed successfully in level {%0}! pocketmine.command.weather.noregistered=level {%0} hasn't registered to WeatherManager. pocketmine.command.weather.invalid=Invalid weather.(0,1,2,3) pocketmine.command.weather.wrong=Wrong parameters. pocketmine.command.weather.invalid.level=Invalid level name. pocketmine.command.whitelist.description=Manages the list of players allowed to use this server pocketmine.command.whitelist.usage=/whitelist pocketmine.crash.create=An unrecoverable error has occurred and the server has crashed. Creating a crash dump pocketmine.crash.error=Could not create crash dump: {%0} pocketmine.crash.submit=Please upload the "{%0}" file to the Crash Archive and submit the link to the Bug Reporting page. Give as much info as you can. pocketmine.crash.archive=The crash dump has been automatically submitted to the Crash Archive. You can view it on {%0} or use the ID #{%1}. pocketmine.debug.enable=LevelDB support enabled pocketmine.player.invalidMove={%0} moved wrongly! pocketmine.player.logIn={%0}[/{%1}:{%2}] [ClientID: {%3}] logged in with entity id {%4} at ({%5}, {%6}, {%7}, {%8}) with a(n) {%9} pocketmine.player.logOut={%0}[/{%1}:{%2}] logged out due to {%3} pocketmine.player.transferred={%0}[/{%1}:{%2}] was transferred to {%3} pocketmine.player.invalidEntity={%0} tried to attack an invalid entity pocketmine.plugin.load=Loading {%0} pocketmine.plugin.enable=Enabling {%0} pocketmine.plugin.disable=Disabling {%0} pocketmine.plugin.restrictedName=Restricted name pocketmine.plugin.incompatibleAPI=Incompatible API version pocketmine.plugin.unknownDependency=Unknown dependency pocketmine.plugin.circularDependency=Circular dependency detected pocketmine.plugin.genericLoadError=Could not load plugin '{%0}' pocketmine.plugin.spacesDiscouraged=Plugin '{%0}' uses spaces in its name, this is discouraged pocketmine.plugin.loadError=Could not load plugin '{%0}': {%1} pocketmine.plugin.duplicateError=Could not load plugin '{%0}': plugin exists pocketmine.plugin.fileError=Could not load '{%0}' in folder '{%1}': {%2} pocketmine.plugin.commandError=Could not load command {%0} for plugin {%1} pocketmine.plugin.aliasError=Could not load alias {%0} for plugin {%1} pocketmine.plugin.deprecatedEvent=Plugin '{%0}' has registered a listener for '{%1}' on method '{%2}', but the event is Deprecated. pocketmine.plugin.eventError="Could not pass event '{%0}' to '{%1}': {%2} on {%3} ================================================ FILE: src/pocketmine/lang/locale/fra.ini ================================================ # Fichier de langue compatible avec Minecraft: Pocket Edition # Aidez moi a traduire (ou à vérifier) les lignes: 186, 212, 276, 277, 286, 290, 295. # Un message n'a pas besoin d'être là pour être montré correctement sur le client. # Seuls les messages affichés dans Genisys se doivent d'être ici language.name=Français language.selected=Sélection de {%0} ({%1}) comme langue de base multiplayer.player.joined={%0} a rejoint la partie multiplayer.player.left={%0} a quitté la partie chat.type.text={%0} : {%1} chat.type.emote=* {%0} {%1} chat.type.announcement=[{%0}] {%1} chat.type.admin=[{%0}: {%1}] chat.type.achievement={%0} vient d'obtenir le succès {%1} disconnectionScreen.outdatedClient=Client expiré ! disconnectionScreen.outdatedServer=Serveur expiré ! disconnectionScreen.serverFull=Le serveur est complet ! disconnectionScreen.noReason=Déconnecté du serveur disconnectionScreen.invalidSkin=Skin invalide ! disconnectionScreen.invalidName=Nom invalide ! death.fell.accident.generic={%0} a fait une terrible chute death.attack.inFire={%0} a péri dans les flammes death.attack.onFire={%0} s'est fait carboniser à mort death.attack.lava={%0} a essayé de nager dans la lave death.attack.inWall={%0} a suffoqué dans un mur death.attack.drown={%0} s'est noyé death.attack.cactus={%0} s'est fait piquer à mort death.attack.generic={%0} est mort death.attack.explosion={%0} est mort dans une explosion death.attack.explosion.player={%0} est mort dans une explosion causé par {%1} death.attack.magic={%0} a été tué par la magie death.attack.wither={%0} sécha death.attack.mob={%0} a été tué par {%1} death.attack.player={%0} a été tué par {%1} death.attack.player.item={%0} a été tué par {%1} en utilisant {%2} death.attack.arrow={%0} s'est fait tiré dessus par {%1} death.attack.arrow.item={%0} s'est fait tiré dessus par {%1} en utilisant {%2} death.attack.fall={%0} a frappé le sol trop rapidement death.attack.outOfWorld={%0} a littéralement quitté notre monde gameMode.survival=Mode Survie gameMode.creative=Mode Créatif gameMode.adventure=Mode Aventure gameMode.spectator=Mode Spectateur gameMode.changed=Votre mode de jeu a été mis à jour potion.moveSpeed=Vitesse potion.moveSlowdown=Lenteur potion.digSpeed=Célérité potion.digSlowDown=Fatigue potion.damageBoost=Force potion.heal=Vie instantanée potion.harm=Dégats instantanés potion.jump=Saut potion.confusion=Nausée potion.regeneration=Régénération potion.resistance=Résistance potion.fireResistance=Résistance au feu potion.waterBreathing=Respiration aquatique potion.invisibility=Invisibilité potion.blindness=Cécité potion.nightVision=Vision nocturne potion.hunger=Faim potion.weakness=Faiblesse potion.poison=Poison potion.wither=Wither potion.healthBoost=Soin instantané potion.absorption=Absorption potion.saturation=Satiété commands.generic.exception=Une erreur inconnue est survenue lors de la tentative d'exécution de cette commande commands.generic.permission=Vous n'avez pas la permission d'utiliser cette commande commands.generic.notFound=Cette commande n'est pas reconnue : faites /help pour obtenir la liste des commandes commands.generic.player.notFound=Le joueur n'a pas été trouvé commands.generic.usage=Utilisation : {%0} commands.generic.level=Monde commands.generic.seed=Semence commands.generic.name=Nom commands.generic.generator=Générateur commands.generic.opt.missing=Propriétés manquantes, merci de confirmer et d'appuyer sur entrée. commands.generic.runingame=Cette commande ne fonctionne qu'en jeu. commands.time.added=Heure avancée de {%0} commands.time.set=Heure fixée à {%0} commands.time.query=Temps : {%0} commands.me.usage=/me commands.give.item.notFound=Il n'y a pas d'objet connu ayant pour nom {%0} commands.give.success=Don de {%0} * {%1} à {%2} commands.give.tagError=L'analyse syntaxique du data tag a échoué : {%0} commands.effect.usage=/effect [temps] [amplificateur] [masquer les particules] OU /effect clear commands.effect.notFound=Il n'y a pas d'effet connu ayant pour ID {%0} commands.effect.success=Don de l'effet {%0} (ID {%1}) * {%2} à {%3} pour une durée de {%4} secondes commands.effect.success.removed=Retrait de l'effet {%0} de {%1} commands.effect.success.removed.all=Retrait de tous les effets de {%0} commands.effect.failure.notActive=Impossible de supprimer l'effet {%0} de {%1} car ce joueur ne possède pas cet effet commands.effect.failure.notActive.all=Impossible de supprimer les effets du joueur {%0} car ce joueur ne possède aucun effets commands.enchant.noItem=Le joueur n'a pas d'objet en main commands.enchant.notFound=Il n'y a pas d'enchantement avec l'ID {%0} commands.enchant.success=Enchantement réussi commands.enchant.cantEnchant=L'enchantement sélectionné ne peut être ajouté à l'élément cible commands.enchant.usage=/enchant [niveau] commands.particle.success=L'effet {%0} apparaît {%1} fois commands.particle.notFound=Effet inconnu {%0} commands.players.usage=/list commands.players.list=Il y'a {%0}/{%1} joueurs en ligne: commands.kill.successful={%0} a été anéanti(e) commands.banlist.ips=Il y'a un total de {%0} IP banni(s) : commands.banlist.players=Il y'a un total de {%0} joueur(s) banni(s) : commands.banlist.cids=Il y'a un total de {%0} CID banni(s) : commands.banlist.usage=/banlist [ip|players|cids] commands.defaultgamemode.usage=/defaultgamemode commands.defaultgamemode.success=Le mode de jeu par défaut est maintenant {%0} commands.op.success={%0} a été promu opérateur commands.op.usage=/op commands.deop.success={%0} a perdu ses privilèges d'opérateur commands.deop.usage=/deop commands.say.usage=/say commands.seed.usage=/seed commands.seed.success=Semence : {%0} commands.bancidbyname.success=CID du joueur {%0} banni commands.bancidbyname.usage=/bancidbyname commands.bancid.success=CID {%0} banni commands.bancid.usage=/bancid commands.unbancid.usage=/pardoncid commands.unbancid.success=CID {%0} débanni commands.ban.success=Joueur {%0} banni commands.ban.usage=/ban [raison ...] [temps(jour)] commands.unban.success=Joueur {%0} débanni commands.unban.usage=/pardon commands.banip.invalid=Vous avez entré une adresse IP invalide ou un joueur qui n'est pas connecté. commands.banip.success=Adresse IP {%0} bannie commands.banip.success.players=L'adresse IP {%0} appartenant à {%1} a était bannie commands.banip.usage=/ban-ip [raison ...] commands.unbanip.invalid=Vous avez entré une adresse IP invalide commands.unbanip.success=Adresse IP {%0} débannie commands.unbanip.usage=/pardon-ip commands.banipbyname.success=L'adresse IP du joueur {%0} a été bannie commands.banipbyname.usage=/banipbyname commands.save.usage=/save-all commands.save-on.usage=/save-on commands.save-off.usage=/save-off commands.save.enabled=Sauvegarde automatique du monde activée commands.save.disabled=Sauvegarde automatique du monde désactivée commands.save.start=Sauvegarde... commands.save.success=Monde sauvegardé commands.setblock.usage=/setblock [dégâts] command.setblock.invalidBlock=Vous avez entré un nom/ID de bloc invalide commands.stop.usage=/stop commands.stop.start=Arrêt du serveur commands.kick.success={%0} s'est fait éjecter du serveur commands.kick.success.reason={%0} s'est fait éjecter du serveur pour la raison suivante : {%1}' commands.kick.usage=/kick [raison ...] commands.tp.success={%0} s'est fait téléporter vers {%1} commands.tp.success.coordinates={%0} s'est fait téléporter en {%1}, {%2}, {%3} commands.tp.usage=/tp [joueur cible] OU /tp [joueur cible] [ ] commands.whitelist.list=Il y a {%0} joueur(s) (sur {%1} détecté(s)) dans la liste blanche : commands.whitelist.enabled=Liste blanche activée commands.whitelist.disabled=Liste blanche désactivée commands.whitelist.reloaded=Liste blanche réactualisée commands.whitelist.add.success={%0} a été ajouté(e) à la liste blanche commands.whitelist.add.usage=/whitelist add commands.whitelist.remove.success=Le joueur {%0} a été retiré(e) de la liste blanche commands.whitelist.remove.usage=/whitelist remove commands.whitelist.usage=/whitelist commands.gamemode.success.self=Votre mode de jeu a été changé en Mode {%2} commands.gamemode.success.other=Le mode de jeu de {%1} a été changé en Mode {%2} commands.gamemode.usage=/gamemode [joueur] commands.help.header=--- Affichage de la page d'aide {%0} sur {%1} (/help ) --- commands.help.usage=/help [page|nom de la commande] commands.message.usage=/tell commands.message.sameTarget=Vous ne pouvez pas vous envoyer de message privé ! commands.xp.usage=/xp commands.difficulty.usage=/difficulty commands.difficulty.success=La difficulté a été changée en {%0} commands.spawnpoint.usage=/spawnpoint [joueur] [ ] commands.spawnpoint.success=Le nouveau point d'apparition de {%0} a été défini en ({%1}, {%2}, {%3}) commands.setworldspawn.usage=/setworldspawn [ ] commands.setworldspawn.success=Le point d'appartition du monde a été défini en ({%0}, {%1}, {%2}) commands.summon.usage=/summon [nom de l'entité] [ ] [dataTag] # -------------------- Fichiers de langue PocketMine, uniquement pour la console -------------------- pocketmine.data.playerNotFound=Données du joueur "{%0}" invalides , création d'un nouveau profil pocketmine.data.playerCorrupted=Données du joueur "{%0}" corrompus, création d'un nouveau profil pocketmine.data.playerOld=Ancienne données du joueur "{%0}" trouvées, mise à jour du profil pocketmine.data.saveError=Impossible de sauvegarder le joueur "{%0}": {%1} pocketmine.level.notFound=Le monde "{%0}" est introuvable pocketmine.level.loadError=Impossible de charger le monde "{%0}": {%1} pocketmine.level.generationError=Impossible de générer le monde "{%0}": {%1} pocketmine.level.tickError=Impossible de vérifier le monde "{%0}": {%1} pocketmine.level.chunkUnloadError=Une erreur est survenue lors du déchargement d'un chunk: {%0} pocketmine.level.backgroundGeneration=L'apparition du terrain pour le niveau "{%0}" est entrain de ce générer en arrière-plan pocketmine.level.defaultError=Aucun niveau par défaut n'a été chargée pocketmine.level.preparing=Préparation du terrain "{%0}" pocketmine.level.unloading=Déchargement du terrain "{%0}" pocketmine.server.start=Démarrage du serveur Minecraft PE version {%0} pocketmine.server.networkError=[Réseau] Arrêt de l'interface {%0} à cause de {%1} pocketmine.server.networkStart=Ouverture du serveur sur {%0}:{%1} pocketmine.server.info=Ce serveur fonctionne sur {%0} version {%1} "{%2}" (API {%3}) pocketmine.server.info.extended=Ce serveur fonctionne sur {%0} {%1} "{%2}" API implantée {%3} pour Minecraft: Pocket Edition {%4} (version du protocol {%5}) pocketmine.server.license={%0} est distribué sous la licence GPL version 3 et plus pocketmine.server.tickOverload=Désynchronisation ! Est-ce que le serveur surchargé ? pocketmine.server.startFinished=Démarrer en ({%0}s)! Pour afficher l'aide tapez "/help" ou "/?" pocketmine.server.defaultGameMode=Mode de jeu par défaut : {%0} pocketmine.server.query.start=Démarrage de GS4 statut listener pocketmine.server.query.info=Réglage du port Query sur {%0} pocketmine.server.query.running=Le Query fonctionne sur {%0}:{%1} pocketmine.command.alias.illegal=Le pseudonyme {%0} ne peut pas être enregistré car il contient des caractères illégaux pocketmine.command.alias.notFound=Le pseudonyme {%0} ne peut pas être enregistré car il contient des commandes qui n'existent pas: {%1} pocketmine.command.exception=Commande d'exécution d'exception non gérée '{%0}' dans {%1}: {%2} pocketmine.commands.cave.usage=/cave | /cave getmypos pocketmine.commands.cave.info=Angle de rotation:{%0} Longueur:{%1} Branch Number:{%2} Force:{%3} pocketmine.commands.cave.start=Génération de la caverne, veuillez patientez... pocketmine.commands.cave.success=Caverne généré pocketmine.command.plugins.description=Obtention de la liste des plugins en cours d'exécution sur le serveur pocketmine.command.plugins.success=Plugins ({%0}) : {%1} pocketmine.command.plugins.usage=/plugins pocketmine.command.reload.description=Recharge la configuration du serveur et des plugins pocketmine.command.reload.usage=/reload pocketmine.command.reload.reloading=Rechargement du serveur... pocketmine.command.reload.reloaded=Rechargement du serveur terminé. pocketmine.command.lvdat.description=Modification des propriétés de la carte. pocketmine.command.lvdat.changed=Changement {%1} du monde "{%0}", les changements nécessites un redémarrage de serveur. pocketmine.command.lvdat.fixname=Changement du nom terminé "{%0}", les changements nécessites un redémarrage de serveur. pocketmine.command.lvdat.nofound=le monde "{%0}" ne fonctionne pas ou le chargement a échoué pocketmine.command.lvdat.preset=Réglage du générateur (prédéfini) pocketmine.command.status.description=Relecture des performances du serveur. pocketmine.command.status.usage=/status pocketmine.command.status.title=Statut du serveur pocketmine.command.status.player=Nombre de joueur(s): pocketmine.command.status.days=jour(s) pocketmine.command.status.hours=heure(s) pocketmine.command.status.minutes=minute(s) pocketmine.command.status.seconds=seconde(s) pocketmine.command.status.uptime=Durée de fonctionnement: pocketmine.command.status.AverageTPS=Moyenne de TPS: pocketmine.command.status.CurrentTPS=TPS actuel: pocketmine.command.status.Networkupload=Débit montant de réseau : pocketmine.command.status.Networkdownload=Débit descendant de réseau : pocketmine.command.status.Threadcount=Thread count: pocketmine.command.status.Mainmemory=Main thread memory: pocketmine.command.status.Totalmemory=Mémoire totale: pocketmine.command.status.Totalvirtualmemory=Mémoire virtuelle totale: pocketmine.command.status.Heapmemory=Mémoire utilisée: pocketmine.command.status.Maxmemorysystem=Mémoire maximale (système): pocketmine.command.status.Maxmemorymanager=Mémoire maximale (gestionnaire): pocketmine.command.status.World=Monde pocketmine.command.status.chunks=Chunks, pocketmine.command.status.entities=Entités, pocketmine.command.status.tiles=Tiles. pocketmine.command.status.Time=Temps pocketmine.command.status.ms=ms pocketmine.command.gc.description=Ramassage des déchets pocketmine.command.gc.usage=/gc pocketmine.command.gc.title=Collection du Rapport pocketmine.command.gc.chunks=Chunks: pocketmine.command.gc.entities=Entités: pocketmine.command.gc.tiles=Tiles: pocketmine.command.gc.cycles=Cycles: pocketmine.command.gc.memory=Release memory: pocketmine.command.biome.description=Changez le biome de la zone.(Pour changer la neige ou la pluie) pocketmine.command.biome.posset=Réglage de la position {%3} à ({%1},{%2}) dans le niveau {%0} pocketmine.command.biome.get=L'ID de biome où vous êtes est {%0}. Couleurr: {%1},{%2},{%3} pocketmine.command.biome.wrongLev=Impossible de définir cette position dans un niveau différent. pocketmine.command.biome.wrongBio=Mauvais ID de biome. e.g. 1 (Plaine), 2 (Désert),13 (Montagnes de glace),6 (Marécage) pocketmine.command.biome.wrongCol=Mauvaise couleur. e.g. 146,188,89 .Use "/biome get" pour obtenir une autre couleur. pocketmine.command.biome.noPos=Veuillez utiliser "/biome pos1|pos2" pour sélectionner la première zone. pocketmine.command.biome.set=Réglage du biome dans la zone sélectionnée à {%0} pocketmine.command.biome.color=Réglage de la couleur de l'herbe à {%0},{%1},{%2} pocketmine.command.timings.description=Synchronisation des rapports pour voir les performances du serveur. pocketmine.command.timings.usage=/timings pocketmine.command.timings.enable=Activer le minutage et réinitialiser pocketmine.command.timings.disable=Désactiver le minutage pocketmine.command.timings.timingsDisabled=Merci d'activer les rapports de performance en tapant la commande /timings on pocketmine.command.timings.reset=Minutage réinitialisé pocketmine.command.timings.pasteError=Une erreur s'est produite lors de la génération du rapport pocketmine.command.timings.timingsUpload=Rapports de performance mis à jours sur {%0} pocketmine.command.timings.timingsRead=Vous pouvez lire les résultats dans {%0} pocketmine.command.timings.timingsWrite=Rapports de performance écrits sur {%0} pocketmine.command.version.description=Obtenez la version de ce serveur, y compris tous les plugins utilisés pocketmine.command.version.usage=/version [nom du plugin] pocketmine.command.version.noSuchPlugin=Ce serveur n'utilise pas ce plugin. Utilisez / plugins pour obtenir la liste des plugins. pocketmine.command.give.description=Donne au joueur spécifié une certaine quantité d'items pocketmine.command.give.usage=/give [montant] [tags...] pocketmine.command.kill.description=Se suicider ou tuer d'autres joueurs pocketmine.command.kill.usage=/kill [joueur] pocketmine.command.particle.description=Ajoute des particules à un monde pocketmine.command.particle.usage=/particle [nombre] [data] pocketmine.command.time.description=Change le temps sur chaque monde pocketmine.command.time.usage=/time OU /time pocketmine.command.bancidbyname.description=Empêche le CID du joueur spécifié d'accéder à ce serveur pocketmine.command.bancid.description=Empêche le CID spécifié d'accéder à ce serveur pocketmine.command.banipbyname.description=Empêche l'adresse IP du joueur spécifiée d'accéder a ce serveur pocketmine.command.ban.player.description=Empêche le joueur spécifié d'accéder a ce serveur pocketmine.command.ban.ip.description=Empêche l'adresse IP spécifiée d'accéder a ce serveur pocketmine.command.banlist.description=Voir la liste de tous les joueurs bannis sur ce serveur pocketmine.command.defaultgamemode.description=Réglez le mode de jeu par défaut pocketmine.command.deop.description=Enléve les droits d'administration à un joueur spécifié. pocketmine.command.difficulty.description=Définit la difficulté du jeu pocketmine.command.enchant.description=Ajoute un enchantement sur un item pocketmine.command.effect.description=Ajoute/Supprime des effets sur un joueur spécifié pocketmine.command.gamemode.description=Change le mode de jeu d'un joueur spécifique pocketmine.command.help.description=Affiche le menu d'aide pocketmine.command.kick.description=Exclu le joueur spécifié du serveur pocketmine.command.list.description=Liste de tous les joueurs en ligne pocketmine.command.me.description=Exécute l'action spécifiée dans le chat pocketmine.command.op.description=Donne le statut d'opérateur au joueur spécifié pocketmine.command.unban.cid.description=Autorise le CID spécifié à accéder au serveur pocketmine.command.unban.player.description=Autorise le joueur spécifié à accéder au serveur pocketmine.command.unban.ip.description=Autorise l'adresse IP spécifiée à accéder au serveur pocketmine.command.save.description=Sauvegarde le serveur sur le disque pocketmine.command.saveoff.description=Désactive la sauvegarde automatique pocketmine.command.saveon.description=Active la sauvegarde automatique pocketmine.command.say.description=Diffuse un message dans le chat pocketmine.command.seed.description=Affiche le seed du serveur pocketmine.command.setworldspawn.description=Définit le point d'apparition du monde.Si aucunes coordonnées ne sont spécifiées, les coordonnées du joueur sont utilisés. pocketmine.command.spawnpoint.description=Définit le point d'apparition d'un joueur pocketmine.command.stop.description=Arrête le serveur pocketmine.command.tp.description=Téléporte le joueur donné (ou vous-même) à un autre joueur ou vers des coordonnées pocketmine.command.tell.description=Envoie un message privé au joueur donné pocketmine.command.xp.description=Ajouter de l'expérience a un joueur donné pocketmine.command.summon.description=Invoque une entité à l'emplacement d'un joueur ou à un emplacement spécifique pocketmine.command.fill.description=remplit une sélection spécifique avec des blocs pocketmine.command.setblock.description=Remplace un bloc par un autre pocketmine.command.weather.description=Réglez la météo pour le niveau pocketmine.command.weather.usage=/weather pocketmine.command.weather.changed=Météo changé avec succès au niveau {%0}! pocketmine.command.weather.noregistered=Le niveau {%0} n'est pas enregistré à WeatherManager. pocketmine.command.weather.invalid=Météo Invalide.(0,1,2,3) pocketmine.command.weather.wrong=Mauvais paramètres. pocketmine.command.weather.invalid.level=Nom du niveau invalide. pocketmine.command.whitelist.description=Gère la liste des joueurs autorisés à accéder à serveur pocketmine.crash.create=Une erreur irréversible s'est produite causant un crash du serveur. Création d'un rapport de crash pocketmine.crash.error=Impossible de créer un fichier de crash : {%0} pocketmine.crash.submit=Soumettez le fichier "{%0}" à l'archive des crashs et soumettez le lien vers la page des rapports de bugs. Donnez le plus d'informations possible. pocketmine.crash.archive=Le rapport de crash a été automatiquement soumis à l'archive des crashs. Vous pouvez le voir sur {%0} ou utilisez l'ID #{%1}. pocketmine.debug.enable=Support LevelDB activé pocketmine.player.invalidMove={%0} se déplace incorrectement ! pocketmine.player.logIn={%0}[/{%1}:{%2}] [IDClient: {%3}] s'est connecté avec l'ID d'entité {%4} à ({%5}, {%6}, {%7}, {%8}) pocketmine.player.logOut={%0}[/{%1}:{%2}] déconnecté en raison de {%3} pocketmine.player.transferred={%0}[/{%1}:{%2}] a été transféré à {%3} pocketmine.player.invalidEntity={%0} essaye d'attaquer une entité invalide pocketmine.plugin.load=Chargement {%0} pocketmine.plugin.enable=Activation de {%0} pocketmine.plugin.disable=Désactivation de {%0} pocketmine.plugin.restrictedName=Nom restreint pocketmine.plugin.incompatibleAPI=Version d'API Incompatible pocketmine.plugin.unknownDependency=Dépendance inconnu pocketmine.plugin.circularDependency=Dépendance circulaire détectée pocketmine.plugin.genericLoadError=Impossible de charger le plugin '{%0}' pocketmine.plugin.spacesDiscouraged=Le plugin '{%0} utilise des espaces dans son nom, ceci est déconseillé pocketmine.plugin.loadError=Impossible de charger le plugin '{%0}': {%1} pocketmine.plugin.duplicateError=Impossible de charger le plugin '{%0}': le plugin existe pocketmine.plugin.fileError=Impossible de charger '{%0}' dans le dossier '{%1}': {%2} pocketmine.plugin.commandError=Impossible de charger la commande {%0} pour le plugin {%1} pocketmine.plugin.aliasError=L'alias {%0} ne peut pas être chargé pour le plugin {%1} pocketmine.plugin.deprecatedEvent=Le plugin '{%0}' a enregistré un auditeur pour '{%1} "sur la méthode'{%2}', mais l'événement est obsolète. pocketmine.plugin.eventError="Impossible de passer l’événement '{%0}' de '{%1}': {%2} sur {%3}" ================================================ FILE: src/pocketmine/lang/locale/jpn.ini ================================================ # Language file compatible with Minecraft: Pocket Edition identifiers # # A message doesn't need to be there to be shown correctly on the client. # Only messages shown in PocketMine itself need to be here language.name=日本語 language.selected={%0} ({%1}) を言語に選択しました multiplayer.player.joined={%0} がゲームに参加しました multiplayer.player.left={%0} が退出しました chat.type.text={%0} : {%1} chat.type.emote=* {%0} {%1} chat.type.announcement=[{%0}] {%1} chat.type.admin=[{%0}: {%1}] chat.type.achievement={%0}は{%1}の実績を手に入れました disconnectionScreen.notAuthenticated=Xboxでのログインが必要です disconnectionScreen.outdatedClient=古いクライアントです! disconnectionScreen.outdatedServer=古いサーバーです! disconnectionScreen.serverFull=サーバーは満員です! disconnectionScreen.noReason=サーバーから切断されました disconnectionScreen.invalidSkin=無効なスキンです disconnectionScreen.invalidName=無効な名前です! death.fell.accident.generic={%0} は高いところから落ちた death.attack.inFire={%0}は炎に巻かれてしまった death.attack.onFire={%0}はこんがりと焼けてしまった death.attack.lava={%0}は溶岩遊泳を試みた death.attack.inWall={%0}は壁の中で窒息してしまった death.attack.drown={%0}は溺れ死んだ death.attack.cactus={%0}はサボテンに刺されて死んだ death.attack.generic={%0}は死んでしまった death.attack.explosion={%0}は爆発に巻き込まれてしまった death.attack.explosion.player={%0}は{%1}に爆破されてしまった death.attack.magic={%0}は魔法で殺された death.attack.wither={%0}は干からびてしまった death.attack.mob={%0}は{%1}に殺害された death.attack.player={%0}は{%1}に殺害された death.attack.player.item={%0}は{%1}の{%2}で殺害された death.attack.arrow={%0}は{%1}に射抜かれた death.attack.arrow.item={%0}は{%1}の{%2}で射抜かれた death.attack.fall={%0}は地面と強く激突してしまった death.attack.outOfWorld={%0}は奈落の底へ落ちてしまった gameMode.survival=サバイバルモード gameMode.creative=クリエイティブモード gameMode.adventure=アドベンチャーモード gameMode.spectator=スペクテイターモード gameMode.changed=ゲームモードが変更されました potion.moveSpeed=移動速度上昇 potion.moveSlowdown=移動速度低下 potion.digSpeed=採掘速度上昇 potion.digSlowDown=採掘速度低下 potion.damageBoost=攻撃力上昇 potion.heal=即時回復 potion.harm=ダメージ potion.jump=跳躍力上昇 potion.confusion=吐き気 potion.regeneration=再生能力 potion.resistance=耐性 potion.fireResistance=火炎耐性 potion.waterBreathing=水中呼吸 potion.invisibility=透明化 potion.blindness=盲目 potion.nightVision=暗視 potion.hunger=空腹 potion.weakness=弱体化 potion.poison=毒 potion.wither=ウィザー potion.healthBoost=体力増強 potion.absorption=衝撃吸収 potion.saturation=満腹度回復 commands.generic.exception=コマンドの実行中に不明なエラーが発生しました commands.generic.permission=このコマンドを使用する権限がありません commands.generic.notFound=未知のコマンドです。/helpでコマンドの一覧を確認してください commands.generic.player.notFound=プレイヤーが見つかりません commands.generic.usage=使い方: {%0} commands.generic.level=level-name commands.generic.seed=seed-name commands.generic.name=name commands.generic.generator=generator-name commands.generic.opt.missing=必要なオプションが入力されていません。確認して再入力してください。 commands.generic.runingame=コマンドはゲーム内で使用してください。 commands.time.added=時間を{%0}進めました commands.time.set=現在時刻を{%0}に設定しました commands.time.query=現在の時間は {%0} です commands.me.usage=/me <アクション> commands.give.item.notFound={%0}という名前のアイテムはありません commands.give.success={%0} を {%1} 個 {%2} に与えました commands.give.tagError=データタグの解析に失敗しました: {%0} commands.effect.usage=/effect <プレイヤー名> <効果> [秒数] [倍数] [パーティクルの設定] か /effect <プレイヤー名> clear commands.effect.notFound=ID{%0}のエフェクトは存在しません commands.effect.success={%3}に{%0} (ID {%1})×{%2}を{%4}秒間与えました commands.effect.success.removed={%0}を{%1}から除去しました commands.effect.success.removed.all={%0}からすべての効果を除去しました commands.effect.failure.notActive={%1} は {%0} という効果を受けていないので除去することができませんでした commands.effect.failure.notActive.all={%0}はステータス効果を受けていないので除去することはできませんでした commands.enchant.noItem=対象のプレイヤーはアイテムを持っていません commands.enchant.notFound=ID {%0} に該当するエンチャントはありません commands.enchant.success=エンチャントに成功しました commands.enchant.cantEnchant=このアイテムはエンチャントをすることができません commands.enchant.usage=/enchant <プレイヤー名> <エンチャントID> [レベル] commands.particle.success=効果{%0}を{%1}回発生させます commands.particle.notFound=存在しないエフェクト:{%0} commands.players.usage=/list commands.players.list=現在{%0}人(最大{%1}人)がオンライン: commands.kill.successful={%0}を殺しました commands.banlist.ips=%d 個のIPアドレスがBANされています: commands.banlist.players={%0} 人のプレイヤーがBANされています: commands.banlist.cids={%0} 個のCIDがBANされています: commands.banlist.usage=/banlist [ips|players|cids] commands.defaultgamemode.usage=/defaultgamemode <モード> commands.defaultgamemode.success=ワールドのデフォルトのゲームモードを {%0} にしました。 commands.op.success={%0} にオペレーター権を付与しました commands.op.usage=/op <プレイヤー名> commands.deop.success={%0} からオペレーター権を剥奪しました commands.deop.usage=/deop <プレイヤー名> commands.say.usage=/say <メッセージ ...> commands.seed.usage=/seed commands.seed.success=Seed値:{%0} commands.bancidbyname.success=プレイヤー {%0}のCIDはBANされました。 commands.bancidbyname.usage=/bancidbyname commands.bancid.success=CID: {%0}はBANされました commands.bancid.usage=/bancid commands.unbancid.usage=/pardoncid commands.unbancid.success=CID {%0} のBANが解除されました commands.ban.success={%0} をBANしました commands.ban.usage=/ban <プレイヤー名> [理由 ...] [時間(日)] commands.unban.success={%0} のbanを解除しました commands.unban.usage=/pardon <プレイヤー名> commands.banip.invalid=無効なIPアドレスが入力されたか、プレイヤーがオンラインになっていません commands.banip.success=IPアドレス "{%0}" をBANしました。 commands.banip.success.players={%1} のIPアドレス {%0} をBANしました commands.banip.usage=/ban-ip [理由] commands.unbanip.invalid=無効なIPアドレスです commands.unbanip.success=IPアドレス {%0} のBANが解除されました commands.unbanip.usage=/pardon-ip commands.banipbyname.success=プレイヤー {%0}の IPはBANされました。 commands.banipbyname.usage=/banipbyname <プレイヤー> commands.save.usage=/save-all commands.save-on.usage=/save-on commands.save-off.usage=/save-off commands.save.enabled=ワールドの自動保存を有効にしました commands.save.disabled=ワールドの自動保存を無効にしました commands.save.start=保存中… commands.save.success=ワールドを保存しました commands.setblock.usage=/setblock <ブロック名> [データ値] command.fill.invalidBlock=無効なブロック名/ID commands.stop.usage=/stop commands.stop.start=サーバーを停止しています commands.kick.success={%0}さんはkickされてゲームから切断しました。 commands.kick.success.reason={%0} を {%1} サーバーからキックした commands.kick.usage=/kick <プレイヤー名> [理由...] commands.tp.success={%0} から {%1} へワープしました commands.tp.success.coordinates={%0} は {%1}, {%2}, {%3} にテレポートしました commands.tp.usage=/tp [対象のプレイヤー] <移動先のプレイヤー> または /tp [対象のプレイヤー] [ のみ] commands.whitelist.list=ホワイトリストには {%0} 人 (サーバー全体では {%1} 人) のプレイヤーがいます commands.whitelist.enabled=ホワイトリストを有効にしました commands.whitelist.disabled=ホワイトリストを無効にしました commands.whitelist.reloaded=ホワイトリストを再読み込みしました commands.whitelist.add.success={%0} をホワイトリストに追加しました commands.whitelist.add.usage=/whitelist add <プレイヤー名> commands.whitelist.remove.success={%0} をホワイトリストから削除しました commands.whitelist.remove.usage=/whitelist remove <プレイヤー名> commands.whitelist.usage=/whitelist commands.gamemode.success.self=ゲームモードを{%2}に変更しました commands.gamemode.success.other={%2}のゲームモードを{%2}に変更しました commands.gamemode.usage=/gamemode <モード> [プレイヤー名] commands.help.header=--- ヘルプページの {%0} / {%1} ページを表示(/help <ページ番号>) --- commands.help.usage=/help [ページ|コマンド名] commands.message.usage=/tell <プレイヤー名> <プライベートメッセージ> commands.message.sameTarget=自分自身にプライベートメッセージを送信することはできません! commands.xp.usage=/xp <経験値またはレベル+L> <プレイヤー名> commands.difficulty.usage=/difficulty <難易度> commands.difficulty.success=ゲームの難易度を{%0}に設定しました commands.spawnpoint.usage=/spawnpoint [プレイヤー名] [] commands.spawnpoint.success={%0} のスポーン地点を ({%1}, {%2}, {%3}) に変更しました commands.setworldspawn.usage=/setworldspawn [ ] commands.setworldspawn.success=({%0},{%1},{%2}) にワールドのスポーンポイントを設定します commands.summon.usage=/summon [エンティティ名] [ ] [データタグ] # -------------------- PocketMine language files, only for console -------------------- pocketmine.data.playerNotFound={%0} のプレイヤーデータが見つからないため、新たに作成します pocketmine.data.playerCorrupted={%0} のデータの破損が見つかったため、新たに作成します pocketmine.data.playerOld={%0} の古いプレイヤーデータが見つかったため、更新します pocketmine.data.saveError={%0}のデータを保存できませんでした: {%1} pocketmine.level.notFound=ワールド "{%0}" が見つかりません pocketmine.level.loadError=ワールド "{%0}" を読み込むことができませんでした: {%1} pocketmine.level.generationError=ワールド"{%0}"を生成することができませんでした: {%1} pocketmine.level.tickError=ワールド "{%0}" をチェックすることができませんでした: {%1} pocketmine.level.chunkUnloadError=チャンクの書き込み中にエラーが発生しました: {%0} pocketmine.level.backgroundGeneration=ワールド"{%0}"の地形をバックグラウンドで生成しています pocketmine.level.defaultError=デフォルトのワールドが読み込まれていません pocketmine.level.preparing=ワールド "{%0}" を読み込んでいます pocketmine.level.unloading=ワールド"{%0}"の書き込みをしています pocketmine.server.start=Minecraft: PEサーバー({%0}に対応)を起動しています pocketmine.server.networkError=[Network] {%1}によって{%0}のインターフェイスが停止しました pocketmine.server.networkStart={%0}:{%1}上でサーバーを開始しています pocketmine.server.info=このサーバーは{%0}のバージョン{%1}「{%2}」(API {%3})で動作しています pocketmine.server.info.extended.title=-----サーバーの情報----- pocketmine.server.info.extended1=このサーバーは {%0}{%1} を実行しています (コードネーム "{%2}") pocketmine.server.info.extended2=PHPバージョン: {%0} pocketmine.server.info.extended3=API: {%0} (iTX APIバージョン {%1}) pocketmine.server.info.extended4=目的のクライアント: Minecraft PE {%0} (プロトコルバージョン {%1}) pocketmine.server.license={%0}はLGPLライセンスに基づき配布されています pocketmine.server.tickOverload=注意! サーバーが高負荷になっている可能性があります pocketmine.server.startFinished=起動完了({%0}秒)! "help"または"?"でヘルプを表示 pocketmine.server.defaultGameMode=デフォルトゲームタイプ: {%0} pocketmine.server.query.start=GS4ステータス リスナーを開始 pocketmine.server.query.info=クエリポートを設定: {%0} pocketmine.server.query.running=クエリーは {%0}:{%1} で動作しています pocketmine.command.alias.illegal=無効な文字が含まれているため、{%0}を登録できませんでした pocketmine.command.alias.notFound=存在しないコマンドが含まれているため、{%0}を登録できませんでした: {%1} pocketmine.command.exception=コマンド'{%0}'を{%1}で実行中に、処理できない例外が発生:{%2} pocketmine.command.plugins.description=サーバー上で実行されているプラグインを一覧にして表示します pocketmine.command.plugins.success=プラグイン ({%0}): {%1} pocketmine.command.plugins.usage=/plugins pocketmine.command.reload.description=サーバーの設定やプラグインを再読み込みします pocketmine.command.reload.usage=/reload pocketmine.command.reload.reloading=サーバーを再読み込みしています... pocketmine.command.reload.reloaded=再読み込みが完了しました pocketmine.command.status.description=サーバーのパフォーマンスを読み出します pocketmine.command.status.usage=/status pocketmine.command.status.title=サーバーステータス pocketmine.command.status.player=プレイヤー人数: pocketmine.command.status.days=日数 pocketmine.command.status.hours=時間 pocketmine.command.status.minutes=分 pocketmine.command.status.seconds=秒 pocketmine.command.status.uptime=起動時間: pocketmine.command.status.AverageTPS=平均のTPS: pocketmine.command.status.CurrentTPS=現在のTPS: pocketmine.command.status.Networkupload=ネットワークアップロード: pocketmine.command.status.Networkdownload=ネットワークダウンロード: pocketmine.command.status.Threadcount=スレッド数: pocketmine.command.status.Mainmemory=メインスレッド数: pocketmine.command.status.Totalmemory=合計のメモリ使用量: pocketmine.command.status.Totalvirtualmemory=合計の仮想メモリ使用量: pocketmine.command.status.Heapmemory=ヒープ領域: pocketmine.command.status.Maxmemorysystem=最大メモリ (システム): pocketmine.command.status.Maxmemorymanager=最大メモリ (マネージャー): pocketmine.command.status.World=ワールド pocketmine.command.status.chunks=チャンク, pocketmine.command.status.entities=エンティティ, pocketmine.command.status.tiles=タイル. pocketmine.command.status.Time=時間 pocketmine.command.status.ms=ms pocketmine.command.gc.description=ガベージコレクションのタスクを起動 pocketmine.command.gc.usage=/gc pocketmine.command.gc.title=コレクションレポート pocketmine.command.gc.chunks=チャンク: pocketmine.command.gc.entities=エンティティ: pocketmine.command.gc.tiles=タイル: pocketmine.command.gc.cycles=サイクル: pocketmine.command.gc.memory=解放されたメモリ: pocketmine.command.timings.description=サーバーのパフォーマンスを確認する記録のタイミングを設定します pocketmine.command.timings.usage=/timings pocketmine.command.timings.enable=タイミングをリセット及び有効にしました pocketmine.command.timings.disable=タイミングを無効にしました pocketmine.command.timings.timingsDisabled=/timings と入力してタイミングを有効にしてください pocketmine.command.timings.reset=タイミングをリセットしました pocketmine.command.timings.pasteError=レポートのペースト中にエラーが発生しました pocketmine.command.timings.timingsUpload=タイミングを{%0}にアップロードします pocketmine.command.timings.timingsRead=結果は {%0} で見ることができます pocketmine.command.timings.timingsWrite=タイミングを {%0} に書き込んでいます pocketmine.command.version.description=使用しているプラグインを含めたこのサーバーのバージョンを取得します pocketmine.command.version.usage=/version [プラグイン名] pocketmine.command.version.noSuchPlugin=このサーバーではその名前のどのプラグインも実行しまいません プラグインリストを取得するには /plugins を使用してください pocketmine.command.give.description=指定したプレイヤーに一定量のアイテムを付与します pocketmine.command.give.usage=/give <プレイヤー名> <アイテム[:ダメージ値]> [量] [タグ] pocketmine.command.kill.description=自殺または他のプレイヤーを殺すことができます pocketmine.command.kill.usage=/kill [プレイヤー名] pocketmine.command.particle.description=ワールドにパーティクルを追加します pocketmine.command.particle.usage=/particle <パーティクル名> [量] [データ値] pocketmine.command.time.description=それぞれのワールドの時間を変更します pocketmine.command.time.usage=/time <値> または /time pocketmine.command.bancidbyname.description=指定したCIDをプレイヤー名によってサーバーの使用を拒否します pocketmine.command.bancid.description=指定したCIDからのサーバーの使用を拒否します pocketmine.command.banipbyname.description=指定したIPアドレスをプレイヤー名によってサーバーの使用を拒否します pocketmine.command.ban.player.description=指定したプレーヤーのサーバーの使用を拒否します pocketmine.command.ban.ip.description=指定したIPアドレスからのサーバーの使用を拒否します pocketmine.command.banlist.description=このサーバーでBANされたすべてのプレイヤーを表示します pocketmine.command.defaultgamemode.description=デフォルトのゲームモードを設定します pocketmine.command.deop.description=指定プレイヤーのOP権限を剥奪します pocketmine.command.difficulty.description=ゲーム難易度を設定します pocketmine.command.enchant.description=アイテムにエンチャントを付加します pocketmine.command.effect.description=プレイヤーに効果を付与/削除します pocketmine.command.gamemode.description=プレイヤーのゲームモードを変更します pocketmine.command.help.description=ヘルプメニューを表示します pocketmine.command.kick.description=指定したプレイヤーをサーバーから追い出します pocketmine.command.list.description=サーバーに接続しているプレイヤーを一覧表示します pocketmine.command.me.description=チャットで指定したアクションを実行します pocketmine.command.op.description=指定したプレイヤーにOP権限を付与します pocketmine.command.unban.cid.description=指定したCIDのBANを解除します pocketmine.command.unban.player.description=指定したプレイヤーのBANを解除します pocketmine.command.unban.ip.description=指定したIPアドレスのBANを解除します pocketmine.command.save.description=サーバーを保存します pocketmine.command.saveoff.description=サーバーの自動保存を無効にします pocketmine.command.saveon.description=サーバーの自動保存を有効にします pocketmine.command.say.description=送信者として指定したメッセージを送信します pocketmine.command.seed.description=ワールドのシード値を表示します pocketmine.command.setworldspawn.description=ワールドのスポーン地点を設定します 座標が指定されていない場合はプレイヤーの現在地の座標が使用されます pocketmine.command.spawnpoint.description=プレイヤーのスポーン地点を設定します pocketmine.command.stop.description=サーバーを停止します pocketmine.command.tp.description=他のプレイヤーまたは指定した座標にプレイヤー(または自分自身)をテレポートさせます pocketmine.command.tell.description=指定したプレイヤーにプライベートメッセージを送信します pocketmine.command.xp.description=指定プレイヤーに経験値またはレベルを増加する pocketmine.command.summon.description=プレーヤーの場所または特定の場所での実体を召喚 pocketmine.command.fill.description=指定した範囲を、指定するブロックIDで置換します。 pocketmine.command.setblock.description=指定した座標にブロックを配置します。 pocketmine.command.weather.description=ワールドの天気をセットします pocketmine.command.weather.usage=/weather pocketmine.command.weather.changed=ワールド {%0} で天気の変更に成功しました! pocketmine.command.weather.noregistered=ワールド {%0} はWeatherManagerに登録されていません pocketmine.command.weather.invalid=不正な天気。(0,1,2,3) pocketmine.command.weather.wrong=間違ったパラメーター. pocketmine.command.weather.invalid.level=不正なワールド名。 pocketmine.command.whitelist.description=このサーバーの利用を許可するプレイヤーリストを管理します pocketmine.crash.create=リカバリー不可能なエラーが発生し、サーバーがクラッシュ(動作を停止)しました。クラッシュダンプを作成しました pocketmine.crash.error=クラッシュダンプの作成に失敗: {%0} pocketmine.crash.submit=「{%0}」のファイルをクラッシュアーカイブにアップロードし、そのリンクを送ってください 出来る限り多くの情報を記載してください pocketmine.crash.archive=クラッシュダンプが自動的にクラッシュアーカイブに提出されました {%0}で閲覧またはID #{%1}を使用可能です pocketmine.debug.enable=LevelDB形式の対応を有効にしました pocketmine.player.invalidMove={%0} が正しく移動できませんでした! pocketmine.player.logIn={%0}[/{%1}:{%2}] [クライアントID: {%3}] エンティティId {%4} として{%9}で ({%5}, {%6}, {%7}, {%8}) に入室しました。 pocketmine.player.logOut={%0}[/{%1}:{%2}] は {%3} で退出しました。 pocketmine.player.transferred={%0}[/{%1}:{%2}] は {%3}に転送されました。 pocketmine.player.invalidEntity={%0} は無効なエンティティに攻撃しました pocketmine.plugin.load={%0} を読み込み中... pocketmine.plugin.enable={%0}を有効にしています… pocketmine.plugin.disable={%0}を無効にしています… pocketmine.plugin.restrictedName=不審な名前 pocketmine.plugin.incompatibleAPI=互換性がないAPIバージョン pocketmine.plugin.unknownDependency=不明な依存関係 pocketmine.plugin.circularDependency=循環依存が検出されました pocketmine.plugin.genericLoadError=プラグイン'{%0}'の読み込みに失敗しました pocketmine.plugin.spacesDiscouraged=プラグイン'{%0}'の名前に空白が使用されていますが、これはおすすめしません pocketmine.plugin.loadError=プラグイン'{%0}'を読み込むことができませんでした: {%1} pocketmine.plugin.duplicateError=プラグイン'{%0}'を読み込むことができませんでした: 同じプラグインが存在します pocketmine.plugin.fileError=フォルダー'{%0}'内の'{%1}'を読み込むことができませんでした: {%2} pocketmine.plugin.commandError=プラグイン{%1}のコマンド{%0}を読み込むことができませんでした pocketmine.plugin.aliasError=プラグイン {%1} で、コマンド{%0} に対するエイリアスを登録できませんでした pocketmine.plugin.deprecatedEvent=プラグイン'{%0}'がメソッド'{%2}'に'{%1}'のリスナーを登録しましたが、イベントが廃止されました pocketmine.plugin.eventError="'{%1}'に'{%0}'Eventを渡すことができませんでした: {%3}での{%2}" ================================================ FILE: src/pocketmine/lang/locale/kor.ini ================================================ # Language file compatible with Minecraft: Pocket Edition identifiers # # A message doesn't need to be there to be shown correctly on the client. # Only messages shown in PocketMine itself need to be here language.name="한국어 | Johnmacrocraft가 번역, ParkChanSol이 도움" language.selected={%0} ({%1})(이)가 기본 언어로 설정되었습니다. multiplayer.player.joined={%0} 님이 게임에 접속했습니다 multiplayer.player.left={%0}님이 게임에서 나갔습니다 chat.type.text={%0} : {%1} chat.type.emote=* {%0} {%1} chat.type.announcement=[{%0}] {%1} chat.type.admin=[{%0}: {%1}] chat.type.achievement={%0}님이 업적 {%1}을(를) 달성했습니다 disconnectionScreen.notAuthenticated=Xbox 로그인이 필요합니다! disconnectionScreen.outdatedClient=오래된 클라이언트입니다! disconnectionScreen.outdatedServer=오래된 서버입니다! disconnectionScreen.serverFull=서버 최대 인원 수를 초과하였습니다. disconnectionScreen.noReason=서버와의 연결이 끊어졌습니다. disconnectionScreen.invalidSkin=올바르지 않거나 지원되지 않는 스킨입니다. disconnectionScreen.invalidName=올바르지 않거나 잘못된 형식의 이름입니다! death.fell.accident.generic={%0} 님이 높은 곳에서 떨어졌습니다 death.attack.inFire={%0} 님이 불에 타 죽었습니다 death.attack.onFire={%0} 님이 불에 타서 죽었습니다 death.attack.lava={%0} 님이 용암에 빠져 죽었습니다 death.attack.inWall={%0} 님이 벽에 끼어 질식사했습니다 death.attack.drown={%0} 님이 익사했습니다 death.attack.cactus={%0} 님이 가시에 찔려서 사망했습니다 death.attack.generic={%0} 님이 사망했습니다 death.attack.explosion={%0}님이 폭발로 인해 사망하셨습니다. death.attack.explosion.player={%0} 님이 {%1} 님에 의해 폭사했습니다 death.attack.magic={%0}님이 마법에 의해 죽었습니다 death.attack.wither={%0} 님이 말라서 죽었습니다 death.attack.mob={%0} 님이 {%1} 에게 살해당했습니다 death.attack.player={%0} 님이 {%1} 에게 살해당했습니다 death.attack.player.item={%0} 님이 {%1} 님에게 {%2} (으)로 살해당했습니다 death.attack.arrow={%0} 님이 {%1} 님에게 저격당했습니다 death.attack.arrow.item={%0}님이 {%1}님에게 {%2}(으)로 저격당했습니다. death.attack.fall={%0} 님이 너무 높은 곳에서 떨어져 사망했습니다 death.attack.outOfWorld={%0} 님이 세계 밖으로 떨어져서 사망했습니다 gameMode.survival=서바이벌 모드 gameMode.creative=크리에이티브 모드 gameMode.adventure=모험 모드 gameMode.spectator=관전 모드 gameMode.changed=게임 모드가 변경되었습니다 potion.moveSpeed=신속 potion.moveSlowdown=구속 potion.digSpeed=성급함 potion.digSlowDown=피로 potion.damageBoost=힘 potion.heal=즉시 회복 potion.harm=즉시 데미지 potion.jump=점프 강화 potion.confusion=멀미 potion.regeneration=재생 potion.resistance=저항 potion.fireResistance=화염 저항 potion.waterBreathing=수중 호흡 potion.invisibility=투명화 potion.blindness=실명 potion.nightVision=야간 투시 potion.hunger=허기 potion.weakness=나약함 potion.poison=독 potion.wither=위더 potion.healthBoost=체력 강화 potion.absorption=흡수 potion.saturation=포화 commands.generic.exception=이 명령을 수행하는 동안 알 수 없는 오류가 발생했습니다 commands.generic.permission=이 명령을 사용할 수 있는 권한이 없습니다 commands.generic.notFound=알 수 없는 명령입니다. /help 로 명령어 목록을 보세요 commands.generic.player.notFound=플레이어를 찾을 수 없습니다 commands.generic.usage=사용법: {%0} commands.generic.level=레벨 이름 commands.generic.seed=시드 이름 commands.generic.name=이름 commands.generic.generator=생성기 이름 commands.generic.opt.missing=필요한 속성이 없습니다. 확인하고 다시 입력해 주세요. commands.generic.runingame=게임 안에서 이 명령어를 사용해 주세요. commands.time.added=시간을 {%0}만큼 추가하였습니다 commands.time.set={%0}으로 시간을 설정하였습니다 commands.time.query=현재 시간은 {%0}입니다 commands.me.usage=/me <행동> commands.give.item.notFound={%0}의 이름을 가진 아이템이 없습니다 commands.give.success={%2}님에게 {%0}을(를) {%1}개 주었습니다 commands.give.tagError=데이터 태그 분석 실패: {%0} commands.effect.usage=/effect <유저명> <효과명> [초] [증폭] [입자 숨김] 또는 /effect <유저명> clear (효과 없애기) commands.effect.notFound=효과 ID {%0}는 존재하지 않습니다 commands.effect.success=효과 {%0} (ID {%1} * {%2})를 {%3}에게 {%4}초간 주었습니다 commands.effect.success.removed= {%1}님에게서 효과 {%0}을(를) 제거하였습니다. commands.effect.success.removed.all={%0} 님의 모든 효과를 제거하였습니다. commands.effect.failure.notActive={%1}님에게 {%0}효과를 뺏을 수 없습니다, 어떤 효과도 걸려있지 않습니다. commands.effect.failure.notActive.all={%0}님에게 어떠한 효과도 걸려있지 않기 때문에 효과를 뺏을 수 없습니다 commands.enchant.noItem=대상이 아이템을 들고 있지 않아 마법을 부여 할 수 없습니다 commands.enchant.notFound=ID가 {%0} 인 마법 부여 효과는 없습니다 commands.enchant.success=마법 부여를 완료하였습니다 commands.enchant.cantEnchant=선택한 마법 부여는 대상 아이템에 추가될 수 없습니다 commands.enchant.usage=/enchant <플레이어> <마법 부여 ID> [레벨] commands.particle.success={%0}효과를 {%1} 초간 지속합니다 commands.particle.notFound={%0}는 알 수 없는 효과명입니다 commands.players.usage=/list commands.players.list=현재 {%0}/{%1} 명이 접속 중입니다: commands.kill.successful={%0}님을 죽였습니다 commands.banlist.ips=총 %d개의 차단된 IP 주소들이 있습니다: commands.banlist.players=총 {%0}명의 차단된 플레이어가 있습니다: commands.banlist.cids=총 {%0}개의 차단된 CID들이 있습니다: commands.banlist.usage=/banlist [아이피|플레이어 이름] commands.defaultgamemode.usage=/defaultgamemode <게임 모드> commands.defaultgamemode.success=기본 게임모드가 {%0}로 변경되었습니다. commands.op.success={%0}을 OP로 설정하였습니다 commands.op.usage=/op <플레이어> commands.deop.success={%0}님의 OP 권한을 박탈하였습니다 commands.deop.usage=/deop <플레이어> commands.say.usage=/say <메시지 ...> commands.seed.usage=/seed commands.seed.success=시드: {%0} commands.bancidbyname.success=플레이어 {%0}님의 CID를 차단하였습니다 commands.bancidbyname.usage=/bancidbyname <플레이어 이름> commands.bancid.success=CID 차단: {%0} commands.bancid.usage=/bancid <클라이언트 ID> commands.unbancid.usage=/pardoncid <클라이언트 ID> commands.unbancid.success=CID {%0}의 차단을 해제하였습니다 commands.ban.success=플레이어 {%0}를 차단하였습니다 commands.ban.usage=/ban <이름> [사유 ...] commands.unban.success=플레이어 {%0}님을 차단 해제하였습니다 commands.unban.usage=/pardon <차단 해제할 플레이어 이름> commands.banip.invalid=잘못된 IP 주소이거나 현재 접속해있지 않은 플레이어입니다 commands.banip.success=IP 주소 {%0}을(를) 차단하였습니다 commands.banip.success.players={%1}님의 IP 주소 {%0}을(를) 차단하였습니다 commands.banip.usage=/ban-ip [사유 ...] commands.unbanip.invalid=잘못된 IP 주소를 입력하였습니다 commands.unbanip.success=IP 주소 {%0}의 차단을 해제하였습니다 commands.unbanip.usage=/pardon-ip commands.banipbyname.success=플레이어 {%0}님의 IP를 차단하였습니다 commands.banipbyname.usage=/banipbyname <플레이어 이름> commands.save.usage=/save-all commands.save-on.usage=/save-on commands.save-off.usage=/save-off commands.save.enabled=월드 자동 저장 기능을 활성화하였습니다 commands.save.disabled=월드 자동 저장 기능을 비활성화하였습니다 commands.save.start=월드를 저장 중... commands.save.success=월드를 저장했습니다 commands.setblock.usage=/setblock <블럭 이름> [손상 정도] command.setblock.invalidBlock=잘못된 블럭 이름/ID입니다 commands.stop.usage=/stop commands.stop.start=서버를 끄는 중입니다 commands.kick.success={%0}을 게임에서 강제 추방시켰습니다 commands.kick.success.reason={%0}님이 강제 추방되셨습니다. (사유: {%1}) commands.kick.usage=/kick <플레이어> [사유 ...] commands.tp.success={%0}을(를) {%1}(으)로 이동시켰습니다 commands.tp.success.coordinates={%0}님을 X:{%1}, Y:{%2}, Z:{%3}(으)로 이동시켰습니다 commands.tp.usage=/tp [이동시킬 플레이어] <도착 지점 플레이어> 또는 /tp [이동시킬 플레이어] [ ] commands.whitelist.list=(보여진 {%1} 중) {%0}명의 화이트리스트 된 플레이어가 있습니다: commands.whitelist.enabled=화이트리스트 기능을 활성화하였습니다 commands.whitelist.disabled=화이트리스트 기능을 비활성화하였습니다 commands.whitelist.reloaded=화이트리스트를 새로고침하였습니다. commands.whitelist.add.success={%0}님을 화이트리스트에 추가하였습니다 commands.whitelist.add.usage=/whitelist add <플레이어> commands.whitelist.remove.success={%0}님을 화이트리스트에서 삭제하였습니다 commands.whitelist.remove.usage=/whitelist remove <플레이어명> commands.whitelist.usage=/whitelist commands.gamemode.success.self=자신의 게임 모드를 {%2}으로 설정하였습니다 commands.gamemode.success.other={%1}님의 게임 모드를 {%2}로 설정했습니다 commands.gamemode.usage=/gamemode <모드> [플레이어명] commands.help.header=--- 전체 페이지 {%1} 중 {%0}번째 페이지 (/help <페이지>) --- commands.help.usage=/help [페이지|명령어] commands.message.usage=/tell <플레이어> <비밀 메시지 ...> commands.message.sameTarget=자기 자신에게 비밀 메시지를 보낼 수 없습니다! commands.difficulty.usage=/difficulty <난이도> commands.difficulty.success=게임 난이도를 {%0}(으)로 설정하였습니다 commands.spawnpoint.usage=/spawnpoint [플레이어] [ ] commands.spawnpoint.success={%0}의 스폰 위치를 ({%1}, {%2}, {%3})로 설정하였습니다 commands.setworldspawn.usage=/setworldspawn [ ] commands.setworldspawn.success=월드의 스폰 지점을 ({%0}, {%1}, {%2})로 설정하였습니다 commands.summon.usage=/summon [엔티티] [ ] [NBTTag] commands.xp.failure.withdrawXp=0보다 작은 경험치는 줄 수 없습니다 commands.xp.success=%s에게 경험치 %s을(를) 주었습니다 commands.xp.success.levels=%s에게 %s 레벨이 지급되었습니다 commands.xp.success.negative.levels=%s의 레벨을 %s 낮추었습니다 commands.xp.usage=/xp <수량> [플레이어] 또는 /xp <수량>L [플레이어] # -------------------- PocketMine language files, only for console -------------------- pocketmine.data.playerNotFound=플레이어 "{%0}"님의 데이터를 찾지 못했습니다. 새 프로필을 생성합니다. pocketmine.data.playerCorrupted=플레이어 "{%0}"님의 데이터가 손상되었습니다. 새로운 프로필을 생성합니다. pocketmine.data.playerOld=플레이어 "{%0}"님의 데이터가 오래되었습니다. 프로필을 업데이트합니다. pocketmine.data.saveError=플레이어 "{%0}"님의 데이터를 저장하지 못했습니다: {%1} pocketmine.level.notFound="{%0}" 월드를 찾지 못했습니다 pocketmine.level.loadError=월드 "{%0}"를(을) 불러오지 못했습니다: {%1} pocketmine.level.generationError=월드 "{%0}"를(을) 생성하지 못했습니다: {%1} pocketmine.level.tickError="{%0}" 월드의 틱을 처리하지 못했습니다: {%1} pocketmine.level.chunkUnloadError=청크 언로드 중 오류가 발생했습니다: {%0} pocketmine.level.backgroundGeneration=월드 "{%0}"의 스폰 지형이 백그라운드에서 생성됩니다... pocketmine.level.defaultError=기본 월드를 찾을 수 없습니다 pocketmine.level.preparing=월드 "{%0}"를(을) 준비 중입니다... pocketmine.level.unloading=월드 "{%0}"를(을) 언로드 중입니다... pocketmine.server.start=마인크래프트: 포켓 에디션 (버전 {%0}용) 서버를 시작 중입니다... pocketmine.server.networkError=[Network] 인터페이스 {%0}가 {%1}로 인해 작동 중단되었습니다. pocketmine.server.networkStart=서버를 {%0}:{%1}에서 여는 중입니다 pocketmine.server.info=이 서버는 {%0} (버전 {%1} "{%2}" (API {%3}))을 구동 중입니다 pocketmine.server.info.extended.title=-----서버 정보----- pocketmine.server.info.extended1=이 서버는 {%0}{%1} (코드네임 "{%2}")을 구동 중입니다 pocketmine.server.info.extended2=PHP 버전: {%0} pocketmine.server.info.extended3=API: {%0} (iTX API 버전 {%1}) pocketmine.server.info.extended4=대상 클라이언트: Minecraft PE {%0} (프로토콜 버전 {%1}) pocketmine.server.license={%0}는(은) LGPL 라이센스 하에서 배포됩니다 pocketmine.server.tickOverload=서버 처리 속도가 느려졌습니다. 서버가 과부하되었는지 확인해주시기 바랍니다. pocketmine.server.startFinished=서버 구동 준비 완료! :D ({%0}초) 명령어 목록을 확인하시려면 "help" 또는 "?"를 입력해 주세요. pocketmine.server.defaultGameMode=기본 게임 모드: {%0} pocketmine.server.query.start=GS4 쿼리 리스너를 시작 중입니다 pocketmine.server.query.info=쿼리 포트를 {%0}로 설정합니다 pocketmine.server.query.running=쿼리 서버가 {%0}:{%1}에서 실행 중입니다 pocketmine.command.alias.illegal=명령어 별칭 {%0}을 등록하지 못했습니다. 잘못된 문자를 포함하고 있습니다. pocketmine.command.alias.notFound=명령어 별칭 {%0}을 등록하지 못했습니다. 존재하지 않는 명령어를 포함합니다: {%1} pocketmine.command.exception=플러그인 {%1}에서 명령어 '{%0}'을 실행하는 중 예외가 발생했습니다: {%2} pocketmine.commands.cave.usage=/cave <회전 각도> <길이> <지점 번호> <강도> <레벨 이름> | /cave getmypos pocketmine.commands.cave.info=회전 각도:{%0} 길이:{%1} 지점 번호:{%2} 강도:{%3} pocketmine.commands.cave.start=동굴을 생성하는 중입니다. 잠시만 기다리세요 pocketmine.commands.cave.success=동굴이 생성되었습니다 pocketmine.command.plugins.description=서버에서 실행 중인 플러그인 목록을 불러옵니다. pocketmine.command.plugins.success=플러그인 목록 (전체 {%0}개): {%1} pocketmine.command.plugins.usage=/plugins pocketmine.command.reload.description=서버 설정과 플러그인을 다시 불러옵니다. pocketmine.command.reload.usage=/reload pocketmine.command.reload.reloading=서버를 다시 불러오는 중입니다... pocketmine.command.reload.reloaded=서버 설정을 다시 불러왔습니다. pocketmine.command.lvdat.description=맵의 속성을 변경합니다. pocketmine.command.lvdat.changed=레벨 "{%0}"의 {%1}(을)를 변경하였습니다. 일부 변경은 서버 재부팅이 필요합니다. pocketmine.command.lvdat.fixname=성공적으로 레벨 "{%0}"의 이름을 변경하였습니다. 일부 변경은 서버 재부팅이 필요합니다. pocketmine.command.lvdat.nofound=레벨 "{%0}"이 없거나 로드하지 못했습니다. pocketmine.command.lvdat.preset=생성기 설정 (프리셋) pocketmine.command.status.description=서버의 현재 상태를 확인합니다 (TPS 등) pocketmine.command.status.usage=/status pocketmine.command.status.title=서버 상태 pocketmine.command.status.player=플레이어 수: pocketmine.command.status.days=일 pocketmine.command.status.hours=시간 pocketmine.command.status.minutes=분 pocketmine.command.status.seconds=초 pocketmine.command.status.uptime=작업 시간: pocketmine.command.status.AverageTPS=평균 TPS: pocketmine.command.status.CurrentTPS=현재 TPS: pocketmine.command.status.Networkupload=네트워크 업로드: pocketmine.command.status.Networkdownload=네트워크 다운로드: pocketmine.command.status.Threadcount=스레드 수: pocketmine.command.status.Mainmemory=메인 스레드 메모리: pocketmine.command.status.Totalmemory=총 메모리: pocketmine.command.status.Totalvirtualmemory=총 가상 메모리: pocketmine.command.status.Heapmemory=힙 메모리: pocketmine.command.status.Maxmemorysystem=최대 메모리 (시스템): pocketmine.command.status.Maxmemorymanager=최대 메모리 (관리자): pocketmine.command.status.World=월드 pocketmine.command.status.chunks=청크, pocketmine.command.status.entities=엔티티, pocketmine.command.status.tiles=타일. pocketmine.command.status.Time=시간 pocketmine.command.status.ms=ms pocketmine.command.gc.description=가비지 컬렉션 작업을 시작합니다 pocketmine.command.gc.usage=/gc pocketmine.command.gc.title=모음 보고서 pocketmine.command.gc.chunks=청크: pocketmine.command.gc.entities=엔티티: pocketmine.command.gc.tiles=타일: pocketmine.command.gc.cycles=주기: pocketmine.command.gc.memory=릴리스 메모리: pocketmine.command.biome.description=영역의 바이옴을 변경합니다.(눈 또는 비 변경) pocketmine.command.biome.posset=위치 {%3}(을)를 다음 위치로 설정하였습니다:({%1},{%2})[{%0}] pocketmine.command.biome.get=현재 당신이 계신 바이옵의 ID는 {%0}입니다. 색상:{%1},{%2},{%3} pocketmine.command.biome.wrongLev=포인트를 각자 다른 레벨에서 설정할 수 없습니다. pocketmine.command.biome.wrongBio=잘못된 바이옴의 ID입니다. e.g. 1 (평야), 2 (사막),13 (얼음 산),6 (습지대) pocketmine.command.biome.wrongCol=잘못된 색입니다. e.g. 146,188,89 ."/biome get"을 사용하여 다른 색을 가져오세요. pocketmine.command.biome.noPos=영역을 선택하려면 먼저 "/biome pos1|pos2"를 사용해주세요. pocketmine.command.biome.set=바이옴을 다음으로 설정하였습니다:{%0} pocketmine.command.biome.color=색을 다음으로 설정하였습니다:{%0},{%1},{%2} pocketmine.command.timings.description=서버 성능 확인을 위해 타이밍을 기록합니다. pocketmine.command.timings.usage=/timings pocketmine.command.timings.enable=타이밍 & 초기화 기능을 활성화하였습니다 pocketmine.command.timings.disable=타이밍이 비활성화되었습니다. pocketmine.command.timings.timingsDisabled=/timings on를 사용하여 타이밍 기능을 활성화해주세요. pocketmine.command.timings.reset=타이밍을 초기화하였습니다 pocketmine.command.timings.pasteError=보고 목록에 붙이는 동안 에러가 발생 하였습니다. pocketmine.command.timings.timingsUpload=타이밍이 {%0}로 업로드가 되었습니다. pocketmine.command.timings.timingsRead=당신은 {%0} 에서 결과를 읽을수 있습니다. pocketmine.command.timings.timingsWrite=타이밍이 {%0}에 써졌습니다. pocketmine.command.version.description=현재 구동되고 있는 서버 버전과 플러그인 정보를 출력합니다. pocketmine.command.version.usage=/version [플러그인 이름] pocketmine.command.version.noSuchPlugin=입력하신 플러그인을 찾을 수 없습니다. /plugins를 입력해 플러그인 목록을 확인하세요. pocketmine.command.give.description=해당 플레이어에게 아이템을 지정한 양만큼 줍니다 pocketmine.command.give.usage=/give <플레이어> <아이템[:손상 정도]> [양] [태그...] pocketmine.command.kill.description=자신 또는 다른 플레이어를 죽입니다 pocketmine.command.kill.usage=/kill [죽일 플레이어] pocketmine.command.particle.description=월드에 파티클을 추가합니다. pocketmine.command.particle.usage=/particle <이름> [양] [데이터] pocketmine.command.time.description=월드 시간을 변경합니다. pocketmine.command.time.usage=/time <값> 또는 /time pocketmine.command.bancidbyname.description=지정된 플레이어의 CID가 서버에 접속할 수 없게 차단합니다 pocketmine.command.bancid.description=지정된 CID가 서버에 들어오지 못하게 차단합니다 pocketmine.command.banipbyname.description=지정된 플레이어의 IP 주소가 서버에 접속할 수 없게 차단합니다 pocketmine.command.ban.player.description=지정된 플레이어가 서버에 들어오지 못하게 차단합니다 pocketmine.command.ban.ip.description=지정된 IP 주소가 서버에 접속할 수 없게 차단합니다 pocketmine.command.banlist.description=이 서버에서 차단한 모든 플레이어 목록을 보여줍니다. pocketmine.command.defaultgamemode.description=기본 게임모드를 설정합니다 pocketmine.command.deop.description=지정된 플레이어의 OP 권한을 해제합니다 pocketmine.command.difficulty.description=게임의 난이도를 설정합니다. pocketmine.command.enchant.description=아이템에 마법을 부여합니다 pocketmine.command.effect.description=플레이어에게 효과를 부여하거나 제거합니다. pocketmine.command.gamemode.description=지정된 플레이어의 게임 모드를 변경합니다 pocketmine.command.help.description=모든 명령어 목록을 보여줍니다. pocketmine.command.kick.description=해당 플레이어를 서버에서 퇴장시킵니다 pocketmine.command.list.description=현재 접속 중인 플레이어의 목록을 보여줍니다 pocketmine.command.me.description=채팅에서 지정된 동작을 합니다 pocketmine.command.op.description=지정된 플레이어에게 OP 권한을 부여합니다 pocketmine.command.unban.cid.description=지정된 CID가 서버를 이용할 수 있도록 차단 해제합니다 pocketmine.command.unban.player.description=해당 플레이어의 차단을 해제합니다. pocketmine.command.unban.ip.description=지정된 IP 주소가 서버를 이용할 수 있도록 차단 해제합니다 pocketmine.command.save.description=서버를 디스크에 저장합니다 pocketmine.command.saveoff.description=서버 자동 저장을 비활성화합니다 pocketmine.command.saveon.description=서버 자동 저장을 활성화합니다 pocketmine.command.say.description=입력된 메시지를 서버 전체에 표시합니다 pocketmine.command.seed.description=월드의 시드를 보여줍니다. pocketmine.command.setworldspawn.description=월드의 부활 지점을 지정합니다. 좌표를 입력하지 않으면 현재 플레이어의 좌표로 지정됩니다. pocketmine.command.spawnpoint.description=플레이어의 부활 지점을 정합니다 pocketmine.command.stop.description=서버를 정지합니다 pocketmine.command.tp.description=해당 플레이어 (또는 자신을) 지정한 플레이어나 좌표로 이동시킵니다 pocketmine.command.tell.description=해당 플레이어에게 개인 메세지를 보냅니다 pocketmine.command.xp.description=지정한 플레이어에게 경험치 또는 경험치 레벨을 줍니다 pocketmine.command.summon.description=플레이어의 위치나 지정된 위치에 엔티티를 생성합니다 pocketmine.command.fill.description=지정한 영역을 블럭으로 채웁니다 pocketmine.command.setblock.description=블럭을 다른 블럭으로 변경합니다 pocketmine.command.weather.description=레벨의 날씨를 설정합니다 pocketmine.command.weather.usage=/weather <레벨 이름 또는 날씨|날씨 (rain|sunny|clear)> pocketmine.command.weather.changed=성공적으로 레벨 {%0}의 날씨를 변경하였습니다! pocketmine.command.weather.noregistered=레벨 {%0}(은)는 날씨 관리자에 등록되지 않았습니다. pocketmine.command.weather.invalid=잘못된 날씨입니다.(0,1,2,3) pocketmine.command.weather.wrong=잘못된 매개 변수 입니다. pocketmine.command.weather.invalid.level=올바르지 않은 레벨 이름입니다. pocketmine.command.whitelist.description=서버를 이용할 수 있는 플레이어 목록을 관리합니다 pocketmine.crash.create=복구가 불가능한 오류가 발생하여 서버가 강제로 종료되었습니다. 크래시 덤프를 생성합니다. pocketmine.crash.error=크래시 덤프를 생성하지 못했습니다: {%0} pocketmine.crash.submit="{%0}"파일을 Crash Archive에 업로드하고 버그 리포팅 페이지에 링크를 보내 주세요. 가능한 한 많은 정보를 주시면 감사하겠습니다. pocketmine.crash.archive=크래시 덤프가 자동으로 Crash Archive에 전송되었습니다. {%0}에서 확인하거나 ID #{%1}을 사용하세요. pocketmine.debug.enable=LevelDB 지원이 활성화되었습니다 pocketmine.player.invalidMove={%0}님이 잘못된 움직임을 보였습니다! pocketmine.player.logIn={%0}[/{%1}:{%2}] 이 엔티티 ID {%3} (으)로 ({%4}, {%5}, {%6}, {%7})에 접속했습니다 pocketmine.player.logOut={%0}[/{%1}:{%2}] 님의 연결이 끊어졌습니다. 이유: {%3} pocketmine.player.transferred={%0}[/{%1}:{%2}] 님이 {%3}로 옮겨졌습니다 pocketmine.player.invalidEntity={%0}이 유효하지 않은 엔티티를 공격하려 시도했습니다 pocketmine.plugin.load=플러그인 {%0}을(를) 로딩 중입니다. pocketmine.plugin.enable=플러그인 {%0}을(를) 활성화 중입니다 pocketmine.plugin.disable=플러그인 {%0}을(를) 비활성화 중입니다 pocketmine.plugin.restrictedName=제한된 이름입니다! pocketmine.plugin.incompatibleAPI=호환되지 않는 API 버전입니다! pocketmine.plugin.unknownDependency=알 수 없는 의존 플러그인입니다! pocketmine.plugin.circularDependency=의존성 플러그인입니다. pocketmine.plugin.genericLoadError=플러그인 '{%0}'을(를) 로드하지 못했습니다 pocketmine.plugin.spacesDiscouraged=플러그인 '{%0}'의 이름에 공백 문자가 섞여 있습니다. 이는 권장되지 않습니다. pocketmine.plugin.loadError=플러그인 '{%0}'을(를) 로드하지 못했습니다: {%1} pocketmine.plugin.duplicateError=플러그인 '{%0}'을(를) 로드하지 못했습니다: 이미 존재하는 플러그인입니다 pocketmine.plugin.fileError=폴더 '{%1}'에서 플러그인 '{%0}'을(를) 로드하지 못했습니다: {%2} pocketmine.plugin.commandError=명령어 {%0}을(를) 플러그인 {%1}에서 로드하지 못했습니다 pocketmine.plugin.aliasError=플러그인 {%1}의 명령어 별칭 {%0}을(를) 로드하지 못했습니다 pocketmine.plugin.deprecatedEvent=플러그인 '{%0}'이(가) 이벤트 '{%1}'을(를) 위한 리스너를 메서드 '{%2}'에 대해 추가했지만, 이 이벤트의 사용은 권장되지 않습니다. pocketmine.plugin.eventError="이벤트 '{%0}'을(를) 플러그인 '{%1}'에 전달하지 못했습니다: {%2} ({%3})" ================================================ FILE: src/pocketmine/lang/locale/rus.ini ================================================ # Language file compatible with Minecraft: Pocket Edition identifiers # # A message doesn't need to be there to be shown correctly on the client. # Only messages shown in PocketMine itself need to be here language.name=Russian language.selected=Выбран {%0} ({%1}) в качестве главного языка multiplayer.player.joined={%0} присоединился к игре multiplayer.player.left={%0} вышел из игры chat.type.text={%0} : {%1} chat.type.emote=* {%0} {%1} chat.type.announcement=[{%0}] {%1} chat.type.admin=[{%0}: {%1}] chat.type.achievement=Игрок {%0} получил достижение {%1}! disconnectionScreen.notAuthenticated=Требуется авторизация в Xbox! disconnectionScreen.outdatedClient=Устаревший клиент! disconnectionScreen.outdatedServer=Устаревший сервер! disconnectionScreen.serverFull=Сервер переполнен! disconnectionScreen.noReason=Отключен от сервера. disconnectionScreen.invalidSkin=Неверный формат скина! disconnectionScreen.invalidName=Недопустимое имя! death.fell.accident.generic={%0} упал с высокого места death.attack.inFire={%0} сгорел death.attack.onFire={%0} сгорел death.attack.lava={%0} решил поплавать в лаве death.attack.inWall={%0} задохнулся в стене death.attack.drown={%0} утонул death.attack.cactus={%0} закололся до смерти death.attack.generic={%0} умер death.attack.explosion={%0} взорвался death.attack.explosion.player={%0} был взорван {%1} death.attack.magic={%0} убит магией death.attack.wither={%0} иссушен death.attack.mob={%0} был убит {%1} death.attack.player={%0} был убит {%1} death.attack.player.item={%0} был убит {%1} используя {%2} death.attack.arrow={%0} был застрелен {%1} death.attack.arrow.item={%0} был застрелен {%1} используя {%2} death.attack.fall={%0} упал с высокого места death.attack.outOfWorld={%0} выпал из мира gameMode.survival=Режим выживания gameMode.creative=Творческий режим gameMode.adventure=Приключенческий режим gameMode.spectator=Режим наблюдателя gameMode.changed=Ваш игровой режим был обновлён potion.moveSpeed=Скорость potion.moveSlowdown=Замедление potion.digSpeed=Спешка potion.digSlowDown=Усталость potion.damageBoost=Сила potion.heal=Мгновенное лечение potion.harm=Мгновенный Урон potion.jump=Усиление прыжка potion.confusion=Тошнота potion.regeneration=Регенерация potion.resistance=Сопротивление урону potion.fireResistance=Огнестойкость potion.waterBreathing=Подводное дыхание potion.invisibility=Невидимость potion.blindness=Слепота potion.nightVision=Ночное зрение potion.hunger=Голод potion.weakness=Слабость potion.poison=Отравление potion.wither=Иссушение potion.healthBoost=Повышение здоровья potion.absorption=Поглощение potion.saturation=Насыщенность commands.generic.exception=Произошла неизвестная ошибка при выполнении команды commands.generic.permission=У Вас недостаточно прав для использования этой команды commands.generic.notFound=Неизвестная команда. Используйте /help для списка команд commands.generic.player.notFound=Игрок не найден commands.generic.usage=Использование: {%0} commands.generic.level=level-name commands.generic.seed=seed-name commands.generic.name=name commands.generic.generator=generator-name commands.generic.opt.missing=Требуемые настройки отсутствуют,пожалуйста повторите ввод. commands.generic.runingame=Используйте эту команду в игре. commands.time.added=Добавлено {%0} к времени commands.time.set=Установлено время {%0} commands.time.query=Время ‒ {%0} commands.me.usage=/me <действие ...> commands.give.item.notFound=Предмет с именем {%0} не найден commands.give.success=Дано {%0} * {%1} игроку {%2} commands.give.tagError=Разбор тега данных не удался: {%0} commands.effect.usage=/effect <игрок> <эффект> [кол-во секунд] [уровень] [убратьЧастицы] ИЛИ /effect <игрок> clear commands.effect.notFound=Не существует эффекта с ID {%0} commands.effect.success=Дано {%0} (ID {%1}) * {%2} to {%3} на {%4} секунд commands.effect.success.removed=Эффект {%0} убран у {%1} commands.effect.success.removed.all=Все эффекты были сняты с {%0} commands.effect.failure.notActive=Невозможно убрать {%0} у {%1}, поскольку игрок не имеет данного эффекта commands.effect.failure.notActive.all=Невозможно убрать эффекты у {%0}, потому что игрок не имеет никаких эффектов commands.enchant.noItem=У игрока нет такого предмета commands.enchant.notFound=Нет такого зачарования с ID {%0} commands.enchant.success=Зачарование прошло успешно commands.enchant.cantEnchant=This item cannot be enchanted commands.enchant.usage=/enchant <игрок> [уровень] commands.particle.success=Проигрываются частицы эффекта {%0} {%1} раз commands.particle.notFound=Неизвестный эффект {%0} commands.players.usage=/list commands.players.list=Сейчас {%0}/{%1} игроков на сервере: commands.kill.successful=Убит {%0} commands.banlist.ips=Всего заблокировано %d IP адресов : commands.banlist.players=Всего заблокирован(о) {%0} игрок(ов): commands.banlist.usage=/banlist [ips|players] commands.defaultgamemode.usage=/defaultgamemode <режим игры> commands.defaultgamemode.success=Игровой режим мира по умолчанию ‒ {%0} commands.op.success={%0} теперь оператор сервера commands.op.usage=/op <игрок> commands.deop.success={%0} больше не оператор сервера commands.deop.usage=/deop <игрок> commands.say.usage=/say <сообщение ...> commands.seed.usage=/seed commands.seed.success=Сид: {%0} commands.bancidbyname.success=Игрок {%0} заблокирован по номеру устройства commands.bancidbyname.usage=/bancidbyname <ИмяИгрока> commands.bancid.success=Заблокирован номер устройства: {%0} commands.bancid.usage=/bancid <НомерУстройства> commands.unbancid.usage=/pardoncid <НомерУстройства> commands.ban.success=Заблокирован игрок {%0} commands.ban.usage=/ban <ИмяИгрока> [причина ...] [время(день)] commands.unban.success=Разблокирован игрок {%0} commands.unban.usage=/pardon <имя> commands.banip.invalid=Вы ввели неправильный IP-адрес или данный игрок не в сети commands.banip.success=Заблокирован IP адрес {%0} commands.banip.success.players=Заблокирован IP адрес {%0} принадлежащий {%1} commands.banip.usage=/ban-ip [причина ...] commands.unbanip.invalid=Вы ввели неправильный IP адрес commands.unbanip.success=Разблокирован IP адрес {%0} commands.unbanip.usage=/pardon-ip <адрес> commands.banipbyname.success=Игрок с ником {%0} забанен по IP commands.banipbyname.usage=/banipbyname commands.save.usage=/save-all commands.save-on.usage=/save-on commands.save-off.usage=/save-off commands.save.enabled=Включено авто-сохранение commands.save.disabled=Автоматическое сохранение мира отключено commands.save.start=Сохранение... commands.save.success=Мир сохранён commands.setblock.usage=/setblock <ИмяБлока> [Урон] command.setblock.invalidBlock=Некорректное имя блока/айди commands.stop.usage=/stop commands.stop.start=Остановка сервера commands.kick.success=Кикнут {%0} с сервера commands.kick.success.reason=Кикнут {%0} с сервера: '{%1}' commands.kick.usage=/kick <игрок> [причина ...] commands.tp.success=Телепортирован {%0} к {%1} commands.tp.success.coordinates=Телепортирован {%0} на {%1}, {%2}, {%3} commands.tp.usage=/tp [целевой игрок] <назначенный игрок> или /tp [целевой игрок] [ ] commands.whitelist.list=В вайтлисте {%0} игроков (из {%1} отображаемых): commands.whitelist.enabled=Вайтлист включен commands.whitelist.disabled=Вайтлист выключен commands.whitelist.reloaded=Список разрешенных игроков перезагружен commands.whitelist.add.success={%0} добавлен в вайтлист commands.whitelist.add.usage=/whitelist add <игрок> commands.whitelist.remove.success={%0} убран из вайтлиста commands.whitelist.remove.usage=/whitelist remove <игрок> commands.whitelist.usage=/whitelist commands.gamemode.success.self=Установлен режим игры {%2} для себя commands.gamemode.success.other=Игровой режим игрока {%1} изменён на {%2} commands.gamemode.usage=/gamemode <игровой режим> [игрок] commands.help.header=--- Отображается страница {%0} из {%1} (/help <страница>) --- commands.help.usage=/help [страница|имя команды] commands.message.usage=/tell <игрок> <личное сообщение...> commands.message.sameTarget=Вы не можете отправить сообщение самому себе! commands.xp.usage=/xp <Опыт или Уровень опыта + Число> <Игрок> commands.difficulty.usage=/difficulty <сложность игры> commands.difficulty.success=Установлена сложность игры на {%0} commands.spawnpoint.usage=/spawnpoint [игрок] [ ] commands.spawnpoint.success=Установлена точка респауна игрока {%0} ({%1}, {%2}, {%3}) commands.setworldspawn.usage=/setworldspawn [ ] commands.setworldspawn.success=Установлен респаун мира на ({%0}, {%1}, {%2}) commands.summon.usage=/summon [сущность] [ ] [Имя] # -------------------- PocketMine language files, only for console -------------------- pocketmine.data.playerNotFound=Информация об игроке "{%0}" не найдена, создаётся новый профиль pocketmine.data.playerCorrupted=Повреждённые данные у игрока "{%0}", создание нового профиля pocketmine.data.playerOld=Обнаружен старый профиль игрока "{%0}", обновляем на новый pocketmine.data.saveError=Невозможно сохранить игрока "{%0}": {%1} pocketmine.level.notFound=Мир "{%0}" не найден pocketmine.level.loadError=Невозможно загрузить мир"{%0}": {%1} pocketmine.level.generationError=Невозможно сгенерировать уровень "{%0}": {%1} pocketmine.level.tickError=Ошибка прорисовки мира "{%0}": {%1} pocketmine.level.chunkUnloadError=Ошибка при выгрузке чанка: {%0} pocketmine.level.backgroundGeneration=Генерируется территория спауна для мира "{%0}" pocketmine.level.defaultError=Мир по умолчанию не загружен pocketmine.level.preparing=Подготовка мира "{%0}" pocketmine.level.unloading=Выгрузка мира "{%0}" pocketmine.server.start=Запускается сервер Minecraft: PE версии {%0} pocketmine.server.networkError=[Сеть] Остановлен интерфейс {%0} из-за {%1} pocketmine.server.networkStart=Открытие сервера на {%0}:{%1} pocketmine.server.info.extended=Сервер использует {%0} {%1}「 {%2}」 , версия API {%3} для Minecraft: PE {%4} (версия протокола {%5}) pocketmine.server.info=Этот сервер использует {%0}, версию {%1} "{%2}" (API {%3}) pocketmine.server.license={%0} распространяется под лицензией LGPL pocketmine.server.tickOverload=Сервер перегружен! pocketmine.server.startFinished=Загружено ({%0} секунд)! Для справки введите "help" или "?" pocketmine.server.defaultGameMode=Игровой режим по умолчанию: {%0} pocketmine.server.query.start=Запуск прослушивателя статуса GS4 pocketmine.server.query.info=Установлен порт query на {%0} pocketmine.server.query.running=Query запущен на {%0}:{%1} pocketmine.command.alias.illegal=Невозможно зарегистрировать альтернативу команды {%0}, поскольку он содержит недопустимые знаки pocketmine.command.alias.notFound=Невозможно зарегистрировать альтернативу команды {%0}, поскольку он содержит недопустимые команды: {%1} pocketmine.command.exception=Необработанное исключение при выполнении команды '{%0}' в {%1}: {%2} pocketmine.commands.cave.usage=/cave <угол_поворота> <длина> <Номер ветви> <прочность> <название_мира> | /cave getmypos pocketmine.commands.cave.info=Угол поворота:{%0} Длина:{%1} Номер ветви:{%2} Прочность:{%3} pocketmine.commands.cave.start=Начало создания пещеры, пожалуйста, подождите! pocketmine.command.plugins.description=Получить список установленых плагинов на этом сервере pocketmine.command.plugins.success=Плагины ({%0}): {%1} pocketmine.command.plugins.usage=/plugins pocketmine.command.reload.description=Перезагрузить настройки сервера и плагины pocketmine.command.reload.usage=/reload pocketmine.command.reload.reloading=Перезагрузка сервера... pocketmine.command.reload.reloaded=Перезагрузка завершена. pocketmine.command.lvdat.description=Изменить настройки карты. pocketmine.command.lvdat.changed=Был сменена настройка {%1} для мира "{%0}", для применения некоторых изменений нужна перезагрузка. pocketmine.command.lvdat.fixname=Имя для мира "{%0}" было успешно исправлено, для применения некоторых изменений нужна перезагрузка. pocketmine.command.lvdat.nofound=Мир "{%0}" не найден или не загружен. pocketmine.command.lvdat.preset=Настройки генератора (пресеты) pocketmine.command.status.description=Отображает производительность сервера. pocketmine.command.status.usage=/status pocketmine.command.status.title=Статус сервера pocketmine.command.status.player=Число игроков: pocketmine.command.status.days=дн. pocketmine.command.status.hours=ч pocketmine.command.status.minutes=мин pocketmine.command.status.seconds=сек pocketmine.command.status.uptime=Аптайм: pocketmine.command.status.AverageTPS=Средний TPS: pocketmine.command.status.CurrentTPS=Текущий TPS: pocketmine.command.status.Networkupload=Данных отправлено: pocketmine.command.status.Networkdownload=Данных получено: pocketmine.command.status.Threadcount=Количество потоков: pocketmine.command.status.Mainmemory=Основная память потока: pocketmine.command.status.Totalmemory=Общая память: pocketmine.command.status.Totalvirtualmemory=Общая виртуальная память: pocketmine.command.status.Heapmemory=Heap memory: pocketmine.command.status.Maxmemorysystem=Максимальная память (системы): pocketmine.command.status.Maxmemorymanager=Максимальная память (менеджер): pocketmine.command.status.World=Мир pocketmine.command.status.chunks=чанков, pocketmine.command.status.entities=сущностей, pocketmine.command.status.tiles=тайлов. pocketmine.command.status.Time=Время pocketmine.command.status.ms=мс pocketmine.command.gc.description=Запускает процессы сборщика мусора pocketmine.command.gc.usage=/gc pocketmine.command.gc.title=Отчёт очистки pocketmine.command.gc.chunks=Чанков: pocketmine.command.gc.entities=Сущностей: pocketmine.command.gc.tiles=Тайлов: pocketmine.command.gc.cycles=Циклов: pocketmine.command.gc.memory=Освобождённая память: pocketmine.command.biome.description=Изменяет биом выделенной области. (Для смены снега на дождь) pocketmine.command.biome.posset=Установлена точка {%3} на позиции: ({%1},{%2})[{%0}] pocketmine.command.biome.get=ID биома, где ты стоишь: {%0}. Цвет:{%1},{%2},{%3} pocketmine.command.biome.wrongLev=Нельзя устанавливать точки в разнымх мирах. pocketmine.command.biome.wrongBio=Неверный ID биома. Пример: 1 (Равнины), 2 (Пустыня),13 (Ледяные горы),6 (Болото) pocketmine.command.biome.wrongCol=Неверный цвет! Пример: 146,188,89. Используй: "/biome get" чтобы получить остальные цвета. pocketmine.command.biome.noPos=Используйте "/biome pos1|pos2" чтобы отметить точки. pocketmine.command.biome.set=Был установлен цвет:{%0} pocketmine.command.biome.color=Выбран цвет: {%0},{%1},{%2} pocketmine.command.timings.description=Записывает тайминги, чтобы показать производительность сервера. pocketmine.command.timings.usage=/timings pocketmine.command.timings.enable=Тайминги включены и сброшены. pocketmine.command.timings.disable=Тайминги выключены pocketmine.command.timings.timingsDisabled=Пожалуйста, включите тайминги, набрав в чате /timings on pocketmine.command.timings.reset=Тайминги сброшены pocketmine.command.timings.pasteError=Произошла ошибка при сохранении таймингов pocketmine.command.timings.timingsUpload=Тайминги загружены в {%0} pocketmine.command.timings.timingsRead=Вы можете прочесть результаты таймингов в {%0} pocketmine.command.timings.timingsWrite=Тайминги записаны в {%0} pocketmine.command.version.description=Получает версию сервера, включая все используемые плагины pocketmine.command.version.usage=/version [имя плагина] pocketmine.command.version.noSuchPlugin=Этот сервер не использует плагин с таким именем. Используйте /plugins, чтобы получить список используемых плагинов. pocketmine.command.give.description=Даёт определённому игроку определённое количество предметов pocketmine.command.give.usage=/give <игрок> <предмет[:урон]> [количество] [тэги...] pocketmine.command.kill.description=Совершает суицид или убивает других игроков pocketmine.command.kill.usage=/kill [игрок] pocketmine.command.particle.description=Добавляет частицы в мир pocketmine.command.particle.usage=/particle <имя> [значение] [данные] pocketmine.command.time.description=Изменяет время в каждом мире pocketmine.command.time.usage=/time ИЛИ /time pocketmine.command.bancidbyname.description=Бан игрока по уникальному номеру устройства pocketmine.command.bancid.description=Бан уникального номера устройства pocketmine.command.banipbyname.description=Бан игрока по IP pocketmine.command.ban.player.description=Бан игрока pocketmine.command.ban.ip.description=Бан IP-адреса pocketmine.command.banlist.description=Список забаненных игроков pocketmine.command.defaultgamemode.description=Устанавливает режим игры по умолчанию pocketmine.command.deop.description=Убирает статус оператора с определённого игрока pocketmine.command.difficulty.description=Устанавливает сложность игры pocketmine.command.enchant.description=Добавляет зачарование предметам pocketmine.command.effect.description=Добавляет/Убирает эффекты у игроков pocketmine.command.gamemode.description=Изменяет режим игры у игрока pocketmine.command.help.description=Показывает меню помощи pocketmine.command.kick.description=Заставить выйти определённого игрока с сервера pocketmine.command.list.description=Показывает всех игроков на сервере pocketmine.command.me.description=Выполняет указанное действие в чате pocketmine.command.op.description=Даёт определённому игроку статус оператора pocketmine.command.unban.cid.description=Разбан уникального номера устройства игрока pocketmine.command.unban.player.description=Разбан игрока по нику pocketmine.command.unban.ip.description=Разбан IP-адреса игрока pocketmine.command.save.description=Сохраняет все файлы сервера pocketmine.command.saveoff.description=Отключает автосохранение сервера pocketmine.command.saveon.description=Включает автосохранение сервера pocketmine.command.say.description=Транслирует сообщение в чат pocketmine.command.seed.description=Показывает сид мира pocketmine.command.setworldspawn.description=Устанавливает точку респауна мира. Если координаты не введены, будут использованы координаты игрока. pocketmine.command.spawnpoint.description=Устанавливает точку возрождения игрока pocketmine.command.stop.description=Выключает сервер pocketmine.command.tp.description=Телепортирует определённого игрока (или Вас) к другому игроку или на другие координаты pocketmine.command.tell.description=Отправляет приватное сообщение указанному игроку pocketmine.command.xp.description=Добавляет опыт или уровень опыта игроку pocketmine.command.summon.description=Создаёт сущность по установленным координатам или координатом игрока pocketmine.command.fill.description=Заполняет выделенную область блоками pocketmine.command.setblock.description=Заменяет блок по координатам pocketmine.command.weather.description=Установка погоды в мире pocketmine.command.weather.usage=/weather <мир> (0,1,2,3) pocketmine.command.weather.changed=Погода успешно изменена в мире {%0}! pocketmine.command.weather.noregistered=Мир {%0} не был зарегистрирован в менеджере погоды. pocketmine.command.weather.invalid=Некорректная погода.(0,1,2,3) pocketmine.command.weather.wrong=Некорректные параметры. pocketmine.command.weather.invalid.level=Некорректное имя мира. pocketmine.command.whitelist.description=Управление вайтлистом сервера pocketmine.crash.create=Произошла фатальная ошибка и сервер вышел из строя. Создание аварийного дампа pocketmine.crash.error=Не удалось создать дамп: {%0} pocketmine.crash.submit=Пожалуйста, загрузите файл"{%0}" в краш-архив и отправьте ссылку на страницу исправления ошибок. Дайте как можно больше информации. pocketmine.crash.archive=Дамп выхода из строя сервера был автоматически отправлен в краш-архив. Вы можете его просмотреть тут: {%0}, или использовать ID #{%1}. pocketmine.debug.enable=Поддержка LevelDB включена pocketmine.player.invalidMove={%0} некорректно передвинулся! pocketmine.player.logIn={%0}[/{%1}:{%2}] вошел с id сущности {%3} на ({%4}, {%5}, {%6}, {%7}) pocketmine.player.logOut={%0}[/{%1}:{%2}] отключился: {%3} pocketmine.player.transferred={%0}[/{%1}:{%2}] был перемещен в {%3} pocketmine.player.invalidEntity={%0} попытался атаковать неправильную сущность pocketmine.plugin.load=Загрузка {%0} pocketmine.plugin.enable=Включение {%0} pocketmine.plugin.disable=Выключение {%0} pocketmine.plugin.restrictedName=Ограниченное имя pocketmine.plugin.incompatibleAPI=Несовместимая версия API pocketmine.plugin.unknownDependency=Неизвестная зависимость pocketmine.plugin.circularDependency=Обнаружена круговая зависимость pocketmine.plugin.genericLoadError=Невозможно загрузить плагин '{%0}' pocketmine.plugin.spacesDiscouraged=Плагин '{%0}' использует пробелы в его имени, это недопустимо pocketmine.plugin.loadError=Невозможно загрузить плагин '{%0}': {%1} pocketmine.plugin.duplicateError=Невозможно загрузить '{%0}': плагин уже существует pocketmine.plugin.fileError=Невозможно загрузить '{%0}' в папке '{%1}': {%2} pocketmine.plugin.commandError=Невозможно загрузить команду {%0} для плагина {%1} pocketmine.plugin.aliasError=Невозможно загрузить сокращение {%0} для плагина {%1} pocketmine.plugin.deprecatedEvent=Плагин '{%0}' зарегистрировал прослушивателя для '{%1}', используя метод '{%2}', но событие было отменено. pocketmine.plugin.eventError="Невозможно обработать событие '{%0}' в '{%1}': {%2} в {%3}" ================================================ FILE: src/pocketmine/lang/locale/tur.ini ================================================ language.name=Türkçe language.selected={%0} ({%1}) konsol dili olarak seçildi multiplayer.player.joined={%0} oyuna katıldı multiplayer.player.left={%0} oyundan ayrıldı chat.type.text={%0} : {%1} chat.type.achievement={%0} adlı oyuncu {%1} başarımını kazandı disconnectionScreen.outdatedClient=Eski sürüm! disconnectionScreen.outdatedServer=Sunucu daha eski bir sürümde! disconnectionScreen.serverFull=Sunucu dolu! disconnectionScreen.noReason=Sunucu ile olan bağlantınız kesildi disconnectionScreen.invalidSkin=Geçersiz skin! disconnectionScreen.invalidName=Geçersiz isim! death.fell.accident.generic={%0} Yüksekten düşerek öldü death.attack.inFire={%0} yanarak öldü death.attack.onFire={%0} yanarak can verdi death.attack.lava={%0} lavda yüzmeye çalıştı death.attack.inWall={%0} duvarda sıkışarak öldü death.attack.drown={%0} boğuldu death.attack.cactus={%0} delinerek öldü death.attack.generic={%0} öldü death.attack.explosion={%0} patlayarak öldü death.attack.explosion.player={%0}, {%1} tarafından havaya uçuruldu death.attack.magic={%0} sihirlenerek öldürüldü death.attack.wither={%0} witherlanarak öldü death.attack.mob={%0}, {%1} tarafından öldürüldü death.attack.player={%0}, {%1} tarafından öldürüldü death.attack.player.item={%0}, {%1} tarafından {%2} kullanılarak öldürüldü death.attack.arrow={%0}, {%1} tarafından vuruldu death.attack.arrow.item={%0}, {%1} adlı oyuncuyu {%2} kullanarak vurdu death.attack.fall={%0} yüksekten düşerek öldü death.attack.outOfWorld={%0} dünyadan aşağı düşerek can verdi gameMode.survival=Hayatta Kalma Modu gameMode.creative=Yaratıcı Mod gameMode.adventure=Maceracı Mod gameMode.spectator=İzleyici Mod gameMode.changed=Oyun modunuz güncellendi potion.moveSpeed=Hız potion.moveSlowdown=Yavaşlık potion.digSpeed=Sürat potion.digSlowDown=Kazı Yorgunluğu potion.damageBoost=Güç potion.heal=Anında Can potion.harm=Anlık Zarar potion.jump=Zıplama Artışı potion.confusion=Bulantı potion.regeneration=Yenilenme potion.resistance=Dayanıklılık potion.fireResistance=Ateş Dayanıklılığı potion.waterBreathing=Suda Nefes Alma potion.invisibility=Görünmezlik potion.blindness=Körlük potion.nightVision=Gece Görüşü potion.hunger=Açlık potion.weakness=Zayıflık potion.poison=Zehir potion.wither=Wither potion.healthBoost=Can Artışı potion.absorption=Emme potion.saturation=Doyma commands.generic.exception=Komut uygulanırken bilinmeyen bir hata oluştu commands.generic.permission=Bu komutu kullanma yetkiniz yok! commands.generic.notFound=Bilinmeyen komut. Tüm komutları görmek için /help yazınız commands.generic.player.notFound=Belirtilen oyuncu bulunamadı commands.generic.usage=Kullanış: {%0} commands.time.added=Saate {%0} eklendi commands.time.set=Dünya saati şu saate ayarlandı: {%0} commands.time.query=Dünya saati: {%0} commands.me.usage=/me commands.give.item.notFound=Belirtilen eşya bulunamıyor: {%0} commands.give.success={%0} * {%1}, {%2}'ye verildi commands.give.tagError=Veri başlığı ayrıştırması başarısız oldu: {%0} commands.effect.usage=/effect [saniye] [yükselme] [EfektleriGizle] yada /effect clear commands.effect.notFound=Bilinmeyen efekt: {%0} commands.effect.success={%3} adlı oyuncuya {%4} saniyeliğine {%0} (ID {%1}) * {%2} verildi commands.effect.success.removed={%0}, {%1}'den alındı commands.effect.success.removed.all={%0} adlı oyuncunun bütün efektleri alındı commands.effect.failure.notActive={%0} adlı oyuncudan {%1} efekti alınamadı. Çünkü bu efekte sahip değil commands.effect.failure.notActive.all={%0} adlı oyuncunun efektleri alınamadı. Çünkü hiç bir efekte sahip değil commands.enchant.noItem=Hedefin elinde eşya tutmuyor commands.enchant.notFound={%0} kodunda bir büyü yok commands.enchant.success=Büyü işlemi başarılı commands.enchant.usage=/enchant [seviye] commands.particle.success={%0} efekti {%1} kere oynatılıyor commands.particle.notFound={%0} - Bilinmeyen efekt ismi. commands.players.usage=/list commands.players.list=Çevrimiçi oyuncular: {%0}/{%1} commands.kill.successful={%0} adlı oyuncu öldürüldü commands.banlist.ips=Toplam %d adet engellenmiş IP adresi var: commands.banlist.players=Toplam {%0} adet engellenmiş oyuncu var: commands.banlist.usage=/banlist [ips|players] commands.defaultgamemode.usage=/defaultgamemode commands.defaultgamemode.success=Dünyanın varsayılan oyun modu artık : {%0} commands.op.success={%0} adlı kullanıcıya OP yetkisi verildi. commands.op.usage=/op commands.deop.success={%0} adlı kullanıcının OP yetkisi alındı. commands.deop.usage=/deop commands.say.usage=/say commands.seed.usage=/seed commands.seed.success=Seed: {%0} commands.ban.success={%0} adlı oyuncu banlandı commands.ban.usage=/ban [sebep...] commands.unban.success={%0} adlı oyuncunun engeli kaldırıldı commands.unban.usage=/pardon commands.banip.invalid=Bilinmeyen IP adresi veya çevrimiçi olmayan oyuncu adı girdiniz commands.banip.success={%0} IP adresi engellendi commands.banip.success.players=Engellenen IP adresi {%0}, {%1} kullanıcısına ait commands.banip.usage=/ban-ip [sebep ...] commands.unbanip.invalid=Geçersiz IP adresi girdiniz commands.unbanip.success={%0} IP adresinin engeli kaldırıldı commands.unbanip.usage=/pardon-ip commands.save.usage=/save-all commands.save-on.usage=/save-on commands.save-off.usage=/save-off commands.save.enabled=Otomatik kaydetme özelliği etkinleştirildi commands.save.disabled=Otomatik kaydetme özelliği devre dışı commands.save.start=Kaydediliyor... commands.save.success=Dünya kaydedildi commands.stop.usage=/stop commands.stop.start=Sunucu durduruluyor commands.kick.success={%0} oyundan atıldı commands.kick.success.reason={%0} oyundan atıldı: '{%1}' commands.kick.usage=/kick [sebep ...] commands.tp.success={%0}, {%1} adlı oyuncuya ışınlandı commands.tp.success.coordinates={%0} adlı oyuncu {%1}, {%2}, {%3} koordinatlarına ışınlandı commands.tp.usage=/tp [oyuncu1] yada /tp [oyuncu] [ ] commands.whitelist.list=Toplam {%0} (görünen {%1}) whitelist oyuncusu var: commands.whitelist.enabled=Whitelist aktif edildi commands.whitelist.disabled=Whitelist deaktif edildi commands.whitelist.reloaded=Whitelist yeniden yüklendi commands.whitelist.add.success={%0} adlı oyuncu whitelist listesine eklendi commands.whitelist.add.usage=/whitelist add commands.whitelist.remove.success={%0} adlı oyuncu whitelist listesinden kaldırıldı commands.whitelist.remove.usage=/whitelist remove commands.whitelist.usage=/whitelist commands.gamemode.success.self=Oyun modunuz {%2} moduna değiştirildi commands.gamemode.success.other={%1} adlı oyuncunun oyun modu {%2} moduna değiştirildi commands.gamemode.usage=/gamemode [oyuncu] commands.help.header=--- Yardım sayfası {%0} / {%1} (/help ) --- commands.help.usage=/help [sayfa|komut adı] commands.message.usage=/tell commands.message.sameTarget=Kendinize mesaj yollayamazsınız! commands.difficulty.usage=/difficulty commands.difficulty.success=Oyun zorluk seviyesi başarıyla değiştirildi! commands.spawnpoint.usage=/spawnpoint [oyuncu] [ ] commands.spawnpoint.success={%0} adlı oyuncunun başlangıç noktası ({%1}, {%2}, {%3}) koordinatlarına olarak değiştirildi commands.setworldspawn.usage=/setworldspawn [ ] commands.setworldspawn.success=Dünya başlangıç noktası ({%0}, {%1}, {%2}) koordinatlarına değiştirildi pocketmine.data.playerNotFound=Oyuncu verisi bulunamadı: "{%0}", yeni profil oluşturuluyor pocketmine.data.playerCorrupted=Bozuk veri "{%0}" bulunamadı, yeni profil oluşturuluyor pocketmine.data.playerOld=Eski veri "{%0}" bulundu, profil güncelleniyor pocketmine.data.saveError=Oyuncu kaydedilemiyor "{%0}": {%1} pocketmine.level.notFound=Dünya "{%0}" bulunamadı pocketmine.level.loadError=Dünya yüklenmiyor "{%0}": {%1} pocketmine.level.generationError=Dünya oluşturulamıyor "{%0}": {%1} pocketmine.level.tickError=Dünya işaretlenemiyor "{%0}": {%1} pocketmine.level.chunkUnloadError=Chunk yüklemesi kaldırılırken hata: {%0} pocketmine.level.backgroundGeneration=Spawn bölgesi "{%0}" oluşturulmaya başlandı pocketmine.level.defaultError=Varsayılan dünya yüklenemedi pocketmine.level.preparing=Dünya hazırlanıyor: "{%0}" pocketmine.level.unloading=Dünya yüklemesi kaldırılıyor "{%0}" pocketmine.server.start=Minecraft: PE sunucu versiyonu {%0} başlatılıyor pocketmine.server.networkError=[Şebeke] Arayüz {%1} için {%0} nedeniyle durduruldu pocketmine.server.networkStart=Sunucu {%0}:{%1} adresinde açılıyor pocketmine.server.info=Bu sunucu {%0} versiyonunda {%1} "{%2}" (API {%3}) çalışıyor pocketmine.server.info.extended=This server is running {%0} {%1} 「{%2}」 implementing API version {%3} for Minecraft: PE {%4} (protocol version {%5}) pocketmine.server.license={%0} is distributed under the LGPL License pocketmine.server.tickOverload=HATA! Sunucu aşırı mı yüklendi? pocketmine.server.startFinished={%0} saniye içinde tamamlandı. Yardım için /help yazınız pocketmine.server.defaultGameMode=Varsayılan oyun modu: {%0} pocketmine.server.query.start=GS4 durum dinleyicisi başlatılıyor pocketmine.server.query.info=Query port kuruluyor: {%0} pocketmine.server.query.running=Şurada sorgulanıyor: {%0}:{%1} pocketmine.command.alias.illegal={%0} kayıt edilemiyor. Çünkü yasaklı karakterler içeriyor pocketmine.command.alias.notFound=Takma ad {%0} bulunamadı. Çünkü olmayan bu komutları içerir: {%1} pocketmine.command.exception=İşlenmeyen işletme komutu: '{%0}' 'de {%1}: {%2} pocketmine.command.plugins.description=Sunucuda çalışan eklentileri gösterir pocketmine.command.plugins.success=Eklentiler ({%0}): {%1} pocketmine.command.plugins.usage=/plugins pocketmine.command.reload.description=Sunucu ayarlarını ve eklentilerini yeniden yükler pocketmine.command.reload.usage=/reload pocketmine.command.reload.reloading=Sunucu yeniden yükleniyor... pocketmine.command.reload.reloaded=Yeniden yükleme tamamlandı. pocketmine.command.status.description=Sunucunun performansını geri okur. pocketmine.command.status.usage=/status pocketmine.command.gc.description=Toplanmış atık dosyaları yakar pocketmine.command.gc.usage=/gc pocketmine.command.timings.description=Timings'i sunucunun performansını görmek için kaydeder. pocketmine.command.timings.usage=/timings pocketmine.command.timings.enable=Timings & Reset aktif edildi pocketmine.command.timings.disable=Timings devre dışı bırakıldı pocketmine.command.timings.timingsDisabled=Timings'i aktif etmek için /timings on yazınız pocketmine.command.timings.reset=Timigs yeniden başlatma pocketmine.command.timings.pasteError=Raporu geçerken bir hata meydana geldi pocketmine.command.timings.timingsUpload=Timings şuraya yüklendi: {%0} pocketmine.command.timings.timingsRead=Sonuçları {%0}'da okuyabilirsin pocketmine.command.timings.timingsWrite=Timings şuraya yazıldı: {%0} pocketmine.command.version.description=Sunucunun ve kullanımda olan eklentilerin versiyonunu alırsınız pocketmine.command.version.usage=/version [eklenti adı] pocketmine.command.version.noSuchPlugin=Bu sunucu bu isimde olan bir eklenti çalıştıramaz. /plugins yazarak eklenti listesine bakabilirsiniz pocketmine.command.give.description=Belirtilen oyuncuya belli miktarda eşya verir pocketmine.command.give.usage=/give [miktar] [başlıklar..] pocketmine.command.kill.description=Kendini veya diğer oyuncuları öldür pocketmine.command.kill.usage=/kill [oyuncu] pocketmine.command.particle.description=Dünyaya parçacık efekti eklendi pocketmine.command.particle.usage=/particle [adet] [data] pocketmine.command.time.description=Her bir dünyadaki saati değiştir pocketmine.command.time.usage=/time veya /time pocketmine.command.ban.player.description=Oyuncuyu sunucudan engeller pocketmine.command.ban.ip.description=Belirlenen IP adresini sunucudan engelller pocketmine.command.banlist.description=Sunucudan engellenen oyuncuları gör pocketmine.command.defaultgamemode.description=Varsayılan oyun modunu ayarla pocketmine.command.deop.description=Belirlenen oyuncunun OP durumunu alır pocketmine.command.difficulty.description=Varsayılan oyun zorluğunu ayarla pocketmine.command.enchant.description=Eşyalara büyü ekler pocketmine.command.effect.description=Oyuncuya efekt ekle veya kaldır pocketmine.command.gamemode.description=Belirlenen oyuncunun oyun modunu değiştirir pocketmine.command.help.description=Yardım menüsünü gösterir pocketmine.command.kick.description=Belirlenen oyuncuyu sunucudan atar pocketmine.command.list.description=Çevrimiçi oyuncular listesi pocketmine.command.me.description=Yaptığınız aksiyonu sohbette anlatır pocketmine.command.op.description=Belirlenen oyuncuya OP verir pocketmine.command.unban.player.description=Belirlenen oyuncunun sunucuya girme engelini açar pocketmine.command.unban.ip.description=Belirlenen IP adresinin sunucuya girme engelini açar pocketmine.command.save.description=Sunucuyu diske kaydeder pocketmine.command.saveoff.description=Sunucunun otomatik kaydetmesini deaktif eder pocketmine.command.saveon.description=Sunucunun otomatik kaydetmesini aktif eder pocketmine.command.say.description=Belirlenen mesajı gönderici olarak yayınlar pocketmine.command.seed.description=Dünyanın seed'ini gösterir pocketmine.command.setworldspawn.description=Dünya başlangıç noktasını ayarlar. pocketmine.command.spawnpoint.description=Oyuncunun başlangıç noktasını ayarlar pocketmine.command.stop.description=Sunucuyu durdurur pocketmine.command.tp.description=Belirlenen oyuncuya ışınlar veya belirlenen oyuncuyu başka bir oyuncuya ışınlar pocketmine.command.tell.description=Belirlenen oyuncuya özel mesaj yollar pocketmine.command.whitelist.description=Sunucuya girme hakkı olan oyuncuları ayarlar pocketmine.crash.create=Bilinmeyen bir hata oluştu ve sunucu çöktü. Çökme arşivi oluşturuluyor pocketmine.crash.error=Çökme arşivi oluşturulamadı: {%0} pocketmine.crash.submit=Lütfen "{%0}" dosyasını Çökme Arşivine yükleyin ve linki Bug Raporlama sayfasına gönderin. Size bir çok bilgi sunulacak. pocketmine.crash.archive=Çökme bilgisi Çökme Arşivine otomatik olarak gönderildi. Onu {%0}'da görüntüleyebilirsin veya ID #{%1}'i kullanabilirsin. pocketmine.debug.enable=LevelDB yardımı aktif edildi pocketmine.player.invalidMove={%0} moved wrongly! pocketmine.player.logIn={%0}[/{%1}:{%2}], {%3} Entity ID'si ile ({%4}, {%5}, {%6}, {%7})'da giriş yaptı pocketmine.player.logOut={%0}[/{%1}:{%2}], {%3} sebebiyle çıkış yaptı pocketmine.player.invalidEntity={%0} bilinmeyen bir varlığa saldırmayı denedi pocketmine.plugin.load={%0} Yükleniyor pocketmine.plugin.enable={%0} aktif ediliyor pocketmine.plugin.disable={%0} deaktif ediliyor pocketmine.plugin.restrictedName=Yasak ad pocketmine.plugin.incompatibleAPI=Uyumsuz API versiyonu pocketmine.plugin.unknownDependency=Bilinmeyen bağımlı pocketmine.plugin.circularDependency=Dairesel bağımlı tespit edildi pocketmine.plugin.genericLoadError=Eklenti yüklenemiyor '{%0}' pocketmine.plugin.spacesDiscouraged='{%0}' eklentisi isminde boşluk barındırıyor, vazgeçildi pocketmine.plugin.loadError=Eklenti yüklenemedi '{%0}': {%1} pocketmine.plugin.duplicateError='{%0}' adlı eklenti zaten var olduğundan yüklenemedi pocketmine.plugin.fileError='{%0}' eklentisi '{%1}': {%2} dosyasında yüklenemedi pocketmine.plugin.commandError={%0} komutu {%1} eklentisi için yüklenemedi pocketmine.plugin.aliasError={%0} takma adı {%1} eklentisi için yüklenemedi pocketmine.plugin.deprecatedEvent='{%0}' eklentisi bir '{%1}' dinleyicisi için '{%2}' yönteminde kayıt oldu, ama durum önerilmiyor. pocketmine.plugin.eventError="'{%0}' olayı '{%1}': {%2} 'ye {%3}'de geçilemiyor" ================================================ FILE: src/pocketmine/lang/locale/ukr.ini ================================================ # Language file compatible with Minecraft: Pocket Edition identifiers # # A message doesn't need to be there to be shown correctly on the client. # Only messages shown in PocketMine itself need to be here language.name=Ukrainian language.selected=Обрано {%0} ({%1}) в якості основної мови multiplayer.player.joined={%0} приєднався до гри multiplayer.player.left={%0} покинув гру chat.type.text={%0} : {%1} chat.type.emote=* {%0} {%1} chat.type.announcement=[{%0}] {%1} chat.type.admin=[{%0}: {%1}] chat.type.achievement=Гравець {%0} отримав досягнення {%1}! disconnectionScreen.notAuthenticated=Необхідно авторизуватися в Xbox! disconnectionScreen.outdatedClient=Застарілий клієнт! disconnectionScreen.outdatedServer=Застарілий сервер! disconnectionScreen.serverFull=Сервер переповнений! disconnectionScreen.noReason=Від’єднано від серверу. disconnectionScreen.invalidSkin=Помилковий формат скіну! disconnectionScreen.invalidName=Неприпустиме нік! death.fell.accident.generic={%0} впав з високого місця death.attack.inFire={%0} згорів death.attack.onFire={%0} згорів death.attack.lava={%0} вирішив поплавати в лаві... death.attack.inWall={%0} задихнувся в стіні death.attack.drown={%0} потонув death.attack.cactus={%0} став жертвою кактуса ;-) death.attack.generic={%0} помер death.attack.explosion={%0} вибухнув death.attack.explosion.player={%0} був підірваний {%1} death.attack.magic={%0} вбитий магією death.attack.wither={%0} висушений death.attack.mob={%0} був вбитий {%1} death.attack.player={%0} був вбитий {%1} death.attack.player.item={%0} був вбитий {%1} з {%2} death.attack.arrow={%0} був застрелений {%1} death.attack.arrow.item={%0} був застрелений {%1} з {%2} death.attack.fall={%0} впав з високого місця death.attack.outOfWorld={%0} випав зі світу gameMode.survival=Режим виживання gameMode.creative=Творчий режим gameMode.adventure=Пригодницький режим gameMode.spectator=Режим спостерігача gameMode.changed=Ваш ігровий режим було оновлено potion.moveSpeed=Швидкість potion.moveSlowdown=Уповільнення potion.digSpeed=Поспіх potion.digSlowDown=Втома potion.damageBoost=Сила potion.heal=Миттєве Лікування potion.harm=Миттєва Втрата potion.jump=Посилення Стрибка potion.confusion=Нудота potion.regeneration=Регенерація potion.resistance=Супротив Втратам potion.fireResistance=Вогнестійкість potion.waterBreathing=Підводне Дихання potion.invisibility=Невидимість potion.blindness=Сліпота potion.nightVision=Нічний Зір potion.hunger=Голод potion.weakness=Слабкість potion.poison=Отруєння potion.wither=Висушення potion.healthBoost=Підвищення Здоров’я potion.absorption=Поглинання potion.saturation=Насичення commands.generic.exception=Увага! Невідома помилка при виконанні команди commands.generic.permission=У Вас недостатньо прав для використання цієї команди commands.generic.notFound=Невідома команда. Використовуйте /help для перегляду списку команд commands.generic.player.notFound=Гравця не знайдено commands.generic.usage=Використання: {%0} commands.generic.level=Назва світу commands.generic.seed=Назва сіду commands.generic.name=нік commands.generic.generator=Назва генератору commands.generic.opt.missing=Необхідні налаштування відсутні,будь ласка, повторіть введення. commands.generic.runingame=Використовуйте цю команду тільки в грі. commands.time.added=Додано {%0} до часу commands.time.set=Встановлено час - {%0} commands.time.query=Час ‒ {%0} commands.me.usage=/me <дія> commands.give.item.notFound=Предмет з іменем {%0} не знайдено commands.give.success=Дано {%0} Х {%1} гравцю {%2} commands.give.tagError=Розбір тегу данных завершився невдало: {%0} commands.effect.usage=/effect <гравець> <ефект> [к-сть секунд] [рівень] [прибратиЧастинки] АБО /effect <гравець> clear commands.effect.notFound=Не існує ефекту з ID {%0} commands.effect.success=Дано {%0} (ID {%1}) Х {%2} гравцю {%3} на {%4} секунд commands.effect.success.removed=Ефект {%0} забрано у {%1} commands.effect.success.removed.all=Всі ефекти були зняті з {%0} commands.effect.failure.notActive=Неможливо прибрати ефект {%0} у {%1}, поскільки гравець не має цього ефекту commands.effect.failure.notActive.all=Неможливо прибрати ефекти у {%0}, поскільки гравець взагалі без ефектів commands.enchant.maxLevel=Діапазон рівнів зачарування: 1 - {%0} commands.enchant.noItem=У гравця немає такого предмету commands.enchant.notFound=Немає такого зачарування з ID {%0} commands.enchant.success=Зачарування завершилося вдало commands.enchant.cantEnchant=Цей предмет неможливо зачарувати commands.enchant.usage=/enchant <гравець> [рівень] commands.particle.success=Відтворюються частинки ефекту {%0} {%1} разів commands.particle.notFound=Невідомий ефект {%0} commands.players.usage=/list commands.players.list=Зараз {%0}/{%1} гравців на сервері: commands.kill.successful=Вбито {%0} commands.banlist.ips=Всього заблоковано %d IP адрес: commands.banlist.players=Всього заблоковано {%0} гравців: commands.banlist.cids=Всього заблоковано {%0} номерів пристроїв: commands.banlist.usage=/banlist [ips|players] commands.defaultgamemode.usage=/defaultgamemode <режим гри> commands.defaultgamemode.success=Ігровой режим світу за замовчуванням ‒ {%0} commands.op.success={%0} тепер оператор серверу commands.op.usage=/op <гравець> commands.deop.success={%0} вже не оператор серверу commands.deop.usage=/deop <гравець> commands.say.usage=/say <повідомлення> commands.seed.usage=/seed commands.seed.success=Сід: {%0} commands.bancidbyname.success=Гравця {%0} заблоковано по номеру пристрою commands.bancidbyname.usage=/bancidbyname <нік гравця> commands.bancid.success=Заблоковано номер пристрою: {%0} commands.bancid.usage=/bancid <номер пристрою> commands.unbancid.usage=/pardoncid <номер пристрою> commands.unbancid.success=Розблоковано номер пристрою {%0} commands.ban.success=Заблоковано гравця {%0} commands.ban.usage=/ban <нік гравця> [причина] [час(в днях)] commands.unban.success=Розблоковано гравця {%0} commands.unban.usage=/pardon <нік> commands.banip.invalid=Ви ввели неправильну IP-адресу чи даний гравець не в мережі commands.banip.success=Заблоковано IP адресу {%0} commands.banip.success.players=Заблоковано IP адресу {%0}, що належав {%1} commands.banip.usage=/ban-ip [причина] commands.unbanip.invalid=Ви ввели неправильну IP адресу commands.unbanip.success=Розблоковано IP адресу {%0} commands.unbanip.usage=/pardon-ip <адреса> commands.banipbyname.success=Гравця з ніком {%0} заблоковано по IP commands.banipbyname.usage=/banipbyname <гравець> commands.fill.usage=/fill commands.save.usage=/save-all commands.save-on.usage=/save-on commands.save-off.usage=/save-off commands.save.enabled=Ввімкнено автоматичне збереження світу commands.save.disabled=Автоматичне збереження світу було вимкнено commands.save.start=Збереження світу... commands.save.success=Світ збережено commands.setblock.usage=/setblock <назва блоку> [втрата] command.setblock.invalidBlock=Недопустима назва блоку чи ID commands.stop.usage=/stop commands.stop.start=Зупинка серверу commands.kick.success={%0} було від’єднано від серверу commands.kick.success.reason=Гравця {%0} було від’єднано від серверу. Причина: '{%1}' commands.kick.usage=/kick <гравець> [причина] commands.tp.success=Переміщено {%0} до {%1} commands.tp.success.coordinates=Переміщено {%0} на координати {%1}, {%2}, {%3} commands.tp.usage=/tp [цільовий гравець] <назначений гравець> або /tp [цільовий гравець] [ ] commands.whitelist.list=Білий список має {%0} гравців (з {%1} відображених): commands.whitelist.enabled=Білий список ввімкнено commands.whitelist.disabled=Білий список вимкнено commands.whitelist.reloaded=Білий список перезавантажено commands.whitelist.add.success={%0} додано до білого списку commands.whitelist.add.usage=/whitelist add <гравець> commands.whitelist.remove.success={%0} видалено з білого списку commands.whitelist.remove.usage=/whitelist remove <гравець> commands.whitelist.usage=/whitelist commands.gamemode.success.self=Встановлено власний режим гри - {%2} commands.gamemode.success.other=Режим гри для гравця {%1} змінено на {%2} commands.gamemode.usage=/gamemode <режим гри> [гравець] commands.help.header=--- Строінка {%0} з {%1} (/help <сторінка>) --- commands.help.usage=/help [сторінка|назва команди] commands.message.usage=/tell <гравець> <приватне повідомлення> commands.message.sameTarget=Нащо самому собі писати приватне повідомлення?! commands.difficulty.usage=/difficulty <складність гри> commands.difficulty.success=Складність гри змінено на {%0} commands.spawnpoint.usage=/spawnpoint [гравець] [ ] commands.spawnpoint.success=Місце появи гравця {%0} - ({%1}, {%2}, {%3}) commands.setworldspawn.usage=/setworldspawn [ ] commands.setworldspawn.success=Місце появи у світі - ({%0}, {%1}, {%2}) commands.summon.usage=/summon [сутність] [ ] [назва] commands.xp.failure.withdrawXp=Неможливо дати гравцю від’ємне значення досвіду commands.xp.success=Дано {%0} досвіду гравцю {%1} commands.xp.success.levels=Дано {%0} рівнів досвіду гравцю {%1} commands.xp.success.negative.levels=Забрано {%0} рівнів досвіду від гравця {%1} commands.xp.usage=/xp <кількість> [гравець] або /xp <кількість рівнів> [гравець] # -------------------- PocketMine language files, only for console -------------------- pocketmine.data.playerNotFound=Інформацію про гравця "{%0}" не знайдено, створюємо новий профіль pocketmine.data.playerCorrupted=Дані гравця "{%0}" пошкоджено, створюємо новий профіль pocketmine.data.playerOld=Профіль гравця "{%0}" застарілий, оновлюємо на новий pocketmine.data.saveError=Помилка збереження профіля гравця "{%0}": {%1} pocketmine.level.notFound=Світ "{%0}" не знайдено pocketmine.level.loadError=Помилка завантаження світу "{%0}": {%1} pocketmine.level.generationError=Помилка генерації світу "{%0}": {%1} pocketmine.level.tickError=Помилка промальовки світу "{%0}": {%1} pocketmine.level.chunkUnloadError=Помилка під час вивантаження чанку: {%0} pocketmine.level.backgroundGeneration=Генерується місце появи для світу "{%0}" pocketmine.level.defaultError=Помилка завантаження світу за замовчуванням pocketmine.level.preparing=Підготовка світу "{%0}" pocketmine.level.unloading=Вивантаження світу "{%0}" pocketmine.server.start=Запускається сервер Minecraft PE {%0} pocketmine.server.networkError=[Мережа] Зупинено інтерфейс {%0}. Причина: {%1} pocketmine.server.networkStart=Реєстрація серверу на {%0}:{%1} pocketmine.server.info.extended=Сервер використовує {%0} {%1} 「{%2}」. Версія API: {%3}. Версія Minecraft PE: {%4}. Протокол версії {%5} pocketmine.server.info=Цей сервер використовує {%0}, версію {%1} "{%2}" (API {%3}) pocketmine.server.license={%0} розповсюджується за ліцензією LGPL pocketmine.server.tickOverload=Сервер перевантажено! pocketmine.server.startFinished=Завантаження виконано. Час: {%0} секунд. Довідка по командам - "help" або "?" pocketmine.server.defaultGameMode=Режим гри за замовчуванням: {%0} pocketmine.server.query.start=Запуск прослуховувача GS4 pocketmine.server.query.info=QUERY порт - {%0} pocketmine.server.query.running=Реєстрація QUERY на {%0}:{%1} pocketmine.command.alias.illegal=Введено недопустимі знаки для альтернативи команди {%0} pocketmine.command.alias.notFound=Помилка реєстрації альтернативи команди {%0}: знайдено недопустимі команди - {%1} pocketmine.command.exception=Необроблене виключення при виконанні команди '{%0}' в {%1}: {%2} pocketmine.commands.cave.usage=/cave <кут повороту> <довжина> <номер вітки> <міцність> <назва світу> | /cave getmypos pocketmine.commands.cave.info=Кут повороту: {%0}. Довжина: {%1}. Номер вітки: {%2}. Міцність: {%3}. pocketmine.commands.cave.start=Створення печери розпочато! Будь ласка, зачекайте. pocketmine.command.plugins.description=Список плагінів серверу pocketmine.command.plugins.success=Плагіни ({%0}): {%1} pocketmine.command.plugins.usage=/plugins pocketmine.command.reload.description=Перезавантажити налаштування серверу и плагіни pocketmine.command.reload.usage=/reload pocketmine.command.reload.reloading=Перезавантаження серверу... pocketmine.command.reload.reloaded=Перезавантаження виконано. pocketmine.command.lvdat.description=Змінити налаштування мапи. pocketmine.command.lvdat.changed=Змінено параметр {%1} для світу "{%0}", необхідне перезавантаження для активації змін. pocketmine.command.lvdat.fixname=Назву світу "{%0}" було успішно відредаговано, необхідне перезавантаження для активації змін. pocketmine.command.lvdat.nofound=Світ "{%0}" не знайдено або не завантажено. pocketmine.command.lvdat.preset=Налаштування генератору (пресети) pocketmine.command.status.description=Інформація про продуктивність серверу. pocketmine.command.status.usage=/status pocketmine.command.status.title=Статус серверу pocketmine.command.status.player=Всього гравців: pocketmine.command.status.days=днів pocketmine.command.status.hours=годин pocketmine.command.status.minutes=хвилин pocketmine.command.status.seconds=секунд pocketmine.command.status.uptime=Час роботи серверу: pocketmine.command.status.AverageTPS=Середній TPS: pocketmine.command.status.CurrentTPS=Поточний TPS: pocketmine.command.status.Networkupload=Даних відправлено: pocketmine.command.status.Networkdownload=Даних отримано: pocketmine.command.status.Threadcount=Кількість потоків: pocketmine.command.status.Mainmemory=Основна пам’ять потоку: pocketmine.command.status.Totalmemory=Загальна пам’ять: pocketmine.command.status.Totalvirtualmemory=Загальна віртуальна пам’ять: pocketmine.command.status.Heapmemory=Пам’ять кучі (Java Heap Memory): pocketmine.command.status.Maxmemorysystem=Максимальна пам’ять системи: pocketmine.command.status.Maxmemorymanager=Максимальна пам’ять менеджера: pocketmine.command.status.World=Світ pocketmine.command.status.chunks=чанків, pocketmine.command.status.entities=сутностей, pocketmine.command.status.tiles=тайлів. pocketmine.command.status.Time=Час pocketmine.command.status.ms=мс pocketmine.command.gc.description=Запускає процес збирання сміття pocketmine.command.gc.usage=/gc pocketmine.command.gc.title=Результат очистки pocketmine.command.gc.chunks=Чанків: pocketmine.command.gc.entities=Сутностей: pocketmine.command.gc.tiles=Тайлів: pocketmine.command.gc.cycles=Циклів: pocketmine.command.gc.memory=Звільнена пам’ять: pocketmine.command.biome.description=Змінює біом обраної області. pocketmine.command.biome.posset=Встановлено точку {%3} на позиції: ({%1},{%2})[{%0}] pocketmine.command.biome.get=ID біома, де Ви знаходитесь: {%0}. Колір: {%1},{%2},{%3} pocketmine.command.biome.wrongLev=Точки знаходяться в різних світах. pocketmine.command.biome.wrongBio=Помилковий ID біому. Приклад біомів: 1 (Рівнини), 2 (Пустеля),13 (Льодяні гори),6 (Болото) pocketmine.command.biome.wrongCol=Неправильний колір! Приклад: 146,188,89. Використовуйте: "/biome get" для отримання решти кольорів. pocketmine.command.biome.noPos=Використовуйте "/biome pos1|pos2", щоб встановити точки. pocketmine.command.biome.set=Колір було змінено на {%0} pocketmine.command.biome.color=Обрано колір {%0},{%1},{%2} pocketmine.command.timings.description=Фіксує інформацію про середню продуктивність серверу. pocketmine.command.timings.usage=/timings pocketmine.command.timings.enable=Розпочато фіксування даних про продуктивність серверу. pocketmine.command.timings.disable=Фіксування даних про продуктивність серверу припинено. pocketmine.command.timings.timingsDisabled=Для повторної активації фіксування даних про продуктивність серверу використовуйте /timings on pocketmine.command.timings.reset=Дані про продуктивність серверу було скинуто pocketmine.command.timings.pasteError=Помилка при фіксуванні даних про продуктивність серверу pocketmine.command.timings.timingsUpload=Дані про продуктивність серверу завантажені з {%0} pocketmine.command.timings.timingsRead=Дані про продуктивність серверу Ви можете отримати тут: {%0} pocketmine.command.timings.timingsWrite=Дані про продуктивність серверу збережені до {%0} pocketmine.command.version.description=Інформація про версію серверу і про встановлені плагіни pocketmine.command.version.usage=/version [назва плагіну] pocketmine.command.version.noSuchPlugin=Плагіна з такою назвою не знайдено. Використовуйте /plugins для отримання списку плагінів. pocketmine.command.give.description=Дає гравцю зазначену к-сть предмету pocketmine.command.give.usage=/give <гравець> <предмет[:втрата]> [кількість] [теги] pocketmine.command.kill.description=Скоєння самогубства або вбивство іншого гравця pocketmine.command.kill.usage=/kill [гравець] pocketmine.command.particle.description=Додавання частинок у світ pocketmine.command.particle.usage=/particle <назва> [значення] [дані] pocketmine.command.time.description=Перегляд або глобальна зміна часу pocketmine.command.time.usage=/time <значення> або /time pocketmine.command.bancidbyname.description=Блокування гравця по номеру пристрою pocketmine.command.bancid.description=Блокування номера пристрою pocketmine.command.banipbyname.description=Блокування гравця по IP pocketmine.command.ban.player.description=Блокування гравця pocketmine.command.ban.ip.description=Блокування IP адреси pocketmine.command.banlist.description=Список заблокованих гравців pocketmine.command.defaultgamemode.description=Встановлює режим гри за замовчуванням pocketmine.command.deop.description=Знімає статус оператора з гравця pocketmine.command.difficulty.description=Встановлює складність гри pocketmine.command.enchant.description=Додає зачарування предметам pocketmine.command.effect.description=Зміна ефектів у гравців pocketmine.command.gamemode.description=Зміна режиму гри у гравця pocketmine.command.help.description=Довідка pocketmine.command.kick.description=Примусове від’єднання гравця від серверу pocketmine.command.list.description=Список гравців серверу, що зараз грають pocketmine.command.me.description=Виконує вказану дію в чаті pocketmine.command.op.description=Встановлює статус оператора для гравця pocketmine.command.unban.cid.description=Розблокування номера пристрою pocketmine.command.unban.player.description=Розблокування гравця по ніку pocketmine.command.unban.ip.description=Розблокування IP адреси pocketmine.command.save.description=Збереження файлів серверу pocketmine.command.saveoff.description=Вимикання автоматичного збереження pocketmine.command.saveon.description=Вмикання автоматичного збереження pocketmine.command.say.description=Написати в чат від імені серверу pocketmine.command.seed.description=Поточний сід світу pocketmine.command.setworldspawn.description=Встановлення місце появи гравців у світі. pocketmine.command.spawnpoint.description=Встановлення місце появи окремого гравця у світі pocketmine.command.stop.description=Вимикання серверу pocketmine.command.tp.description=Переміщення гравців pocketmine.command.tell.description=Надсилання приватного повідомлення гравцю pocketmine.command.xp.description=Зміна досвіду в гравця pocketmine.command.summon.description=Створення сутності pocketmine.command.fill.description=Заповнює вибрану область блоками pocketmine.command.fill.missingParameter=Пропущені параметри pocketmine.command.fill.usage=/fill pocketmine.command.setblock.description=Змінює блок по відповідних координатах pocketmine.command.weather.description=Зміна погоди у світі pocketmine.command.weather.usage=/weather <світ> (0,1,2,3) pocketmine.command.weather.changed=Успішно змінено погоду в світі {%0}! pocketmine.command.weather.noregistered=Світ {%0} не було зареєстровано в менеджері погоди. pocketmine.command.weather.invalid=Помилкове значення погоди. pocketmine.command.weather.wrong=Некорректні параметри. pocketmine.command.weather.invalid.level=Неправильна назва світу. pocketmine.command.whitelist.description=Керування білим списком серверу pocketmine.crash.create=Увага! Роботу серверу припинено через фатальну помилку. Створення аварійного дампу. pocketmine.crash.error=Помилка створення дампу: {%0} pocketmine.crash.submit=Буль ласка, завантажте файл "{%0}" до Crash Archive і надішліть посилання на сторінку Bug Reporting. Вкажіть якомога більше інформації про цю помилку. pocketmine.crash.archive=Аварійний дамп було автоматично відправлено до Crash Archive. Ви можете подивитися його тут: {%0}, або використати ID #{%1}. pocketmine.debug.enable=Підтримку LevelDB ввімкнено pocketmine.player.invalidMove={%0} некорректно пересунувся! pocketmine.player.logIn={%0}[/{%1}:{%2}] приєднався з ID сутності {%3} на ({%4}, {%5}, {%6}, {%7}) pocketmine.player.logOut={%0}[/{%1}:{%2}] від’єднався: {%3} pocketmine.player.transferred={%0}[/{%1}:{%2}] було переміщено до {%3} pocketmine.player.invalidEntity={%0} спробував атакувати некорректну сутність pocketmine.plugin.load=Завантаження {%0} pocketmine.plugin.enable=Ввімкнення {%0} pocketmine.plugin.disable=Вимкнення {%0} pocketmine.plugin.restrictedName=Обмежене ім’я pocketmine.plugin.incompatibleAPI=Несумісна версія API pocketmine.plugin.unknownDependency=Невідома залежність pocketmine.plugin.circularDependency=Виявлена кругова залежність pocketmine.plugin.genericLoadError=Помилка завантаження плагіну '{%0}' pocketmine.plugin.spacesDiscouraged=Назва плагіну '{%0}' вказана з використанням пробілів, що є недопустимим pocketmine.plugin.loadError=Помилка завантаження плагіну '{%0}': {%1} pocketmine.plugin.duplicateError=Помилка завантаження '{%0}': плагін вже існує pocketmine.plugin.fileError=Помилка завантаження '{%0}' в папці '{%1}': {%2} pocketmine.plugin.commandError=Помилка завантаження команди {%0} для плагіну {%1} pocketmine.plugin.aliasError=Помилка завантаження альтернативу {%0} для плагіну {%1} pocketmine.plugin.deprecatedEvent=Плагін '{%0}' зареєстрував прослуховувача для '{%1}', використовуючи метод '{%2}'. Ця подія є застарілою. pocketmine.plugin.eventError="Помилка обробки події '{%0}' в '{%1}': {%2} в {%3}" ================================================ FILE: src/pocketmine/lang/locale/vie.ini ================================================ # Language file compatible with Minecraft: Pocket Edition identifiers # # A message doesn't need to be there to be shown correctly on the client. # Only messages shown in PocketMine itself need to be here language.name=Vietnamese language.selected=Chọn {%0} ({%1}) là ngôn ngữ chính multiplayer.player.joined={%0} tham gia server multiplayer.player.left={%0} rời khỏi server chat.type.text={%0} : {%1} chat.type.emote=* {%0} {%1} chat.type.announcement=[{%0}] {%1} chat.type.admin=[{%0}: {%1}] chat.type.achievement={%0} vừa nhận được danh hiệu {%1} disconnectionScreen.outdatedClient=Phiên bản không hợp lệ! disconnectionScreen.outdatedServer=Server có phiên bản không hợp lệ! disconnectionScreen.serverFull=Server đã đầy! disconnectionScreen.noReason=Mất kết nối tới server disconnectionScreen.invalidSkin=Skin không hợp lệ! disconnectionScreen.invalidName=Tên không được chứa kí tự hay dấu cách! death.fell.accident.generic={%0} ngã từ khu vực cao! death.attack.inFire={%0} đi vào đám cháy! death.attack.onFire={%0} bỏng tới chết! death.attack.lava={%0} thử bơi vào dung nham death.attack.inWall={%0} kẹt trong tường death.attack.drown={%0} chết chìm death.attack.cactus={%0} bị đâm tới chết death.attack.generic={%0} chết death.attack.explosion={%0} bị bùng nổ death.attack.explosion.player={%0} bị nổ bởi {%1} death.attack.magic={%0} bị giết bởi phép thuật death.attack.wither={%0} hóa xương death.attack.mob={%0} bị giết bởi {%1} death.attack.player={%0} bị giết bởi {%1} death.attack.player.item={%0} bị giết bởi {%1} sử dụng {%2} death.attack.arrow={%0} bị bắn bởi {%1} death.attack.arrow.item={%0} bị bắn bởi {%1} sử dụng {%2} death.attack.fall={%0} rơi xuống đất quá nặng death.attack.outOfWorld={%0} rơi khỏi thế giới gameMode.survival=Chế độ sống sốt gameMode.creative=Chế độ sáng tạo gameMode.adventure=Chế độ phiêu lưu gameMode.spectator=Chế độ theo dõi gameMode.changed=Chế độ chơi của bạn đã được cập nhật potion.moveSpeed=Nhanh nhẹn potion.moveSlowdown=Chậm chạp potion.digSpeed=Sức đào nhanh potion.digSlowDown=Sức đào chậm potion.damageBoost=Khỏe mạnh potion.heal=Hồi máu tức thì potion.harm=Sát thương tức thì potion.jump=Nhảy cao potion.confusion=Choáng váng potion.regeneration=Hồi máu mỗi giây potion.resistance=Kháng sát thương potion.fireResistance=Kháng lửa potion.waterBreathing=Thở dưới nước potion.invisibility=Tàng hình potion.blindness=Mù potion.nightVision=Mắt Cú potion.hunger=Đói potion.weakness=Yếu potion.poison=Độc potion.wither=Xương potion.healthBoost=Thêm máu potion.absorption=Khiên máu potion.saturation=Hút máu commands.generic.exception=Lỗi khi thưc hiện câu lệnh này commands.generic.permission=Bạn không có quyền sử dụng câu lệnh này commands.generic.notFound=Sai cú pháp. Nhấn /help để xem các câu lệnh commands.generic.player.notFound=Không tìm thấy người chơi này commands.generic.usage=Cú pháp: {%0} commands.generic.level=Tên-level commands.generic.seed=Tên-seed commands.generic.name=Tên commands.generic.generator=Tên-tạo commands.generic.opt.missing=Thiếu phần yêu cầu! Vui lòng xác nhận và đánh lại. commands.generic.runingame=Chạy câu lệnh này trong game commands.time.added=Thêm {%0} vào thời gian commands.time.set=Đặt thời gian là {%0} commands.time.query=Thời gian là {%0} commands.me.usage=/me commands.give.item.notFound=Không có đồ nào có id {%0} commands.give.success=Đưa {%0} * {%1} cho {%2} commands.give.tagError=Data tag parsing failed: {%0} commands.effect.usage=/effect [giây] [cấp độ] [bổ sung lời nhắn] hoặc /effect clear commands.effect.notFound=Không có dạng hiệu ứng với id {%0} commands.effect.success=Tạo hiệu ứng {%0} (ID {%1}) * {%2} cho {%3} là {%4} seconds commands.effect.success.removed=Lấy hết {%0} từ {%1} commands.effect.success.removed.all=Lấy tất cả hiệu ứng từ {%0} commands.effect.failure.notActive=Không thể lấy {%0} từ {%1} vì họ không có hiệu ứng commands.effect.failure.notActive.all=Không thể lấy tất cả hiệu ứng từ {%0} vì họ không có commands.enchant.noItem=Người chơi không cầm vũ khí commands.enchant.notFound=Không có dạng cường hóa với id {%0} commands.enchant.success=Cường hóa thành công commands.enchant.cantEnchant=This item cannot be enchanted commands.enchant.usage=/enchant [cấp độ] commands.particle.success=Chơi cường hóa {%0} trong thời gian là {%1} commands.particle.notFound=Tên cường hóa không hợp lệ với {%0} commands.players.usage=/list commands.players.list=Có tất cả {%0}/{%1} đang online: commands.kill.successful=Giết {%0} commands.banlist.ips=Có tất cả %d IP bị cấm trong server: commands.banlist.players=Có tất cả {%0} người chơi bị cấm: commands.banlist.usage=/banlist [ips|players] commands.defaultgamemode.usage=/defaultgamemode commands.defaultgamemode.success=Chế độ mặc định của world được đặt là {%0} commands.op.success=Bổ nhiệm {%0} commands.op.usage=/op commands.deop.success=Bãi nhiệm {%0} commands.deop.usage=/deop commands.say.usage=/say commands.seed.usage=/seed commands.seed.success=Hạt giống: {%0} commands.bancidbyname.success=Cấm hệ thống của người chơi {%0} commands.bancidbyname.usage=/bancidbyname commands.bancid.success=Cấm hệ thống: {%0} commands.bancid.usage=/bancid commands.unbancid.usage=/pardoncid commands.ban.success=Cấm người chơi {%0} commands.ban.usage=/ban [lý do ...] [time(day)] commands.unban.success=Hủy cấm người chơi {%0} commands.unban.usage=/pardon commands.banip.invalid=IP không hợp lệ hoặc người chơi chưa online commands.banip.success=Cấm địa chỉ {%0} commands.banip.success.players=Cấm địa chỉ {%0} thuộc về {%1} commands.banip.usage=/ban-ip <địa chỉ|tên người chơi> [lý do ...] commands.unbanip.invalid=Địa chỉ không hợp lệ commands.unbanip.success=Hủy cấm địa chỉ {%0} commands.unbanip.usage=/pardon-ip <địa chỉ> commands.banipbyname.success=Cấm địa chỉ của người chơi {%0} commands.banipbyname.usage=/banipbyname commands.save.usage=/save-all commands.save-on.usage=/save-on commands.save-off.usage=/save-off commands.save.enabled=Bật chế độ tự động lưu thế giới commands.save.disabled=Tắt chế độ tự động lưu thế giới commands.save.start=Đang lưu... commands.save.success=Đã lưu toàn bộ! commands.setblock.usage=/setblock [damage] command.setblock.invalidBlock=ID/tên block không hợp lệ commands.stop.usage=/stop commands.stop.start=Đang dừng server... commands.kick.success=Đuổi người chơi {%0} khỏi server commands.kick.success.reason=Đuổi {%0} khỏi server. Lý do: '{%1}' commands.kick.usage=/kick [lý do ...] commands.tp.success=Di chuyển người chơi {%0} đến {%1} commands.tp.success.coordinates=Di chuyển người chơi {%0} đến {%1}, {%2}, {%3} commands.tp.usage=/tp [người chơi 1] OR /tp [người chơi 1] [ ] commands.whitelist.list=Có tất cả {%0} (ngoại trừ {%1}) người chơi trong danh sách trắng: commands.whitelist.enabled=Bật danh sách trắng commands.whitelist.disabled=Tắt danh sách trắng commands.whitelist.reloaded=Tải lại danh sách trắng commands.whitelist.add.success=Thêm {%0} vào danh sách trắng commands.whitelist.add.usage=/whitelist add commands.whitelist.remove.success=Loại {%0} khỏi danh sách trắng commands.whitelist.remove.usage=/whitelist remove commands.whitelist.usage=/whitelist commands.gamemode.success.self=Đặt chế độ chơi là {%2} commands.gamemode.success.other=Đặt {%1} cho {%2} commands.gamemode.usage=/gamemode [người chơi] commands.help.header=---> Xem trang hướng dẫn {%0} trong {%1} (/help ) --- commands.help.usage=/help [sô trang|câu lệnh] commands.message.usage=/tell commands.message.sameTarget=Bạn không thể gửi cho chính mình! commands.xp.usage=/xp commands.difficulty.usage=/difficulty commands.difficulty.success=Đặt mức độ chơi ở mức {%0} commands.spawnpoint.usage=/spawnpoint [người chơi] [ ] commands.spawnpoint.success=Đặt điểm spawn {%0} ở ({%1}, {%2}, {%3}) commands.setworldspawn.usage=/setworldspawn [ ] commands.setworldspawn.success=Đặt điểm mặc định ở ({%0}, {%1}, {%2}) commands.summon.usage=/summon [tên mob] [ ] [đặt tên cho mob] # -------------------- PocketMine language files, only for console -------------------- pocketmine.data.playerNotFound=Player data not found for "{%0}", creating new profile pocketmine.data.playerCorrupted=Corrupted data found for "{%0}", creating new profile pocketmine.data.playerOld=Old Player data found for "{%0}", upgrading profile pocketmine.data.saveError=Could not save player "{%0}": {%1} pocketmine.level.notFound=Level "{%0}" not found pocketmine.level.loadError=Could not load level "{%0}": {%1} pocketmine.level.generationError=Could not generate level "{%0}": {%1} pocketmine.level.tickError=Could not tick level "{%0}": {%1} pocketmine.level.chunkUnloadError=Error while unloading a chunk: {%0} pocketmine.level.backgroundGeneration=Spawn terrain for level "{%0}" is being generated in the background pocketmine.level.defaultError=No default level has been loaded pocketmine.level.preparing=Preparing level "{%0}" pocketmine.level.unloading=Unloading level "{%0}" pocketmine.server.start=Starting Minecraft: PE server version {%0} pocketmine.server.networkError=[Network] Stopped interface {%0} due to {%1} pocketmine.server.networkStart=Opening server on {%0}:{%1} pocketmine.server.info=This server is running {%0} version {%1} "{%2}" (API {%3}) pocketmine.server.info.extended=This server is running {%0} {%1} 「{%2}」 implementing API version {%3} for Minecraft: PE {%4} (protocol version {%5}) pocketmine.server.license={%0} is distributed under the LGPL License pocketmine.server.tickOverload=Can't keep up! Is the server overloaded? pocketmine.server.startFinished=Done ({%0}s)! For help, type "help" or "?" pocketmine.server.defaultGameMode=Default game type: {%0} pocketmine.server.query.start=Starting GS4 status listener pocketmine.server.query.info=Setting query port to {%0} pocketmine.server.query.running=Query running on {%0}:{%1} pocketmine.command.alias.illegal=Could not register alias {%0} because it contains illegal characters pocketmine.command.alias.notFound=Could not register alias {%0} because it contains commands that do not exist: {%1} pocketmine.command.exception=Unhandled exception executing command '{%0}' in {%1}: {%2} pocketmine.command.plugins.description=Gets a list of plugins running on the server pocketmine.command.plugins.success=Plugins ({%0}): {%1} pocketmine.command.plugins.usage=/plugins pocketmine.command.reload.description=Reloads the server configuration and plugins pocketmine.command.reload.usage=/reload pocketmine.command.reload.reloading=Reloading server... pocketmine.command.reload.reloaded=Reload complete. pocketmine.command.status.description=Reads back the server's performance. pocketmine.command.status.usage=/status pocketmine.command.status.title=Server status pocketmine.command.status.player=Player count: pocketmine.command.status.days=days pocketmine.command.status.hours=hours pocketmine.command.status.minutes=minutes pocketmine.command.status.seconds=seconds pocketmine.command.status.uptime=Uptime: pocketmine.command.status.AverageTPS=Average TPS: pocketmine.command.status.CurrentTPS=Current TPS: pocketmine.command.status.Networkupload=Network upload: pocketmine.command.status.Networkdownload=Network download: pocketmine.command.status.Threadcount=Thread count: pocketmine.command.status.Mainmemory=Main thread memory: pocketmine.command.status.Totalmemory=Total memory: pocketmine.command.status.Totalvirtualmemory=Total virtual memory: pocketmine.command.status.Heapmemory=Heap memory: pocketmine.command.status.Maxmemorysystem=Maximum memory (system): pocketmine.command.status.Maxmemorymanager=Maximum memory (manager): pocketmine.command.status.World=World pocketmine.command.status.chunks=chunks, pocketmine.command.status.entities=entities, pocketmine.command.status.tiles=tiles. pocketmine.command.status.Time=Time pocketmine.command.status.ms=ms pocketmine.command.gc.description=Fires garbage collection tasks pocketmine.command.gc.usage=/gc pocketmine.command.gc.title=Danh sách dọn dẹp: pocketmine.command.gc.chunks=Chunks: pocketmine.command.gc.entities=Vật thể: pocketmine.command.gc.tiles=Tiles: pocketmine.command.gc.cycles=Cycles: pocketmine.command.gc.memory=Bộ nhớ được thả: pocketmine.command.timings.description=Records timings to see performance of the server. pocketmine.command.timings.usage=/timings pocketmine.command.timings.enable=Enabled Timings & Reset pocketmine.command.timings.disable=Disabled Timings pocketmine.command.timings.timingsDisabled=Please enable timings by typing /timings on pocketmine.command.timings.reset=Timings reset pocketmine.command.timings.pasteError=An error happened while pasting the report pocketmine.command.timings.timingsUpload=Timings uploaded to {%0} pocketmine.command.timings.timingsRead=You can read the results at {%0} pocketmine.command.timings.timingsWrite=Timings written to {%0} pocketmine.command.version.description=Gets the version of this server including any plugins in use pocketmine.command.version.usage=/version [plugin name] pocketmine.command.version.noSuchPlugin=This server is not running any plugin by that name. Use /plugins to get a list of plugins. pocketmine.command.give.description=Gives the specified player a certain amount of items pocketmine.command.give.usage=/give [amount] [tags...] pocketmine.command.kill.description=Commit suicide or kill other players pocketmine.command.kill.usage=/kill [player] pocketmine.command.particle.description=Adds particles to a world pocketmine.command.particle.usage=/particle [count] [data] pocketmine.command.time.description=Changes the time on each world pocketmine.command.time.usage=/time OR /time pocketmine.command.bancidbyname.description=Prevents the specified CID by name from using this server禁止指定玩家的設備 ID pocketmine.command.bancid.description=Prevents the specified CID from using this server pocketmine.command.banipbyname.description=Prevents the specified IP address by name from using this server pocketmine.command.ban.player.description=Prevents the specified player from using this server pocketmine.command.ban.ip.description=Prevents the specified IP address from using this server pocketmine.command.banlist.description=View all players banned from this server pocketmine.command.defaultgamemode.description=Set the default gamemode pocketmine.command.deop.description=Takes the specified player's operator status pocketmine.command.difficulty.description=Sets the game difficulty pocketmine.command.enchant.description=Adds enchantments on items pocketmine.command.effect.description=Adds/Removes effects on players pocketmine.command.gamemode.description=Changes the player to a specific game mode pocketmine.command.help.description=Shows the help menu pocketmine.command.kick.description=Removes the specified player from the server pocketmine.command.list.description=Lists all online players pocketmine.command.me.description=Performs the specified action in chat pocketmine.command.op.description=Gives the specified player operator status pocketmine.command.unban.cid.description=Allows the specified CID to use this server pocketmine.command.unban.player.description=Allows the specified player to use this server pocketmine.command.unban.ip.description=Allows the specified IP address to use this server pocketmine.command.save.description=Saves the server to disk pocketmine.command.saveoff.description=Disables server autosaving pocketmine.command.saveon.description=Enables server autosaving pocketmine.command.say.description=Broadcasts the given message as the sender pocketmine.command.seed.description=Shows the world seed pocketmine.command.setworldspawn.description=Sets a worlds's spawn point. If no coordinates are specified, the player's coordinates will be used. pocketmine.command.spawnpoint.description=Sets a player's spawn point pocketmine.command.stop.description=Stops the server pocketmine.command.tp.description=Teleports the given player (or yourself) to another player or coordinates pocketmine.command.tell.description=Sends a private message to the given player pocketmine.command.xp.description=Add experience or experience level to the given player pocketmine.command.summon.description=Summons a entity at the player's location or a specific location pocketmine.command.fill.description=fills a specific selection with blocks pocketmine.command.setblock.description=Changes a block to another block pocketmine.command.weather.description=Set weather for level pocketmine.command.weather.usage=/weather pocketmine.command.weather.changed=Weather changed successfully in level {%0}! pocketmine.command.weather.noregistered=level {%0} hasn't registered to WeatherManager. pocketmine.command.weather.invalid=Invalid weather.(0,1,2,3) pocketmine.command.weather.wrong=Wrong parameters. pocketmine.command.weather.invalid.level=Invalid level name. pocketmine.command.whitelist.description=Manages the list of players allowed to use this server pocketmine.crash.create=An unrecoverable error has occurred and the server has crashed. Creating a crash dump pocketmine.crash.error=Could not create crash dump: {%0} pocketmine.crash.submit=Please upload the "{%0}" file to the Crash Archive and submit the link to the Bug Reporting page. Give as much info as you can. pocketmine.crash.archive=The crash dump has been automatically submitted to the Crash Archive. You can view it on {%0} or use the ID #{%1}. pocketmine.debug.enable=LevelDB support enabled pocketmine.player.invalidMove={%0} moved wrongly! pocketmine.player.logIn={%0}[/{%1}:{%2}] [ClientID: {%3}] logged in with entity id {%4} at ({%5}, {%6}, {%7}, {%8}) pocketmine.player.logOut={%0}[/{%1}:{%2}] logged out due to {%3} pocketmine.player.transferred={%0}[/{%1}:{%2}] was transferred to {%3} pocketmine.player.invalidEntity={%0} tried to attack an invalid entity pocketmine.plugin.load=Loading {%0} pocketmine.plugin.enable=Enabling {%0} pocketmine.plugin.disable=Disabling {%0} pocketmine.plugin.restrictedName=Restricted name pocketmine.plugin.incompatibleAPI=Incompatible API version pocketmine.plugin.unknownDependency=Unknown dependency pocketmine.plugin.circularDependency=Circular dependency detected pocketmine.plugin.genericLoadError=Could not load plugin '{%0}' pocketmine.plugin.spacesDiscouraged=Plugin '{%0}' uses spaces in its name, this is discouraged pocketmine.plugin.loadError=Could not load plugin '{%0}': {%1} pocketmine.plugin.duplicateError=Could not load plugin '{%0}': plugin exists pocketmine.plugin.fileError=Could not load '{%0}' in folder '{%1}': {%2} pocketmine.plugin.commandError=Could not load command {%0} for plugin {%1} pocketmine.plugin.aliasError=Could not load alias {%0} for plugin {%1} pocketmine.plugin.deprecatedEvent=Plugin '{%0}' has registered a listener for '{%1}' on method '{%2}', but the event is Deprecated. pocketmine.plugin.eventError="Could not pass event '{%0}' to '{%1}': {%2} on {%3} ================================================ FILE: src/pocketmine/lang/locale/zho.ini ================================================ # Language file compatible with Minecraft: Pocket Edition identifiers= #= # A message doesn't need to be there to be shown correctly on the client.= # Only messages shown in PocketMine itself need to be here= language.name=中文(繁體) language.selected=設定 {%0} ({%1}) 為基本語言 multiplayer.player.joined={%0} 加入了伺服器 multiplayer.player.left={%0} 離開了伺服器 chat.type.text={%0} : {%1} chat.type.emote=* {%0} {%1} chat.type.announcement=[{%0}] {%1} chat.type.admin=[{%0}: {%1}] chat.type.achievement={%0} 剛剛獲得了成就 {%1} disconnectionScreen.notAuthenticated=您需要 Xbox 登入 disconnectionScreen.outdatedClient=用戶端版本過舊! disconnectionScreen.outdatedServer=伺服器版本過舊! disconnectionScreen.serverFull=伺服器人數已滿! disconnectionScreen.noReason=與伺服器連線中斷 disconnectionScreen.invalidSkin=無效的皮膚! disconnectionScreen.invalidName=無效名稱! death.fell.accident.generic={%0} 從高處摔落 death.attack.inFire={%0} 在火焰中升天 death.attack.onFire={%0} 被燒死了 death.attack.lava={%0} 嘗試在熔岩游泳 death.attack.inWall={%0} 在牆壁裡窒息 death.attack.drown={%0} 溺死了 death.attack.cactus={%0} 被仙人掌刺死了 death.attack.generic={%0} 已死亡 death.attack.explosion={%0} 被炸飛了 death.attack.explosion.player={%0} 被 {%1} 炸死了 death.attack.magic={%0} 被魔法殺死了 death.attack.wither={%0} 凋零至死了 death.attack.mob={%0} 被 {%1} 殺死了 death.attack.player={%0} 被 {%1} 殺死了 death.attack.player.item={%0} 被 {%1} 用 {%2} 殺死了 death.attack.arrow={%0} 被 {%1} 射殺了 death.attack.arrow.item={%0} 被 {%1} 用 {%2} 射殺了 death.attack.fall={%0} 以為能安然無恙的著地 death.attack.outOfWorld={%0} 掉到世界外面了 gameMode.survival=生存模式 gameMode.creative=創造模式 gameMode.adventure=冒險模式 gameMode.spectator=觀眾模式 gameMode.changed=您的遊戲模式已更新 potion.moveSpeed=移動加速 potion.moveSlowdown=移動減速 potion.digSpeed=挖掘加速 potion.digSlowDown=挖掘減速 potion.damageBoost=增加攻擊力 potion.heal=立即治療 potion.harm=立即傷害 potion.jump=跳躍提升 potion.confusion=噁心 potion.regeneration=恢復 potion.resistance=抗性 potion.fireResistance=抗火性 potion.waterBreathing=水下呼吸 potion.invisibility=隱身 potion.blindness=失明 potion.nightVision=夜視 potion.hunger=飢餓 potion.weakness=虛弱 potion.poison=中毒 potion.wither=凋零 potion.healthBoost=生命值提升 potion.absorption=吸收 potion.saturation=飽食度 commands.generic.exception=嘗試執行此指令時發生未知錯誤 commands.generic.permission=您沒有權限使用此指令 commands.generic.notFound=未知的指令。請使用 /help 來顯示指令列表。 commands.generic.player.notFound=找不到該玩家 commands.generic.usage=用法:{%0} commands.generic.level=地圖名 commands.generic.seed=種子碼 commands.generic.name=名稱 commands.generic.generator=生成器名稱 commands.generic.opt.missing=指令缺少參數,請確認後重新輸入。 commands.generic.runingame=請在遊戲中使用該指令 commands.time.added=時間增加了 {%0} commands.time.set=時間設定為 {%0} commands.time.query=現在時間是 {%0} commands.me.usage=/me commands.give.item.notFound=ID為 {%0} 的物品並不存在 commands.give.success=將 {%0} * {%1} 給 {%2} commands.give.tagError=數據格式不正確: {%0} commands.effect.usage=/effect <玩家名稱> <效果> [秒數] [倍數] [隱藏粒子] 或 /effect <玩家名稱> clear commands.effect.notFound=ID為 {%0} 的特殊效果並不存在 commands.effect.success=對 {%3} 加上了 {%4} 秒的 {%0} (ID {%1}) * {%2} commands.effect.success.removed=從 {%1} 身上移除了 {%0} commands.effect.success.removed.all=已解除 {%0} 身上所有特殊狀態 commands.effect.failure.notActive=無法從 {%1} 身上移除 {%0},因為其身上無此效果 commands.effect.failure.notActive.all=無法移除效果因為 {%0} 身上沒有任何效果 commands.enchant.noItem=目標沒有手持一樣物品 commands.enchant.notFound=沒有一個附魔ID為 {%0} commands.enchant.success=附魔完成 commands.enchant.cantEnchant=這件物品不能被附魔! commands.enchant.usage=/enchant <玩家名稱> <附魔ID> [物品等級] commands.particle.success=正在應用 {%0} 效果 {%1} 次 commands.particle.notFound=未知的效果名稱 {%0} commands.players.usage=/list commands.players.list=共有 {%0}/{%1} 玩家在線上: commands.kill.successful=已刪除 {%0} commands.banlist.ips=共有 %d 個被封鎖的 IP 位址: commands.banlist.players=共有 {%0} 個被封鎖的玩家: commands.banlist.cids=共有 {%0} 禁止的 CIDs: commands.banlist.usage=/banlist [ips|players] commands.defaultgamemode.usage=/defaultgamemode <模式> commands.defaultgamemode.success=預設遊戲模式已設定為 {%0} commands.op.success={%0} 獲得管理員權限 commands.op.usage=/op <玩家名稱> commands.deop.success={%0} 移除管理員 commands.deop.usage=/deop <玩家名稱> commands.say.usage=/say <訊息...> commands.seed.usage=/seed commands.seed.success=種子碼:{%0} commands.bancidbyname.success=封鎖玩家 {%0} 的CID commands.bancidbyname.usage=/bancidbyname <玩家名稱> commands.bancid.success=封鎖CID: {%0} commands.bancid.usage=/bancid <設備ID> commands.unbancid.usage=/pardoncid <設備ID> commands.unbancid.success=Unbanned CID {%0} commands.ban.success=封鎖玩家 {%0} commands.ban.usage=/ban <玩家名稱> [原因...] [時間(天)] commands.unban.success=解封玩家 {%0} commands.unban.usage=/pardon <玩家名稱> commands.banip.invalid=您輸入了一個無效的 IP 位址、玩家名稱或不在線上 commands.banip.success=封鎖 IP 位址 {%0} 。 commands.banip.success.players=封鎖 IP 位址 {%0} 來自 {%1} commands.banip.usage=/ban-ip [原因 ...] commands.unbanip.invalid=您輸入了一個無效的 IP 位址 commands.unbanip.success=解除封鎖 IP 位址 {%0} commands.unbanip.usage=/pardon-ip commands.banipbyname.success=封鎖玩家 {%0} 的IP commands.banipbyname.usage=/banipbyname <玩家名稱> commands.save.usage=/save-all commands.save-on.usage=/save-on commands.save-off.usage=/save-off commands.save.enabled=開啟地圖自動存檔功能 commands.save.disabled=關閉地圖自動存檔功能 commands.save.start=正在儲存... commands.save.success=儲存完畢 commands.setblock.usage=/setblock <方塊名> [數據值] command.setblock.invalidBlock=無效的方塊名稱/ID commands.stop.usage=/stop commands.stop.start=停止伺服器中 commands.kick.success={%0} 從遊戲中被踢出 commands.kick.success.reason={%0} 從遊戲中被踢出:{%1} commands.kick.usage=/kick <玩家名稱> [原因...] commands.tp.success=已傳送 {%0} 至 {%1} commands.tp.success.coordinates=已傳送 {%0} 至 {%1}, {%2}, {%3} commands.tp.usage=/tp [玩家名稱] <目標玩家> 或是 /tp [玩家名稱] [ ] commands.whitelist.list=有 {%0} 人(全部 {%1} 人) 為白名單玩家: commands.whitelist.enabled=已開啟白名單 commands.whitelist.disabled=已關閉白名單 commands.whitelist.reloaded=重置白名單 commands.whitelist.add.success=新增 {%0} 至白名單 commands.whitelist.add.usage=/whitelist add <玩家名稱> commands.whitelist.remove.success=從白名單刪除 {%0} commands.whitelist.remove.usage=/whitelist remove <玩家名稱> commands.whitelist.usage=/whitelist commands.gamemode.success.self=設定自身的遊戲模式為 {%2} commands.gamemode.success.other=設定 {%1} 的遊戲模式為 {%2} commands.gamemode.usage=/gamemode <模式> [玩家名稱] commands.help.header=--- 查看幫助列表第 {%0} 頁共 {%1} 頁 (/help ) --- commands.help.usage=/help [頁數|指令名稱] commands.message.usage=/tell <玩家名稱> <訊息...> commands.message.sameTarget=您不能傳送訊息給自己! commands.xp.usage=/xp <經驗值或等級+L> <玩家名稱> commands.difficulty.usage=/difficulty <難度> commands.difficulty.success=設定遊戲難度為 {%0} commands.spawnpoint.usage=/spawnpoint [玩家名稱] [ ] commands.spawnpoint.success=設定 {%0} 的重生點為 ({%1}, {%2}, {%3}) commands.setworldspawn.usage=/setworldspawn [ ] commands.setworldspawn.success=設定世界重生點為 ({%0}, {%1}, {%2}) commands.summon.usage=/summon <實體名稱> [ ] [數據標籤] # -------------------- PocketMine language files, only for console -------------------- pocketmine.data.playerNotFound=無法找到玩家數據 "{%0}",正在創建新的數據檔 pocketmine.data.playerCorrupted=發現損壞的數據 "{%0}",創建新的設定檔 pocketmine.data.playerOld=發現舊的玩家數據 "{%0}",更新設定檔 pocketmine.data.saveError=無法儲存 "{%0}" 的玩家資料:{%1} pocketmine.level.notFound=無法找到 "{%0}" 地圖 pocketmine.level.loadError=無法讀取地圖 "{%0}":{%1} pocketmine.level.generationError=無法產生地圖 "{%0}":{%1} pocketmine.level.tickError=計算地圖「{%0}」時出現錯誤︰{%1} pocketmine.level.chunkUnloadError=移除一個區塊時發生錯誤:{%0} pocketmine.level.backgroundGeneration=正在於背景生成世界 「{%0}「 的地形 pocketmine.level.defaultError=沒有讀取預設的地圖 pocketmine.level.preparing=準備地圖中... "{%0}" pocketmine.level.unloading=正在移除地圖 "{%0}" pocketmine.server.start=正在啟動支援 Minecraft:PE {%0} 版本的伺服器 pocketmine.server.networkError=[網路] 停止接口 {%0} 由於 {%1} pocketmine.server.networkStart=正在啟動伺服器在 {%0}:{%1} pocketmine.server.info=此伺服器正在運作 {%0} {%1} 版本 "{%2}" (API {%3}) pocketmine.server.info.extended=此伺服器正在運作 {%0} {%1} 「{%2}」 執行 API 版本 {%3} 支援 Minecraft:PE {%4} (協定版本 {%5}) pocketmine.server.info.extended1=本伺服器正運行 {%0}{%1} (代號 "{%2}") pocketmine.server.info.extended2=PHP 版本: {%0} pocketmine.server.info.extended3=API: {%0} (iTX API 版本 {%1}) pocketmine.server.info.extended4=Target client: Minecraft PE {%0} (protocol 版本 {%1}) pocketmine.server.license={%0} 根據 LGPL 許可發佈 pocketmine.server.tickOverload=注意!伺服器有超載的可能 pocketmine.server.startFinished=讀取完成 ({%0}s)!如需幫助,請輸入 "help" 或 "?" pocketmine.server.defaultGameMode=預設的遊戲類型:{%0} pocketmine.server.query.start=啟動 GS4 狀態監聽器 pocketmine.server.query.info=設定 query 接口到 {%0} pocketmine.server.query.running=Query 運作在 {%0}:{%1} pocketmine.command.alias.illegal=不能註冊別名 {%0},因為它包含非法字符 pocketmine.command.alias.notFound=未能登記別稱 {%0} ,因為它包含不存在的指令: {%1} pocketmine.command.exception=於 {%1} 執行指令 「{%0}「 時,出現了未被處理的錯誤: {%2} pocketmine.command.plugins.description=獲取在伺服器上運行的插件列表 pocketmine.command.plugins.success=插件 ({%0}):{%1} pocketmine.command.plugins.usage=/plugins pocketmine.command.reload.description=重新讀取伺服器設定和插件 pocketmine.command.reload.usage=/reload pocketmine.command.reload.reloading=重新讀取伺服器... pocketmine.command.reload.reloaded=重新讀取完成 pocketmine.command.status.description=重新讀取伺服器的性能。 pocketmine.command.status.usage=/status pocketmine.command.status.title=伺服器狀態 pocketmine.command.status.player=伺服器人數: pocketmine.command.status.days=天 pocketmine.command.status.hours=小時 pocketmine.command.status.minutes=分 pocketmine.command.status.seconds=秒 pocketmine.command.status.uptime=運行時間: pocketmine.command.status.AverageTPS=平均TPS: pocketmine.command.status.CurrentTPS=瞬時TPS: pocketmine.command.status.Networkupload=網路上傳: pocketmine.command.status.Networkdownload=網路下載: pocketmine.command.status.Threadcount=線程總數: pocketmine.command.status.Mainmemory=線程總數: pocketmine.command.status.Totalmemory=總記憶體: pocketmine.command.status.Totalvirtualmemory=總虛擬記憶體: pocketmine.command.status.Heapmemory=堆棧記憶體: pocketmine.command.status.Maxmemorysystem=系統最大記憶體: pocketmine.command.status.Maxmemorymanager=核心全域最大記憶體: pocketmine.command.status.World=世界 pocketmine.command.status.chunks=區塊, pocketmine.command.status.entities=實體, pocketmine.command.status.tiles=tiles. pocketmine.command.status.Time=時間 pocketmine.command.status.ms=毫秒 pocketmine.command.gc.description=啟動垃圾清除任務 pocketmine.command.gc.usage=/gc pocketmine.command.gc.title=垃圾回收結果 pocketmine.command.gc.chunks=區塊: pocketmine.command.gc.entities=實體: pocketmine.command.gc.tiles=方塊: pocketmine.command.gc.cycles=循環: pocketmine.command.gc.memory=記憶體釋放: pocketmine.command.timings.description=紀錄計時數據,以檢視伺服器的性能。 pocketmine.command.timings.usage=/timings pocketmine.command.timings.enable=啟用定時和重啟 pocketmine.command.timings.disable=停用定時 pocketmine.command.timings.timingsDisabled=啟用定時工具透過 /timings on pocketmine.command.timings.reset=定時重啟 pocketmine.command.timings.pasteError=已記錄在事件記錄檔中 pocketmine.command.timings.timingsUpload=計時數據已被上載至 {%0} pocketmine.command.timings.timingsRead=你可以在 {%0} 閱讀計時結果 pocketmine.command.timings.timingsWrite=計時數據已被儲存至 {%0} pocketmine.command.version.description=檢視此伺服器 (及其使用的插件) 的版本 pocketmine.command.version.usage=/version [插件名稱] pocketmine.command.version.noSuchPlugin=該伺服器沒有運行任何叫這個名稱的插件。使用 /plugins 來獲得插件列表。 pocketmine.command.give.description=給指定玩家一定數量的物品 pocketmine.command.give.usage=/give <玩家名稱> <物品[:耐久度]> [數量] [附加數據值] pocketmine.command.kill.description=自殺或殺死其他玩家 pocketmine.command.kill.usage=/kill [玩家名稱] pocketmine.command.particle.description=加入粒子效果至世界 pocketmine.command.particle.usage=/particle <玩家名稱> [數量] [數據值] pocketmine.command.time.description=更改每個世界的時間 pocketmine.command.time.usage=/time <數值> 或 /time pocketmine.command.bancidbyname.description=禁止指定玩家的設備 ID pocketmine.command.bancid.description=禁止指定的設備 ID pocketmine.command.banipbyname.description=禁止指定玩家的 IP pocketmine.command.ban.player.description=禁止指定的玩家使用此伺服器 pocketmine.command.ban.ip.description=禁止指定的 IP 位址使用此伺服器 pocketmine.command.banlist.description=查看來自該伺服器禁止的所有玩家 pocketmine.command.defaultgamemode.description=設定預設的遊戲模式 pocketmine.command.deop.description=移除指定玩家的管理員權限 pocketmine.command.difficulty.description=設定遊戲的難易度 pocketmine.command.enchant.description=把物件附魔 pocketmine.command.effect.description=增加/減少玩家身上的效果 pocketmine.command.gamemode.description=改變玩家到一個特定的遊戲模式 pocketmine.command.help.description=顯示幫助列表 pocketmine.command.kick.description=從伺服器中刪除指定玩家 pocketmine.command.list.description=顯示在線玩家列表 pocketmine.command.me.description=於聊天中作出指定的動作 pocketmine.command.op.description=賦予指定玩家管理員權限 pocketmine.command.unban.cid.description=允許指定 CID 使用此伺服器 pocketmine.command.unban.player.description=允許指定玩家使用此伺服器 pocketmine.command.unban.ip.description=允許指定 IP 位址使用此伺服器 pocketmine.command.save.description=儲存伺服器到磁碟上 pocketmine.command.saveoff.description=停用自動儲存伺服器 pocketmine.command.saveon.description=啟用自動儲存伺服器 pocketmine.command.say.description=以發送指令者身份廣播指定的訊息 pocketmine.command.seed.description=顯示世界種子碼 pocketmine.command.setworldspawn.description=設定一個世界重生點。未指定坐標,將使用玩家的坐標。 pocketmine.command.spawnpoint.description=設定玩家重生點 pocketmine.command.stop.description=關閉伺服器 pocketmine.command.tp.description=傳送指定玩家(或是自己)到另一位玩家或座標 pocketmine.command.tell.description=傳送私訊給指定玩家 pocketmine.command.xp.description=給指定玩家新增經驗值或等級 pocketmine.command.summon.description=召喚指定的實體於玩家位置或指定位置 ocketmine.command.fill.description=填充了指定方塊 pocketmine.command.setblock.description=將一個方塊更改為另一個方塊 pocketmine.command.weather.description=設定指定地圖的天氣 pocketmine.command.weather.usage=/weather <地圖名稱 天氣|天氣> pocketmine.command.weather.changed=成功設定地圖{%0}的天氣 pocketmine.command.weather.noregistered=地圖{%0}未註冊到天氣管理器 pocketmine.command.weather.invalid=無效的天氣!請輸入天氣 0,1,2,3。 pocketmine.command.weather.wrong=錯誤的參數 pocketmine.command.weather.invalid.level=錯誤的地圖名稱 pocketmine.command.whitelist.description=管理員允許使用此伺服器的玩家列表 pocketmine.crash.create=一個不能回復的錯誤發生了,使伺服器崩潰。正在儲存錯誤報告。 pocketmine.crash.error=未能儲存錯誤報告︰{%0} pocketmine.crash.submit=請上載檔案「{%0}」至線上崩潰儲存庫,並把所獲之連結提交至漏洞報告網頁。請盡量提供更多資料。 pocketmine.crash.archive=毀損傾印報告已經自動地被提交到毀損傾印存檔。你可以在{%0} 查看到它或使用ID #{%1}。 pocketmine.debug.enable=啟用 LevelDB 支援 pocketmine.player.invalidMove={%0} 行動可疑! pocketmine.player.logIn={%0}[/{%1}:{%2}] [ClientID: {%3}] 登入遊戲,實體ID為 {%4} 座標位在 ({%5}, {%6}, {%7}, {%8}) pocketmine.player.logOut={%0}[/{%1}:{%2}] 登出遊戲,原因: {%3} pocketmine.player.transferred={%0}[/{%1}:{%2}] 被傳送到 {%3} pocketmine.player.invalidEntity={%0} 嘗試攻擊一個無效的實體 pocketmine.plugin.load=讀取中... {%0} pocketmine.plugin.enable=開啟中... {%0} pocketmine.plugin.disable=關閉中... {%0} pocketmine.plugin.restrictedName=受限的名稱 pocketmine.plugin.incompatibleAPI=不相容的API版本 pocketmine.plugin.unknownDependency=本插件無法單獨使用 pocketmine.plugin.circularDependency=檢測出循環依賴 pocketmine.plugin.genericLoadError=無法讀取插件 '{%0}' pocketmine.plugin.spacesDiscouraged=插件 '{%0}' 在名稱中使用了空格,不建議這樣做 pocketmine.plugin.loadError=無法讀取插件 '{%0}':{%1} pocketmine.plugin.duplicateError=無法讀取插件 '{%0}':已有相同插件 pocketmine.plugin.fileError=無法讀取在 '{%1}' 資料夾中的 '{%0}':{%2} pocketmine.plugin.commandError=無法讀取 {%1} 插件的 {%0} 指令 pocketmine.plugin.aliasError=無法讀取 {%1} 插件的 {%0} 別名 pocketmine.plugin.deprecatedEvent=插件 '{%0}' 已經使用 '{%2}' 方法註冊了一個在 '{%1}' 的監聽器,但是該事件已過時。 pocketmine.plugin.eventError="無法處理事件 '{%0}' 至 '{%1}':{%2} 在 {%3} 上" ================================================ FILE: src/pocketmine/level/ChunkLoader.php ================================================ registerChunkLoader($this, $chunkX, $chunkZ) * Unregister Level->unregisterChunkLoader($this, $chunkX, $chunkZ) * * WARNING: When moving this object around in the world or destroying it, * be sure to free the existing references from Level, otherwise you'll leak memory. */ interface ChunkLoader{ /** * Returns the ChunkLoader id. * Call Level::generateChunkLoaderId($this) to generate and save it * * @return int */ public function getLoaderId(); /** * Returns if the chunk loader is currently active * * @return bool */ public function isLoaderActive(); /** * @return Position */ public function getPosition(); /** * @return float */ public function getX(); /** * @return float */ public function getZ(); /** * @return Level */ public function getLevel(); /** * This method will be called when a Chunk is replaced by a new one * * @param Chunk $chunk */ public function onChunkChanged(Chunk $chunk); /** * This method will be called when a registered chunk is loaded * * @param Chunk $chunk */ public function onChunkLoaded(Chunk $chunk); /** * This method will be called when a registered chunk is unloaded * * @param Chunk $chunk */ public function onChunkUnloaded(Chunk $chunk); /** * This method will be called when a registered chunk is populated * Usually it'll be sent with another call to onChunkChanged() * * @param Chunk $chunk */ public function onChunkPopulated(Chunk $chunk); /** * This method will be called when a block changes in a registered chunk * * @param Block|Vector3 $block */ public function onBlockChanged(Vector3 $block); } ================================================ FILE: src/pocketmine/level/ChunkManager.php ================================================ level = $center->getLevel(); $this->source = $center; $this->size = max($size, 0); $this->what = $what; $this->dropItem = $dropItem; } /** * @return bool */ public function explodeA() : bool{ if($this->size < 0.1){ return false; } $vector = new Vector3(0, 0, 0); $vBlock = new Vector3(0, 0, 0); $mRays = intval($this->rays - 1); for($i = 0; $i < $this->rays; ++$i){ for($j = 0; $j < $this->rays; ++$j){ for($k = 0; $k < $this->rays; ++$k){ if($i === 0 or $i === $mRays or $j === 0 or $j === $mRays or $k === 0 or $k === $mRays){ $vector->setComponents($i / $mRays * 2 - 1, $j / $mRays * 2 - 1, $k / $mRays * 2 - 1); $vector->setComponents(($vector->x / ($len = $vector->length())) * $this->stepLen, ($vector->y / $len) * $this->stepLen, ($vector->z / $len) * $this->stepLen); $pointerX = $this->source->x; $pointerY = $this->source->y; $pointerZ = $this->source->z; for($blastForce = $this->size * (mt_rand(700, 1300) / 1000); $blastForce > 0; $blastForce -= $this->stepLen * 0.75){ $x = (int) $pointerX; $y = (int) $pointerY; $z = (int) $pointerZ; $vBlock->x = $pointerX >= $x ? $x : $x - 1; $vBlock->y = $pointerY >= $y ? $y : $y - 1; $vBlock->z = $pointerZ >= $z ? $z : $z - 1; if($vBlock->y < 0 or $vBlock->y >= Level::Y_MAX){ break; } $block = $this->level->getBlock($vBlock); if($block->getId() !== 0){ $blastForce -= ($block->getResistance() / 5 + 0.3) * $this->stepLen; if($blastForce > 0){ if(!isset($this->affectedBlocks[$index = Level::blockHash($block->x, $block->y, $block->z)])){ $this->affectedBlocks[$index] = $block; } } } $pointerX += $vector->x; $pointerY += $vector->y; $pointerZ += $vector->z; } } } } } return true; } public function explodeB() : bool{ $send = []; $updateBlocks = []; $source = (new Vector3($this->source->x, $this->source->y, $this->source->z))->floor(); $yield = (1 / $this->size) * 100; if($this->what instanceof Entity){ $this->level->getServer()->getPluginManager()->callEvent($ev = new EntityExplodeEvent($this->what, $this->source, $this->affectedBlocks, $yield)); if($ev->isCancelled()){ return false; }else{ $yield = $ev->getYield(); $this->affectedBlocks = $ev->getBlockList(); } } $explosionSize = $this->size * 2; $minX = Math::floorFloat($this->source->x - $explosionSize - 1); $maxX = Math::ceilFloat($this->source->x + $explosionSize + 1); $minY = Math::floorFloat($this->source->y - $explosionSize - 1); $maxY = Math::ceilFloat($this->source->y + $explosionSize + 1); $minZ = Math::floorFloat($this->source->z - $explosionSize - 1); $maxZ = Math::ceilFloat($this->source->z + $explosionSize + 1); $explosionBB = new AxisAlignedBB($minX, $minY, $minZ, $maxX, $maxY, $maxZ); $list = $this->level->getNearbyEntities($explosionBB, $this->what instanceof Entity ? $this->what : null); foreach($list as $entity){ $distance = $entity->distance($this->source) / $explosionSize; if($distance <= 1){ $motion = $entity->subtract($this->source)->normalize(); $impact = (1 - $distance) * ($exposure = 1); $damage = (int) ((($impact * $impact + $impact) / 2) * 8 * $explosionSize + 1); if($this->what instanceof Entity){ $ev = new EntityDamageByEntityEvent($this->what, $entity, EntityDamageEvent::CAUSE_ENTITY_EXPLOSION, $damage); }elseif($this->what instanceof Block){ $ev = new EntityDamageByBlockEvent($this->what, $entity, EntityDamageEvent::CAUSE_BLOCK_EXPLOSION, $damage); }else{ $ev = new EntityDamageEvent($entity, EntityDamageEvent::CAUSE_BLOCK_EXPLOSION, $damage); } if($entity->attack($ev->getFinalDamage(), $ev) === true){ $ev->useArmors(); } $entity->setMotion($motion->multiply($impact)); } } $air = Item::get(Item::AIR); foreach($this->affectedBlocks as $block){ if($block->getId() === Block::TNT){ $mot = (new Random())->nextSignedFloat() * M_PI * 2; $tnt = Entity::createEntity("PrimedTNT", $this->level->getChunk($block->x >> 4, $block->z >> 4), new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $block->x + 0.5), new DoubleTag("", $block->y), new DoubleTag("", $block->z + 0.5) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", -sin($mot) * 0.02), new DoubleTag("", 0.2), new DoubleTag("", -cos($mot) * 0.02) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", 0), new FloatTag("", 0) ]), "Fuse" => new ByteTag("Fuse", mt_rand(10, 30)) ])); $tnt->spawnToAll(); }elseif($this->dropItem and mt_rand(0, 100) < $yield){ foreach($block->getDrops($air) as $drop){ $this->level->dropItem($block->add(0.5, 0.5, 0.5), Item::get(...$drop)); } } $this->level->setBlockIdAt($block->x, $block->y, $block->z, 0); $pos = new Vector3($block->x, $block->y, $block->z); for($side = 0; $side < 5; $side++){ $sideBlock = $pos->getSide($side); if(!isset($this->affectedBlocks[$index = Level::blockHash($sideBlock->x, $sideBlock->y, $sideBlock->z)]) and !isset($updateBlocks[$index])){ $this->level->getServer()->getPluginManager()->callEvent($ev = new BlockUpdateEvent($this->level->getBlock($sideBlock))); if(!$ev->isCancelled()){ $ev->getBlock()->onUpdate(Level::BLOCK_UPDATE_NORMAL); } $updateBlocks[$index] = true; } } $send[] = new Vector3($block->x - $source->x, $block->y - $source->y, $block->z - $source->z); } $pk = new ExplodePacket(); $pk->x = $this->source->x; $pk->y = $this->source->y; $pk->z = $this->source->z; $pk->radius = $this->size; $pk->records = $send; $this->level->addChunkPacket($source->x >> 4, $source->z >> 4, $pk); $this->level->addParticle(new HugeExplodeSeedParticle($source)); return true; } } ================================================ FILE: src/pocketmine/level/Level.php ================================================ class Level implements ChunkManager, Metadatable{ private static $levelIdCounter = 1; private static $chunkLoaderCounter = 1; public static $COMPRESSION_LEVEL = 8; const Y_MASK = 0xFF; const Y_MAX = 0x100; //256 const BLOCK_UPDATE_NORMAL = 1; const BLOCK_UPDATE_RANDOM = 2; const BLOCK_UPDATE_SCHEDULED = 3; const BLOCK_UPDATE_WEAK = 4; const BLOCK_UPDATE_TOUCH = 5; const TIME_DAY = 0; const TIME_SUNSET = 12000; const TIME_NIGHT = 14000; const TIME_SUNRISE = 23000; const TIME_FULL = 24000; const DIMENSION_NORMAL = 0; const DIMENSION_NETHER = 1; /** @var Tile[] */ private $tiles = []; private $motionToSend = []; private $moveToSend = []; /** @var Player[] */ private $players = []; /** @var Entity[] */ private $entities = []; /** @var Entity[] */ public $updateEntities = []; /** @var Tile[] */ public $updateTiles = []; private $blockCache = []; /** @var DataPacket[] */ private $chunkCache = []; private $cacheChunks = false; private $sendTimeTicker = 0; /** @var Server */ private $server; /** @var int */ private $levelId; /** @var LevelProvider */ private $provider; /** @var ChunkLoader[] */ private $loaders = []; /** @var int[] */ private $loaderCounter = []; /** @var ChunkLoader[][] */ private $chunkLoaders = []; /** @var Player[][] */ private $playerLoaders = []; /** @var DataPacket[] */ private $chunkPackets = []; /** @var float[] */ private $unloadQueue; private $time; public $stopTime; private $folderName; /** @var Chunk[] */ private $chunks = []; /** @var Vector3[][] */ private $changedBlocks = []; /** @var ReversePriorityQueue */ private $updateQueue; private $updateQueueIndex = []; /** @var Player[][] */ private $chunkSendQueue = []; private $chunkSendTasks = []; private $chunkPopulationQueue = []; private $chunkPopulationLock = []; private $chunkGenerationQueue = []; private $chunkGenerationQueueSize = 8; private $chunkPopulationQueueSize = 2; private $autoSave = true; /** @var BlockMetadataStore */ private $blockMetadata; /** @var Position */ private $temporalPosition; /** @var Vector3 */ private $temporalVector; /** @var \SplFixedArray */ private $blockStates; public $sleepTicks = 0; private $chunkTickRadius; private $chunkTickList = []; private $chunksPerTick; private $clearChunksOnTick; private $randomTickBlocks = [ Block::GRASS => Grass::class, Block::SAPLING => Sapling::class, Block::LEAVES => Leaves::class, Block::WHEAT_BLOCK => Wheat::class, Block::COCOA_BLOCK => CocoaBlock::class, Block::FARMLAND => Farmland::class, Block::SNOW_LAYER => SnowLayer::class, Block::ICE => Ice::class, Block::CACTUS => Cactus::class, Block::SUGARCANE_BLOCK => Sugarcane::class, Block::RED_MUSHROOM => RedMushroom::class, Block::BROWN_MUSHROOM => BrownMushroom::class, Block::PUMPKIN_STEM => PumpkinStem::class, Block::NETHER_WART_BLOCK => NetherWart::class, Block::MELON_STEM => MelonStem::class, //Block::VINE => true, Block::MYCELIUM => Mycelium::class, //Block::COCOA_BLOCK => true, Block::CARROT_BLOCK => Carrot::class, Block::POTATO_BLOCK => Potato::class, Block::LEAVES2 => Leaves2::class, Block::BEETROOT_BLOCK => Beetroot::class, ]; /** @var LevelTimings */ public $timings; private $tickRate; public $tickRateTime = 0; public $tickRateCounter = 0; /** @var Generator */ private $generator; /** @var Generator */ private $generatorInstance; private $closed = false; /** @var Weather */ private $weather; private $blockTempData = []; private $dimension = self::DIMENSION_NORMAL; /** * This method is internal use only. Do not use this in plugins * * @param Vector3 $pos * @param $data */ public function setBlockTempData(Vector3 $pos, $data = null){ if($data == null and isset($this->blockTempData[self::blockHash($pos->x, $pos->y, $pos->z)])){ unset($this->blockTempData[self::blockHash($pos->x, $pos->y, $pos->z)]); }else{ $this->blockTempData[self::blockHash($pos->x, $pos->y, $pos->z)] = $data; } } /** * This method is internal use only. Do not use this in plugins * * @param Vector3 $pos * @return int */ public function getBlockTempData(Vector3 $pos){ if(isset($this->blockTempData[self::blockHash($pos->x, $pos->y, $pos->z)])){ return $this->blockTempData[self::blockHash($pos->x, $pos->y, $pos->z)]; } return 0; } /** * Returns the chunk unique hash/key * * @param int $x * @param int $z * * @return string */ public static function chunkHash(int $x, int $z){ return PHP_INT_SIZE === 8 ? (($x & 0xFFFFFFFF) << 32) | ($z & 0xFFFFFFFF) : $x . ":" . $z; } public static function blockHash(int $x, int $y, int $z){ return PHP_INT_SIZE === 8 ? (($x & 0xFFFFFFF) << 36) | (($y & Level::Y_MASK) << 28) | ($z & 0xFFFFFFF) : $x . ":" . $y . ":" . $z; } public static function getBlockXYZ($hash, &$x, &$y, &$z){ if(PHP_INT_SIZE === 8){ $x = $hash >> 36; $y = ($hash >> 28) & Level::Y_MASK; //it's always positive $z = ($hash & 0xFFFFFFF) << 36 >> 36; }else{ $hash = explode(":", $hash); $x = (int) $hash[0]; $y = (int) $hash[1]; $z = (int) $hash[2]; } } public static function getXZ($hash, &$x, &$z){ if(PHP_INT_SIZE === 8){ $x = $hash >> 32; $z = ($hash & 0xFFFFFFFF) << 32 >> 32; }else{ $hash = explode(":", $hash); $x = (int) $hash[0]; $z = (int) $hash[1]; } } public static function generateChunkLoaderId(ChunkLoader $loader) : int{ if($loader->getLoaderId() === 0 or $loader->getLoaderId() === null or $loader->getLoaderId() === null){ return self::$chunkLoaderCounter++; }else{ throw new \InvalidStateException("ChunkLoader has a loader id already assigned: " . $loader->getLoaderId()); } } /** * Init the default level data * * @param Server $server * @param string $name * @param string $path * @param string $provider Class that extends LevelProvider * * @throws \Throwable */ public function __construct(Server $server, string $name, string $path, string $provider){ $this->blockStates = Block::$fullList; $this->levelId = static::$levelIdCounter++; $this->blockMetadata = new BlockMetadataStore($this); $this->server = $server; $this->autoSave = $server->getAutoSave(); /** @var LevelProvider $provider */ if(is_subclass_of($provider, LevelProvider::class, true)){ $this->provider = new $provider($this, $path); }else{ throw new LevelException("Provider is not a subclass of LevelProvider"); } $this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.level.preparing", [$this->provider->getName()])); $this->generator = Generator::getGenerator($this->provider->getGenerator()); $this->folderName = $name; $this->updateQueue = new ReversePriorityQueue(); $this->updateQueue->setExtractFlags(\SplPriorityQueue::EXTR_BOTH); $this->time = (int) $this->provider->getTime(); $this->chunkTickRadius = min($this->server->getViewDistance(), max(1, (int) $this->server->getProperty("chunk-ticking.tick-radius", 4))); $this->chunksPerTick = (int) $this->server->getProperty("chunk-ticking.per-tick", 40); $this->chunkGenerationQueueSize = (int) $this->server->getProperty("chunk-generation.queue-size", 8); $this->chunkPopulationQueueSize = (int) $this->server->getProperty("chunk-generation.population-queue-size", 2); $this->chunkTickList = []; $this->clearChunksOnTick = (bool) $this->server->getProperty("chunk-ticking.clear-tick-list", true); $this->cacheChunks = (bool) $this->server->getProperty("chunk-sending.cache-chunks", false); $this->timings = new LevelTimings($this); $this->temporalPosition = new Position(0, 0, 0, $this); $this->temporalVector = new Vector3(0, 0, 0); $this->tickRate = 1; $this->weather = new Weather($this, 0); if($this->server->netherEnabled and $this->server->netherName == $this->folderName) $this->setDimension(self::DIMENSION_NETHER); else $this->setDimension(self::DIMENSION_NORMAL); if($this->server->weatherEnabled and $this->getDimension() == self::DIMENSION_NORMAL){ $this->weather->setCanCalculate(true); }else $this->weather->setCanCalculate(false); } public function setDimension(int $dimension){ $this->dimension = $dimension; } public function getDimension() : int{ return $this->dimension; } /** * @return Weather */ public function getWeather(){ return $this->weather; } public function getTickRate() : int{ return $this->tickRate; } public function getTickRateTime(){ return $this->tickRateTime; } public function setTickRate(int $tickRate){ $this->tickRate = $tickRate; } public function initLevel(){ $generator = $this->generator; $this->generatorInstance = new $generator($this->provider->getGeneratorOptions()); $this->generatorInstance->init($this, new Random($this->getSeed())); $this->registerGenerator(); } public function getWaterHeight() : int{ if($this->generatorInstance instanceof Generator){ return $this->generatorInstance->getWaterHeight(); } return 0; } public function registerGenerator(){ $size = $this->server->getScheduler()->getAsyncTaskPoolSize(); for($i = 0; $i < $size; ++$i){ $this->server->getScheduler()->scheduleAsyncTaskToWorker(new GeneratorRegisterTask($this, $this->generatorInstance), $i); } } public function unregisterGenerator(){ $size = $this->server->getScheduler()->getAsyncTaskPoolSize(); for($i = 0; $i < $size; ++$i){ $this->server->getScheduler()->scheduleAsyncTaskToWorker(new GeneratorUnregisterTask($this, $this->generatorInstance), $i); } } /** * @return BlockMetadataStore */ public function getBlockMetadata() : BlockMetadataStore{ return $this->blockMetadata; } /** * @return Server */ public function getServer() : Server{ return $this->server; } /** * @return LevelProvider */ final public function getProvider(){ return $this->provider; } /** * Returns the unique level identifier * * @return int */ final public function getId() : int{ return $this->levelId; } public function isClosed() : bool{ return $this->closed; } public function close(){ assert(!$this->closed, "Tried to close a level which is already closed"); if($this->getAutoSave()){ $this->save(); } foreach($this->chunks as $chunk){ $this->unloadChunk($chunk->getX(), $chunk->getZ(), false); } $this->unregisterGenerator(); $this->provider->close(); $this->provider = null; $this->blockMetadata = null; $this->blockCache = []; $this->temporalPosition = null; $this->closed = true; } public function addSound(Sound $sound, array $players = null){ $pk = $sound->encode(); if($players === null){ if($pk !== null){ if(!is_array($pk)){ $this->addChunkPacket($sound->x >> 4, $sound->z >> 4, $pk); }else{ foreach($pk as $e){ $this->addChunkPacket($sound->x >> 4, $sound->z >> 4, $e); } } } }else{ if($pk !== null){ if(!is_array($pk)){ $this->server->broadcastPacket($players, $pk); }else{ $this->server->batchPackets($players, $pk, false); } } } } public function addParticle(Particle $particle, array $players = null){ $pk = $particle->encode(); if($players === null){ if($pk !== null){ if(!is_array($pk)){ $this->addChunkPacket($particle->x >> 4, $particle->z >> 4, $pk); }else{ foreach($pk as $e){ $this->addChunkPacket($particle->x >> 4, $particle->z >> 4, $e); } } } }else{ if($pk !== null){ if(!is_array($pk)){ $this->server->broadcastPacket($players, $pk); }else{ $this->server->batchPackets($players, $pk, false); } } } } /** * @return bool */ public function getAutoSave() : bool{ return $this->autoSave; } /** * @param bool $value */ public function setAutoSave(bool $value){ $this->autoSave = $value; } /** * Unloads the current level from memory safely * * @param bool $force default false, force unload of default level * * @return bool */ public function unload(bool $force = false) : bool{ $ev = new LevelUnloadEvent($this); if($this === $this->server->getDefaultLevel() and $force !== true){ $ev->setCancelled(true); } $this->server->getPluginManager()->callEvent($ev); if(!$force and $ev->isCancelled()){ return false; } $this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.level.unloading", [$this->getName()])); $defaultLevel = $this->server->getDefaultLevel(); foreach($this->getPlayers() as $player){ if($this === $defaultLevel or $defaultLevel === null){ $player->close($player->getLeaveMessage(), "Forced default level unload"); }elseif($defaultLevel instanceof Level){ $player->teleport($this->server->getDefaultLevel()->getSafeSpawn()); } } if($this === $defaultLevel){ $this->server->setDefaultLevel(null); } $this->close(); return true; } /** * Gets the players being used in a specific chunk * * @param int $chunkX * @param int $chunkZ * * @return Player[] */ public function getChunkPlayers(int $chunkX, int $chunkZ) : array{ return isset($this->playerLoaders[$index = Level::chunkHash($chunkX, $chunkZ)]) ? $this->playerLoaders[$index] : []; } /** * Gets the chunk loaders being used in a specific chunk * * @param int $chunkX * @param int $chunkZ * * @return ChunkLoader[] */ public function getChunkLoaders(int $chunkX, int $chunkZ) : array{ return isset($this->chunkLoaders[$index = Level::chunkHash($chunkX, $chunkZ)]) ? $this->chunkLoaders[$index] : []; } public function addChunkPacket(int $chunkX, int $chunkZ, DataPacket $packet){ if(!isset($this->chunkPackets[$index = Level::chunkHash($chunkX, $chunkZ)])){ $this->chunkPackets[$index] = [$packet]; }else{ $this->chunkPackets[$index][] = $packet; } } public function registerChunkLoader(ChunkLoader $loader, int $chunkX, int $chunkZ, bool $autoLoad = true){ $hash = $loader->getLoaderId(); if(!isset($this->chunkLoaders[$index = Level::chunkHash($chunkX, $chunkZ)])){ $this->chunkLoaders[$index] = []; $this->playerLoaders[$index] = []; }elseif(isset($this->chunkLoaders[$index][$hash])){ return; } $this->chunkLoaders[$index][$hash] = $loader; if($loader instanceof Player){ $this->playerLoaders[$index][$hash] = $loader; } if(!isset($this->loaders[$hash])){ $this->loaderCounter[$hash] = 1; $this->loaders[$hash] = $loader; }else{ ++$this->loaderCounter[$hash]; } $this->cancelUnloadChunkRequest($chunkX, $chunkZ); if($autoLoad){ $this->loadChunk($chunkX, $chunkZ); } } public function unregisterChunkLoader(ChunkLoader $loader, int $chunkX, int $chunkZ){ if(isset($this->chunkLoaders[$index = Level::chunkHash($chunkX, $chunkZ)][$hash = $loader->getLoaderId()])){ unset($this->chunkLoaders[$index][$hash]); unset($this->playerLoaders[$index][$hash]); if(count($this->chunkLoaders[$index]) === 0){ unset($this->chunkLoaders[$index]); unset($this->playerLoaders[$index]); $this->unloadChunkRequest($chunkX, $chunkZ, true); } if(--$this->loaderCounter[$hash] === 0){ unset($this->loaderCounter[$hash]); unset($this->loaders[$hash]); } } } /** * WARNING: Do not use this, it's only for internal use. * Changes to this function won't be recorded on the version. */ public function checkTime(){ if($this->stopTime == true){ return; }else{ $this->time += 1; } } /** * WARNING: Do not use this, it's only for internal use. * Changes to this function won't be recorded on the version. */ public function sendTime(){ $pk = new SetTimePacket(); $pk->time = (int) $this->time; $pk->started = $this->stopTime == false; $this->server->broadcastPacket($this->players, $pk); } /** * WARNING: Do not use this, it's only for internal use. * Changes to this function won't be recorded on the version. * * @param int $currentTick * * @return bool */ public function doTick(int $currentTick){ $this->timings->doTick->startTiming(); $this->checkTime(); if(++$this->sendTimeTicker === 280){ $this->sendTime(); $this->sendTimeTicker = 0; } $this->weather->calcWeather($currentTick); $this->unloadChunks(); //Do block updates $this->timings->doTickPending->startTiming(); if($this->updateQueue->count() > 0 and $this->updateQueue->current()["priority"] <= $currentTick){ $block = $this->getBlock($this->updateQueue->extract()["data"]); unset($this->updateQueueIndex[Level::blockHash($block->x, $block->y, $block->z)]); $block->onUpdate(self::BLOCK_UPDATE_SCHEDULED); } $this->timings->doTickPending->stopTiming(); $this->timings->entityTick->startTiming(); //Update entities that need update Timings::$tickEntityTimer->startTiming(); foreach($this->updateEntities as $id => $entity){ if($entity->closed or !$entity->onUpdate($currentTick)){ unset($this->updateEntities[$id]); } } Timings::$tickEntityTimer->stopTiming(); $this->timings->entityTick->stopTiming(); $this->timings->tileEntityTick->startTiming(); Timings::$tickTileEntityTimer->startTiming(); //Update tiles that need update if(count($this->updateTiles) > 0){ foreach($this->updateTiles as $id => $tile){ if($tile->onUpdate() !== true){ unset($this->updateTiles[$id]); } } } Timings::$tickTileEntityTimer->stopTiming(); $this->timings->tileEntityTick->stopTiming(); $this->timings->doTickTiles->startTiming(); if(($currentTick % 2) === 0) $this->tickChunks(); $this->timings->doTickTiles->stopTiming(); if(count($this->changedBlocks) > 0){ if(count($this->players) > 0){ foreach($this->changedBlocks as $index => $blocks){ unset($this->chunkCache[$index]); Level::getXZ($index, $chunkX, $chunkZ); if(count($blocks) > 512){ $chunk = $this->getChunk($chunkX, $chunkZ); foreach($this->getChunkPlayers($chunkX, $chunkZ) as $p){ $p->onChunkChanged($chunk); } }else{ $this->sendBlocks($this->getChunkPlayers($chunkX, $chunkZ), $blocks, UpdateBlockPacket::FLAG_ALL); } } }else{ $this->chunkCache = []; } $this->changedBlocks = []; } $this->processChunkRequest(); if($this->sleepTicks > 0 and --$this->sleepTicks <= 0){ $this->checkSleep(); } foreach($this->moveToSend as $index => $entry){ Level::getXZ($index, $chunkX, $chunkZ); foreach($entry as $e) { $this->addChunkPacket($chunkX, $chunkZ, $e); } } $this->moveToSend = []; foreach($this->motionToSend as $index => $entry){ Level::getXZ($index, $chunkX, $chunkZ); foreach($entry as $entity){ $pk = new SetEntityMotionPacket(); $pk->eid = $entity[0]; $pk->motionX = $entity[1]; $pk->motionY = $entity[2]; $pk->motionZ = $entity[3]; $this->addChunkPacket($chunkX, $chunkZ, $pk); } } $this->motionToSend = []; foreach($this->chunkPackets as $index => $entries){ Level::getXZ($index, $chunkX, $chunkZ); $chunkPlayers = $this->getChunkPlayers($chunkX, $chunkZ); if(count($chunkPlayers) > 0){ foreach($entries as $pk){ $this->server->broadcastPacket($chunkPlayers, $pk); } } } $this->chunkPackets = []; $this->timings->doTick->stopTiming(); } public function checkSleep(){ if(count($this->players) === 0){ return; } $resetTime = true; foreach($this->getPlayers() as $p){ if(!$p->isSleeping()){ $resetTime = false; break; } } if($resetTime){ $time = $this->getTime() % Level::TIME_FULL; if($time >= Level::TIME_NIGHT and $time < Level::TIME_SUNRISE){ $this->setTime($this->getTime() + Level::TIME_FULL - $time); foreach($this->getPlayers() as $p){ $p->stopSleep(); } } } } public function sendBlockExtraData(int $x, int $y, int $z, int $id, int $data, array $targets = null){ $pk = new LevelEventPacket; $pk->evid = LevelEventPacket::EVENT_SET_DATA; $pk->x = $x + 0.5; $pk->y = $y + 0.5; $pk->z = $z + 0.5; $pk->data = ($data << 8) | $id; $this->server->broadcastPacket($targets === null ? $this->getChunkPlayers($x >> 4, $z >> 4) : $targets, $pk); } /** * @param Player[] $target * @param Block[] $blocks * @param int $flags * @param bool $optimizeRebuilds */ public function sendBlocks(array $target, array $blocks, $flags = UpdateBlockPacket::FLAG_NONE, bool $optimizeRebuilds = false){ if($optimizeRebuilds){ $chunks = []; foreach($blocks as $b){ if($b === null){ continue; } $pk = new UpdateBlockPacket(); $first = false; if(!isset($chunks[$index = Level::chunkHash($b->x >> 4, $b->z >> 4)])){ $chunks[$index] = true; $first = true; } $pk->x = $b->x; $pk->z = $b->z; $pk->y = $b->y; if($b instanceof Block){ $pk->blockId = $b->getId(); $pk->blockData = $b->getDamage(); }else{ $fullBlock = $this->getFullBlock($b->x, $b->y, $b->z); $pk->blockId = $fullBlock >> 4; $pk->blockData = $fullBlock & 0xf; } $pk->flags = $first ? $flags : UpdateBlockPacket::FLAG_NONE; $this->server->broadcastPacket($target, $pk); } }else{ foreach($blocks as $b){ if($b === null){ continue; } $pk = new UpdateBlockPacket(); $pk->x = $b->x; $pk->z = $b->z; $pk->y = $b->y; if($b instanceof Block){ $pk->blockId = $b->getId(); $pk->blockData = $b->getDamage(); }else{ $fullBlock = $this->getFullBlock($b->x, $b->y, $b->z); $pk->blockId = $fullBlock >> 4; $pk->blockData = $fullBlock & 0xf; } $pk->flags = $flags; $this->server->broadcastPacket($target, $pk); } } } public function clearCache(bool $full = false){ if($full){ $this->chunkCache = []; $this->blockCache = []; }else{ if(count($this->chunkCache) > 768){ $this->chunkCache = []; } if(count($this->blockCache) > 2048){ $this->blockCache = []; } } } public function clearChunkCache(int $chunkX, int $chunkZ){ unset($this->chunkCache[Level::chunkHash($chunkX, $chunkZ)]); } private function tickChunks(){ if($this->chunksPerTick <= 0 or count($this->loaders) === 0){ $this->chunkTickList = []; return; } $chunksPerLoader = min(200, max(1, (int) ((($this->chunksPerTick - count($this->loaders)) / count($this->loaders)) + 0.5))); $randRange = 3 + $chunksPerLoader / 30; $randRange = (int) ($randRange > $this->chunkTickRadius ? $this->chunkTickRadius : $randRange); foreach($this->loaders as $loader){ $chunkX = $loader->getX() >> 4; $chunkZ = $loader->getZ() >> 4; $index = Level::chunkHash($chunkX, $chunkZ); $existingLoaders = max(0, isset($this->chunkTickList[$index]) ? $this->chunkTickList[$index] : 0); $this->chunkTickList[$index] = $existingLoaders + 1; for($chunk = 0; $chunk < $chunksPerLoader; ++$chunk){ $dx = mt_rand(-$randRange, $randRange); $dz = mt_rand(-$randRange, $randRange); $hash = Level::chunkHash($dx + $chunkX, $dz + $chunkZ); if(!isset($this->chunkTickList[$hash]) and isset($this->chunks[$hash])){ $this->chunkTickList[$hash] = -1; } } } foreach($this->chunkTickList as $index => $loaders){ Level::getXZ($index, $chunkX, $chunkZ); if(!isset($this->chunks[$index]) or ($chunk = $this->getChunk($chunkX, $chunkZ, false)) === null){ unset($this->chunkTickList[$index]); continue; }elseif($loaders <= 0){ unset($this->chunkTickList[$index]); } foreach($chunk->getEntities() as $entity){ $entity->scheduleUpdate(); } foreach($chunk->getSubChunks() as $Y => $subChunk){ if(!$subChunk->isEmpty()){ $k = mt_rand(0, 0x7fffffff); for($i = 0; $i < 3; ++$i, $k >>= 10){ $x = $k & 0x0f; $y = ($k >> 8) & 0x0f; $z = ($k >> 16) & 0x0f; $blockId = $subChunk->getBlockId($x, $y, $z); if(isset($this->randomTickBlocks[$blockId])){ $class = $this->randomTickBlocks[$blockId]; /** @var Block $block */ $block = new $class($subChunk->getBlockData($x, $y, $z)); $block->x = $chunkX * 16 + $x; $block->y = ($Y << 4) + $y; $block->z = $chunkZ * 16 + $z; $block->level = $this; $block->onUpdate(self::BLOCK_UPDATE_RANDOM); } } } } } if($this->clearChunksOnTick){ $this->chunkTickList = []; } } public function __debugInfo() : array{ return []; } /** * @param bool $force * * @return bool */ public function save(bool $force = false) : bool{ if(!$this->getAutoSave() and !$force){ return false; } $this->server->getPluginManager()->callEvent(new LevelSaveEvent($this)); $this->provider->setTime((int) $this->time); $this->saveChunks(); if($this->provider instanceof BaseLevelProvider){ $this->provider->saveLevelData(); } return true; } public function saveChunks(){ foreach($this->chunks as $chunk){ if($chunk->hasChanged()){ $this->provider->setChunk($chunk->getX(), $chunk->getZ(), $chunk); $this->provider->saveChunk($chunk->getX(), $chunk->getZ()); $chunk->setChanged(false); } } } /** * @param Vector3 $pos */ public function updateAround(Vector3 $pos){ $pos = $pos->floor(); $this->server->getPluginManager()->callEvent($ev = new BlockUpdateEvent($this->getBlock($this->temporalVector->setComponents($pos->x, $pos->y - 1, $pos->z)))); if(!$ev->isCancelled()){ $ev->getBlock()->onUpdate(self::BLOCK_UPDATE_NORMAL); } $this->server->getPluginManager()->callEvent($ev = new BlockUpdateEvent($this->getBlock($this->temporalVector->setComponents($pos->x, $pos->y + 1, $pos->z)))); if(!$ev->isCancelled()){ $ev->getBlock()->onUpdate(self::BLOCK_UPDATE_NORMAL); } $this->server->getPluginManager()->callEvent($ev = new BlockUpdateEvent($this->getBlock($this->temporalVector->setComponents($pos->x - 1, $pos->y, $pos->z)))); if(!$ev->isCancelled()){ $ev->getBlock()->onUpdate(self::BLOCK_UPDATE_NORMAL); } $this->server->getPluginManager()->callEvent($ev = new BlockUpdateEvent($this->getBlock($this->temporalVector->setComponents($pos->x + 1, $pos->y, $pos->z)))); if(!$ev->isCancelled()){ $ev->getBlock()->onUpdate(self::BLOCK_UPDATE_NORMAL); } $this->server->getPluginManager()->callEvent($ev = new BlockUpdateEvent($this->getBlock($this->temporalVector->setComponents($pos->x, $pos->y, $pos->z - 1)))); if(!$ev->isCancelled()){ $ev->getBlock()->onUpdate(self::BLOCK_UPDATE_NORMAL); } $this->server->getPluginManager()->callEvent($ev = new BlockUpdateEvent($this->getBlock($this->temporalVector->setComponents($pos->x, $pos->y, $pos->z + 1)))); if(!$ev->isCancelled()){ $ev->getBlock()->onUpdate(self::BLOCK_UPDATE_NORMAL); } } /** * @param Vector3 $pos * @param int $delay */ public function scheduleUpdate(Vector3 $pos, int $delay){ if(isset($this->updateQueueIndex[$index = Level::blockHash($pos->x, $pos->y, $pos->z)]) and $this->updateQueueIndex[$index] <= $delay){ return; } $this->updateQueueIndex[$index] = $delay; $this->updateQueue->insert(new Vector3((int) $pos->x, (int) $pos->y, (int) $pos->z), (int) $delay + $this->server->getTick()); } /** * @param AxisAlignedBB $bb * @param bool $targetFirst * * @return Block[] */ public function getCollisionBlocks(AxisAlignedBB $bb, bool $targetFirst = false) : array{ $minX = Math::floorFloat($bb->minX); $minY = Math::floorFloat($bb->minY); $minZ = Math::floorFloat($bb->minZ); $maxX = Math::ceilFloat($bb->maxX); $maxY = Math::ceilFloat($bb->maxY); $maxZ = Math::ceilFloat($bb->maxZ); $collides = []; if($targetFirst){ for($z = $minZ; $z <= $maxZ; ++$z){ for($x = $minX; $x <= $maxX; ++$x){ for($y = $minY; $y <= $maxY; ++$y){ $block = $this->getBlock($this->temporalVector->setComponents($x, $y, $z)); if($block->getId() !== 0 and $block->collidesWithBB($bb)){ return [$block]; } } } } }else{ for($z = $minZ; $z <= $maxZ; ++$z){ for($x = $minX; $x <= $maxX; ++$x){ for($y = $minY; $y <= $maxY; ++$y){ $block = $this->getBlock($this->temporalVector->setComponents($x, $y, $z)); if($block->getId() !== 0 and $block->collidesWithBB($bb)){ $collides[] = $block; } } } } } return $collides; } /** * @param Vector3 $pos * * @return bool */ public function isFullBlock(Vector3 $pos) : bool{ if($pos instanceof Block){ if($pos->isSolid()){ return true; } $bb = $pos->getBoundingBox(); }else{ $bb = $this->getBlock($pos)->getBoundingBox(); } return $bb !== null and $bb->getAverageEdgeLength() >= 1; } /** * @param Entity $entity * @param AxisAlignedBB $bb * @param boolean $entities * * @return AxisAlignedBB[] */ public function getCollisionCubes(Entity $entity, AxisAlignedBB $bb, bool $entities = true) : array{ $minX = Math::floorFloat($bb->minX); $minY = Math::floorFloat($bb->minY); $minZ = Math::floorFloat($bb->minZ); $maxX = Math::ceilFloat($bb->maxX); $maxY = Math::ceilFloat($bb->maxY); $maxZ = Math::ceilFloat($bb->maxZ); $collides = []; for($z = $minZ; $z <= $maxZ; ++$z){ for($x = $minX; $x <= $maxX; ++$x){ for($y = $minY; $y <= $maxY; ++$y){ $block = $this->getBlock($this->temporalVector->setComponents($x, $y, $z)); if(!$block->canPassThrough() and $block->collidesWithBB($bb)){ $collides[] = $block->getBoundingBox(); } } } } if($entities){ foreach($this->getCollidingEntities($bb->grow(0.25, 0.25, 0.25), $entity) as $ent){ $collides[] = clone $ent->boundingBox; } } return $collides; } /* public function rayTraceBlocks(Vector3 $pos1, Vector3 $pos2, $flag = false, $flag1 = false, $flag2 = false){ if(!is_nan($pos1->x) and !is_nan($pos1->y) and !is_nan($pos1->z)){ if(!is_nan($pos2->x) and !is_nan($pos2->y) and !is_nan($pos2->z)){ $x1 = (int) $pos1->x; $y1 = (int) $pos1->y; $z1 = (int) $pos1->z; $x2 = (int) $pos2->x; $y2 = (int) $pos2->y; $z2 = (int) $pos2->z; $block = $this->getBlock(Vector3::createVector($x1, $y1, $z1)); if(!$flag1 or $block->getBoundingBox() !== null){ $ob = $block->calculateIntercept($pos1, $pos2); if($ob !== null){ return $ob; } } $movingObjectPosition = null; $k = 200; while($k-- >= 0){ if(is_nan($pos1->x) or is_nan($pos1->y) or is_nan($pos1->z)){ return null; } if($x1 === $x2 and $y1 === $y2 and $z1 === $z2){ return $flag2 ? $movingObjectPosition : null; } $flag3 = true; $flag4 = true; $flag5 = true; $i = 999; $j = 999; $k = 999; if($x1 > $x2){ $i = $x2 + 1; }elseif($x1 < $x2){ $i = $x2; }else{ $flag3 = false; } if($y1 > $y2){ $j = $y2 + 1; }elseif($y1 < $y2){ $j = $y2; }else{ $flag4 = false; } if($z1 > $z2){ $k = $z2 + 1; }elseif($z1 < $z2){ $k = $z2; }else{ $flag5 = false; } //TODO } } } } */ public function getFullLight(Vector3 $pos) : int{ $chunk = $this->getChunk($pos->x >> 4, $pos->z >> 4, false); $level = 0; if($chunk !== null){ $level = $chunk->getBlockSkyLight($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f); //TODO: decrease light level by time of day if($level < 15){ $level = max($chunk->getBlockLight($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f)); } } return $level; } /** * @param $x * @param $y * @param $z * * @return int bitmap, (id << 4) | data */ public function getFullBlock(int $x, int $y, int $z) : int{ return $this->getChunk($x >> 4, $z >> 4, false)->getFullBlock($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f); } /** * Gets the Block object on the Vector3 location * * @param Vector3 $pos * @param boolean $cached * * @return Block */ public function getBlock(Vector3 $pos, $cached = true) : Block{ $pos = $pos->floor(); $index = Level::blockHash($pos->x, $pos->y, $pos->z); if($cached and isset($this->blockCache[$index])){ return $this->blockCache[$index]; }elseif($pos->y >= 0 and $pos->y < $this->provider->getWorldHeight() and isset($this->chunks[$chunkIndex = Level::chunkHash($pos->x >> 4, $pos->z >> 4)])){ $fullState = $this->chunks[$chunkIndex]->getFullBlock($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f); }else{ $fullState = 0; } $block = clone $this->blockStates[$fullState & 0xfff]; $block->x = $pos->x; $block->y = $pos->y; $block->z = $pos->z; $block->level = $this; return $this->blockCache[$index] = $block; } public function updateAllLight(Vector3 $pos){ $this->updateBlockSkyLight($pos->x, $pos->y, $pos->z); $this->updateBlockLight($pos->x, $pos->y, $pos->z); } public function updateBlockSkyLight(int $x, int $y, int $z){ //TODO } public function updateBlockLight(int $x, int $y, int $z){ $lightPropagationQueue = new \SplQueue(); $lightRemovalQueue = new \SplQueue(); $visited = []; $removalVisited = []; $oldLevel = $this->getBlockLightAt($x, $y, $z); $newLevel = (int) Block::$light[$this->getBlockIdAt($x, $y, $z)]; if($oldLevel !== $newLevel){ $this->setBlockLightAt($x, $y, $z, $newLevel); if($newLevel < $oldLevel){ $removalVisited[Level::blockHash($x, $y, $z)] = true; $lightRemovalQueue->enqueue([new Vector3($x, $y, $z), $oldLevel]); }else{ $visited[Level::blockHash($x, $y, $z)] = true; $lightPropagationQueue->enqueue(new Vector3($x, $y, $z)); } } while(!$lightRemovalQueue->isEmpty()){ /** @var Vector3 $node */ $val = $lightRemovalQueue->dequeue(); $node = $val[0]; $lightLevel = $val[1]; $this->computeRemoveBlockLight($node->x - 1, $node->y, $node->z, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); $this->computeRemoveBlockLight($node->x + 1, $node->y, $node->z, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); $this->computeRemoveBlockLight($node->x, $node->y - 1, $node->z, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); $this->computeRemoveBlockLight($node->x, $node->y + 1, $node->z, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); $this->computeRemoveBlockLight($node->x, $node->y, $node->z - 1, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); $this->computeRemoveBlockLight($node->x, $node->y, $node->z + 1, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); } while(!$lightPropagationQueue->isEmpty()){ /** @var Vector3 $node */ $node = $lightPropagationQueue->dequeue(); $lightLevel = $this->getBlockLightAt($node->x, $node->y, $node->z) - (int) Block::$lightFilter[$this->getBlockIdAt($node->x, $node->y, $node->z)]; if($lightLevel >= 1){ $this->computeSpreadBlockLight($node->x - 1, $node->y, $node->z, $lightLevel, $lightPropagationQueue, $visited); $this->computeSpreadBlockLight($node->x + 1, $node->y, $node->z, $lightLevel, $lightPropagationQueue, $visited); $this->computeSpreadBlockLight($node->x, $node->y - 1, $node->z, $lightLevel, $lightPropagationQueue, $visited); $this->computeSpreadBlockLight($node->x, $node->y + 1, $node->z, $lightLevel, $lightPropagationQueue, $visited); $this->computeSpreadBlockLight($node->x, $node->y, $node->z - 1, $lightLevel, $lightPropagationQueue, $visited); $this->computeSpreadBlockLight($node->x, $node->y, $node->z + 1, $lightLevel, $lightPropagationQueue, $visited); } } } private function computeRemoveBlockLight(int $x, int $y, int $z, int $currentLight, \SplQueue $queue, \SplQueue $spreadQueue, array &$visited, array &$spreadVisited){ if($y < 0) return; $current = $this->getBlockLightAt($x, $y, $z); if($current !== 0 and $current < $currentLight){ $this->setBlockLightAt($x, $y, $z, 0); if(!isset($visited[$index = Level::blockHash($x, $y, $z)])){ $visited[$index] = true; if($current > 1){ $queue->enqueue([new Vector3($x, $y, $z), $current]); } } }elseif($current >= $currentLight){ if(!isset($spreadVisited[$index = Level::blockHash($x, $y, $z)])){ $spreadVisited[$index] = true; $spreadQueue->enqueue(new Vector3($x, $y, $z)); } } } private function computeSpreadBlockLight(int $x, int $y, int $z, int $currentLight, \SplQueue $queue, array &$visited){ if($y < 0) return; $current = $this->getBlockLightAt($x, $y, $z); if($current < $currentLight){ $this->setBlockLightAt($x, $y, $z, $currentLight); if(!isset($visited[$index = Level::blockHash($x, $y, $z)])){ $visited[$index] = true; if($currentLight > 1){ $queue->enqueue(new Vector3($x, $y, $z)); } } } } /** * Sets on Vector3 the data from a Block object, * does block updates and puts the changes to the send queue. * * If $direct is true, it'll send changes directly to players. if false, it'll be queued * and the best way to send queued changes will be done in the next tick. * This way big changes can be sent on a single chunk update packet instead of thousands of packets. * * If $update is true, it'll get the neighbour blocks (6 sides) and update them. * If you are doing big changes, you might want to set this to false, then update manually. * * @param Vector3 $pos * @param Block $block * @param bool $direct @deprecated * @param bool $update * * @return bool Whether the block has been updated or not */ public function setBlock(Vector3 $pos, Block $block, bool $direct = false, bool $update = true) : bool{ $pos = $pos->floor(); if($pos->y < 0 or $pos->y >= $this->provider->getWorldHeight()){ return false; } if($this->getChunk($pos->x >> 4, $pos->z >> 4, true)->setBlock($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f, $block->getId(), $block->getDamage())){ if(!($pos instanceof Position)){ $pos = $this->temporalPosition->setComponents($pos->x, $pos->y, $pos->z); } $block->position($pos); unset($this->blockCache[Level::blockHash($pos->x, $pos->y, $pos->z)]); $index = Level::chunkHash($pos->x >> 4, $pos->z >> 4); if($direct === true){ $this->sendBlocks($this->getChunkPlayers($pos->x >> 4, $pos->z >> 4), [$block], UpdateBlockPacket::FLAG_ALL_PRIORITY); unset($this->chunkCache[$index]); }else{ if(!isset($this->changedBlocks[$index])){ $this->changedBlocks[$index] = []; } $this->changedBlocks[$index][Level::blockHash($block->x, $block->y, $block->z)] = clone $block; } foreach($this->getChunkLoaders($pos->x >> 4, $pos->z >> 4) as $loader){ $loader->onBlockChanged($block); } if($update === true){ $this->updateAllLight($block); $this->server->getPluginManager()->callEvent($ev = new BlockUpdateEvent($block)); if(!$ev->isCancelled()){ foreach($this->getNearbyEntities(new AxisAlignedBB($block->x - 1, $block->y - 1, $block->z - 1, $block->x + 1, $block->y + 1, $block->z + 1)) as $entity){ $entity->scheduleUpdate(); } $ev->getBlock()->onUpdate(self::BLOCK_UPDATE_NORMAL); } $this->updateAround($pos); } return true; } return false; } /** * @param Vector3 $source * @param Item $item * @param Vector3 $motion * @param int $delay * * @return null|Entity|DroppedItem|\pocketmine\entity\Projectile */ public function dropItem(Vector3 $source, Item $item, Vector3 $motion = null, int $delay = 10){ $motion = $motion === null ? new Vector3(lcg_value() * 0.2 - 0.1, 0.2, lcg_value() * 0.2 - 0.1) : $motion; if($item->getId() > 0 and $item->getCount() > 0){ $itemEntity = Entity::createEntity("Item", $this->getChunk($source->getX() >> 4, $source->getZ() >> 4, true), new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $source->getX()), new DoubleTag("", $source->getY()), new DoubleTag("", $source->getZ()) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", $motion->x), new DoubleTag("", $motion->y), new DoubleTag("", $motion->z) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", lcg_value() * 360), new FloatTag("", 0) ]), "Health" => new ShortTag("Health", 5), "Item" => $item->nbtSerialize(-1, "Item"), "PickupDelay" => new ShortTag("PickupDelay", $delay) ])); $itemEntity->spawnToAll(); return $itemEntity; } return null; } /** * Tries to break a block using a item, including Player time checks if available * It'll try to lower the durability if Item is a tool, and set it to Air if broken. * * @param Vector3 $vector * @param Item &$item (if null, can break anything) * @param Player $player * @param bool $createParticles * * @return bool */ public function useBreakOn(Vector3 $vector, Item &$item = null, Player $player = null, bool $createParticles = false) : bool{ $target = $this->getBlock($vector); if($item === null){ $item = Item::get(Item::AIR, 0, 0); } if($player !== null){ $ev = new BlockBreakEvent($player, $target, $item, ($player->isCreative() or $this->server->allowInstabreak)); if($player->isAdventure() or $player->isSpectator() or ($player->isSurvival() and $item instanceof Item and !$target->isBreakable($item))){ $ev->setCancelled(); }elseif(!$player->isOp() and ($distance = $this->server->getSpawnRadius()) > -1){ $t = new Vector2($target->x, $target->z); $s = new Vector2($this->getSpawnLocation()->x, $this->getSpawnLocation()->z); if(count($this->server->getOps()->getAll()) > 0 and $t->distance($s) <= $distance){ //set it to cancelled so plugins can bypass this $ev->setCancelled(); } } $this->server->getPluginManager()->callEvent($ev); if($ev->isCancelled()){ return false; } $breakTime = $target->getBreakTime($item); if($player->isCreative() and $breakTime > 0.15){ $breakTime = 0.15; } if($player->hasEffect(Effect::SWIFTNESS)){ $breakTime *= 1 - (0.2 * ($player->getEffect(Effect::SWIFTNESS)->getAmplifier() + 1)); } if($player->hasEffect(Effect::MINING_FATIGUE)){ $breakTime *= 1 + (0.3 * ($player->getEffect(Effect::MINING_FATIGUE)->getAmplifier() + 1)); } $breakTime -= 0.05; //1 tick compensation if(!$ev->getInstaBreak() and ($player->lastBreak + $breakTime) > microtime(true)){ return false; } $player->lastBreak = microtime(true); $drops = $ev->getDrops(); if($player->isSurvival() and $this->getServer()->expEnabled){ $exp = 0; if($item->getEnchantmentLevel(Enchantment::TYPE_MINING_SILK_TOUCH) === 0){ switch($target->getId()){ case Block::COAL_ORE: $exp = mt_rand(0, 2); break; case Block::DIAMOND_ORE: case Block::EMERALD_ORE: $exp = mt_rand(3, 7); break; case Block::NETHER_QUARTZ_ORE: case Block::LAPIS_ORE: $exp = mt_rand(2, 5); break; case Block::REDSTONE_ORE: case Block::GLOWING_REDSTONE_ORE: $exp = mt_rand(1, 5); break; } } switch($target->getId()){ case Block::MONSTER_SPAWNER: $exp = mt_rand(15, 43); break; } if($exp > 0){$this->spawnXPOrb($vector->add(0, 1, 0), $exp);} } }elseif($item !== null and !$target->isBreakable($item)){ return false; }else{ $drops = $target->getDrops($item); //Fixes tile entities being deleted before getting drops foreach($drops as $k => $i){ if((isset ($i[0])) && (isset ($i[1])) && (isset ($i[2]))) $drops[$k] = Item::get($i[0], $i[1], $i[2]); } } $above = $this->getBlock(new Vector3($target->x, $target->y + 1, $target->z)); if($above !== null){ if($above->getId() === Item::FIRE){ $this->setBlock($above, new Air(), true); } } $tag = $item->getNamedTagEntry("CanDestroy"); if($tag instanceof ListTag){ $canBreak = false; foreach($tag as $v){ if($v instanceof StringTag){ $entry = Item::fromString($v->getValue()); if($entry->getId() > 0 and $entry->getBlock() !== null and $entry->getBlock()->getId() === $target->getId()){ $canBreak = true; break; } } } if(!$canBreak){ return false; } } if($createParticles){ $this->addParticle(new DestroyBlockParticle($target, $target)); } $target->onBreak($item); $tile = $this->getTile($target); if($tile !== null){ if($tile instanceof InventoryHolder){ if($tile instanceof Chest){ $tile->unpair(); } foreach($tile->getInventory()->getContents() as $chestItem){ $this->dropItem($target, $chestItem); } } $tile->close(); } if($item !== null){ $item->useOn($target); if($item->isTool() and $item->getDamage() >= $item->getMaxDurability()){ $item = Item::get(Item::AIR, 0, 0); } } if($player === null or $player->isSurvival()){ foreach($drops as $drop){ if($drop->getCount() > 0){ $this->dropItem($vector->add(0.5, 0.5, 0.5), $drop); } } } return true; } /** * Uses a item on a position and face, placing it or activating the block * * @param Vector3 $vector * @param Item $item * @param int $face * @param float $fx default 0.0 * @param float $fy default 0.0 * @param float $fz default 0.0 * @param Player $player default null * * @return bool */ public function useItemOn(Vector3 $vector, Item &$item, int $face, float $fx = 0.0, float $fy = 0.0, float $fz = 0.0, Player $player = null) : bool{ $target = $this->getBlock($vector); $block = $target->getSide($face); if($block->y >= $this->provider->getWorldHeight() or $block->y < 0){ //TODO: build height limit messages for custom world heights and mcregion cap return false; } if($target->getId() === Item::AIR){ return false; } if($player !== null){ $ev = new PlayerInteractEvent($player, $item, $target, $face, $target->getId() === 0 ? PlayerInteractEvent::RIGHT_CLICK_AIR : PlayerInteractEvent::RIGHT_CLICK_BLOCK); if(!$player->isOp() and ($distance = $this->server->getSpawnRadius()) > -1){ $t = new Vector2($target->x, $target->z); $s = new Vector2($this->getSpawnLocation()->x, $this->getSpawnLocation()->z); if(count($this->server->getOps()->getAll()) > 0 and $t->distance($s) <= $distance){ //set it to cancelled so plugins can bypass this $ev->setCancelled(); } } if($player->isSpectator()){ $ev->setCancelled(); } $this->server->getPluginManager()->callEvent($ev); if(!$ev->isCancelled()){ $target->onUpdate(self::BLOCK_UPDATE_TOUCH); if(!$player->isSneaking()){ if($target->canBeActivated() === true and $target->onActivate($item, $player) === true){ if($item->getCount() <= 0){ $item = Item::get(Item::AIR, 0, 0); }elseif($item->isTool() and $item->getDamage() >= $item->getMaxDurability()){ $item = Item::get(Item::AIR, 0, 0); } return true; } if($item->canBeActivated() and $item->onActivate($this, $player, $block, $target, $face, $fx, $fy, $fz)){ if($item->getCount() <= 0){ $item = Item::get(Item::AIR, 0, 0); return true; }elseif($item->isTool() and $item->getDamage() >= $item->getMaxDurability()){ $item = Item::get(Item::AIR, 0, 0); return true; } } } /*if(!$player->isSneaking() and $target->canBeActivated() === true and $target->onActivate($item, $player) === true){ return true; } if(!$player->isSneaking() and $item->canBeActivated() and $item->onActivate($this, $player, $block, $target, $face, $fx, $fy, $fz)){ if($item->getCount() <= 0){ $item = Item::get(Item::AIR, 0, 0); return true; } }*/ }else{ return false; } }elseif($target->canBeActivated() === true and $target->onActivate($item, $player) === true){ return true; } if($item->canBePlaced()){ $hand = $item->getBlock(); $hand->position($block); }else{ return false; } if(!($block->canBeReplaced() === true or ($hand->getId() === Item::SLAB and $block->getId() === Item::SLAB))){ return false; } if($target->canBeReplaced() === true){ $block = $target; $hand->position($block); //$face = -1; } if($hand->isSolid() === true and $hand->getBoundingBox() !== null){ $entities = $this->getCollidingEntities($hand->getBoundingBox()); $realCount = 0; foreach($entities as $e){ if($e instanceof Arrow or $e instanceof DroppedItem or ($e instanceof Player and $e->isSpectator())){ continue; } ++$realCount; } if($player !== null){ if(($diff = $player->getNextPosition()->subtract($player->getPosition())) and $diff->lengthSquared() > 0.00001){ $bb = $player->getBoundingBox()->getOffsetBoundingBox($diff->x, $diff->y, $diff->z); if($hand->getBoundingBox()->intersectsWith($bb)){ ++$realCount; } } } if($realCount > 0){ return false; //Entity in block } } $tag = $item->getNamedTagEntry("CanPlaceOn"); if($tag instanceof ListTag){ $canPlace = false; foreach($tag as $v){ if($v instanceof StringTag){ $entry = Item::fromString($v->getValue()); if($entry->getId() > 0 and $entry->getBlock() !== null and $entry->getBlock()->getId() === $target->getId()){ $canPlace = true; break; } } } if(!$canPlace){ return false; } } if($player !== null){ $ev = new BlockPlaceEvent($player, $hand, $block, $target, $item); if(!$player->isOp() and ($distance = $this->server->getSpawnRadius()) > -1){ $t = new Vector2($target->x, $target->z); $s = new Vector2($this->getSpawnLocation()->x, $this->getSpawnLocation()->z); if(count($this->server->getOps()->getAll()) > 0 and $t->distance($s) <= $distance){ //set it to cancelled so plugins can bypass this $ev->setCancelled(); } } $this->server->getPluginManager()->callEvent($ev); if($ev->isCancelled()){ return false; } } if($hand->place($item, $block, $target, $face, $fx, $fy, $fz, $player) === false){ return false; } $this->addSound(new BlockPlaceSound($this->getBlock($block))); //Get updated block, $block is still the original block and cannot be used directly if($hand->getId() === Item::SIGN_POST or $hand->getId() === Item::WALL_SIGN){ $nbt = new CompoundTag("", [ "id" => new StringTag("id", Tile::SIGN), "x" => new IntTag("x", $block->x), "y" => new IntTag("y", $block->y), "z" => new IntTag("z", $block->z), "Text1" => new StringTag("Text1", ""), "Text2" => new StringTag("Text2", ""), "Text3" => new StringTag("Text3", ""), "Text4" => new StringTag("Text4", "") ]); if($player !== null){ $nbt->Creator = new StringTag("Creator", $player->getRawUniqueId()); } if($item->hasCustomBlockData()){ foreach($item->getCustomBlockData() as $key => $v){ $nbt->{$key} = $v; } } Tile::createTile("Sign", $this->getChunk($block->x >> 4, $block->z >> 4), $nbt); } if ($player != null && $player->isCreative()) { $item->setCount($item->getCount()); } else { $item->setCount($item->getCount() - 1); } if($item->getCount() <= 0){ $item = Item::get(Item::AIR, 0, 0); } return true; } /** * @param int $entityId * * @return Entity */ public function getEntity(int $entityId){ return isset($this->entities[$entityId]) ? $this->entities[$entityId] : null; } /** * Gets the list of all the entities in this level * * @return Entity[] */ public function getEntities() : array{ return $this->entities; } /** * Returns the entities colliding the current one inside the AxisAlignedBB * * @param AxisAlignedBB $bb * @param Entity $entity * * @return Entity[] */ public function getCollidingEntities(AxisAlignedBB $bb, Entity $entity = null) : array{ $nearby = []; if($entity === null or $entity->canCollide){ $minX = Math::floorFloat(($bb->minX - 2) / 16); $maxX = Math::ceilFloat(($bb->maxX + 2) / 16); $minZ = Math::floorFloat(($bb->minZ - 2) / 16); $maxZ = Math::ceilFloat(($bb->maxZ + 2) / 16); for($x = $minX; $x <= $maxX; ++$x){ for($z = $minZ; $z <= $maxZ; ++$z){ foreach($this->getChunkEntities($x, $z) as $ent){ if($ent instanceof Player and $ent->isSpectator()){ continue; } if($entity == null){ if($ent->boundingBox->intersectsWith($bb)){ $nearby[] = $ent; } }elseif($entity instanceof Entity and $ent !== $entity and $entity->canCollideWith($ent)){ if($ent->boundingBox->intersectsWith($bb)){ $nearby[] = $ent; } } } } } } return $nearby; } /** * Returns the entities near the current one inside the AxisAlignedBB * * @param AxisAlignedBB $bb * @param Entity $entity * * @return Entity[] */ public function getNearbyEntities(AxisAlignedBB $bb, Entity $entity = null) : array{ $nearby = []; $minX = Math::floorFloat(($bb->minX - 2) / 16); $maxX = Math::ceilFloat(($bb->maxX + 2) / 16); $minZ = Math::floorFloat(($bb->minZ - 2) / 16); $maxZ = Math::ceilFloat(($bb->maxZ + 2) / 16); for($x = $minX; $x <= $maxX; ++$x){ for($z = $minZ; $z <= $maxZ; ++$z){ foreach($this->getChunkEntities($x, $z) as $ent){ if($ent instanceof Player and $ent->isSpectator()){ continue; } if($ent !== $entity and $ent->boundingBox->intersectsWith($bb)){ $nearby[] = $ent; } } } } return $nearby; } public function getNearbyExperienceOrb(AxisAlignedBB $bb) : array{ $nearby = []; foreach($this->getNearbyEntities($bb) as $entity){ if($entity instanceof XPOrb){ $nearby[] = $entity; } } return $nearby; } /** * Returns a list of the Tile entities in this level * * @return Tile[] */ public function getTiles() : array{ return $this->tiles; } /** * @param $tileId * * @return Tile */ public function getTileById(int $tileId){ return isset($this->tiles[$tileId]) ? $this->tiles[$tileId] : null; } /** * Returns a list of the players in this level * * @return Player[] */ public function getPlayers() : array{ return $this->players; } /** * @return ChunkLoader[] */ public function getLoaders() : array{ return $this->loaders; } /** * Returns the Tile in a position, or null if not found * * @param Vector3 $pos * * @return Tile */ public function getTile(Vector3 $pos){ $chunk = $this->getChunk($pos->x >> 4, $pos->z >> 4, false); if($chunk !== null){ return $chunk->getTile($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f); } return null; } /** * Returns a list of the entities on a given chunk * * @param int $X * @param int $Z * * @return Entity[] */ public function getChunkEntities($X, $Z) : array{ return ($chunk = $this->getChunk($X, $Z)) !== null ? $chunk->getEntities() : []; } /** * Gives a list of the Tile entities on a given chunk * * @param int $X * @param int $Z * * @return Tile[] */ public function getChunkTiles($X, $Z) : array{ return ($chunk = $this->getChunk($X, $Z)) !== null ? $chunk->getTiles() : []; } /** * Gets the raw block id. * * @param int $x * @param int $y * @param int $z * * @return int 0-255 */ public function getBlockIdAt(int $x, int $y, int $z) : int{ return $this->getChunk($x >> 4, $z >> 4, true)->getBlockId($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f); } /** * Sets the raw block id. * * @param int $x * @param int $y * @param int $z * @param int $id 0-255 */ public function setBlockIdAt(int $x, int $y, int $z, int $id){ unset($this->blockCache[Level::blockHash($x, $y, $z)]); $this->getChunk($x >> 4, $z >> 4, true)->setBlockId($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f, $id & 0xff); if(!isset($this->changedBlocks[$index = Level::chunkHash($x >> 4, $z >> 4)])){ $this->changedBlocks[$index] = []; } $this->changedBlocks[$index][Level::blockHash($x, $y, $z)] = $v = new Vector3($x, $y, $z); foreach($this->getChunkLoaders($x >> 4, $z >> 4) as $loader){ $loader->onBlockChanged($v); } } /** * Gets the raw block extra data * * @param int $x * @param int $y * @param int $z * * @return int 16-bit */ public function getBlockExtraDataAt(int $x, int $y, int $z) : int{ return $this->getChunk($x >> 4, $z >> 4, true)->getBlockExtraData($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f); } /** * Sets the raw block metadata. * * @param int $x * @param int $y * @param int $z * @param int $id * @param int $data */ public function setBlockExtraDataAt(int $x, int $y, int $z, int $id, int $data){ $this->getChunk($x >> 4, $z >> 4, true)->setBlockExtraData($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f, ($data << 8) | $id); $this->sendBlockExtraData($x, $y, $z, $id, $data); } /** * Gets the raw block metadata * * @param int $x * @param int $y * @param int $z * * @return int 0-15 */ public function getBlockDataAt(int $x, int $y, int $z) : int{ return $this->getChunk($x >> 4, $z >> 4, true)->getBlockData($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f); } /** * Sets the raw block metadata. * * @param int $x * @param int $y * @param int $z * @param int $data 0-15 */ public function setBlockDataAt(int $x, int $y, int $z, int $data){ unset($this->blockCache[Level::blockHash($x, $y, $z)]); $this->getChunk($x >> 4, $z >> 4, true)->setBlockData($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f, $data & 0x0f); if(!isset($this->changedBlocks[$index = Level::chunkHash($x >> 4, $z >> 4)])){ $this->changedBlocks[$index] = []; } $this->changedBlocks[$index][Level::blockHash($x, $y, $z)] = $v = new Vector3($x, $y, $z); foreach($this->getChunkLoaders($x >> 4, $z >> 4) as $loader){ $loader->onBlockChanged($v); } } /** * Gets the raw block skylight level * * @param int $x * @param int $y * @param int $z * * @return int 0-15 */ public function getBlockSkyLightAt(int $x, int $y, int $z) : int{ return $this->getChunk($x >> 4, $z >> 4, true)->getBlockSkyLight($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f); } /** * Sets the raw block skylight level. * * @param int $x * @param int $y * @param int $z * @param int $level 0-15 */ public function setBlockSkyLightAt(int $x, int $y, int $z, int $level){ $this->getChunk($x >> 4, $z >> 4, true)->setBlockSkyLight($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f, $level & 0x0f); } /** * Gets the raw block light level * * @param int $x * @param int $y * @param int $z * * @return int 0-15 */ public function getBlockLightAt(int $x, int $y, int $z) : int{ return $this->getChunk($x >> 4, $z >> 4, true)->getBlockLight($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f); } /** * Sets the raw block light level. * * @param int $x * @param int $y * @param int $z * @param int $level 0-15 */ public function setBlockLightAt(int $x, int $y, int $z, int $level){ $this->getChunk($x >> 4, $z >> 4, true)->setBlockLight($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f, $level & 0x0f); } /** * @param int $x * @param int $z * * @return int */ public function getBiomeId(int $x, int $z) : int{ return $this->getChunk($x >> 4, $z >> 4, true)->getBiomeId($x & 0x0f, $z & 0x0f); } /** * @param int $x * @param int $z * * @return int */ public function getHeightMap(int $x, int $z) : int{ return $this->getChunk($x >> 4, $z >> 4, true)->getHeightMap($x & 0x0f, $z & 0x0f); } /** * @param int $x * @param int $z * @param int $biomeId */ public function setBiomeId(int $x, int $z, int $biomeId){ $this->getChunk($x >> 4, $z >> 4, true)->setBiomeId($x & 0x0f, $z & 0x0f, $biomeId); } /** * @param int $x * @param int $z * @param int $value */ public function setHeightMap(int $x, int $z, int $value){ $this->getChunk($x >> 4, $z >> 4, true)->setHeightMap($x & 0x0f, $z & 0x0f, $value); } /** * @return Chunk[] */ public function getChunks() : array{ return $this->chunks; } /** * Gets the Chunk object * * @param int $x * @param int $z * @param bool $create Whether to generate the chunk if it does not exist * * @return Chunk */ public function getChunk(int $x, int $z, bool $create = false){ if(isset($this->chunks[$index = Level::chunkHash($x, $z)])){ return $this->chunks[$index]; }elseif($this->loadChunk($x, $z, $create)){ return $this->chunks[$index]; } return null; } public function generateChunkCallback(int $x, int $z, Chunk $chunk){ Timings::$generationCallbackTimer->startTiming(); if(isset($this->chunkPopulationQueue[$index = Level::chunkHash($x, $z)])){ $oldChunk = $this->getChunk($x, $z, false); for($xx = -1; $xx <= 1; ++$xx){ for($zz = -1; $zz <= 1; ++$zz){ unset($this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)]); } } unset($this->chunkPopulationQueue[$index]); $chunk->setProvider($this->provider); $this->setChunk($x, $z, $chunk, false); $chunk = $this->getChunk($x, $z, false); if($chunk !== null and ($oldChunk === null or $oldChunk->isPopulated() === false) and $chunk->isPopulated() and $chunk->getProvider() !== null){ $this->server->getPluginManager()->callEvent(new ChunkPopulateEvent($chunk)); foreach($this->getChunkLoaders($x, $z) as $loader){ $loader->onChunkPopulated($chunk); } } }elseif(isset($this->chunkGenerationQueue[$index]) or isset($this->chunkPopulationLock[$index])){ unset($this->chunkGenerationQueue[$index]); unset($this->chunkPopulationLock[$index]); $chunk->setProvider($this->provider); $this->setChunk($x, $z, $chunk, false); }else{ $chunk->setProvider($this->provider); $this->setChunk($x, $z, $chunk, false); } Timings::$generationCallbackTimer->stopTiming(); } /** * @param int $chunkX * @param int $chunkZ * @param Chunk $chunk * @param bool $unload */ public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk = null, bool $unload = true){ if($chunk === null){ return; } $index = Level::chunkHash($chunkX, $chunkZ); $oldChunk = $this->getChunk($chunkX, $chunkZ, false); if($unload and $oldChunk !== null){ $this->unloadChunk($chunkX, $chunkZ, false, false); $this->provider->setChunk($chunkX, $chunkZ, $chunk); $this->chunks[$index] = $chunk; }else{ $oldEntities = $oldChunk !== null ? $oldChunk->getEntities() : []; $oldTiles = $oldChunk !== null ? $oldChunk->getTiles() : []; $this->provider->setChunk($chunkX, $chunkZ, $chunk); $this->chunks[$index] = $chunk; foreach($oldEntities as $entity){ $chunk->addEntity($entity); $entity->chunk = $chunk; } foreach($oldTiles as $tile){ $chunk->addTile($tile); $tile->chunk = $chunk; } } unset($this->chunkCache[$index]); $chunk->setChanged(); if(!$this->isChunkInUse($chunkX, $chunkZ)){ $this->unloadChunkRequest($chunkX, $chunkZ); }else{ foreach($this->getChunkLoaders($chunkX, $chunkZ) as $loader){ $loader->onChunkChanged($chunk); } } } /** * Directly send a lightning to a player * * @deprecated * * @param int $x * @param int $y * @param int $z * @param Player $p */ public function sendLighting(int $x, int $y, int $z, Player $p){ $pk = new AddEntityPacket(); $pk->type = Lightning::NETWORK_ID; $pk->eid = mt_rand(10000000, 100000000); $pk->x = $x; $pk->y = $y; $pk->z = $z; $pk->metadata = array(3, 3, 3, 3); $p->dataPacket($pk); } /** * Add a lightning * * @param Vector3 $pos * @return Lightning */ public function spawnLightning(Vector3 $pos) : Lightning{ $nbt = new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $pos->getX()), new DoubleTag("", $pos->getY()), new DoubleTag("", $pos->getZ()) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", 0), new DoubleTag("", 0), new DoubleTag("", 0) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", 0), new FloatTag("", 0) ]), ]); $chunk = $this->getChunk($pos->x >> 4, $pos->z >> 4, false); $lightning = new Lightning($chunk, $nbt); $lightning->spawnToAll(); return $lightning; } /** * Add an experience orb * * @param Vector3 $pos * @param int $exp * @return bool|XPOrb */ public function spawnXPOrb(Vector3 $pos, int $exp = 1){ if($exp > 0){ $nbt = new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $pos->getX()), new DoubleTag("", $pos->getY() + 0.5), new DoubleTag("", $pos->getZ()) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", 0), new DoubleTag("", 0), new DoubleTag("", 0) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", 0), new FloatTag("", 0) ]), "Experience" => new LongTag("Experience", $exp), ]); $chunk = $this->getChunk($pos->x >> 4, $pos->z >> 4, false); $expOrb = new XPOrb($chunk, $nbt); $expOrb->spawnToAll(); return $expOrb; } return false; } /** * Gets the highest block Y value at a specific $x and $z * * @param int $x * @param int $z * * @return int 0-255 */ public function getHighestBlockAt(int $x, int $z) : int{ return $this->getChunk($x >> 4, $z >> 4, true)->getHighestBlockAt($x & 0x0f, $z & 0x0f); } public function canBlockSeeSky(Vector3 $pos) : bool{ return $this->getHighestBlockAt($pos->getFloorX(), $pos->getFloorZ()) < $pos->getY(); } /** * @param int $x * @param int $z * * @return bool */ public function isChunkLoaded(int $x, int $z) : bool{ return isset($this->chunks[Level::chunkHash($x, $z)]) or $this->provider->isChunkLoaded($x, $z); } /** * @param int $x * @param int $z * * @return bool */ public function isChunkGenerated(int $x, int $z) : bool{ $chunk = $this->getChunk($x, $z); return $chunk !== null ? $chunk->isGenerated() : false; } /** * @param int $x * @param int $z * * @return bool */ public function isChunkPopulated(int $x, int $z) : bool{ $chunk = $this->getChunk($x, $z); return $chunk !== null ? $chunk->isPopulated() : false; } /** * Returns a Position pointing to the spawn * * @return Position */ public function getSpawnLocation(): Position{ return Position::fromObject($this->provider->getSpawn(), $this); } /** * Sets the level spawn location * * @param Vector3 $pos */ public function setSpawnLocation(Vector3 $pos){ $previousSpawn = $this->getSpawnLocation(); $this->provider->setSpawn($pos); $this->server->getPluginManager()->callEvent(new SpawnChangeEvent($this, $previousSpawn)); } public function requestChunk(int $x, int $z, Player $player){ $index = Level::chunkHash($x, $z); if(!isset($this->chunkSendQueue[$index])){ $this->chunkSendQueue[$index] = []; } $this->chunkSendQueue[$index][$player->getLoaderId()] = $player; } private function sendChunkFromCache($x, $z){ if(isset($this->chunkSendTasks[$index = Level::chunkHash($x, $z)])){ foreach($this->chunkSendQueue[$index] as $player){ /** @var Player $player */ if($player->isConnected() and isset($player->usedChunks[$index])){ $player->sendChunk($x, $z, $this->chunkCache[$index]); } } unset($this->chunkSendQueue[$index]); unset($this->chunkSendTasks[$index]); } } private function processChunkRequest(){ if(count($this->chunkSendQueue) > 0){ $this->timings->syncChunkSendTimer->startTiming(); $x = null; $z = null; foreach($this->chunkSendQueue as $index => $players){ if(isset($this->chunkSendTasks[$index])){ continue; } Level::getXZ($index, $x, $z); $this->chunkSendTasks[$index] = true; if(isset($this->chunkCache[$index])){ $this->sendChunkFromCache($x, $z); continue; } $this->timings->syncChunkSendPrepareTimer->startTiming(); $task = $this->provider->requestChunkTask($x, $z); if($task !== null){ $this->server->getScheduler()->scheduleAsyncTask($task); } $this->timings->syncChunkSendPrepareTimer->stopTiming(); } $this->timings->syncChunkSendTimer->stopTiming(); } } public function chunkRequestCallback(int $x, int $z, string $payload){ $this->timings->syncChunkSendTimer->startTiming(); $index = Level::chunkHash($x, $z); if(!isset($this->chunkCache[$index]) and $this->cacheChunks and $this->server->getMemoryManager()->canUseChunkCache()){ $this->chunkCache[$index] = Level::getChunkCacheFromData($x, $z, $payload); $this->sendChunkFromCache($x, $z); $this->timings->syncChunkSendTimer->stopTiming(); return; } if(isset($this->chunkSendTasks[$index])){ foreach($this->chunkSendQueue[$index] as $player){ /** @var Player $player */ if($player->isConnected() and isset($player->usedChunks[$index])){ $player->sendChunk($x, $z, $payload); } } unset($this->chunkSendQueue[$index]); unset($this->chunkSendTasks[$index]); } $this->timings->syncChunkSendTimer->stopTiming(); } /** * Removes the entity from the level index * * @param Entity $entity * * @throws LevelException */ public function removeEntity(Entity $entity){ if($entity->getLevel() !== $this){ throw new LevelException("Invalid Entity level"); } if($entity instanceof Player){ unset($this->players[$entity->getId()]); $this->checkSleep(); }else{ $entity->close(); } unset($this->entities[$entity->getId()]); unset($this->updateEntities[$entity->getId()]); } /** * @param Entity $entity * * @throws LevelException */ public function addEntity(Entity $entity){ if($entity->getLevel() !== $this){ throw new LevelException("Invalid Entity level"); } if($entity instanceof Player){ $this->players[$entity->getId()] = $entity; } $this->entities[$entity->getId()] = $entity; } /** * @param Tile $tile * * @throws LevelException */ public function addTile(Tile $tile){ if($tile->getLevel() !== $this){ throw new LevelException("Invalid Tile level"); } $this->tiles[$tile->getId()] = $tile; $this->clearChunkCache($tile->getX() >> 4, $tile->getZ() >> 4); } /** * @param Tile $tile * * @throws LevelException */ public function removeTile(Tile $tile){ if($tile->getLevel() !== $this){ throw new LevelException("Invalid Tile level"); } unset($this->tiles[$tile->getId()]); unset($this->updateTiles[$tile->getId()]); $this->clearChunkCache($tile->getX() >> 4, $tile->getZ() >> 4); } /** * @param int $x * @param int $z * * @return bool */ public function isChunkInUse(int $x, int $z) : bool{ return isset($this->chunkLoaders[$index = Level::chunkHash($x, $z)]) and count($this->chunkLoaders[$index]) > 0; } /** * @param int $x * @param int $z * @param bool $generate * * @return bool */ public function loadChunk(int $x, int $z, bool $generate = true) : bool{ if(isset($this->chunks[$index = Level::chunkHash($x, $z)])){ return true; } $this->timings->syncChunkLoadTimer->startTiming(); $this->cancelUnloadChunkRequest($x, $z); $chunk = $this->provider->getChunk($x, $z, $generate); if($chunk === null){ if($generate){ throw new \InvalidStateException("Could not create new Chunk"); } return false; } $this->chunks[$index] = $chunk; $chunk->initChunk(); if($chunk->getProvider() !== null){ $this->server->getPluginManager()->callEvent(new ChunkLoadEvent($chunk, !$chunk->isGenerated())); }else{ $this->unloadChunk($x, $z, false); $this->timings->syncChunkLoadTimer->stopTiming(); return false; } if(!$chunk->isLightPopulated() and $chunk->isPopulated() and $this->getServer()->getProperty("chunk-ticking.light-updates", false)){ $this->getServer()->getScheduler()->scheduleAsyncTask(new LightPopulationTask($this, $chunk)); } if($this->isChunkInUse($x, $z)){ foreach($this->getChunkLoaders($x, $z) as $loader){ $loader->onChunkLoaded($chunk); } }else{ $this->unloadChunkRequest($x, $z); } $this->timings->syncChunkLoadTimer->stopTiming(); return true; } private function queueUnloadChunk(int $x, int $z){ $this->unloadQueue[$index = Level::chunkHash($x, $z)] = microtime(true); unset($this->chunkTickList[$index]); } public function unloadChunkRequest(int $x, int $z, bool $safe = true) : bool{ if(($safe === true and $this->isChunkInUse($x, $z)) or $this->isSpawnChunk($x, $z)){ return false; } $this->queueUnloadChunk($x, $z); return true; } public function cancelUnloadChunkRequest(int $x, int $z){ unset($this->unloadQueue[Level::chunkHash($x, $z)]); } public function unloadChunk(int $x, int $z, bool $safe = true, bool $trySave = true) : bool{ if(($safe === true and $this->isChunkInUse($x, $z))){ return false; } if(!$this->isChunkLoaded($x, $z)){ return true; } $this->timings->doChunkUnload->startTiming(); $index = Level::chunkHash($x, $z); $chunk = $this->getChunk($x, $z); if($chunk !== null and $chunk->getProvider() !== null){ $this->server->getPluginManager()->callEvent($ev = new ChunkUnloadEvent($chunk)); if($ev->isCancelled()){ $this->timings->doChunkUnload->stopTiming(); return false; } } try{ if($chunk !== null){ if($trySave and $this->getAutoSave()){ $entities = 0; foreach($chunk->getEntities() as $e){ if($e instanceof Player){ continue; } ++$entities; } if($chunk->hasChanged() or count($chunk->getTiles()) > 0 or $entities > 0){ $this->provider->setChunk($x, $z, $chunk); $this->provider->saveChunk($x, $z); } } foreach($this->getChunkLoaders($x, $z) as $loader){ $loader->onChunkUnloaded($chunk); } } $this->provider->unloadChunk($x, $z, $safe); }catch(\Throwable $e){ $logger = $this->server->getLogger(); $logger->error($this->server->getLanguage()->translateString("pocketmine.level.chunkUnloadError", [$e->getMessage()])); $logger->logException($e); } unset($this->chunks[$index]); unset($this->chunkTickList[$index]); unset($this->chunkCache[$index]); $this->timings->doChunkUnload->stopTiming(); return true; } /** * Returns true if the spawn is part of the spawn * * @param int $X * @param int $Z * * @return bool */ public function isSpawnChunk(int $X, int $Z) : bool{ $spawnX = $this->provider->getSpawn()->getX() >> 4; $spawnZ = $this->provider->getSpawn()->getZ() >> 4; return abs($X - $spawnX) <= 1 and abs($Z - $spawnZ) <= 1; } /** * @param Vector3 $spawn default null * * @return bool|Position */ public function getSafeSpawn($spawn = null){ if(!($spawn instanceof Vector3) or $spawn->y < 1){ $spawn = $this->getSpawnLocation(); } if($spawn instanceof Vector3){ $max = $this->provider->getWorldHeight(); $v = $spawn->floor(); $chunk = $this->getChunk($v->x >> 4, $v->z >> 4, false); $x = $v->x & 0x0f; $z = $v->z & 0x0f; if($chunk !== null){ $y = (int) min($max - 2, $v->y); $wasAir = ($chunk->getBlockId($x, $y - 1, $z) === 0); for(; $y > 0; --$y){ $b = $chunk->getFullBlock($x, $y, $z); $block = Block::get($b >> 4, $b & 0x0f); if($this->isFullBlock($block)){ if($wasAir){ $y++; break; } }else{ $wasAir = true; } } for(; $y >= 0 and $y < $max; ++$y){ $b = $chunk->getFullBlock($x, $y + 1, $z); $block = Block::get($b >> 4, $b & 0x0f); if(!$this->isFullBlock($block)){ $b = $chunk->getFullBlock($x, $y, $z); $block = Block::get($b >> 4, $b & 0x0f); if(!$this->isFullBlock($block)){ return new Position($spawn->x, $y === (int) $spawn->y ? $spawn->y : $y, $spawn->z, $this); } }else{ ++$y; } } $v->y = $y; } return new Position($spawn->x, $v->y, $spawn->z, $this); } return false; } /** * Gets the current time * * @return int */ public function getTime() : int{ return $this->time; } /** * Returns the Level name * * @return string */ public function getName() : string{ return $this->provider->getName(); } /** * Returns the Level folder name * * @return string */ public function getFolderName() : string{ return $this->folderName; } /** * Sets the current time on the level * * @param int $time */ public function setTime(int $time){ $this->time = $time; $this->sendTime(); } /** * Stops the time for the level, will not save the lock state to disk */ public function stopTime(){ $this->stopTime = true; $this->sendTime(); } /** * Start the time again, if it was stopped */ public function startTime(){ $this->stopTime = false; $this->sendTime(); } /** * Gets the level seed * * @return int|string */ public function getSeed(){ return $this->provider->getSeed(); } /** * Sets the seed for the level * * @param int $seed */ public function setSeed(int $seed){ $this->provider->setSeed($seed); } public function populateChunk(int $x, int $z, bool $force = false) : bool{ if(isset($this->chunkPopulationQueue[$index = Level::chunkHash($x, $z)]) or (count($this->chunkPopulationQueue) >= $this->chunkPopulationQueueSize and !$force)){ return false; } $chunk = $this->getChunk($x, $z, true); if(!$chunk->isPopulated()){ Timings::$populationTimer->startTiming(); $populate = true; for($xx = -1; $xx <= 1; ++$xx){ for($zz = -1; $zz <= 1; ++$zz){ if(isset($this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)])){ $populate = false; break; } } } if($populate){ if(!isset($this->chunkPopulationQueue[$index])){ $this->chunkPopulationQueue[$index] = true; for($xx = -1; $xx <= 1; ++$xx){ for($zz = -1; $zz <= 1; ++$zz){ $this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)] = true; } } $task = new PopulationTask($this, $chunk); $this->server->getScheduler()->scheduleAsyncTask($task); } } Timings::$populationTimer->stopTiming(); return false; } return true; } public function generateChunk(int $x, int $z, bool $force = false){ if(count($this->chunkGenerationQueue) >= $this->chunkGenerationQueueSize and !$force){ return; } if(!isset($this->chunkGenerationQueue[$index = Level::chunkHash($x, $z)])){ Timings::$generationTimer->startTiming(); $this->chunkGenerationQueue[$index] = true; $task = new GenerationTask($this, $this->getChunk($x, $z, true)); $this->server->getScheduler()->scheduleAsyncTask($task); Timings::$generationTimer->stopTiming(); } } public function regenerateChunk(int $x, int $z){ $this->unloadChunk($x, $z, false); $this->cancelUnloadChunkRequest($x, $z); $this->generateChunk($x, $z); //TODO: generate & refresh chunk from the generator object } public function doChunkGarbageCollection(){ $this->timings->doChunkGC->startTiming(); $X = null; $Z = null; foreach($this->chunks as $index => $chunk){ if(!isset($this->unloadQueue[$index])){ Level::getXZ($index, $X, $Z); if(!$this->isSpawnChunk($X, $Z)){ $this->unloadChunkRequest($X, $Z, true); } } } foreach($this->provider->getLoadedChunks() as $chunk){ if(!isset($this->chunks[Level::chunkHash($chunk->getX(), $chunk->getZ())])){ $this->provider->unloadChunk($chunk->getX(), $chunk->getZ(), false); } } $this->provider->doGarbageCollection(); $this->timings->doChunkGC->stopTiming(); } public function unloadChunks(bool $force = false){ if(count($this->unloadQueue) > 0){ $maxUnload = 96; $now = microtime(true); foreach($this->unloadQueue as $index => $time){ Level::getXZ($index, $X, $Z); if(!$force){ if($maxUnload <= 0){ break; }elseif($time > ($now - 30)){ continue; } } //If the chunk can't be unloaded, it stays on the queue if($this->unloadChunk($X, $Z, true)){ unset($this->unloadQueue[$index]); --$maxUnload; } } } } /** * @param int $chunkX * @param int $chunkZ * @param string $payload * * @return DataPacket */ public static function getChunkCacheFromData($chunkX, $chunkZ, $payload){ $pk = new FullChunkDataPacket(); $pk->chunkX = $chunkX; $pk->chunkZ = $chunkZ; $pk->data = $payload; $pk->encode(); $batch = new BatchPacket(); $batch->payload = zlib_encode(Binary::writeUnsignedVarInt(strlen($pk->getBuffer())) . $pk->getBuffer(), ZLIB_ENCODING_DEFLATE, Server::getInstance()->networkCompressionLevel); $batch->encode(); $batch->isEncoded = true; return $batch; } public function setMetadata($metadataKey, MetadataValue $metadataValue){ $this->server->getLevelMetadata()->setMetadata($this, $metadataKey, $metadataValue); } public function getMetadata($metadataKey){ return $this->server->getLevelMetadata()->getMetadata($this, $metadataKey); } public function hasMetadata($metadataKey){ return $this->server->getLevelMetadata()->hasMetadata($this, $metadataKey); } public function removeMetadata($metadataKey, Plugin $plugin){ $this->server->getLevelMetadata()->removeMetadata($this, $metadataKey, $plugin); } public function addEntityMotion(int $chunkX, int $chunkZ, int $entityId, float $x, float $y, float $z){ if(!isset($this->motionToSend[$index = Level::chunkHash($chunkX, $chunkZ)])){ $this->motionToSend[$index] = []; } $this->motionToSend[$index][$entityId] = [$entityId, $x, $y, $z]; } public function addEntityMovement(int $chunkX, int $chunkZ, int $entityId, float $x, float $y, float $z, float $yaw, float $pitch, $headYaw = null){ if(!isset($this->moveToSend[$index = Level::chunkHash($chunkX, $chunkZ)])){ $this->moveToSend[$index] = []; } $pk = new MoveEntityPacket(); $pk->eid = $entityId; $pk->x = $x; $pk->y = $y; $pk->z = $z; $pk->yaw = $yaw; $pk->headYaw = $headYaw === null ? $yaw : $headYaw; $pk->pitch = $pitch; $this->moveToSend[$index][$entityId] = $pk; } public function addPlayerMovement($chunkX, $chunkZ, $entityId, $x, $y, $z, $yaw, $pitch, $onGround, $headYaw = null){ if(!isset($this->moveToSend[$index = Level::chunkHash($chunkX, $chunkZ)])){ $this->moveToSend[$index] = []; } $pk = new MovePlayerPacket(); $pk->eid = $entityId; $pk->x = $x; $pk->y = $y; $pk->z = $z; $pk->yaw = $headYaw === null ? $yaw : $headYaw; $pk->bodyYaw = $yaw; $pk->pitch = $pitch; $pk->onGround = $onGround; $pk->mode = MovePlayerPacket::MODE_NORMAL; $this->moveToSend[$index][$entityId] = $pk; } } ================================================ FILE: src/pocketmine/level/LevelException.php ================================================ x = $x; $this->y = $y; $this->z = $z; $this->yaw = $yaw; $this->pitch = $pitch; $this->level = $level; } /** * @param Vector3 $pos * @param Level|null $level default null * @param float $yaw default 0.0 * @param float $pitch default 0.0 * * @return Location */ public static function fromObject(Vector3 $pos, Level $level = null, $yaw = 0.0, $pitch = 0.0){ return new Location($pos->x, $pos->y, $pos->z, $yaw, $pitch, ($level === null) ? (($pos instanceof Position) ? $pos->level : null) : $level); } public function add($x, $y = 0, $z = 0, $yaw = 0, $pitch = 0){ if($x instanceof Location){ return new Location($this->x + $x->x, $this->y + $x->y, $this->z + $x->z, $this->yaw + $x->yaw, $this->pitch + $x->pitch, $this->level); }else{ return new Location($this->x + $x, $this->y + $y, $this->z + $z, $this->yaw + $yaw, $this->pitch + $pitch, $this->level); } } public function getYaw(){ return $this->yaw; } public function getPitch(){ return $this->pitch; } public function fromObjectAdd(Vector3 $pos, $x, $y, $z){ if($pos instanceof Location){ $this->yaw = $pos->yaw; $this->pitch = $pos->pitch; } parent::fromObjectAdd($pos, $x, $y, $z); return $this; } public function __toString(){ return "Location (level=" . ($this->isValid() ? $this->getLevel()->getName() : "null") . ", x=$this->x, y=$this->y, z=$this->z, yaw=$this->yaw, pitch=$this->pitch)"; } } ================================================ FILE: src/pocketmine/level/MovingObjectPosition.php ================================================ typeOfHit = 0; $ob->blockX = $x; $ob->blockY = $y; $ob->blockZ = $z; $ob->hitVector = new Vector3($hitVector->x, $hitVector->y, $hitVector->z); return $ob; } /** * @param Entity $entity * * @return MovingObjectPosition */ public static function fromEntity(Entity $entity){ $ob = new MovingObjectPosition; $ob->typeOfHit = 1; $ob->entityHit = $entity; $ob->hitVector = new Vector3($entity->x, $entity->y, $entity->z); return $ob; } } ================================================ FILE: src/pocketmine/level/Position.php ================================================ x = $x; $this->y = $y; $this->z = $z; $this->level = $level; } public static function fromObject(Vector3 $pos, Level $level = null){ return new Position($pos->x, $pos->y, $pos->z, $level); } public function add($x, $y = 0, $z = 0){ if($x instanceof Vector3){ return new Position($this->x + $x->x, $this->y + $x->y, $this->z + $x->z, $this->level); }else{ return new Position($this->x + $x, $this->y + $y, $this->z + $z, $this->level); } } /** * @return Level */ public function getLevel(){ if($this->level !== null and $this->level->isClosed()){ MainLogger::getLogger()->debug("Position was holding a reference to an unloaded Level"); $this->level = null; } return $this->level; } /** * Sets the target Level of the position. * * @param Level|null $level * * @return $this * * @throws \InvalidArgumentException if the specified Level has been closed */ public function setLevel(Level $level = null){ if($level !== null and $level->isClosed()){ throw new \InvalidArgumentException("Specified level has been unloaded and cannot be used"); } $this->level = $level; return $this; } /** * Checks if this object has a valid reference to a loaded Level * * @return bool */ public function isValid(){ return $this->getLevel() instanceof Level; } /** * Returns a side Vector * * @param int $side * @param int $step * * @return Position * * @throws LevelException */ public function getSide($side, $step = 1){ if(!$this->isValid()){ throw new LevelException("Undefined Level reference"); } return Position::fromObject(parent::getSide($side, $step), $this->level); } public function __toString(){ return "Position(level=" . ($this->isValid() ? $this->getLevel()->getName() : "null") . ",x=" . $this->x . ",y=" . $this->y . ",z=" . $this->z . ")"; } /** * @param $x * @param $y * @param $z * * @return Position */ public function setComponents($x, $y, $z){ $this->x = $x; $this->y = $y; $this->z = $z; return $this; } public function fromObjectAdd(Vector3 $pos, $x, $y, $z){ if($pos instanceof Position){ $this->level = $pos->level; } parent::fromObjectAdd($pos, $x, $y, $z); return $this; } } ================================================ FILE: src/pocketmine/level/SimpleChunkManager.php ================================================ seed = $seed; $this->waterHeight = $waterHeight; } public function getWaterHeight() : int{ return $this->waterHeight; } /** * Gets the raw block id. * * @param int $x * @param int $y * @param int $z * * @return int 0-255 */ public function getBlockIdAt(int $x, int $y, int $z) : int{ if($chunk = $this->getChunk($x >> 4, $z >> 4)){ return $chunk->getBlockId($x & 0xf, $y & Level::Y_MASK, $z & 0xf); } return 0; } /** * Sets the raw block id. * * @param int $x * @param int $y * @param int $z * @param int $id 0-255 */ public function setBlockIdAt(int $x, int $y, int $z, int $id){ if($chunk = $this->getChunk($x >> 4, $z >> 4)){ $chunk->setBlockId($x & 0xf, $y & Level::Y_MASK, $z & 0xf, $id); } } /** * Gets the raw block metadata * * @param int $x * @param int $y * @param int $z * * @return int 0-15 */ public function getBlockDataAt(int $x, int $y, int $z) : int{ if($chunk = $this->getChunk($x >> 4, $z >> 4)){ return $chunk->getBlockData($x & 0xf, $y & Level::Y_MASK, $z & 0xf); } return 0; } /** * Sets the raw block metadata. * * @param int $x * @param int $y * @param int $z * @param int $data 0-15 */ public function setBlockDataAt(int $x, int $y, int $z, int $data){ if($chunk = $this->getChunk($x >> 4, $z >> 4)){ $chunk->setBlockData($x & 0xf, $y & Level::Y_MASK, $z & 0xf, $data); } } /** * Gets the raw block light level * * @param int $x * @param int $y * @param int $z * * @return int 0-15 */ public function getBlockLightAt(int $x, int $y, int $z) : int{ if($chunk = $this->getChunk($x >> 4, $z >> 4)){ return $chunk->getBlockLight($x & 0x0f, $y & 0x7f, $z & 0x0f); } return 0; } /** * Sets the raw block light level. * * @param int $x * @param int $y * @param int $z * @param int $level 0-15 */ public function setBlockLightAt(int $x, int $y, int $z, int $level){ if($chunk = $this->getChunk($x >> 4, $z >> 4)){ $chunk->setBlockLight($x & 0x0f, $y & 0x7f, $z & 0x0f, $level & 0x0f); } } /** * Updates the light around the block * * @param $x * @param $y * @param $z */ public function updateBlockLight(int $x, int $y, int $z){ $lightPropagationQueue = new \SplQueue(); $lightRemovalQueue = new \SplQueue(); $visited = []; $removalVisited = []; $oldLevel = $this->getBlockLightAt($x, $y, $z); $newLevel = (int) Block::$light[$this->getBlockIdAt($x, $y, $z)]; if($oldLevel !== $newLevel){ $this->setBlockLightAt($x, $y, $z, $newLevel); if($newLevel < $oldLevel){ $removalVisited[Level::blockHash($x, $y, $z)] = true; $lightRemovalQueue->enqueue([new Vector3($x, $y, $z), $oldLevel]); }else{ $visited[Level::blockHash($x, $y, $z)] = true; $lightPropagationQueue->enqueue(new Vector3($x, $y, $z)); } } while(!$lightRemovalQueue->isEmpty()){ /** @var Vector3 $node */ $val = $lightRemovalQueue->dequeue(); $node = $val[0]; $lightLevel = $val[1]; $this->computeRemoveBlockLight($node->x - 1, $node->y, $node->z, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); $this->computeRemoveBlockLight($node->x + 1, $node->y, $node->z, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); $this->computeRemoveBlockLight($node->x, $node->y - 1, $node->z, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); $this->computeRemoveBlockLight($node->x, $node->y + 1, $node->z, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); $this->computeRemoveBlockLight($node->x, $node->y, $node->z - 1, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); $this->computeRemoveBlockLight($node->x, $node->y, $node->z + 1, $lightLevel, $lightRemovalQueue, $lightPropagationQueue, $removalVisited, $visited); } while(!$lightPropagationQueue->isEmpty()){ /** @var Vector3 $node */ $node = $lightPropagationQueue->dequeue(); $lightLevel = $this->getBlockLightAt($node->x, $node->y, $node->z) - (int) Block::$lightFilter[$this->getBlockIdAt($node->x, $node->y, $node->z)]; if($lightLevel >= 1){ $this->computeSpreadBlockLight($node->x - 1, $node->y, $node->z, $lightLevel, $lightPropagationQueue, $visited); $this->computeSpreadBlockLight($node->x + 1, $node->y, $node->z, $lightLevel, $lightPropagationQueue, $visited); $this->computeSpreadBlockLight($node->x, $node->y - 1, $node->z, $lightLevel, $lightPropagationQueue, $visited); $this->computeSpreadBlockLight($node->x, $node->y + 1, $node->z, $lightLevel, $lightPropagationQueue, $visited); $this->computeSpreadBlockLight($node->x, $node->y, $node->z - 1, $lightLevel, $lightPropagationQueue, $visited); $this->computeSpreadBlockLight($node->x, $node->y, $node->z + 1, $lightLevel, $lightPropagationQueue, $visited); } } } private function computeRemoveBlockLight($x, $y, $z, $currentLight, \SplQueue $queue, \SplQueue $spreadQueue, array &$visited, array &$spreadVisited){ $current = $this->getBlockLightAt($x, $y, $z); if($current !== 0 and $current < $currentLight){ $this->setBlockLightAt($x, $y, $z, 0); if(!isset($visited[$index = Level::blockHash($x, $y, $z)])){ $visited[$index] = true; if($current > 1){ $queue->enqueue([new Vector3($x, $y, $z), $current]); } } }elseif($current >= $currentLight){ if(!isset($spreadVisited[$index = Level::blockHash($x, $y, $z)])){ $spreadVisited[$index] = true; $spreadQueue->enqueue(new Vector3($x, $y, $z)); } } } private function computeSpreadBlockLight($x, $y, $z, $currentLight, \SplQueue $queue, array &$visited){ $current = $this->getBlockLightAt($x, $y, $z); if($current < $currentLight){ $this->setBlockLightAt($x, $y, $z, $currentLight); if(!isset($visited[$index = Level::blockHash($x, $y, $z)])){ $visited[$index] = true; if($currentLight > 1){ $queue->enqueue(new Vector3($x, $y, $z)); } } } } /** * @param int $chunkX * @param int $chunkZ * * @return Chunk|null */ public function getChunk(int $chunkX, int $chunkZ){ return isset($this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)]) ? $this->chunks[$index] : null; } /** * @param int $chunkX * @param int $chunkZ * @param Chunk $chunk */ public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk = null){ if($chunk === null){ unset($this->chunks[Level::chunkHash($chunkX, $chunkZ)]); return; } $this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk; } public function cleanChunks(){ $this->chunks = []; } /** * Gets the level seed * * @return int|string */ public function getSeed(){ return $this->seed; } } ================================================ FILE: src/pocketmine/level/WeakPosition.php ================================================ x = $x; $this->y = $y; $this->z = $z; $this->levelId = ($level !== null ? $level->getId() : -1); } public static function fromObject(Vector3 $pos, Level $level = null){ return new WeakPosition($pos->x, $pos->y, $pos->z, $level); } /** * @return Level|null */ public function getLevel(){ return Server::getInstance()->getLevel($this->levelId); } /** * @param Level|null $level * * @return $this * * @throws \InvalidArgumentException if the specified Level has been closed */ public function setLevel(Level $level = null){ if($level !== null and $level->isClosed()){ throw new \InvalidArgumentException("Specified level has been unloaded and cannot be used"); } $this->levelId = ($level !== null ? $level->getId() : -1); return $this; } /** * Returns a side Vector * * @param int $side * @param int $step * * @return WeakPosition * * @throws LevelException */ public function getSide($side, $step = 1){ assert($this->isValid()); return WeakPosition::fromObject(parent::getSide($side, $step), $this->level); } public function __toString(){ return "Weak" . parent::__toString(); } } ================================================ FILE: src/pocketmine/level/format/Chunk.php ================================================ provider = $provider; $this->x = $chunkX; $this->z = $chunkZ; $this->height = $provider !== null ? ($provider->getWorldHeight() >> 4) : 16; $this->emptySubChunk = new EmptySubChunk(); foreach($subChunks as $y => $subChunk){ if($y < 0 or $y >= $this->height){ throw new ChunkException("Invalid subchunk index $y!"); } if($subChunk->isEmpty()){ $this->subChunks[$y] = $this->emptySubChunk; }else{ $this->subChunks[$y] = $subChunk; } } for($i = 0; $i < $this->height; ++$i){ if(!isset($this->subChunks[$i])){ $this->subChunks[$i] = $this->emptySubChunk; } } if(count($heightMap) === 256){ $this->heightMap = $heightMap; }else{ assert(count($heightMap) === 0, "Wrong HeightMap value count, expected 256, got " . count($heightMap)); $val = ($this->height * 16) - 1; $this->heightMap = array_fill(0, 256, $val); } if(strlen($biomeIds) === 256){ $this->biomeIds = $biomeIds; }else{ assert(strlen($biomeIds) === 0, "Wrong BiomeIds value count, expected 256, got " . strlen($biomeIds)); $this->biomeIds = str_repeat("\x00", 256); } $this->NBTtiles = $tiles; $this->NBTentities = $entities; } /** * @return int */ public function getX() : int{ return $this->x; } /** * @return int */ public function getZ() : int{ return $this->z; } public function setX(int $x){ $this->x = $x; } /** * @param int $z */ public function setZ(int $z){ $this->z = $z; } /** * @return LevelProvider|null */ public function getProvider(){ return $this->provider; } /** * @param LevelProvider $provider */ public function setProvider(LevelProvider $provider){ $this->provider = $provider; } /** * Returns the chunk height in count of subchunks. * * @return int */ public function getHeight() : int{ return $this->height; } /** * Returns a bitmap of block ID and meta at the specified chunk block coordinates * * @param int $x 0-15 * @param int $y * @param int $z 0-15 * * @return int bitmap, (id << 4) | meta */ public function getFullBlock(int $x, int $y, int $z) : int{ return $this->getSubChunk($y >> 4)->getFullBlock($x, $y & 0x0f, $z); } /** * Sets block ID and meta in one call at the specified chunk block coordinates * * @param int $x 0-15 * @param int $y * @param int $z 0-15 * @param int|null $blockId 0-255 if null, does not change * @param int|null $meta 0-15 if null, does not change * * @return bool */ public function setBlock(int $x, int $y, int $z, $blockId = null, $meta = null) : bool{ if($this->getSubChunk($y >> 4, true)->setBlock($x, $y & 0x0f, $z, $blockId !== null ? ($blockId & 0xff) : null, $meta !== null ? ($meta & 0x0f) : null)){ $this->hasChanged = true; return true; } return false; } /** * Returns the block ID at the specified chunk block coordinates * * @param int $x 0-15 * @param int $y * @param int $z 0-15 * * @return int 0-255 */ public function getBlockId(int $x, int $y, int $z) : int{ return $this->getSubChunk($y >> 4)->getBlockId($x, $y & 0x0f, $z); } /** * Sets the block ID at the specified chunk block coordinates * * @param int $x 0-15 * @param int $y * @param int $z 0-15 * @param int $id 0-255 */ public function setBlockId(int $x, int $y, int $z, int $id){ if($this->getSubChunk($y >> 4, true)->setBlockId($x, $y & 0x0f, $z, $id)){ $this->hasChanged = true; } } /** * Returns the block meta value at the specified chunk block coordinates * * @param int $x 0-15 * @param int $y * @param int $z 0-15 * * @return int 0-15 */ public function getBlockData(int $x, int $y, int $z) : int{ return $this->getSubChunk($y >> 4)->getBlockData($x, $y & 0x0f, $z); } /** * Sets the block meta value at the specified chunk block coordinates * * @param int $x 0-15 * @param int $y * @param int $z 0-15 * @param int $data 0-15 */ public function setBlockData(int $x, int $y, int $z, int $data){ if($this->getSubChunk($y >> 4)->setBlockData($x, $y & 0x0f, $z, $data)){ $this->hasChanged = true; } } /** * Returns the raw block extra data value at the specified chunk block coordinates, or 0 if no data exists * * @param int $x 0-15 * @param int $y * @param int $z 0-15 * * @return int bitmap, (meta << 8) | id */ public function getBlockExtraData(int $x, int $y, int $z) : int{ return $this->extraData[Chunk::chunkBlockHash($x, $y, $z)] ?? 0; } /** * Sets the raw block extra data value at the specified chunk block coordinates * * @param int $x 0-15 * @param int $y * @param int $z 0-15 * @param int $data bitmap, (meta << 8) | id */ public function setBlockExtraData(int $x, int $y, int $z, int $data){ if($data === 0){ unset($this->extraData[Chunk::chunkBlockHash($x, $y, $z)]); }else{ $this->extraData[Chunk::chunkBlockHash($x, $y, $z)] = $data; } $this->hasChanged = true; } /** * Returns the sky light level at the specified chunk block coordinates * * @param int $x 0-15 * @param int $y * @param int $z 0-15 * * @return int 0-15 */ public function getBlockSkyLight(int $x, int $y, int $z) : int{ return $this->getSubChunk($y >> 4)->getBlockSkyLight($x, $y & 0x0f, $z); } /** * Sets the sky light level at the specified chunk block coordinates * * @param int $x 0-15 * @param int $y * @param int $z 0-15 * @param int $level 0-15 */ public function setBlockSkyLight(int $x, int $y, int $z, int $level){ if($this->getSubChunk($y >> 4)->setBlockSkyLight($x, $y & 0x0f, $z, $level)){ $this->hasChanged = true; } } /** * Returns the block light level at the specified chunk block coordinates * * @param int $x 0-15 * @param int $y 0-15 * @param int $z 0-15 * * @return int 0-15 */ public function getBlockLight(int $x, int $y, int $z) : int{ return $this->getSubChunk($y >> 4)->getBlockLight($x, $y & 0x0f, $z); } /** * Sets the block light level at the specified chunk block coordinates * * @param int $x 0-15 * @param int $y 0-15 * @param int $z 0-15 * @param int $level 0-15 */ public function setBlockLight(int $x, int $y, int $z, int $level){ if($this->getSubChunk($y >> 4)->setBlockLight($x, $y & 0x0f, $z, $level)){ $this->hasChanged = true; } } /** * Returns the Y coordinate of the highest non-air block at the specified X/Z chunk block coordinates * * @param int $x 0-15 * @param int $z 0-15 * @param bool $useHeightMap whether to use pre-calculated heightmap values or not * * @return int */ public function getHighestBlockAt(int $x, int $z, bool $useHeightMap = true) : int{ if($useHeightMap){ $height = $this->getHeightMap($x, $z); if($height !== 0 and $height !== 255){ return $height; } } $index = $this->getHighestSubChunkIndex(); if($index < 0){ return 0; } $height = $index << 4; for($y = $index; $y >= 0; --$y){ $height = $this->getSubChunk($y)->getHighestBlockAt($x, $z) | ($y << 4); if($height !== -1){ break; } } $this->setHeightMap($x, $z, $height); return $height; } /** * Returns the heightmap value at the specified X/Z chunk block coordinates * * @param int $x 0-15 * @param int $z 0-15 * * @return int */ public function getHeightMap(int $x, int $z) : int{ return $this->heightMap[($z << 4) | $x]; } /** * Returns the heightmap value at the specified X/Z chunk block coordinates * @param int $x 0-15 * @param int $z 0-15 * @param int $value */ public function setHeightMap(int $x, int $z, int $value){ $this->heightMap[($z << 4) | $x] = $value; } /** * Recalculates the heightmap for the whole chunk. */ public function recalculateHeightMap(){ for($z = 0; $z < 16; ++$z){ for($x = 0; $x < 16; ++$x){ $this->setHeightMap($x, $z, $this->getHighestBlockAt($x, $z, false)); } } } /** * Performs basic sky light population on the chunk. * * TODO: rewrite this, use block light filters and diffusion, actual proper sky light population */ public function populateSkyLight(){ for($x = 0; $x < 16; ++$x){ for($z = 0; $z < 16; ++$z){ $heightMap = $this->getHeightMap($x, $z); $y = ($this->getHighestSubChunkIndex() + 1) << 4; for(; $y > $heightMap; --$y){ $this->setBlockSkyLight($x, $y, $z, 15); } for(; $y > 0 and $this->getBlockId($x, $y, $z) === Block::AIR; --$y){ $this->setBlockSkyLight($x, $y, $z, 15); } $this->setHeightMap($x, $z, $y); for(; $y > 0; --$y){ $this->setBlockSkyLight($x, $y, $z, 0); } } } } /** * Returns the biome ID at the specified X/Z chunk block coordinates * * @param int $x 0-15 * @param int $z 0-15 * * @return int 0-255 */ public function getBiomeId(int $x, int $z) : int{ return ord($this->biomeIds{($z << 4) | $x}); } /** * Sets the biome ID at the specified X/Z chunk block coordinates * * @param int $x 0-15 * @param int $z 0-15 * @param int $biomeId 0-255 */ public function setBiomeId(int $x, int $z, int $biomeId){ $this->hasChanged = true; $this->biomeIds{($z << 4) | $x} = chr($biomeId & 0xff); } /** * Returns a column of block IDs from bottom to top at the specified X/Z chunk block coordinates. * @param int $x 0-15 * @param int $z 0-15 * * @return string */ public function getBlockIdColumn(int $x, int $z) : string{ $result = ""; foreach($this->subChunks as $subChunk){ $result .= $subChunk->getBlockIdColumn($x, $z); } return $result; } /** * Returns a column of block meta values from bottom to top at the specified X/Z chunk block coordinates. * @param int $x 0-15 * @param int $z 0-15 * * @return string */ public function getBlockDataColumn(int $x, int $z) : string{ $result = ""; foreach($this->subChunks as $subChunk){ $result .= $subChunk->getBlockDataColumn($x, $z); } return $result; } /** * Returns a column of sky light values from bottom to top at the specified X/Z chunk block coordinates. * @param int $x 0-15 * @param int $z 0-15 * * @return string */ public function getBlockSkyLightColumn(int $x, int $z) : string{ $result = ""; foreach($this->subChunks as $subChunk){ $result .= $subChunk->getSkyLightColumn($x, $z); } return $result; } /** * Returns a column of block light values from bottom to top at the specified X/Z chunk block coordinates. * @param int $x 0-15 * @param int $z 0-15 * * @return string */ public function getBlockLightColumn(int $x, int $z) : string{ $result = ""; foreach($this->subChunks as $subChunk){ $result .= $subChunk->getBlockLightColumn($x, $z); } return $result; } /** * @return bool */ public function isLightPopulated() : bool{ return $this->lightPopulated; } /** * @param bool $value */ public function setLightPopulated(bool $value = true){ $this->lightPopulated = $value; } /** * @return bool */ public function isPopulated() : bool{ return $this->terrainPopulated; } /** * @param bool $value */ public function setPopulated(bool $value = true){ $this->terrainPopulated = $value; } /** * @return bool */ public function isGenerated() : bool{ return $this->terrainGenerated; } /** * @param bool $value */ public function setGenerated(bool $value = true){ $this->terrainGenerated = $value; } /** * @param Entity $entity */ public function addEntity(Entity $entity){ $this->entities[$entity->getId()] = $entity; if(!($entity instanceof Player) and $this->isInit){ $this->hasChanged = true; } } /** * @param Entity $entity */ public function removeEntity(Entity $entity){ unset($this->entities[$entity->getId()]); if(!($entity instanceof Player) and $this->isInit){ $this->hasChanged = true; } } /** * @param Tile $tile */ public function addTile(Tile $tile){ $this->tiles[$tile->getId()] = $tile; if(isset($this->tileList[$index = (($tile->x & 0x0f) << 12) | (($tile->z & 0x0f) << 8) | ($tile->y & 0xff)]) and $this->tileList[$index] !== $tile){ $this->tileList[$index]->close(); } $this->tileList[$index] = $tile; if($this->isInit){ $this->hasChanged = true; } } /** * @param Tile $tile */ public function removeTile(Tile $tile){ unset($this->tiles[$tile->getId()]); unset($this->tileList[(($tile->x & 0x0f) << 12) | (($tile->z & 0x0f) << 8) | ($tile->y & 0xff)]); if($this->isInit){ $this->hasChanged = true; } } /** * Returns an array of entities currently using this chunk. * * @return Entity[] */ public function getEntities() : array{ return $this->entities; } /** * @return Tile[] */ public function getTiles() : array{ return $this->tiles; } /** * Returns the tile at the specified chunk block coordinates, or null if no tile exists. * * @param int $x 0-15 * @param int $y * @param int $z 0-15 * * @return Tile|null */ public function getTile(int $x, int $y, int $z){ $index = ($x << 12) | ($z << 8) | $y; return $this->tileList[$index] ?? null; } /** * Unloads the chunk, closing entities and tiles. * * @param bool $save * @param bool $safe Whether to check if there are still players using this chunk * * @return bool */ public function unload(bool $save = true, bool $safe = true) : bool{ $level = $this->getProvider(); if($level === null){ return true; } if($save === true and $this->hasChanged){ $level->saveChunk($this->getX(), $this->getZ()); } if($safe === true){ foreach($this->getEntities() as $entity){ if($entity instanceof Player){ return false; } } } foreach($this->getEntities() as $entity){ if($entity instanceof Player){ continue; } $entity->close(); } foreach($this->getTiles() as $tile){ $tile->close(); } $this->provider = null; return true; } /** * Deserializes tiles and entities from NBT * TODO: remove this */ public function initChunk(){ if($this->getProvider() instanceof LevelProvider and !$this->isInit){ $changed = false; if($this->NBTentities !== null){ $this->getProvider()->getLevel()->timings->syncChunkLoadEntitiesTimer->startTiming(); foreach($this->NBTentities as $nbt){ if($nbt instanceof CompoundTag){ if(!isset($nbt->id)){ $changed = true; continue; } if(($nbt["Pos"][0] >> 4) !== $this->x or ($nbt["Pos"][2] >> 4) !== $this->z){ $changed = true; continue; //Fixes entities allocated in wrong chunks. } if(($entity = Entity::createEntity($nbt["id"], $this, $nbt)) instanceof Entity){ $entity->spawnToAll(); }else{ $changed = true; continue; } } } $this->getProvider()->getLevel()->timings->syncChunkLoadEntitiesTimer->stopTiming(); $this->getProvider()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->startTiming(); foreach($this->NBTtiles as $nbt){ if($nbt instanceof CompoundTag){ if(!isset($nbt->id)){ $changed = true; continue; } if(($nbt["x"] >> 4) !== $this->x or ($nbt["z"] >> 4) !== $this->z){ $changed = true; continue; //Fixes tiles allocated in wrong chunks. } if(Tile::createTile($nbt["id"], $this, $nbt) === null){ $changed = true; continue; } } } $this->getProvider()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->stopTiming(); $this->NBTentities = null; $this->NBTtiles = null; } $this->hasChanged = $changed; $this->isInit = true; } } /** * @return string */ public function getBiomeIdArray() : string{ return $this->biomeIds; } /** * @return int[] */ public function getHeightMapArray() : array{ return $this->heightMap; } /** * @return int[] */ public function getBlockExtraDataArray() : array{ return $this->extraData; } /** * @return bool */ public function hasChanged() : bool{ return $this->hasChanged; } /** * @param bool $value */ public function setChanged(bool $value = true){ $this->hasChanged = $value; } /** * Returns the subchunk at the specified subchunk Y coordinate, or an empty, unmodifiable stub if it does not exist or the coordinate is out of range. * * @param int $y * @param bool $generateNew Whether to create a new, modifiable subchunk if there is not one in place * * @return SubChunk|EmptySubChunk */ public function getSubChunk(int $y, bool $generateNew = false) : SubChunk{ if($y < 0 or $y >= $this->height){ return $this->emptySubChunk; }elseif($generateNew and $this->subChunks[$y] instanceof EmptySubChunk){ $this->subChunks[$y] = new SubChunk(); } assert($this->subChunks[$y] !== null, "Somehow something broke, no such subchunk at index $y"); return $this->subChunks[$y]; } /** * Sets a subchunk in the chunk index * @param int $y * @param SubChunk|null $subChunk * @param bool $allowEmpty Whether to check if the chunk is empty, and if so replace it with an empty stub * * @return bool */ public function setSubChunk(int $y, SubChunk $subChunk = null, bool $allowEmpty = false) : bool{ if($y < 0 or $y >= $this->height){ return false; } if($subChunk === null or ($subChunk->isEmpty() and !$allowEmpty)){ $this->subChunks[$y] = $this->emptySubChunk; }else{ $this->subChunks[$y] = $subChunk; } $this->hasChanged = true; return true; } /** * @return SubChunk[] */ public function getSubChunks() : array{ return $this->subChunks; } /** * Returns the Y coordinate of the highest non-empty subchunk in this chunk. * * @return int */ public function getHighestSubChunkIndex() : int{ for($y = count($this->subChunks) - 1; $y >= 0; --$y){ if($this->subChunks[$y] === null or $this->subChunks[$y] instanceof EmptySubChunk){ //No need to thoroughly prune empties at runtime, this will just reduce performance. continue; } break; } return $y; } /** * Returns the count of subchunks that need sending to players * * @return int */ public function getSubChunkSendCount() : int{ return $this->getHighestSubChunkIndex() + 1; } /** * Disposes of empty subchunks */ public function pruneEmptySubChunks(){ foreach($this->subChunks as $y => $subChunk){ if($y < 0 or $y >= $this->height){ assert(false, "Invalid subchunk index"); unset($this->subChunks[$y]); }elseif($subChunk instanceof EmptySubChunk){ continue; }elseif($subChunk->isEmpty()){ //normal subchunk full of air, remove it and replace it with an empty stub $this->subChunks[$y] = $this->emptySubChunk; }else{ continue; //do not set changed } $this->hasChanged = true; } } /** * Serializes the chunk for sending to players * * @return string */ public function networkSerialize() : string{ $result = ""; $subChunkCount = $this->getSubChunkSendCount(); $result .= chr($subChunkCount); for($y = 0; $y < $subChunkCount; ++$y){ $result .= $this->subChunks[$y]->networkSerialize(); } $result .= pack("v*", ...$this->heightMap) . $this->biomeIds . chr(0); //border block array count //Border block entry format: 1 byte (4 bits X, 4 bits Z). These are however useless since they crash the regular client. $extraData = new BinaryStream(); $extraData->putVarInt(count($this->extraData)); //WHY, Mojang, WHY foreach($this->extraData as $key => $value){ $extraData->putVarInt($key); $extraData->putLShort($value); } $result .= $extraData->getBuffer(); if(count($this->tiles) > 0){ $nbt = new NBT(NBT::LITTLE_ENDIAN); $list = []; foreach($this->tiles as $tile){ if($tile instanceof Spawnable){ $list[] = $tile->getSpawnCompound(); } } $nbt->setData($list); $result .= $nbt->write(true); } return $result; } /** * Fast-serializes the chunk for passing between threads * TODO: tiles and entities * * @return string */ public function fastSerialize() : string{ $stream = new BinaryStream(); $stream->putInt($this->x); $stream->putInt($this->z); $count = 0; $subChunks = ""; foreach($this->subChunks as $y => $subChunk){ if($subChunk instanceof EmptySubChunk){ continue; } ++$count; $subChunks .= chr($y) . $subChunk->fastSerialize(); } $stream->putByte($count); $stream->put($subChunks); $stream->put(pack("C*", ...$this->heightMap) . $this->biomeIds . chr(($this->lightPopulated ? 1 << 2 : 0) | ($this->terrainPopulated ? 1 << 1 : 0) | ($this->terrainGenerated ? 1 : 0))); return $stream->getBuffer(); } /** * Deserializes a fast-serialized chunk * * @param string $data * @param LevelProvider|null $provider * * @return Chunk */ public static function fastDeserialize(string $data, LevelProvider $provider = null){ $stream = new BinaryStream(); $stream->setBuffer($data); $data = null; $x = $stream->getInt(); $z = $stream->getInt(); $subChunks = []; $count = $stream->getByte(); for($y = 0; $y < $count; ++$y){ $subChunks[$stream->getByte()] = SubChunk::fastDeserialize($stream->get(10240)); } $heightMap = array_values(unpack("C*", $stream->get(256))); $biomeIds = $stream->get(256); $chunk = new Chunk($provider, $x, $z, $subChunks, [], [], $biomeIds, $heightMap); $flags = $stream->getByte(); $chunk->lightPopulated = (bool) ($flags & 4); $chunk->terrainPopulated = (bool) ($flags & 2); $chunk->terrainGenerated = (bool) ($flags & 1); return $chunk; } //TODO: get rid of this public static function getEmptyChunk(int $x, int $z, LevelProvider $provider = null) : Chunk{ return new Chunk($provider, $x, $z); } /** * Creates a block hash from chunk block coordinates. Used for extra data keys in chunk packets. * @internal * * @param int $x 0-15 * @param int $y 0-255 * @param int $z 0-15 * * @return int */ public static function chunkBlockHash(int $x, int $y, int $z) : int{ return ($x << 12) | ($z << 8) | $y; } } ================================================ FILE: src/pocketmine/level/format/EmptySubChunk.php ================================================ ids, $ids, 4096); self::assignData($this->data, $data, 2048); self::assignData($this->skyLight, $skyLight, 2048, "\xff"); self::assignData($this->blockLight, $blockLight, 2048); } public function isEmpty() : bool{ assert(strlen($this->ids) === 4096, "Wrong length of ID array, expecting 4096 bytes, got " . strlen($this->ids)); return substr_count($this->ids, "\x00") === 4096; } public function getBlockId(int $x, int $y, int $z) : int{ return ord($this->ids{($x << 8) | ($z << 4) | $y}); } public function setBlockId(int $x, int $y, int $z, int $id) : bool{ $this->ids{($x << 8) | ($z << 4) | $y} = chr($id); return true; } public function getBlockData(int $x, int $y, int $z) : int{ $m = ord($this->data{($x << 7) + ($z << 3) + ($y >> 1)}); if(($y & 1) === 0){ return $m & 0x0f; }else{ return $m >> 4; } } public function setBlockData(int $x, int $y, int $z, int $data) : bool{ $i = ($x << 7) | ($z << 3) | ($y >> 1); if(($y & 1) === 0){ $this->data{$i} = chr((ord($this->data{$i}) & 0xf0) | ($data & 0x0f)); }else{ $this->data{$i} = chr((($data & 0x0f) << 4) | (ord($this->data{$i}) & 0x0f)); } return true; } public function getFullBlock(int $x, int $y, int $z) : int{ $i = ($x << 8) | ($z << 4) | $y; if(($y & 1) === 0){ return (ord($this->ids{$i}) << 4) | (ord($this->data{$i >> 1}) & 0x0f); }else{ return (ord($this->ids{$i}) << 4) | (ord($this->data{$i >> 1}) >> 4); } } public function setBlock(int $x, int $y, int $z, $id = null, $data = null) : bool{ $i = ($x << 8) | ($z << 4) | $y; $changed = false; if($id !== null){ $block = chr($id); if($this->ids{$i} !== $block){ $this->ids{$i} = $block; $changed = true; } } if($data !== null){ $i >>= 1; $byte = ord($this->data{$i}); if(($y & 1) === 0){ $this->data{$i} = chr(($byte & 0xf0) | ($data & 0x0f)); }else{ $this->data{$i} = chr((($data & 0x0f) << 4) | ($byte & 0x0f)); } if($this->data{$i} !== $byte){ $changed = true; } } return $changed; } public function getBlockLight(int $x, int $y, int $z) : int{ $byte = ord($this->blockLight{($x << 7) + ($z << 3) + ($y >> 1)}); if(($y & 1) === 0){ return $byte & 0x0f; }else{ return $byte >> 4; } } public function setBlockLight(int $x, int $y, int $z, int $level) : bool{ $i = ($x << 7) + ($z << 3) + ($y >> 1); $byte = ord($this->blockLight{$i}); if(($y & 1) === 0){ $this->blockLight{$i} = chr(($byte & 0xf0) | ($level & 0x0f)); }else{ $this->blockLight{$i} = chr((($level & 0x0f) << 4) | ($byte & 0x0f)); } return true; } public function getBlockSkyLight(int $x, int $y, int $z) : int{ $byte = ord($this->skyLight{($x << 7) + ($z << 3) + ($y >> 1)}); if(($y & 1) === 0){ return $byte & 0x0f; }else{ return $byte >> 4; } } public function setBlockSkyLight(int $x, int $y, int $z, int $level) : bool{ $i = ($x << 7) + ($z << 3) + ($y >> 1); $byte = ord($this->skyLight{$i}); if(($y & 1) === 0){ $this->skyLight{$i} = chr(($byte & 0xf0) | ($level & 0x0f)); }else{ $this->skyLight{$i} = chr((($level & 0x0f) << 4) | ($byte & 0x0f)); } return true; } public function getHighestBlockAt(int $x, int $z) : int{ for($y = 15; $y >= 0; --$y){ if($this->ids{($x << 8) | ($z << 4) | $y} !== "\x00"){ return $y; } } return -1; //highest block not in this subchunk } public function getBlockIdColumn(int $x, int $z) : string{ return substr($this->ids, (($x << 8) | ($z << 4)), 16); } public function getBlockDataColumn(int $x, int $z) : string{ return substr($this->data, (($x << 7) | ($z << 3)), 8); } public function getBlockLightColumn(int $x, int $z) : string{ return substr($this->blockLight, (($x << 7) | ($z << 3)), 8); } public function getSkyLightColumn(int $x, int $z) : string{ return substr($this->skyLight, (($x << 7) | ($z << 3)), 8); } public function getBlockIdArray() : string{ assert(strlen($this->ids) === 4096, "Wrong length of ID array, expecting 4096 bytes, got " . strlen($this->ids)); return $this->ids; } public function getBlockDataArray() : string{ assert(strlen($this->data) === 2048, "Wrong length of data array, expecting 2048 bytes, got " . strlen($this->data)); return $this->data; } public function getSkyLightArray() : string{ assert(strlen($this->skyLight) === 2048, "Wrong length of skylight array, expecting 2048 bytes, got " . strlen($this->skyLight)); return $this->skyLight; } public function getBlockLightArray() : string{ assert(strlen($this->blockLight) === 2048, "Wrong length of light array, expecting 2048 bytes, got " . strlen($this->blockLight)); return $this->blockLight; } public function networkSerialize() : string{ // storage version, ids, data, skylight, blocklight return "\x00" . $this->ids . $this->data . $this->skyLight . $this->blockLight; } public function fastSerialize() : string{ return $this->ids . $this->data . $this->skyLight . $this->blockLight; } public static function fastDeserialize(string $data) : SubChunk{ return new SubChunk( substr($data, 0, 4096), //ids substr($data, 4096, 2048), //data substr($data, 6144, 2048), //sky light substr($data, 8192, 2048) //block light ); } } ================================================ FILE: src/pocketmine/level/format/io/BaseLevelProvider.php ================================================ level = $level; $this->path = $path; if(!file_exists($this->path)){ mkdir($this->path, 0777, true); } $nbt = new NBT(NBT::BIG_ENDIAN); $nbt->readCompressed(file_get_contents($this->getPath() . "level.dat")); $levelData = $nbt->getData(); if($levelData->Data instanceof CompoundTag){ $this->levelData = $levelData->Data; }else{ throw new LevelException("Invalid level.dat"); } if(!isset($this->levelData->generatorName)){ $this->levelData->generatorName = new StringTag("generatorName", Generator::getGenerator("DEFAULT")); } if(!isset($this->levelData->generatorOptions)){ $this->levelData->generatorOptions = new StringTag("generatorOptions", ""); } $this->asyncChunkRequest = (bool) $this->level->getServer()->getProperty("chunk-sending.async-chunk-request", false); } public function getPath() : string{ return $this->path; } public function getServer(){ return $this->level->getServer(); } public function getLevel(){ return $this->level; } public function getName() : string{ return (string) $this->levelData["LevelName"]; } public function getTime(){ return $this->levelData["Time"]; } public function setTime($value){ $this->levelData->Time = new LongTag("Time", $value); } public function getSeed(){ return $this->levelData["RandomSeed"]; } public function setSeed($value){ $this->levelData->RandomSeed = new LongTag("RandomSeed", (int) $value); } public function getSpawn() : Vector3{ return new Vector3((float) $this->levelData["SpawnX"], (float) $this->levelData["SpawnY"], (float) $this->levelData["SpawnZ"]); } public function setSpawn(Vector3 $pos){ $this->levelData->SpawnX = new IntTag("SpawnX", (int) $pos->x); $this->levelData->SpawnY = new IntTag("SpawnY", (int) $pos->y); $this->levelData->SpawnZ = new IntTag("SpawnZ", (int) $pos->z); } public function doGarbageCollection(){ } /** * @return CompoundTag */ public function getLevelData() : CompoundTag{ return $this->levelData; } public function saveLevelData(){ $nbt = new NBT(NBT::BIG_ENDIAN); $nbt->setData(new CompoundTag("", [ "Data" => $this->levelData ])); $buffer = $nbt->writeCompressed(); file_put_contents($this->getPath() . "level.dat", $buffer); } public function requestChunkTask(int $x, int $z){ $chunk = $this->getChunk($x, $z, false); if(!($chunk instanceof Chunk)){ throw new ChunkException("Invalid Chunk sent"); } if($this->asyncChunkRequest){ return new ChunkRequestTask($this->level, $chunk); } //non-async, call the callback directly with serialized data $this->getLevel()->chunkRequestCallback($x, $z, $chunk->networkSerialize()); return null; } } ================================================ FILE: src/pocketmine/level/format/io/ChunkException.php ================================================ levelId = $level->getId(); $this->chunk = $chunk->fastSerialize(); $this->chunkX = $chunk->getX(); $this->chunkZ = $chunk->getZ(); //TODO: serialize tiles with chunks $tiles = ""; $nbt = new NBT(NBT::LITTLE_ENDIAN); foreach($chunk->getTiles() as $tile){ if($tile instanceof Spawnable){ $nbt->setData($tile->getSpawnCompound()); $tiles .= $nbt->write(true); } } $this->tiles = $tiles; } public function onRun(){ $chunk = Chunk::fastDeserialize($this->chunk); $ordered = $chunk->networkSerialize() . $this->tiles; $this->setResult($ordered, false); } public function onCompletion(Server $server){ $level = $server->getLevel($this->levelId); if($level instanceof Level and $this->hasResult()){ $level->chunkRequestCallback($this->chunkX, $this->chunkZ, $this->getResult()); } } } ================================================ FILE: src/pocketmine/level/format/io/ChunkUtils.php ================================================ XZY and vice versa) * * @param string $array length 4096 * * @return string length 4096 */ public static final function reorderByteArray(string $array) : string{ $result = str_repeat("\x00", 4096); if($array !== $result){ $i = 0; $zM = 0; $yM = 0; for($x = 0; $x < 16; ++$x){ $zM = $x + 256; for($z = $x; $z < $zM; $z += 16){ $yM = $z + 4096; for($y = $z; $y < $yM; $y += 256){ $result{$i} = $array{$y}; ++$i; } } } } return $result; } /** * Re-orders a nibble array (YZX -> XZY and vice versa) * * @param string $array length 2048 * * @param string $commonValue * @return string length 2048 */ public static final function reorderNibbleArray(string $array, string $commonValue = "\x00") : string{ $result = str_repeat($commonValue, 2048); if($array !== $result){ $i = 0; for($x = 0; $x < 8; ++$x){ for($z = 0; $z < 16; ++$z){ $zx = (($z << 3) | $x); for($y = 0; $y < 8; ++$y){ $j = (($y << 8) | $zx); $j80 = ($j | 0x80); if($array{$j} === $commonValue and $array{$j80} === $commonValue){ //values are already filled }else{ $i1 = ord($array{$j}); $i2 = ord($array{$j80}); $result{$i} = chr(($i2 << 4) | ($i1 & 0x0f)); $result{$i | 0x80} = chr(($i1 >> 4) | ($i2 & 0xf0)); } $i++; } } $i += 128; } } return $result; } /** * Converts pre-MCPE-1.0 biome color array to biome ID array. * * @param int[] $array of biome color values * * @return string */ public static function convertBiomeColors(array $array) : string{ $result = str_repeat("\x00", 256); foreach($array as $i => $color){ $result{$i} = chr(($color >> 24) & 0xff); } return $result; } } ================================================ FILE: src/pocketmine/level/format/io/LevelProvider.php ================================================ xPos = new IntTag("xPos", $chunk->getX()); $nbt->zPos = new IntTag("zPos", $chunk->getZ()); $nbt->V = new ByteTag("V", 1); $nbt->LastUpdate = new LongTag("LastUpdate", 0); //TODO $nbt->InhabitedTime = new LongTag("InhabitedTime", 0); //TODO $nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $chunk->isPopulated()); $nbt->LightPopulated = new ByteTag("LightPopulated", $chunk->isLightPopulated()); $nbt->Sections = new ListTag("Sections", []); $nbt->Sections->setTagType(NBT::TAG_Compound); $subChunks = -1; foreach($chunk->getSubChunks() as $y => $subChunk){ if($subChunk->isEmpty()){ continue; } $nbt->Sections[++$subChunks] = new CompoundTag(null, [ "Y" => new ByteTag("Y", $y), "Blocks" => new ByteArrayTag("Blocks", ChunkUtils::reorderByteArray($subChunk->getBlockIdArray())), //Generic in-memory chunks are currently always XZY "Data" => new ByteArrayTag("Data", ChunkUtils::reorderNibbleArray($subChunk->getBlockDataArray())), "SkyLight" => new ByteArrayTag("SkyLight", ChunkUtils::reorderNibbleArray($subChunk->getSkyLightArray(), "\xff")), "BlockLight" => new ByteArrayTag("BlockLight", ChunkUtils::reorderNibbleArray($subChunk->getBlockLightArray())) ]); } $nbt->Biomes = new ByteArrayTag("Biomes", $chunk->getBiomeIdArray()); $nbt->HeightMap = new IntArrayTag("HeightMap", $chunk->getHeightMapArray()); $entities = []; foreach($chunk->getEntities() as $entity){ if(!($entity instanceof Player) and !$entity->closed){ $entity->saveNBT(); $entities[] = $entity->namedtag; } } $nbt->Entities = new ListTag("Entities", $entities); $nbt->Entities->setTagType(NBT::TAG_Compound); $tiles = []; foreach($chunk->getTiles() as $tile){ $tile->saveNBT(); $tiles[] = $tile->namedtag; } $nbt->TileEntities = new ListTag("TileEntities", $tiles); $nbt->TileEntities->setTagType(NBT::TAG_Compound); //TODO: TileTicks $writer = new NBT(NBT::BIG_ENDIAN); $nbt->setName("Level"); $writer->setData(new CompoundTag("", ["Level" => $nbt])); return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); } public function nbtDeserialize(string $data){ $nbt = new NBT(NBT::BIG_ENDIAN); try{ $nbt->readCompressed($data, ZLIB_ENCODING_DEFLATE); $chunk = $nbt->getData(); if(!isset($chunk->Level) or !($chunk->Level instanceof CompoundTag)){ throw new ChunkException("Invalid NBT format"); } $chunk = $chunk->Level; $subChunks = []; if($chunk->Sections instanceof ListTag){ foreach($chunk->Sections as $subChunk){ if($subChunk instanceof CompoundTag){ $subChunks[$subChunk->Y->getValue()] = new SubChunk( ChunkUtils::reorderByteArray($subChunk->Blocks->getValue()), ChunkUtils::reorderNibbleArray($subChunk->Data->getValue()), ChunkUtils::reorderNibbleArray($subChunk->SkyLight->getValue(), "\xff"), ChunkUtils::reorderNibbleArray($subChunk->BlockLight->getValue()) ); } } } if(isset($chunk->BiomeColors)){ $biomeIds = ChunkUtils::convertBiomeColors($chunk->BiomeColors->getValue()); //Convert back to PC format (RIP colours D:) }elseif(isset($chunk->Biomes)){ $biomeIds = $chunk->Biomes->getValue(); }else{ $biomeIds = ""; } $result = new Chunk( $this, $chunk["xPos"], $chunk["zPos"], $subChunks, isset($chunk->Entities) ? $chunk->Entities->getValue() : [], isset($chunk->TileEntities) ? $chunk->TileEntities->getValue() : [], $biomeIds, isset($chunk->HeightMap) ? $chunk->HeightMap->getValue() : [] ); $result->setLightPopulated(isset($chunk->LightPopulated) ? ((bool) $chunk->LightPopulated->getValue()) : false); $result->setPopulated(isset($chunk->TerrainPopulated) ? ((bool) $chunk->TerrainPopulated->getValue()) : false); $result->setGenerated(true); return $result; }catch(\Throwable $e){ MainLogger::getLogger()->logException($e); return null; } } public static function getProviderName() : string{ return "anvil"; } public function getWorldHeight() : int{ //TODO: add world height options return 256; } } ================================================ FILE: src/pocketmine/level/format/io/region/McRegion.php ================================================ xPos = new IntTag("xPos", $chunk->getX()); $nbt->zPos = new IntTag("zPos", $chunk->getZ()); $nbt->V = new ByteTag("V", 0); //guess $nbt->LastUpdate = new LongTag("LastUpdate", 0); //TODO $nbt->InhabitedTime = new LongTag("InhabitedTime", 0); //TODO $nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $chunk->isPopulated()); $nbt->LightPopulated = new ByteTag("LightPopulated", $chunk->isLightPopulated()); $ids = ""; $data = ""; $skyLight = ""; $blockLight = ""; $subChunks = $chunk->getSubChunks(); for($x = 0; $x < 16; ++$x){ for($z = 0; $z < 16; ++$z){ for($y = 0; $y < 8; ++$y){ $subChunk = $subChunks[$y]; $ids .= $subChunk->getBlockIdColumn($x, $z); $data .= $subChunk->getBlockDataColumn($x, $z); $skyLight .= $subChunk->getSkyLightColumn($x, $z); $blockLight .= $subChunk->getBlockLightColumn($x, $z); } } } $nbt->Blocks = new ByteArrayTag("Blocks", $ids); $nbt->Data = new ByteArrayTag("Data", $data); $nbt->SkyLight = new ByteArrayTag("SkyLight", $skyLight); $nbt->BlockLight = new ByteArrayTag("BlockLight", $blockLight); $nbt->Biomes = new ByteArrayTag("Biomes", $chunk->getBiomeIdArray()); $nbt->HeightMap = new ByteArrayTag("HeightMap", pack("C*", ...$chunk->getHeightMapArray())); $entities = []; foreach($chunk->getEntities() as $entity){ if(!($entity instanceof Player) and !$entity->closed){ $entity->saveNBT(); $entities[] = $entity->namedtag; } } $nbt->Entities = new ListTag("Entities", $entities); $nbt->Entities->setTagType(NBT::TAG_Compound); $tiles = []; foreach($chunk->getTiles() as $tile){ $tile->saveNBT(); $tiles[] = $tile->namedtag; } $nbt->TileEntities = new ListTag("TileEntities", $tiles); $nbt->TileEntities->setTagType(NBT::TAG_Compound); //TODO: TileTicks $writer = new NBT(NBT::BIG_ENDIAN); $nbt->setName("Level"); $writer->setData(new CompoundTag("", ["Level" => $nbt])); return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); } /** * @param string $data * * @return Chunk|null */ public function nbtDeserialize(string $data){ $nbt = new NBT(NBT::BIG_ENDIAN); try{ $nbt->readCompressed($data, ZLIB_ENCODING_DEFLATE); $chunk = $nbt->getData(); if(!isset($chunk->Level) or !($chunk->Level instanceof CompoundTag)){ throw new ChunkException("Invalid NBT format"); } $chunk = $chunk->Level; $subChunks = []; $fullIds = isset($chunk->Blocks) ? $chunk->Blocks->getValue() : str_repeat("\x00", 32768); $fullData = isset($chunk->Data) ? $chunk->Data->getValue() : (str_repeat("\x00", 16384)); $fullSkyLight = isset($chunk->SkyLight) ? $chunk->SkyLight->getValue() : str_repeat("\xff", 16384); $fullBlockLight = isset($chunk->BlockLight) ? $chunk->BlockLight->getValue() : (str_repeat("\x00", 16384)); for($y = 0; $y < 8; ++$y){ $offset = ($y << 4); $ids = ""; for($i = 0; $i < 256; ++$i){ $ids .= substr($fullIds, $offset, 16); $offset += 128; } $data = ""; $offset = ($y << 3); for($i = 0; $i < 256; ++$i){ $data .= substr($fullData, $offset, 8); $offset += 64; } $skyLight = ""; $offset = ($y << 3); for($i = 0; $i < 256; ++$i){ $skyLight .= substr($fullSkyLight, $offset, 8); $offset += 64; } $blockLight = ""; $offset = ($y << 3); for($i = 0; $i < 256; ++$i){ $blockLight .= substr($fullBlockLight, $offset, 8); $offset += 64; } $subChunks[$y] = new SubChunk($ids, $data, $skyLight, $blockLight); } if(isset($chunk->BiomeColors)){ $biomeIds = ChunkUtils::convertBiomeColors($chunk->BiomeColors->getValue()); //Convert back to original format }elseif(isset($chunk->Biomes)){ $biomeIds = $chunk->Biomes->getValue(); }else{ $biomeIds = ""; } $heightMap = []; if(isset($chunk->HeightMap)){ if($chunk->HeightMap instanceof ByteArrayTag){ $heightMap = array_values(unpack("C*", $chunk->HeightMap->getValue())); }elseif($chunk->HeightMap instanceof IntArrayTag){ $heightMap = $chunk->HeightMap->getValue(); #blameshoghicp } } $result = new Chunk( $this, $chunk["xPos"], $chunk["zPos"], $subChunks, isset($chunk->Entities) ? $chunk->Entities->getValue() : [], isset($chunk->TileEntities) ? $chunk->TileEntities->getValue() : [], $biomeIds, $heightMap ); $result->setLightPopulated(isset($chunk->LightPopulated) ? ((bool) $chunk->LightPopulated->getValue()) : false); $result->setPopulated(isset($chunk->TerrainPopulated) ? ((bool) $chunk->TerrainPopulated->getValue()) : false); $result->setGenerated(true); return $result; }catch(\Throwable $e){ MainLogger::getLogger()->logException($e); return null; } } public static function getProviderName() : string{ return "mcregion"; } public function getWorldHeight() : int{ //TODO: add world height options return 128; } public static function isValid(string $path) : bool{ $isValid = (file_exists($path . "/level.dat") and is_dir($path . "/region/")); if($isValid){ $files = glob($path . "/region/*.mc*"); if(empty($files)){ //possible glob() issue on some systems $files = array_filter(scandir($path . "/region/"), function($file){ return substr($file, strrpos($file, ".") + 1, 2) === "mc"; //region file }); } foreach($files as $f){ if(substr($f, strrpos($f, ".") + 1) !== static::REGION_FILE_EXTENSION){ $isValid = false; break; } } } return $isValid; } public static function generate(string $path, string $name, $seed, string $generator, array $options = []){ if(!file_exists($path)){ mkdir($path, 0777, true); } if(!file_exists($path . "/region")){ mkdir($path . "/region", 0777); } //TODO, add extra details $levelData = new CompoundTag("Data", [ "hardcore" => new ByteTag("hardcore", 0), "initialized" => new ByteTag("initialized", 1), "GameType" => new IntTag("GameType", 0), "generatorVersion" => new IntTag("generatorVersion", 1), //2 in MCPE "SpawnX" => new IntTag("SpawnX", 256), "SpawnY" => new IntTag("SpawnY", 70), "SpawnZ" => new IntTag("SpawnZ", 256), "version" => new IntTag("version", 19133), "DayTime" => new IntTag("DayTime", 0), "LastPlayed" => new LongTag("LastPlayed", microtime(true) * 1000), "RandomSeed" => new LongTag("RandomSeed", $seed), "SizeOnDisk" => new LongTag("SizeOnDisk", 0), "Time" => new LongTag("Time", 0), "generatorName" => new StringTag("generatorName", Generator::getGeneratorName($generator)), "generatorOptions" => new StringTag("generatorOptions", isset($options["preset"]) ? $options["preset"] : ""), "LevelName" => new StringTag("LevelName", $name), "GameRules" => new CompoundTag("GameRules", []) ]); $nbt = new NBT(NBT::BIG_ENDIAN); $nbt->setData(new CompoundTag("", [ "Data" => $levelData ])); $buffer = $nbt->writeCompressed(); file_put_contents($path . "level.dat", $buffer); } public function getGenerator() : string{ return (string) $this->levelData["generatorName"]; } public function getGeneratorOptions() : array{ return ["preset" => $this->levelData["generatorOptions"]]; } public function getChunk(int $chunkX, int $chunkZ, bool $create = false){ $index = Level::chunkHash($chunkX, $chunkZ); if(isset($this->chunks[$index])){ return $this->chunks[$index]; }else{ $this->loadChunk($chunkX, $chunkZ, $create); return $this->chunks[$index] ?? null; } } public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk){ $chunk->setProvider($this); self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ); $this->loadRegion($regionX, $regionZ); $chunk->setX($chunkX); $chunk->setZ($chunkZ); if(isset($this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)]) and $this->chunks[$index] !== $chunk){ $this->unloadChunk($chunkX, $chunkZ, false); } $this->chunks[$index] = $chunk; } public function saveChunk(int $chunkX, int $chunkZ) : bool{ if($this->isChunkLoaded($chunkX, $chunkZ)){ $this->getRegion($chunkX >> 5, $chunkZ >> 5)->writeChunk($this->getChunk($chunkX, $chunkZ)); return true; } return false; } public function saveChunks(){ foreach($this->chunks as $chunk){ $this->saveChunk($chunk->getX(), $chunk->getZ()); } } public function loadChunk(int $chunkX, int $chunkZ, bool $create = false) : bool{ $index = Level::chunkHash($chunkX, $chunkZ); if(isset($this->chunks[$index])){ return true; } $regionX = $regionZ = null; self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ); /** @noinspection PhpStrictTypeCheckingInspection */ $this->loadRegion($regionX, $regionZ); $this->level->timings->syncChunkLoadDataTimer->startTiming(); /** @noinspection PhpStrictTypeCheckingInspection */ $chunk = $this->getRegion($regionX, $regionZ)->readChunk($chunkX - $regionX * 32, $chunkZ - $regionZ * 32); if($chunk === null and $create){ $chunk = $this->getEmptyChunk($chunkX, $chunkZ); } $this->level->timings->syncChunkLoadDataTimer->stopTiming(); if($chunk !== null){ $this->chunks[$index] = $chunk; return true; }else{ return false; } } public function unloadChunk(int $chunkX, int $chunkZ, bool $safe = true) : bool{ $chunk = $this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)] ?? null; if($chunk instanceof Chunk and $chunk->unload(false, $safe)){ unset($this->chunks[$index]); return true; } return false; } public function unloadChunks(){ foreach($this->chunks as $chunk){ $this->unloadChunk($chunk->getX(), $chunk->getZ(), false); } $this->chunks = []; } public function isChunkLoaded(int $chunkX, int $chunkZ) : bool{ return isset($this->chunks[Level::chunkHash($chunkX, $chunkZ)]); } public function isChunkGenerated(int $chunkX, int $chunkZ) : bool{ if(($region = $this->getRegion($chunkX >> 5, $chunkZ >> 5)) !== null){ return $region->chunkExists($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32) and $this->getChunk($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32, true)->isGenerated(); } return false; } public function isChunkPopulated(int $chunkX, int $chunkZ) : bool{ $chunk = $this->getChunk($chunkX, $chunkZ); if($chunk !== null){ return $chunk->isPopulated(); }else{ return false; } } public function getLoadedChunks() : array{ return $this->chunks; } public function doGarbageCollection(){ $limit = time() - 300; foreach($this->regions as $index => $region){ if($region->lastUsed <= $limit){ $region->close(); unset($this->regions[$index]); } } } /** * @param int $chunkX * @param int $chunkZ * @param int &$x * @param int &$z */ public static function getRegionIndex(int $chunkX, int $chunkZ, &$x, &$z){ $x = $chunkX >> 5; $z = $chunkZ >> 5; } /** * @param int $chunkX * @param int $chunkZ * * @return Chunk */ public function getEmptyChunk(int $chunkX, int $chunkZ){ return Chunk::getEmptyChunk($chunkX, $chunkZ, $this); } /** * @param int $x * @param int $z * * @return RegionLoader */ protected function getRegion(int $x, int $z){ return $this->regions[Level::chunkHash($x, $z)] ?? null; } /** * @param int $x * @param int $z */ protected function loadRegion(int $x, int $z){ if(!isset($this->regions[$index = Level::chunkHash($x, $z)])){ $this->regions[$index] = new RegionLoader($this, $x, $z, static::REGION_FILE_EXTENSION); } } public function close(){ $this->unloadChunks(); foreach($this->regions as $index => $region){ $region->close(); unset($this->regions[$index]); } $this->level = null; } } ================================================ FILE: src/pocketmine/level/format/io/region/PMAnvil.php ================================================ xPos = new IntTag("xPos", $chunk->getX()); $nbt->zPos = new IntTag("zPos", $chunk->getZ()); $nbt->V = new ByteTag("V", 1); $nbt->LastUpdate = new LongTag("LastUpdate", 0); //TODO $nbt->InhabitedTime = new LongTag("InhabitedTime", 0); //TODO $nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $chunk->isPopulated()); $nbt->LightPopulated = new ByteTag("LightPopulated", $chunk->isLightPopulated()); $nbt->Sections = new ListTag("Sections", []); $nbt->Sections->setTagType(NBT::TAG_Compound); $subChunks = -1; foreach($chunk->getSubChunks() as $y => $subChunk){ if($subChunk->isEmpty()){ continue; } $nbt->Sections[++$subChunks] = new CompoundTag(null, [ "Y" => new ByteTag("Y", $y), "Blocks" => new ByteArrayTag("Blocks", $subChunk->getBlockIdArray()), "Data" => new ByteArrayTag("Data", $subChunk->getBlockDataArray()), "SkyLight" => new ByteArrayTag("SkyLight", $subChunk->getSkyLightArray()), "BlockLight" => new ByteArrayTag("BlockLight", $subChunk->getBlockLightArray()) ]); } $nbt->Biomes = new ByteArrayTag("Biomes", $chunk->getBiomeIdArray()); $nbt->HeightMap = new IntArrayTag("HeightMap", $chunk->getHeightMapArray()); $entities = []; foreach($chunk->getEntities() as $entity){ if(!($entity instanceof Player) and !$entity->closed){ $entity->saveNBT(); $entities[] = $entity->namedtag; } } $nbt->Entities = new ListTag("Entities", $entities); $nbt->Entities->setTagType(NBT::TAG_Compound); $tiles = []; foreach($chunk->getTiles() as $tile){ $tile->saveNBT(); $tiles[] = $tile->namedtag; } $nbt->TileEntities = new ListTag("TileEntities", $tiles); $nbt->TileEntities->setTagType(NBT::TAG_Compound); //TODO: TileTicks $writer = new NBT(NBT::BIG_ENDIAN); $nbt->setName("Level"); $writer->setData(new CompoundTag("", ["Level" => $nbt])); return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); } public function nbtDeserialize(string $data){ $nbt = new NBT(NBT::BIG_ENDIAN); try{ $nbt->readCompressed($data, ZLIB_ENCODING_DEFLATE); $chunk = $nbt->getData(); if(!isset($chunk->Level) or !($chunk->Level instanceof CompoundTag)){ throw new ChunkException("Invalid NBT format"); } $chunk = $chunk->Level; $subChunks = []; if($chunk->Sections instanceof ListTag){ foreach($chunk->Sections as $subChunk){ if($subChunk instanceof CompoundTag){ $subChunks[$subChunk->Y->getValue()] = new SubChunk( $subChunk->Blocks->getValue(), $subChunk->Data->getValue(), $subChunk->SkyLight->getValue(), $subChunk->BlockLight->getValue() ); } } } $result = new Chunk( $this, $chunk["xPos"], $chunk["zPos"], $subChunks, isset($chunk->Entities) ? $chunk->Entities->getValue() : [], isset($chunk->TileEntities) ? $chunk->TileEntities->getValue() : [], isset($chunk->Biomes) ? $chunk->Biomes->getValue() : "", isset($chunk->HeightMap) ? $chunk->HeightMap->getValue() : [] ); $result->setLightPopulated(isset($chunk->LightPopulated) ? ((bool) $chunk->LightPopulated->getValue()) : false); $result->setPopulated(isset($chunk->TerrainPopulated) ? ((bool) $chunk->TerrainPopulated->getValue()) : false); $result->setGenerated(true); return $result; }catch(\Throwable $e){ MainLogger::getLogger()->logException($e); return null; } } public static function getProviderName() : string{ return "pmanvil"; } } ================================================ FILE: src/pocketmine/level/format/io/region/RegionLoader.php ================================================ x = $regionX; $this->z = $regionZ; $this->levelProvider = $level; $this->filePath = $this->levelProvider->getPath() . "region/r.$regionX.$regionZ.$fileExtension"; $exists = file_exists($this->filePath); if(!$exists){ touch($this->filePath); } $this->filePointer = fopen($this->filePath, "r+b"); stream_set_read_buffer($this->filePointer, 1024 * 16); //16KB stream_set_write_buffer($this->filePointer, 1024 * 16); //16KB if(!$exists){ $this->createBlank(); }else{ $this->loadLocationTable(); } $this->lastUsed = time(); } public function __destruct(){ if(is_resource($this->filePointer)){ $this->writeLocationTable(); fclose($this->filePointer); } } protected function isChunkGenerated(int $index) : bool{ return !($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0); } public function readChunk(int $x, int $z){ $index = self::getChunkOffset($x, $z); if($index < 0 or $index >= 4096){ return null; } $this->lastUsed = time(); if(!$this->isChunkGenerated($index)){ return null; } fseek($this->filePointer, $this->locationTable[$index][0] << 12); $length = Binary::readInt(fread($this->filePointer, 4)); $compression = ord(fgetc($this->filePointer)); if($length <= 0 or $length > self::MAX_SECTOR_LENGTH){ //Not yet generated / corrupted if($length >= self::MAX_SECTOR_LENGTH){ $this->locationTable[$index][0] = ++$this->lastSector; $this->locationTable[$index][1] = 1; MainLogger::getLogger()->error("Corrupted chunk header detected"); } return null; } if($length > ($this->locationTable[$index][1] << 12)){ //Invalid chunk, bigger than defined number of sectors MainLogger::getLogger()->error("Corrupted bigger chunk detected"); $this->locationTable[$index][1] = $length >> 12; $this->writeLocationIndex($index); }elseif($compression !== self::COMPRESSION_ZLIB and $compression !== self::COMPRESSION_GZIP){ MainLogger::getLogger()->error("Invalid compression type"); return null; } $chunk = $this->levelProvider->nbtDeserialize(fread($this->filePointer, $length - 1)); if($chunk instanceof Chunk){ return $chunk; }else{ MainLogger::getLogger()->error("Corrupted chunk detected"); return null; } } public function chunkExists(int $x, int $z) : bool{ return $this->isChunkGenerated(self::getChunkOffset($x, $z)); } protected function saveChunk(int $x, int $z, string $chunkData){ $length = strlen($chunkData) + 1; if($length + 4 > self::MAX_SECTOR_LENGTH){ throw new ChunkException("Chunk is too big! ".($length + 4)." > ".self::MAX_SECTOR_LENGTH); } $sectors = (int) ceil(($length + 4) / 4096); $index = self::getChunkOffset($x, $z); $indexChanged = false; if($this->locationTable[$index][1] < $sectors){ $this->locationTable[$index][0] = $this->lastSector + 1; $this->lastSector += $sectors; //The GC will clean this shift "later" $indexChanged = true; }elseif($this->locationTable[$index][1] != $sectors){ $indexChanged = true; } $this->locationTable[$index][1] = $sectors; $this->locationTable[$index][2] = time(); fseek($this->filePointer, $this->locationTable[$index][0] << 12); fwrite($this->filePointer, str_pad(Binary::writeInt($length) . chr(self::COMPRESSION_ZLIB) . $chunkData, $sectors << 12, "\x00", STR_PAD_RIGHT)); if($indexChanged){ $this->writeLocationIndex($index); } } public function removeChunk(int $x, int $z){ $index = self::getChunkOffset($x, $z); $this->locationTable[$index][0] = 0; $this->locationTable[$index][1] = 0; } public function writeChunk(Chunk $chunk){ $this->lastUsed = time(); $chunkData = $this->levelProvider->nbtSerialize($chunk); if($chunkData !== false){ $this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $chunkData); } } protected static function getChunkOffset(int $x, int $z) : int{ return $x + ($z << 5); } public function close(){ $this->writeLocationTable(); fclose($this->filePointer); $this->levelProvider = null; } public function doSlowCleanUp() : int{ for($i = 0; $i < 1024; ++$i){ if($this->locationTable[$i][0] === 0 or $this->locationTable[$i][1] === 0){ continue; } fseek($this->filePointer, $this->locationTable[$i][0] << 12); $chunk = fread($this->filePointer, $this->locationTable[$i][1] << 12); $length = Binary::readInt(substr($chunk, 0, 4)); if($length <= 1){ $this->locationTable[$i] = [0, 0, 0]; //Non-generated chunk, remove it from index } try{ $chunk = zlib_decode(substr($chunk, 5)); }catch(\Throwable $e){ $this->locationTable[$i] = [0, 0, 0]; //Corrupted chunk, remove it continue; } $chunk = chr(self::COMPRESSION_ZLIB) . zlib_encode($chunk, ZLIB_ENCODING_DEFLATE, 9); $chunk = Binary::writeInt(strlen($chunk)) . $chunk; $sectors = (int) ceil(strlen($chunk) / 4096); if($sectors > $this->locationTable[$i][1]){ $this->locationTable[$i][0] = $this->lastSector + 1; $this->lastSector += $sectors; } fseek($this->filePointer, $this->locationTable[$i][0] << 12); fwrite($this->filePointer, str_pad($chunk, $sectors << 12, "\x00", STR_PAD_RIGHT)); } $this->writeLocationTable(); $n = $this->cleanGarbage(); $this->writeLocationTable(); return $n; } private function cleanGarbage() : int{ $sectors = []; foreach($this->locationTable as $index => $data){ //Calculate file usage if($data[0] === 0 or $data[1] === 0){ $this->locationTable[$index] = [0, 0, 0]; continue; } for($i = 0; $i < $data[1]; ++$i){ $sectors[$data[0]] = $index; } } if(count($sectors) === ($this->lastSector - 2)){ //No collection needed return 0; } ksort($sectors); $shift = 0; $lastSector = 1; //First chunk - 1 fseek($this->filePointer, 8192); $sector = 2; foreach($sectors as $sector => $index){ if(($sector - $lastSector) > 1){ $shift += $sector - $lastSector - 1; } if($shift > 0){ fseek($this->filePointer, $sector << 12); $old = fread($this->filePointer, 4096); fseek($this->filePointer, ($sector - $shift) << 12); fwrite($this->filePointer, $old, 4096); } $this->locationTable[$index][0] -= $shift; $lastSector = $sector; } ftruncate($this->filePointer, ($sector + 1) << 12); //Truncate to the end of file written return $shift; } protected function loadLocationTable(){ fseek($this->filePointer, 0); $this->lastSector = 1; $data = unpack("N*", fread($this->filePointer, 4 * 1024 * 2)); //1024 records * 4 bytes * 2 times for($i = 0; $i < 1024; ++$i){ $index = $data[$i + 1]; $this->locationTable[$i] = [$index >> 8, $index & 0xff, $data[1024 + $i + 1]]; if(($this->locationTable[$i][0] + $this->locationTable[$i][1] - 1) > $this->lastSector){ $this->lastSector = $this->locationTable[$i][0] + $this->locationTable[$i][1] - 1; } } } private function writeLocationTable(){ $write = []; for($i = 0; $i < 1024; ++$i){ $write[] = (($this->locationTable[$i][0] << 8) | $this->locationTable[$i][1]); } for($i = 0; $i < 1024; ++$i){ $write[] = $this->locationTable[$i][2]; } fseek($this->filePointer, 0); fwrite($this->filePointer, pack("N*", ...$write), 4096 * 2); } protected function writeLocationIndex($index){ fseek($this->filePointer, $index << 2); fwrite($this->filePointer, Binary::writeInt(($this->locationTable[$index][0] << 8) | $this->locationTable[$index][1]), 4); fseek($this->filePointer, 4096 + ($index << 2)); fwrite($this->filePointer, Binary::writeInt($this->locationTable[$index][2]), 4); } protected function createBlank(){ fseek($this->filePointer, 0); ftruncate($this->filePointer, 0); $this->lastSector = 1; $table = ""; for($i = 0; $i < 1024; ++$i){ $this->locationTable[$i] = [0, 0]; $table .= Binary::writeInt(0); } $time = time(); for($i = 0; $i < 1024; ++$i){ $this->locationTable[$i][2] = $time; $table .= Binary::writeInt($time); } fwrite($this->filePointer, $table, 4096 * 2); } public function getX() : int{ return $this->x; } public function getZ() : int{ return $this->z; } } ================================================ FILE: src/pocketmine/level/generator/Flat.php ================================================ options; } public function getName() : string{ return "flat"; } public function __construct(array $options = []){ $this->preset = "2;7,2x3,2;1;"; $this->options = $options; $this->chunk = null; if(isset($this->options["decoration"])){ $ores = new Ore(); $ores->setOreTypes([ new object\OreType(new CoalOre(), 20, 16, 0, 128), new object\OreType(New IronOre(), 20, 8, 0, 64), new object\OreType(new RedstoneOre(), 8, 7, 0, 16), new object\OreType(new LapisOre(), 1, 6, 0, 32), new object\OreType(new GoldOre(), 2, 8, 0, 32), new object\OreType(new DiamondOre(), 1, 7, 0, 16), new object\OreType(new Dirt(), 20, 32, 0, 128), new object\OreType(new Gravel(), 10, 16, 0, 128), ]); $this->populators[] = $ores; } } protected function parsePreset($preset, $chunkX, $chunkZ){ $this->preset = $preset; $preset = explode(";", $preset); $version = (int) $preset[0]; $blocks = $preset[1] ?? ""; $biome = $preset[2] ?? 1; $options = $preset[3] ?? ""; preg_match_all('#^(([0-9]*x|)([0-9]{1,3})(|:[0-9]{0,2}))$#m', str_replace(",", "\n", $blocks), $matches); $y = 0; $this->structure = []; $this->chunks = []; foreach($matches[3] as $i => $b){ $b = Item::fromString($b . $matches[4][$i]); $cnt = $matches[2][$i] === "" ? 1 : intval($matches[2][$i]); for($cY = $y, $y += $cnt; $cY < $y; ++$cY){ $this->structure[$cY] = [$b->getId(), $b->getDamage()]; } } $this->floorLevel = $y; for(; $y < 0xFF; ++$y){ $this->structure[$y] = [0, 0]; } $this->chunk = clone $this->level->getChunk($chunkX, $chunkZ); $this->chunk->setGenerated(); for($Z = 0; $Z < 16; ++$Z){ for($X = 0; $X < 16; ++$X){ $this->chunk->setBiomeId($X, $Z, $biome); for($y = 0; $y < 128; ++$y){ $this->chunk->setBlock($X, $y, $Z, ...$this->structure[$y]); } } } preg_match_all('#(([0-9a-z_]{1,})\(?([0-9a-z_ =:]{0,})\)?),?#', $options, $matches); foreach($matches[2] as $i => $option){ $params = true; if($matches[3][$i] !== ""){ $params = []; $p = explode(" ", $matches[3][$i]); foreach($p as $k){ $k = explode("=", $k); if(isset($k[1])){ $params[$k[0]] = $k[1]; } } } $this->options[$option] = $params; } } public function init(ChunkManager $level, Random $random){ $this->level = $level; $this->random = $random; /* // Commented out : We want to delay this if(isset($this->options["preset"]) and $this->options["preset"] != ""){ $this->parsePreset($this->options["preset"]); }else{ $this->parsePreset($this->preset); } */ } public function generateChunk($chunkX, $chunkZ){ if($this->chunk === null){ if(isset($this->options["preset"]) and $this->options["preset"] != ""){ $this->parsePreset($this->options["preset"], $chunkX, $chunkZ); }else{ $this->parsePreset($this->preset, $chunkX, $chunkZ); } } $chunk = clone $this->chunk; $chunk->setX($chunkX); $chunk->setZ($chunkZ); $this->level->setChunk($chunkX, $chunkZ, $chunk); } public function populateChunk($chunkX, $chunkZ){ $this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed()); foreach($this->populators as $populator){ $populator->populate($this->level, $chunkX, $chunkZ, $this->random); } } public function getSpawn(){ return new Vector3(128, $this->floorLevel, 128); } } ================================================ FILE: src/pocketmine/level/generator/GenerationTask.php ================================================ state = true; $this->levelId = $level->getId(); $this->chunk = $chunk->fastSerialize(); } public function onRun(){ /** @var SimpleChunkManager $manager */ $manager = $this->getFromThreadStore("generation.level{$this->levelId}.manager"); /** @var Generator $generator */ $generator = $this->getFromThreadStore("generation.level{$this->levelId}.generator"); if($manager === null or $generator === null){ $this->state = false; return; } /** @var Chunk $chunk */ $chunk = Chunk::fastDeserialize($this->chunk); if($chunk === null){ //TODO error return; } $manager->setChunk($chunk->getX(), $chunk->getZ(), $chunk); $generator->generateChunk($chunk->getX(), $chunk->getZ()); $chunk = $manager->getChunk($chunk->getX(), $chunk->getZ()); $chunk->setGenerated(); $this->chunk = $chunk->fastSerialize(); $manager->setChunk($chunk->getX(), $chunk->getZ(), null); } public function onCompletion(Server $server){ $level = $server->getLevel($this->levelId); if($level !== null){ if($this->state === false){ $level->registerGenerator(); return; } /** @var Chunk $chunk */ $chunk = Chunk::fastDeserialize($this->chunk, $level->getProvider()); if($chunk === null){ //TODO error return; } $level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk); } } } ================================================ FILE: src/pocketmine/level/generator/Generator.php ================================================ $c){ if($c === $class){ return $name; } } return "unknown"; } /** * @param Noise $noise * @param int $xSize * @param int $samplingRate * @param int $x * @param int $y * @param int $z * * @return \SplFixedArray */ public static function getFastNoise1D(Noise $noise, $xSize, $samplingRate, $x, $y, $z){ if($samplingRate === 0){ throw new \InvalidArgumentException("samplingRate cannot be 0"); } if ($xSize % $samplingRate !== 0) { throw new \InvalidArgumentCountException("xSize % samplingRate must return 0"); } $noiseArray = new \SplFixedArray($xSize + 1); for($xx = 0; $xx <= $xSize; $xx += $samplingRate){ $noiseArray[$xx] = $noise->noise3D($xx + $x, $y, $z); } for($xx = 0; $xx < $xSize; ++$xx){ if($xx % $samplingRate !== 0){ $nx = (int) ($xx / $samplingRate) * $samplingRate; $noiseArray[$xx] = Noise::linearLerp($xx, $nx, $nx + $samplingRate, $noiseArray[$nx], $noiseArray[$nx + $samplingRate]); } } return $noiseArray; } /** * @param Noise $noise * @param int $xSize * @param int $zSize * @param int $samplingRate * @param int $x * @param int $y * @param int $z * * @return \SplFixedArray */ public static function getFastNoise2D(Noise $noise, $xSize, $zSize, $samplingRate, $x, $y, $z){ if($samplingRate === 0){ throw new \InvalidArgumentException("samplingRate cannot be 0"); } if ($xSize % $samplingRate !== 0) { throw new \InvalidArgumentCountException("xSize % samplingRate must return 0"); } if ($zSize % $samplingRate !== 0) { throw new \InvalidArgumentCountException("zSize % samplingRate must return 0"); } $noiseArray = new \SplFixedArray($xSize + 1); for($xx = 0; $xx <= $xSize; $xx += $samplingRate){ $noiseArray[$xx] = new \SplFixedArray($zSize + 1); for($zz = 0; $zz <= $zSize; $zz += $samplingRate){ $noiseArray[$xx][$zz] = $noise->noise3D($x + $xx, $y, $z + $zz); } } for($xx = 0; $xx < $xSize; ++$xx){ if($xx % $samplingRate !== 0){ $noiseArray[$xx] = new \SplFixedArray($zSize + 1); } for($zz = 0; $zz < $zSize; ++$zz){ if($xx % $samplingRate !== 0 or $zz % $samplingRate !== 0){ $nx = (int) ($xx / $samplingRate) * $samplingRate; $nz = (int) ($zz / $samplingRate) * $samplingRate; $noiseArray[$xx][$zz] = Noise::bilinearLerp( $xx, $zz, $noiseArray[$nx][$nz], $noiseArray[$nx][$nz + $samplingRate], $noiseArray[$nx + $samplingRate][$nz], $noiseArray[$nx + $samplingRate][$nz + $samplingRate], $nx, $nx + $samplingRate, $nz, $nz + $samplingRate ); } } } return $noiseArray; } /** * @param Noise $noise * @param int $xSize * @param int $ySize * @param int $zSize * @param int $xSamplingRate * @param int $ySamplingRate * @param int $zSamplingRate * @param int $x * @param int $y * @param int $z * * @return \SplFixedArray */ public static function getFastNoise3D(Noise $noise, $xSize, $ySize, $zSize, $xSamplingRate, $ySamplingRate, $zSamplingRate, $x, $y, $z){ if($xSamplingRate === 0){ throw new \InvalidArgumentException("xSamplingRate cannot be 0"); } if($zSamplingRate === 0){ throw new \InvalidArgumentException("zSamplingRate cannot be 0"); } if($ySamplingRate === 0){ throw new \InvalidArgumentException("ySamplingRate cannot be 0"); } if ($xSize % $xSamplingRate !== 0) { throw new \InvalidArgumentCountException("xSize % xSamplingRate must return 0"); } if ($zSize % $zSamplingRate !== 0) { throw new \InvalidArgumentCountException("zSize % zSamplingRate must return 0"); } if ($ySize % $ySamplingRate !== 0) { throw new \InvalidArgumentCountException("ySize % ySamplingRate must return 0"); } $noiseArray = array_fill(0, $xSize + 1, array_fill(0, $zSize + 1, [])); for($xx = 0; $xx <= $xSize; $xx += $xSamplingRate){ for($zz = 0; $zz <= $zSize; $zz += $zSamplingRate){ for($yy = 0; $yy <= $ySize; $yy += $ySamplingRate){ $noiseArray[$xx][$zz][$yy] = $noise->noise3D($x + $xx, $y + $yy, $z + $zz, true); } } } for($xx = 0; $xx < $xSize; ++$xx){ for($zz = 0; $zz < $zSize; ++$zz){ for($yy = 0; $yy < $ySize; ++$yy){ if($xx % $xSamplingRate !== 0 or $zz % $zSamplingRate !== 0 or $yy % $ySamplingRate !== 0){ $nx = (int) ($xx / $xSamplingRate) * $xSamplingRate; $ny = (int) ($yy / $ySamplingRate) * $ySamplingRate; $nz = (int) ($zz / $zSamplingRate) * $zSamplingRate; $nnx = $nx + $xSamplingRate; $nny = $ny + $ySamplingRate; $nnz = $nz + $zSamplingRate; $dx1 = (($nnx - $xx) / ($nnx - $nx)); $dx2 = (($xx - $nx) / ($nnx - $nx)); $dy1 = (($nny - $yy) / ($nny - $ny)); $dy2 = (($yy - $ny) / ($nny - $ny)); $noiseArray[$xx][$zz][$yy] = (($nnz - $zz) / ($nnz - $nz)) * ( $dy1 * ( $dx1 * $noiseArray[$nx][$nz][$ny] + $dx2 * $noiseArray[$nnx][$nz][$ny] ) + $dy2 * ( $dx1 * $noiseArray[$nx][$nz][$nny] + $dx2 * $noiseArray[$nnx][$nz][$nny] ) ) + (($zz - $nz) / ($nnz - $nz)) * ( $dy1 * ( $dx1 * $noiseArray[$nx][$nnz][$ny] + $dx2 * $noiseArray[$nnx][$nnz][$ny] ) + $dy2 * ( $dx1 * $noiseArray[$nx][$nnz][$nny] + $dx2 * $noiseArray[$nnx][$nnz][$nny] ) ); } } } } return $noiseArray; } public function getWaterHeight() : int{ return 0; } public abstract function __construct(array $settings = []); public abstract function init(ChunkManager $level, Random $random); public abstract function generateChunk($chunkX, $chunkZ); public abstract function populateChunk($chunkX, $chunkZ); public abstract function getSettings(); public abstract function getName(); public abstract function getSpawn(); } ================================================ FILE: src/pocketmine/level/generator/GeneratorRegisterTask.php ================================================ generator = get_class($generator); $this->waterHeight = $generator->getWaterHeight(); $this->settings = serialize($generator->getSettings()); $this->seed = $level->getSeed(); $this->levelId = $level->getId(); } public function onRun(){ Block::init(); Biome::init(); $manager = new SimpleChunkManager($this->seed, $this->waterHeight); $this->saveToThreadStore("generation.level{$this->levelId}.manager", $manager); /** @var Generator $generator */ $generator = $this->generator; $generator = new $generator(unserialize($this->settings)); $generator->init($manager, new Random($manager->getSeed())); $this->saveToThreadStore("generation.level{$this->levelId}.generator", $generator); } } ================================================ FILE: src/pocketmine/level/generator/GeneratorUnregisterTask.php ================================================ levelId = $level->getId(); } public function onRun(){ $this->saveToThreadStore("generation.level{$this->levelId}.manager", null); $this->saveToThreadStore("generation.level{$this->levelId}.generator", null); } } ================================================ FILE: src/pocketmine/level/generator/LightPopulationTask.php ================================================ levelId = $level->getId(); $this->chunk = $chunk->fastSerialize(); } public function onRun(){ /** @var Chunk $chunk */ $chunk = Chunk::fastDeserialize($this->chunk); if($chunk === null){ //TODO error return; } $chunk->recalculateHeightMap(); $chunk->populateSkyLight(); $chunk->setLightPopulated(); $this->chunk = $chunk->fastSerialize(); } public function onCompletion(Server $server){ $level = $server->getLevel($this->levelId); if($level !== null){ /** @var Chunk $chunk */ $chunk = Chunk::fastDeserialize($this->chunk, $level->getProvider()); if($chunk === null){ //TODO error return; } $level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk); } } } ================================================ FILE: src/pocketmine/level/generator/PopulationTask.php ================================================ state = true; $this->levelId = $level->getId(); $this->chunk = $chunk->fastSerialize(); for($i = 0; $i < 9; ++$i){ if($i === 4){ continue; } $xx = -1 + $i % 3; $zz = -1 + (int) ($i / 3); $ck = $level->getChunk($chunk->getX() + $xx, $chunk->getZ() + $zz, false); $this->{"chunk$i"} = $ck !== null ? $ck->fastSerialize() : null; } } public function onRun(){ /** @var SimpleChunkManager $manager */ $manager = $this->getFromThreadStore("generation.level{$this->levelId}.manager"); /** @var Generator $generator */ $generator = $this->getFromThreadStore("generation.level{$this->levelId}.generator"); if($manager === null or $generator === null){ $this->state = false; return; } /** @var Chunk[] $chunks */ $chunks = []; $chunk = Chunk::fastDeserialize($this->chunk); for($i = 0; $i < 9; ++$i){ if($i === 4){ continue; } $xx = -1 + $i % 3; $zz = -1 + (int) ($i / 3); $ck = $this->{"chunk$i"}; if($ck === null){ $chunks[$i] = Chunk::getEmptyChunk($chunk->getX() + $xx, $chunk->getZ() + $zz); }else{ $chunks[$i] = Chunk::fastDeserialize($ck); } } if($chunk === null){ //TODO error return; } $manager->setChunk($chunk->getX(), $chunk->getZ(), $chunk); if(!$chunk->isGenerated()){ $generator->generateChunk($chunk->getX(), $chunk->getZ()); $chunk->setGenerated(); } foreach($chunks as $c){ if($c !== null){ $manager->setChunk($c->getX(), $c->getZ(), $c); if(!$c->isGenerated()){ $generator->generateChunk($c->getX(), $c->getZ()); $c = $manager->getChunk($c->getX(), $c->getZ()); $c->setGenerated(); } } } $generator->populateChunk($chunk->getX(), $chunk->getZ()); $chunk = $manager->getChunk($chunk->getX(), $chunk->getZ()); $chunk->recalculateHeightMap(); $chunk->populateSkyLight(); $chunk->setLightPopulated(); $chunk->setPopulated(); $this->chunk = $chunk->fastSerialize(); $manager->setChunk($chunk->getX(), $chunk->getZ(), null); foreach($chunks as $i => $c){ if($c !== null){ $c = $chunks[$i] = $manager->getChunk($c->getX(), $c->getZ()); if(!$c->hasChanged()){ $chunks[$i] = null; } }else{ //This way non-changed chunks are not set $chunks[$i] = null; } } $manager->cleanChunks(); for($i = 0; $i < 9; ++$i){ if($i === 4){ continue; } $this->{"chunk$i"} = $chunks[$i] !== null ? $chunks[$i]->fastSerialize() : null; } } public function onCompletion(Server $server){ $level = $server->getLevel($this->levelId); if($level !== null){ if($this->state === false){ $level->registerGenerator(); return; } $chunk = Chunk::fastDeserialize($this->chunk, $level->getProvider()); if($chunk === null){ //TODO error return; } for($i = 0; $i < 9; ++$i){ if($i === 4){ continue; } $c = $this->{"chunk$i"}; if($c !== null){ $c = Chunk::fastDeserialize($c, $level->getProvider()); $level->generateChunkCallback($c->getX(), $c->getZ(), $c); } } $level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk); } } } ================================================ FILE: src/pocketmine/level/generator/Void.php ================================================ options = $settings; } public function init(ChunkManager $level, Random $random){ $this->level = $level; $this->random = $random; } public function generateChunk($chunkX, $chunkZ){ if($this->emptyChunk === null){ $this->chunk = clone $this->level->getChunk($chunkX, $chunkZ); $this->chunk->setGenerated(); for($Z = 0; $Z < 16; ++$Z){ for($X = 0; $X < 16; ++$X){ $this->chunk->setBiomeId($X, $Z, 1); for($y = 0; $y < 128; ++$y){ $this->chunk->setBlockId($X, $y, $Z, Block::AIR); } } } $spawn = $this->getSpawn(); if($spawn->getX() >> 4 === $chunkX and $spawn->getZ() >> 4 === $chunkZ){ $this->chunk->setBlockId(0, 64, 0, Block::GRASS); }else{ $this->emptyChunk = clone $this->chunk; } }else{ $this->chunk = clone $this->emptyChunk; } $chunk = clone $this->chunk; $chunk->setX($chunkX); $chunk->setZ($chunkZ); $this->level->setChunk($chunkX, $chunkZ, $chunk); } public function populateChunk($chunkX, $chunkZ){ } public function getSpawn(){ return new Vector3(128, 72, 128); } } ================================================ FILE: src/pocketmine/level/generator/biome/Biome.php ================================================ setId((int) $id); $flowerPopFound = false; foreach($biome->getPopulators() as $populator){ if($populator instanceof Flower){ $flowerPopFound = true; break; } } if($flowerPopFound === false){ $flower = new Flower(); $biome->addPopulator($flower); } } public static function init(){ self::register(self::OCEAN, new OceanBiome()); self::register(self::PLAINS, new PlainBiome()); self::register(self::DESERT, new DesertBiome()); self::register(self::MOUNTAINS, new MountainsBiome()); self::register(self::FOREST, new ForestBiome()); self::register(self::TAIGA, new TaigaBiome()); self::register(self::SWAMP, new SwampBiome()); self::register(self::RIVER, new RiverBiome()); self::register(self::BEACH, new BeachBiome()); self::register(self::MESA, new MesaBiome()); self::register(self::ICE_PLAINS, new IcePlainsBiome()); self::register(self::SMALL_MOUNTAINS, new SmallMountainsBiome()); self::register(self::HELL, new HellBiome()); self::register(self::BIRCH_FOREST, new ForestBiome(ForestBiome::TYPE_BIRCH)); } /** * @param $id * * @return Biome */ public static function getBiome($id){ return isset(self::$biomes[$id]) ? self::$biomes[$id] : self::$biomes[self::OCEAN]; } public function clearPopulators(){ $this->populators = []; } public function addPopulator(Populator $populator){ $this->populators[get_class($populator)] = $populator; } public function removePopulator($class){ if(isset($this->populators[$class])){ unset($this->populators[$class]); } } public function populateChunk(ChunkManager $level, $chunkX, $chunkZ, Random $random){ foreach($this->populators as $populator){ $populator->populate($level, $chunkX, $chunkZ, $random); } } public function getPopulators(){ return $this->populators; } public function setId($id){ if(!$this->registered){ $this->registered = true; $this->id = $id; } } public function getId(){ return $this->id; } public abstract function getName(); public function getMinElevation(){ return $this->minElevation; } public function getMaxElevation(){ return $this->maxElevation; } public function setElevation($min, $max){ $this->minElevation = $min; $this->maxElevation = $max; } /** * @return Block[] */ public function getGroundCover(){ return $this->groundCover; } /** * @param Block[] $covers */ public function setGroundCover(array $covers){ $this->groundCover = $covers; } public function getTemperature(){ return $this->temperature; } public function getRainfall(){ return $this->rainfall; } } ================================================ FILE: src/pocketmine/level/generator/biome/BiomeSelector.php ================================================ fallback = $fallback; $this->temperature = new Simplex($random, 2, 1 / 16, 1 / 512); $this->rainfall = new Simplex($random, 2, 1 / 16, 1 / 512); } public function lookup($temperature, $rainfall){ if($rainfall < 0.25){ if($temperature < 0.7){ return Biome::OCEAN; }elseif($temperature < 0.85){ return Biome::RIVER; }else{ return Biome::SWAMP; } }elseif($rainfall < 0.60){ if($temperature < 0.25){ return Biome::ICE_PLAINS; }elseif($temperature < 0.75){ return Biome::PLAINS; }else{ return Biome::DESERT; } }elseif($rainfall < 0.80){ if($temperature < 0.25){ return Biome::TAIGA; }elseif($temperature < 0.75){ return Biome::FOREST; }else{ return Biome::BIRCH_FOREST; } }else{ if($temperature < 0.25){ return Biome::MOUNTAINS; }elseif($temperature < 0.70){ return Biome::SMALL_MOUNTAINS; }elseif($temperature <= 2.0){ return Biome::MESA; }else{ return Biome::RIVER; } } } public function recalculate(){ $this->map = new \SplFixedArray(64 * 64); for($i = 0; $i < 64; ++$i){ for($j = 0; $j < 64; ++$j){ $this->map[$i + ($j << 6)] = $this->lookup($i / 63, $j / 63); } } } public function addBiome(Biome $biome){ $this->biomes[$biome->getId()] = $biome; } public function getTemperature($x, $z){ return ($this->temperature->noise2D($x, $z, true) + 1) / 2; } public function getRainfall($x, $z){ return ($this->rainfall->noise2D($x, $z, true) + 1) / 2; } /** * @param $x * @param $z * * @return Biome */ public function pickBiome($x, $z){ $temperature = (int) ($this->getTemperature($x, $z) * 63); $rainfall = (int) ($this->getRainfall($x, $z) * 63); $biomeId = $this->map[$temperature + ($rainfall << 6)]; return isset($this->biomes[$biomeId]) ? $this->biomes[$biomeId] : $this->fallback; } } ================================================ FILE: src/pocketmine/level/generator/nether/Nether.php ================================================ waterHeight; } public function getSettings(){ return []; } public function init(ChunkManager $level, Random $random){ $this->level = $level; $this->random = $random; $this->random->setSeed($this->level->getSeed()); $this->noiseBase = new Simplex($this->random, 8, 2 / 8, 2 / 128); $this->random->setSeed($this->level->getSeed()); $ores = new NetherOre(); $ores->setOreTypes([ new OreType(new NetherQuartzOre(), 20, 16, 0, 126), new OreType(new SoulSand(), 5, 64, 0, 126), new OreType(new Gravel(), 8, 33, 0, 126), new OreType(new Lava(), 1, 16, 0, $this->waterHeight), ]); $this->populators[] = $ores; $this->populators[] = new NetherGlowStone(); $groundFire = new GroundFire(); $groundFire->setBaseAmount(1); $groundFire->setRandomAmount(1); $this->populators[] = $groundFire; $lava = new NetherLava(); $lava->setBaseAmount(0); $lava->setRandomAmount(0); $this->populators[] = $lava; } public function generateChunk($chunkX, $chunkZ){ $this->random->setSeed(0xdeadbeef ^ $chunkX ^ $chunkZ ^ $this->level->getSeed()); $noise = Generator::getFastNoise3D($this->noiseBase, 16, 128, 16, 4, 8, 4, $chunkX * 16, 0, $chunkZ * 16); $chunk = $this->level->getChunk($chunkX, $chunkZ); for($x = 0; $x < 16; ++$x){ for($z = 0; $z < 16; ++$z){ $biome = Biome::getBiome(Biome::HELL); $chunk->setBiomeId($x, $z, $biome->getId()); for($y = 0; $y < 128; ++$y){//The nether is still 128 blocks, #BlameMojang if($y === 0 or $y === 127){ $chunk->setBlockId($x, $y, $z, Block::BEDROCK); continue; } $noiseValue = (abs($this->emptyHeight - $y) / $this->emptyHeight) * $this->emptyAmplitude - $noise[$x][$z][$y]; $noiseValue -= 1 - $this->density; if($noiseValue > 0){ $chunk->setBlockId($x, $y, $z, Block::NETHERRACK); }elseif($y <= $this->waterHeight){ $chunk->setBlockId($x, $y, $z, Block::STILL_LAVA); $chunk->setBlockLight($x, $y + 1, $z, 15); } } } } foreach($this->generationPopulators as $populator){ $populator->populate($this->level, $chunkX, $chunkZ, $this->random); } } public function populateChunk($chunkX, $chunkZ){ $this->random->setSeed(0xdeadbeef ^ $chunkX ^ $chunkZ ^ $this->level->getSeed()); foreach($this->populators as $populator){ $populator->populate($this->level, $chunkX, $chunkZ, $this->random); } $chunk = $this->level->getChunk($chunkX, $chunkZ); $biome = Biome::getBiome($chunk->getBiomeId(7, 7)); // If implement more biomes and stronghold, please add here. $biome->populateChunk($this->level, $chunkX, $chunkZ, $this->random); } public function getSpawn(){ return new Vector3(127.5, 128, 127.5); } } ================================================ FILE: src/pocketmine/level/generator/nether/biome/HellBiome.php ================================================ type = $type; $this->random = $random; } public function getType(){ return $this->type; } public function canPlaceObject(ChunkManager $level, $x, $y, $z){ return ($level->getBlockIdAt($x, $y, $z) === 87); } public function placeObject(ChunkManager $level, $x, $y, $z){ $clusterSize = (int) $this->type->clusterSize; $angle = $this->random->nextFloat() * M_PI; $offset = VectorMath::getDirection2D($angle)->multiply($clusterSize)->divide(8); $x1 = $x + 8 + $offset->x; $x2 = $x + 8 - $offset->x; $z1 = $z + 8 + $offset->y; $z2 = $z + 8 - $offset->y; $y1 = $y + $this->random->nextBoundedInt(3) + 2; $y2 = $y + $this->random->nextBoundedInt(3) + 2; for($count = 0; $count <= $clusterSize; ++$count){ $seedX = $x1 + ($x2 - $x1) * $count / $clusterSize; $seedY = $y1 + ($y2 - $y1) * $count / $clusterSize; $seedZ = $z1 + ($z2 - $z1) * $count / $clusterSize; $size = ((sin($count * (M_PI / $clusterSize)) + 1) * $this->random->nextFloat() * $clusterSize / 16 + 1) / 2; $startX = (int) ($seedX - $size); $startY = (int) ($seedY - $size); $startZ = (int) ($seedZ - $size); $endX = (int) ($seedX + $size); $endY = (int) ($seedY + $size); $endZ = (int) ($seedZ + $size); //echo "ORE: $startX, $startY, $startZ,, $endX, $endY, $endZ\n"; for($x = $startX; $x <= $endX; ++$x){ $sizeX = ($x + 0.5 - $seedX) / $size; $sizeX *= $sizeX; if($sizeX < 1){ for($y = $startY; $y <= $endY; ++$y){ $sizeY = ($y + 0.5 - $seedY) / $size; $sizeY *= $sizeY; if($y > 0 and ($sizeX + $sizeY) < 1){ for($z = $startZ; $z <= $endZ; ++$z){ $sizeZ = ($z + 0.5 - $seedZ) / $size; $sizeZ *= $sizeZ; if(($sizeX + $sizeY + $sizeZ) < 1 and $level->getBlockIdAt($x, $y, $z) === 87){ $level->setBlockIdAt($x, $y, $z, $this->type->material->getId()); if($this->type->material->getDamage() !== 0){ $level->setBlockDataAt($x, $y, $z, $this->type->material->getDamage()); } //echo "Placed to $x, $y, $z\n"; } } } } } } } } } ================================================ FILE: src/pocketmine/level/generator/nether/populator/GroundFire.php ================================================ level = $level; $amount = $this->getAmount($random); for($i = 0; $i < $amount; ++$i){ $x = $random->nextRange($chunkX * 16, $chunkX * 16 + 15); $z = $random->nextRange($chunkZ * 16, $chunkZ * 16 + 15); $y = $this->getHighestWorkableBlock($x, $z); if($y !== -1 and $this->canGroundFireStay($x, $y, $z)){ $this->level->setBlockIdAt($x, $y, $z, Block::FIRE); $this->level->updateBlockLight($x, $y, $z); } } } private function canGroundFireStay($x, $y, $z){ $b = $this->level->getBlockIdAt($x, $y, $z); return ($b === Block::AIR or $b === Block::SNOW_LAYER) and $this->level->getBlockIdAt($x, $y - 1, $z) === 87; } private function getHighestWorkableBlock($x, $z){ for($y = 0; $y <= 127; ++$y){ $b = $this->level->getBlockIdAt($x, $y, $z); if($b == Block::AIR){ break; } } return $y === 0 ? -1 : $y; } } ================================================ FILE: src/pocketmine/level/generator/nether/populator/NetherGlowStone.php ================================================ level = $level; $type = new OreType(new Glowstone(), 1, 20, 128, 10); $ore = new ObjectOre($random, $type); for($i = 0; $i < $ore->type->clusterCount; ++$i){ $x = $random->nextRange($chunkX << 4, ($chunkX << 4) + 15); $z = $random->nextRange($chunkZ << 4, ($chunkZ << 4) + 15); $y = $this->getHighestWorkableBlock($x, $z); $ore->placeObject($level, $x, $y, $z); } } private function getHighestWorkableBlock($x, $z){ for($y = 127; $y >= 0; --$y){ $b = $this->level->getBlockIdAt($x, $y, $z); if($b == 0){ break; } } return $y === 0 ? -1 : ++$y; } } ================================================ FILE: src/pocketmine/level/generator/nether/populator/NetherLava.php ================================================ level = $level; $amount = $this->getAmount($random); for($i = 0; $i < $amount; ++$i){ $x = $random->nextRange($chunkX * 16, $chunkX * 16 + 15); $z = $random->nextRange($chunkZ * 16, $chunkZ * 16 + 15); $y = $this->getHighestWorkableBlock($x, $z); if($y !== -1 and $this->canNetherLavaStay($x, $y, $z)){ $this->level->setBlockIdAt($x, $y, $z, Block::LAVA); $this->level->updateBlockLight($x, $y, $z); $this->lavaSpread($x, $y, $z); } } } } private function getFlowDecay($x1, $y1, $z1, $x2, $y2, $z2){ if($this->level->getBlockIdAt($x1, $y1, $z1) !== $this->level->getBlockIdAt($x2, $y2, $z2)){ return -1; }else{ return $this->level->getBlockDataAt($x2, $y2, $z2); } } private function lavaSpread($x, $y, $z){ if($this->level->getChunk($x >> 4, $z >> 4) == null){ return; } $decay = $this->getFlowDecay($x, $y, $z, $x, $y, $z); $multiplier = 2; if($decay > 0){ $smallestFlowDecay = -100; $smallestFlowDecay = $this->getSmallestFlowDecay($x, $y, $z, $x, $y, $z - 1, $smallestFlowDecay); $smallestFlowDecay = $this->getSmallestFlowDecay($x, $y, $z, $x, $y, $z + 1, $smallestFlowDecay); $smallestFlowDecay = $this->getSmallestFlowDecay($x, $y, $z, $x - 1, $y, $z, $smallestFlowDecay); $smallestFlowDecay = $this->getSmallestFlowDecay($x, $y, $z, $x + 1, $y, $z, $smallestFlowDecay); $k = $smallestFlowDecay + $multiplier; if($k >= 8 or $smallestFlowDecay < 0){ $k = -1; } if(($topFlowDecay = $this->getFlowDecay($x, $y, $z, $x, $y + 1, $z)) >= 0){ if($topFlowDecay >= 8){ $k = $topFlowDecay; }else{ $k = $topFlowDecay | 0x08; } } if($decay < 8 and $k < 8 and $k > 1 and mt_rand(0, 4) !== 0){ $k = $decay; } if($k !== $decay){ $decay = $k; if($decay < 0){ $this->level->setBlockIdAt($x, $y, $z, 0); }else{ $this->level->setBlockIdAt($x, $y, $z, Block::LAVA); $this->level->setBlockDataAt($x, $y, $z, $decay); $this->level->updateBlockLight($x, $y, $z); $this->lavaSpread($x, $y, $z); return; } } } if($this->canFlowInto($x, $y - 1, $z)){ if($decay >= 8){ $this->flowIntoBlock($x, $y - 1, $z, $decay); }else{ $this->flowIntoBlock($x, $y - 1, $z, $decay | 0x08); } }elseif($decay >= 0 and ($decay === 0 or !$this->canFlowInto($x, $y - 1, $z))){ $flags = $this->getOptimalFlowDirections($x, $y, $z); $l = $decay + $multiplier; if($decay >= 8){ $l = 1; } if($l >= 8){ return; } if($flags[0]){ $this->flowIntoBlock($x - 1, $y, $z, $l); } if($flags[1]){ $this->flowIntoBlock($x + 1, $y, $z, $l); } if($flags[2]){ $this->flowIntoBlock($x, $y, $z - 1, $l); } if($flags[3]){ $this->flowIntoBlock($x, $y, $z + 1, $l); } } } private function flowIntoBlock($x, $y, $z, $newFlowDecay){ if($this->level->getBlockIdAt($x, $y, $z) === Block::AIR){ $this->level->setBlockIdAt($x, $y, $z, Block::LAVA); $this->level->setBlockDataAt($x, $y, $z, $newFlowDecay); $this->level->updateBlockLight($x, $y, $z); $this->lavaSpread($x, $y, $z); } } private function canFlowInto($x, $y, $z){ $id = $this->level->getBlockIdAt($x, $y, $z); if($id === Block::AIR or $id === Block::LAVA or $id === Block::STILL_LAVA){ return true; } return false; } private function calculateFlowCost($xx, $yy, $zz, $accumulatedCost, $previousDirection){ $cost = 1000; for($j = 0; $j < 4; ++$j){ if( ($j === 0 and $previousDirection === 1) or ($j === 1 and $previousDirection === 0) or ($j === 2 and $previousDirection === 3) or ($j === 3 and $previousDirection === 2) ){ $x = $xx; $y = $yy; $z = $zz; if($j === 0){ --$x; }elseif($j === 1){ ++$x; }elseif($j === 2){ --$z; }elseif($j === 3){ ++$z; } if(!$this->canFlowInto($x, $y, $z)){ continue; }elseif($this->canFlowInto($x, $y, $z) and $this->level->getBlockDataAt($x, $y, $z) === 0){ continue; }elseif($this->canFlowInto($x, $y - 1, $z)){ return $accumulatedCost; } if($accumulatedCost >= 4){ continue; } $realCost = $this->calculateFlowCost($x, $y, $z, $accumulatedCost + 1, $j); if($realCost < $cost){ $cost = $realCost; } } } return $cost; } private function getOptimalFlowDirections($xx, $yy, $zz){ $flowCost = [0, 0, 0, 0]; $isOptimalFlowDirection = [0, 0, 0, 0]; for($j = 0; $j < 4; ++$j){ $flowCost[$j] = 1000; $x = $xx; $y = $yy; $z = $zz; if($j === 0){ --$x; }elseif($j === 1){ ++$x; }elseif($j === 2){ --$z; }elseif($j === 3){ ++$z; } if(!$this->canFlowInto($x, $y, $z)){ continue; }elseif($this->canFlowInto($x, $y, $z) and $this->level->getBlockDataAt($x, $y, $z) === 0){ continue; }elseif($this->canFlowInto($x, $y - 1, $z)){ $flowCost[$j] = 0; }else{ $flowCost[$j] = $this->calculateFlowCost($x, $y, $z, 1, $j); } } $minCost = $flowCost[0]; for($i = 1; $i < 4; ++$i){ if($flowCost[$i] < $minCost){ $minCost = $flowCost[$i]; } } for($i = 0; $i < 4; ++$i){ $isOptimalFlowDirection[$i] = ($flowCost[$i] === $minCost); } return $isOptimalFlowDirection; } private function getSmallestFlowDecay($x1, $y1, $z1, $x2, $y2, $z2, $decay){ $blockDecay = $this->getFlowDecay($x1, $y1, $z1, $x2, $y2, $z2); if($blockDecay < 0){ return $decay; }elseif($blockDecay === 0){ //Nothing to do! }elseif($blockDecay >= 8){ $blockDecay = 0; } return ($decay >= 0 && $blockDecay >= $decay) ? $decay : $blockDecay; } private function canNetherLavaStay($x, $y, $z){ $b = $this->level->getBlockIdAt($x, $y, $z); return $b === Block::AIR; } private function getHighestWorkableBlock($x, $z){ for($y = 127; $y >= 0; --$y){ $b = $this->level->getBlockIdAt($x, $y, $z); if($b == Block::AIR){ break; } } return $y === 0 ? -1 : $y; } } ================================================ FILE: src/pocketmine/level/generator/nether/populator/NetherOre.php ================================================ oreTypes as $type){ $ore = new ObjectOre($random, $type); for($i = 0; $i < $ore->type->clusterCount; ++$i){ $x = $random->nextRange($chunkX << 4, ($chunkX << 4) + 15); $y = $random->nextRange($ore->type->minHeight, $ore->type->maxHeight); $z = $random->nextRange($chunkZ << 4, ($chunkZ << 4) + 15); if($ore->canPlaceObject($level, $x, $y, $z)){ $ore->placeObject($level, $x, $y, $z); } } } } public function setOreTypes(array $types){ $this->oreTypes = $types; } } ================================================ FILE: src/pocketmine/level/generator/noise/Noise.php ================================================ = 0 ? (int) $x : (int) ($x - 1); } public static function fade($x){ return $x * $x * $x * ($x * ($x * 6 - 15) + 10); } public static function lerp($x, $y, $z){ return $y + $x * ($z - $y); } public static function linearLerp($x, $x1, $x2, $q0, $q1){ return (($x2 - $x) / ($x2 - $x1)) * $q0 + (($x - $x1) / ($x2 - $x1)) * $q1; } public static function bilinearLerp($x, $y, $q00, $q01, $q10, $q11, $x1, $x2, $y1, $y2){ $dx1 = (($x2 - $x) / ($x2 - $x1)); $dx2 = (($x - $x1) / ($x2 - $x1)); return (($y2 - $y) / ($y2 - $y1)) * ( $dx1 * $q00 + $dx2 * $q10 ) + (($y - $y1) / ($y2 - $y1)) * ( $dx1 * $q01 + $dx2 * $q11 ); } public static function trilinearLerp($x, $y, $z, $q000, $q001, $q010, $q011, $q100, $q101, $q110, $q111, $x1, $x2, $y1, $y2, $z1, $z2) { $dx1 = (($x2 - $x) / ($x2 - $x1)); $dx2 = (($x - $x1) / ($x2 - $x1)); $dy1 = (($y2 - $y) / ($y2 - $y1)); $dy2 = (($y - $y1) / ($y2 - $y1)); return (($z2 - $z) / ($z2 - $z1)) * ( $dy1 * ( $dx1 * $q000 + $dx2 * $q100 ) + $dy2 * ( $dx1 * $q001 + $dx2 * $q101 ) ) + (($z - $z1) / ($z2 - $z1)) * ( $dy1 * ( $dx1 * $q010 + $dx2 * $q110 ) + $dy2 * ( $dx1 * $q011 + $dx2 * $q111 ) ); } public static function grad($hash, $x, $y, $z){ $hash &= 15; $u = $hash < 8 ? $x : $y; $v = $hash < 4 ? $y : (($hash === 12 or $hash === 14) ? $x : $z); return (($hash & 1) === 0 ? $u : -$u) + (($hash & 2) === 0 ? $v : -$v); } abstract public function getNoise2D($x, $z); abstract public function getNoise3D($x, $y, $z); public function noise2D($x, $z, $normalized = false){ $result = 0; $amp = 1; $freq = 1; $max = 0; $x *= $this->expansion; $z *= $this->expansion; for($i = 0; $i < $this->octaves; ++$i){ $result += $this->getNoise2D($x * $freq, $z * $freq) * $amp; $max += $amp; $freq *= 2; $amp *= $this->persistence; } if($normalized === true){ $result /= $max; } return $result; } public function noise3D($x, $y, $z, $normalized = false){ $result = 0; $amp = 1; $freq = 1; $max = 0; $x *= $this->expansion; $y *= $this->expansion; $z *= $this->expansion; for($i = 0; $i < $this->octaves; ++$i){ $result += $this->getNoise3D($x * $freq, $y * $freq, $z * $freq) * $amp; $max += $amp; $freq *= 2; $amp *= $this->persistence; } if($normalized === true){ $result /= $max; } return $result; } public function setOffset($x, $y, $z){ $this->offsetX = $x; $this->offsetY = $y; $this->offsetZ = $z; } } ================================================ FILE: src/pocketmine/level/generator/noise/Perlin.php ================================================ octaves = $octaves; $this->persistence = $persistence; $this->expansion = $expansion; $this->offsetX = $random->nextFloat() * 256; $this->offsetY = $random->nextFloat() * 256; $this->offsetZ = $random->nextFloat() * 256; for($i = 0; $i < 512; ++$i){ $this->perm[$i] = 0; } for($i = 0; $i < 256; ++$i){ $this->perm[$i] = $random->nextBoundedInt(256); } for($i = 0; $i < 256; ++$i){ $pos = $random->nextBoundedInt(256 - $i) + $i; $old = $this->perm[$i]; $this->perm[$i] = $this->perm[$pos]; $this->perm[$pos] = $old; $this->perm[$i + 256] = $this->perm[$i]; } } public function getNoise3D($x, $y, $z){ $x += $this->offsetX; $y += $this->offsetY; $z += $this->offsetZ; $floorX = (int) $x; $floorY = (int) $y; $floorZ = (int) $z; $X = $floorX & 0xFF; $Y = $floorY & 0xFF; $Z = $floorZ & 0xFF; $x -= $floorX; $y -= $floorY; $z -= $floorZ; //Fade curves //$fX = self::fade($x); //$fY = self::fade($y); //$fZ = self::fade($z); $fX = $x * $x * $x * ($x * ($x * 6 - 15) + 10); $fY = $y * $y * $y * ($y * ($y * 6 - 15) + 10); $fZ = $z * $z * $z * ($z * ($z * 6 - 15) + 10); //Cube corners $A = $this->perm[$X] + $Y; $B = $this->perm[$X + 1] + $Y; $AA = $this->perm[$A] + $Z; $AB = $this->perm[$A + 1] + $Z; $BA = $this->perm[$B] + $Z; $BB = $this->perm[$B + 1] + $Z; $AA1 = self::grad($this->perm[$AA], $x, $y, $z); $BA1 = self::grad($this->perm[$BA], $x - 1, $y, $z); $AB1 = self::grad($this->perm[$AB], $x, $y - 1, $z); $BB1 = self::grad($this->perm[$BB], $x - 1, $y - 1, $z); $AA2 = self::grad($this->perm[$AA + 1], $x, $y, $z - 1); $BA2 = self::grad($this->perm[$BA + 1], $x - 1, $y, $z - 1); $AB2 = self::grad($this->perm[$AB + 1], $x, $y - 1, $z - 1); $BB2 = self::grad($this->perm[$BB + 1], $x - 1, $y - 1, $z - 1); $xLerp11 = $AA1 + $fX * ($BA1 - $AA1); $zLerp1 = $xLerp11 + $fY * ($AB1 + $fX * ($BB1 - $AB1) - $xLerp11); $xLerp21 = $AA2 + $fX * ($BA2 - $AA2); return $zLerp1 + $fZ * ($xLerp21 + $fY * ($AB2 + $fX * ($BB2 - $AB2) - $xLerp21) - $zLerp1); /* return self::lerp( $fZ, self::lerp( $fY, self::lerp( $fX, self::grad($this->perm[$AA], $x, $y, $z), self::grad($this->perm[$BA], $x - 1, $y, $z) ), self::lerp( $fX, self::grad($this->perm[$AB], $x, $y - 1, $z), self::grad($this->perm[$BB], $x - 1, $y - 1, $z) ) ), self::lerp( $fY, self::lerp( $fX, self::grad($this->perm[$AA + 1], $x, $y, $z - 1), self::grad($this->perm[$BA + 1], $x - 1, $y, $z - 1) ), self::lerp( $fX, self::grad($this->perm[$AB + 1], $x, $y - 1, $z - 1), self::grad($this->perm[$BB + 1], $x - 1, $y - 1, $z - 1) ) ) ); */ } public function getNoise2D($x, $y){ return $this->getNoise3D($x, $y, 0); } } ================================================ FILE: src/pocketmine/level/generator/noise/Simplex.php ================================================ offsetW = $random->nextFloat() * 256; self::$SQRT_3 = sqrt(3); self::$SQRT_5 = sqrt(5); self::$F2 = 0.5 * (self::$SQRT_3 - 1); self::$G2 = (3 - self::$SQRT_3) / 6; self::$G22 = self::$G2 * 2.0 - 1; self::$F3 = 1.0 / 3.0; self::$G3 = 1.0 / 6.0; self::$F4 = (self::$SQRT_5 - 1.0) / 4.0; self::$G4 = (5.0 - self::$SQRT_5) / 20.0; self::$G42 = self::$G4 * 2.0; self::$G43 = self::$G4 * 3.0; self::$G44 = self::$G4 * 4.0 - 1.0; } protected static function dot2D($g, $x, $y){ return $g[0] * $x + $g[1] * $y; } protected static function dot3D($g, $x, $y, $z){ return $g[0] * $x + $g[1] * $y + $g[2] * $z; } protected static function dot4D($g, $x, $y, $z, $w){ return $g[0] * $x + $g[1] * $y + $g[2] * $z + $g[3] * $w; } public function getNoise3D($x, $y, $z){ $x += $this->offsetX; $y += $this->offsetY; $z += $this->offsetZ; // Skew the input space to determine which simplex cell we're in $s = ($x + $y + $z) * self::$F3; // Very nice and simple skew factor for 3D $i = (int) ($x + $s); $j = (int) ($y + $s); $k = (int) ($z + $s); $t = ($i + $j + $k) * self::$G3; // Unskew the cell origin back to (x,y,z) space $x0 = $x - ($i - $t); // The x,y,z distances from the cell origin $y0 = $y - ($j - $t); $z0 = $z - ($k - $t); // For the 3D case, the simplex shape is a slightly irregular tetrahedron. // Determine which simplex we are in. if($x0 >= $y0){ if($y0 >= $z0){ $i1 = 1; $j1 = 0; $k1 = 0; $i2 = 1; $j2 = 1; $k2 = 0; } // X Y Z order elseif($x0 >= $z0){ $i1 = 1; $j1 = 0; $k1 = 0; $i2 = 1; $j2 = 0; $k2 = 1; } // X Z Y order else{ $i1 = 0; $j1 = 0; $k1 = 1; $i2 = 1; $j2 = 0; $k2 = 1; } // Z X Y order }else{ // x0 0){ $gi0 = self::$grad3[$this->perm[$ii + $this->perm[$jj + $this->perm[$kk]]] % 12]; $n += $t0 * $t0 * $t0 * $t0 * ($gi0[0] * $x0 + $gi0[1] * $y0 + $gi0[2] * $z0); } $t1 = 0.6 - $x1 * $x1 - $y1 * $y1 - $z1 * $z1; if($t1 > 0){ $gi1 = self::$grad3[$this->perm[$ii + $i1 + $this->perm[$jj + $j1 + $this->perm[$kk + $k1]]] % 12]; $n += $t1 * $t1 * $t1 * $t1 * ($gi1[0] * $x1 + $gi1[1] * $y1 + $gi1[2] * $z1); } $t2 = 0.6 - $x2 * $x2 - $y2 * $y2 - $z2 * $z2; if($t2 > 0){ $gi2 = self::$grad3[$this->perm[$ii + $i2 + $this->perm[$jj + $j2 + $this->perm[$kk + $k2]]] % 12]; $n += $t2 * $t2 * $t2 * $t2 * ($gi2[0] * $x2 + $gi2[1] * $y2 + $gi2[2] * $z2); } $t3 = 0.6 - $x3 * $x3 - $y3 * $y3 - $z3 * $z3; if($t3 > 0){ $gi3 = self::$grad3[$this->perm[$ii + 1 + $this->perm[$jj + 1 + $this->perm[$kk + 1]]] % 12]; $n += $t3 * $t3 * $t3 * $t3 * ($gi3[0] * $x3 + $gi3[1] * $y3 + $gi3[2] * $z3); } // Add contributions from each corner to get the noise value. // The result is scaled to stay just inside [-1,1] return 32.0 * $n; } public function getNoise2D($x, $y){ $x += $this->offsetX; $y += $this->offsetY; // Skew the input space to determine which simplex cell we're in $s = ($x + $y) * self::$F2; // Hairy factor for 2D $i = (int) ($x + $s); $j = (int) ($y + $s); $t = ($i + $j) * self::$G2; // Unskew the cell origin back to (x,y) space $x0 = $x - ($i - $t); // The x,y distances from the cell origin $y0 = $y - ($j - $t); // For the 2D case, the simplex shape is an equilateral triangle. // Determine which simplex we are in. if($x0 > $y0){ $i1 = 1; $j1 = 0; } // lower triangle, XY order: (0,0)->(1,0)->(1,1) else{ $i1 = 0; $j1 = 1; } // upper triangle, YX order: (0,0)->(0,1)->(1,1) // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where // c = (3-sqrt(3))/6 $x1 = $x0 - $i1 + self::$G2; // Offsets for middle corner in (x,y) unskewed coords $y1 = $y0 - $j1 + self::$G2; $x2 = $x0 + self::$G22; // Offsets for last corner in (x,y) unskewed coords $y2 = $y0 + self::$G22; // Work out the hashed gradient indices of the three simplex corners $ii = $i & 255; $jj = $j & 255; $n = 0; // Calculate the contribution from the three corners $t0 = 0.5 - $x0 * $x0 - $y0 * $y0; if($t0 > 0){ $gi0 = self::$grad3[$this->perm[$ii + $this->perm[$jj]] % 12]; $n += $t0 * $t0 * $t0 * $t0 * ($gi0[0] * $x0 + $gi0[1] * $y0); // (x,y) of grad3 used for 2D gradient } $t1 = 0.5 - $x1 * $x1 - $y1 * $y1; if($t1 > 0){ $gi1 = self::$grad3[$this->perm[$ii + $i1 + $this->perm[$jj + $j1]] % 12]; $n += $t1 * $t1 * $t1 * $t1 * ($gi1[0] * $x1 + $gi1[1] * $y1); } $t2 = 0.5 - $x2 * $x2 - $y2 * $y2; if($t2 > 0){ $gi2 = self::$grad3[$this->perm[$ii + 1 + $this->perm[$jj + 1]] % 12]; $n += $t2 * $t2 * $t2 * $t2 * ($gi2[0] * $x2 + $gi2[1] * $y2); } // Add contributions from each corner to get the noise value. // The result is scaled to return values in the interval [-1,1]. return 70.0 * $n; } /** * Computes and returns the 4D simplex noise for the given coordinates in * 4D space * * @param float $x X coordinate * @param float $y Y coordinate * @param float $z Z coordinate * @param float $w W coordinate * * @return float Noise at given location, from range -1 to 1 */ /*public function getNoise4D($x, $y, $z, $w){ x += offsetX; y += offsetY; z += offsetZ; w += offsetW; n0, n1, n2, n3, n4; // Noise contributions from the five corners // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in s = (x + y + z + w) * self::$F4; // Factor for 4D skewing i = floor(x + s); j = floor(y + s); k = floor(z + s); l = floor(w + s); t = (i + j + k + l) * self::$G4; // Factor for 4D unskewing X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space Y0 = j - t; Z0 = k - t; W0 = l - t; x0 = x - X0; // The x,y,z,w distances from the cell origin y0 = y - Y0; z0 = z - Z0; w0 = w - W0; // For the 4D case, the simplex is a 4D shape I won't even try to describe. // To find out which of the 24 possible simplices we're in, we need to // determine the magnitude ordering of x0, y0, z0 and w0. // The method below is a good way of finding the ordering of x,y,z,w and // then find the correct traversal order for the simplex we’re in. // First, six pair-wise comparisons are performed between each possible pair // of the four coordinates, and the results are used to add up binary bits // for an integer index. c1 = (x0 > y0) ? 32 : 0; c2 = (x0 > z0) ? 16 : 0; c3 = (y0 > z0) ? 8 : 0; c4 = (x0 > w0) ? 4 : 0; c5 = (y0 > w0) ? 2 : 0; c6 = (z0 > w0) ? 1 : 0; c = c1 + c2 + c3 + c4 + c5 + c6; i1, j1, k1, l1; // The integer offsets for the second simplex corner i2, j2, k2, l2; // The integer offsets for the third simplex corner i3, j3, k3, l3; // The integer offsets for the fourth simplex corner // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0; j1 = simplex[c][1] >= 3 ? 1 : 0; k1 = simplex[c][2] >= 3 ? 1 : 0; l1 = simplex[c][3] >= 3 ? 1 : 0; // The number 2 in the "simplex" array is at the second largest coordinate. i2 = simplex[c][0] >= 2 ? 1 : 0; j2 = simplex[c][1] >= 2 ? 1 : 0; k2 = simplex[c][2] >= 2 ? 1 : 0; l2 = simplex[c][3] >= 2 ? 1 : 0; // The number 1 in the "simplex" array is at the second smallest coordinate. i3 = simplex[c][0] >= 1 ? 1 : 0; j3 = simplex[c][1] >= 1 ? 1 : 0; k3 = simplex[c][2] >= 1 ? 1 : 0; l3 = simplex[c][3] >= 1 ? 1 : 0; // The fifth corner has all coordinate offsets = 1, so no need to look that up. x1 = x0 - i1 + self::$G4; // Offsets for second corner in (x,y,z,w) coords y1 = y0 - j1 + self::$G4; z1 = z0 - k1 + self::$G4; w1 = w0 - l1 + self::$G4; x2 = x0 - i2 + self::$G42; // Offsets for third corner in (x,y,z,w) coords y2 = y0 - j2 + self::$G42; z2 = z0 - k2 + self::$G42; w2 = w0 - l2 + self::$G42; x3 = x0 - i3 + self::$G43; // Offsets for fourth corner in (x,y,z,w) coords y3 = y0 - j3 + self::$G43; z3 = z0 - k3 + self::$G43; w3 = w0 - l3 + self::$G43; x4 = x0 + self::$G44; // Offsets for last corner in (x,y,z,w) coords y4 = y0 + self::$G44; z4 = z0 + self::$G44; w4 = w0 + self::$G44; // Work out the hashed gradient indices of the five simplex corners ii = i & 255; jj = j & 255; kk = k & 255; ll = l & 255; gi0 = $this->perm[ii + $this->perm[jj + $this->perm[kk + $this->perm[ll]]]] % 32; gi1 = $this->perm[ii + i1 + $this->perm[jj + j1 + $this->perm[kk + k1 + $this->perm[ll + l1]]]] % 32; gi2 = $this->perm[ii + i2 + $this->perm[jj + j2 + $this->perm[kk + k2 + $this->perm[ll + l2]]]] % 32; gi3 = $this->perm[ii + i3 + $this->perm[jj + j3 + $this->perm[kk + k3 + $this->perm[ll + l3]]]] % 32; gi4 = $this->perm[ii + 1 + $this->perm[jj + 1 + $this->perm[kk + 1 + $this->perm[ll + 1]]]] % 32; // Calculate the contribution from the five corners t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0; if(t0 < 0){ n0 = 0.0; }else{ t0 *= t0; n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0); } t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1; if(t1 < 0){ n1 = 0.0; }else{ t1 *= t1; n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1); } t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2; if(t2 < 0){ n2 = 0.0; }else{ t2 *= t2; n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2); } t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3; if(t3 < 0){ n3 = 0.0; }else{ t3 *= t3; n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3); } t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4; if(t4 < 0){ n4 = 0.0; }else{ t4 *= t4; n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4); } // Sum up and scale the result to cover the range [-1,1] return 27.0 * (n0 + n1 + n2 + n3 + n4); }*/ } ================================================ FILE: src/pocketmine/level/generator/normal/Normal.php ================================================ level->getSeed(); $hash *= $hash + 223; $xNoise = $hash >> 20 & 3; $zNoise = $hash >> 22 & 3; if($xNoise == 3){ $xNoise = 1; } if($zNoise == 3){ $zNoise = 1; } return $this->selector->pickBiome($x + $xNoise - 1, $z + $zNoise - 1); } public function init(ChunkManager $level, Random $random){ $this->level = $level; $this->random = $random; $this->random->setSeed($this->level->getSeed()); $this->noiseBase = new Simplex($this->random, 4, 1 / 4, 1 / 32); $this->random->setSeed($this->level->getSeed()); $this->selector = new BiomeSelector($this->random, Biome::getBiome(Biome::OCEAN)); $this->selector->addBiome(Biome::getBiome(Biome::OCEAN)); $this->selector->addBiome(Biome::getBiome(Biome::PLAINS)); $this->selector->addBiome(Biome::getBiome(Biome::DESERT)); $this->selector->addBiome(Biome::getBiome(Biome::MOUNTAINS)); $this->selector->addBiome(Biome::getBiome(Biome::FOREST)); $this->selector->addBiome(Biome::getBiome(Biome::TAIGA)); $this->selector->addBiome(Biome::getBiome(Biome::SWAMP)); $this->selector->addBiome(Biome::getBiome(Biome::RIVER)); $this->selector->addBiome(Biome::getBiome(Biome::ICE_PLAINS)); $this->selector->addBiome(Biome::getBiome(Biome::SMALL_MOUNTAINS)); $this->selector->addBiome(Biome::getBiome(Biome::BIRCH_FOREST)); $this->selector->addBiome(Biome::getBiome(Biome::BEACH)); $this->selector->addBiome(Biome::getBiome(Biome::MESA)); $this->selector->recalculate(); $cover = new GroundCover(); $this->generationPopulators[] = $cover; $ores = new Ore(); $ores->setOreTypes([ new OreType(new CoalOre(), 20, 17, 0, 128), new OreType(new IronOre(), 20, 9, 0, 64), new OreType(new RedstoneOre(), 8, 8, 0, 16), new OreType(new LapisOre(), 1, 7, 0, 16), new OreType(new GoldOre(), 2, 9, 0, 32), new OreType(new DiamondOre(), 1, 8, 0, 16), new OreType(new Dirt(), 10, 33, 0, 128), new OreType(new Gravel(), 8, 33, 0, 128), new OreType(new Stone(Stone::GRANITE), 10, 33, 0, 80), new OreType(new Stone(Stone::DIORITE), 10, 33, 0, 80), new OreType(new Stone(Stone::ANDESITE), 10, 33, 0, 80) ]); $this->populators[] = $ores; } public function generateChunk($chunkX, $chunkZ){ $this->random->setSeed(0xdeadbeef ^ $chunkX ^ $chunkZ ^ $this->level->getSeed()); $noise = Generator::getFastNoise3D($this->noiseBase, 16, 128, 16, 4, 8, 4, $chunkX * 16, 0, $chunkZ * 16); $chunk = $this->level->getChunk($chunkX, $chunkZ); $biomeCache = []; for($x = 0; $x < 16; ++$x){ for($z = 0; $z < 16; ++$z){ $minSum = 0; $maxSum = 0; $weightSum = 0; $biome = $this->pickBiome($chunkX * 16 + $x, $chunkZ * 16 + $z); $chunk->setBiomeId($x, $z, $biome->getId()); for($sx = -self::$SMOOTH_SIZE; $sx <= self::$SMOOTH_SIZE; ++$sx){ for($sz = -self::$SMOOTH_SIZE; $sz <= self::$SMOOTH_SIZE; ++$sz){ $weight = self::$GAUSSIAN_KERNEL[$sx + self::$SMOOTH_SIZE][$sz + self::$SMOOTH_SIZE]; if($sx === 0 and $sz === 0){ $adjacent = $biome; }else{ $index = Level::chunkHash($chunkX * 16 + $x + $sx, $chunkZ * 16 + $z + $sz); if(isset($biomeCache[$index])){ $adjacent = $biomeCache[$index]; }else{ $biomeCache[$index] = $adjacent = $this->pickBiome($chunkX * 16 + $x + $sx, $chunkZ * 16 + $z + $sz); } } $minSum += ($adjacent->getMinElevation() - 1) * $weight; $maxSum += $adjacent->getMaxElevation() * $weight; $weightSum += $weight; } } $minSum /= $weightSum; $maxSum /= $weightSum; $smoothHeight = ($maxSum - $minSum) / 2; for($y = 0; $y < 128; ++$y){ if($y === 0){ $chunk->setBlockId($x, $y, $z, Block::BEDROCK); continue; } $noiseValue = $noise[$x][$z][$y] - 1 / $smoothHeight * ($y - $smoothHeight - $minSum); if($noiseValue > 0){ $chunk->setBlockId($x, $y, $z, Block::STONE); }elseif($y <= $this->waterHeight){ $chunk->setBlockId($x, $y, $z, Block::STILL_WATER); } } } } foreach($this->generationPopulators as $populator){ $populator->populate($this->level, $chunkX, $chunkZ, $this->random); } } public function populateChunk($chunkX, $chunkZ){ $this->random->setSeed(0xdeadbeef ^ ($chunkX << 16) ^ ($chunkZ << 16) ^ $this->level->getSeed()); foreach($this->populators as $populator){ $populator->populate($this->level, ($chunkX << 16), ($chunkZ << 16), $this->random); } $chunk = $this->level->getChunk($chunkX, $chunkZ); $biome = Biome::getBiome($chunk->getBiomeId(7, 7)); // This is incorrect. Here need add one mt_rand with all biomes and delete temperature & rainfall method. $biome->populateChunk($this->level, $chunkX, $chunkZ, $this->random); } public function getSpawn(){ return new Vector3(127.5, 128, 127.5); } } ================================================ FILE: src/pocketmine/level/generator/normal/Normal2.php ================================================ level->getSeed(); $hash *= $hash + 223; $xNoise = $hash >> 20 & 3; $zNoise = $hash >> 22 & 3; if($xNoise == 3){ $xNoise = 1; } if($zNoise == 3){ $zNoise = 1; } return $this->selector->pickBiome($x + $xNoise - 1, $z + $zNoise - 1); } public function init(ChunkManager $level, Random $random){ $this->level = $level; $this->random = $random; $this->random->setSeed($this->level->getSeed()); $this->noiseSeaFloor = new Simplex($this->random, 1, 1 / 8, 1 / 64); $this->noiseLand = new Simplex($this->random, 2, 1 / 8, 1 / 512); $this->noiseMountains = new Simplex($this->random, 4, 1, 1 / 500); $this->noiseBaseGround = new Simplex($this->random, 4, 1 / 4, 1 / 64); $this->noiseRiver = new Simplex($this->random, 2, 1, 1 / 512); $this->random->setSeed($this->level->getSeed()); $this->selector = new BiomeSelector($this->random, Biome::getBiome(Biome::OCEAN)); $this->heightOffset = $random->nextRange(-5, 3); $this->selector->addBiome(Biome::getBiome(Biome::OCEAN)); $this->selector->addBiome(Biome::getBiome(Biome::PLAINS)); $this->selector->addBiome(Biome::getBiome(Biome::DESERT)); $this->selector->addBiome(Biome::getBiome(Biome::MOUNTAINS)); $this->selector->addBiome(Biome::getBiome(Biome::FOREST)); $this->selector->addBiome(Biome::getBiome(Biome::TAIGA)); $this->selector->addBiome(Biome::getBiome(Biome::SWAMP)); $this->selector->addBiome(Biome::getBiome(Biome::RIVER)); $this->selector->addBiome(Biome::getBiome(Biome::ICE_PLAINS)); $this->selector->addBiome(Biome::getBiome(Biome::SMALL_MOUNTAINS)); $this->selector->addBiome(Biome::getBiome(Biome::BIRCH_FOREST)); $this->selector->addBiome(Biome::getBiome(Biome::BEACH)); $this->selector->addBiome(Biome::getBiome(Biome::MESA)); $this->selector->recalculate(); $cover = new GroundCover(); $this->generationPopulators[] = $cover; $cave = new Cave(); $this->populators[] = $cave; $ores = new Ore(); $ores->setOreTypes([ new OreType(new CoalOre(), 20, 17, 0, 128), new OreType(new IronOre(), 20, 9, 0, 64), new OreType(new RedstoneOre(), 8, 8, 0, 16), new OreType(new LapisOre(), 1, 7, 0, 16), new OreType(new GoldOre(), 2, 9, 0, 32), new OreType(new DiamondOre(), 1, 8, 0, 16), new OreType(new Dirt(), 10, 33, 0, 128), new OreType(new Gravel(), 8, 33, 0, 128), new OreType(new Stone(Stone::GRANITE), 10, 33, 0, 80), new OreType(new Stone(Stone::DIORITE), 10, 33, 0, 80), new OreType(new Stone(Stone::ANDESITE), 10, 33, 0, 80) ]); $this->populators[] = $ores; } public function generateChunk($chunkX, $chunkZ){ $this->random->setSeed(0xdeadbeef ^ $chunkX ^ $chunkZ ^ $this->level->getSeed()); $seaFloorNoise = Generator::getFastNoise2D($this->noiseSeaFloor, 16, 16, 4, $chunkX * 16, 0, $chunkZ * 16); $landNoise = Generator::getFastNoise2D($this->noiseLand, 16, 16, 4, $chunkX * 16, 0, $chunkZ * 16); $mountainNoise = Generator::getFastNoise2D($this->noiseMountains, 16, 16, 4, $chunkX * 16, 0, $chunkZ * 16); $baseNoise = Generator::getFastNoise2D($this->noiseBaseGround, 16, 16, 4, $chunkX * 16, 0, $chunkZ * 16); $riverNoise = Generator::getFastNoise2D($this->noiseRiver, 16, 16, 4, $chunkX * 16, 0, $chunkZ * 16); $chunk = $this->level->getChunk($chunkX, $chunkZ); for($genx = 0; $genx < 16; $genx++){ for($genz = 0; $genz < 16; $genz++){ $canBaseGround = false; $canRiver = true; //using a quadratic function which smooth the world //y = (2.956x)^2 - 0.6, (0 <= x <= 2) $landHeightNoise = $landNoise[$genx][$genz] + 1; $landHeightNoise *= 2.956; $landHeightNoise = $landHeightNoise * $landHeightNoise; $landHeightNoise = $landHeightNoise - 0.6; $landHeightNoise = $landHeightNoise > 0 ? $landHeightNoise : 0; //generate mountains $mountainHeightGenerate = $mountainNoise[$genx][$genz] - 0.2; $mountainHeightGenerate = $mountainHeightGenerate > 0 ? $mountainHeightGenerate : 0; $mountainGenerate = (int) ($this->mountainHeight * $mountainHeightGenerate); $landHeightGenerate = (int) ($this->landHeightRange * $landHeightNoise); if($landHeightGenerate > $this->landHeightRange){ if($landHeightGenerate > $this->landHeightRange){ $canBaseGround = true; } $landHeightGenerate = $this->landHeightRange; } $genyHeight = $this->seaFloorHeight + $landHeightGenerate; $genyHeight += $mountainGenerate; //prepare for generate ocean, desert, and land if($genyHeight < $this->beathStartHeight){ if($genyHeight < $this->beathStartHeight - 5){ $genyHeight += (int) ($this->seaFloorGenerateRange * $seaFloorNoise[$genx][$genz]); } $biome = Biome::getBiome(Biome::OCEAN); if($genyHeight < $this->seaFloorHeight - $this->seaFloorGenerateRange){ $genyHeight = $this->seaFloorHeight; } $canRiver = false; }else if($genyHeight <= $this->beathStopHeight && $genyHeight >= $this->beathStartHeight){ $biome = Biome::getBiome(Biome::BEACH); }else{ $biome = $this->pickBiome($chunkX * 16 + $genx, $chunkZ * 16 + $genz); if($canBaseGround){ $baseGroundHeight = (int) ($this->landHeightRange * $landHeightNoise) - $this->landHeightRange; $baseGroundHeight2 = (int) ($this->basegroundHeight * ($baseNoise[$genx][$genz] + 1)); if($baseGroundHeight2 > $baseGroundHeight) $baseGroundHeight2 = $baseGroundHeight; if($baseGroundHeight2 > $mountainGenerate) $baseGroundHeight2 = $baseGroundHeight2 - $mountainGenerate; else $baseGroundHeight2 = 0; $genyHeight += $baseGroundHeight2; } } if($canRiver && $genyHeight <= $this->seaHeight - 5){ $canRiver = false; } //generate river if($canRiver){ $riverGenerate = $riverNoise[$genx][$genz]; if($riverGenerate > -0.25 && $riverGenerate < 0.25){ $riverGenerate = $riverGenerate > 0 ? $riverGenerate : -$riverGenerate; $riverGenerate = 0.25 - $riverGenerate; //y=x^2 * 4 - 0.0000001 $riverGenerate = $riverGenerate * $riverGenerate * 4; //smooth again $riverGenerate = $riverGenerate - 0.0000001; $riverGenerate = $riverGenerate > 0 ? $riverGenerate : 0; $genyHeight -= $riverGenerate * 64; if($genyHeight < $this->seaHeight){ $biome = Biome::getBiome(Biome::RIVER); //to generate river floor if($genyHeight <= $this->seaHeight - 8){ $genyHeight1 = $this->seaHeight - 9 + (int) ($this->basegroundHeight * ($baseNoise[$genx][$genz] + 1)); $genyHeight2 = $genyHeight < $this->seaHeight - 7 ? $this->seaHeight - 7 : $genyHeight; $genyHeight = $genyHeight1 > $genyHeight2 ? $genyHeight1 : $genyHeight2; } } } } $chunk->setBiomeId($genx, $genz, $biome->getId()); //generating $generateHeight = $genyHeight > $this->seaHeight ? $genyHeight : $this->seaHeight; for($geny = 0; $geny <= $generateHeight; $geny++){ if($geny <= $this->bedrockDepth && ($geny == 0 or $this->random->nextRange(1, 5) == 1)){ $chunk->setBlockId($genx, $geny, $genz, Block::BEDROCK); }elseif($geny > $genyHeight){ if(($biome->getId() == Biome::ICE_PLAINS or $biome->getId() == Biome::TAIGA) and $geny == $this->seaHeight){ $chunk->setBlockId($genx, $geny, $genz, Block::ICE); }else{ $chunk->setBlockId($genx, $geny, $genz, Block::STILL_WATER); } }else{ $chunk->setBlockId($genx, $geny, $genz, Block::STONE); } } } } //populator chunk foreach($this->generationPopulators as $populator){ $populator->populate($this->level, ($chunkX << 16), ($chunkZ << 16), $this->random); } } public function populateChunk($chunkX, $chunkZ){ $this->random->setSeed(0xdeadbeef ^ $chunkX ^ $chunkZ ^ $this->level->getSeed()); foreach($this->populators as $populator){ $populator->populate($this->level, $chunkX, $chunkZ, $this->random); } $chunk = $this->level->getChunk($chunkX, $chunkZ); $biome = Biome::getBiome($chunk->getBiomeId(7, 7)); // same as Normal Generator. $biome->populateChunk($this->level, $chunkX, $chunkZ, $this->random); } public function getSpawn(){ return new Vector3(127.5, 128, 127.5); } } ================================================ FILE: src/pocketmine/level/generator/normal/biome/BeachBiome.php ================================================ removePopulator(Cactus::class); $this->removePopulator(DeadBush::class); $this->setElevation(62, 65); } public function getName() : string{ return "Beach"; } } ================================================ FILE: src/pocketmine/level/generator/normal/biome/DesertBiome.php ================================================ setBaseAmount(1); $deadBush->setRandomAmount(4); $sugarCane = new SugarCane(); $sugarCane->setRandomAmount(20); $sugarCane->setBaseAmount(3); $mushroom = new Mushroom(); $this->addPopulator($mushroom); $this->addPopulator($deadBush); $this->addPopulator($sugarCane); $this->setElevation(63, 74); $this->temperature = 2; $this->rainfall = 0; $this->setGroundCover([ Block::get(Block::SAND, 0), Block::get(Block::SAND, 0), Block::get(Block::SAND, 0), Block::get(Block::SAND, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), ]); } public function getName() : string{ return "Desert"; } } ================================================ FILE: src/pocketmine/level/generator/normal/biome/ForestBiome.php ================================================ type = $type; $trees = new Tree($type === self::TYPE_BIRCH ? Sapling::BIRCH : Sapling::OAK); $trees->setBaseAmount(5); $this->addPopulator($trees); $tallGrass = new TallGrass(); $tallGrass->setBaseAmount(3); $this->addPopulator($tallGrass); $mushroom = new Mushroom(); $this->addPopulator($mushroom); $this->setElevation(63, 81); if($type === self::TYPE_BIRCH){ $this->temperature = 0.5; $this->rainfall = 0.5; }else{ $this->temperature = 0.7; $this->temperature = 0.8; } } public function getName() : string{ return $this->type === self::TYPE_BIRCH ? "Birch Forest" : "Forest"; } } ================================================ FILE: src/pocketmine/level/generator/normal/biome/GrassyBiome.php ================================================ setGroundCover([ Block::get(Block::GRASS, 0), Block::get(Block::DIRT, 0), Block::get(Block::DIRT, 0), Block::get(Block::DIRT, 0), Block::get(Block::DIRT, 0), ]); } } ================================================ FILE: src/pocketmine/level/generator/normal/biome/IcePlainsBiome.php ================================================ setBaseAmount(5); $this->addPopulator($tallGrass); $this->setElevation(63, 74); $this->temperature = 0.05; $this->rainfall = 0.8; } public function getName() : string{ return "Ice Plains"; } } ================================================ FILE: src/pocketmine/level/generator/normal/biome/MesaBiome.php ================================================ setBaseAmount(0); $cactus->setRandomAmount(5); $deadBush = new DeadBush(); $cactus->setBaseAmount(2); $deadBush->setRandomAmount(10); $this->addPopulator($cactus); $this->addPopulator($deadBush); $this->setElevation(63, 81); $this->temperature = 2.0; $this->rainfall = 0.8; $this->setGroundCover([ Block::get(Block::HARDENED_CLAY, 0), Block::get(Block::STAINED_CLAY, StainedClay::CLAY_PINK), Block::get(Block::HARDENED_CLAY, 0), Block::get(Block::STAINED_CLAY, StainedClay::CLAY_ORANGE), Block::get(Block::STAINED_CLAY, StainedClay::CLAY_BLACK), Block::get(Block::STAINED_CLAY, StainedClay::CLAY_GRAY), Block::get(Block::STAINED_CLAY, StainedClay::CLAY_WHITE), Block::get(Block::STAINED_CLAY, StainedClay::CLAY_ORANGE), Block::get(Block::HARDENED_CLAY, 0), Block::get(Block::HARDENED_CLAY, 0), Block::get(Block::HARDENED_CLAY, 0), Block::get(Block::HARDENED_CLAY, 0), Block::get(Block::STAINED_CLAY, StainedClay::CLAY_YELLOW), Block::get(Block::STAINED_CLAY, StainedClay::CLAY_BLACK), Block::get(Block::STAINED_CLAY, StainedClay::CLAY_PINK), Block::get(Block::STAINED_CLAY, StainedClay::CLAY_PINK), Block::get(Block::RED_SANDSTONE, 0), Block::get(Block::STAINED_CLAY, StainedClay::CLAY_WHITE), Block::get(Block::RED_SANDSTONE, 0), Block::get(Block::RED_SANDSTONE, 0), Block::get(Block::RED_SANDSTONE, 0), Block::get(Block::RED_SANDSTONE, 0), Block::get(Block::RED_SANDSTONE, 0), Block::get(Block::RED_SANDSTONE, 0), Block::get(Block::RED_SANDSTONE, 0), Block::get(Block::RED_SANDSTONE, 0), ]); } public function getName() : string{ return "Mesa"; } } ================================================ FILE: src/pocketmine/level/generator/normal/biome/MountainsBiome.php ================================================ setBaseAmount(1); $this->addPopulator($trees); $tallGrass = new TallGrass(); $tallGrass->setBaseAmount(6); $this->addPopulator($tallGrass); //TODO: add emerald $this->setElevation(63, 127); $this->temperature = 0.4; $this->rainfall = 0.5; } public function getName() : string{ return "Mountains"; } } ================================================ FILE: src/pocketmine/level/generator/normal/biome/NormalBiome.php ================================================ setBaseAmount(6); $tallGrass = new TallGrass(); $tallGrass->setBaseAmount(5); $mushroom = new Mushroom(); $this->addPopulator($mushroom); $this->addPopulator($sugarcane); $this->addPopulator($tallGrass); $this->setElevation(46, 68); $this->temperature = 0.5; $this->rainfall = 0.5; } public function getName() : string{ return "Ocean"; } } ================================================ FILE: src/pocketmine/level/generator/normal/biome/PlainBiome.php ================================================ setBaseAmount(6); $tallGrass = new TallGrass(); $tallGrass->setBaseAmount(25); $waterPit = new WaterPit(); $waterPit->setBaseAmount(9999); $lilyPad = new LilyPad(); $lilyPad->setBaseAmount(8); $mushroom = new Mushroom(); $flower = new Flower(); $flower->setBaseAmount(2); $flower->addType([Block::DANDELION, 0]); $flower->addType([Block::RED_FLOWER, FlowerBlock::TYPE_POPPY]); $flower->addType([Block::RED_FLOWER, FlowerBlock::TYPE_AZURE_BLUET]); $flower->addType([Block::RED_FLOWER, FlowerBlock::TYPE_RED_TULIP]); $flower->addType([Block::RED_FLOWER, FlowerBlock::TYPE_ORANGE_TULIP]); $flower->addType([Block::RED_FLOWER, FlowerBlock::TYPE_WHITE_TULIP]); $flower->addType([Block::RED_FLOWER, FlowerBlock::TYPE_PINK_TULIP]); $flower->addType([Block::RED_FLOWER, FlowerBlock::TYPE_OXEYE_DAISY]); $this->addPopulator($mushroom); $this->addPopulator($sugarcane); $this->addPopulator($tallGrass); $this->addPopulator($flower); $this->addPopulator($waterPit); $this->addPopulator($lilyPad); $this->setElevation(61, 68); $this->temperature = 0.8; $this->rainfall = 0.4; } public function getName() : string{ return "Plains"; } } ================================================ FILE: src/pocketmine/level/generator/normal/biome/RiverBiome.php ================================================ setBaseAmount(6); $tallGrass = new TallGrass(); $tallGrass->setBaseAmount(5); $mushroom = new Mushroom(); $this->addPopulator($mushroom); $this->addPopulator($sugarcane); $this->addPopulator($tallGrass); $this->setElevation(58, 62); $this->temperature = 0.5; $this->rainfall = 0.7; } public function getName() : string{ return "River"; } } ================================================ FILE: src/pocketmine/level/generator/normal/biome/SandyBiome.php ================================================ setBaseAmount(2); $cactus->setRandomAmount(1); $deadBush = new DeadBush(); $deadBush->setBaseAmount(2); $this->addPopulator($cactus); $this->addPopulator($deadBush); $this->setElevation(63, 81); $this->temperature = 0.05; $this->rainfall = 0.8; $this->setGroundCover([ Block::get(Block::SAND, 0), Block::get(Block::SAND, 0), Block::get(Block::SAND, 0), Block::get(Block::SAND, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), Block::get(Block::SANDSTONE, 0), ]); } public function getName() : string{ return "Sandy"; } } ================================================ FILE: src/pocketmine/level/generator/normal/biome/SmallMountainsBiome.php ================================================ setElevation(63, 97); } public function getName() : string{ return "Small Mountains"; } } ================================================ FILE: src/pocketmine/level/generator/normal/biome/SnowyBiome.php ================================================ setGroundCover([ Block::get(Block::SNOW_LAYER, 0), Block::get(Block::GRASS, 0), Block::get(Block::DIRT, 0), Block::get(Block::DIRT, 0), Block::get(Block::DIRT, 0), ]); } } ================================================ FILE: src/pocketmine/level/generator/normal/biome/SwampBiome.php ================================================ setBaseAmount(8); $flower->addType([Block::RED_FLOWER, FlowerBlock::TYPE_BLUE_ORCHID]); $lilyPad = new LilyPad(); $lilyPad->setBaseAmount(4); $tallGrass = new TallGrass(); $tallGrass->setBaseAmount(1); $mushroom = new Mushroom(); $sugarCane = new SugarCane(); $sugarCane->setBaseAmount(2); $sugarCane->setRandomAmount(15); $this->addPopulator($mushroom); $this->addPopulator($lilyPad); $this->addPopulator($flower); $this->addPopulator($tallGrass); $this->addPopulator($sugarCane); $this->setElevation(60, 66); $this->temperature = 0.8; $this->rainfall = 0.9; } public function getName() : string{ return "Swamp"; } } ================================================ FILE: src/pocketmine/level/generator/normal/biome/TaigaBiome.php ================================================ setBaseAmount(10); $this->addPopulator($trees); $mossStone = new MossStone(); $mossStone->setBaseAmount(1); $this->addPopulator($mossStone); $mushroom = new Mushroom(); $this->addPopulator($mushroom); $this->setElevation(63, 83); $this->temperature = 0.05; $this->rainfall = 0.8; $this->setGroundCover([ Block::get(Block::PODZOL, 0), Block::get(Block::DIRT, 0), Block::get(Block::DIRT, 0), Block::get(Block::DIRT, 0) ]); } public function getName() : string{ return "Taiga"; } } ================================================ FILE: src/pocketmine/level/generator/normal/biome/WateryBiome.php ================================================ trunkBlock = Block::WOOD2; $this->leafBlock = Block::LEAVES2; $this->leafType = Leaves2::ACACIA; $this->type = Wood2::ACACIA; $this->treeHeight = 8; } /*public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){ }*/ //TODO: rewrite } ================================================ FILE: src/pocketmine/level/generator/normal/object/BigTree.php ================================================ true, Block::LEAVES => true, Block::SAPLING => true ]; /** @var Random */ private $random; private $trunkHeightMultiplier = 0.618; private $trunkHeight; private $leafAmount = 1; private $leafDistanceLimit = 5; private $widthScale = 1; private $branchSlope = 0.381; private $totalHeight; private $baseHeight = 5; public function canPlaceObject(ChunkManager $level, $x, $y, $z, Random $random){ if(!parent::canPlaceObject($level, $x, $y, $z, $random) or $level->getBlockIdAt($x, $y, $z) == Block::WATER or $level->getBlockIdAt($x, $y, $z) == Block::STILL_WATER){ return false; } $base = new Vector3($x, $y, $z); $this->totalHeight = $this->baseHeight + $random->nextBoundedInt(12); $availableSpace = $this->getAvailableBlockSpace($level, $base, $base->add(0, $this->totalHeight - 1, 0)); if($availableSpace > $this->baseHeight or $availableSpace == -1){ if($availableSpace != -1){ $this->totalHeight = $availableSpace; } return true; } return false; } public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){ $this->random = $random; $this->trunkHeight = (int) ($this->totalHeight * $this->trunkHeightMultiplier); $leaves = $this->getLeafGroupPoints($level, $x, $y, $z); foreach($leaves as $leaf){ /** @var Vector3 $leafGroup */ $leafGroup = $leaf[0]; $groupX = $leafGroup->getX(); $groupY = $leafGroup->getY(); $groupZ = $leafGroup->getZ(); for($yy = $groupY; $yy < $groupY + $this->leafDistanceLimit; ++$yy){ $this->generateGroupLayer($level, $groupX, $yy, $groupZ, $this->getLeafGroupLayerSize($yy - $groupY)); } } $trunk = new VectorIterator($level, new Vector3($x, $y -1, $z), new Vector3($x, $y + $this->trunkHeight, $z)); while($trunk->valid()){ $trunk->next(); $pos = $trunk->current(); $level->setBlockIdAt($pos->x, $pos->y, $pos->z, Block::LOG); } $this->generateBranches($level, $x, $y, $z, $leaves); } private function getLeafGroupPoints(ChunkManager $level, $x, $y, $z){ $amount = $this->leafAmount * $this->totalHeight / 13; $groupsPerLayer = (int) (1.382 + $amount * $amount); if($groupsPerLayer == 0){ $groupsPerLayer = 1; } $trunkTopY = $y + $this->trunkHeight; $groups = []; $groupY = $y + $this->totalHeight - $this->leafDistanceLimit; $groups[] = [new Vector3($x, $groupY, $z), $trunkTopY]; for($currentLayer = (int) ($this->totalHeight - $this->leafDistanceLimit); $currentLayer >= 0; $currentLayer--){ $layerSize = $this->getRoughLayerSize($currentLayer); if($layerSize < 0){ $groupY--; continue; } for($count = 0; $count < $groupsPerLayer; $count++){ $scale = $this->widthScale * $layerSize * ($this->random->nextFloat() + 0.328); $randomOffset = Vector2::createRandomDirection($this->random)->multiply($scale); $groupX = (int) ($randomOffset->getX() + $x + 0.5); $groupZ = (int) ($randomOffset->getY() + $z + 0.5); $group = new Vector3($groupX, $groupY, $groupZ); if($this->getAvailableBlockSpace($level, $group, $group->add(0, $this->leafDistanceLimit, 0)) != -1){ continue; } $xOff = (int) ($x - $groupX); $zOff = (int) ($z - $groupZ); $horizontalDistanceToTrunk = sqrt($xOff * $xOff + $zOff * $zOff); $verticalDistanceToTrunk = $horizontalDistanceToTrunk * $this->branchSlope; $yDiff = (int) ($groupY - $verticalDistanceToTrunk); if($yDiff > $trunkTopY){ $base = $trunkTopY; }else{ $base = $yDiff; } if($this->getAvailableBlockSpace($level, new Vector3($x, $base, $z), $group) == -1){ $groups[] = [$group, $base]; } } $groupY--; } return $groups; } private function getLeafGroupLayerSize(int $y){ if($y >= 0 and $y < $this->leafDistanceLimit){ return (int) (($y != ($this->leafDistanceLimit - 1)) ? 3 : 2); } return -1; } private function generateGroupLayer(ChunkManager $level, int $x, int $y, int $z, int $size){ for($xx = $x - $size; $xx <= $x + $size; $xx++){ for($zz = $z - $size; $zz <= $z + $size; $zz++){ $sizeX = abs($x - $xx) + 0.5; $sizeZ = abs($z - $zz) + 0.5; if(($sizeX * $sizeX + $sizeZ * $sizeZ) <= ($size * $size)){ if(isset($this->overridable[$level->getBlockIdAt($xx, $y, $zz)])){ $level->setBlockIdAt($xx, $y, $zz, Block::LEAVES); } } } } } private function getRoughLayerSize(int $layer) : float { $halfHeight = $this->totalHeight / 2; if($layer < ($this->totalHeight / 3)){ return -1; }elseif($layer == $halfHeight){ return $halfHeight / 4; }elseif($layer >= $this->totalHeight or $layer <= 0){ return 0; }else{ return sqrt($halfHeight * $halfHeight - ($layer - $halfHeight) * ($layer - $halfHeight)) / 2; } } private function generateBranches(ChunkManager $level, int $x, int $y, int $z, array $groups){ foreach($groups as $group){ $baseY = $group[1]; if(($baseY - $y) >= ($this->totalHeight * 0.2)){ $base = new Vector3($x, $baseY, $z); $branch = new VectorIterator($level, $base, $group[0]); while($branch->valid()){ $branch->next(); $pos = $branch->current(); $level->setBlockIdAt((int) $pos->x, (int) $pos->y, (int) $pos->z, Block::LOG); $level->updateBlockLight((int) $pos->x, (int) $pos->y, (int) $pos->z); } } } } private function getAvailableBlockSpace(ChunkManager $level, Vector3 $from, Vector3 $to){ $count = 0; $iter = new VectorIterator($level, $from, $to); while($iter->valid()){ $iter->next(); $pos = $iter->current(); if(!isset($this->overridable[$level->getBlockIdAt($pos->x, $pos->y, $pos->z)])){ return $count; } $count++; } return -1; } } ================================================ FILE: src/pocketmine/level/generator/normal/object/BirchTree.php ================================================ trunkBlock = Block::LOG; $this->leafBlock = Block::LEAVES; $this->leafType = Leaves::BIRCH; $this->type = Wood::BIRCH; $this->superBirch = (bool) $superBirch; } public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){ $this->treeHeight = $random->nextBoundedInt(3) + 5; if($this->superBirch){ $this->treeHeight += 5; } parent::placeObject($level, $x, $y, $z, $random); } } ================================================ FILE: src/pocketmine/level/generator/normal/object/CactusStack.php ================================================ random = $random; $this->randomize(); } public function randomize(){ $this->totalHeight = $this->baseHeight + $this->random->nextBoundedInt($this->randomHeight); } public function canPlaceObject(ChunkManager $level, int $x, int $y, int $z) : bool{ $below = $level->getBlockIdAt($x, $y - 1, $z); if($level->getBlockIdAt($x, $y, $z) == Block::AIR and ($below == Block::SAND or $below == Block::CACTUS) and ( $level->getBlockIdAt($x - 1, $y - 1 , $z) == Block::AIR and $level->getBlockIdAt($x + 1, $y - 1, $z) == Block::AIR and $level->getBlockIdAt($x, $y - 1, $z - 1) == Block::AIR and $level->getBlockIdAt($x, $y - 1, $z + 1) == Block::AIR ) ){ return true; } return false; } public function placeObject(ChunkManager $level, int $x, int $y, int $z){ for($yy = 0; $yy < $this->totalHeight; $yy++){ if($level->getBlockIdAt($x, $y + $yy, $z) != Block::AIR){ return; } $level->setBlockIdAt($x, $y + $yy, $z, Block::CACTUS); } } } ================================================ FILE: src/pocketmine/level/generator/normal/object/DarkOakTree.php ================================================ trunkBlock = Block::WOOD2; $this->leafBlock = Block::LEAVES2; $this->leafType = Leaves2::DARK_OAK; $this->type = Wood2::DARK_OAK; $this->treeHeight = 8; } } ================================================ FILE: src/pocketmine/level/generator/normal/object/JungleTree.php ================================================ trunkBlock = Block::LOG; $this->leafBlock = Block::LEAVES; $this->leafType = Leaves::JUNGLE; $this->type = Wood::JUNGLE; $this->treeHeight = 8; } } ================================================ FILE: src/pocketmine/level/generator/normal/object/OakTree.php ================================================ trunkBlock = Block::LOG; $this->leafBlock = Block::LEAVES; $this->leafType = Leaves::OAK; $this->type = Wood::OAK; } public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){ $this->treeHeight = $random->nextBoundedInt(3) + 4; parent::placeObject($level, $x, $y, $z, $random); } } ================================================ FILE: src/pocketmine/level/generator/normal/object/Ore.php ================================================ type = $type; $this->random = $random; } public function getType(){ return $this->type; } public function canPlaceObject(ChunkManager $level, $x, $y, $z){ return (($level->getBlockIdAt($x, $y, $z) === 1) or ($level->getBlockIdAt($x, $y, $z) === 87)); } public function placeObject(ChunkManager $level, $x, $y, $z){ $clusterSize = (int) $this->type->clusterSize; $angle = $this->random->nextFloat() * M_PI; $offset = VectorMath::getDirection2D($angle)->multiply($clusterSize)->divide(8); $x1 = $x + 8 + $offset->x; $x2 = $x + 8 - $offset->x; $z1 = $z + 8 + $offset->y; $z2 = $z + 8 - $offset->y; $y1 = $y + $this->random->nextBoundedInt(3) + 2; $y2 = $y + $this->random->nextBoundedInt(3) + 2; for($count = 0; $count <= $clusterSize; ++$count){ $seedX = $x1 + ($x2 - $x1) * $count / $clusterSize; $seedY = $y1 + ($y2 - $y1) * $count / $clusterSize; $seedZ = $z1 + ($z2 - $z1) * $count / $clusterSize; $size = ((sin($count * (M_PI / $clusterSize)) + 1) * $this->random->nextFloat() * $clusterSize / 16 + 1) / 2; $startX = (int) ($seedX - $size); $startY = (int) ($seedY - $size); $startZ = (int) ($seedZ - $size); $endX = (int) ($seedX + $size); $endY = (int) ($seedY + $size); $endZ = (int) ($seedZ + $size); for($x = $startX; $x <= $endX; ++$x){ $sizeX = ($x + 0.5 - $seedX) / $size; $sizeX *= $sizeX; if($sizeX < 1){ for($y = $startY; $y <= $endY; ++$y){ $sizeY = ($y + 0.5 - $seedY) / $size; $sizeY *= $sizeY; if($y > 0 and ($sizeX + $sizeY) < 1){ for($z = $startZ; $z <= $endZ; ++$z){ $sizeZ = ($z + 0.5 - $seedZ) / $size; $sizeZ *= $sizeZ; if(($sizeX + $sizeY + $sizeZ) < 1 and (($level->getBlockIdAt($x, $y, $z) === 1) or ($level->getBlockIdAt($x, $y, $z) === 87)) ){ $level->setBlockIdAt($x, $y, $z, $this->type->material->getId()); if($this->type->material->getDamage() !== 0){ $level->setBlockDataAt($x, $y, $z, $this->type->material->getDamage()); } } } } } } } } } } ================================================ FILE: src/pocketmine/level/generator/normal/object/OreType.php ================================================ material = $material; $this->clusterCount = (int) $clusterCount; $this->clusterSize = (int) $clusterSize; $this->maxHeight = (int) $maxHeight; $this->minHeight = (int) $minHeight; } } ================================================ FILE: src/pocketmine/level/generator/normal/object/Pond.php ================================================ type = $type; $this->random = $random; } public function canPlaceObject(ChunkManager $level, Vector3 $pos){ } public function placeObject(ChunkManager $level, Vector3 $pos){ } } ================================================ FILE: src/pocketmine/level/generator/normal/object/SpruceTree.php ================================================ trunkBlock = Block::LOG; $this->leafBlock = Block::LEAVES; $this->leafType = Leaves::SPRUCE; $this->type = Wood::SPRUCE; $this->treeHeight = 10; } public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){ $this->treeHeight = $random->nextBoundedInt(4) + 6; $topSize = $this->treeHeight - (1 + $random->nextBoundedInt(2)); $lRadius = 2 + $random->nextBoundedInt(2); $this->placeTrunk($level, $x, $y, $z, $random, $this->treeHeight - $random->nextBoundedInt(3)); $radius = $random->nextBoundedInt(2); $maxR = 1; $minR = 0; for($yy = 0; $yy <= $topSize; ++$yy){ $yyy = $y + $this->treeHeight - $yy; for($xx = $x - $radius; $xx <= $x + $radius; ++$xx){ $xOff = abs($xx - $x); for($zz = $z - $radius; $zz <= $z + $radius; ++$zz){ $zOff = abs($zz - $z); if($xOff === $radius and $zOff === $radius and $radius > 0){ continue; } if(!Block::$solid[$level->getBlockIdAt($xx, $yyy, $zz)]){ $level->setBlockIdAt($xx, $yyy, $zz, $this->leafBlock); $level->setBlockDataAt($xx, $yyy, $zz, $this->type); } } } if($radius >= $maxR){ $radius = $minR; $minR = 1; if(++$maxR > $lRadius){ $maxR = $lRadius; } }else{ ++$radius; } } } } ================================================ FILE: src/pocketmine/level/generator/normal/object/SugarCaneStack.php ================================================ random = $random; $this->randomize(); } public function randomize(){ $this->totalHeight = $this->baseHeight + $this->random->nextBoundedInt($this->randomHeight); } private function isWater(int $id) : bool{ if($id == Block::WATER or $id == Block::STILL_WATER){ return true; } return false; } public function canPlaceObject(ChunkManager $level, int $x, int $y, int $z) : bool{ $below = $level->getBlockIdAt($x, $y - 1, $z); if($level->getBlockIdAt($x, $y, $z) == Block::AIR and ($below == Block::DIRT or $below == Block::GRASS or $below == Block::SAND) and ( $this->isWater($level->getBlockIdAt($x - 1, $y - 1 , $z)) or $this->isWater($level->getBlockIdAt($x + 1, $y - 1, $z)) or $this->isWater($level->getBlockIdAt($x, $y - 1, $z - 1)) or $this->isWater($level->getBlockIdAt($x, $y - 1, $z + 1)) ) ){ return true; } return false; } public function placeObject(ChunkManager $level, int $x, int $y, int $z){ for($yy = 0; $yy < $this->totalHeight; $yy++){ if($level->getBlockIdAt($x, $y + $yy, $z) != Block::AIR){ return; } $level->setBlockIdAt($x, $y + $yy, $z, Block::SUGARCANE_BLOCK); } } } ================================================ FILE: src/pocketmine/level/generator/normal/object/TallGrass.php ================================================ nextRange($pos->x - $radius, $pos->x + $radius); $z = $random->nextRange($pos->z - $radius, $pos->z + $radius); if($level->getBlockIdAt($x, $pos->y + 1, $z) === Block::AIR and $level->getBlockIdAt($x, $pos->y, $z) === Block::GRASS){ $t = $arr[$random->nextRange(0, $arrC)]; $level->setBlockIdAt($x, $pos->y + 1, $z, $t[0]); $level->setBlockDataAt($x, $pos->y + 1, $z, $t[1]); } } } } ================================================ FILE: src/pocketmine/level/generator/normal/object/Tree.php ================================================ true, 6 => true, 17 => true, 18 => true, Block::SNOW_LAYER => true, Block::LOG2 => true, Block::LEAVES2 => true ]; public $type = 0; public $trunkBlock = Block::LOG; public $leafBlock = Block::LEAVES; public $treeHeight = 7; public $leafType = 0; public static function growTree(ChunkManager $level, $x, $y, $z, Random $random, $type = 0, bool $noBigTree = true){ switch($type){ case Sapling::SPRUCE: $tree = new SpruceTree(); break; case Sapling::BIRCH: if($random->nextBoundedInt(39) === 0){ $tree = new BirchTree(true); }else{ $tree = new BirchTree(); } break; case Sapling::JUNGLE: $tree = new JungleTree(); break; case Sapling::ACACIA: $tree = new AcaciaTree(); break; case Sapling::DARK_OAK: $tree = new DarkOakTree(); break; case Sapling::OAK: default: if(!$noBigTree and $random->nextRange(0, 9) === 0){ $tree = new BigTree(); }else{ $tree = new OakTree(); } break; } if($tree->canPlaceObject($level, $x, $y, $z, $random)){ $tree->placeObject($level, $x, $y, $z, $random); } } public function canPlaceObject(ChunkManager $level, $x, $y, $z, Random $random){ $radiusToCheck = 0; for($yy = 0; $yy < $this->treeHeight + 3; ++$yy){ if($yy == 1 or $yy === $this->treeHeight){ ++$radiusToCheck; } for($xx = -$radiusToCheck; $xx < ($radiusToCheck + 1); ++$xx){ for($zz = -$radiusToCheck; $zz < ($radiusToCheck + 1); ++$zz){ if(!isset($this->overridable[$level->getBlockIdAt($x + $xx, $y + $yy, $z + $zz)])){ return false; } } } } return true; } public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){ $this->placeTrunk($level, $x, $y, $z, $random, $this->treeHeight - 1); for($yy = $y - 3 + $this->treeHeight; $yy <= $y + $this->treeHeight; ++$yy){ $yOff = $yy - ($y + $this->treeHeight); $mid = (int) (1 - $yOff / 2); for($xx = $x - $mid; $xx <= $x + $mid; ++$xx){ $xOff = abs($xx - $x); for($zz = $z - $mid; $zz <= $z + $mid; ++$zz){ $zOff = abs($zz - $z); if($xOff === $mid and $zOff === $mid and ($yOff === 0 or $random->nextBoundedInt(2) === 0)){ continue; } if(!Block::$solid[$level->getBlockIdAt($xx, $yy, $zz)]){ $level->setBlockIdAt($xx, $yy, $zz, $this->leafBlock); $level->setBlockDataAt($xx, $yy, $zz, $this->leafType); } } } } } protected function placeTrunk(ChunkManager $level, $x, $y, $z, Random $random, $trunkHeight){ // The base dirt block $level->setBlockIdAt($x, $y - 1, $z, Block::DIRT); for($yy = 0; $yy < $trunkHeight; ++$yy){ $blockId = $level->getBlockIdAt($x, $y + $yy, $z); if(isset($this->overridable[$blockId])){ $level->setBlockIdAt($x, $y + $yy, $z, $this->trunkBlock); $level->setBlockDataAt($x, $y + $yy, $z, $this->type); } } } } ================================================ FILE: src/pocketmine/level/generator/normal/populator/Cactus.php ================================================ level = $level; $amount = $this->getAmount($random); $cactus = new CactusStack($random); for($i = 0; $i < $amount; ++$i){ $x = $random->nextRange($chunkX * 16, $chunkX * 16 + 15); $z = $random->nextRange($chunkZ * 16, $chunkZ * 16 + 15); $y = $this->getHighestWorkableBlock($x, $z); $cactus->randomize(); if($y !== -1 and $cactus->canPlaceObject($level, $x, $y, $z)){ $cactus->placeObject($level, $x, $y, $z); } } } private function getHighestWorkableBlock($x, $z){ for($y = 127; $y >= 0; --$y){ $b = $this->level->getBlockIdAt($x, $y, $z); if($b !== Block::AIR and $b !== Block::LEAVES and $b !== Block::LEAVES2){ break; } } return $y === 0 ? -1 : ++$y; } } ================================================ FILE: src/pocketmine/level/generator/normal/populator/Cave.php ================================================ nextInt(); $secondSeed = $random->nextInt(); for($cxx = 0; $cxx < 1; $cxx++){ for($czz = 0; $czz < 1; $czz++){ $dcx = $chunkX + $cxx; $dcz = $chunkZ + $czz; for($cxxx = -$overlap; $cxxx <= $overlap; $cxxx++){ for($czzz = -$overlap; $czzz <= $overlap; $czzz++){ $dcxx = $dcx + $cxxx; $dczz = $dcz + $czzz; $this->pop($level, $dcxx, $dczz, $dcx, $dcz, new Random(($dcxx * $firstSeed) ^ ($dczz * $secondSeed) ^ $random->getSeed())); } } } } } private function pop(ChunkManager $level, $x, $z, $chunkX, $chunkZ, Random $random){ $c = $level->getChunk($x, $z); $oC = $level->getChunk($chunkX, $chunkZ); if($c == null or $oC == null or ($c != null and !$c->isGenerated()) or ($oC != null and !$oC->isGenerated())){ return; } $chunk = new Vector3($x << 4, 0, $z << 4); $originChunk = new Vector3($chunkX << 4, 0, $chunkZ << 4); if($random->nextRange(0, 15) != 0){ return; } $numberOfCaves = $random->nextRange(0, $random->nextRange(0, $random->nextRange(0, 40) + 1) + 1); for($caveCount = 0; $caveCount < $numberOfCaves; $caveCount++){ $target = new Vector3($chunk->getX() + $random->nextRange(0, 16), $random->nextRange(0, $random->nextRange(0, 120) + 8), $chunk->getZ() + $random->nextRange(0, 16)); $numberOfSmallCaves = 1; if($random->nextRange(0, 4) == 0){ $this->generateLargeCaveBranch($level, $originChunk, $target, new Random($random->nextInt())); $numberOfSmallCaves += $random->nextRange(0, 4); } for($count = 0; $count < $numberOfSmallCaves; $count++){ $randomHorizontalAngle = $random->nextFloat() * pi() * 2; $randomVerticalAngle = (($random->nextFloat() - 0.5) * 2) / 8; $horizontalScale = $random->nextFloat() * 2 + $random->nextFloat(); if($random->nextRange(0, 10) == 0){ $horizontalScale *= $random->nextFloat() * $random->nextFloat() * 3 + 1; } $this->generateCaveBranch($level, $originChunk, $target, $horizontalScale, 1, $randomHorizontalAngle, $randomVerticalAngle, 0, 0, new Random($random->nextInt())); } } } private function generateCaveBranch(ChunkManager $level, Vector3 $chunk, Vector3 $target, $horizontalScale, $verticalScale, $horizontalAngle, $verticalAngle, int $startingNode, int $nodeAmount, Random $random){ $middle = new Vector3($chunk->getX() + 8, 0, $chunk->getZ() + 8); $horizontalOffset = 0; $verticalOffset = 0; if($nodeAmount <= 0){ $size = 7 * 16; $nodeAmount = $size - $random->nextRange(0, $size / 4); } $intersectionMode = $random->nextRange(0, $nodeAmount / 2) + $nodeAmount / 4; $extraVerticalScale = $random->nextRange(0, 6) == 0; if($startingNode == -1){ $startingNode = $nodeAmount / 2; $lastNode = true; }else{ $lastNode = false; } for(; $startingNode < $nodeAmount; $startingNode++){ $horizontalSize = 1.5 + sin($startingNode * pi() / $nodeAmount) * $horizontalScale; $verticalSize = $horizontalSize * $verticalScale; $target = $target->add(VectorMath::getDirection3D($horizontalAngle, $verticalAngle)); if($extraVerticalScale){ $verticalAngle *= 0.92; }else{ $verticalScale *= 0.7; } $verticalAngle += $verticalOffset * 0.1; $horizontalAngle += $horizontalOffset * 0.1; $verticalOffset *= 0.9; $horizontalOffset *= 0.75; $verticalOffset += ($random->nextFloat() - $random->nextFloat()) * $random->nextFloat() * 2; $horizontalOffset += ($random->nextFloat() - $random->nextFloat()) * $random->nextFloat() * 4; if(!$lastNode){ if($startingNode == $intersectionMode and $horizontalScale > 1 and $nodeAmount > 0){ $this->generateCaveBranch($level, $chunk, $target, $random->nextFloat() * 0.5 + 0.5, 1, $horizontalAngle - pi() / 2, $verticalAngle / 3, $startingNode, $nodeAmount, new Random($random->nextInt())); $this->generateCaveBranch($level, $chunk, $target, $random->nextFloat() * 0.5 + 0.5, 1, $horizontalAngle - pi() / 2, $verticalAngle / 3, $startingNode, $nodeAmount, new Random($random->nextInt())); return; } if($random->nextRange(0, 4) == 0){ continue; } } $xOffset = $target->getX() - $middle->getX(); $zOffset = $target->getZ() - $middle->getZ(); $nodesLeft = $nodeAmount - $startingNode; $offsetHorizontalScale = $horizontalScale + 18; if((($xOffset * $xOffset + $zOffset * $zOffset) - $nodesLeft * $nodesLeft) > ($offsetHorizontalScale * $offsetHorizontalScale)){ return; } if($target->getX() < ($middle->getX() - 16 - $horizontalSize * 2) or $target->getZ() < ($middle->getZ() - 16 - $horizontalSize * 2) or $target->getX() > ($middle->getX() + 16 + $horizontalSize * 2) or $target->getZ() > ($middle->getZ() + 16 + $horizontalSize * 2) ){ continue; } $start = new Vector3(floor($target->getX() - $horizontalSize) - $chunk->getX() - 1, floor($target->getY() - $verticalSize) - 1, floor($target->getZ() - $horizontalSize) - $chunk->getZ() - 1); $end = new Vector3(floor($target->getX() + $horizontalSize) - $chunk->getX() + 1, floor($target->getY() + $verticalSize) + 1, floor($target->getZ() + $horizontalSize) - $chunk->getZ() + 1); $node = new CaveNode($level, $chunk, $start, $end, $target, $verticalSize, $horizontalSize); if($node->canPlace()){ $node->place(); } if($lastNode){ break; } } } private function generateLargeCaveBranch(ChunkManager $level, Vector3 $chunk, Vector3 $target, Random $random){ $this->generateCaveBranch($level, $chunk, $target, $random->nextFloat() * 6 + 1, 0.5, 0, 0, -1, -1, $random); } } class CaveNode{ /** @var ChunkManager */ private $level; /** @var Vector3 */ private $chunk; /** @var Vector3 */ private $start; /** @var Vector3 */ private $end; /** @var Vector3 */ private $target; private $verticalSize; private $horizontalSize; public function __construct(ChunkManager $level, Vector3 $chunk, Vector3 $start, Vector3 $end, Vector3 $target, $verticalSize, $horizontalSize){ $this->level = $level; $this->chunk = $chunk; $this->start = $this->clamp($start); $this->end = $this->clamp($end); $this->target = $target; $this->verticalSize = $verticalSize; $this->horizontalSize = $horizontalSize; } private function clamp(Vector3 $pos){ return new Vector3( Math::clamp($pos->getFloorX(), 0, 16), Math::clamp($pos->getFloorY(), 1, 120), Math::clamp($pos->getFloorZ(), 0, 16) ); } public function canPlace(){ for($x = $this->start->getFloorX(); $x < $this->end->getFloorX(); $x++){ for($z = $this->start->getFloorZ(); $z < $this->end->getFloorZ(); $z++){ for($y = $this->end->getFloorY() + 1; $y >= $this->start->getFloorY() - 1; $y--){ $blockId = $this->level->getBlockIdAt($this->chunk->getX() + $x, $y, $this->chunk->getZ() + $z); if($blockId == Block::WATER or $blockId == Block::STILL_WATER){ return false; } if($y != ($this->start->getFloorY() - 1) and $x != ($this->start->getFloorX()) and $x != ($this->end->getFloorX() - 1) and $z != ($this->start->getFloorZ()) and $z != ($this->end->getFloorZ() - 1)){ $y = $this->start->getFloorY(); } } } } return true; } public function place(){ for($x = $this->start->getFloorX(); $x < $this->end->getFloorX(); $x++){ $xOffset = ($this->chunk->getX() + $x + 0.5 - $this->target->getX()) / $this->horizontalSize; for($z = $this->start->getFloorZ(); $z < $this->end->getFloorZ(); $z++){ $zOffset = ($this->chunk->getZ() + $z + 0.5 - $this->target->getZ()) / $this->horizontalSize; if(($xOffset * $xOffset + $zOffset * $zOffset) >= 1){ continue; } for($y = $this->end->getFloorY() - 1; $y >= $this->start->getFloorY(); $y--){ $yOffset = ($y + 0.5 - $this->target->getY()) / $this->verticalSize; if($yOffset > -0.7 and ($xOffset * $xOffset + $yOffset * $yOffset + $zOffset * $zOffset) < 1){ $xx = $this->chunk->getX() + $x; $zz = $this->chunk->getZ() + $z; $blockId = $this->level->getBlockIdAt($xx, $y, $zz); if($blockId == Block::STONE or $blockId == Block::DIRT or $blockId == Block::GRASS){ if($y < 10){ $this->level->setBlockIdAt($xx, $y, $zz, Block::STILL_LAVA); }else{ if($blockId == Block::GRASS and $this->level->getBlockIdAt($xx, $y - 1, $zz) == Block::DIRT){ $this->level->setBlockIdAt($xx, $y - 1, $zz, Block::GRASS); } $this->level->setBlockIdAt($xx, $y, $zz, Block::AIR); } } } } } } } } ================================================ FILE: src/pocketmine/level/generator/normal/populator/DeadBush.php ================================================ level = $level; $amount = $this->getAmount($random); for($i = 0; $i < $amount; ++$i){ $x = $random->nextRange($chunkX * 16, $chunkX * 16 + 15); $z = $random->nextRange($chunkZ * 16, $chunkZ * 16 + 15); $y = $this->getHighestWorkableBlock($x, $z); if($y !== -1 and $this->canDeadBushStay($x, $y, $z)){ $this->level->setBlockIdAt($x, $y, $z, Block::DEAD_BUSH); $this->level->setBlockDataAt($x, $y, $z, 1); } } } private function canDeadBushStay($x, $y, $z){ $b = $this->level->getBlockIdAt($x, $y, $z); return ($b === Block::AIR or $b === Block::SNOW_LAYER) and $this->level->getBlockIdAt($x, $y - 1, $z) === Block::SAND; } private function getHighestWorkableBlock($x, $z){ for($y = 127; $y >= 0; --$y){ $b = $this->level->getBlockIdAt($x, $y, $z); if($b !== Block::AIR and $b !== Block::LEAVES and $b !== Block::LEAVES2 and $b !== Block::SNOW_LAYER){ break; } } return $y === 0 ? -1 : ++$y; } } ================================================ FILE: src/pocketmine/level/generator/normal/populator/Flower.php ================================================ flowerTypes[] = $type; } public function getTypes(){ return $this->flowerTypes; } public function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random){ $this->level = $level; $amount = $this->getAmount($random); if(count($this->flowerTypes) === 0){ $this->addType([Block::DANDELION, 0]); $this->addType([Block::RED_FLOWER, FlowerBlock::TYPE_POPPY]); } $endNum = count($this->flowerTypes) - 1; for($i = 0; $i < $amount; ++$i){ $x = $random->nextRange($chunkX * 16, $chunkX * 16 + 15); $z = $random->nextRange($chunkZ * 16, $chunkZ * 16 + 15); $y = $this->getHighestWorkableBlock($x, $z); if($y !== -1 and $this->canFlowerStay($x, $y, $z)){ $type = mt_rand(0, $endNum); $this->level->setBlockIdAt($x, $y, $z, $this->flowerTypes[$type][0]); $this->level->setBlockDataAt($x, $y, $z, $this->flowerTypes[$type][1]); } } } private function canFlowerStay($x, $y, $z){ $b = $this->level->getBlockIdAt($x, $y, $z); return ($b === Block::AIR or $b === Block::SNOW_LAYER) and $this->level->getBlockIdAt($x, $y - 1, $z) === Block::GRASS; } private function getHighestWorkableBlock($x, $z){ for($y = 127; $y >= 0; --$y){ $b = $this->level->getBlockIdAt($x, $y, $z); if($b !== Block::AIR and $b !== Block::LEAVES and $b !== Block::LEAVES2 and $b !== Block::SNOW_LAYER){ break; } } return $y === 0 ? -1 : ++$y; } } ================================================ FILE: src/pocketmine/level/generator/normal/populator/GroundCover.php ================================================ getChunk($chunkX, $chunkZ); if($level instanceof Level or $level instanceof SimpleChunkManager){ $waterHeight = $level->getWaterHeight(); } else $waterHeight = 0; for($x = 0; $x < 16; ++$x){ for($z = 0; $z < 16; ++$z){ $biome = Biome::getBiome($chunk->getBiomeId($x, $z)); $cover = $biome->getGroundCover(); if(count($cover) > 0){ $diffY = 0; if(!$cover[0]->isSolid()){ $diffY = 1; } $column = $chunk->getBlockIdColumn($x, $z); for($y = 127; $y > 0; --$y){ if($column{$y} !== "\x00" and !Block::get(ord($column{$y}))->isTransparent()){ break; } } $startY = min(127, $y + $diffY); $endY = $startY - count($cover); for($y = $startY; $y > $endY and $y >= 0; --$y){ $b = $cover[$startY - $y]; if($column{$y} === "\x00" and $b->isSolid()){ break; } if($y <= $waterHeight and $b->getId() == Block::GRASS and $chunk->getBlockId($x, $y + 1, $z) == Block::STILL_WATER){ $b = Block::get(Block::DIRT); } if($b->getDamage() === 0){ $chunk->setBlockId($x, $y, $z, $b->getId()); }else{ $chunk->setBlock($x, $y, $z, $b->getId(), $b->getDamage()); } } } } } } } ================================================ FILE: src/pocketmine/level/generator/normal/populator/LilyPad.php ================================================ level = $level; $amount = $this->getAmount($random); for($i = 0; $i < $amount; ++$i){ $x = $random->nextRange($chunkX * 16, $chunkX * 16 + 15); $z = $random->nextRange($chunkZ * 16, $chunkZ * 16 + 15); $y = $this->getHighestWorkableBlock($x, $z); if($y !== -1 and $this->canLilyPadStay($x, $y, $z)){ $this->level->setBlockIdAt($x, $y, $z, Block::WATER_LILY); $this->level->setBlockDataAt($x, $y, $z, 1); } } } private function canLilyPadStay($x, $y, $z){ $b = $this->level->getBlockIdAt($x, $y, $z); return ($b === Block::AIR or $b === Block::SNOW_LAYER) and $this->level->getBlockIdAt($x, $y - 1, $z) === Block::STILL_WATER; } private function getHighestWorkableBlock($x, $z){ for($y = 127; $y >= 0; --$y){ $b = $this->level->getBlockIdAt($x, $y, $z); if($b !== Block::AIR and $b !== Block::LEAVES and $b !== Block::LEAVES2 and $b !== Block::SNOW_LAYER){ break; } } return $y === 0 ? -1 : ++$y; } } ================================================ FILE: src/pocketmine/level/generator/normal/populator/Mineshaft.php ================================================ nextRange(0, self::$ODD) === 0){ //$mineshaft = new Mineshaft($random); } } } ================================================ FILE: src/pocketmine/level/generator/normal/populator/MossStone.php ================================================ level = $level; $amount = $this->getAmount($random); for($i = 0; $i < $amount; ++$i){ $x = $random->nextRange($chunkX * 16, $chunkX * 16 + 15); $z = $random->nextRange($chunkZ * 16, $chunkZ * 16 + 15); $y = $this->getHighestWorkableBlock($x, $z); if($y !== -1 and $this->canMossStoneStay($x, $y, $z)){ $this->level->setBlockIdAt($x, $y, $z, Block::MOSS_STONE); $this->level->setBlockDataAt($x, $y, $z, 1); } } } private function canMossStoneStay($x, $y, $z){ $b = $this->level->getBlockIdAt($x, $y, $z); return ($b === Block::AIR or $b === Block::SNOW_LAYER) and $this->level->getBlockIdAt($x, $y - 1, $z) === Block::PODZOL; } private function getHighestWorkableBlock($x, $z){ for($y = 127; $y >= 0; --$y){ $b = $this->level->getBlockIdAt($x, $y, $z); if($b !== Block::AIR and $b !== Block::LEAVES and $b !== Block::LEAVES2 and $b !== Block::SNOW_LAYER){ break; } } return $y === 0 ? -1 : ++$y; } } ================================================ FILE: src/pocketmine/level/generator/normal/populator/Mushroom.php ================================================ checkOdd($random)){ return; } $this->level = $level; $amount = $this->getAmount($random); for($i = 0; $i < $amount; ++$i){ $x = $chunkX * 16; $z = $chunkZ * 16; for($size = 6; $size > 0; $size--){ $xx = $x - 7 + $random->nextRange(0, 15); $zz = $z - 7 + $random->nextRange(0, 15); $yy = $this->getHighestWorkableBlock($xx, $zz); if($yy !== -1 and $this->canMushroomStay($xx, $yy, $zz)){ $this->level->setBlockIdAt($xx, $yy, $zz, (($random->nextRange(0, 4)) == 0 ? Block::RED_MUSHROOM_BLOCK : Block::BROWN_MUSHROOM_BLOCK)); } } } } private function canMushroomStay($x, $y, $z){ $c = $this->level->getBlockIdAt($x, $y, $z); $b = $this->level->getBlockIdAt($x, $y - 1, $z); return ($c === Block::AIR or $c === Block::SNOW_LAYER) and ($b === Block::MYCELIUM or (!Block::$transparent[$b])); } private function getHighestWorkableBlock($x, $z){ for($y = 127; $y >= 0; --$y){ $b = $this->level->getBlockIdAt($x, $y, $z); if($b !== Block::AIR and $b !== Block::LEAVES and $b !== Block::LEAVES2 and $b !== Block::SNOW_LAYER){ break; } } return $y === 0 ? -1 : ++$y; } } ================================================ FILE: src/pocketmine/level/generator/normal/populator/Ore.php ================================================ oreTypes as $type){ $ore = new ObjectOre($random, $type); for($i = 0; $i < $ore->type->clusterCount; ++$i){ $x = $random->nextRange($chunkX << 4, ($chunkX << 4) + 15); $y = $random->nextRange($ore->type->minHeight, $ore->type->maxHeight); $z = $random->nextRange($chunkZ << 4, ($chunkZ << 4) + 15); if($ore->canPlaceObject($level, $x, $y, $z)){ $ore->placeObject($level, $x, $y, $z); } } } } public function setOreTypes(array $types){ $this->oreTypes = $types; } } ================================================ FILE: src/pocketmine/level/generator/normal/populator/Pond.php ================================================ nextRange(0, $this->waterOdd) === 0){ $x = $random->nextRange($chunkX << 4, ($chunkX << 4) + 16); $y = $random->nextBoundedInt(128); $z = $random->nextRange($chunkZ << 4, ($chunkZ << 4) + 16); $pond = new \pocketmine\level\generator\normal\object\Pond($random, new Water()); if($pond->canPlaceObject($level, $x, $y, $z)){ $pond->placeObject($level, $x, $y, $z); } } } public function setWaterOdd($waterOdd){ $this->waterOdd = $waterOdd; } public function setLavaOdd($lavaOdd){ $this->lavaOdd = $lavaOdd; } public function setLavaSurfaceOdd($lavaSurfaceOdd){ $this->lavaSurfaceOdd = $lavaSurfaceOdd; } } ================================================ FILE: src/pocketmine/level/generator/normal/populator/SugarCane.php ================================================ level = $level; $canes = new SugarCaneStack($random); $successfulClusterCount = 0; for($count = 0; $count < $this->randomAmount; $count++){ $x = $random->nextRange($chunkX * 16, $chunkX * 16 + 15); $z = $random->nextRange($chunkZ * 16, $chunkZ * 16 + 15); $y = $this->getHighestWorkableBlock($x, $z); if($y == -1 or !$canes->canPlaceObject($level, $x, $y, $z)){ continue; } $successfulClusterCount++; $canes->randomize(); $canes->placeObject($level, $x, $y, $z); for($placed = 1; $placed < 4; $placed++){ $xx = $x - 3 + $random->nextBoundedInt(7); $zz = $z - 3 + $random->nextBoundedInt(7); $canes->randomize(); if($canes->canPlaceObject($level, $xx, $y, $zz)){ $canes->placeObject($level, $xx, $y, $zz); } } if($successfulClusterCount >= $this->baseAmount){ return; } } } private function getHighestWorkableBlock($x, $z){ for($y = 127; $y >= 0; --$y){ $b = $this->level->getBlockIdAt($x, $y, $z); if($b !== Block::AIR and $b !== Block::LEAVES and $b !== Block::LEAVES2){ break; } } return $y === 0 ? -1 : ++$y; } } ================================================ FILE: src/pocketmine/level/generator/normal/populator/TallGrass.php ================================================ level = $level; $amount = $this->getAmount($random); for($i = 0; $i < $amount; ++$i){ $x = $random->nextRange($chunkX * 16, $chunkX * 16 + 15); $z = $random->nextRange($chunkZ * 16, $chunkZ * 16 + 15); $y = $this->getHighestWorkableBlock($x, $z); if($y !== -1 and $this->canTallGrassStay($x, $y, $z)){ $this->level->setBlockIdAt($x, $y, $z, Block::TALL_GRASS); $this->level->setBlockDataAt($x, $y, $z, 1); } } } private function canTallGrassStay($x, $y, $z){ $b = $this->level->getBlockIdAt($x, $y, $z); return ($b === Block::AIR or $b === Block::SNOW_LAYER) and $this->level->getBlockIdAt($x, $y - 1, $z) === Block::GRASS; } private function getHighestWorkableBlock($x, $z){ for($y = 127; $y >= 0; --$y){ $b = $this->level->getBlockIdAt($x, $y, $z); if($b !== Block::AIR and $b !== Block::LEAVES and $b !== Block::LEAVES2 and $b !== Block::SNOW_LAYER){ break; } } return $y === 0 ? -1 : ++$y; } } ================================================ FILE: src/pocketmine/level/generator/normal/populator/Tree.php ================================================ type = $type; } public function populate(ChunkManager $level, $chunkX, $chunkZ, Random $random){ $this->level = $level; $amount = $this->getAmount($random); for($i = 0; $i < $amount; ++$i){ $x = $random->nextRange($chunkX << 4, ($chunkX << 4) + 15); $z = $random->nextRange($chunkZ << 4, ($chunkZ << 4) + 15); $y = $this->getHighestWorkableBlock($x, $z); if($y === -1){ continue; } ObjectTree::growTree($this->level, $x, $y, $z, $random, $this->type); } } private function getHighestWorkableBlock($x, $z){ for($y = 127; $y > 0; --$y){ $b = $this->level->getBlockIdAt($x, $y, $z); if($b === Block::DIRT or $b === Block::GRASS or $b === Block::PODZOL){ break; }elseif($b !== 0 and $b !== Block::SNOW_LAYER){ return -1; } } return ++$y; } } ================================================ FILE: src/pocketmine/level/generator/normal/populator/WaterPit.php ================================================ level = $level; $amount = $this->getAmount($random); for($i = 0; $i < $amount; ++$i){ $x = $random->nextRange($chunkX * 16, $chunkX * 16 + 15); $z = $random->nextRange($chunkZ * 16, $chunkZ * 16 + 15); $y = $this->getHighestWorkableBlock($x, $z); if($y !== -1 and $this->canWaterPitStay($x, $y, $z)){ $this->level->setBlockIdAt($x, $y, $z, Block::STILL_WATER); $this->level->setBlockDataAt($x, $y, $z , 8); } } } private function canWaterPitStay($x, $y, $z){ $b = $this->level->getBlockIdAt($x, $y, $z); return ($b === Block::AIR or $b === Block::GRASS) and $this->level->getBlockIdAt($x, $y, $z) === Block::DIRT; } private function getHighestWorkableBlock($x, $z){ for($y = 61; $y >= 0; --$y){ $b = $this->level->getBlockIdAt($x, $y, $z); if($b !== Block::AIR and $b !== Block::LEAVES and $b !== Block::LEAVES2 and $b !== Block::SNOW_LAYER){ break; } } return $y === 0 ? -1 : ++$y; } } ================================================ FILE: src/pocketmine/level/generator/object/Object.php ================================================ baseAmount = $baseAmount; $this->randomAmount = $randomAmount; $this->odd = $odd; } public function setOdd(int $odd){ $this->odd = $odd; } public function checkOdd(Random $random) : bool{ if($random->nextRange(0, $this->odd) == 0){ return true; } return false; } public function getAmount(Random $random){ return $this->baseAmount + $random->nextRange(0, $this->randomAmount + 1); } public final function setBaseAmount(int $baseAmount){ $this->baseAmount = $baseAmount; } public final function setRandomAmount(int $randomAmount){ $this->randomAmount = $randomAmount; } public function getBaseAmount() : int{ return $this->baseAmount; } public function getRandomAmount() : int{ return $this->randomAmount; } } ================================================ FILE: src/pocketmine/level/particle/AngryVillagerParticle.php ================================================ x, $pos->y, $pos->z); $this->data = $b->getId() + ($b->getDamage() << 12); } public function encode(){ $pk = new LevelEventPacket; $pk->evid = LevelEventPacket::EVENT_PARTICLE_DESTROY; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->data = $this->data; return $pk; } } ================================================ FILE: src/pocketmine/level/particle/DustParticle.php ================================================ x, $pos->y, $pos->z); $this->text = $text; $this->title = $title; } public function getText(){ return $this->text; } public function getTitle(){ return $this->title; } public function setText($text){ $this->text = $text; } public function setTitle($title){ $this->title = $title; } public function isInvisible(){ return $this->invisible; } public function setInvisible($value = true){ $this->invisible = (bool) $value; } public function encode(){ $p = []; if($this->entityId === null){ $this->entityId = bcadd("1095216660480", mt_rand(0, 0x7fffffff)); //No conflict with other things }else{ $pk0 = new RemoveEntityPacket(); $pk0->eid = $this->entityId; $p[] = $pk0; } if(!$this->invisible){ $pk = new AddEntityPacket(); $pk->eid = $this->entityId; $pk->type = ItemEntity::NETWORK_ID; $pk->x = $this->x; $pk->y = $this->y - 0.75; $pk->z = $this->z; $pk->speedX = 0; $pk->speedY = 0; $pk->speedZ = 0; $pk->yaw = 0; $pk->pitch = 0; $pk->item = 0; $pk->meta = 0; $flags = 0; $flags |= 1 << Entity::DATA_FLAG_INVISIBLE; $flags |= 1 << Entity::DATA_FLAG_CAN_SHOW_NAMETAG; $flags |= 1 << Entity::DATA_FLAG_ALWAYS_SHOW_NAMETAG; $flags |= 1 << Entity::DATA_FLAG_IMMOBILE; $pk->metadata = [ Entity::DATA_FLAGS => [Entity::DATA_TYPE_LONG, $flags], Entity::DATA_NAMETAG => [Entity::DATA_TYPE_STRING, $this->title . ($this->text !== "" ? "\n" . $this->text : "")], ]; $p[] = $pk; } return $p; } } ================================================ FILE: src/pocketmine/level/particle/GenericParticle.php ================================================ x, $pos->y, $pos->z); $this->id = $id & 0xFFF; $this->data = $data; } public function encode(){ $pk = new LevelEventPacket; $pk->evid = LevelEventPacket::EVENT_ADD_PARTICLE_MASK | $this->id; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->data = $this->data; return $pk; } } ================================================ FILE: src/pocketmine/level/particle/HappyVillagerParticle.php ================================================ getId() << 16) | $item->getDamage()); } } ================================================ FILE: src/pocketmine/level/particle/LavaDripParticle.php ================================================ x, $pos->y, $pos->z); $this->width = $width; $this->height = $height; } public function encode(){ $pk = new LevelEventPacket; $pk->evid = LevelEventPacket::EVENT_PARTICLE_SPAWN; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->data = ($this->width & 0xff) + (($this->height & 0xff) << 8); return $pk; } } ================================================ FILE: src/pocketmine/level/particle/MobSpellParticle.php ================================================ evid = LevelEventPacket::EVENT_PARTICLE_SPLASH; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->data = $this->data; return $pk; } } ================================================ FILE: src/pocketmine/level/particle/SplashParticle.php ================================================ getDamage() << 8) | $b->getId()); } } ================================================ FILE: src/pocketmine/level/particle/WaterDripParticle.php ================================================ data = $b->getId(); } public function encode(){ $pk = new LevelEventPacket; $pk->evid = $this->id; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->data = $this->data; return $pk; } } ================================================ FILE: src/pocketmine/level/sound/ButtonClickSound.php ================================================ x, $pos->y, $pos->z); $this->id = (int) $id; $this->pitch = (float) $pitch * 1000; } protected $pitch = 0; protected $id; public function getPitch(){ return $this->pitch / 1000; } public function setPitch($pitch){ $this->pitch = (float) $pitch * 1000; } public function encode(){ $pk = new LevelEventPacket; $pk->evid = $this->id; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->data = (int) $this->pitch; return $pk; } } ================================================ FILE: src/pocketmine/level/sound/GhastShootSound.php ================================================ instrument = $instrument; $this->pitch = $pitch; } public function encode(){ $pk = new BlockEventPacket(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->case1 = $this->instrument; $pk->case2 = $this->pitch; return $pk; } } ================================================ FILE: src/pocketmine/level/sound/PopSound.php ================================================ x, $pos->y, $pos->z); $this->id = (int) LevelEventPacket::EVENT_SOUND_SPELL; $this->color = ($r << 16 | $g << 8 | $b) & 0xffffff; } public function encode(){ $pk = new LevelEventPacket; $pk->evid = $this->id; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->data = $this->color; return $pk; } } ================================================ FILE: src/pocketmine/level/sound/SplashSound.php ================================================ level = $level; $this->weatherNow = self::SUNNY; $this->duration = $duration; $this->lastUpdate = $level->getServer()->getTick(); $this->temporalVector = new Vector3(0, 0, 0); } public function canCalculate() : bool{ return $this->canCalculate; } public function setCanCalculate(bool $canCalc){ $this->canCalculate = $canCalc; } public function calcWeather($currentTick){ if($this->canCalculate()){ $tickDiff = $currentTick - $this->lastUpdate; $this->duration -= $tickDiff; if($this->duration <= 0){ $duration = mt_rand( min($this->level->getServer()->weatherRandomDurationMin, $this->level->getServer()->weatherRandomDurationMax), max($this->level->getServer()->weatherRandomDurationMin, $this->level->getServer()->weatherRandomDurationMax)); if($this->weatherNow === self::SUNNY){ $weather = $this->randomWeatherData[array_rand($this->randomWeatherData)]; $this->setWeather($weather, $duration); }else{ $weather = self::SUNNY; $this->setWeather($weather, $duration); } } if(($this->weatherNow >= self::RAINY_THUNDER) and ($this->level->getServer()->lightningTime > 0) and is_int($this->duration / $this->level->getServer()->lightningTime)){ $players = $this->level->getPlayers(); if(count($players) > 0){ $p = $players[array_rand($players)]; $x = $p->x + mt_rand(-64, 64); $z = $p->z + mt_rand(-64, 64); $y = $this->level->getHighestBlockAt($x, $z); $this->level->spawnLightning($this->temporalVector->setComponents($x, $y, $z)); } } } $this->lastUpdate = $currentTick; } public function setWeather(int $wea, int $duration = 12000){ $this->level->getServer()->getPluginManager()->callEvent($ev = new WeatherChangeEvent($this->level, $wea, $duration)); if(!$ev->isCancelled()){ $this->weatherNow = $ev->getWeather(); $this->strength1 = mt_rand(90000, 110000); //If we're clearing the weather, it doesn't matter what strength values we set $this->strength2 = mt_rand(30000, 40000); $this->duration = $ev->getDuration(); $this->sendWeatherToAll(); } } public function getRandomWeatherData() : array{ return $this->randomWeatherData; } public function setRandomWeatherData(array $randomWeatherData){ $this->randomWeatherData = $randomWeatherData; } public function getWeather() : int{ return $this->weatherNow; } public static function getWeatherFromString($weather){ if(is_int($weather)){ if($weather <= 3){ return $weather; } return self::SUNNY; } switch(strtolower($weather)){ case "clear": case "sunny": case "fine": return self::SUNNY; case "rain": case "rainy": return self::RAINY; case "thunder": return self::THUNDER; case "rain_thunder": case "rainy_thunder": case "storm": return self::RAINY_THUNDER; default: return self::SUNNY; } } /** * @return bool */ public function isSunny() : bool{ return $this->getWeather() === self::SUNNY; } /** * @return bool */ public function isRainy() : bool{ return $this->getWeather() === self::RAINY; } /** * @return bool */ public function isRainyThunder() : bool{ return $this->getWeather() === self::RAINY_THUNDER; } /** * @return bool */ public function isThunder() : bool{ return $this->getWeather() === self::THUNDER; } public function getStrength() : array{ return [$this->strength1, $this->strength2]; } public function sendWeather(Player $p){ $pks = [ new LevelEventPacket(), new LevelEventPacket() ]; //Set defaults. These will be sent if the case statement defaults. $pks[0]->evid = LevelEventPacket::EVENT_STOP_RAIN; $pks[0]->data = $this->strength1; $pks[1]->evid = LevelEventPacket::EVENT_STOP_THUNDER; $pks[1]->data = $this->strength2; switch($this->weatherNow){ //If the weather is not clear, overwrite the packet values with these case self::RAIN: $pks[0]->evid = LevelEventPacket::EVENT_START_RAIN; $pks[0]->data = $this->strength1; break; case self::RAINY_THUNDER: $pks[0]->evid = LevelEventPacket::EVENT_START_RAIN; $pks[0]->data = $this->strength1; $pks[1]->evid = LevelEventPacket::EVENT_START_THUNDER; $pks[1]->data = $this->strength2; break; case self::THUNDER: $pks[1]->evid = LevelEventPacket::EVENT_START_THUNDER; $pks[1]->data = $this->strength2; break; default: break; } foreach($pks as $pk){ $p->dataPacket($pk); } $p->weatherData = [$this->weatherNow, $this->strength1, $this->strength2]; } public function sendWeatherToAll(){ foreach($this->level->getPlayers() as $player){ $this->sendWeather($player); } } } ================================================ FILE: src/pocketmine/math/AxisAlignedBB.php ================================================ minX = $minX; $this->minY = $minY; $this->minZ = $minZ; $this->maxX = $maxX; $this->maxY = $maxY; $this->maxZ = $maxZ; } public function setBounds($minX, $minY, $minZ, $maxX, $maxY, $maxZ){ $this->minX = $minX; $this->minY = $minY; $this->minZ = $minZ; $this->maxX = $maxX; $this->maxY = $maxY; $this->maxZ = $maxZ; return $this; } public function addCoord($x, $y, $z){ $minX = $this->minX; $minY = $this->minY; $minZ = $this->minZ; $maxX = $this->maxX; $maxY = $this->maxY; $maxZ = $this->maxZ; if($x < 0){ $minX += $x; }elseif($x > 0){ $maxX += $x; } if($y < 0){ $minY += $y; }elseif($y > 0){ $maxY += $y; } if($z < 0){ $minZ += $z; }elseif($z > 0){ $maxZ += $z; } return new AxisAlignedBB($minX, $minY, $minZ, $maxX, $maxY, $maxZ); } public function grow($x, $y, $z){ return new AxisAlignedBB($this->minX - $x, $this->minY - $y, $this->minZ - $z, $this->maxX + $x, $this->maxY + $y, $this->maxZ + $z); } public function expand($x, $y, $z){ $this->minX -= $x; $this->minY -= $y; $this->minZ -= $z; $this->maxX += $x; $this->maxY += $y; $this->maxZ += $z; return $this; } public function offset($x, $y, $z){ $this->minX += $x; $this->minY += $y; $this->minZ += $z; $this->maxX += $x; $this->maxY += $y; $this->maxZ += $z; return $this; } public function shrink($x, $y, $z){ return new AxisAlignedBB($this->minX + $x, $this->minY + $y, $this->minZ + $z, $this->maxX - $x, $this->maxY - $y, $this->maxZ - $z); } public function contract($x, $y, $z){ $this->minX += $x; $this->minY += $y; $this->minZ += $z; $this->maxX -= $x; $this->maxY -= $y; $this->maxZ -= $z; return $this; } public function setBB(AxisAlignedBB $bb){ $this->minX = $bb->minX; $this->minY = $bb->minY; $this->minZ = $bb->minZ; $this->maxX = $bb->maxX; $this->maxY = $bb->maxY; $this->maxZ = $bb->maxZ; return $this; } public function getOffsetBoundingBox($x, $y, $z){ return new AxisAlignedBB($this->minX + $x, $this->minY + $y, $this->minZ + $z, $this->maxX + $x, $this->maxY + $y, $this->maxZ + $z); } public function calculateXOffset(AxisAlignedBB $bb, $x){ if($bb->maxY <= $this->minY or $bb->minY >= $this->maxY){ return $x; } if($bb->maxZ <= $this->minZ or $bb->minZ >= $this->maxZ){ return $x; } if($x > 0 and $bb->maxX <= $this->minX){ $x1 = $this->minX - $bb->maxX; if($x1 < $x){ $x = $x1; } } if($x < 0 and $bb->minX >= $this->maxX){ $x2 = $this->maxX - $bb->minX; if($x2 > $x){ $x = $x2; } } return $x; } public function calculateYOffset(AxisAlignedBB $bb, $y){ if($bb->maxX <= $this->minX or $bb->minX >= $this->maxX){ return $y; } if($bb->maxZ <= $this->minZ or $bb->minZ >= $this->maxZ){ return $y; } if($y > 0 and $bb->maxY <= $this->minY){ $y1 = $this->minY - $bb->maxY; if($y1 < $y){ $y = $y1; } } if($y < 0 and $bb->minY >= $this->maxY){ $y2 = $this->maxY - $bb->minY; if($y2 > $y){ $y = $y2; } } return $y; } public function calculateZOffset(AxisAlignedBB $bb, $z){ if($bb->maxX <= $this->minX or $bb->minX >= $this->maxX){ return $z; } if($bb->maxY <= $this->minY or $bb->minY >= $this->maxY){ return $z; } if($z > 0 and $bb->maxZ <= $this->minZ){ $z1 = $this->minZ - $bb->maxZ; if($z1 < $z){ $z = $z1; } } if($z < 0 and $bb->minZ >= $this->maxZ){ $z2 = $this->maxZ - $bb->minZ; if($z2 > $z){ $z = $z2; } } return $z; } public function intersectsWith(AxisAlignedBB $bb){ if($bb->maxX > $this->minX and $bb->minX < $this->maxX){ if($bb->maxY > $this->minY and $bb->minY < $this->maxY){ return $bb->maxZ > $this->minZ and $bb->minZ < $this->maxZ; } } return false; } public function isVectorInside(Vector3 $vector){ if($vector->x <= $this->minX or $vector->x >= $this->maxX){ return false; } if($vector->y <= $this->minY or $vector->y >= $this->maxY){ return false; } return $vector->z > $this->minZ and $vector->z < $this->maxZ; } public function getAverageEdgeLength(){ return ($this->maxX - $this->minX + $this->maxY - $this->minY + $this->maxZ - $this->minZ) / 3; } public function isVectorInYZ(Vector3 $vector){ return $vector->y >= $this->minY and $vector->y <= $this->maxY and $vector->z >= $this->minZ and $vector->z <= $this->maxZ; } public function isVectorInXZ(Vector3 $vector){ return $vector->x >= $this->minX and $vector->x <= $this->maxX and $vector->z >= $this->minZ and $vector->z <= $this->maxZ; } public function isVectorInXY(Vector3 $vector){ return $vector->x >= $this->minX and $vector->x <= $this->maxX and $vector->y >= $this->minY and $vector->y <= $this->maxY; } public function calculateIntercept(Vector3 $pos1, Vector3 $pos2){ $v1 = $pos1->getIntermediateWithXValue($pos2, $this->minX); $v2 = $pos1->getIntermediateWithXValue($pos2, $this->maxX); $v3 = $pos1->getIntermediateWithYValue($pos2, $this->minY); $v4 = $pos1->getIntermediateWithYValue($pos2, $this->maxY); $v5 = $pos1->getIntermediateWithZValue($pos2, $this->minZ); $v6 = $pos1->getIntermediateWithZValue($pos2, $this->maxZ); if($v1 !== null and !$this->isVectorInYZ($v1)){ $v1 = null; } if($v2 !== null and !$this->isVectorInYZ($v2)){ $v2 = null; } if($v3 !== null and !$this->isVectorInXZ($v3)){ $v3 = null; } if($v4 !== null and !$this->isVectorInXZ($v4)){ $v4 = null; } if($v5 !== null and !$this->isVectorInXY($v5)){ $v5 = null; } if($v6 !== null and !$this->isVectorInXY($v6)){ $v6 = null; } $vector = null; if($v1 !== null and ($vector === null or $pos1->distanceSquared($v1) < $pos1->distanceSquared($vector))){ $vector = $v1; } if($v2 !== null and ($vector === null or $pos1->distanceSquared($v2) < $pos1->distanceSquared($vector))){ $vector = $v2; } if($v3 !== null and ($vector === null or $pos1->distanceSquared($v3) < $pos1->distanceSquared($vector))){ $vector = $v3; } if($v4 !== null and ($vector === null or $pos1->distanceSquared($v4) < $pos1->distanceSquared($vector))){ $vector = $v4; } if($v5 !== null and ($vector === null or $pos1->distanceSquared($v5) < $pos1->distanceSquared($vector))){ $vector = $v5; } if($v6 !== null and ($vector === null or $pos1->distanceSquared($v6) < $pos1->distanceSquared($vector))){ $vector = $v6; } if($vector === null){ return null; } $f = -1; if($vector === $v1){ $f = 4; }elseif($vector === $v2){ $f = 5; }elseif($vector === $v3){ $f = 0; }elseif($vector === $v4){ $f = 1; }elseif($vector === $v5){ $f = 2; }elseif($vector === $v6){ $f = 3; } return MovingObjectPosition::fromBlock(0, 0, 0, $f, $vector); } public function __toString(){ return "AxisAlignedBB({$this->minX}, {$this->minY}, {$this->minZ}, {$this->maxX}, {$this->maxY}, {$this->maxZ})"; } } ================================================ FILE: src/pocketmine/math/Math.php ================================================ = $i ? $i : $i - 1; } public static function ceilFloat($n){ $i = (int) ($n + 1); return $n >= $i ? $i : $i - 1; } public static function clamp($value, $low, $high){ return min($high, max($low, $value)); } public static function solveQuadratic($a, $b, $c): array{ $x[0] = (-$b + sqrt($b ** 2 - 4 * $a * $c)) / (2 * $a); $x[1] = (-$b - sqrt($b ** 2 - 4 * $a * $c)) / (2 * $a); if($x[0] == $x[1]){ return [$x[0]]; } return $x; } } ================================================ FILE: src/pocketmine/math/Matrix.php ================================================ matrix[(int) $offset]); } public function offsetGet($offset){ return $this->matrix[(int) $offset]; } public function offsetSet($offset, $value){ $this->matrix[(int) $offset] = $value; } public function offsetUnset($offset){ unset($this->matrix[(int) $offset]); } public function __construct($rows, $columns, array $set = []){ $this->rows = max(1, (int) $rows); $this->columns = max(1, (int) $columns); $this->set($set); } public function set(array $m){ for($r = 0; $r < $this->rows; ++$r){ $this->matrix[$r] = []; for($c = 0; $c < $this->columns; ++$c){ $this->matrix[$r][$c] = isset($m[$r][$c]) ? $m[$r][$c] : 0; } } } public function getRows(){ return ($this->rows); } public function getColumns(){ return ($this->columns); } public function setElement($row, $column, $value){ if($row > $this->rows or $row < 0 or $column > $this->columns or $column < 0){ return false; } $this->matrix[(int) $row][(int) $column] = $value; return true; } public function getElement($row, $column){ if($row > $this->rows or $row < 0 or $column > $this->columns or $column < 0){ return false; } return $this->matrix[(int) $row][(int) $column]; } public function isSquare(){ return $this->rows === $this->columns; } public function add(Matrix $matrix){ if($this->rows !== $matrix->getRows() or $this->columns !== $matrix->getColumns()){ return false; } $result = new Matrix($this->rows, $this->columns); for($r = 0; $r < $this->rows; ++$r){ for($c = 0; $c < $this->columns; ++$c){ $result->setElement($r, $c, $this->matrix[$r][$c] + $matrix->getElement($r, $c)); } } return $result; } public function substract(Matrix $matrix){ if($this->rows !== $matrix->getRows() or $this->columns !== $matrix->getColumns()){ return false; } $result = clone $this; for($r = 0; $r < $this->rows; ++$r){ for($c = 0; $c < $this->columns; ++$c){ $result->setElement($r, $c, $this->matrix[$r][$c] - $matrix->getElement($r, $c)); } } return $result; } public function multiplyScalar($number){ $result = clone $this; for($r = 0; $r < $this->rows; ++$r){ for($c = 0; $c < $this->columns; ++$c){ $result->setElement($r, $c, $this->matrix[$r][$c] * $number); } } return $result; } public function divideScalar($number){ $result = clone $this; for($r = 0; $r < $this->rows; ++$r){ for($c = 0; $c < $this->columns; ++$c){ $result->setElement($r, $c, $this->matrix[$r][$c] / $number); } } return $result; } public function transpose(){ $result = new Matrix($this->columns, $this->rows); for($r = 0; $r < $this->rows; ++$r){ for($c = 0; $c < $this->columns; ++$c){ $result->setElement($c, $r, $this->matrix[$r][$c]); } } return $result; } //Naive Matrix product, O(n^3) public function product(Matrix $matrix){ if($this->columns !== $matrix->getRows()){ return false; } $c = $matrix->getColumns(); $result = new Matrix($this->rows, $c); for($i = 0; $i < $this->rows; ++$i){ for($j = 0; $j < $c; ++$j){ $sum = 0; for($k = 0; $k < $this->columns; ++$k){ $sum += $this->matrix[$i][$k] * $matrix->getElement($k, $j); } $result->setElement($i, $j, $sum); } } return $result; } //Computation of the determinant of 2x2 and 3x3 matrices public function determinant(){ if($this->isSquare() !== true){ return false; } switch($this->rows){ case 1: return 0; case 2: return $this->matrix[0][0] * $this->matrix[1][1] - $this->matrix[0][1] * $this->matrix[1][0]; case 3: return $this->matrix[0][0] * $this->matrix[1][1] * $this->matrix[2][2] + $this->matrix[0][1] * $this->matrix[1][2] * $this->matrix[2][0] + $this->matrix[0][2] * $this->matrix[1][0] * $this->matrix[2][1] - $this->matrix[2][0] * $this->matrix[1][1] * $this->matrix[0][2] - $this->matrix[2][1] * $this->matrix[1][2] * $this->matrix[0][0] - $this->matrix[2][2] * $this->matrix[1][0] * $this->matrix[0][1]; } return false; } public function __toString(){ $s = ""; for($r = 0; $r < $this->rows; ++$r){ $s .= implode(",", $this->matrix[$r]) . ";"; } return "Matrix({$this->rows}x{$this->columns};" . substr($s, 0, -1) . ")"; } } ================================================ FILE: src/pocketmine/math/Vector2.php ================================================ x = $x; $this->y = $y; } public function getX(){ return $this->x; } public function getY(){ return $this->y; } public function getFloorX(){ return (int) $this->x; } public function getFloorY(){ return (int) $this->y; } public function add($x, $y = 0){ if($x instanceof Vector2){ return $this->add($x->x, $x->y); }else{ return new Vector2($this->x + $x, $this->y + $y); } } public function subtract($x, $y = 0){ if($x instanceof Vector2){ return $this->add(-$x->x, -$x->y); }else{ return $this->add(-$x, -$y); } } public function ceil(){ return new Vector2((int) ($this->x + 1), (int) ($this->y + 1)); } public function floor(){ return new Vector2((int) $this->x, (int) $this->y); } public function round(){ return new Vector2(round($this->x), round($this->y)); } public function abs(){ return new Vector2(abs($this->x), abs($this->y)); } public function multiply($number){ return new Vector2($this->x * $number, $this->y * $number); } public function divide($number){ return new Vector2($this->x / $number, $this->y / $number); } public function distance($x, $y = 0){ if($x instanceof Vector2){ return sqrt($this->distanceSquared($x->x, $x->y)); }else{ return sqrt($this->distanceSquared($x, $y)); } } public function distanceSquared($x, $y = 0){ if($x instanceof Vector2){ return $this->distanceSquared($x->x, $x->y); }else{ return pow($this->x - $x, 2) + pow($this->y - $y, 2); } } public function length(){ return sqrt($this->lengthSquared()); } public function lengthSquared(){ return $this->x * $this->x + $this->y * $this->y; } public function normalize(){ $len = $this->lengthSquared(); if($len != 0){ return $this->divide(sqrt($len)); } return new Vector2(0, 0); } public function dot(Vector2 $v){ return $this->x * $v->x + $this->y * $v->y; } public function __toString(){ return "Vector2(x=" . $this->x . ",y=" . $this->y . ")"; } public static function createRandomDirection(Random $random){ return VectorMath::getDirection2D($random->nextFloat() * 2 * pi()); } } ================================================ FILE: src/pocketmine/math/Vector3.php ================================================ x = $x; $this->y = $y; $this->z = $z; } public function getX(){ return $this->x; } public function getY(){ return $this->y; } public function getZ(){ return $this->z; } public function getFloorX(){ return (int) floor($this->x); } public function getFloorY(){ return (int) floor($this->y); } public function getFloorZ(){ return (int) floor($this->z); } public function getRight(){ return $this->x; } public function getUp(){ return $this->y; } public function getForward(){ return $this->z; } public function getSouth(){ return $this->x; } public function getWest(){ return $this->z; } /** * @param Vector3|int $x * @param int $y * @param int $z * * @return Vector3 */ public function add($x, $y = 0, $z = 0){ if($x instanceof Vector3){ return new Vector3($this->x + $x->x, $this->y + $x->y, $this->z + $x->z); }else{ return new Vector3($this->x + $x, $this->y + $y, $this->z + $z); } } /** * @param Vector3|int $x * @param int $y * @param int $z * * @return Vector3 */ public function subtract($x = 0, $y = 0, $z = 0){ if($x instanceof Vector3){ return $this->add(-$x->x, -$x->y, -$x->z); }else{ return $this->add(-$x, -$y, -$z); } } public function multiply($number){ return new Vector3($this->x * $number, $this->y * $number, $this->z * $number); } public function divide($number){ return new Vector3($this->x / $number, $this->y / $number, $this->z / $number); } public function ceil(){ return new Vector3((int) ceil($this->x), (int) ceil($this->y), (int) ceil($this->z)); } public function floor(){ return new Vector3((int) floor($this->x), (int) floor($this->y), (int) floor($this->z)); } public function round(){ return new Vector3((int) round($this->x), (int) round($this->y), (int) round($this->z)); } public function abs(){ return new Vector3(abs($this->x), abs($this->y), abs($this->z)); } public function getSide($side, $step = 1){ switch((int) $side){ case Vector3::SIDE_DOWN: return new Vector3($this->x, $this->y - $step, $this->z); case Vector3::SIDE_UP: return new Vector3($this->x, $this->y + $step, $this->z); case Vector3::SIDE_NORTH: return new Vector3($this->x, $this->y, $this->z - $step); case Vector3::SIDE_SOUTH: return new Vector3($this->x, $this->y, $this->z + $step); case Vector3::SIDE_WEST: return new Vector3($this->x - $step, $this->y, $this->z); case Vector3::SIDE_EAST: return new Vector3($this->x + $step, $this->y, $this->z); default: return $this; } } public static function getOppositeSide($side){ switch((int) $side){ case Vector3::SIDE_DOWN: return Vector3::SIDE_UP; case Vector3::SIDE_UP: return Vector3::SIDE_DOWN; case Vector3::SIDE_NORTH: return Vector3::SIDE_SOUTH; case Vector3::SIDE_SOUTH: return Vector3::SIDE_NORTH; case Vector3::SIDE_WEST: return Vector3::SIDE_EAST; case Vector3::SIDE_EAST: return Vector3::SIDE_WEST; default: return -1; } } public function distance(Vector3 $pos){ return sqrt($this->distanceSquared($pos)); } public function distanceSquared(Vector3 $pos){ return pow($this->x - $pos->x, 2) + pow($this->y - $pos->y, 2) + pow($this->z - $pos->z, 2); } public function maxPlainDistance($x = 0, $z = 0){ if($x instanceof Vector3){ return $this->maxPlainDistance($x->x, $x->z); }elseif($x instanceof Vector2){ return $this->maxPlainDistance($x->x, $x->y); }else{ return max(abs($this->x - $x), abs($this->z - $z)); } } public function length(){ return sqrt($this->lengthSquared()); } public function lengthSquared(){ return $this->x * $this->x + $this->y * $this->y + $this->z * $this->z; } /** * @return Vector3 */ public function normalize(){ $len = $this->lengthSquared(); if($len > 0){ return $this->divide(sqrt($len)); } return new Vector3(0, 0, 0); } public function dot(Vector3 $v){ return $this->x * $v->x + $this->y * $v->y + $this->z * $v->z; } public function cross(Vector3 $v){ return new Vector3( $this->y * $v->z - $this->z * $v->y, $this->z * $v->x - $this->x * $v->z, $this->x * $v->y - $this->y * $v->x ); } public function equals(Vector3 $v){ return $this->x == $v->x and $this->y == $v->y and $this->z == $v->z; } /** * Returns a new vector with x value equal to the second parameter, along the line between this vector and the * passed in vector, or null if not possible. * * @param Vector3 $v * @param float $x * * @return Vector3 */ public function getIntermediateWithXValue(Vector3 $v, $x){ $xDiff = $v->x - $this->x; $yDiff = $v->y - $this->y; $zDiff = $v->z - $this->z; if(($xDiff * $xDiff) < 0.0000001){ return null; } $f = ($x - $this->x) / $xDiff; if($f < 0 or $f > 1){ return null; }else{ return new Vector3($this->x + $xDiff * $f, $this->y + $yDiff * $f, $this->z + $zDiff * $f); } } /** * Returns a new vector with y value equal to the second parameter, along the line between this vector and the * passed in vector, or null if not possible. * * @param Vector3 $v * @param float $y * * @return Vector3 */ public function getIntermediateWithYValue(Vector3 $v, $y){ $xDiff = $v->x - $this->x; $yDiff = $v->y - $this->y; $zDiff = $v->z - $this->z; if(($yDiff * $yDiff) < 0.0000001){ return null; } $f = ($y - $this->y) / $yDiff; if($f < 0 or $f > 1){ return null; }else{ return new Vector3($this->x + $xDiff * $f, $this->y + $yDiff * $f, $this->z + $zDiff * $f); } } /** * Returns a new vector with z value equal to the second parameter, along the line between this vector and the * passed in vector, or null if not possible. * * @param Vector3 $v * @param float $z * * @return Vector3 */ public function getIntermediateWithZValue(Vector3 $v, $z){ $xDiff = $v->x - $this->x; $yDiff = $v->y - $this->y; $zDiff = $v->z - $this->z; if(($zDiff * $zDiff) < 0.0000001){ return null; } $f = ($z - $this->z) / $zDiff; if($f < 0 or $f > 1){ return null; }else{ return new Vector3($this->x + $xDiff * $f, $this->y + $yDiff * $f, $this->z + $zDiff * $f); } } /** * @param $x * @param $y * @param $z * * @return Vector3 */ public function setComponents($x, $y, $z){ $this->x = $x; $this->y = $y; $this->z = $z; return $this; } /** * @param Vector3 $pos * @param $x * @param $y * @param $z * * @return $this */ public function fromObjectAdd(Vector3 $pos, $x, $y, $z){ $this->x = $pos->x + $x; $this->y = $pos->y + $y; $this->z = $pos->z + $z; return $this; } public function __toString(){ return "Vector3(x=" . $this->x . ",y=" . $this->y . ",z=" . $this->z . ")"; } public static function createRandomDirection(Random $random){ return VectorMath::getDirection3D($random->nextFloat() * 2 * pi(), $random->nextFloat() * 2 * pi()); } } ================================================ FILE: src/pocketmine/math/VectorMath.php ================================================ owningLevel = $owningLevel; } public function disambiguate(Metadatable $block, $metadataKey){ if(!($block instanceof Block)){ throw new \InvalidArgumentException("Argument must be a Block instance"); } return $block->x . ":" . $block->y . ":" . $block->z . ":" . $metadataKey; } public function getMetadata($block, $metadataKey){ if(!($block instanceof Block)){ throw new \InvalidArgumentException("Object must be a Block"); } if($block->getLevel() === $this->owningLevel){ return parent::getMetadata($block, $metadataKey); }else{ throw new \InvalidStateException("Block does not belong to world " . $this->owningLevel->getName()); } } public function hasMetadata($block, $metadataKey){ if(!($block instanceof Block)){ throw new \InvalidArgumentException("Object must be a Block"); } if($block->getLevel() === $this->owningLevel){ return parent::hasMetadata($block, $metadataKey); }else{ throw new \InvalidStateException("Block does not belong to world " . $this->owningLevel->getName()); } } public function removeMetadata($block, $metadataKey, Plugin $owningPlugin){ if(!($block instanceof Block)){ throw new \InvalidArgumentException("Object must be a Block"); } if($block->getLevel() === $this->owningLevel){ parent::hasMetadata($block, $metadataKey); }else{ throw new \InvalidStateException("Block does not belong to world " . $this->owningLevel->getName()); } } public function setMetadata($block, $metadataKey, MetadataValue $newMetadatavalue){ if(!($block instanceof Block)){ throw new \InvalidArgumentException("Object must be a Block"); } if($block->getLevel() === $this->owningLevel){ parent::setMetadata($block, $metadataKey, $newMetadatavalue); }else{ throw new \InvalidStateException("Block does not belong to world " . $this->owningLevel->getName()); } } } ================================================ FILE: src/pocketmine/metadata/EntityMetadataStore.php ================================================ getId() . ":" . $metadataKey; } } ================================================ FILE: src/pocketmine/metadata/LevelMetadataStore.php ================================================ getName()) . ":" . $metadataKey; } } ================================================ FILE: src/pocketmine/metadata/MetadataStore.php ================================================ getOwningPlugin(); if($owningPlugin === null){ throw new PluginException("Plugin cannot be null"); } $key = $this->disambiguate($subject, $metadataKey); if(!isset($this->metadataMap[$key])){ //$entry = new \WeakMap(); $this->metadataMap[$key] = new \SplObjectStorage();//$entry; }else{ $entry = $this->metadataMap[$key]; } $entry[$owningPlugin] = $newMetadataValue; } /** * Returns all metadata values attached to an object. If multiple * have attached metadata, each will value will be included. * * @param mixed $subject * @param string $metadataKey * * @return MetadataValue[] * * @throws \Exception */ public function getMetadata($subject, $metadataKey){ $key = $this->disambiguate($subject, $metadataKey); if(isset($this->metadataMap[$key])){ return $this->metadataMap[$key]; }else{ return []; } } /** * Tests to see if a metadata attribute has been set on an object. * * @param mixed $subject * @param string $metadataKey * * @return bool * * @throws \Exception */ public function hasMetadata($subject, $metadataKey){ return isset($this->metadataMap[$this->disambiguate($subject, $metadataKey)]); } /** * Removes a metadata item owned by a plugin from a subject. * * @param mixed $subject * @param string $metadataKey * @param Plugin $owningPlugin * * @throws \Exception */ public function removeMetadata($subject, $metadataKey, Plugin $owningPlugin){ $key = $this->disambiguate($subject, $metadataKey); if(isset($this->metadataMap[$key])){ unset($this->metadataMap[$key][$owningPlugin]); if($this->metadataMap[$key]->count() === 0){ unset($this->metadataMap[$key]); } } } /** * Invalidates all metadata in the metadata store that originates from the * given plugin. Doing this will force each invalidated metadata item to * be recalculated the next time it is accessed. * * @param Plugin $owningPlugin */ public function invalidateAll(Plugin $owningPlugin){ /** @var $values MetadataValue[] */ foreach($this->metadataMap as $values){ if(isset($values[$owningPlugin])){ $values[$owningPlugin]->invalidate(); } } } /** * Creates a unique name for the object receiving metadata by combining * unique data from the subject with a metadataKey. * * @param Metadatable $subject * @param string $metadataKey * * @return string * * @throws \InvalidArgumentException */ public abstract function disambiguate(Metadatable $subject, $metadataKey); } ================================================ FILE: src/pocketmine/metadata/MetadataValue.php ================================================ */ protected $owningPlugin; protected function __construct(Plugin $owningPlugin){ $this->owningPlugin = new \WeakRef($owningPlugin); } /** * @return Plugin */ public function getOwningPlugin(){ return $this->owningPlugin->get(); } /** * Fetches the value of this metadata item. * * @return mixed */ public abstract function value(); /** * Invalidates this metadata item, forcing it to recompute when next * accessed. */ public abstract function invalidate(); } ================================================ FILE: src/pocketmine/metadata/Metadatable.php ================================================ getName()) . ":" . $metadataKey; } } ================================================ FILE: src/pocketmine/nbt/NBT.php ================================================ /** * Named Binary Tag encoder/decoder */ class NBT{ const LITTLE_ENDIAN = 0; const BIG_ENDIAN = 1; const TAG_End = 0; const TAG_Byte = 1; const TAG_Short = 2; const TAG_Int = 3; const TAG_Long = 4; const TAG_Float = 5; const TAG_Double = 6; const TAG_ByteArray = 7; const TAG_String = 8; const TAG_List = 9; const TAG_Compound = 10; const TAG_IntArray = 11; public $buffer; private $offset; public $endianness; private $data; public static function matchList(ListTag $tag1, ListTag $tag2){ if($tag1->getName() !== $tag2->getName() or $tag1->getCount() !== $tag2->getCount()){ return false; } foreach($tag1 as $k => $v){ if(!($v instanceof Tag)){ continue; } if(!isset($tag2->{$k}) or !($tag2->{$k} instanceof $v)){ return false; } if($v instanceof CompoundTag){ if(!self::matchTree($v, $tag2->{$k})){ return false; } }elseif($v instanceof ListTag){ if(!self::matchList($v, $tag2->{$k})){ return false; } }else{ if($v->getValue() !== $tag2->{$k}->getValue()){ return false; } } } return true; } public static function matchTree(CompoundTag $tag1, CompoundTag $tag2){ if($tag1->getName() !== $tag2->getName() or $tag1->getCount() !== $tag2->getCount()){ return false; } foreach($tag1 as $k => $v){ if(!($v instanceof Tag)){ continue; } if(!isset($tag2->{$k}) or !($tag2->{$k} instanceof $v)){ return false; } if($v instanceof CompoundTag){ if(!self::matchTree($v, $tag2->{$k})){ return false; } }elseif($v instanceof ListTag){ if(!self::matchList($v, $tag2->{$k})){ return false; } }else{ if($v->getValue() !== $tag2->{$k}->getValue()){ return false; } } } return true; } public static function combineCompoundTags(CompoundTag $tag1, CompoundTag $tag2, bool $override = false) : CompoundTag{ $tag1 = clone $tag1; foreach($tag2 as $k => $v){ if(!($v instanceof Tag)){ continue; } if(!isset($tag1->{$k}) or (isset($tag1->{$k}) and $override)){ $tag1->{$k} = clone $v; } } return $tag1; } public static function parseJSON($data, &$offset = 0){ $len = strlen($data); for(; $offset < $len; ++$offset){ $c = $data{$offset}; if($c === "{"){ ++$offset; $data = self::parseCompound($data, $offset); return new CompoundTag("", $data); }elseif($c !== " " and $c !== "\r" and $c !== "\n" and $c !== "\t"){ throw new \Exception("Syntax error: unexpected '$c' at offset $offset"); } } return null; } private static function parseList($str, &$offset = 0){ $len = strlen($str); $key = 0; $value = null; $data = []; for(; $offset < $len; ++$offset){ if($str{$offset - 1} === "]"){ break; }elseif($str{$offset} === "]"){ ++$offset; break; } $value = self::readValue($str, $offset, $type); switch($type){ case NBT::TAG_Byte: $data[$key] = new ByteTag($key, $value); break; case NBT::TAG_Short: $data[$key] = new ShortTag($key, $value); break; case NBT::TAG_Int: $data[$key] = new IntTag($key, $value); break; case NBT::TAG_Long: $data[$key] = new LongTag($key, $value); break; case NBT::TAG_Float: $data[$key] = new FloatTag($key, $value); break; case NBT::TAG_Double: $data[$key] = new DoubleTag($key, $value); break; case NBT::TAG_ByteArray: $data[$key] = new ByteArrayTag($key, $value); break; case NBT::TAG_String: $data[$key] = new StringTag($key, $value); break; case NBT::TAG_List: $data[$key] = new ListTag($key, $value); break; case NBT::TAG_Compound: $data[$key] = new CompoundTag($key, $value); break; case NBT::TAG_IntArray: $data[$key] = new IntArrayTag($key, $value); break; } $key++; } return $data; } private static function parseCompound($str, &$offset = 0){ $len = strlen($str); $data = []; for(; $offset < $len; ++$offset){ if($str{$offset - 1} === "}"){ break; }elseif($str{$offset} === "}"){ ++$offset; break; } $key = self::readKey($str, $offset); $value = self::readValue($str, $offset, $type); switch($type){ case NBT::TAG_Byte: $data[$key] = new ByteTag($key, $value); break; case NBT::TAG_Short: $data[$key] = new ShortTag($key, $value); break; case NBT::TAG_Int: $data[$key] = new IntTag($key, $value); break; case NBT::TAG_Long: $data[$key] = new LongTag($key, $value); break; case NBT::TAG_Float: $data[$key] = new FloatTag($key, $value); break; case NBT::TAG_Double: $data[$key] = new DoubleTag($key, $value); break; case NBT::TAG_ByteArray: $data[$key] = new ByteArrayTag($key, $value); break; case NBT::TAG_String: $data[$key] = new StringTag($key, $value); break; case NBT::TAG_List: $data[$key] = new ListTag($key, $value); break; case NBT::TAG_Compound: $data[$key] = new CompoundTag($key, $value); break; case NBT::TAG_IntArray: $data[$key] = new IntArrayTag($key, $value); break; } } return $data; } private static function readValue($data, &$offset, &$type = null){ $value = ""; $type = null; $inQuotes = false; $len = strlen($data); for(; $offset < $len; ++$offset){ $c = $data{$offset}; if(!$inQuotes and ($c === " " or $c === "\r" or $c === "\n" or $c === "\t" or $c === "," or $c === "}" or $c === "]")){ if($c === "," or $c === "}" or $c === "]"){ break; } }elseif($c === '"'){ $inQuotes = !$inQuotes; if($type === null){ $type = self::TAG_String; }elseif($inQuotes){ throw new \Exception("Syntax error: invalid quote at offset $offset"); } }elseif($c === "\\"){ $value .= isset($data{$offset + 1}) ? $data{$offset + 1} : ""; ++$offset; }elseif($c === "{" and !$inQuotes){ if($value !== ""){ throw new \Exception("Syntax error: invalid compound start at offset $offset"); } ++$offset; $value = self::parseCompound($data, $offset); $type = self::TAG_Compound; break; }elseif($c === "[" and !$inQuotes){ if($value !== ""){ throw new \Exception("Syntax error: invalid list start at offset $offset"); } ++$offset; $value = self::parseList($data, $offset); $type = self::TAG_List; break; }else{ $value .= $c; } } if($value === ""){ throw new \Exception("Syntax error: invalid empty value at offset $offset"); } if($type === null and strlen($value) > 0){ $value = trim($value); $last = strtolower(substr($value, -1)); $part = substr($value, 0, -1); if($last !== "b" and $last !== "s" and $last !== "l" and $last !== "f" and $last !== "d"){ $part = $value; $last = null; } if($last !== "f" and $last !== "d" and ((string) ((int) $part)) === $part){ if($last === "b"){ $type = self::TAG_Byte; }elseif($last === "s"){ $type = self::TAG_Short; }elseif($last === "l"){ $type = self::TAG_Long; }else{ $type = self::TAG_Int; } $value = (int) $part; }elseif(is_numeric($part)){ if($last === "f" or $last === "d" or strpos($part, ".") !== false){ if($last === "f"){ $type = self::TAG_Float; }elseif($last === "d"){ $type = self::TAG_Double; }else{ $type = self::TAG_Float; } $value = (float) $part; }else{ if($last === "l"){ $type = self::TAG_Long; }else{ $type = self::TAG_Int; } $value = $part; } }else{ $type = self::TAG_String; } } return $value; } private static function readKey($data, &$offset){ $key = ""; $len = strlen($data); for(; $offset < $len; ++$offset){ $c = $data{$offset}; if($c === ":"){ ++$offset; break; }elseif($c !== " " and $c !== "\r" and $c !== "\n" and $c !== "\t" and $c !== "\""){ $key .= $c; } } if($key === ""){ throw new \Exception("Syntax error: invalid empty key at offset $offset"); } return $key; } public function get($len){ if($len < 0){ $this->offset = strlen($this->buffer) - 1; return ""; }elseif($len === true){ return substr($this->buffer, $this->offset); } return $len === 1 ? $this->buffer{$this->offset++} : substr($this->buffer, ($this->offset += $len) - $len, $len); } public function put($v){ $this->buffer .= $v; } public function feof(){ return !isset($this->buffer{$this->offset}); } public function __construct($endianness = self::LITTLE_ENDIAN){ $this->offset = 0; $this->endianness = $endianness & 0x01; } public function read($buffer, $doMultiple = false, bool $network = false){ $this->offset = 0; $this->buffer = $buffer; $this->data = $this->readTag($network); if($doMultiple and $this->offset < strlen($this->buffer)){ $this->data = [$this->data]; do{ $this->data[] = $this->readTag($network); }while($this->offset < strlen($this->buffer)); } $this->buffer = ""; } public function readCompressed($buffer, $compression = ZLIB_ENCODING_GZIP){ $this->read(zlib_decode($buffer)); } public function readNetworkCompressed($buffer, $compression = ZLIB_ENCODING_GZIP){ $this->read(zlib_decode($buffer), false, true); } /** * @param bool $network * * @return string|bool */ public function write(bool $network = false){ $this->offset = 0; $this->buffer = ""; if($this->data instanceof CompoundTag){ $this->writeTag($this->data, $network); return $this->buffer; }elseif(is_array($this->data)){ foreach($this->data as $tag){ $this->writeTag($tag, $network); } return $this->buffer; } return false; } public function writeCompressed($compression = ZLIB_ENCODING_GZIP, $level = 7){ if(($write = $this->write()) !== false){ return zlib_encode($write, $compression, $level); } return false; } public function writeNetworkCompressed($compression = ZLIB_ENCODING_GZIP, $level = 7){ if(($write = $this->write(true)) !== false){ return zlib_encode($write, $compression, $level); } return false; } public function readTag(bool $network = false){ if($this->feof()){ $tagType = -1; //prevent crashes for empty tags }else{ $tagType = $this->getByte(); } switch($tagType){ case NBT::TAG_Byte: $tag = new ByteTag($this->getString($network)); $tag->read($this, $network); break; case NBT::TAG_Short: $tag = new ShortTag($this->getString($network)); $tag->read($this, $network); break; case NBT::TAG_Int: $tag = new IntTag($this->getString($network)); $tag->read($this, $network); break; case NBT::TAG_Long: $tag = new LongTag($this->getString($network)); $tag->read($this, $network); break; case NBT::TAG_Float: $tag = new FloatTag($this->getString($network)); $tag->read($this, $network); break; case NBT::TAG_Double: $tag = new DoubleTag($this->getString($network)); $tag->read($this, $network); break; case NBT::TAG_ByteArray: $tag = new ByteArrayTag($this->getString($network)); $tag->read($this, $network); break; case NBT::TAG_String: $tag = new StringTag($this->getString($network)); $tag->read($this, $network); break; case NBT::TAG_List: $tag = new ListTag($this->getString($network)); $tag->read($this, $network); break; case NBT::TAG_Compound: $tag = new CompoundTag($this->getString($network)); $tag->read($this, $network); break; case NBT::TAG_IntArray: $tag = new IntArrayTag($this->getString($network)); $tag->read($this, $network); break; case NBT::TAG_End: //No named tag default: $tag = new EndTag; break; } return $tag; } public function writeTag(Tag $tag, bool $network = false){ $this->putByte($tag->getType()); if($tag instanceof NamedTAG){ $this->putString($tag->getName(), $network); } $tag->write($this, $network); } public function getByte(){ return Binary::readByte($this->get(1)); } public function putByte($v){ $this->buffer .= Binary::writeByte($v); } public function getShort(){ return $this->endianness === self::BIG_ENDIAN ? Binary::readShort($this->get(2)) : Binary::readLShort($this->get(2)); } public function putShort($v){ $this->buffer .= $this->endianness === self::BIG_ENDIAN ? Binary::writeShort($v) : Binary::writeLShort($v); } public function getInt(bool $network = false){ if($network === true){ return Binary::readVarInt($this); } return $this->endianness === self::BIG_ENDIAN ? Binary::readInt($this->get(4)) : Binary::readLInt($this->get(4)); } public function putInt($v, bool $network = false){ if($network === true){ $this->buffer .= Binary::writeVarInt($v); }else{ $this->buffer .= $this->endianness === self::BIG_ENDIAN ? Binary::writeInt($v) : Binary::writeLInt($v); } } public function getLong(){ return $this->endianness === self::BIG_ENDIAN ? Binary::readLong($this->get(8)) : Binary::readLLong($this->get(8)); } public function putLong($v){ $this->buffer .= $this->endianness === self::BIG_ENDIAN ? Binary::writeLong($v) : Binary::writeLLong($v); } public function getFloat(){ return $this->endianness === self::BIG_ENDIAN ? Binary::readFloat($this->get(4)) : Binary::readLFloat($this->get(4)); } public function putFloat($v){ $this->buffer .= $this->endianness === self::BIG_ENDIAN ? Binary::writeFloat($v) : Binary::writeLFloat($v); } public function getDouble(){ return $this->endianness === self::BIG_ENDIAN ? Binary::readDouble($this->get(8)) : Binary::readLDouble($this->get(8)); } public function putDouble($v){ $this->buffer .= $this->endianness === self::BIG_ENDIAN ? Binary::writeDouble($v) : Binary::writeLDouble($v); } public function getString(bool $network = false){ $len = $network ? $this->getByte() : $this->getShort(); return $this->get($len); } public function putString($v, bool $network = false){ if($network === true){ $this->putByte(strlen($v)); }else{ $this->putShort(strlen($v)); } $this->buffer .= $v; } public function getArray(){ $data = []; self::toArray($data, $this->data); } private static function toArray(array &$data, Tag $tag){ /** @var CompoundTag[]|ListTag[]|IntArrayTag[] $tag */ foreach($tag as $key => $value){ if($value instanceof CompoundTag or $value instanceof ListTag or $value instanceof IntArrayTag){ $data[$key] = []; self::toArray($data[$key], $value); }else{ $data[$key] = $value->getValue(); } } } public static function fromArrayGuesser($key, $value){ if(is_int($value)){ return new IntTag($key, $value); }elseif(is_float($value)){ return new FloatTag($key, $value); }elseif(is_string($value)){ return new StringTag($key, $value); }elseif(is_bool($value)){ return new ByteTag($key, $value ? 1 : 0); } return null; } private static function fromArray(Tag $tag, array $data, callable $guesser){ foreach($data as $key => $value){ if(is_array($value)){ $isNumeric = true; $isIntArray = true; foreach($value as $k => $v){ if(!is_numeric($k)){ $isNumeric = false; break; }elseif(!is_int($v)){ $isIntArray = false; } } $tag{$key} = $isNumeric ? ($isIntArray ? new IntArrayTag($key, []) : new ListTag($key, [])) : new CompoundTag($key, []); self::fromArray($tag->{$key}, $value, $guesser); }else{ $v = call_user_func($guesser, $key, $value); if($v instanceof Tag){ $tag{$key} = $v; } } } } public function setArray(array $data, callable $guesser = null){ $this->data = new CompoundTag("", []); self::fromArray($this->data, $data, $guesser === null ? [self::class, "fromArrayGuesser"] : $guesser); } /** * @return CompoundTag|array */ public function getData(){ return $this->data; } /** * @param CompoundTag|array $data */ public function setData($data){ $this->data = $data; } } ================================================ FILE: src/pocketmine/nbt/tag/ByteArrayTag.php ================================================ class ByteArrayTag extends NamedTag{ public function getType(){ return NBT::TAG_ByteArray; } public function read(NBT $nbt, bool $network = false){ $this->value = $nbt->get($nbt->getInt($network)); } public function write(NBT $nbt, bool $network = false){ $nbt->putInt(strlen($this->value), $network); $nbt->put($this->value); } } ================================================ FILE: src/pocketmine/nbt/tag/ByteTag.php ================================================ class ByteTag extends NamedTag{ public function getType(){ return NBT::TAG_Byte; } public function read(NBT $nbt, bool $network = false){ $this->value = $nbt->getByte(); } public function write(NBT $nbt, bool $network = false){ $nbt->putByte($this->value); } } ================================================ FILE: src/pocketmine/nbt/tag/CompoundTag.php ================================================ class CompoundTag extends NamedTag implements \ArrayAccess{ /** * @param string $name * @param NamedTag[] $value */ public function __construct($name = "", $value = []){ $this->__name = $name; foreach($value as $tag){ $this->{$tag->getName()} = $tag; } } public function getCount(){ $count = 0; foreach($this as $tag){ if($tag instanceof Tag){ ++$count; } } return $count; } public function offsetExists($offset){ return isset($this->{$offset}) and $this->{$offset} instanceof Tag; } public function offsetGet($offset){ if(isset($this->{$offset}) and $this->{$offset} instanceof Tag){ if($this->{$offset} instanceof \ArrayAccess){ return $this->{$offset}; }else{ return $this->{$offset}->getValue(); } } return null; } public function offsetSet($offset, $value){ if($value instanceof Tag){ $this->{$offset} = $value; }elseif(isset($this->{$offset}) and $this->{$offset} instanceof Tag){ $this->{$offset}->setValue($value); } } public function offsetUnset($offset){ unset($this->{$offset}); } public function getType(){ return NBT::TAG_Compound; } public function read(NBT $nbt, bool $network = false){ $this->value = []; do{ $tag = $nbt->readTag($network); if($tag instanceof NamedTag and $tag->getName() !== ""){ $this->{$tag->getName()} = $tag; } }while(!($tag instanceof EndTag) and !$nbt->feof()); } public function write(NBT $nbt, bool $network = false){ foreach($this as $tag){ if($tag instanceof Tag and !($tag instanceof EndTag)){ $nbt->writeTag($tag, $network); } } $nbt->writeTag(new EndTag, $network); } public function __toString(){ $str = get_class($this) . "{\n"; foreach($this as $tag){ if($tag instanceof Tag){ $str .= get_class($tag) . ":" . $tag->__toString() . "\n"; } } return $str . "}"; } } ================================================ FILE: src/pocketmine/nbt/tag/DoubleTag.php ================================================ class DoubleTag extends NamedTag{ public function getType(){ return NBT::TAG_Double; } public function read(NBT $nbt, bool $network = false){ $this->value = $nbt->getDouble(); } public function write(NBT $nbt, bool $network = false){ $nbt->putDouble($this->value); } } ================================================ FILE: src/pocketmine/nbt/tag/EndTag.php ================================================ class FloatTag extends NamedTag{ public function getType(){ return NBT::TAG_Float; } public function read(NBT $nbt, bool $network = false){ $this->value = $nbt->getFloat(); } public function write(NBT $nbt, bool $network = false){ $nbt->putFloat($this->value); } } ================================================ FILE: src/pocketmine/nbt/tag/IntArrayTag.php ================================================ class IntArrayTag extends NamedTag{ public function getType(){ return NBT::TAG_IntArray; } public function read(NBT $nbt, bool $network = false){ $size = $nbt->getInt($network); $this->value = array_values(unpack($nbt->endianness === NBT::LITTLE_ENDIAN ? "V*" : "N*", $nbt->get($size * 4))); } public function write(NBT $nbt, bool $network = false){ $nbt->putInt(count($this->value), $network); $nbt->put(pack($nbt->endianness === NBT::LITTLE_ENDIAN ? "V*" : "N*", ...$this->value)); } public function __toString(){ $str = get_class($this) . "{\n"; $str .= implode(", ", $this->value); return $str . "}"; } } ================================================ FILE: src/pocketmine/nbt/tag/IntTag.php ================================================ class IntTag extends NamedTag{ public function getType(){ return NBT::TAG_Int; } public function read(NBT $nbt, bool $network = false){ $this->value = $nbt->getInt($network); } public function write(NBT $nbt, bool $network = false){ $nbt->putInt($this->value, $network); } } ================================================ FILE: src/pocketmine/nbt/tag/ListTag.php ================================================ class ListTag extends NamedTag implements \ArrayAccess, \Countable{ private $tagType; public function __construct($name = "", $value = []){ $this->__name = $name; foreach($value as $k => $v){ $this->{$k} = $v; } } public function &getValue(){ $value = []; foreach($this as $k => $v){ if($v instanceof Tag){ $value[$k] = $v; } } return $value; } public function getCount(){ $count = 0; foreach($this as $tag){ if($tag instanceof Tag){ ++$count; } } return $count; } public function offsetExists($offset){ return isset($this->{$offset}); } public function offsetGet($offset){ if(isset($this->{$offset}) and $this->{$offset} instanceof Tag){ if($this->{$offset} instanceof \ArrayAccess){ return $this->{$offset}; }else{ return $this->{$offset}->getValue(); } } return null; } public function offsetSet($offset, $value){ if($value instanceof Tag){ $this->{$offset} = $value; }elseif($this->{$offset} instanceof Tag){ $this->{$offset}->setValue($value); } } public function offsetUnset($offset){ unset($this->{$offset}); } public function count($mode = COUNT_NORMAL){ for($i = 0; true; $i++){ if(!isset($this->{$i})){ return $i; } if($mode === COUNT_RECURSIVE){ if($this->{$i} instanceof \Countable){ $i += count($this->{$i}); } } } return $i; } public function getType(){ return NBT::TAG_List; } public function setTagType($type){ $this->tagType = $type; } public function getTagType(){ return $this->tagType; } public function read(NBT $nbt, bool $network = false){ $this->value = []; $this->tagType = $nbt->getByte(); $size = $nbt->getInt($network); for($i = 0; $i < $size and !$nbt->feof(); ++$i){ switch($this->tagType){ case NBT::TAG_Byte: $tag = new ByteTag(""); $tag->read($nbt, $network); $this->{$i} = $tag; break; case NBT::TAG_Short: $tag = new ShortTag(""); $tag->read($nbt, $network); $this->{$i} = $tag; break; case NBT::TAG_Int: $tag = new IntTag(""); $tag->read($nbt, $network); $this->{$i} = $tag; break; case NBT::TAG_Long: $tag = new LongTag(""); $tag->read($nbt, $network); $this->{$i} = $tag; break; case NBT::TAG_Float: $tag = new FloatTag(""); $tag->read($nbt, $network); $this->{$i} = $tag; break; case NBT::TAG_Double: $tag = new DoubleTag(""); $tag->read($nbt, $network); $this->{$i} = $tag; break; case NBT::TAG_ByteArray: $tag = new ByteArrayTag(""); $tag->read($nbt, $network); $this->{$i} = $tag; break; case NBT::TAG_String: $tag = new StringTag(""); $tag->read($nbt, $network); $this->{$i} = $tag; break; case NBT::TAG_List: $tag = new TagEnum(""); $tag->read($nbt, $network); $this->{$i} = $tag; break; case NBT::TAG_Compound: $tag = new CompoundTag(""); $tag->read($nbt, $network); $this->{$i} = $tag; break; case NBT::TAG_IntArray: $tag = new IntArrayTag(""); $tag->read($nbt, $network); $this->{$i} = $tag; break; } } } public function write(NBT $nbt, bool $network = false){ if(!isset($this->tagType)){ $id = null; foreach($this as $tag){ if($tag instanceof Tag){ if(!isset($id)){ $id = $tag->getType(); }elseif($id !== $tag->getType()){ return false; } } } $this->tagType = $id; } $nbt->putByte($this->tagType); /** @var Tag[] $tags */ $tags = []; foreach($this as $tag){ if($tag instanceof Tag){ $tags[] = $tag; } } $nbt->putInt(count($tags)); foreach($tags as $tag){ $tag->write($nbt, $network); } } public function __toString(){ $str = get_class($this) . "{\n"; foreach($this as $tag){ if($tag instanceof Tag){ $str .= get_class($tag) . ":" . $tag->__toString() . "\n"; } } return $str . "}"; } } ================================================ FILE: src/pocketmine/nbt/tag/LongTag.php ================================================ class LongTag extends NamedTag{ public function getType(){ return NBT::TAG_Long; } //TODO: check if this also changed to varint public function read(NBT $nbt, bool $network = false){ $this->value = $nbt->getLong(); } public function write(NBT $nbt, bool $network = false){ $nbt->putLong($this->value); } } ================================================ FILE: src/pocketmine/nbt/tag/NamedTag.php ================================================ __name = ($name === null or $name === false) ? "" : $name; if($value !== null){ $this->value = $value; } } public function getName(){ return $this->__name; } public function setName($name){ $this->__name = $name; } } ================================================ FILE: src/pocketmine/nbt/tag/ShortTag.php ================================================ class ShortTag extends NamedTag{ public function getType(){ return NBT::TAG_Short; } public function read(NBT $nbt, bool $network = false){ $this->value = $nbt->getShort(); } public function write(NBT $nbt, bool $network = false){ $nbt->putShort($this->value); } } ================================================ FILE: src/pocketmine/nbt/tag/StringTag.php ================================================ class StringTag extends NamedTag{ public function getType(){ return NBT::TAG_String; } public function read(NBT $nbt, bool $network = false){ $this->value = $nbt->getString($network); } public function write(NBT $nbt, bool $network = false){ $nbt->putString($this->value, $network); } } ================================================ FILE: src/pocketmine/nbt/tag/Tag.php ================================================ value; } public abstract function getType(); public function setValue($value){ $this->value = $value; } abstract public function write(NBT $nbt, bool $network = false); abstract public function read(NBT $nbt, bool $network = false); public function __toString(){ return (string) $this->value; } } ================================================ FILE: src/pocketmine/network/AdvancedSourceInterface.php ================================================ internalData === null ? ($this->internalData = parent::toBinary($internal)) : $this->internalData; } } ================================================ FILE: src/pocketmine/network/CompressBatchedTask.php ================================================ data = $data; $this->targets = serialize($targets); $this->level = $level; } public function onRun(){ try{ $this->final = zlib_encode($this->data, ZLIB_ENCODING_DEFLATE, $this->level); $this->data = null; }catch(\Throwable $e){ } } public function onCompletion(Server $server){ $server->broadcastPacketsCallback($this->final, unserialize($this->targets)); } } ================================================ FILE: src/pocketmine/network/Network.php ================================================ registerPackets(); $this->server = $server; } public function addStatistics($upload, $download) { $this->upload += $upload; $this->download += $download; } public function getUpload() { return $this->upload; } public function getDownload() { return $this->download; } public function resetStatistics() { $this->upload = 0; $this->download = 0; } /** * @return SourceInterface[] */ public function getInterfaces() { return $this->interfaces; } public function processInterfaces() { foreach ($this->interfaces as $interface) { try { $interface->process(); } catch (\Throwable $e) { $logger = $this->server->getLogger(); if (\pocketmine\DEBUG > 1) { if ($logger instanceof MainLogger) { $logger->logException($e); } } $interface->emergencyShutdown(); $this->unregisterInterface($interface); $logger->critical($this->server->getLanguage()->translateString("pocketmine.server.networkError", [get_class($interface), $e->getMessage()])); } } } /** * @param SourceInterface $interface */ public function registerInterface(SourceInterface $interface) { $this->interfaces[$hash = spl_object_hash($interface)] = $interface; if ($interface instanceof AdvancedSourceInterface) { $this->advancedInterfaces[$hash] = $interface; $interface->setNetwork($this); } $interface->setName($this->name); } /** * @param SourceInterface $interface */ public function unregisterInterface(SourceInterface $interface) { unset($this->interfaces[$hash = spl_object_hash($interface)], $this->advancedInterfaces[$hash]); } /** * Sets the server name shown on each interface Query * * @param string $name */ public function setName($name) { $this->name = (string)$name; foreach ($this->interfaces as $interface) { $interface->setName($this->name); } } public function getName() { return $this->name; } public function updateName() { foreach ($this->interfaces as $interface) { $interface->setName($this->name); } } /** * @param int $id 0-255 * @param DataPacket $class */ public function registerPacket($id, $class) { $this->packetPool[$id] = new $class; } public function getServer() { return $this->server; } public function processBatch(BatchPacket $packet, Player $p){ try{ if(strlen($packet->payload) === 0){ //prevent zlib_decode errors for incorrectly-decoded packets throw new \InvalidArgumentException("BatchPacket payload is empty or packet decode error"); } $str = zlib_decode($packet->payload, 1024 * 1024 * 64); //Max 64MB $len = strlen($str); if($len === 0){ throw new \InvalidStateException("Decoded BatchPacket payload is empty"); } $stream = new BinaryStream($str); while($stream->offset < $len){ $buf = $stream->getString(); if(($pk = $this->getPacket(ord($buf{0}))) !== null){ if($pk::NETWORK_ID === Info::BATCH_PACKET){ throw new \InvalidStateException("Invalid BatchPacket inside BatchPacket"); } $pk->setBuffer($buf, 1); $pk->decode(); assert($pk->feof(), "Still " . strlen(substr($pk->buffer, $pk->offset)) . " bytes unread in " . get_class($pk)); $p->handleDataPacket($pk); } } }catch(\Throwable $e){ if(\pocketmine\DEBUG > 1){ $logger = $this->server->getLogger(); if($logger instanceof MainLogger){ $logger->debug("BatchPacket " . " 0x" . bin2hex($packet->payload)); $logger->logException($e); } } } } /** * @param $id * * @return DataPacket */ public function getPacket($id) { /** @var DataPacket $class */ $class = $this->packetPool[$id]; if ($class !== null) { return clone $class; } return null; } /** * @param string $address * @param int $port * @param string $payload */ public function sendPacket($address, $port, $payload) { foreach ($this->advancedInterfaces as $interface) { $interface->sendRawPacket($address, $port, $payload); } } /** * Blocks an IP address from the main interface. Setting timeout to -1 will block it forever * * @param string $address * @param int $timeout */ public function blockAddress($address, $timeout = 300) { foreach ($this->advancedInterfaces as $interface) { $interface->blockAddress($address, $timeout); } } /** * Unblocks an IP address from the main interface. * * @param string $address */ public function unblockAddress($address) { foreach ($this->advancedInterfaces as $interface) { $interface->unblockAddress($address); } } private function registerPackets() { $this->packetPool = new \SplFixedArray(256); $this->registerPacket(ProtocolInfo::ADD_ENTITY_PACKET, AddEntityPacket::class); $this->registerPacket(ProtocolInfo::ADD_HANGING_ENTITY_PACKET, AddHangingEntityPacket::class); $this->registerPacket(ProtocolInfo::ADD_ITEM_ENTITY_PACKET, AddItemEntityPacket::class); $this->registerPacket(ProtocolInfo::ADD_ITEM_PACKET, AddItemPacket::class); $this->registerPacket(ProtocolInfo::ADD_PAINTING_PACKET, AddPaintingPacket::class); $this->registerPacket(ProtocolInfo::ADD_PLAYER_PACKET, AddPlayerPacket::class); $this->registerPacket(ProtocolInfo::ADVENTURE_SETTINGS_PACKET, AdventureSettingsPacket::class); $this->registerPacket(ProtocolInfo::ANIMATE_PACKET, AnimatePacket::class); $this->registerPacket(ProtocolInfo::AVAILABLE_COMMANDS_PACKET, AvailableCommandsPacket::class); $this->registerPacket(ProtocolInfo::BATCH_PACKET, BatchPacket::class); $this->registerPacket(ProtocolInfo::BLOCK_ENTITY_DATA_PACKET, BlockEntityDataPacket::class); $this->registerPacket(ProtocolInfo::BLOCK_EVENT_PACKET, BlockEventPacket::class); $this->registerPacket(ProtocolInfo::BOSS_EVENT_PACKET, BossEventPacket::class); $this->registerPacket(ProtocolInfo::CHANGE_DIMENSION_PACKET, ChangeDimensionPacket::class); $this->registerPacket(ProtocolInfo::CHUNK_RADIUS_UPDATED_PACKET, ChunkRadiusUpdatedPacket::class); $this->registerPacket(ProtocolInfo::COMMAND_STEP_PACKET, CommandStepPacket::class); $this->registerPacket(ProtocolInfo::CONTAINER_CLOSE_PACKET, ContainerClosePacket::class); $this->registerPacket(ProtocolInfo::CONTAINER_OPEN_PACKET, ContainerOpenPacket::class); $this->registerPacket(ProtocolInfo::CONTAINER_SET_CONTENT_PACKET, ContainerSetContentPacket::class); $this->registerPacket(ProtocolInfo::CONTAINER_SET_DATA_PACKET, ContainerSetDataPacket::class); $this->registerPacket(ProtocolInfo::CONTAINER_SET_SLOT_PACKET, ContainerSetSlotPacket::class); $this->registerPacket(ProtocolInfo::CRAFTING_DATA_PACKET, CraftingDataPacket::class); $this->registerPacket(ProtocolInfo::CRAFTING_EVENT_PACKET, CraftingEventPacket::class); $this->registerPacket(ProtocolInfo::DISCONNECT_PACKET, DisconnectPacket::class); $this->registerPacket(ProtocolInfo::DROP_ITEM_PACKET, DropItemPacket::class); $this->registerPacket(ProtocolInfo::ENTITY_EVENT_PACKET, EntityEventPacket::class); $this->registerPacket(ProtocolInfo::EXPLODE_PACKET, ExplodePacket::class); $this->registerPacket(ProtocolInfo::FULL_CHUNK_DATA_PACKET, FullChunkDataPacket::class); $this->registerPacket(ProtocolInfo::HURT_ARMOR_PACKET, HurtArmorPacket::class); $this->registerPacket(ProtocolInfo::INTERACT_PACKET, InteractPacket::class); $this->registerPacket(ProtocolInfo::INVENTORY_ACTION_PACKET, InventoryActionPacket::class); $this->registerPacket(ProtocolInfo::ITEM_FRAME_DROP_ITEM_PACKET, ItemFrameDropItemPacket::class); $this->registerPacket(ProtocolInfo::LEVEL_EVENT_PACKET, LevelEventPacket::class); $this->registerPacket(ProtocolInfo::LEVEL_SOUND_EVENT_PACKET, LevelSoundEventPacket::class); $this->registerPacket(ProtocolInfo::LOGIN_PACKET, LoginPacket::class); $this->registerPacket(ProtocolInfo::MOB_ARMOR_EQUIPMENT_PACKET, MobArmorEquipmentPacket::class); $this->registerPacket(ProtocolInfo::MOB_EQUIPMENT_PACKET, MobEquipmentPacket::class); $this->registerPacket(ProtocolInfo::MOVE_ENTITY_PACKET, MoveEntityPacket::class); $this->registerPacket(ProtocolInfo::MOVE_PLAYER_PACKET, MovePlayerPacket::class); $this->registerPacket(ProtocolInfo::PLAYER_ACTION_PACKET, PlayerActionPacket::class); $this->registerPacket(ProtocolInfo::PLAYER_FALL_PACKET, PlayerFallPacket::class); $this->registerPacket(ProtocolInfo::PLAYER_INPUT_PACKET, PlayerInputPacket::class); $this->registerPacket(ProtocolInfo::PLAYER_LIST_PACKET, PlayerListPacket::class); $this->registerPacket(ProtocolInfo::PLAY_STATUS_PACKET, PlayStatusPacket::class); $this->registerPacket(ProtocolInfo::REMOVE_BLOCK_PACKET, RemoveBlockPacket::class); $this->registerPacket(ProtocolInfo::REMOVE_ENTITY_PACKET, RemoveEntityPacket::class); $this->registerPacket(ProtocolInfo::REPLACE_ITEM_IN_SLOT_PACKET, ReplaceItemInSlotPacket::class); $this->registerPacket(ProtocolInfo::REQUEST_CHUNK_RADIUS_PACKET, RequestChunkRadiusPacket::class); $this->registerPacket(ProtocolInfo::RESOURCE_PACK_CLIENT_RESPONSE_PACKET, ResourcePackClientResponsePacket::class); $this->registerPacket(ProtocolInfo::RESOURCE_PACKS_INFO_PACKET, ResourcePacksInfoPacket::class); $this->registerPacket(ProtocolInfo::RESPAWN_PACKET, RespawnPacket::class); $this->registerPacket(ProtocolInfo::SET_COMMANDS_ENABLED_PACKET, SetCommandsEnabledPacket::class); $this->registerPacket(ProtocolInfo::SET_DIFFICULTY_PACKET, SetDifficultyPacket::class); $this->registerPacket(ProtocolInfo::SET_ENTITY_DATA_PACKET, SetEntityDataPacket::class); $this->registerPacket(ProtocolInfo::SET_ENTITY_LINK_PACKET, SetEntityLinkPacket::class); $this->registerPacket(ProtocolInfo::SET_ENTITY_MOTION_PACKET, SetEntityMotionPacket::class); $this->registerPacket(ProtocolInfo::SET_HEALTH_PACKET, SetHealthPacket::class); $this->registerPacket(ProtocolInfo::SET_PLAYER_GAME_TYPE_PACKET, SetPlayerGameTypePacket::class); $this->registerPacket(ProtocolInfo::SET_SPAWN_POSITION_PACKET, SetSpawnPositionPacket::class); $this->registerPacket(ProtocolInfo::SET_TIME_PACKET, SetTimePacket::class); $this->registerPacket(ProtocolInfo::SHOW_CREDITS_PACKET, ShowCreditsPacket::class); $this->registerPacket(ProtocolInfo::SPAWN_EXPERIENCE_ORB_PACKET, SpawnExperienceOrbPacket::class); $this->registerPacket(ProtocolInfo::START_GAME_PACKET, StartGamePacket::class); $this->registerPacket(ProtocolInfo::TAKE_ITEM_ENTITY_PACKET, TakeItemEntityPacket::class); $this->registerPacket(ProtocolInfo::TEXT_PACKET, TextPacket::class); $this->registerPacket(ProtocolInfo::TRANSFER_PACKET, TransferPacket::class); $this->registerPacket(ProtocolInfo::UPDATE_BLOCK_PACKET, UpdateBlockPacket::class); $this->registerPacket(ProtocolInfo::USE_ITEM_PACKET, UseItemPacket::class); } } ================================================ FILE: src/pocketmine/network/RakLibInterface.php ================================================ server = $server; $this->identifiers = []; $this->rakLib = new RakLibServer($this->server->getLogger(), $this->server->getLoader(), $this->server->getPort(), $this->server->getIp() === "" ? "0.0.0.0" : $this->server->getIp()); $this->interface = new ServerHandler($this->rakLib, $this); } public function setNetwork(Network $network){ $this->network = $network; } public function process(){ $work = false; if($this->interface->handlePacket()){ $work = true; $lasttime = time(); while($this->interface->handlePacket()){ $diff = time() - $lasttime; if($diff >= 1) break; } } if($this->rakLib->isTerminated()){ $this->network->unregisterInterface($this); throw new \Exception("RakLib Thread crashed"); } return $work; } public function closeSession($identifier, $reason){ if(isset($this->players[$identifier])){ $player = $this->players[$identifier]; unset($this->identifiers[spl_object_hash($player)]); unset($this->players[$identifier]); unset($this->identifiersACK[$identifier]); $player->close($player->getLeaveMessage(), $reason); } } public function close(Player $player, $reason = "unknown reason"){ if(isset($this->identifiers[$h = spl_object_hash($player)])){ unset($this->players[$this->identifiers[$h]]); unset($this->identifiersACK[$this->identifiers[$h]]); $this->interface->closeSession($this->identifiers[$h], $reason); unset($this->identifiers[$h]); } } public function shutdown(){ $this->interface->shutdown(); } public function emergencyShutdown(){ $this->interface->emergencyShutdown(); } public function openSession($identifier, $address, $port, $clientID){ $ev = new PlayerCreationEvent($this, Player::class, Player::class, null, $address, $port); $this->server->getPluginManager()->callEvent($ev); $class = $ev->getPlayerClass(); $player = new $class($this, $ev->getClientId(), $ev->getAddress(), $ev->getPort()); $this->players[$identifier] = $player; $this->identifiersACK[$identifier] = 0; $this->identifiers[spl_object_hash($player)] = $identifier; $this->server->addPlayer($identifier, $player); } public function handleEncapsulated($identifier, EncapsulatedPacket $packet, $flags){ if(isset($this->players[$identifier])){ try{ if($packet->buffer !== ""){ $pk = $this->getPacket($packet->buffer); if($pk !== null){ $pk->decode(); assert($pk->feof(), "Still " . strlen(substr($pk->buffer, $pk->offset)) . " bytes unread!"); $this->players[$identifier]->handleDataPacket($pk); } } }catch(\Throwable $e){ $logger = $this->server->getLogger(); if(\pocketmine\DEBUG > 1 and isset($pk)){ $logger->debug("Exception in packet " . get_class($pk) . " 0x" . bin2hex($packet->buffer)); } $logger->logException($e); } } } public function blockAddress($address, $timeout = 300){ $this->interface->blockAddress($address, $timeout); } public function unblockAddress($address){ $this->interface->unblockAddress($address); } public function handleRaw($address, $port, $payload){ $this->server->handlePacket($address, $port, $payload); } public function sendRawPacket($address, $port, $payload){ $this->interface->sendRaw($address, $port, $payload); } public function notifyACK($identifier, $identifierACK){ } public function setName($name){ if($this->server->isDServerEnabled()){ if($this->server->dserverConfig["motdMaxPlayers"] > 0) $pc = $this->server->dserverConfig["motdMaxPlayers"]; elseif($this->server->dserverConfig["motdAllPlayers"]) $pc = $this->server->getDServerMaxPlayers(); else $pc = $this->server->getMaxPlayers(); if($this->server->dserverConfig["motdPlayers"]) $poc = $this->server->getDServerOnlinePlayers(); else $poc = count($this->server->getOnlinePlayers()); }else{ $info = $this->server->getQueryInformation(); $pc = $info->getMaxPlayerCount(); $poc = $info->getPlayerCount(); } $this->interface->sendOption("name", "MCPE;" . rtrim(addcslashes($name, ";"), '\\') . ";" . Info::CURRENT_PROTOCOL . ";" . Info::MINECRAFT_VERSION_NETWORK . ";" . $poc . ";" . $pc ); } public function setPortCheck($name){ $this->interface->sendOption("portChecking", (bool) $name); } public function handleOption($name, $value){ if($name === "bandwidth"){ $v = unserialize($value); $this->network->addStatistics($v["up"], $v["down"]); } } public function putPacket(Player $player, DataPacket $packet, $needACK = false, $immediate = false){ if(isset($this->identifiers[$h = spl_object_hash($player)])){ $identifier = $this->identifiers[$h]; $pk = null; if(!$packet->isEncoded){ $packet->encode(); }elseif(!$needACK){ if(!isset($packet->__encapsulatedPacket)){ $packet->__encapsulatedPacket = new CachedEncapsulatedPacket; $packet->__encapsulatedPacket->identifierACK = null; $packet->__encapsulatedPacket->buffer = chr(0xfe) . $packet->buffer; $packet->__encapsulatedPacket->reliability = 3; $packet->__encapsulatedPacket->orderChannel = 0; } $pk = $packet->__encapsulatedPacket; } if(!$immediate and !$needACK and $packet::NETWORK_ID !== ProtocolInfo::BATCH_PACKET and Network::$BATCH_THRESHOLD >= 0 and strlen($packet->buffer) >= Network::$BATCH_THRESHOLD){ $this->server->batchPackets([$player], [$packet], true); return null; } if($pk === null){ $pk = new EncapsulatedPacket(); $pk->buffer = chr(0xfe) . $packet->buffer; $packet->reliability = 3; $packet->orderChannel = 0; if($needACK === true){ $pk->identifierACK = $this->identifiersACK[$identifier]++; } } $this->interface->sendEncapsulated($identifier, $pk, ($needACK === true ? RakLib::FLAG_NEED_ACK : 0) | ($immediate === true ? RakLib::PRIORITY_IMMEDIATE : RakLib::PRIORITY_NORMAL)); return $pk->identifierACK; } return null; } private function getPacket($buffer){ $pid = ord($buffer{0}); $start = 1; if($pid == 0xfe){ $pid = ord($buffer{1}); $start++; } if(($data = $this->network->getPacket($pid)) === null){ return null; } $data->setBuffer($buffer, $start); return $data; } } ================================================ FILE: src/pocketmine/network/SourceInterface.php ================================================ #ifndef COMPILE use pocketmine\utils\Binary; #endif class AddEntityPacket extends DataPacket{ const NETWORK_ID = Info::ADD_ENTITY_PACKET; public $eid; public $type; public $x; public $y; public $z; public $speedX; public $speedY; public $speedZ; public $yaw; public $pitch; public $modifiers; public $metadata = []; public $links = []; public function decode(){ } public function encode(){ $this->reset(); $this->putEntityId($this->eid); //EntityUniqueID - TODO: verify this $this->putEntityId($this->eid); $this->putUnsignedVarInt($this->type); $this->putVector3f($this->x, $this->y, $this->z); $this->putVector3f($this->speedX, $this->speedY, $this->speedZ); $this->putLFloat($this->pitch * (256 / 360)); $this->putLFloat($this->yaw * (256 / 360)); $this->putUnsignedVarInt($this->modifiers); //attributes? $meta = Binary::writeMetadata($this->metadata); $this->put($meta); $this->putUnsignedVarInt(count($this->links)); foreach($this->links as $link){ $this->putEntityId($link[0]); $this->putEntityId($link[1]); $this->putByte($link[2]); } } } ================================================ FILE: src/pocketmine/network/protocol/AddHangingEntityPacket.php ================================================ class AddHangingEntityPacket extends DataPacket{ const NETWORK_ID = Info::ADD_HANGING_ENTITY_PACKET; public $entityUniqueId; public $entityRuntimeId; public $x; public $y; public $z; public $unknown; public function decode(){ } public function encode(){ $this->reset(); $this->putEntityId($this->entityUniqueId); $this->putEntityId($this->entityRuntimeId); $this->putBlockCoords($this->x, $this->y, $this->z); $this->putVarInt($this->unknown); } } ================================================ FILE: src/pocketmine/network/protocol/AddItemEntityPacket.php ================================================ class AddItemEntityPacket extends DataPacket{ const NETWORK_ID = Info::ADD_ITEM_ENTITY_PACKET; public $eid; public $item; public $x; public $y; public $z; public $speedX; public $speedY; public $speedZ; public function decode(){ } public function encode(){ $this->reset(); $this->putEntityId($this->eid); //EntityUniqueID $this->putEntityId($this->eid); //EntityRuntimeID $this->putSlot($this->item); $this->putVector3f($this->x, $this->y, $this->z); $this->putVector3f($this->speedX, $this->speedY, $this->speedZ); } } ================================================ FILE: src/pocketmine/network/protocol/AddItemPacket.php ================================================ class AddItemPacket extends DataPacket{ const NETWORK_ID = Info::ADD_ITEM_PACKET; public $item; public function decode(){ } public function encode(){ $this->reset(); $this->putSlot($this->item); } } ================================================ FILE: src/pocketmine/network/protocol/AddPaintingPacket.php ================================================ class AddPaintingPacket extends DataPacket{ const NETWORK_ID = Info::ADD_PAINTING_PACKET; public $eid; public $x; public $y; public $z; public $direction; public $title; public function decode(){ } public function encode(){ $this->reset(); $this->putEntityId($this->eid); //EntityUniqueID $this->putEntityId($this->eid); //EntityRuntimeID $this->putBlockCoords($this->x, $this->y, $this->z); $this->putVarInt($this->direction); $this->putString($this->title); } } ================================================ FILE: src/pocketmine/network/protocol/AddPlayerPacket.php ================================================ #ifndef COMPILE use pocketmine\utils\Binary; #endif class AddPlayerPacket extends DataPacket{ const NETWORK_ID = Info::ADD_PLAYER_PACKET; public $uuid; public $username; public $eid; public $x; public $y; public $z; public $speedX; public $speedY; public $speedZ; public $pitch; public $headYaw; public $yaw; public $item; public $metadata = []; public function decode(){ } public function encode(){ $this->reset(); $this->putUUID($this->uuid); $this->putString($this->username); $this->putEntityId($this->eid); //EntityUniqueID $this->putEntityId($this->eid); //EntityRuntimeID $this->putVector3f($this->x, $this->y, $this->z); $this->putVector3f($this->speedX, $this->speedY, $this->speedZ); $this->putLFloat($this->pitch); $this->putLFloat($this->headYaw ?? $this->yaw); $this->putLFloat($this->yaw); $this->putSlot($this->item); $meta = Binary::writeMetadata($this->metadata); $this->put($meta); } } ================================================ FILE: src/pocketmine/network/protocol/AdventureSettingsPacket.php ================================================ class AdventureSettingsPacket extends DataPacket{ const NETWORK_ID = Info::ADVENTURE_SETTINGS_PACKET; const PERMISSION_NORMAL = 0; const PERMISSION_OPERATOR = 1; const PERMISSION_HOST = 2; const PERMISSION_AUTOMATION = 3; const PERMISSION_ADMIN = 4; public $worldImmutable; public $noPvp; public $noPvm; public $noMvp; public $autoJump; public $allowFlight; public $noClip; public $isFlying; /* bit mask | flag name 0x00000001 world_immutable 0x00000002 no_pvp 0x00000004 no_pvm 0x00000008 no_mvp 0x00000010 ? 0x00000020 auto_jump 0x00000040 allow_fly 0x00000080 noclip 0x00000100 ? 0x00000200 is_flying */ public $flags = 0; public $userPermission; public function decode(){ $this->flags = $this->getUnsignedVarInt(); $this->userPermission = $this->getUnsignedVarInt(); $this->worldImmutable = (bool) ($this->flags & 1); $this->noPvp = (bool) ($this->flags & (1 << 1)); $this->noPvm = (bool) ($this->flags & (1 << 2)); $this->noMvp = (bool) ($this->flags & (1 << 3)); $this->autoJump = (bool) ($this->flags & (1 << 5)); $this->allowFlight = (bool) ($this->flags & (1 << 6)); $this->noClip = (bool) ($this->flags & (1 << 7)); $this->isFlying = (bool) ($this->flags & (1 << 9)); } public function encode(){ $this->reset(); $this->flags |= ((int) $this->worldImmutable); $this->flags |= ((int) $this->noPvp) << 1; $this->flags |= ((int) $this->noPvm) << 2; $this->flags |= ((int) $this->noMvp) << 3; $this->flags |= ((int) $this->autoJump) << 5; $this->flags |= ((int) $this->allowFlight) << 6; $this->flags |= ((int) $this->noClip) << 7; $this->flags |= ((int) $this->isFlying) << 9; $this->putUnsignedVarInt($this->flags); $this->putUnsignedVarInt($this->userPermission); } } ================================================ FILE: src/pocketmine/network/protocol/AnimatePacket.php ================================================ class AnimatePacket extends DataPacket{ const NETWORK_ID = Info::ANIMATE_PACKET; public $action; public $eid; public function decode(){ $this->action = $this->getVarInt(); $this->eid = $this->getEntityId(); } public function encode(){ $this->reset(); $this->putVarInt($this->action); $this->putEntityId($this->eid); } } ================================================ FILE: src/pocketmine/network/protocol/AvailableCommandsPacket.php ================================================ class AvailableCommandsPacket extends DataPacket{ const NETWORK_ID = Info::AVAILABLE_COMMANDS_PACKET; public $commands; //JSON-encoded command data public $unknown; public function decode(){ } public function encode(){ $this->reset(); $this->putString($this->commands); $this->putString($this->unknown); } } ================================================ FILE: src/pocketmine/network/protocol/BatchPacket.php ================================================ class BatchPacket extends DataPacket{ const NETWORK_ID = Info::BATCH_PACKET; public $payload; public function decode(){ $this->payload = $this->getString(); } public function encode(){ $this->reset(); $this->putString($this->payload); } } ================================================ FILE: src/pocketmine/network/protocol/BlockEntityDataPacket.php ================================================ class BlockEntityDataPacket extends DataPacket{ const NETWORK_ID = Info::BLOCK_ENTITY_DATA_PACKET; public $x; public $y; public $z; public $namedtag; public function decode(){ $this->getBlockCoords($this->x, $this->y, $this->z); $this->namedtag = $this->get(true); } public function encode(){ $this->reset(); $this->putBlockCoords($this->x, $this->y, $this->z); $this->put($this->namedtag); } } ================================================ FILE: src/pocketmine/network/protocol/BlockEventPacket.php ================================================ class BlockEventPacket extends DataPacket{ const NETWORK_ID = Info::BLOCK_EVENT_PACKET; public $x; public $y; public $z; public $case1; public $case2; public function decode(){ } public function encode(){ $this->reset(); $this->putBlockCoords($this->x, $this->y, $this->z); $this->putVarInt($this->case1); $this->putVarInt($this->case2); } } ================================================ FILE: src/pocketmine/network/protocol/BossEventPacket.php ================================================ use pocketmine\utils\Binary; class BossEventPacket extends DataPacket{ const NETWORK_ID = Info::BOSS_EVENT_PACKET; public $eid; public $type; public function decode(){ } public function encode(){ $this->reset(); $this->putVarInt($this->eid); $this->putUnsignedVarInt($this->type); } } ================================================ FILE: src/pocketmine/network/protocol/CameraPacket.php ================================================ namespace pocketmine\network\protocol; class CameraPacket extends DataPacket{ const NETWORK_ID = Info::CAMERA_PACKET; public $eid; public function decode(){ } public function encode(){ $this->reset(); $this->putVarInt($this->eid); $this->putVarInt($this->eid); } } ================================================ FILE: src/pocketmine/network/protocol/ChangeDimensionPacket.php ================================================ class ChangeDimensionPacket extends DataPacket{ const NETWORK_ID = Info::CHANGE_DIMENSION_PACKET; const DIMENSION_NORMAL = 0; const DIMENSION_NETHER = 1; public $dimension; public $x; public $y; public $z; public $unknown; //bool public function decode(){ } public function encode(){ $this->reset(); $this->putVarInt($this->dimension); $this->putVector3f($this->x, $this->y, $this->z); $this->putBool($this->unknown); } } ================================================ FILE: src/pocketmine/network/protocol/ChunkRadiusUpdatedPacket.php ================================================ reset(); $this->putVarInt($this->radius); } } ================================================ FILE: src/pocketmine/network/protocol/CommandStepPacket.php ================================================ class CommandStepPacket extends DataPacket{ const NETWORK_ID = Info::COMMAND_STEP_PACKET; /** * unknown (string) * unknown (string) * unknown (uvarint) * unknown (uvarint) * unknown (bool) * unknown (uvarint64) * unknown (string) * unknown (string) * https://gist.github.com/dktapps/8285b93af4ca38e0104bfeb9a6c87afd */ public $command; public $overload; public $uvarint1; public $uvarint2; public $bool; public $uvarint64; public $args; //JSON formatted command arguments public $string4; public function decode(){ $this->command = $this->getString(); $this->overload = $this->getString(); $this->uvarint1 = $this->getUnsignedVarInt(); $this->uvarint2 = $this->getUnsignedVarInt(); $this->bool = (bool) $this->getByte(); $this->uvarint64 = $this->getUnsignedVarInt(); //TODO: varint64 $this->args = json_decode($this->getString()); $this->string4 = $this->getString(); while(!$this->feof()){ $this->getByte(); //prevent assertion errors. TODO: find out why there are always 3 extra bytes at the end of this packet. } } public function encode(){ } } ================================================ FILE: src/pocketmine/network/protocol/ContainerClosePacket.php ================================================ class ContainerClosePacket extends DataPacket{ const NETWORK_ID = Info::CONTAINER_CLOSE_PACKET; public $windowid; public function decode(){ $this->windowid = $this->getByte(); } public function encode(){ $this->reset(); $this->putByte($this->windowid); } } ================================================ FILE: src/pocketmine/network/protocol/ContainerOpenPacket.php ================================================ class ContainerOpenPacket extends DataPacket{ const NETWORK_ID = Info::CONTAINER_OPEN_PACKET; public $windowid; public $type; public $slots; public $x; public $y; public $z; public $entityId = -1; public function decode(){ } public function encode(){ $this->reset(); $this->putByte($this->windowid); $this->putByte($this->type); $this->putVarInt($this->slots); $this->putBlockCoords($this->x, $this->y, $this->z); $this->putEntityId($this->entityId); } } ================================================ FILE: src/pocketmine/network/protocol/ContainerSetContentPacket.php ================================================ class ContainerSetContentPacket extends DataPacket{ const NETWORK_ID = Info::CONTAINER_SET_CONTENT_PACKET; const SPECIAL_INVENTORY = 0; const SPECIAL_ARMOR = 0x78; const SPECIAL_CREATIVE = 0x79; const SPECIAL_HOTBAR = 0x7a; public $windowid; public $slots = []; public $hotbar = []; public function clean(){ $this->slots = []; $this->hotbar = []; return parent::clean(); } public function decode(){ $this->windowid = $this->getByte(); $count = $this->getUnsignedVarInt(); for($s = 0; $s < $count and !$this->feof(); ++$s){ $this->slots[$s] = $this->getSlot(); } if($this->windowid === self::SPECIAL_INVENTORY){ $count = $this->getUnsignedVarInt(); for($s = 0; $s < $count and !$this->feof(); ++$s){ $this->hotbar[$s] = $this->getVarInt(); } } } public function encode(){ $this->reset(); $this->putByte($this->windowid); $this->putUnsignedVarInt(count($this->slots)); foreach($this->slots as $slot){ $this->putSlot($slot); } if($this->windowid === self::SPECIAL_INVENTORY and count($this->hotbar) > 0){ $this->putUnsignedVarInt(count($this->hotbar)); foreach($this->hotbar as $slot){ $this->putVarInt($slot); } }else{ $this->putUnsignedVarInt(0); } } } ================================================ FILE: src/pocketmine/network/protocol/ContainerSetDataPacket.php ================================================ class ContainerSetDataPacket extends DataPacket{ const NETWORK_ID = Info::CONTAINER_SET_DATA_PACKET; public $windowid; public $property; public $value; public function decode(){ } public function encode(){ $this->reset(); $this->putByte($this->windowid); $this->putVarInt($this->property); $this->putVarInt($this->value); } } ================================================ FILE: src/pocketmine/network/protocol/ContainerSetSlotPacket.php ================================================ use pocketmine\item\Item; class ContainerSetSlotPacket extends DataPacket{ const NETWORK_ID = Info::CONTAINER_SET_SLOT_PACKET; public $windowid; public $slot; /** @var Item */ public $item; public $hotbarSlot; public $unknown; public function decode(){ $this->windowid = $this->getByte(); $this->slot = $this->getVarInt(); $this->hotbarSlot = $this->getVarInt(); $this->item = $this->getSlot(); $this->unknown = $this->getByte(); } public function encode(){ $this->reset(); $this->putByte($this->windowid); $this->putVarInt($this->slot); $this->putVarInt($this->hotbarSlot); $this->putSlot($this->item); $this->putByte($this->unknown); } } ================================================ FILE: src/pocketmine/network/protocol/CraftingDataPacket.php ================================================ use pocketmine\inventory\FurnaceRecipe; use pocketmine\inventory\ShapedRecipe; use pocketmine\inventory\ShapelessRecipe; use pocketmine\item\Item; use pocketmine\utils\BinaryStream; class CraftingDataPacket extends DataPacket{ const NETWORK_ID = Info::CRAFTING_DATA_PACKET; const ENTRY_SHAPELESS = 0; const ENTRY_SHAPED = 1; const ENTRY_FURNACE = 2; const ENTRY_FURNACE_DATA = 3; const ENTRY_MULTI = 4; /** @var object[] */ public $entries = []; public $cleanRecipes = false; public function clean(){ $this->entries = []; return parent::clean(); } public function decode(){ $entries = []; $recipeCount = $this->getUnsignedVarInt(); for($i = 0; $i < $recipeCount; ++$i){ $entry = []; $entry["type"] = $recipeType = $this->getVarInt(); switch($recipeType){ case self::ENTRY_SHAPELESS: $ingredientCount = $this->getUnsignedVarInt(); /** @var Item */ $entry["input"] = []; for($j = 0; $j < $ingredientCount; ++$j){ $entry["input"][] = $this->getSlot(); } $resultCount = $this->getUnsignedVarInt(); $entry["output"] = []; for($k = 0; $k < $resultCount; ++$k){ $entry["output"][] = $this->getSlot(); } $entry["uuid"] = $this->getUUID()->toString(); break; case self::ENTRY_SHAPED: $entry["width"] = $this->getVarInt(); $entry["height"] = $this->getVarInt(); $count = $entry["width"] * $entry["height"]; $entry["input"] = []; for($j = 0; $j < $count; ++$j){ $entry["input"][] = $this->getSlot(); } $resultCount = $this->getUnsignedVarInt(); $entry["output"] = []; for($k = 0; $k < $resultCount; ++$k){ $entry["output"][] = $this->getSlot(); } $entry["uuid"] = $this->getUUID()->toString(); break; case self::ENTRY_FURNACE: case self::ENTRY_FURNACE_DATA: $entry["inputId"] = $this->getVarInt(); if($recipeType === self::ENTRY_FURNACE_DATA){ $entry["inputDamage"] = $this->getVarInt(); } $entry["output"] = $this->getSlot(); break; case self::ENTRY_MULTI: $entry["uuid"] = $this->getUUID()->toString(); break; default: throw new \UnexpectedValueException("Unhandled recipe type $recipeType!"); //do not continue attempting to decode } $entries[] = $entry; } $this->getBool(); //cleanRecipes } private static function writeEntry($entry, BinaryStream $stream){ if($entry instanceof ShapelessRecipe){ return self::writeShapelessRecipe($entry, $stream); }elseif($entry instanceof ShapedRecipe){ return self::writeShapedRecipe($entry, $stream); }elseif($entry instanceof FurnaceRecipe){ return self::writeFurnaceRecipe($entry, $stream); } //TODO: add MultiRecipe return -1; } private static function writeShapelessRecipe(ShapelessRecipe $recipe, BinaryStream $stream){ $stream->putUnsignedVarInt($recipe->getIngredientCount()); foreach($recipe->getIngredientList() as $item){ $stream->putSlot($item); } $stream->putUnsignedVarInt(1); $stream->putSlot($recipe->getResult()); $stream->putUUID($recipe->getId()); return CraftingDataPacket::ENTRY_SHAPELESS; } private static function writeShapedRecipe(ShapedRecipe $recipe, BinaryStream $stream){ $stream->putVarInt($recipe->getWidth()); $stream->putVarInt($recipe->getHeight()); for($z = 0; $z < $recipe->getHeight(); ++$z){ for($x = 0; $x < $recipe->getWidth(); ++$x){ $stream->putSlot($recipe->getIngredient($x, $z)); } } $stream->putUnsignedVarInt(1); $stream->putSlot($recipe->getResult()); $stream->putUUID($recipe->getId()); return CraftingDataPacket::ENTRY_SHAPED; } private static function writeFurnaceRecipe(FurnaceRecipe $recipe, BinaryStream $stream){ if(!$recipe->getInput()->hasAnyDamageValue()){ //Data recipe $stream->putVarInt($recipe->getInput()->getId()); $stream->putVarInt($recipe->getInput()->getDamage()); $stream->putSlot($recipe->getResult()); return CraftingDataPacket::ENTRY_FURNACE_DATA; }else{ $stream->putVarInt($recipe->getInput()->getId()); $stream->putSlot($recipe->getResult()); return CraftingDataPacket::ENTRY_FURNACE; } } public function addShapelessRecipe(ShapelessRecipe $recipe){ $this->entries[] = $recipe; } public function addShapedRecipe(ShapedRecipe $recipe){ $this->entries[] = $recipe; } public function addFurnaceRecipe(FurnaceRecipe $recipe){ $this->entries[] = $recipe; } public function encode(){ $this->reset(); $this->putUnsignedVarInt(count($this->entries)); $writer = new BinaryStream(); foreach($this->entries as $d){ $entryType = self::writeEntry($d, $writer); if($entryType >= 0){ $this->putVarInt($entryType); $this->put($writer->getBuffer()); }else{ $this->putVarInt(-1); } $writer->reset(); } $this->putBool($this->cleanRecipes); } } ================================================ FILE: src/pocketmine/network/protocol/CraftingEventPacket.php ================================================ class CraftingEventPacket extends DataPacket{ const NETWORK_ID = Info::CRAFTING_EVENT_PACKET; public $windowId; public $type; public $id; public $input = []; public $output = []; public function clean(){ $this->input = []; $this->output = []; return parent::clean(); } public function decode(){ $this->windowId = $this->getByte(); $this->type = $this->getVarInt(); $this->id = $this->getUUID(); $size = $this->getUnsignedVarInt(); for($i = 0; $i < $size and $i < 128; ++$i){ $this->input[] = $this->getSlot(); } $size = $this->getUnsignedVarInt(); for($i = 0; $i < $size and $i < 128; ++$i){ $this->output[] = $this->getSlot(); } } public function encode(){ } } ================================================ FILE: src/pocketmine/network/protocol/DataPacket.php ================================================ #ifndef COMPILE #endif use pocketmine\utils\BinaryStream; use pocketmine\utils\Utils; abstract class DataPacket extends BinaryStream{ const NETWORK_ID = 0; public $isEncoded = false; public function pid(){ return $this::NETWORK_ID; } abstract public function encode(); abstract public function decode(); public function reset(){ $this->buffer = chr($this::NETWORK_ID); $this->offset = 0; } public function clean(){ $this->buffer = null; $this->isEncoded = false; $this->offset = 0; return $this; } public function __debugInfo(){ $data = []; foreach($this as $k => $v){ if($k === "buffer"){ $data[$k] = bin2hex($v); }elseif(is_string($v) or (is_object($v) and method_exists($v, "__toString"))){ $data[$k] = Utils::printable((string) $v); }else{ $data[$k] = $v; } } return $data; } } ================================================ FILE: src/pocketmine/network/protocol/DisconnectPacket.php ================================================ class DisconnectPacket extends DataPacket{ const NETWORK_ID = Info::DISCONNECT_PACKET; public $hideDisconnectionScreen = false; public $message; public function decode(){ $this->hideDisconnectionScreen = $this->getBool(); $this->message = $this->getString(); } public function encode(){ $this->reset(); $this->putBool($this->hideDisconnectionScreen); $this->putString($this->message); } } ================================================ FILE: src/pocketmine/network/protocol/DropItemPacket.php ================================================ class DropItemPacket extends DataPacket{ const NETWORK_ID = Info::DROP_ITEM_PACKET; public $type; public $item; public function decode(){ $this->type = $this->getByte(); $this->item = $this->getSlot(); } public function encode(){ } } ================================================ FILE: src/pocketmine/network/protocol/EntityEventPacket.php ================================================ class EntityEventPacket extends DataPacket{ const NETWORK_ID = Info::ENTITY_EVENT_PACKET; const HURT_ANIMATION = 2; const DEATH_ANIMATION = 3; const TAME_FAIL = 6; const TAME_SUCCESS = 7; const SHAKE_WET = 8; const USE_ITEM = 9; const EAT_GRASS_ANIMATION = 10; const FISH_HOOK_BUBBLE = 11; const FISH_HOOK_POSITION = 12; const FISH_HOOK_HOOK = 13; const FISH_HOOK_TEASE = 14; const SQUID_INK_CLOUD = 15; const AMBIENT_SOUND = 16; const RESPAWN = 17; //TODO add new events public $eid; public $event; public $unknown; public function decode(){ $this->eid = $this->getEntityId(); $this->event = $this->getByte(); $this->unknown = $this->getVarInt(); } public function encode(){ $this->reset(); $this->putEntityId($this->eid); $this->putByte($this->event); $this->putVarInt($this->unknown); } } ================================================ FILE: src/pocketmine/network/protocol/ExplodePacket.php ================================================ class ExplodePacket extends DataPacket{ const NETWORK_ID = Info::EXPLODE_PACKET; public $x; public $y; public $z; public $radius; public $records = []; public function clean(){ $this->records = []; return parent::clean(); } public function decode(){ } public function encode(){ $this->reset(); $this->putVector3f($this->x, $this->y, $this->z); $this->putLFloat($this->radius); $this->putUnsignedVarInt(count($this->records)); if(count($this->records) > 0){ foreach($this->records as $record){ $this->putBlockCoords($record->x, $record->y, $record->z); } } } } ================================================ FILE: src/pocketmine/network/protocol/FullChunkDataPacket.php ================================================ class FullChunkDataPacket extends DataPacket{ const NETWORK_ID = Info::FULL_CHUNK_DATA_PACKET; public $chunkX; public $chunkZ; public $data; public function decode(){ } public function encode(){ $this->reset(); $this->putVarInt($this->chunkX); $this->putVarInt($this->chunkZ); $this->putString($this->data); } } ================================================ FILE: src/pocketmine/network/protocol/HurtArmorPacket.php ================================================ class HurtArmorPacket extends DataPacket{ const NETWORK_ID = Info::HURT_ARMOR_PACKET; public $health; public function decode(){ } public function encode(){ $this->reset(); $this->putVarInt($this->health); } } ================================================ FILE: src/pocketmine/network/protocol/Info.php ================================================ class InteractPacket extends DataPacket{ const NETWORK_ID = Info::INTERACT_PACKET; const ACTION_RIGHT_CLICK = 1; const ACTION_LEFT_CLICK = 2; const ACTION_LEAVE_VEHICLE = 3; const ACTION_MOUSEOVER = 4; public $action; public $eid; public $target; public function decode(){ $this->action = $this->getByte(); $this->target = $this->getEntityId(); } public function encode(){ $this->reset(); $this->putByte($this->action); $this->putEntityId($this->target); } } ================================================ FILE: src/pocketmine/network/protocol/InventoryActionPacket.php ================================================ class InventoryActionPacket extends DataPacket{ const NETWORK_ID = Info::INVENTORY_ACTION_PACKET; public $unknown; public $item; public function decode(){ } public function encode(){ $this->putUnsignedVarInt($this->unknown); $this->putSlot($this->item); } } ================================================ FILE: src/pocketmine/network/protocol/ItemFrameDropItemPacket.php ================================================ class ItemFrameDropItemPacket extends DataPacket{ const NETWORK_ID = Info::ITEM_FRAME_DROP_ITEM_PACKET; public $x; public $y; public $z; public $item; public function decode(){ $this->getBlockCoords($this->x, $this->y, $this->z); $this->item = $this->getSlot(); } public function encode(){ } } ================================================ FILE: src/pocketmine/network/protocol/LevelEventPacket.php ================================================ class LevelEventPacket extends DataPacket{ const NETWORK_ID = Info::LEVEL_EVENT_PACKET; const EVENT_SOUND_CLICK = 1000; const EVENT_SOUND_CLICK_FAIL = 1001; const EVENT_SOUND_SHOOT = 1002; const EVENT_SOUND_DOOR = 1003; const EVENT_SOUND_FIZZ = 1004; const EVENT_SOUND_TNT = 1005; const EVENT_SOUND_GHAST = 1007; const EVENT_SOUND_GHAST_SHOOT = 1008; const EVENT_SOUND_BLAZE_SHOOT = 1009; const EVENT_SOUND_DOOR_BUMP = 1010; const EVENT_SOUND_DOOR_CRASH = 1012; const EVENT_SOUND_BAT_FLY = 1015; const EVENT_SOUND_ZOMBIE_INFECT = 1016; const EVENT_SOUND_ZOMBIE_HEAL = 1017; const EVENT_SOUND_ENDERMAN_TELEPORT = 1018; const EVENT_SOUND_ANVIL_BREAK = 1020; //This sound is played on the anvil's final use, NOT when the block is broken. const EVENT_SOUND_ANVIL_USE = 1021; const EVENT_SOUND_ANVIL_FALL = 1022; const EVENT_SOUND_DROP_ITEM = 1030; const EVENT_SOUND_THROW_PROJECTILE = 1031; const EVENT_SOUND_ITEMFRAME_ADD_ITEM = 1040; const EVENT_SOUND_ITEMFRAME_PLACE = 1041; //1042 is item frame, but cannot tell exactly what. const EVENT_SOUND_ITEMFRAME_DROP_ITEM = 1043; const EVENT_SOUND_ITEMFRAME_ROTATE_ITEM = 1044; //1050 sounds a lot like skeleton walking but different. TODO: find out exactly what it is. const EVENT_SOUND_EXP_PICKUP = 1051; const EVENT_SOUND_BLOCK_PLACE = 1052; const EVENT_SOUND_BUTTON_CLICK = 3500; const EVENT_PARTICLE_SHOOT = 2000; const EVENT_PARTICLE_DESTROY = 2001; const EVENT_PARTICLE_SPLASH = 2002; //This is actually the splash potion sound with particles const EVENT_PARTICLE_EYE_DESPAWN = 2003; const EVENT_PARTICLE_SPAWN = 2004; const EVENT_START_RAIN = 3001; const EVENT_START_THUNDER = 3002; const EVENT_STOP_RAIN = 3003; const EVENT_STOP_THUNDER = 3004; const EVENT_SOUND_EXPLODE = 3501; /* 3502-3509 are splash SOUNDS with particles. Probably for cauldrons. */ const EVENT_SOUND_SPELL = 3504; const EVENT_SOUND_SPLASH = 3506; const EVENT_SOUND_GRAY_SPLASH = 3507;//TODO: fix name const EVENT_SET_DATA = 4000; const EVENT_PLAYERS_SLEEPING = 9800; const EVENT_ADD_PARTICLE_MASK = 0x4000; public $evid; public $x = 0; //Weather effects don't have coordinates public $y = 0; public $z = 0; public $data; public function decode(){ } public function encode(){ $this->reset(); $this->putVarInt($this->evid); $this->putVector3f($this->x, $this->y, $this->z); $this->putVarInt($this->data); } } ================================================ FILE: src/pocketmine/network/protocol/LevelSoundEventPacket.php ================================================ class LevelSoundEventPacket extends DataPacket{ const NETWORK_ID = Info::LEVEL_SOUND_EVENT_PACKET; const SOUND_ITEM_USE_ON = 0; const SOUND_HIT = 1; const SOUND_STEP = 2; const SOUND_JUMP = 3; const SOUND_BREAK = 4; const SOUND_PLACE = 5; const SOUND_HEAVY_STEP = 6; const SOUND_GALLOP = 7; const SOUND_FALL = 8; const SOUND_AMBIENT = 9; const SOUND_AMBIENT_BABY = 10; const SOUND_AMBIENT_IN_WATER = 11; const SOUND_BREATHE = 12; const SOUND_DEATH = 13; const SOUND_DEATH_IN_WATER = 14; const SOUND_DEATH_TO_ZOMBIE = 15; const SOUND_HURT = 16; const SOUND_HURT_IN_WATER = 17; const SOUND_MAD = 18; const SOUND_BOOST = 19; const SOUND_BOW = 20; const SOUND_SQUISH_BIG = 21; const SOUND_SQUISH_SMALL = 22; const SOUND_FALL_BIG = 23; const SOUND_FALL_SMALL = 24; const SOUND_SPLASH = 25; const SOUND_FIZZ = 26; const SOUND_FLAP = 27; const SOUND_SWIM = 28; const SOUND_DRINK = 29; const SOUND_EAT = 30; const SOUND_TAKEOFF = 31; const SOUND_SHAKE = 32; const SOUND_PLOP = 33; const SOUND_LAND = 34; const SOUND_SADDLE = 35; const SOUND_ARMOR = 36; const SOUND_ADD_CHEST = 37; const SOUND_THROW = 38; const SOUND_ATTACK = 39; const SOUND_ATTACK_NODAMAGE = 40; const SOUND_WARN = 41; const SOUND_SHEAR = 42; const SOUND_MILK = 43; const SOUND_THUNDER = 44; const SOUND_EXPLODE = 45; const SOUND_FIRE = 46; const SOUND_IGNITE = 47; const SOUND_FUSE = 48; const SOUND_STARE = 49; const SOUND_SPAWN = 50; const SOUND_SHOOT = 51; const SOUND_BREAK_BLOCK = 52; const SOUND_REMEDY = 53; const SOUND_UNFECT = 54; const SOUND_LEVELUP = 55; const SOUND_BOW_HIT = 56; const SOUND_BULLET_HIT = 57; const SOUND_EXTINGUISH_FIRE = 58; const SOUND_ITEM_FIZZ = 59; const SOUND_CHEST_OPEN = 60; const SOUND_CHEST_CLOSED = 61; const SOUND_POWER_ON = 62; const SOUND_POWER_OFF = 63; const SOUND_ATTACH = 64; const SOUND_DETACH = 65; const SOUND_DENY = 66; const SOUND_TRIPOD = 67; const SOUND_POP = 68; const SOUND_DROP_SLOT = 69; const SOUND_NOTE = 70; const SOUND_THORNS = 71; const SOUND_PISTON_IN = 72; const SOUND_PISTON_OUT = 73; const SOUND_PORTAL = 74; const SOUND_WATER = 75; const SOUND_LAVA_POP = 76; const SOUND_LAVA = 77; const SOUND_BURP = 78; const SOUND_BUCKET_FILL_WATER = 79; const SOUND_BUCKET_FILL_LAVA = 80; const SOUND_BUCKET_EMPTY_WATER = 81; const SOUND_BUCKET_EMPTY_LAVA = 82; const SOUND_GUARDIAN_FLOP = 83; const SOUND_ELDERGUARDIAN_CURSE = 84; const SOUND_MOB_WARNING = 85; const SOUND_MOB_WARNING_BABY = 86; const SOUND_TELEPORT = 87; const SOUND_SHULKER_OPEN = 88; const SOUND_SHULKER_CLOSE = 89; const SOUND_DEFAULT = 90; const SOUND_UNDEFINED = 91; public $sound; public $x; public $y; public $z; public $extraData = -1; public $pitch = 1; public $unknownBool = false; public $unknownBool2 = false; public function decode(){ $this->sound = $this->getByte(); $this->getVector3f($this->x, $this->y, $this->z); $this->extraData = $this->getVarInt(); $this->pitch = $this->getVarInt(); $this->unknownBool = $this->getBool(); $this->unknownBool2 = $this->getBool(); } public function encode(){ $this->reset(); $this->putByte($this->sound); $this->putVector3f($this->x, $this->y, $this->z); $this->putVarInt($this->extraData); $this->putVarInt($this->pitch); $this->putBool($this->unknownBool); $this->putBool($this->unknownBool2); } } ================================================ FILE: src/pocketmine/network/protocol/LoginPacket.php ================================================ class LoginPacket extends DataPacket{ const NETWORK_ID = Info::LOGIN_PACKET; const MOJANG_PUBKEY = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8ELkixyLcwlZryUQcu1TvPOmI2B7vX83ndnWRUaXm74wFfa5f/lwQNTfrLVHa2PmenpGI6JhIMUJaWZrjmMj90NoKNFSNBuKdm8rYiXsfaz3K36x/1U26HpG0ZxK/V1V"; const EDITION_POCKET = 0; public $username; public $protocol; public $gameEdition; public $clientUUID; public $clientId; public $identityPublicKey; public $serverAddress; public $deviceModel; public $skinId = null; public $skin = null; public function decode(){ $this->protocol = $this->getInt(); if($this->protocol !== Info::CURRENT_PROTOCOL){ return; //Do not attempt to decode for non-accepted protocols } $this->gameEdition = $this->getByte(); $str = zlib_decode($this->getString(), 1024 * 1024 * 64); $this->setBuffer($str, 0); $time = time(); $chainData = json_decode($this->get($this->getLInt()))->{"chain"}; // Start with the trusted one $chainKey = self::MOJANG_PUBKEY; while(!empty($chainData)){ foreach($chainData as $index => $chain){ list($verified, $webtoken) = $this->decodeToken($chain, $chainKey); if(isset($webtoken["extraData"])){ if(isset($webtoken["extraData"]["displayName"])){ $this->username = $webtoken["extraData"]["displayName"]; } if(isset($webtoken["extraData"]["identity"])){ $this->clientUUID = $webtoken["extraData"]["identity"]; } } if($verified){ $verified = isset($webtoken["nbf"]) && $webtoken["nbf"] <= $time && isset($webtoken["exp"]) && $webtoken["exp"] > $time; } if($verified and isset($webtoken["identityPublicKey"])){ // Looped key chain. #blamemojang if($webtoken["identityPublicKey"] != self::MOJANG_PUBKEY) $chainKey = $webtoken["identityPublicKey"]; break; }elseif($chainKey === null){ // We have already gave up break; } } if(!$verified && $chainKey !== null){ $chainKey = null; }else{ unset($chainData[$index]); } } list($verified, $skinToken) = $this->decodeToken($this->get($this->getLInt()), $chainKey); if(isset($skinToken["ClientRandomId"])){ $this->clientId = $skinToken["ClientRandomId"]; } if(isset($skinToken["ServerAddress"])){ $this->serverAddress = $skinToken["ServerAddress"]; } if(isset($skinToken["SkinData"])){ $this->skin = base64_decode($skinToken["SkinData"]); } if(isset($skinToken["SkinId"])){ $this->skinId = $skinToken["SkinId"]; } if(isset($skinToken["DeviceModel"])){ $this->deviceModel = $skinToken["DeviceModel"]; } if($verified){ $this->identityPublicKey = $chainKey; } } public function encode(){ } public function decodeToken($token, $key){ $tokens = explode(".", $token); list($headB64, $payloadB64, $sigB64) = $tokens; if($key !== null and extension_loaded("openssl")){ $sig = base64_decode(strtr($sigB64, '-_', '+/'), true); $rawLen = 48; // ES384 for($i = $rawLen; $i > 0 and $sig[$rawLen - $i] == chr(0); $i--) {} $j = $i + (ord($sig[$rawLen - $i]) >= 128 ? 1 : 0); for($k = $rawLen; $k > 0 and $sig[2 * $rawLen - $k] == chr(0); $k--) {} $l = $k + (ord($sig[2 * $rawLen - $k]) >= 128 ? 1 : 0); $len = 2 + $j + 2 + $l; $derSig = chr(48); if($len > 255){ throw new \RuntimeException("Invalid signature format"); }elseif($len >= 128){ $derSig .= chr(81); } $derSig .= chr($len) . chr(2) . chr($j); $derSig .= str_repeat(chr(0), $j - $i) . substr($sig, $rawLen - $i, $i); $derSig .= chr(2) . chr($l); $derSig .= str_repeat(chr(0), $l - $k) . substr($sig, 2 * $rawLen - $k, $k); $verified = openssl_verify($headB64 . "." . $payloadB64, $derSig, "-----BEGIN PUBLIC KEY-----\n" . wordwrap($key, 64, "\n", true) . "\n-----END PUBLIC KEY-----\n", OPENSSL_ALGO_SHA384) === 1; }else{ $verified = false; } return array($verified, json_decode(base64_decode($payloadB64), true)); } } ================================================ FILE: src/pocketmine/network/protocol/MobArmorEquipmentPacket.php ================================================ class MobArmorEquipmentPacket extends DataPacket{ const NETWORK_ID = Info::MOB_ARMOR_EQUIPMENT_PACKET; public $eid; public $slots = []; public function decode(){ $this->eid = $this->getEntityId(); $this->slots[0] = $this->getSlot(); $this->slots[1] = $this->getSlot(); $this->slots[2] = $this->getSlot(); $this->slots[3] = $this->getSlot(); } public function encode(){ $this->reset(); $this->putEntityId($this->eid); $this->putSlot($this->slots[0]); $this->putSlot($this->slots[1]); $this->putSlot($this->slots[2]); $this->putSlot($this->slots[3]); } } ================================================ FILE: src/pocketmine/network/protocol/MobEffectPacket.php ================================================ class MobEffectPacket extends DataPacket{ const NETWORK_ID = Info::MOB_EFFECT_PACKET; const EVENT_ADD = 1; const EVENT_MODIFY = 2; const EVENT_REMOVE = 3; public $eid; public $eventId; public $effectId; public $amplifier; public $particles = true; public $duration; public function decode(){ } public function encode(){ $this->reset(); $this->putEntityId($this->eid); $this->putByte($this->eventId); $this->putVarInt($this->effectId); $this->putVarInt($this->amplifier); $this->putBool($this->particles); $this->putVarInt($this->duration); } } ================================================ FILE: src/pocketmine/network/protocol/MobEquipmentPacket.php ================================================ class MobEquipmentPacket extends DataPacket{ const NETWORK_ID = Info::MOB_EQUIPMENT_PACKET; public $eid; public $item; public $slot; public $selectedSlot; public $unknownByte; public function decode(){ $this->eid = $this->getEntityId(); //EntityRuntimeID $this->item = $this->getSlot(); $this->slot = $this->getByte(); $this->selectedSlot = $this->getByte(); $this->unknownByte = $this->getByte(); } public function encode(){ $this->reset(); $this->putEntityId($this->eid); //EntityRuntimeID $this->putSlot($this->item); $this->putByte($this->slot); $this->putByte($this->selectedSlot); $this->putByte($this->unknownByte); } } ================================================ FILE: src/pocketmine/network/protocol/MoveEntityPacket.php ================================================ class MoveEntityPacket extends DataPacket{ const NETWORK_ID = Info::MOVE_ENTITY_PACKET; public $eid; public $x; public $y; public $z; public $yaw; public $headYaw; public $pitch; public function decode(){ $this->eid = $this->getEntityId(); $this->getVector3f($this->x, $this->y, $this->z); $this->pitch = $this->getByte() * (360.0 / 256); $this->yaw = $this->getByte() * (360.0 / 256); $this->headYaw = $this->getByte() * (360.0 / 256); } public function encode(){ $this->reset(); $this->putEntityId($this->eid); $this->putVector3f($this->x, $this->y, $this->z); $this->putByte($this->pitch / (360.0 / 256)); $this->putByte($this->yaw / (360.0 / 256)); $this->putByte($this->headYaw / (360.0 / 256)); } } ================================================ FILE: src/pocketmine/network/protocol/MovePlayerPacket.php ================================================ class MovePlayerPacket extends DataPacket{ const NETWORK_ID = Info::MOVE_PLAYER_PACKET; const MODE_NORMAL = 0; const MODE_RESET = 1; const MODE_ROTATION = 2; public $eid; public $x; public $y; public $z; public $yaw; public $bodyYaw; public $pitch; public $mode = self::MODE_NORMAL; public $onGround; public function clean(){ $this->teleport = false; return parent::clean(); } public function decode(){ $this->eid = $this->getEntityId(); //EntityRuntimeID $this->getVector3f($this->x, $this->y, $this->z); $this->pitch = $this->getLFloat(); $this->yaw = $this->getLFloat(); $this->bodyYaw = $this->getLFloat(); $this->mode = $this->getByte(); $this->onGround = $this->getBool(); } public function encode(){ $this->reset(); $this->putEntityId($this->eid); //EntityRuntimeID $this->putVector3f($this->x, $this->y, $this->z); $this->putLFloat($this->pitch); $this->putLFloat($this->yaw); $this->putLFloat($this->bodyYaw); //TODO $this->putByte($this->mode); $this->putBool($this->onGround); } } ================================================ FILE: src/pocketmine/network/protocol/PlayStatusPacket.php ================================================ class PlayStatusPacket extends DataPacket{ const NETWORK_ID = Info::PLAY_STATUS_PACKET; const LOGIN_SUCCESS = 0; const LOGIN_FAILED_CLIENT = 1; const LOGIN_FAILED_SERVER = 2; const PLAYER_SPAWN = 3; public $status; public function decode(){ } public function encode(){ $this->reset(); $this->putInt($this->status); } } ================================================ FILE: src/pocketmine/network/protocol/PlayerActionPacket.php ================================================ class PlayerActionPacket extends DataPacket{ const NETWORK_ID = Info::PLAYER_ACTION_PACKET; const ACTION_START_BREAK = 0; const ACTION_ABORT_BREAK = 1; const ACTION_STOP_BREAK = 2; const ACTION_RELEASE_ITEM = 5; const ACTION_STOP_SLEEPING = 6; const ACTION_SPAWN_SAME_DIMENSION = 7; const ACTION_JUMP = 8; const ACTION_START_SPRINT = 9; const ACTION_STOP_SPRINT = 10; const ACTION_START_SNEAK = 11; const ACTION_STOP_SNEAK = 12; const ACTION_SPAWN_OVERWORLD = 13; const ACTION_SPAWN_NETHER = 14; const ACTION_START_GLIDE = 15; const ACTION_STOP_GLIDE = 16; public $eid; public $action; public $x; public $y; public $z; public $face; public function decode(){ $this->eid = $this->getEntityId(); $this->action = $this->getVarInt(); $this->getBlockCoords($this->x, $this->y, $this->z); $this->face = $this->getVarInt(); } public function encode(){ $this->reset(); $this->putEntityId($this->eid); $this->putVarInt($this->action); $this->putBlockCoords($this->x, $this->y, $this->z); $this->putVarInt($this->face); } } ================================================ FILE: src/pocketmine/network/protocol/PlayerFallPacket.php ================================================ class PlayerFallPacket extends DataPacket{ const NETWORK_ID = Info::PLAYER_FALL_PACKET; public $unknown; //betting this is fall distance, but let's make sure first public function decode(){ $this->unknown = $this->getLFloat(); } public function encode(){ } } ================================================ FILE: src/pocketmine/network/protocol/PlayerInputPacket.php ================================================ class PlayerInputPacket extends DataPacket{ const NETWORK_ID = Info::PLAYER_INPUT_PACKET; public $motionX; public $motionY; public $unknownBool1; public $unknownBool2; public function decode(){ $this->motionX = $this->getLFloat(); $this->motionY = $this->getLFloat(); $this->unknownBool1 = $this->getBool(); $this->unknownBool2 = $this->getBool(); } public function encode(){ } } ================================================ FILE: src/pocketmine/network/protocol/PlayerListPacket.php ================================================ class PlayerListPacket extends DataPacket{ const NETWORK_ID = Info::PLAYER_LIST_PACKET; const TYPE_ADD = 0; const TYPE_REMOVE = 1; //REMOVE: UUID, ADD: UUID, entity id, name, skinId, skin /** @var array[] */ public $entries = []; public $type; public function clean(){ $this->entries = []; return parent::clean(); } public function decode(){ } public function encode(){ $this->reset(); $this->putByte($this->type); $this->putUnsignedVarInt(count($this->entries)); foreach($this->entries as $d){ if($this->type === self::TYPE_ADD){ $this->putUUID($d[0]); $this->putEntityId($d[1]); $this->putString($d[2]); $this->putString($d[3]); $this->putString($d[4]); }else{ $this->putUUID($d[0]); } } } } ================================================ FILE: src/pocketmine/network/protocol/RemoveBlockPacket.php ================================================ class RemoveBlockPacket extends DataPacket{ const NETWORK_ID = Info::REMOVE_BLOCK_PACKET; public $x; public $y; public $z; public function decode(){ $this->getBlockCoords($this->x, $this->y, $this->z); } public function encode(){ } } ================================================ FILE: src/pocketmine/network/protocol/RemoveEntityPacket.php ================================================ class RemoveEntityPacket extends DataPacket{ const NETWORK_ID = Info::REMOVE_ENTITY_PACKET; public $eid; public function decode(){ } public function encode(){ $this->reset(); $this->putEntityId($this->eid); } } ================================================ FILE: src/pocketmine/network/protocol/ReplaceItemInSlotPacket.php ================================================ class ReplaceItemInSlotPacket extends DataPacket{ const NETWORK_ID = Info::REPLACE_ITEM_IN_SLOT_PACKET; public $item; public function decode(){ } public function encode(){ $this->reset(); $this->putSlot($this->item); } } ================================================ FILE: src/pocketmine/network/protocol/RequestChunkRadiusPacket.php ================================================ radius = $this->getVarInt(); } public function encode(){ } } ================================================ FILE: src/pocketmine/network/protocol/ResourcePackClientResponsePacket.php ================================================ class ResourcePackClientResponsePacket extends DataPacket{ const NETWORK_ID = Info::RESOURCE_PACK_CLIENT_RESPONSE_PACKET; public $unknownByte; public $unknownShort; public function decode(){ $this->unknownByte = $this->getByte(); $this->unknownShort = $this->getShort(); } public function encode(){ } } ================================================ FILE: src/pocketmine/network/protocol/ResourcePacksInfoPacket.php ================================================ class ResourcePacksInfoPacket extends DataPacket{ const NETWORK_ID = Info::RESOURCE_PACKS_INFO_PACKET; public $mustAccept = false; //force client to use selected resource packs /** @var ResourcePackInfoEntry */ public $behaviourPackEntries = []; /** @var ResourcePackInfoEntry */ public $resourcePackEntries = []; public function decode(){ } public function encode(){ $this->reset(); $this->putBool($this->mustAccept); $this->putShort(count($this->behaviourPackEntries)); foreach($this->behaviourPackEntries as $entry){ $this->putString($entry->getPackId()); $this->putString($entry->getVersion()); $this->putLong($entry->getPackSize()); } $this->putShort(count($this->resourcePackEntries)); foreach($this->resourcePackEntries as $entry){ $this->putString($entry->getPackId()); $this->putString($entry->getVersion()); $this->putLong($entry->getPackSize()); } } } ================================================ FILE: src/pocketmine/network/protocol/RespawnPacket.php ================================================ class RespawnPacket extends DataPacket{ const NETWORK_ID = Info::RESPAWN_PACKET; public $x; public $y; public $z; public function decode(){ $this->x = $this->getLFloat(); $this->y = $this->getLFloat(); $this->z = $this->getLFloat(); } public function encode(){ $this->reset(); $this->putLFloat($this->x); $this->putLFloat($this->y); $this->putLFloat($this->z); } } ================================================ FILE: src/pocketmine/network/protocol/SetCommandsEnabledPacket.php ================================================ class SetCommandsEnabledPacket extends DataPacket{ const NETWORK_ID = Info::SET_COMMANDS_ENABLED_PACKET; public $enabled; public function decode(){ } public function encode(){ $this->reset(); $this->putBool($this->enabled); } } ================================================ FILE: src/pocketmine/network/protocol/SetDifficultyPacket.php ================================================ class SetDifficultyPacket extends DataPacket{ const NETWORK_ID = Info::SET_DIFFICULTY_PACKET; public $difficulty; public function decode(){ $this->difficulty = $this->getUnsignedVarInt(); } public function encode(){ $this->reset(); $this->putUnsignedVarInt($this->difficulty); } } ================================================ FILE: src/pocketmine/network/protocol/SetEntityDataPacket.php ================================================ #ifndef COMPILE use pocketmine\utils\Binary; #endif class SetEntityDataPacket extends DataPacket{ const NETWORK_ID = Info::SET_ENTITY_DATA_PACKET; public $eid; public $metadata; public function decode(){ } public function encode(){ $this->reset(); $this->putEntityId($this->eid); $meta = Binary::writeMetadata($this->metadata); $this->put($meta); } } ================================================ FILE: src/pocketmine/network/protocol/SetEntityLinkPacket.php ================================================ class SetEntityLinkPacket extends DataPacket{ const NETWORK_ID = Info::SET_ENTITY_LINK_PACKET; const TYPE_REMOVE = 0; const TYPE_RIDE = 1; const TYPE_PASSENGER = 2; public $from; public $to; public $type; public function decode(){ } public function encode(){ $this->reset(); $this->putEntityId($this->from); $this->putEntityId($this->to); $this->putByte($this->type); } } ================================================ FILE: src/pocketmine/network/protocol/SetEntityMotionPacket.php ================================================ class SetEntityMotionPacket extends DataPacket{ const NETWORK_ID = Info::SET_ENTITY_MOTION_PACKET; public $eid; public $motionX; public $motionY; public $motionZ; public function clean(){ $this->entities = []; return parent::clean(); } public function decode(){ } public function encode(){ $this->reset(); $this->putEntityId($this->eid); $this->putVector3f($this->motionX, $this->motionY, $this->motionZ); } } ================================================ FILE: src/pocketmine/network/protocol/SetHealthPacket.php ================================================ class SetHealthPacket extends DataPacket{ const NETWORK_ID = Info::SET_HEALTH_PACKET; public $health; public function decode(){ $this->health = $this->getVarInt(); } public function encode(){ $this->reset(); $this->putVarInt($this->health); } } ================================================ FILE: src/pocketmine/network/protocol/SetPlayerGameTypePacket.php ================================================ class SetPlayerGameTypePacket extends DataPacket{ const NETWORK_ID = Info::SET_PLAYER_GAME_TYPE_PACKET; public $gamemode; public function decode(){ $this->gamemode = $this->getVarInt(); } public function encode(){ $this->reset(); $this->putVarInt($this->gamemode); } } ================================================ FILE: src/pocketmine/network/protocol/SetSpawnPositionPacket.php ================================================ class SetSpawnPositionPacket extends DataPacket{ const NETWORK_ID = Info::SET_SPAWN_POSITION_PACKET; public $unknown; public $x; public $y; public $z; public $unknownBool; public function decode(){ } public function encode(){ $this->reset(); $this->putVarInt($this->unknown); $this->putBlockCoords($this->x, $this->y, $this->z); $this->putBool($this->unknownBool); } } ================================================ FILE: src/pocketmine/network/protocol/SetTimePacket.php ================================================ class SetTimePacket extends DataPacket{ const NETWORK_ID = Info::SET_TIME_PACKET; public $time; public $started = true; public function decode(){ } public function encode(){ $this->reset(); $this->putVarInt($this->time); $this->putBool($this->started); } } ================================================ FILE: src/pocketmine/network/protocol/ShowCreditsPacket.php ================================================ class ShowCreditsPacket extends DataPacket{ const NETWORK_ID = Info::SHOW_CREDITS_PACKET; public $eid; public function decode(){ $this->eid = $this->getEntityId(); //EntityRuntimeID } public function encode(){ $this->reset(); $this->putEntityId($this->eid); //EntityRuntimeID } } ================================================ FILE: src/pocketmine/network/protocol/SpawnExperienceOrbPacket.php ================================================ class SpawnExperienceOrbPacket extends DataPacket{ const NETWORK_ID = Info::SPAWN_EXPERIENCE_ORB_PACKET; public $x; public $y; public $z; public $amount; public function decode(){ } public function encode(){ $this->reset(); $this->putVector3f($this->x, $this->y, $this->z); $this->putVarInt($this->amount); } } ================================================ FILE: src/pocketmine/network/protocol/StartGamePacket.php ================================================ class StartGamePacket extends DataPacket{ const NETWORK_ID = Info::START_GAME_PACKET; public $entityUniqueId; public $entityRuntimeId; public $x; public $y; public $z; public $seed; public $dimension; public $generator = 1; //default infinite - 0 old, 1 infinite, 2 flat public $gamemode; public $difficulty; public $spawnX; public $spawnY; public $spawnZ; public $hasAchievementsDisabled = 1; public $dayCycleStopTime = -1; //-1 = not stopped, any positive value = stopped at that time public $eduMode = 0; public $rainLevel; public $lightningLevel; public $commandsEnabled; public $isTexturePacksRequired = 0; public $unknown; public $worldName; public function decode(){ } public function encode(){ $this->reset(); $this->putEntityId($this->entityUniqueId); //EntityUniqueID $this->putEntityId($this->entityRuntimeId); //EntityRuntimeID $this->putVector3f($this->x, $this->y, $this->z); $this->putLFloat(0); //TODO: find out what these are (yaw/pitch?) $this->putLFloat(0); $this->putVarInt($this->seed); $this->putVarInt($this->dimension); $this->putVarInt($this->generator); $this->putVarInt($this->gamemode); $this->putVarInt($this->difficulty); $this->putBlockCoords($this->spawnX, $this->spawnY, $this->spawnZ); $this->putBool($this->hasAchievementsDisabled); $this->putVarInt($this->dayCycleStopTime); $this->putBool($this->eduMode); $this->putLFloat($this->rainLevel); $this->putLFloat($this->lightningLevel); $this->putBool($this->commandsEnabled); $this->putBool($this->isTexturePacksRequired); $this->putString($this->unknown); $this->putString($this->worldName); } } ================================================ FILE: src/pocketmine/network/protocol/StrangePacket.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ namespace pocketmine\network\protocol; class StrangePacket extends DataPacket{ const NETWORK_ID = 0x1b; public $address; public $port = 19132; public function pid(){ return 0x1b; } protected function putAddress($addr, $port, $version = 4){ $this->putByte($version); if($version === 4){ foreach(explode(".", $addr) as $b){ $this->putByte((~((int) $b)) & 0xff); } $this->putShort($port); }else{ //IPv6 } } public function decode(){ } public function encode(){ $this->reset(); $this->putAddress($this->address, $this->port); } } ================================================ FILE: src/pocketmine/network/protocol/TakeItemEntityPacket.php ================================================ class TakeItemEntityPacket extends DataPacket{ const NETWORK_ID = Info::TAKE_ITEM_ENTITY_PACKET; public $target; public $eid; public function decode(){ } public function encode(){ $this->reset(); $this->putEntityId($this->target); $this->putEntityId($this->eid); } } ================================================ FILE: src/pocketmine/network/protocol/TextPacket.php ================================================ class TextPacket extends DataPacket{ const NETWORK_ID = Info::TEXT_PACKET; const TYPE_RAW = 0; const TYPE_CHAT = 1; const TYPE_TRANSLATION = 2; const TYPE_POPUP = 3; const TYPE_TIP = 4; const TYPE_SYSTEM = 5; const TYPE_WHISPER = 6; public $type; public $source; public $message; public $parameters = []; public function decode(){ $this->type = $this->getByte(); switch($this->type){ case self::TYPE_POPUP: case self::TYPE_CHAT: /** @noinspection PhpMissingBreakStatementInspection */ case self::TYPE_WHISPER: $this->source = $this->getString(); case self::TYPE_RAW: case self::TYPE_TIP: case self::TYPE_SYSTEM: $this->message = $this->getString(); break; case self::TYPE_TRANSLATION: $this->message = $this->getString(); $count = $this->getUnsignedVarInt(); for($i = 0; $i < $count; ++$i){ $this->parameters[] = $this->getString(); } } } public function encode(){ $this->reset(); $this->putByte($this->type); switch($this->type){ case self::TYPE_POPUP: case self::TYPE_CHAT: /** @noinspection PhpMissingBreakStatementInspection */ case self::TYPE_WHISPER: $this->putString($this->source); case self::TYPE_RAW: case self::TYPE_TIP: case self::TYPE_SYSTEM: $this->putString($this->message); break; case self::TYPE_TRANSLATION: $this->putString($this->message); $this->putUnsignedVarInt(count($this->parameters)); foreach($this->parameters as $p){ $this->putString($p); } } } } ================================================ FILE: src/pocketmine/network/protocol/TransferPacket.php ================================================ class TransferPacket extends DataPacket{ const NETWORK_ID = Info::TRANSFER_PACKET; public $address; public $port = 19132; public function decode(){ } public function encode(){ $this->reset(); $this->putString($this->address); $this->putLShort($this->port); } } ================================================ FILE: src/pocketmine/network/protocol/UpdateAttributesPacket.php ================================================ use pocketmine\entity\Attribute; class UpdateAttributesPacket extends DataPacket{ const NETWORK_ID = Info::UPDATE_ATTRIBUTES_PACKET; public $entityId; /** @var Attribute[] */ public $entries = []; public function decode(){ } public function encode(){ $this->reset(); $this->putEntityId($this->entityId); $this->putUnsignedVarInt(count($this->entries)); foreach($this->entries as $entry){ $this->putLFloat($entry->getMinValue()); $this->putLFloat($entry->getMaxValue()); $this->putLFloat($entry->getValue()); $this->putLFloat($entry->getDefaultValue()); $this->putString($entry->getName()); } } } ================================================ FILE: src/pocketmine/network/protocol/UpdateBlockPacket.php ================================================ class UpdateBlockPacket extends DataPacket{ const NETWORK_ID = Info::UPDATE_BLOCK_PACKET; const FLAG_NONE = 0b0000; const FLAG_NEIGHBORS = 0b0001; const FLAG_NETWORK = 0b0010; const FLAG_NOGRAPHIC = 0b0100; const FLAG_PRIORITY = 0b1000; const FLAG_ALL = (self::FLAG_NEIGHBORS | self::FLAG_NETWORK); const FLAG_ALL_PRIORITY = (self::FLAG_ALL | self::FLAG_PRIORITY); public $x; public $z; public $y; public $blockId; public $blockData; public $flags; public function decode(){ } public function encode(){ $this->reset(); $this->putBlockCoords($this->x, $this->y, $this->z); $this->putUnsignedVarInt($this->blockId); $this->putUnsignedVarInt(($this->flags << 4) | $this->blockData); } } ================================================ FILE: src/pocketmine/network/protocol/UseItemPacket.php ================================================ class UseItemPacket extends DataPacket{ const NETWORK_ID = Info::USE_ITEM_PACKET; public $x; public $y; public $z; public $blockId; public $face; public $item; public $fx; public $fy; public $fz; public $posX; public $posY; public $posZ; public $slot; public function decode(){ $this->getBlockCoords($this->x, $this->y, $this->z); $this->blockId = $this->getUnsignedVarInt(); $this->face = $this->getVarInt(); $this->getVector3f($this->fx, $this->fy, $this->fz); $this->getVector3f($this->posX, $this->posY, $this->posZ); $this->slot = $this->getVarInt(); $this->item = $this->getSlot(); } public function encode(){ } } ================================================ FILE: src/pocketmine/network/query/QueryHandler.php ================================================ server = Server::getInstance(); $this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.server.query.start")); $addr = ($ip = $this->server->getIp()) != "" ? $ip : "0.0.0.0"; $port = $this->server->getPort(); $this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.server.query.info", [$port])); /* The Query protocol is built on top of the existing Minecraft PE UDP network stack. Because the 0xFE packet does not exist in the MCPE protocol, we can identify Query packets and remove them from the packet queue. Then, the Query class handles itself sending the packets in raw form, because packets can conflict with the MCPE ones. */ $this->regenerateToken(); $this->lastToken = $this->token; $this->regenerateInfo(); $this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.server.query.running", [$addr, $port])); } public function regenerateInfo(){ $ev = $this->server->getQueryInformation(); $this->longData = $ev->getLongQuery(); $this->shortData = $ev->getShortQuery(); $this->timeout = microtime(true) + $ev->getTimeout(); } public function regenerateToken(){ $this->lastToken = $this->token; $this->token = random_bytes(16); } public static function getTokenString($token, $salt){ return Binary::readInt(substr(hash("sha512", $salt . ":" . $token, true), 7, 4)); } public function handle($address, $port, $packet){ $offset = 2; $packetType = ord($packet{$offset++}); $sessionID = Binary::readInt(substr($packet, $offset, 4)); $offset += 4; $payload = substr($packet, $offset); switch($packetType){ case self::HANDSHAKE: //Handshake $reply = chr(self::HANDSHAKE); $reply .= Binary::writeInt($sessionID); $reply .= self::getTokenString($this->token, $address) . "\x00"; $this->server->getNetwork()->sendPacket($address, $port, $reply); break; case self::STATISTICS: //Stat $token = Binary::readInt(substr($payload, 0, 4)); if($token !== self::getTokenString($this->token, $address) and $token !== self::getTokenString($this->lastToken, $address)){ break; } $reply = chr(self::STATISTICS); $reply .= Binary::writeInt($sessionID); if($this->timeout < microtime(true)){ $this->regenerateInfo(); } if(strlen($payload) === 8){ $reply .= $this->longData; }else{ $reply .= $this->shortData; } $this->server->getNetwork()->sendPacket($address, $port, $reply); break; } } } ================================================ FILE: src/pocketmine/network/rcon/RCON.php ================================================ server = $server; $this->workers = []; $this->password = (string) $password; $this->server->getLogger()->info("Starting remote control listener"); if($this->password === ""){ $this->server->getLogger()->critical("RCON can't be started: Empty password"); return; } $this->threads = (int) max(1, $threads); $this->clientsPerThread = (int) max(1, $clientsPerThread); $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if($this->socket === false or !socket_bind($this->socket, $interface, (int) $port) or !socket_listen($this->socket)){ $this->server->getLogger()->critical("RCON can't be started: " . socket_strerror(socket_last_error())); $this->threads = 0; return; } socket_set_block($this->socket); for($n = 0; $n < $this->threads; ++$n){ $this->workers[$n] = new RCONInstance($this->server->getLogger(), $this->socket, $this->password, $this->clientsPerThread); } socket_getsockname($this->socket, $addr, $port); $this->server->getLogger()->info("RCON running on $addr:$port"); } public function stop(){ for($n = 0; $n < $this->threads; ++$n){ $this->workers[$n]->close(); Server::microSleep(50000); $this->workers[$n]->close(); $this->workers[$n]->quit(); } @socket_close($this->socket); $this->threads = 0; } public function check(){ $d = Utils::getRealMemoryUsage(); $u = Utils::getMemoryUsage(true); $usage = round(($u[0] / 1024) / 1024, 2) . "/" . round(($d[0] / 1024) / 1024, 2) . "/" . round(($u[1] / 1024) / 1024, 2) . "/" . round(($u[2] / 1024) / 1024, 2) . " MB @ " . Utils::getThreadCount() . " threads"; $serverStatus = serialize([ "online" => count($this->server->getOnlinePlayers()), "max" => $this->server->getMaxPlayers(), "upload" => round($this->server->getNetwork()->getUpload() / 1024, 2), "download" => round($this->server->getNetwork()->getDownload() / 1024, 2), "tps" => $this->server->getTicksPerSecondAverage(), "load" => $this->server->getTickUsageAverage(), "usage" => $usage ]); for($n = 0; $n < $this->threads; ++$n){ if(!$this->workers[$n]->isTerminated()){ $this->workers[$n]->serverStatus = $serverStatus; } if($this->workers[$n]->isTerminated() === true){ $this->workers[$n] = new RCONInstance($this->socket, $this->password, $this->clientsPerThread); }elseif($this->workers[$n]->isWaiting()){ if($this->workers[$n]->response !== ""){ $this->server->getLogger()->info($this->workers[$n]->response); $this->workers[$n]->synchronized(function(RCONInstance $thread){ $thread->notify(); }, $this->workers[$n]); }else{ $response = new RemoteConsoleCommandSender(); $command = $this->workers[$n]->cmd; $this->server->getPluginManager()->callEvent($ev = new RemoteServerCommandEvent($response, $command)); if(!$ev->isCancelled()){ $this->server->dispatchCommand($ev->getSender(), $ev->getCommand()); } $this->workers[$n]->response = $response->getMessage(); $this->workers[$n]->synchronized(function(RCONInstance $thread){ $thread->notify(); }, $this->workers[$n]); } } } } } ================================================ FILE: src/pocketmine/network/rcon/RCONInstance.php ================================================ waiting === true; } public function __construct($logger, $socket, $password, $maxClients = 50){ $this->logger = $logger; $this->stop = false; $this->cmd = ""; $this->response = ""; $this->socket = $socket; $this->password = $password; $this->maxClients = (int) $maxClients; for($n = 0; $n < $this->maxClients; ++$n){ $this->{"client" . $n} = null; $this->{"status" . $n} = 0; $this->{"timeout" . $n} = 0; } $this->start(); } private function writePacket($client, $requestID, $packetType, $payload){ $pk = Binary::writeLInt((int) $requestID) . Binary::writeLInt((int) $packetType) . $payload . "\x00\x00"; //Terminate payload and packet return socket_write($client, Binary::writeLInt(strlen($pk)) . $pk); } private function readPacket($client, &$size, &$requestID, &$packetType, &$payload){ socket_set_nonblock($client); $d = @socket_read($client, 4); if($this->stop === true){ return false; }elseif($d === false){ return null; }elseif($d === "" or strlen($d) < 4){ return false; } socket_set_block($client); $size = Binary::readLInt($d); if($size < 0 or $size > 65535){ return false; } $requestID = Binary::readLInt(socket_read($client, 4)); $packetType = Binary::readLInt(socket_read($client, 4)); $payload = rtrim(socket_read($client, $size + 2)); //Strip two null bytes return true; } public function close(){ $this->stop = true; } public function run(){ while($this->stop !== true){ $this->synchronized(function(){ $this->wait(2000); }); $r = [$socket = $this->socket]; $w = null; $e = null; if(socket_select($r, $w, $e, 0) === 1){ if(($client = socket_accept($this->socket)) !== false){ socket_set_block($client); socket_set_option($client, SOL_SOCKET, SO_KEEPALIVE, 1); $done = false; for($n = 0; $n < $this->maxClients; ++$n){ if($this->{"client" . $n} === null){ $this->{"client" . $n} = $client; $this->{"status" . $n} = 0; $this->{"timeout" . $n} = microtime(true) + 5; $done = true; break; } } if($done === false){ @socket_close($client); } } } for($n = 0; $n < $this->maxClients; ++$n){ $client = &$this->{"client" . $n}; if($client !== null){ if($this->{"status" . $n} !== -1 and $this->stop !== true){ if($this->{"status" . $n} === 0 and $this->{"timeout" . $n} < microtime(true)){ //Timeout $this->{"status" . $n} = -1; continue; } $p = $this->readPacket($client, $size, $requestID, $packetType, $payload); if($p === false){ $this->{"status" . $n} = -1; continue; }elseif($p === null){ continue; } switch($packetType){ case 9: //Protocol check if($this->{"status" . $n} !== 1){ $this->{"status" . $n} = -1; continue; } $this->writePacket($client, $requestID, 0, RCON::PROTOCOL_VERSION); $this->response = ""; if($payload == RCON::PROTOCOL_VERSION) $this->logger->setSendMsg(true); //GeniRCON output break; case 4: //Logger if($this->{"status" . $n} !== 1){ $this->{"status" . $n} = -1; continue; } $res = (array) [ "serverStatus" => unserialize($this->serverStatus), "logger" => str_replace("\n", "\r\n", trim($this->logger->getMessages())) ]; $this->writePacket($client, $requestID, 0, serialize($res)); $this->response = ""; break; case 3: //Login if($this->{"status" . $n} !== 0){ $this->{"status" . $n} = -1; continue; } if($payload === $this->password){ socket_getpeername($client, $addr, $port); $this->response = "[INFO] Successful Rcon connection from: /$addr:$port"; $this->response = ""; $this->writePacket($client, $requestID, 2, ""); $this->{"status" . $n} = 1; }else{ $this->{"status" . $n} = -1; $this->writePacket($client, -1, 2, ""); continue; } break; case 2: //Command if($this->{"status" . $n} !== 1){ $this->{"status" . $n} = -1; continue; } if(strlen($payload) > 0){ $this->cmd = ltrim($payload); $this->synchronized(function(){ $this->waiting = true; $this->wait(); }); $this->waiting = false; $this->writePacket($client, $requestID, 0, str_replace("\n", "\r\n", trim($this->response))); $this->response = ""; $this->cmd = ""; } break; } }else{ @socket_set_option($client, SOL_SOCKET, SO_LINGER, ["l_onoff" => 1, "l_linger" => 1]); @socket_shutdown($client, 2); @socket_set_block($client); @socket_read($client, 1); @socket_close($client); $this->{"status" . $n} = 0; $this->{"client" . $n} = null; } } } } unset($this->socket, $this->cmd, $this->response, $this->stop); exit(0); } public function getThreadName(){ return "RCON"; } } ================================================ FILE: src/pocketmine/network/upnp/UPnP.php ================================================ StaticPortMappingCollection)){ return false; } $com->StaticPortMappingCollection->Add($port, "UDP", $port, $myLocalIP, true, "PocketMine-MP"); }catch(\Throwable $e){ return false; } return true; } public static function RemovePortForward($port){ if(Utils::$online === false){ return false; } if(Utils::getOS() != "win" or !class_exists("COM")){ return false; } $port = (int) $port; try{ $com = new \COM("HNetCfg.NATUPnP") or false; if($com === false or !is_object($com->StaticPortMappingCollection)){ return false; } $com->StaticPortMappingCollection->Remove($port, "UDP"); }catch(\Throwable $e){ return false; } return true; } } ================================================ FILE: src/pocketmine/permission/BanEntry.php ================================================ name = strtolower($name); $this->creationDate = new \DateTime(); } public function getName(){ return $this->name; } public function getCreated(){ return $this->creationDate; } public function setCreated(\DateTime $date){ $this->creationDate = $date; } public function getSource(){ return $this->source; } public function setSource($source){ $this->source = $source; } public function getExpires(){ return $this->expirationDate; } /** * @param \DateTime $date */ public function setExpires($date){ $this->expirationDate = $date; } public function hasExpired(){ $now = new \DateTime(); return $this->expirationDate === null ? false : $this->expirationDate < $now; } public function getReason(){ return $this->reason; } public function setReason($reason){ $this->reason = $reason; } public function getString(){ $str = ""; $str .= $this->getName(); $str .= "|"; $str .= $this->getCreated()->format(self::$format); $str .= "|"; $str .= $this->getSource(); $str .= "|"; $str .= $this->getExpires() === null ? "Forever" : $this->getExpires()->format(self::$format); $str .= "|"; $str .= $this->getReason(); return $str; } /** * @param string $str * * @return BanEntry */ public static function fromString($str){ if(strlen($str) < 2){ return null; }else{ $str = explode("|", trim($str)); $entry = new BanEntry(trim(array_shift($str))); if(count($str) > 0){ $datetime = \DateTime::createFromFormat(self::$format, array_shift($str)); if(!($datetime instanceof \DateTime)){ MainLogger::getLogger()->alert("Error parsing date for BanEntry for player \"" . $entry->getName() . "\", the format may be invalid!"); return $entry; } $entry->setCreated($datetime); if(count($str) > 0){ $entry->setSource(trim(array_shift($str))); if(count($str) > 0){ $expire = trim(array_shift($str)); if(strtolower($expire) !== "forever" and strlen($expire) > 0){ $entry->setExpires(\DateTime::createFromFormat(self::$format, $expire)); } if(count($str) > 0){ $entry->setReason(trim(array_shift($str))); } } } } return $entry; } } } ================================================ FILE: src/pocketmine/permission/BanList.php ================================================ file = $file; } /** * @return bool */ public function isEnabled(){ return $this->enabled === true; } /** * @param bool $flag */ public function setEnabled($flag){ $this->enabled = (bool) $flag; } /** * @return BanEntry[] */ public function getEntries(){ $this->removeExpired(); return $this->list; } /** * @param string $name * * @return bool */ public function isBanned($name){ $name = strtolower($name); if(!$this->isEnabled()){ return false; }else{ $this->removeExpired(); return isset($this->list[$name]); } } /** * @param BanEntry $entry */ public function add(BanEntry $entry){ $this->list[$entry->getName()] = $entry; $this->save(); } /** * @param string $target * @param string $reason * @param \DateTime $expires * @param string $source * * @return BanEntry */ public function addBan($target, $reason = null, $expires = null, $source = null){ $entry = new BanEntry($target); $entry->setSource($source != null ? $source : $entry->getSource()); $entry->setExpires($expires); $entry->setReason($reason != null ? $reason : $entry->getReason()); $this->list[$entry->getName()] = $entry; $this->save(); return $entry; } /** * @param string $name */ public function remove($name){ $name = strtolower($name); if(isset($this->list[$name])){ unset($this->list[$name]); $this->save(); } } public function removeExpired(){ foreach($this->list as $name => $entry){ if($entry->hasExpired()){ unset($this->list[$name]); } } } public function load(){ $this->list = []; $fp = @fopen($this->file, "r"); if(is_resource($fp)){ while(($line = fgets($fp)) !== false){ if($line{0} !== "#"){ $entry = BanEntry::fromString($line); if($entry instanceof BanEntry){ $this->list[$entry->getName()] = $entry; } } } fclose($fp); }else{ MainLogger::getLogger()->error("Could not load ban list"); } } public function save($flag = true){ $this->removeExpired(); $fp = @fopen($this->file, "w"); if(is_resource($fp)){ if($flag === true){ fwrite($fp, "# Updated " . strftime("%x %H:%M", time()) . " by " . Server::getInstance()->getName() . " " . Server::getInstance()->getPocketMineVersion() . "\n"); fwrite($fp, "# victim name | ban date | banned by | banned until | reason\n\n"); } foreach($this->list as $entry){ fwrite($fp, $entry->getString() . "\n"); } fclose($fp); }else{ MainLogger::getLogger()->error("Could not save ban list"); } } } ================================================ FILE: src/pocketmine/permission/DefaultPermissions.php ================================================ getChildren()[$perm->getName()] = true; return self::registerPermission($perm); } Server::getInstance()->getPluginManager()->addPermission($perm); return Server::getInstance()->getPluginManager()->getPermission($perm->getName()); } public static function registerCorePermissions(){ $parent = self::registerPermission(new Permission(self::ROOT, "Allows using all PocketMine commands and utilities")); $broadcasts = self::registerPermission(new Permission(self::ROOT . ".broadcast", "Allows the user to receive all broadcast messages"), $parent); self::registerPermission(new Permission(self::ROOT . ".broadcast.admin", "Allows the user to receive administrative broadcasts", Permission::DEFAULT_OP), $broadcasts); self::registerPermission(new Permission(self::ROOT . ".broadcast.user", "Allows the user to receive user broadcasts", Permission::DEFAULT_TRUE), $broadcasts); $broadcasts->recalculatePermissibles(); $commands = self::registerPermission(new Permission(self::ROOT . ".command", "Allows using all PocketMine commands"), $parent); $whitelist = self::registerPermission(new Permission(self::ROOT . ".command.whitelist", "Allows the user to modify the server whitelist", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.whitelist.add", "Allows the user to add a player to the server whitelist"), $whitelist); self::registerPermission(new Permission(self::ROOT . ".command.whitelist.remove", "Allows the user to remove a player to the server whitelist"), $whitelist); self::registerPermission(new Permission(self::ROOT . ".command.whitelist.reload", "Allows the user to reload the server whitelist"), $whitelist); self::registerPermission(new Permission(self::ROOT . ".command.whitelist.enable", "Allows the user to enable the server whitelist"), $whitelist); self::registerPermission(new Permission(self::ROOT . ".command.whitelist.disable", "Allows the user to disable the server whitelist"), $whitelist); self::registerPermission(new Permission(self::ROOT . ".command.whitelist.list", "Allows the user to list all the players on the server whitelist"), $whitelist); $whitelist->recalculatePermissibles(); $ban = self::registerPermission(new Permission(self::ROOT . ".command.ban", "Allows the user to ban people", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.ban.player", "Allows the user to ban players"), $ban); self::registerPermission(new Permission(self::ROOT . ".command.ban.ip", "Allows the user to ban IP addresses"), $ban); $ban->recalculatePermissibles(); $unban = self::registerPermission(new Permission(self::ROOT . ".command.unban", "Allows the user to unban people", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.unban.player", "Allows the user to unban players"), $unban); self::registerPermission(new Permission(self::ROOT . ".command.unban.ip", "Allows the user to unban IP addresses"), $unban); $unban->recalculatePermissibles(); $op = self::registerPermission(new Permission(self::ROOT . ".command.op", "Allows the user to change operators", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.op.give", "Allows the user to give a player operator status"), $op); self::registerPermission(new Permission(self::ROOT . ".command.op.take", "Allows the user to take a players operator status"), $op); $op->recalculatePermissibles(); $save = self::registerPermission(new Permission(self::ROOT . ".command.save", "Allows the user to save the worlds", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.save.enable", "Allows the user to enable automatic saving"), $save); self::registerPermission(new Permission(self::ROOT . ".command.save.disable", "Allows the user to disable automatic saving"), $save); self::registerPermission(new Permission(self::ROOT . ".command.save.perform", "Allows the user to perform a manual save"), $save); $save->recalculatePermissibles(); $time = self::registerPermission(new Permission(self::ROOT . ".command.time", "Allows the user to alter the time", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.time.add", "Allows the user to fast-forward time"), $time); self::registerPermission(new Permission(self::ROOT . ".command.time.set", "Allows the user to change the time"), $time); self::registerPermission(new Permission(self::ROOT . ".command.time.start", "Allows the user to restart the time"), $time); self::registerPermission(new Permission(self::ROOT . ".command.time.stop", "Allows the user to stop the time"), $time); self::registerPermission(new Permission(self::ROOT . ".command.time.query", "Allows the user query the time"), $time); $time->recalculatePermissibles(); $kill = self::registerPermission(new Permission(self::ROOT . ".command.kill", "Allows the user to kill players", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.kill.self", "Allows the user to commit suicide", Permission::DEFAULT_TRUE), $kill); self::registerPermission(new Permission(self::ROOT . ".command.kill.other", "Allows the user to kill other players"), $kill); $kill->recalculatePermissibles(); self::registerPermission(new Permission(self::ROOT . ".command.me", "Allows the user to perform a chat action", Permission::DEFAULT_TRUE), $commands); self::registerPermission(new Permission(self::ROOT . ".command.tell", "Allows the user to privately message another player", Permission::DEFAULT_TRUE), $commands); self::registerPermission(new Permission(self::ROOT . ".command.say", "Allows the user to talk as the console", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.give", "Allows the user to give items to players", Permission::DEFAULT_OP), $commands); $effect = self::registerPermission(new Permission(self::ROOT . ".command.effect", "Allows the user to give/take potion effects", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.effect.other", "Allows the user to give/take potion effects for other", Permission::DEFAULT_OP), $commands); $effect->recalculatePermissibles(); self::registerPermission(new Permission(self::ROOT . ".command.enchant", "Allows the user to enchant items", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.particle", "Allows the user to create particle effects", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.teleport", "Allows the user to teleport players", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.kick", "Allows the user to kick players", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.stop", "Allows the user to stop the server", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.list", "Allows the user to list all online players", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.help", "Allows the user to view the help menu", Permission::DEFAULT_TRUE), $commands); self::registerPermission(new Permission(self::ROOT . ".command.plugins", "Allows the user to view the list of plugins", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.reload", "Allows the user to reload the server settings", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.version", "Allows the user to view the version of the server", Permission::DEFAULT_TRUE), $commands); self::registerPermission(new Permission(self::ROOT . ".command.gamemode", "Allows the user to change the gamemode of players", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.defaultgamemode", "Allows the user to change the default gamemode", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.seed", "Allows the user to view the seed of the world", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.status", "Allows the user to view the server performance", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.gc", "Allows the user to fire garbage collection tasks", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.dumpmemory", "Allows the user to dump memory contents", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.timings", "Allows the user to records timings for all plugin events", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.spawnpoint", "Allows the user to change player's spawnpoint", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.setworldspawn", "Allows the user to change the world spawn", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.extractphar", "", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.extractplugin", "", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.makeplugin", "", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.makeserver", "", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.bancid", "", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.pardoncid", "", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.bancidbyname", "", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.banipbyname", "", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.weather", "", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.loadplugin", "", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.lvdat", "", Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.biome", "" , Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.cave", "" , Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.setblock", "" , Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.fill", "" , Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.summon", "" , Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.xp", "" , Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.chunkinfo", "" , Permission::DEFAULT_OP), $commands); self::registerPermission(new Permission(self::ROOT . ".command.transfer", "" , Permission::DEFAULT_OP), $commands); $commands->recalculatePermissibles(); $parent->recalculatePermissibles(); } } ================================================ FILE: src/pocketmine/permission/Permissible.php ================================================ opable = $opable; if($opable instanceof Permissible){ $this->parent = $opable; } } public function __destruct(){ $this->parent = null; $this->opable = null; } /** * @return bool */ public function isOp(){ if($this->opable === null){ return false; }else{ return $this->opable->isOp(); } } /** * @param bool $value * * @throws \Throwable */ public function setOp($value){ if($this->opable === null){ throw new \LogicException("Cannot change op value as no ServerOperator is set"); }else{ $this->opable->setOp($value); } } /** * @param Permission|string $name * * @return bool */ public function isPermissionSet($name){ return isset($this->permissions[$name instanceof Permission ? $name->getName() : $name]); } /** * @param Permission|string $name * * @return bool */ public function hasPermission($name){ if($name instanceof Permission){ $name = $name->getName(); } if($this->isPermissionSet($name)){ return $this->permissions[$name]->getValue(); } if(($perm = Server::getInstance()->getPluginManager()->getPermission($name)) !== null){ $perm = $perm->getDefault(); return $perm === Permission::DEFAULT_TRUE or ($this->isOp() and $perm === Permission::DEFAULT_OP) or (!$this->isOp() and $perm === Permission::DEFAULT_NOT_OP); }else{ return Permission::$DEFAULT_PERMISSION === Permission::DEFAULT_TRUE or ($this->isOp() and Permission::$DEFAULT_PERMISSION === Permission::DEFAULT_OP) or (!$this->isOp() and Permission::$DEFAULT_PERMISSION === Permission::DEFAULT_NOT_OP); } } /** * //TODO: tick scheduled attachments * * @param Plugin $plugin * @param string $name * @param bool $value * * @return PermissionAttachment * * @throws PluginException */ public function addAttachment(Plugin $plugin, $name = null, $value = null){ if($plugin === null){ throw new PluginException("Plugin cannot be null"); }elseif(!$plugin->isEnabled()){ throw new PluginException("Plugin " . $plugin->getDescription()->getName() . " is disabled"); } $result = new PermissionAttachment($plugin, $this->parent !== null ? $this->parent : $this); $this->attachments[spl_object_hash($result)] = $result; if($name !== null and $value !== null){ $result->setPermission($name, $value); } $this->recalculatePermissions(); return $result; } /** * @param PermissionAttachment $attachment * * @throws \Throwable */ public function removeAttachment(PermissionAttachment $attachment){ if($attachment === null){ throw new \InvalidStateException("Attachment cannot be null"); } if(isset($this->attachments[spl_object_hash($attachment)])){ unset($this->attachments[spl_object_hash($attachment)]); if(($ex = $attachment->getRemovalCallback()) !== null){ $ex->attachmentRemoved($attachment); } $this->recalculatePermissions(); } } public function recalculatePermissions(){ Timings::$permissibleCalculationTimer->startTiming(); $this->clearPermissions(); $defaults = Server::getInstance()->getPluginManager()->getDefaultPermissions($this->isOp()); Server::getInstance()->getPluginManager()->subscribeToDefaultPerms($this->isOp(), $this->parent !== null ? $this->parent : $this); foreach($defaults as $perm){ $name = $perm->getName(); $this->permissions[$name] = new PermissionAttachmentInfo($this->parent !== null ? $this->parent : $this, $name, null, true); Server::getInstance()->getPluginManager()->subscribeToPermission($name, $this->parent !== null ? $this->parent : $this); $this->calculateChildPermissions($perm->getChildren(), false, null); } foreach($this->attachments as $attachment){ $this->calculateChildPermissions($attachment->getPermissions(), false, $attachment); } Timings::$permissibleCalculationTimer->stopTiming(); } public function clearPermissions(){ foreach(array_keys($this->permissions) as $name){ Server::getInstance()->getPluginManager()->unsubscribeFromPermission($name, $this->parent !== null ? $this->parent : $this); } Server::getInstance()->getPluginManager()->unsubscribeFromDefaultPerms(false, $this->parent !== null ? $this->parent : $this); Server::getInstance()->getPluginManager()->unsubscribeFromDefaultPerms(true, $this->parent !== null ? $this->parent : $this); $this->permissions = []; } /** * @param bool[] $children * @param bool $invert * @param PermissionAttachment $attachment */ private function calculateChildPermissions(array $children, $invert, $attachment){ foreach($children as $name => $v){ $perm = Server::getInstance()->getPluginManager()->getPermission($name); $value = ($v xor $invert); $this->permissions[$name] = new PermissionAttachmentInfo($this->parent !== null ? $this->parent : $this, $name, $attachment, $value); Server::getInstance()->getPluginManager()->subscribeToPermission($name, $this->parent !== null ? $this->parent : $this); if($perm instanceof Permission){ $this->calculateChildPermissions($perm->getChildren(), !$value, $attachment); } } } /** * @return PermissionAttachmentInfo[] */ public function getEffectivePermissions(){ return $this->permissions; } } ================================================ FILE: src/pocketmine/permission/Permission.php ================================================ name = $name; $this->description = $description !== null ? $description : ""; $this->defaultValue = $defaultValue !== null ? $defaultValue : self::$DEFAULT_PERMISSION; $this->children = $children; $this->recalculatePermissibles(); } /** * @return string */ public function getName(){ return $this->name; } /** * @return string[] */ public function &getChildren(){ return $this->children; } /** * @return string */ public function getDefault(){ return $this->defaultValue; } /** * @param string $value */ public function setDefault($value){ if($value !== $this->defaultValue){ $this->defaultValue = $value; $this->recalculatePermissibles(); } } /** * @return string */ public function getDescription(){ return $this->description; } /** * @param string $value */ public function setDescription($value){ $this->description = $value; } /** * @return Permissible[] */ public function getPermissibles(){ return Server::getInstance()->getPluginManager()->getPermissionSubscriptions($this->name); } public function recalculatePermissibles(){ $perms = $this->getPermissibles(); Server::getInstance()->getPluginManager()->recalculatePermissionDefaults($this); foreach($perms as $p){ $p->recalculatePermissions(); } } /** * @param string|Permission $name * @param $value * * @return Permission|void Permission if $name is a string, void if it's a Permission */ public function addParent($name, $value){ if($name instanceof Permission){ $name->getChildren()[$this->getName()] = $value; $name->recalculatePermissibles(); return; }else{ $perm = Server::getInstance()->getPluginManager()->getPermission($name); if($perm === null){ $perm = new Permission($name); Server::getInstance()->getPluginManager()->addPermission($perm); } $this->addParent($perm, $value); return $perm; } } /** * @param array $data * @param $default * * @return Permission[] */ public static function loadPermissions(array $data, $default = self::DEFAULT_OP){ $result = []; foreach($data as $key => $entry){ $result[] = self::loadPermission($key, $entry, $default, $result); } return $result; } /** * @param string $name * @param array $data * @param string $default * @param array $output * * @return Permission * * @throws \Throwable */ public static function loadPermission($name, array $data, $default = self::DEFAULT_OP, &$output = []){ $desc = null; $children = []; if(isset($data["default"])){ $value = Permission::getByName($data["default"]); if($value !== null){ $default = $value; }else{ throw new \InvalidStateException("'default' key contained unknown value"); } } if(isset($data["children"])){ if(is_array($data["children"])){ foreach($data["children"] as $k => $v){ if(is_array($v)){ if(($perm = self::loadPermission($k, $v, $default, $output)) !== null){ $output[] = $perm; } } $children[$k] = true; } }else{ throw new \InvalidStateException("'children' key is of wrong type"); } } if(isset($data["description"])){ $desc = $data["description"]; } return new Permission($name, $desc, $default, $children); } } ================================================ FILE: src/pocketmine/permission/PermissionAttachment.php ================================================ isEnabled()){ throw new PluginException("Plugin " . $plugin->getDescription()->getName() . " is disabled"); } $this->permissible = $permissible; $this->plugin = $plugin; } /** * @return Plugin */ public function getPlugin(){ return $this->plugin; } /** * @param PermissionRemovedExecutor $ex */ public function setRemovalCallback(PermissionRemovedExecutor $ex){ $this->removed = $ex; } /** * @return PermissionRemovedExecutor */ public function getRemovalCallback(){ return $this->removed; } /** * @return Permissible */ public function getPermissible(){ return $this->permissible; } /** * @return bool[] */ public function getPermissions(){ return $this->permissions; } /** * @return bool[] */ public function clearPermissions(){ $this->permissions = []; $this->permissible->recalculatePermissions(); } /** * @param bool[] $permissions */ public function setPermissions(array $permissions){ foreach($permissions as $key => $value){ $this->permissions[$key] = (bool) $value; } $this->permissible->recalculatePermissions(); } /** * @param string[] $permissions */ public function unsetPermissions(array $permissions){ foreach($permissions as $node){ unset($this->permissions[$node]); } $this->permissible->recalculatePermissions(); } /** * @param string|Permission $name * @param bool $value */ public function setPermission($name, $value){ $name = $name instanceof Permission ? $name->getName() : $name; if(isset($this->permissions[$name])){ if($this->permissions[$name] === $value){ return; } unset($this->permissions[$name]); //Fixes children getting overwritten } $this->permissions[$name] = $value; $this->permissible->recalculatePermissions(); } /** * @param string|Permission $name */ public function unsetPermission($name){ $name = $name instanceof Permission ? $name->getName() : $name; if(isset($this->permissions[$name])){ unset($this->permissions[$name]); $this->permissible->recalculatePermissions(); } } /** * @return void */ public function remove(){ $this->permissible->removeAttachment($this); } } ================================================ FILE: src/pocketmine/permission/PermissionAttachmentInfo.php ================================================ permissible = $permissible; $this->permission = $permission; $this->attachment = $attachment; $this->value = $value; } /** * @return Permissible */ public function getPermissible(){ return $this->permissible; } /** * @return string */ public function getPermission(){ return $this->permission; } /** * @return PermissionAttachment */ public function getAttachment(){ return $this->attachment; } /** * @return bool */ public function getValue(){ return $this->value; } } ================================================ FILE: src/pocketmine/permission/PermissionRemovedExecutor.php ================================================ method = $method; } public function execute(Listener $listener, Event $event){ $listener->{$this->getMethod()}($event); } public function getMethod(){ return $this->method; } } ================================================ FILE: src/pocketmine/plugin/PharPluginLoader.php ================================================ server = $server; } /** * Loads the plugin contained in $file * * @param string $file * * @return Plugin * * @throws \Throwable */ public function loadPlugin($file){ if(($description = $this->getPluginDescription($file)) instanceof PluginDescription){ $this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.plugin.load", [$description->getFullName()])); $dataFolder = dirname($file) . DIRECTORY_SEPARATOR . $description->getName(); if(file_exists($dataFolder) and !is_dir($dataFolder)){ throw new \InvalidStateException("Projected dataFolder '" . $dataFolder . "' for " . $description->getName() . " exists and is not a directory"); } $file = "phar://$file"; $className = $description->getMain(); $this->server->getLoader()->addPath("$file/src"); if(class_exists($className, true)){ $plugin = new $className(); $this->initPlugin($plugin, $description, $dataFolder, $file); return $plugin; }else{ throw new PluginException("Couldn't load plugin " . $description->getName() . ": main class not found"); } } return null; } /** * Gets the PluginDescription from the file * * @param string $file * * @return PluginDescription */ public function getPluginDescription($file){ $phar = new \Phar($file); if(isset($phar["plugin.yml"])){ $pluginYml = $phar["plugin.yml"]; if($pluginYml instanceof \PharFileInfo){ return new PluginDescription($pluginYml->getContent()); } } return null; } /** * Returns the filename patterns that this loader accepts * * @return array */ public function getPluginFilters(){ return "/\\.phar$/i"; } /** * @param PluginBase $plugin * @param PluginDescription $description * @param string $dataFolder * @param string $file */ private function initPlugin(PluginBase $plugin, PluginDescription $description, $dataFolder, $file){ $plugin->init($this, $this->server, $description, $dataFolder, $file); $plugin->onLoad(); } /** * @param Plugin $plugin */ public function enablePlugin(Plugin $plugin){ if($plugin instanceof PluginBase and !$plugin->isEnabled()){ $this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.plugin.enable", [$plugin->getDescription()->getFullName()])); $plugin->setEnabled(true); $this->server->getPluginManager()->callEvent(new PluginEnableEvent($plugin)); } } /** * @param Plugin $plugin */ public function disablePlugin(Plugin $plugin){ if($plugin instanceof PluginBase and $plugin->isEnabled()){ $this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.plugin.disable", [$plugin->getDescription()->getFullName()])); $this->server->getPluginManager()->callEvent(new PluginDisableEvent($plugin)); $plugin->setEnabled(false); } } } ================================================ FILE: src/pocketmine/plugin/Plugin.php ================================================ isEnabled === true; } /** * @param bool $boolean */ public final function setEnabled($boolean = true){ if($this->isEnabled !== $boolean){ $this->isEnabled = $boolean; if($this->isEnabled === true){ $this->onEnable(); }else{ $this->onDisable(); } } } /** * @return bool */ public final function isDisabled(){ return $this->isEnabled === false; } public final function getDataFolder(){ return $this->dataFolder; } public final function getDescription(){ return $this->description; } public final function init(PluginLoader $loader, Server $server, PluginDescription $description, $dataFolder, $file){ if($this->initialized === false){ $this->initialized = true; $this->loader = $loader; $this->server = $server; $this->description = $description; $this->dataFolder = rtrim($dataFolder, "\\/") . "/"; $this->file = rtrim($file, "\\/") . "/"; $this->configFile = $this->dataFolder . "config.yml"; $this->logger = new PluginLogger($this); } } /** * @return PluginLogger */ public function getLogger(){ return $this->logger; } /** * @return bool */ public final function isInitialized(){ return $this->initialized; } /** * @param string $name * * @return Command|PluginIdentifiableCommand */ public function getCommand($name){ $command = $this->getServer()->getPluginCommand($name); if($command === null or $command->getPlugin() !== $this){ $command = $this->getServer()->getPluginCommand(strtolower($this->description->getName()) . ":" . $name); } if($command instanceof PluginIdentifiableCommand and $command->getPlugin() === $this){ return $command; }else{ return null; } } /** * @param CommandSender $sender * @param Command $command * @param string $label * @param array $args * * @return bool */ public function onCommand(CommandSender $sender, Command $command, $label, array $args){ return false; } /** * @return bool */ protected function isPhar(){ return substr($this->file, 0, 7) === "phar://"; } /** * Gets an embedded resource on the plugin file. * WARNING: You must close the resource given using fclose() * * @param string $filename * * @return resource Resource data, or null */ public function getResource($filename){ $filename = rtrim(str_replace("\\", "/", $filename), "/"); if(file_exists($this->file . "resources/" . $filename)){ return fopen($this->file . "resources/" . $filename, "rb"); } return null; } /** * @param string $filename * @param bool $replace * * @return bool */ public function saveResource($filename, $replace = false){ if(trim($filename) === ""){ return false; } if(($resource = $this->getResource($filename)) === null){ return false; } $out = $this->dataFolder . $filename; if(!file_exists(dirname($out))){ mkdir(dirname($out), 0755, true); } if(file_exists($out) and $replace !== true){ return false; } $ret = stream_copy_to_stream($resource, $fp = fopen($out, "wb")) > 0; fclose($fp); fclose($resource); return $ret; } /** * Returns all the resources packaged with the plugin * * @return string[] */ public function getResources(){ $resources = []; if(is_dir($this->file . "resources/")){ foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->file . "resources/")) as $resource){ $resources[] = $resource; } } return $resources; } /** * @return Config */ public function getConfig(){ if(!isset($this->config)){ $this->reloadConfig(); } return $this->config; } public function saveConfig(){ if($this->getConfig()->save() === false){ $this->getLogger()->critical("Could not save config to " . $this->configFile); } } public function saveDefaultConfig(){ if(!file_exists($this->configFile)){ $this->saveResource("config.yml", false); } } public function reloadConfig(){ $this->config = new Config($this->configFile); if(($configStream = $this->getResource("config.yml")) !== null){ $this->config->setDefaults(yaml_parse(config::fixYAMLIndexes(stream_get_contents($configStream)))); fclose($configStream); } } /** * @return Server */ public final function getServer(){ return $this->server; } /** * @return string */ public final function getName(){ return $this->description->getName(); } /** * @return string */ public final function getFullName(){ return $this->description->getFullName(); } protected function getFile(){ return $this->file; } /** * @return PluginLoader */ public function getPluginLoader(){ return $this->loader; } } ================================================ FILE: src/pocketmine/plugin/PluginDescription.php ================================================ loadMap(!is_array($yamlString) ? \yaml_parse($yamlString) : $yamlString); } /** * @param array $plugin * * @throws PluginException */ private function loadMap(array $plugin){ $this->name = preg_replace("[^A-Za-z0-9 _.-]", "", $plugin["name"]); if($this->name === ""){ throw new PluginException("Invalid PluginDescription name"); } $this->name = str_replace(" ", "_", $this->name); $this->version = $plugin["version"]; $this->main = $plugin["main"]; $this->api = !is_array($plugin["api"]) ? [$plugin["api"]] : $plugin["api"]; if(!isset($plugin["geniapi"])){ $this->geniapi = ["1.0.0"]; }else{ $this->geniapi = !is_array($plugin["geniapi"]) ? [$plugin["geniapi"]] : $plugin["geniapi"]; } if(stripos($this->main, "pocketmine\\") === 0){ throw new PluginException("Invalid PluginDescription main, cannot start within the PocketMine namespace"); } if(isset($plugin["commands"]) and is_array($plugin["commands"])){ $this->commands = $plugin["commands"]; } if(isset($plugin["depend"])){ $this->depend = (array) $plugin["depend"]; } if(isset($plugin["softdepend"])){ $this->softDepend = (array) $plugin["softdepend"]; } if(isset($plugin["loadbefore"])){ $this->loadBefore = (array) $plugin["loadbefore"]; } if(isset($plugin["website"])){ $this->website = $plugin["website"]; } if(isset($plugin["description"])){ $this->description = $plugin["description"]; } if(isset($plugin["prefix"])){ $this->prefix = $plugin["prefix"]; } if(isset($plugin["load"])){ $order = strtoupper($plugin["load"]); if(!defined(PluginLoadOrder::class . "::" . $order)){ throw new PluginException("Invalid PluginDescription load"); }else{ $this->order = constant(PluginLoadOrder::class . "::" . $order); } } $this->authors = []; if(isset($plugin["author"])){ $this->authors[] = $plugin["author"]; } if(isset($plugin["authors"])){ foreach($plugin["authors"] as $author){ $this->authors[] = $author; } } if(isset($plugin["permissions"])){ $this->permissions = Permission::loadPermissions($plugin["permissions"]); } } /** * @return string */ public function getFullName(){ return $this->name . " v" . $this->version; } /** * @return array */ public function getCompatibleApis(){ return $this->api; } /** * @return array */ public function getCompatibleGeniApis(){ return $this->geniapi; } /** * @return array */ public function getAuthors(){ return $this->authors; } /** * @return string */ public function getPrefix(){ return $this->prefix; } /** * @return array */ public function getCommands(){ return $this->commands; } /** * @return array */ public function getDepend(){ return $this->depend; } /** * @return string */ public function getDescription(){ return $this->description; } /** * @return array */ public function getLoadBefore(){ return $this->loadBefore; } /** * @return string */ public function getMain(){ return $this->main; } /** * @return string */ public function getName(){ return $this->name; } /** * @return int */ public function getOrder(){ return $this->order; } /** * @return Permission[] */ public function getPermissions(){ return $this->permissions; } /** * @return array */ public function getSoftDepend(){ return $this->softDepend; } /** * @return string */ public function getVersion(){ return $this->version; } /** * @return string */ public function getWebsite(){ return $this->website; } } ================================================ FILE: src/pocketmine/plugin/PluginLoadOrder.php ================================================ attachments[spl_object_hash($attachment)] = $attachment; } public function removeAttachment(\LoggerAttachment $attachment){ unset($this->attachments[spl_object_hash($attachment)]); } public function removeAttachments(){ $this->attachments = []; } public function getAttachments(){ return $this->attachments; } /** * @param Plugin $context */ public function __construct(Plugin $context){ $prefix = $context->getDescription()->getPrefix(); $this->pluginName = $prefix != null ? "[$prefix] " : "[" . $context->getDescription()->getName() . "] "; } public function emergency($message){ $this->log(LogLevel::EMERGENCY, $message); } public function alert($message){ $this->log(LogLevel::ALERT, $message); } public function critical($message){ $this->log(LogLevel::CRITICAL, $message); } public function error($message){ $this->log(LogLevel::ERROR, $message); } public function warning($message){ $this->log(LogLevel::WARNING, $message); } public function notice($message){ $this->log(LogLevel::NOTICE, $message); } public function info($message){ $this->log(LogLevel::INFO, $message); } public function debug($message){ $this->log(LogLevel::DEBUG, $message); } public function logException(\Throwable $e, $trace = null){ Server::getInstance()->getLogger()->logException($e, $trace); } public function log($level, $message){ Server::getInstance()->getLogger()->log($level, $this->pluginName . $message); foreach($this->attachments as $attachment){ $attachment->log($level, $message); } } } ================================================ FILE: src/pocketmine/plugin/PluginManager.php ================================================ server = $server; $this->commandMap = $commandMap; } /** * @param string $name * * @return null|Plugin */ public function getPlugin($name){ if(isset($this->plugins[$name])){ return $this->plugins[$name]; } return null; } /** * @param string $loaderName A PluginLoader class name * * @return boolean */ public function registerInterface($loaderName){ if(is_subclass_of($loaderName, PluginLoader::class)){ $loader = new $loaderName($this->server); }else{ return false; } $this->fileAssociations[$loaderName] = $loader; return true; } /** * @return Plugin[] */ public function getPlugins(){ return $this->plugins; } /** * @param string $path * @param PluginLoader[] $loaders * * @return Plugin */ public function loadPlugin($path, $loaders = null){ foreach(($loaders === null ? $this->fileAssociations : $loaders) as $loader){ if(preg_match($loader->getPluginFilters(), basename($path)) > 0){ $description = $loader->getPluginDescription($path); if($description instanceof PluginDescription){ if(($plugin = $loader->loadPlugin($path)) instanceof Plugin){ $this->plugins[$plugin->getDescription()->getName()] = $plugin; $pluginCommands = $this->parseYamlCommands($plugin); if(count($pluginCommands) > 0){ $this->commandMap->registerAll($plugin->getDescription()->getName(), $pluginCommands); } return $plugin; } } } } return null; } /** * @param string $directory * @param array $newLoaders * * @return Plugin[] */ public function loadPlugins($directory, $newLoaders = null){ if(is_dir($directory)){ $plugins = []; $loadedPlugins = []; $dependencies = []; $softDependencies = []; if(is_array($newLoaders)){ $loaders = []; foreach($newLoaders as $key){ if(isset($this->fileAssociations[$key])){ $loaders[$key] = $this->fileAssociations[$key]; } } }else{ $loaders = $this->fileAssociations; } foreach($loaders as $loader){ foreach(new \RegexIterator(new \DirectoryIterator($directory), $loader->getPluginFilters()) as $file){ if($file === "." or $file === ".."){ continue; } $file = $directory . $file; try{ $description = $loader->getPluginDescription($file); if($description instanceof PluginDescription){ $name = $description->getName(); if(stripos($name, "pocketmine") !== false or stripos($name, "minecraft") !== false or stripos($name, "mojang") !== false){ $this->server->getLogger()->error($this->server->getLanguage()->translateString("pocketmine.plugin.loadError", [$name, "%pocketmine.plugin.restrictedName"])); continue; }elseif(strpos($name, " ") !== false){ $this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.plugin.spacesDiscouraged", [$name])); } if(isset($plugins[$name]) or $this->getPlugin($name) instanceof Plugin){ $this->server->getLogger()->error($this->server->getLanguage()->translateString("pocketmine.plugin.duplicateError", [$name])); continue; } $compatible = false; //Check multiple dependencies foreach($description->getCompatibleApis() as $version){ //Format: majorVersion.minorVersion.patch $version = array_map("intval", explode(".", $version)); $apiVersion = array_map("intval", explode(".", $this->server->getApiVersion())); //Completely different API version if($version[0] > $apiVersion[0]){ continue; } //If the plugin uses new API if($version[0] < $apiVersion[0]){ $compatible = true; break; } //If the plugin requires new API features, being backwards compatible if($version[1] > $apiVersion[1]){ continue; } $compatible = true; break; } $compatiblegeniapi = false; foreach($description->getCompatibleGeniApis() as $version){ //Format: majorVersion.minorVersion.patch $version = array_map("intval", explode(".", $version)); $apiVersion = array_map("intval", explode(".", $this->server->getGeniApiVersion())); //Completely different API version if($version[0] > $apiVersion[0]){ continue; } //If the plugin uses new API if($version[0] < $apiVersion[0]){ $compatiblegeniapi = true; break; } //If the plugin requires new API features, being backwards compatible if($version[1] > $apiVersion[1]){ continue; } if($version[1] == $apiVersion[1] and $version[2] > $apiVersion[2]){ continue; } $compatiblegeniapi = true; break; } if($compatible === false){ $this->server->getLogger()->error($this->server->getLanguage()->translateString("pocketmine.plugin.loadError", [$name, "%pocketmine.plugin.incompatibleAPI"])); continue; } if($compatiblegeniapi === false){ $this->server->getLogger()->error("Could not load plugin '{$description->getName()}': Incompatible GeniAPI version"); continue; } $plugins[$name] = $file; $softDependencies[$name] = (array) $description->getSoftDepend(); $dependencies[$name] = (array) $description->getDepend(); foreach($description->getLoadBefore() as $before){ if(isset($softDependencies[$before])){ $softDependencies[$before][] = $name; }else{ $softDependencies[$before] = [$name]; } } } }catch(\Throwable $e){ $this->server->getLogger()->error($this->server->getLanguage()->translateString("pocketmine.plugin.fileError", [$file, $directory, $e->getMessage()])); $this->server->getLogger()->logException($e); } } } while(count($plugins) > 0){ $missingDependency = true; foreach($plugins as $name => $file){ if(isset($dependencies[$name])){ foreach($dependencies[$name] as $key => $dependency){ if(isset($loadedPlugins[$dependency]) or $this->getPlugin($dependency) instanceof Plugin){ unset($dependencies[$name][$key]); }elseif(!isset($plugins[$dependency])){ $this->server->getLogger()->critical($this->server->getLanguage()->translateString("pocketmine.plugin.loadError", [$name, "%pocketmine.plugin.unknownDependency"])); break; } } if(count($dependencies[$name]) === 0){ unset($dependencies[$name]); } } if(isset($softDependencies[$name])){ foreach($softDependencies[$name] as $key => $dependency){ if(isset($loadedPlugins[$dependency]) or $this->getPlugin($dependency) instanceof Plugin){ unset($softDependencies[$name][$key]); } } if(count($softDependencies[$name]) === 0){ unset($softDependencies[$name]); } } if(!isset($dependencies[$name]) and !isset($softDependencies[$name])){ unset($plugins[$name]); $missingDependency = false; if($plugin = $this->loadPlugin($file, $loaders) and $plugin instanceof Plugin){ $loadedPlugins[$name] = $plugin; }else{ $this->server->getLogger()->critical($this->server->getLanguage()->translateString("pocketmine.plugin.genericLoadError", [$name])); } } } if($missingDependency === true){ foreach($plugins as $name => $file){ if(!isset($dependencies[$name])){ unset($softDependencies[$name]); unset($plugins[$name]); $missingDependency = false; if($plugin = $this->loadPlugin($file, $loaders) and $plugin instanceof Plugin){ $loadedPlugins[$name] = $plugin; }else{ $this->server->getLogger()->critical($this->server->getLanguage()->translateString("pocketmine.plugin.genericLoadError", [$name])); } } } //No plugins loaded :( if($missingDependency === true){ foreach($plugins as $name => $file){ $this->server->getLogger()->critical($this->server->getLanguage()->translateString("pocketmine.plugin.loadError", [$name, "%pocketmine.plugin.circularDependency"])); } $plugins = []; } } } TimingsCommand::$timingStart = microtime(true); return $loadedPlugins; }else{ TimingsCommand::$timingStart = microtime(true); return []; } } /** * @param string $name * * @return null|Permission */ public function getPermission($name){ if(isset($this->permissions[$name])){ return $this->permissions[$name]; } return null; } /** * @param Permission $permission * * @return bool */ public function addPermission(Permission $permission){ if(!isset($this->permissions[$permission->getName()])){ $this->permissions[$permission->getName()] = $permission; $this->calculatePermissionDefault($permission); return true; } return false; } /** * @param string|Permission $permission */ public function removePermission($permission){ if($permission instanceof Permission){ unset($this->permissions[$permission->getName()]); }else{ unset($this->permissions[$permission]); } } /** * @param boolean $op * * @return Permission[] */ public function getDefaultPermissions($op){ if($op === true){ return $this->defaultPermsOp; }else{ return $this->defaultPerms; } } /** * @param Permission $permission */ public function recalculatePermissionDefaults(Permission $permission){ if(isset($this->permissions[$permission->getName()])){ unset($this->defaultPermsOp[$permission->getName()]); unset($this->defaultPerms[$permission->getName()]); $this->calculatePermissionDefault($permission); } } /** * @param Permission $permission */ private function calculatePermissionDefault(Permission $permission){ Timings::$permissionDefaultTimer->startTiming(); if($permission->getDefault() === Permission::DEFAULT_OP or $permission->getDefault() === Permission::DEFAULT_TRUE){ $this->defaultPermsOp[$permission->getName()] = $permission; $this->dirtyPermissibles(true); } if($permission->getDefault() === Permission::DEFAULT_NOT_OP or $permission->getDefault() === Permission::DEFAULT_TRUE){ $this->defaultPerms[$permission->getName()] = $permission; $this->dirtyPermissibles(false); } Timings::$permissionDefaultTimer->stopTiming(); } /** * @param boolean $op */ private function dirtyPermissibles($op){ foreach($this->getDefaultPermSubscriptions($op) as $p){ $p->recalculatePermissions(); } } /** * @param string $permission * @param Permissible $permissible */ public function subscribeToPermission($permission, Permissible $permissible){ if(!isset($this->permSubs[$permission])){ $this->permSubs[$permission] = []; } $this->permSubs[$permission][spl_object_hash($permissible)] = $permissible; } /** * @param string $permission * @param Permissible $permissible */ public function unsubscribeFromPermission($permission, Permissible $permissible){ if(isset($this->permSubs[$permission])){ unset($this->permSubs[$permission][spl_object_hash($permissible)]); if(count($this->permSubs[$permission]) === 0){ unset($this->permSubs[$permission]); } } } /** * @param string $permission * * @return Permissible[] */ public function getPermissionSubscriptions($permission){ if(isset($this->permSubs[$permission])){ return $this->permSubs[$permission];//Return and ignore the further code $subs = []; foreach($this->permSubs[$permission] as $k => $perm){ /** @var \WeakRef $perm */ if($perm->acquire()){ $subs[] = $perm->get(); $perm->release(); }else{ unset($this->permSubs[$permission][$k]); } } return $subs; } return []; } /** * @param boolean $op * @param Permissible $permissible */ public function subscribeToDefaultPerms($op, Permissible $permissible){ if($op === true){ $this->defSubsOp[spl_object_hash($permissible)] = $permissible; }else{ $this->defSubs[spl_object_hash($permissible)] = $permissible; } } /** * @param boolean $op * @param Permissible $permissible */ public function unsubscribeFromDefaultPerms($op, Permissible $permissible){ if($op === true){ unset($this->defSubsOp[spl_object_hash($permissible)]); }else{ unset($this->defSubs[spl_object_hash($permissible)]); } } /** * @param boolean $op * * @return Permissible[] */ public function getDefaultPermSubscriptions($op){ $subs = []; if($op === true){ return $this->defSubsOp;//Return and ignore further code foreach($this->defSubsOp as $k => $perm){ /** @var \WeakRef $perm */ if($perm->acquire()){ $subs[] = $perm->get(); $perm->release(); }else{ unset($this->defSubsOp[$k]); } } }else{ return $this->defSubs;//Return and ignore further code foreach($this->defSubs as $k => $perm){ /** @var \WeakRef $perm */ if($perm->acquire()){ $subs[] = $perm->get(); $perm->release(); }else{ unset($this->defSubs[$k]); } } } return $subs; } /** * @return Permission[] */ public function getPermissions(){ return $this->permissions; } /** * @param Plugin $plugin * * @return bool */ public function isPluginEnabled(Plugin $plugin){ if($plugin instanceof Plugin and isset($this->plugins[$plugin->getDescription()->getName()])){ return $plugin->isEnabled(); }else{ return false; } } /** * @param Plugin $plugin */ public function enablePlugin(Plugin $plugin){ if(!$plugin->isEnabled()){ try{ foreach($plugin->getDescription()->getPermissions() as $perm){ $this->addPermission($perm); } $plugin->getPluginLoader()->enablePlugin($plugin); }catch(\Throwable $e){ $this->server->getLogger()->logException($e); $this->disablePlugin($plugin); } } } /** * @param Plugin $plugin * * @return PluginCommand[] */ protected function parseYamlCommands(Plugin $plugin){ $pluginCmds = []; foreach($plugin->getDescription()->getCommands() as $key => $data){ if(strpos($key, ":") !== false){ $this->server->getLogger()->critical($this->server->getLanguage()->translateString("pocketmine.plugin.commandError", [$key, $plugin->getDescription()->getFullName()])); continue; } if(is_array($data)){ $newCmd = new PluginCommand($key, $plugin); if(isset($data["description"])){ $newCmd->setDescription($data["description"]); } if(isset($data["usage"])){ $newCmd->setUsage($data["usage"]); } if(isset($data["aliases"]) and is_array($data["aliases"])){ $aliasList = []; foreach($data["aliases"] as $alias){ if(strpos($alias, ":") !== false){ $this->server->getLogger()->critical($this->server->getLanguage()->translateString("pocketmine.plugin.aliasError", [$alias, $plugin->getDescription()->getFullName()])); continue; } $aliasList[] = $alias; } $newCmd->setAliases($aliasList); } if(isset($data["permission"])){ $newCmd->setPermission($data["permission"]); } if(isset($data["permission-message"])){ $newCmd->setPermissionMessage($data["permission-message"]); } $pluginCmds[] = $newCmd; } } return $pluginCmds; } public function disablePlugins(){ foreach($this->getPlugins() as $plugin){ $this->disablePlugin($plugin); } } /** * @param Plugin $plugin */ public function disablePlugin(Plugin $plugin){ if($plugin->isEnabled()){ try{ $plugin->getPluginLoader()->disablePlugin($plugin); }catch(\Throwable $e){ $this->server->getLogger()->logException($e); } $this->server->getScheduler()->cancelTasks($plugin); HandlerList::unregisterAll($plugin); foreach($plugin->getDescription()->getPermissions() as $perm){ $this->removePermission($perm); } } } public function clearPlugins(){ $this->disablePlugins(); $this->plugins = []; $this->fileAssociations = []; $this->permissions = []; $this->defaultPerms = []; $this->defaultPermsOp = []; } /** * Calls an event * * @param Event $event */ public function callEvent(Event $event){ foreach($event->getHandlers()->getRegisteredListeners() as $registration){ if(!$registration->getPlugin()->isEnabled()){ continue; } try{ $registration->callEvent($event); }catch(\Throwable $e){ $this->server->getLogger()->critical( $this->server->getLanguage()->translateString("pocketmine.plugin.eventError", [ $event->getEventName(), $registration->getPlugin()->getDescription()->getFullName(), $e->getMessage(), get_class($registration->getListener()) ])); $this->server->getLogger()->logException($e); } } } /** * Registers all the events in the given Listener class * * @param Listener $listener * @param Plugin $plugin * * @throws PluginException */ public function registerEvents(Listener $listener, Plugin $plugin){ if(!$plugin->isEnabled()){ throw new PluginException("Plugin attempted to register " . get_class($listener) . " while not enabled"); } $reflection = new \ReflectionClass(get_class($listener)); foreach($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method){ if(!$method->isStatic()){ $priority = EventPriority::NORMAL; $ignoreCancelled = false; if(preg_match("/^[\t ]*\\* @priority[\t ]{1,}([a-zA-Z]{1,})/m", (string) $method->getDocComment(), $matches) > 0){ $matches[1] = strtoupper($matches[1]); if(defined(EventPriority::class . "::" . $matches[1])){ $priority = constant(EventPriority::class . "::" . $matches[1]); } } if(preg_match("/^[\t ]*\\* @ignoreCancelled[\t ]{1,}([a-zA-Z]{1,})/m", (string) $method->getDocComment(), $matches) > 0){ $matches[1] = strtolower($matches[1]); if($matches[1] === "false"){ $ignoreCancelled = false; }elseif($matches[1] === "true"){ $ignoreCancelled = true; } } $parameters = $method->getParameters(); if(count($parameters) === 1 and $parameters[0]->getClass() instanceof \ReflectionClass and is_subclass_of($parameters[0]->getClass()->getName(), Event::class)){ $class = $parameters[0]->getClass()->getName(); $reflection = new \ReflectionClass($class); if(strpos((string) $reflection->getDocComment(), "@deprecated") !== false and $this->server->getProperty("settings.deprecated-verbose", true)){ $this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.plugin.deprecatedEvent", [ $plugin->getName(), $class, get_class($listener) . "->" . $method->getName() . "()" ])); } $this->registerEvent($class, $listener, $priority, new MethodEventExecutor($method->getName()), $plugin, $ignoreCancelled); } } } } /** * @param string $event Class name that extends Event * @param Listener $listener * @param int $priority * @param EventExecutor $executor * @param Plugin $plugin * @param bool $ignoreCancelled * * @throws PluginException */ public function registerEvent($event, Listener $listener, $priority, EventExecutor $executor, Plugin $plugin, $ignoreCancelled = false){ if(!is_subclass_of($event, Event::class)){ throw new PluginException($event . " is not an Event"); } $class = new \ReflectionClass($event); if($class->isAbstract()){ throw new PluginException($event . " is an abstract Event"); } if($class->getProperty("handlerList")->getDeclaringClass()->getName() !== $event){ throw new PluginException($event . " does not have a handler list"); } if(!$plugin->isEnabled()){ throw new PluginException("Plugin attempted to register " . $event . " while not enabled"); } $timings = new TimingsHandler("Plugin: " . $plugin->getDescription()->getFullName() . " Event: " . get_class($listener) . "::" . ($executor instanceof MethodEventExecutor ? $executor->getMethod() : "???") . "(" . (new \ReflectionClass($event))->getShortName() . ")", self::$pluginParentTimer); $this->getEventListeners($event)->register(new RegisteredListener($listener, $executor, $priority, $plugin, $ignoreCancelled, $timings)); } /** * @param $event * * @return HandlerList */ private function getEventListeners($event){ if($event::$handlerList === null){ $event::$handlerList = new HandlerList(); } return $event::$handlerList; } /** * @return bool */ public function useTimings(){ return self::$useTimings; } /** * @param bool $use */ public function setUseTimings($use){ self::$useTimings = (bool) $use; } } ================================================ FILE: src/pocketmine/plugin/RegisteredListener.php ================================================ listener = $listener; $this->priority = $priority; $this->plugin = $plugin; $this->executor = $executor; $this->ignoreCancelled = $ignoreCancelled; $this->timings = $timings; } /** * @return Listener */ public function getListener(){ return $this->listener; } /** * @return Plugin */ public function getPlugin(){ return $this->plugin; } /** * @return int */ public function getPriority(){ return $this->priority; } /** * @param Event $event */ public function callEvent(Event $event){ if($event instanceof Cancellable and $event->isCancelled() and $this->isIgnoringCancelled()){ return; } $this->timings->startTiming(); $this->executor->execute($this->listener, $event); $this->timings->stopTiming(); } public function __destruct(){ $this->timings->remove(); } /** * @return bool */ public function isIgnoringCancelled(){ return $this->ignoreCancelled === true; } } ================================================ FILE: src/pocketmine/plugin/ScriptPluginLoader.php ================================================ server = $server; } /** * Loads the plugin contained in $file * * @param string $file * * @return Plugin * * @throws \Throwable */ public function loadPlugin($file){ if(($description = $this->getPluginDescription($file)) instanceof PluginDescription){ $this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.plugin.load", [$description->getFullName()])); $dataFolder = dirname($file) . DIRECTORY_SEPARATOR . $description->getName(); if(file_exists($dataFolder) and !is_dir($dataFolder)){ throw new \InvalidStateException("Projected dataFolder '" . $dataFolder . "' for " . $description->getName() . " exists and is not a directory"); } include_once($file); $className = $description->getMain(); if(class_exists($className, true)){ $plugin = new $className(); $this->initPlugin($plugin, $description, $dataFolder, $file); return $plugin; }else{ throw new PluginException("Couldn't load plugin " . $description->getName() . ": main class not found"); } } return null; } /** * Gets the PluginDescription from the file * * @param string $file * * @return PluginDescription */ public function getPluginDescription($file){ $content = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); $data = []; $insideHeader = false; foreach($content as $line){ if(!$insideHeader and strpos($line, "/**") !== false){ $insideHeader = true; } if(preg_match("/^[ \t]+\\*[ \t]+@([a-zA-Z]+)([ \t]+(.*))?$/", $line, $matches) > 0){ $key = $matches[1]; $content = trim($matches[3] ?? ""); if($key === "notscript"){ return null; } $data[$key] = $content; } if($insideHeader and strpos($line, "*/") !== false){ break; } } if($insideHeader){ return new PluginDescription($data); } return null; } /** * Returns the filename patterns that this loader accepts * * @return array */ public function getPluginFilters(){ return "/\\.php$/i"; } /** * @param PluginBase $plugin * @param PluginDescription $description * @param string $dataFolder * @param string $file */ private function initPlugin(PluginBase $plugin, PluginDescription $description, $dataFolder, $file){ $plugin->init($this, $this->server, $description, $dataFolder, $file); $plugin->onLoad(); } /** * @param Plugin $plugin */ public function enablePlugin(Plugin $plugin){ if($plugin instanceof PluginBase and !$plugin->isEnabled()){ $this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.plugin.enable", [$plugin->getDescription()->getFullName()])); $plugin->setEnabled(true); $this->server->getPluginManager()->callEvent(new PluginEnableEvent($plugin)); } } /** * @param Plugin $plugin */ public function disablePlugin(Plugin $plugin){ if($plugin instanceof PluginBase and $plugin->isEnabled()){ $this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.plugin.disable", [$plugin->getDescription()->getFullName()])); $this->server->getPluginManager()->callEvent(new PluginDisableEvent($plugin)); $plugin->setEnabled(false); } } } ================================================ FILE: src/pocketmine/resourcepacks/ResourcePackInfoEntry.php ================================================ packId = $packId; $this->version = $version; $this->packSize = $packSize; } public function getPackId() : string{ return $this->packId; } public function getVersion() : string{ return $this->version; } public function getPackSize(){ return $this->packSize; } } ================================================ FILE: src/pocketmine/resources/command_default.json ================================================ { "aliases": [], "description": "insert_description_here", "overloads": { "default": { "input": { "parameters": [ { "name": "args", "type": "rawtext", "optional": true } ] }, "output": {} } }, "permission": "any" } ================================================ FILE: src/pocketmine/resources/creativeitems.json ================================================ [ { "id": 138, "damage": 0, "count": 1, "nbt": "" }, { "id": 66, "damage": 0, "count": 1, "nbt": "" }, { "id": 27, "damage": 0, "count": 1, "nbt": "" }, { "id": 28, "damage": 0, "count": 1, "nbt": "" }, { "id": 126, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 98, "damage": 0, "count": 1, "nbt": "" }, { "id": 98, "damage": 1, "count": 1, "nbt": "" }, { "id": 98, "damage": 2, "count": 1, "nbt": "" }, { "id": 98, "damage": 3, "count": 1, "nbt": "" }, { "id": 48, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 45, "damage": 0, "count": 1, "nbt": "" }, { "id": 1, "damage": 0, "count": 1, "nbt": "" }, { "id": 1, "damage": 1, "count": 1, "nbt": "" }, { "id": 1, "damage": 2, "count": 1, "nbt": "" }, { "id": 1, "damage": 3, "count": 1, "nbt": "" }, { "id": 1, "damage": 4, "count": 1, "nbt": "" }, { "id": 1, "damage": 5, "count": 1, "nbt": "" }, { "id": 1, "damage": 6, "count": 1, "nbt": "" }, { "id": 3, "damage": 0, "count": 1, "nbt": "" }, { "id": 243, "damage": 0, "count": 1, "nbt": "" }, { "id": 2, "damage": 0, "count": 1, "nbt": "" }, { "id": 110, "damage": 0, "count": 1, "nbt": "" }, { "id": 82, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 159, "damage": 0, "count": 1, "nbt": "" }, { "id": 159, "damage": 1, "count": 1, "nbt": "" }, { "id": 159, "damage": 2, "count": 1, "nbt": "" }, { "id": 159, "damage": 3, "count": 1, "nbt": "" }, { "id": 159, "damage": 4, "count": 1, "nbt": "" }, { "id": 159, "damage": 5, "count": 1, "nbt": "" }, { "id": 159, "damage": 6, "count": 1, "nbt": "" }, { "id": 159, "damage": 7, "count": 1, "nbt": "" }, { "id": 159, "damage": 8, "count": 1, "nbt": "" }, { "id": 159, "damage": 9, "count": 1, "nbt": "" }, { "id": 159, "damage": 10, "count": 1, "nbt": "" }, { "id": 159, "damage": 11, "count": 1, "nbt": "" }, { "id": 159, "damage": 12, "count": 1, "nbt": "" }, { "id": 159, "damage": 13, "count": 1, "nbt": "" }, { "id": 159, "damage": 14, "count": 1, "nbt": "" }, { "id": 159, "damage": 15, "count": 1, "nbt": "" }, { "id": 24, "damage": 0, "count": 1, "nbt": "" }, { "id": 24, "damage": 1, "count": 1, "nbt": "" }, { "id": 24, "damage": 2, "count": 1, "nbt": "" }, { "id": 179, "damage": 0, "count": 1, "nbt": "" }, { "id": 179, "damage": 1, "count": 1, "nbt": "" }, { "id": 179, "damage": 2, "count": 1, "nbt": "" }, { "id": 12, "damage": 0, "count": 1, "nbt": "" }, { "id": 12, "damage": 1, "count": 1, "nbt": "" }, { "id": 13, "damage": 0, "count": 1, "nbt": "" }, { "id": 139, "damage": 0, "count": 1, "nbt": "" }, { "id": 139, "damage": 1, "count": 1, "nbt": "" }, { "id": 17, "damage": 0, "count": 1, "nbt": "" }, { "id": 17, "damage": 1, "count": 1, "nbt": "" }, { "id": 17, "damage": 2, "count": 1, "nbt": "" }, { "id": 17, "damage": 3, "count": 1, "nbt": "" }, { "id": 162, "damage": 0, "count": 1, "nbt": "" }, { "id": 162, "damage": 1, "count": 1, "nbt": "" }, { "id": 112, "damage": 0, "count": 1, "nbt": "" }, { "id": 87, "damage": 0, "count": 1, "nbt": "" }, { "id": 88, "damage": 0, "count": 1, "nbt": "" }, { "id": 7, "damage": 0, "count": 1, "nbt": "" }, { "id": 67, "damage": 0, "count": 1, "nbt": "" }, { "id": 53, "damage": 0, "count": 1, "nbt": "" }, { "id": 134, "damage": 0, "count": 1, "nbt": "" }, { "id": 135, "damage": 0, "count": 1, "nbt": "" }, { "id": 136, "damage": 0, "count": 1, "nbt": "" }, { "id": 163, "damage": 0, "count": 1, "nbt": "" }, { "id": 164, "damage": 0, "count": 1, "nbt": "" }, { "id": 108, "damage": 0, "count": 1, "nbt": "" }, { "id": 128, "damage": 0, "count": 1, "nbt": "" }, { "id": 180, "damage": 0, "count": 1, "nbt": "" }, { "id": 109, "damage": 0, "count": 1, "nbt": "" }, { "id": 114, "damage": 0, "count": 1, "nbt": "" }, { "id": 156, "damage": 0, "count": 1, "nbt": "" }, { "id": 203, "damage": 0, "count": 1, "nbt": "" }, { "id": 44, "damage": 0, "count": 1, "nbt": "" }, { "id": 44, "damage": 3, "count": 1, "nbt": "" }, { "id": 111, "damage": 0, "count": 1, "nbt": "" }, { "id": 158, "damage": 0, "count": 1, "nbt": "" }, { "id": 158, "damage": 1, "count": 1, "nbt": "" }, { "id": 158, "damage": 2, "count": 1, "nbt": "" }, { "id": 158, "damage": 3, "count": 1, "nbt": "" }, { "id": 158, "damage": 4, "count": 1, "nbt": "" }, { "id": 158, "damage": 5, "count": 1, "nbt": "" }, { "id": 44, "damage": 4, "count": 1, "nbt": "" }, { "id": 44, "damage": 1, "count": 1, "nbt": "" }, { "id": 182, "damage": 0, "count": 1, "nbt": "" }, { "id": 182, "damage": 1, "count": 1, "nbt": "" }, { "id": 44, "damage": 5, "count": 1, "nbt": "" }, { "id": 44, "damage": 7, "count": 1, "nbt": "" }, { "id": 44, "damage": 6, "count": 1, "nbt": "" }, { "id": 182, "damage": 1, "count": 1, "nbt": "" }, { "id": 155, "damage": 0, "count": 1, "nbt": "" }, { "id": 155, "damage": 2, "count": 1, "nbt": "" }, { "id": 155, "damage": 1, "count": 1, "nbt": "" }, { "id": 168, "damage": 0, "count": 1, "nbt": "" }, { "id": 168, "damage": 2, "count": 1, "nbt": "" }, { "id": 168, "damage": 1, "count": 1, "nbt": "" }, { "id": 169, "damage": 0, "count": 1, "nbt": "" }, { "id": 201, "damage": 0, "count": 1, "nbt": "" }, { "id": 201, "damage": 2, "count": 1, "nbt": "" }, { "id": 240, "damage": 0, "count": 1, "nbt": "" }, { "id": 200, "damage": 0, "count": 1, "nbt": "" }, { "id": 263, "damage": 0, "count": 1, "nbt": "" }, { "id": 263, "damage": 1, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 388, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 281, "damage": 0, "count": 1, "nbt": "" }, { "id": 287, "damage": 0, "count": 1, "nbt": "" }, { "id": 288, "damage": 0, "count": 1, "nbt": "" }, { "id": 318, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 415, "damage": 0, "count": 1, "nbt": "" }, { "id": 337, "damage": 0, "count": 1, "nbt": "" }, { "id": 353, "damage": 0, "count": 1, "nbt": "" }, { "id": 336, "damage": 0, "count": 1, "nbt": "" }, { "id": 405, "damage": 0, "count": 1, "nbt": "" }, { "id": 16, "damage": 0, "count": 1, "nbt": "" }, { "id": 15, "damage": 0, "count": 1, "nbt": "" }, { "id": 14, "damage": 0, "count": 1, "nbt": "" }, { "id": 56, "damage": 0, "count": 1, "nbt": "" }, { "id": 21, "damage": 0, "count": 1, "nbt": "" }, { "id": 73, "damage": 0, "count": 1, "nbt": "" }, { "id": 129, "damage": 0, "count": 1, "nbt": "" }, { "id": 153, "damage": 0, "count": 1, "nbt": "" }, { "id": 41, "damage": 0, "count": 1, "nbt": "" }, { "id": 42, "damage": 0, "count": 1, "nbt": "" }, { "id": 57, "damage": 0, "count": 1, "nbt": "" }, { "id": 22, "damage": 0, "count": 1, "nbt": "" }, { "id": 173, "damage": 0, "count": 1, "nbt": "" }, { "id": 133, "damage": 0, "count": 1, "nbt": "" }, { "id": 152, "damage": 0, "count": 1, "nbt": "" }, { "id": 406, "damage": 0, "count": 1, "nbt": "" }, { "id": 49, "damage": 0, "count": 1, "nbt": "" }, { "id": 79, "damage": 0, "count": 1, "nbt": "" }, { "id": 174, "damage": 0, "count": 1, "nbt": "" }, { "id": 80, "damage": 0, "count": 1, "nbt": "" }, { "id": 78, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 89, "damage": 0, "count": 1, "nbt": "" }, { "id": 106, "damage": 0, "count": 1, "nbt": "" }, { "id": 65, "damage": 0, "count": 1, "nbt": "" }, { "id": 19, "damage": 0, "count": 1, "nbt": "" }, { "id": 19, "damage": 0, "count": 1, "nbt": "" }, { "id": 50, "damage": 0, "count": 1, "nbt": "" }, { "id": 102, "damage": 0, "count": 1, "nbt": "" }, { "id": 325, "damage": 0, "count": 1, "nbt": "" }, { "id": 325, "damage": 1, "count": 1, "nbt": "" }, { "id": 325, "damage": 8, "count": 1, "nbt": "" }, { "id": 325, "damage": 10, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 340, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 352, "damage": 0, "count": 1, "nbt": "" }, { "id": 324, "damage": 0, "count": 1, "nbt": "" }, { "id": 427, "damage": 0, "count": 1, "nbt": "" }, { "id": 428, "damage": 0, "count": 1, "nbt": "" }, { "id": 429, "damage": 0, "count": 1, "nbt": "" }, { "id": 430, "damage": 0, "count": 1, "nbt": "" }, { "id": 431, "damage": 0, "count": 1, "nbt": "" }, { "id": 330, "damage": 0, "count": 1, "nbt": "" }, { "id": 395, "damage": 0, "count": 1, "nbt": "" }, { "id": 96, "damage": 0, "count": 1, "nbt": "" }, { "id": 167, "damage": 0, "count": 1, "nbt": "" }, { "id": 85, "damage": 0, "count": 1, "nbt": "" }, { "id": 85, "damage": 1, "count": 1, "nbt": "" }, { "id": 85, "damage": 2, "count": 1, "nbt": "" }, { "id": 85, "damage": 3, "count": 1, "nbt": "" }, { "id": 85, "damage": 4, "count": 1, "nbt": "" }, { "id": 85, "damage": 5, "count": 1, "nbt": "" }, { "id": 113, "damage": 0, "count": 1, "nbt": "" }, { "id": 107, "damage": 0, "count": 1, "nbt": "" }, { "id": 183, "damage": 0, "count": 1, "nbt": "" }, { "id": 184, "damage": 0, "count": 1, "nbt": "" }, { "id": 185, "damage": 0, "count": 1, "nbt": "" }, { "id": 187, "damage": 0, "count": 1, "nbt": "" }, { "id": 186, "damage": 0, "count": 1, "nbt": "" }, { "id": 101, "damage": 0, "count": 1, "nbt": "" }, { "id": 355, "damage": 0, "count": 1, "nbt": "" }, { "id": 47, "damage": 0, "count": 1, "nbt": "" }, { "id": 323, "damage": 0, "count": 1, "nbt": "" }, { "id": 321, "damage": 0, "count": 1, "nbt": "" }, { "id": 389, "damage": 0, "count": 1, "nbt": "" }, { "id": 58, "damage": 0, "count": 1, "nbt": "" }, { "id": 245, "damage": 0, "count": 1, "nbt": "" }, { "id": 54, "damage": 0, "count": 1, "nbt": "" }, { "id": 146, "damage": 0, "count": 1, "nbt": "" }, { "id": 61, "damage": 0, "count": 1, "nbt": "" }, { "id": 379, "damage": 0, "count": 1, "nbt": "" }, { "id": 380, "damage": 0, "count": 1, "nbt": "" }, { "id": 25, "damage": 0, "count": 1, "nbt": "" }, { "id": 46, "damage": 0, "count": 1, "nbt": "" }, { "id": 206, "damage": 0, "count": 1, "nbt": "" }, { "id": 121, "damage": 0, "count": 1, "nbt": "" }, { "id": 208, "damage": 0, "count": 1, "nbt": "" }, { "id": 120, "damage": 0, "count": 1, "nbt": "" }, { "id": 145, "damage": 0, "count": 1, "nbt": "" }, { "id": 145, "damage": 4, "count": 1, "nbt": "" }, { "id": 145, "damage": 8, "count": 1, "nbt": "" }, { "id": 37, "damage": 0, "count": 1, "nbt": "" }, { "id": 38, "damage": 0, "count": 1, "nbt": "" }, { "id": 38, "damage": 1, "count": 1, "nbt": "" }, { "id": 38, "damage": 2, "count": 1, "nbt": "" }, { "id": 38, "damage": 3, "count": 1, "nbt": "" }, { "id": 38, "damage": 4, "count": 1, "nbt": "" }, { "id": 38, "damage": 5, "count": 1, "nbt": "" }, { "id": 38, "damage": 6, "count": 1, "nbt": "" }, { "id": 38, "damage": 7, "count": 1, "nbt": "" }, { "id": 38, "damage": 8, "count": 1, "nbt": "" }, { "id": 175, "damage": 0, "count": 1, "nbt": "" }, { "id": 175, "damage": 1, "count": 1, "nbt": "" }, { "id": 175, "damage": 2, "count": 1, "nbt": "" }, { "id": 175, "damage": 3, "count": 1, "nbt": "" }, { "id": 175, "damage": 4, "count": 1, "nbt": "" }, { "id": 175, "damage": 5, "count": 1, "nbt": "" }, { "id": 39, "damage": 0, "count": 1, "nbt": "" }, { "id": 40, "damage": 0, "count": 1, "nbt": "" }, { "id": 99, "damage": 14, "count": 1, "nbt": "" }, { "id": 100, "damage": 14, "count": 1, "nbt": "" }, { "id": 99, "damage": 0, "count": 1, "nbt": "" }, { "id": 99, "damage": 15, "count": 1, "nbt": "" }, { "id": 81, "damage": 0, "count": 1, "nbt": "" }, { "id": 103, "damage": 0, "count": 1, "nbt": "" }, { "id": 86, "damage": 0, "count": 1, "nbt": "" }, { "id": 91, "damage": 0, "count": 1, "nbt": "" }, { "id": 30, "damage": 0, "count": 1, "nbt": "" }, { "id": 170, "damage": 0, "count": 1, "nbt": "" }, { "id": 338, "damage": 0, "count": 1, "nbt": "" }, { "id": 296, "damage": 0, "count": 1, "nbt": "" }, { "id": 31, "damage": 1, "count": 1, "nbt": "" }, { "id": 31, "damage": 2, "count": 1, "nbt": "" }, { "id": 32, "damage": 0, "count": 1, "nbt": "" }, { "id": 6, "damage": 0, "count": 1, "nbt": "" }, { "id": 6, "damage": 1, "count": 1, "nbt": "" }, { "id": 6, "damage": 2, "count": 1, "nbt": "" }, { "id": 6, "damage": 3, "count": 1, "nbt": "" }, { "id": 6, "damage": 4, "count": 1, "nbt": "" }, { "id": 6, "damage": 5, "count": 1, "nbt": "" }, { "id": 18, "damage": 0, "count": 1, "nbt": "" }, { "id": 18, "damage": 1, "count": 1, "nbt": "" }, { "id": 18, "damage": 2, "count": 1, "nbt": "" }, { "id": 18, "damage": 3, "count": 1, "nbt": "" }, { "id": 161, "damage": 0, "count": 1, "nbt": "" }, { "id": 161, "damage": 1, "count": 1, "nbt": "" }, { "id": 262, "damage": 6, "count": 1, "nbt": "" }, { "id": 262, "damage": 7, "count": 1, "nbt": "" }, { "id": 262, "damage": 8, "count": 1, "nbt": "" }, { "id": 262, "damage": 9, "count": 1, "nbt": "" }, { "id": 262, "damage": 10, "count": 1, "nbt": "" }, { "id": 262, "damage": 11, "count": 1, "nbt": "" }, { "id": 262, "damage": 12, "count": 1, "nbt": "" }, { "id": 262, "damage": 13, "count": 1, "nbt": "" }, { "id": 262, "damage": 14, "count": 1, "nbt": "" }, { "id": 262, "damage": 15, "count": 1, "nbt": "" }, { "id": 262, "damage": 16, "count": 1, "nbt": "" }, { "id": 262, "damage": 17, "count": 1, "nbt": "" }, { "id": 262, "damage": 18, "count": 1, "nbt": "" }, { "id": 262, "damage": 19, "count": 1, "nbt": "" }, { "id": 262, "damage": 20, "count": 1, "nbt": "" }, { "id": 262, "damage": 21, "count": 1, "nbt": "" }, { "id": 262, "damage": 22, "count": 1, "nbt": "" }, { "id": 262, "damage": 23, "count": 1, "nbt": "" }, { "id": 262, "damage": 24, "count": 1, "nbt": "" }, { "id": 262, "damage": 25, "count": 1, "nbt": "" }, { "id": 262, "damage": 26, "count": 1, "nbt": "" }, { "id": 262, "damage": 27, "count": 1, "nbt": "" }, { "id": 262, "damage": 28, "count": 1, "nbt": "" }, { "id": 262, "damage": 29, "count": 1, "nbt": "" }, { "id": 262, "damage": 30, "count": 1, "nbt": "" }, { "id": 262, "damage": 31, "count": 1, "nbt": "" }, { "id": 262, "damage": 32, "count": 1, "nbt": "" }, { "id": 262, "damage": 33, "count": 1, "nbt": "" }, { "id": 262, "damage": 34, "count": 1, "nbt": "" }, { "id": 262, "damage": 35, "count": 1, "nbt": "" }, { "id": 262, "damage": 36, "count": 1, "nbt": "" }, { "id": 262, "damage": 37, "count": 1, "nbt": "" }, { "id": 295, "damage": 0, "count": 1, "nbt": "" }, { "id": 361, "damage": 0, "count": 1, "nbt": "" }, { "id": 362, "damage": 0, "count": 1, "nbt": "" }, { "id": 458, "damage": 0, "count": 1, "nbt": "" }, { "id": 344, "damage": 0, "count": 1, "nbt": "" }, { "id": 260, "damage": 0, "count": 1, "nbt": "" }, { "id": 322, "damage": 0, "count": 1, "nbt": "" }, { "id": 466, "damage": 0, "count": 1, "nbt": "" }, { "id": 349, "damage": 0, "count": 1, "nbt": "" }, { "id": 460, "damage": 0, "count": 1, "nbt": "" }, { "id": 461, "damage": 0, "count": 1, "nbt": "" }, { "id": 462, "damage": 0, "count": 1, "nbt": "" }, { "id": 350, "damage": 0, "count": 1, "nbt": "" }, { "id": 463, "damage": 0, "count": 1, "nbt": "" }, { "id": 367, "damage": 0, "count": 1, "nbt": "" }, { "id": 282, "damage": 0, "count": 1, "nbt": "" }, { "id": 297, "damage": 0, "count": 1, "nbt": "" }, { "id": 319, "damage": 0, "count": 1, "nbt": "" }, { "id": 320, "damage": 0, "count": 1, "nbt": "" }, { "id": 365, "damage": 0, "count": 1, "nbt": "" }, { "id": 366, "damage": 0, "count": 1, "nbt": "" }, { "id": 423, "damage": 0, "count": 1, "nbt": "" }, { "id": 424, "damage": 0, "count": 1, "nbt": "" }, { "id": 363, "damage": 0, "count": 1, "nbt": "" }, { "id": 364, "damage": 0, "count": 1, "nbt": "" }, { "id": 360, "damage": 0, "count": 1, "nbt": "" }, { "id": 391, "damage": 0, "count": 1, "nbt": "" }, { "id": 392, "damage": 0, "count": 1, "nbt": "" }, { "id": 393, "damage": 0, "count": 1, "nbt": "" }, { "id": 394, "damage": 0, "count": 1, "nbt": "" }, { "id": 457, "damage": 0, "count": 1, "nbt": "" }, { "id": 459, "damage": 0, "count": 1, "nbt": "" }, { "id": 354, "damage": 0, "count": 1, "nbt": "" }, { "id": 357, "damage": 0, "count": 1, "nbt": "" }, { "id": 400, "damage": 0, "count": 1, "nbt": "" }, { "id": 411, "damage": 0, "count": 1, "nbt": "" }, { "id": 412, "damage": 0, "count": 1, "nbt": "" }, { "id": 413, "damage": 0, "count": 1, "nbt": "" }, { "id": 432, "damage": 0, "count": 1, "nbt": "" }, { "id": 433, "damage": 0, "count": 1, "nbt": "" }, { "id": 399, "damage": 0, "count": 1, "nbt": "" }, { "id": 420, "damage": 0, "count": 1, "nbt": "" }, { "id": 421, "damage": 0, "count": 1, "nbt": "" }, { "id": 378, "damage": 0, "count": 1, "nbt": "" }, { "id": 369, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 396, "damage": 0, "count": 1, "nbt": "" }, { "id": 382, "damage": 0, "count": 1, "nbt": "" }, { "id": 414, "damage": 0, "count": 1, "nbt": "" }, { "id": 370, "damage": 0, "count": 1, "nbt": "" }, { "id": 341, "damage": 0, "count": 1, "nbt": "" }, { "id": 377, "damage": 0, "count": 1, "nbt": "" }, { "id": 372, "damage": 0, "count": 1, "nbt": "" }, { "id": 289, "damage": 0, "count": 1, "nbt": "" }, { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 348, "damage": 0, "count": 1, "nbt": "" }, { "id": 375, "damage": 0, "count": 1, "nbt": "" }, { "id": 376, "damage": 0, "count": 1, "nbt": "" }, { "id": 437, "damage": 0, "count": 1, "nbt": "" }, { "id": 397, "damage": 0, "count": 1, "nbt": "" }, { "id": 397, "damage": 1, "count": 1, "nbt": "" }, { "id": 397, "damage": 2, "count": 1, "nbt": "" }, { "id": 397, "damage": 3, "count": 1, "nbt": "" }, { "id": 397, "damage": 4, "count": 1, "nbt": "" }, { "id": 397, "damage": 5, "count": 1, "nbt": "" }, { "id": 261, "damage": 0, "count": 1, "nbt": "" }, { "id": 346, "damage": 0, "count": 1, "nbt": "" }, { "id": 259, "damage": 0, "count": 1, "nbt": "" }, { "id": 359, "damage": 0, "count": 1, "nbt": "" }, { "id": 347, "damage": 0, "count": 1, "nbt": "" }, { "id": 345, "damage": 0, "count": 1, "nbt": "" }, { "id": 398, "damage": 0, "count": 1, "nbt": "" }, { "id": 328, "damage": 0, "count": 1, "nbt": "" }, { "id": 342, "damage": 0, "count": 1, "nbt": "" }, { "id": 408, "damage": 0, "count": 1, "nbt": "" }, { "id": 407, "damage": 0, "count": 1, "nbt": "" }, { "id": 333, "damage": 0, "count": 1, "nbt": "" }, { "id": 333, "damage": 1, "count": 1, "nbt": "" }, { "id": 333, "damage": 2, "count": 1, "nbt": "" }, { "id": 333, "damage": 3, "count": 1, "nbt": "" }, { "id": 333, "damage": 4, "count": 1, "nbt": "" }, { "id": 333, "damage": 5, "count": 1, "nbt": "" }, { "id": 329, "damage": 0, "count": 1, "nbt": "" }, { "id": 416, "damage": 0, "count": 1, "nbt": "" }, { "id": 417, "damage": 0, "count": 1, "nbt": "" }, { "id": 418, "damage": 0, "count": 1, "nbt": "" }, { "id": 419, "damage": 0, "count": 1, "nbt": "" }, { "id": 390, "damage": 0, "count": 1, "nbt": "" }, { "id": 383, "damage": 15, "count": 1, "nbt": "" }, { "id": 383, "damage": 10, "count": 1, "nbt": "" }, { "id": 383, "damage": 11, "count": 1, "nbt": "" }, { "id": 383, "damage": 12, "count": 1, "nbt": "" }, { "id": 383, "damage": 13, "count": 1, "nbt": "" }, { "id": 383, "damage": 14, "count": 1, "nbt": "" }, { "id": 383, "damage": 28, "count": 1, "nbt": "" }, { "id": 383, "damage": 22, "count": 1, "nbt": "" }, { "id": 383, "damage": 16, "count": 1, "nbt": "" }, { "id": 383, "damage": 19, "count": 1, "nbt": "" }, { "id": 383, "damage": 18, "count": 1, "nbt": "" }, { "id": 383, "damage": 23, "count": 1, "nbt": "" }, { "id": 383, "damage": 24, "count": 1, "nbt": "" }, { "id": 383, "damage": 25, "count": 1, "nbt": "" }, { "id": 383, "damage": 26, "count": 1, "nbt": "" }, { "id": 383, "damage": 27, "count": 1, "nbt": "" }, { "id": 383, "damage": 33, "count": 1, "nbt": "" }, { "id": 383, "damage": 38, "count": 1, "nbt": "" }, { "id": 383, "damage": 39, "count": 1, "nbt": "" }, { "id": 383, "damage": 34, "count": 1, "nbt": "" }, { "id": 383, "damage": 48, "count": 1, "nbt": "" }, { "id": 383, "damage": 46, "count": 1, "nbt": "" }, { "id": 383, "damage": 37, "count": 1, "nbt": "" }, { "id": 383, "damage": 35, "count": 1, "nbt": "" }, { "id": 383, "damage": 32, "count": 1, "nbt": "" }, { "id": 383, "damage": 36, "count": 1, "nbt": "" }, { "id": 383, "damage": 47, "count": 1, "nbt": "" }, { "id": 383, "damage": 17, "count": 1, "nbt": "" }, { "id": 383, "damage": 40, "count": 1, "nbt": "" }, { "id": 383, "damage": 45, "count": 1, "nbt": "" }, { "id": 383, "damage": 49, "count": 1, "nbt": "" }, { "id": 383, "damage": 50, "count": 1, "nbt": "" }, { "id": 383, "damage": 55, "count": 1, "nbt": "" }, { "id": 383, "damage": 42, "count": 1, "nbt": "" }, { "id": 383, "damage": 41, "count": 1, "nbt": "" }, { "id": 383, "damage": 43, "count": 1, "nbt": "" }, { "id": 383, "damage": 54, "count": 1, "nbt": "" }, { "id": 97, "damage": 0, "count": 1, "nbt": "" }, { "id": 97, "damage": 1, "count": 1, "nbt": "" }, { "id": 97, "damage": 2, "count": 1, "nbt": "" }, { "id": 97, "damage": 3, "count": 1, "nbt": "" }, { "id": 97, "damage": 4, "count": 1, "nbt": "" }, { "id": 97, "damage": 5, "count": 1, "nbt": "" }, { "id": 384, "damage": 0, "count": 1, "nbt": "" }, { "id": 385, "damage": 0, "count": 1, "nbt": "" }, { "id": 268, "damage": 0, "count": 1, "nbt": "" }, { "id": 290, "damage": 0, "count": 1, "nbt": "" }, { "id": 269, "damage": 0, "count": 1, "nbt": "" }, { "id": 270, "damage": 0, "count": 1, "nbt": "" }, { "id": 271, "damage": 0, "count": 1, "nbt": "" }, { "id": 272, "damage": 0, "count": 1, "nbt": "" }, { "id": 291, "damage": 0, "count": 1, "nbt": "" }, { "id": 273, "damage": 0, "count": 1, "nbt": "" }, { "id": 274, "damage": 0, "count": 1, "nbt": "" }, { "id": 275, "damage": 0, "count": 1, "nbt": "" }, { "id": 267, "damage": 0, "count": 1, "nbt": "" }, { "id": 292, "damage": 0, "count": 1, "nbt": "" }, { "id": 256, "damage": 0, "count": 1, "nbt": "" }, { "id": 257, "damage": 0, "count": 1, "nbt": "" }, { "id": 258, "damage": 0, "count": 1, "nbt": "" }, { "id": 276, "damage": 0, "count": 1, "nbt": "" }, { "id": 293, "damage": 0, "count": 1, "nbt": "" }, { "id": 277, "damage": 0, "count": 1, "nbt": "" }, { "id": 278, "damage": 0, "count": 1, "nbt": "" }, { "id": 279, "damage": 0, "count": 1, "nbt": "" }, { "id": 283, "damage": 0, "count": 1, "nbt": "" }, { "id": 294, "damage": 0, "count": 1, "nbt": "" }, { "id": 284, "damage": 0, "count": 1, "nbt": "" }, { "id": 285, "damage": 0, "count": 1, "nbt": "" }, { "id": 286, "damage": 0, "count": 1, "nbt": "" }, { "id": 298, "damage": 0, "count": 1, "nbt": "" }, { "id": 299, "damage": 0, "count": 1, "nbt": "" }, { "id": 300, "damage": 0, "count": 1, "nbt": "" }, { "id": 301, "damage": 0, "count": 1, "nbt": "" }, { "id": 302, "damage": 0, "count": 1, "nbt": "" }, { "id": 303, "damage": 0, "count": 1, "nbt": "" }, { "id": 304, "damage": 0, "count": 1, "nbt": "" }, { "id": 305, "damage": 0, "count": 1, "nbt": "" }, { "id": 306, "damage": 0, "count": 1, "nbt": "" }, { "id": 307, "damage": 0, "count": 1, "nbt": "" }, { "id": 308, "damage": 0, "count": 1, "nbt": "" }, { "id": 309, "damage": 0, "count": 1, "nbt": "" }, { "id": 310, "damage": 0, "count": 1, "nbt": "" }, { "id": 311, "damage": 0, "count": 1, "nbt": "" }, { "id": 312, "damage": 0, "count": 1, "nbt": "" }, { "id": 313, "damage": 0, "count": 1, "nbt": "" }, { "id": 314, "damage": 0, "count": 1, "nbt": "" }, { "id": 315, "damage": 0, "count": 1, "nbt": "" }, { "id": 316, "damage": 0, "count": 1, "nbt": "" }, { "id": 317, "damage": 0, "count": 1, "nbt": "" }, { "id": 444, "damage": 0, "count": 1, "nbt": "" }, { "id": 445, "damage": 0, "count": 1, "nbt": "" }, { "id": 69, "damage": 0, "count": 1, "nbt": "" }, { "id": 123, "damage": 0, "count": 1, "nbt": "" }, { "id": 76, "damage": 0, "count": 1, "nbt": "" }, { "id": 72, "damage": 0, "count": 1, "nbt": "" }, { "id": 70, "damage": 0, "count": 1, "nbt": "" }, { "id": 147, "damage": 0, "count": 1, "nbt": "" }, { "id": 148, "damage": 0, "count": 1, "nbt": "" }, { "id": 143, "damage": 5, "count": 1, "nbt": "" }, { "id": 77, "damage": 5, "count": 1, "nbt": "" }, { "id": 151, "damage": 0, "count": 1, "nbt": "" }, { "id": 131, "damage": 0, "count": 1, "nbt": "" }, { "id": 356, "damage": 0, "count": 1, "nbt": "" }, { "id": 404, "damage": 0, "count": 1, "nbt": "" }, { "id": 23, "damage": 3, "count": 1, "nbt": "" }, { "id": 125, "damage": 3, "count": 1, "nbt": "" }, { "id": 33, "damage": 1, "count": 1, "nbt": "" }, { "id": 29, "damage": 1, "count": 1, "nbt": "" }, { "id": 251, "damage": 0, "count": 1, "nbt": "" }, { "id": 122, "damage": 0, "count": 1, "nbt": "" }, { "id": 410, "damage": 0, "count": 1, "nbt": "" }, { "id": 332, "damage": 0, "count": 1, "nbt": "" }, { "id": 368, "damage": 0, "count": 1, "nbt": "" }, { "id": 381, "damage": 0, "count": 1, "nbt": "" }, { "id": 426, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 422, "damage": 0, "count": 1, "nbt": "" }, { "id": 52, "damage": 0, "count": 1, "nbt": "" }, { "id": 116, "damage": 0, "count": 1, "nbt": "" }, { "id": 165, "damage": 0, "count": 1, "nbt": "" }, { "id": 130, "damage": 0, "count": 1, "nbt": "" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0000\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0000\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0000\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0000\u0000\u0002\u0003\u0000lvl\u0004\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0001\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0001\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0001\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0001\u0000\u0002\u0003\u0000lvl\u0004\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0002\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0002\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0002\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0002\u0000\u0002\u0003\u0000lvl\u0004\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0003\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0003\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0003\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0003\u0000\u0002\u0003\u0000lvl\u0004\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0004\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0004\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0004\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0004\u0000\u0002\u0003\u0000lvl\u0004\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0005\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0005\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0005\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0006\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0006\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0006\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0007\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0007\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0007\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\b\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\t\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\t\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\t\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\t\u0000\u0002\u0003\u0000lvl\u0004\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\t\u0000\u0002\u0003\u0000lvl\u0005\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\n\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\n\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\n\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\n\u0000\u0002\u0003\u0000lvl\u0004\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\n\u0000\u0002\u0003\u0000lvl\u0005\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u000b\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u000b\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u000b\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u000b\u0000\u0002\u0003\u0000lvl\u0004\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u000b\u0000\u0002\u0003\u0000lvl\u0005\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\f\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\f\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\r\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\r\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u000e\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u000e\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u000e\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u000f\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u000f\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u000f\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u000f\u0000\u0002\u0003\u0000lvl\u0004\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u000f\u0000\u0002\u0003\u0000lvl\u0005\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0010\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0011\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0011\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0011\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0012\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0012\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0012\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0013\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0013\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0013\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0013\u0000\u0002\u0003\u0000lvl\u0004\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0013\u0000\u0002\u0003\u0000lvl\u0005\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0014\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0014\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0015\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0016\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0017\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0017\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0017\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0018\u0000\u0002\u0003\u0000lvl\u0001\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0018\u0000\u0002\u0003\u0000lvl\u0002\u0000\u0000\u0000" }, { "id": 403, "damage": 0, "count": 1, "nbt": "\n\u0000\u0000\t\u0004\u0000ench\n\u0001\u0000\u0000\u0000\u0002\u0002\u0000id\u0018\u0000\u0002\u0003\u0000lvl\u0003\u0000\u0000\u0000" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" }, { "id": 35, "damage": 8, "count": 1, "nbt": "" }, { "id": 35, "damage": 7, "count": 1, "nbt": "" }, { "id": 35, "damage": 15, "count": 1, "nbt": "" }, { "id": 35, "damage": 12, "count": 1, "nbt": "" }, { "id": 35, "damage": 14, "count": 1, "nbt": "" }, { "id": 35, "damage": 1, "count": 1, "nbt": "" }, { "id": 35, "damage": 4, "count": 1, "nbt": "" }, { "id": 35, "damage": 5, "count": 1, "nbt": "" }, { "id": 35, "damage": 13, "count": 1, "nbt": "" }, { "id": 35, "damage": 9, "count": 1, "nbt": "" }, { "id": 35, "damage": 3, "count": 1, "nbt": "" }, { "id": 35, "damage": 11, "count": 1, "nbt": "" }, { "id": 35, "damage": 10, "count": 1, "nbt": "" }, { "id": 35, "damage": 2, "count": 1, "nbt": "" }, { "id": 35, "damage": 6, "count": 1, "nbt": "" }, { "id": 171, "damage": 0, "count": 1, "nbt": "" }, { "id": 171, "damage": 8, "count": 1, "nbt": "" }, { "id": 171, "damage": 7, "count": 1, "nbt": "" }, { "id": 171, "damage": 15, "count": 1, "nbt": "" }, { "id": 171, "damage": 12, "count": 1, "nbt": "" }, { "id": 171, "damage": 14, "count": 1, "nbt": "" }, { "id": 171, "damage": 1, "count": 1, "nbt": "" }, { "id": 171, "damage": 4, "count": 1, "nbt": "" }, { "id": 171, "damage": 5, "count": 1, "nbt": "" }, { "id": 171, "damage": 13, "count": 1, "nbt": "" }, { "id": 171, "damage": 9, "count": 1, "nbt": "" }, { "id": 171, "damage": 3, "count": 1, "nbt": "" }, { "id": 171, "damage": 11, "count": 1, "nbt": "" }, { "id": 171, "damage": 10, "count": 1, "nbt": "" }, { "id": 171, "damage": 2, "count": 1, "nbt": "" }, { "id": 171, "damage": 6, "count": 1, "nbt": "" }, { "id": 351, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 8, "count": 1, "nbt": "" }, { "id": 351, "damage": 7, "count": 1, "nbt": "" }, { "id": 351, "damage": 15, "count": 1, "nbt": "" }, { "id": 351, "damage": 12, "count": 1, "nbt": "" }, { "id": 351, "damage": 14, "count": 1, "nbt": "" }, { "id": 351, "damage": 1, "count": 1, "nbt": "" }, { "id": 351, "damage": 4, "count": 1, "nbt": "" }, { "id": 351, "damage": 5, "count": 1, "nbt": "" }, { "id": 351, "damage": 13, "count": 1, "nbt": "" }, { "id": 351, "damage": 9, "count": 1, "nbt": "" }, { "id": 351, "damage": 3, "count": 1, "nbt": "" }, { "id": 351, "damage": 11, "count": 1, "nbt": "" }, { "id": 351, "damage": 10, "count": 1, "nbt": "" }, { "id": 351, "damage": 2, "count": 1, "nbt": "" }, { "id": 351, "damage": 6, "count": 1, "nbt": "" }, { "id": 374, "damage": 0, "count": 1, "nbt": "" }, { "id": 373, "damage": 0, "count": 1, "nbt": "" }, { "id": 373, "damage": 1, "count": 1, "nbt": "" }, { "id": 373, "damage": 2, "count": 1, "nbt": "" }, { "id": 373, "damage": 3, "count": 1, "nbt": "" }, { "id": 373, "damage": 4, "count": 1, "nbt": "" }, { "id": 373, "damage": 5, "count": 1, "nbt": "" }, { "id": 373, "damage": 6, "count": 1, "nbt": "" }, { "id": 373, "damage": 7, "count": 1, "nbt": "" }, { "id": 373, "damage": 8, "count": 1, "nbt": "" }, { "id": 373, "damage": 9, "count": 1, "nbt": "" }, { "id": 373, "damage": 10, "count": 1, "nbt": "" }, { "id": 373, "damage": 11, "count": 1, "nbt": "" }, { "id": 373, "damage": 12, "count": 1, "nbt": "" }, { "id": 373, "damage": 13, "count": 1, "nbt": "" }, { "id": 373, "damage": 14, "count": 1, "nbt": "" }, { "id": 373, "damage": 15, "count": 1, "nbt": "" }, { "id": 373, "damage": 16, "count": 1, "nbt": "" }, { "id": 373, "damage": 17, "count": 1, "nbt": "" }, { "id": 373, "damage": 18, "count": 1, "nbt": "" }, { "id": 373, "damage": 19, "count": 1, "nbt": "" }, { "id": 373, "damage": 20, "count": 1, "nbt": "" }, { "id": 373, "damage": 21, "count": 1, "nbt": "" }, { "id": 373, "damage": 22, "count": 1, "nbt": "" }, { "id": 373, "damage": 23, "count": 1, "nbt": "" }, { "id": 373, "damage": 24, "count": 1, "nbt": "" }, { "id": 373, "damage": 25, "count": 1, "nbt": "" }, { "id": 373, "damage": 26, "count": 1, "nbt": "" }, { "id": 373, "damage": 27, "count": 1, "nbt": "" }, { "id": 373, "damage": 28, "count": 1, "nbt": "" }, { "id": 373, "damage": 29, "count": 1, "nbt": "" }, { "id": 373, "damage": 30, "count": 1, "nbt": "" }, { "id": 373, "damage": 31, "count": 1, "nbt": "" }, { "id": 373, "damage": 32, "count": 1, "nbt": "" }, { "id": 373, "damage": 33, "count": 1, "nbt": "" }, { "id": 373, "damage": 34, "count": 1, "nbt": "" }, { "id": 373, "damage": 35, "count": 1, "nbt": "" }, { "id": 373, "damage": 36, "count": 1, "nbt": "" }, { "id": 438, "damage": 0, "count": 1, "nbt": "" }, { "id": 438, "damage": 1, "count": 1, "nbt": "" }, { "id": 438, "damage": 2, "count": 1, "nbt": "" }, { "id": 438, "damage": 3, "count": 1, "nbt": "" }, { "id": 438, "damage": 4, "count": 1, "nbt": "" }, { "id": 438, "damage": 5, "count": 1, "nbt": "" }, { "id": 438, "damage": 6, "count": 1, "nbt": "" }, { "id": 438, "damage": 7, "count": 1, "nbt": "" }, { "id": 438, "damage": 8, "count": 1, "nbt": "" }, { "id": 438, "damage": 9, "count": 1, "nbt": "" }, { "id": 438, "damage": 10, "count": 1, "nbt": "" }, { "id": 438, "damage": 11, "count": 1, "nbt": "" }, { "id": 438, "damage": 12, "count": 1, "nbt": "" }, { "id": 438, "damage": 13, "count": 1, "nbt": "" }, { "id": 438, "damage": 14, "count": 1, "nbt": "" }, { "id": 438, "damage": 15, "count": 1, "nbt": "" }, { "id": 438, "damage": 16, "count": 1, "nbt": "" }, { "id": 438, "damage": 17, "count": 1, "nbt": "" }, { "id": 438, "damage": 18, "count": 1, "nbt": "" }, { "id": 438, "damage": 19, "count": 1, "nbt": "" }, { "id": 438, "damage": 20, "count": 1, "nbt": "" }, { "id": 438, "damage": 21, "count": 1, "nbt": "" }, { "id": 438, "damage": 22, "count": 1, "nbt": "" }, { "id": 438, "damage": 23, "count": 1, "nbt": "" }, { "id": 438, "damage": 24, "count": 1, "nbt": "" }, { "id": 438, "damage": 25, "count": 1, "nbt": "" }, { "id": 438, "damage": 26, "count": 1, "nbt": "" }, { "id": 438, "damage": 27, "count": 1, "nbt": "" }, { "id": 438, "damage": 28, "count": 1, "nbt": "" }, { "id": 438, "damage": 29, "count": 1, "nbt": "" }, { "id": 438, "damage": 30, "count": 1, "nbt": "" }, { "id": 438, "damage": 31, "count": 1, "nbt": "" }, { "id": 438, "damage": 32, "count": 1, "nbt": "" }, { "id": 438, "damage": 33, "count": 1, "nbt": "" }, { "id": 438, "damage": 34, "count": 1, "nbt": "" }, { "id": 438, "damage": 35, "count": 1, "nbt": "" }, { "id": 438, "damage": 36, "count": 1, "nbt": "" }, { "id": 441, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 1, "count": 1, "nbt": "" }, { "id": 441, "damage": 2, "count": 1, "nbt": "" }, { "id": 441, "damage": 3, "count": 1, "nbt": "" }, { "id": 441, "damage": 4, "count": 1, "nbt": "" }, { "id": 441, "damage": 5, "count": 1, "nbt": "" }, { "id": 441, "damage": 6, "count": 1, "nbt": "" }, { "id": 441, "damage": 7, "count": 1, "nbt": "" }, { "id": 441, "damage": 8, "count": 1, "nbt": "" }, { "id": 441, "damage": 9, "count": 1, "nbt": "" }, { "id": 441, "damage": 10, "count": 1, "nbt": "" }, { "id": 441, "damage": 11, "count": 1, "nbt": "" }, { "id": 441, "damage": 12, "count": 1, "nbt": "" }, { "id": 441, "damage": 13, "count": 1, "nbt": "" }, { "id": 441, "damage": 14, "count": 1, "nbt": "" }, { "id": 441, "damage": 15, "count": 1, "nbt": "" }, { "id": 441, "damage": 16, "count": 1, "nbt": "" }, { "id": 441, "damage": 17, "count": 1, "nbt": "" }, { "id": 441, "damage": 18, "count": 1, "nbt": "" }, { "id": 441, "damage": 19, "count": 1, "nbt": "" }, { "id": 441, "damage": 20, "count": 1, "nbt": "" }, { "id": 441, "damage": 21, "count": 1, "nbt": "" }, { "id": 441, "damage": 22, "count": 1, "nbt": "" }, { "id": 441, "damage": 23, "count": 1, "nbt": "" }, { "id": 441, "damage": 24, "count": 1, "nbt": "" }, { "id": 441, "damage": 25, "count": 1, "nbt": "" }, { "id": 441, "damage": 26, "count": 1, "nbt": "" }, { "id": 441, "damage": 27, "count": 1, "nbt": "" }, { "id": 441, "damage": 28, "count": 1, "nbt": "" }, { "id": 441, "damage": 29, "count": 1, "nbt": "" }, { "id": 441, "damage": 30, "count": 1, "nbt": "" }, { "id": 441, "damage": 31, "count": 1, "nbt": "" }, { "id": 441, "damage": 32, "count": 1, "nbt": "" }, { "id": 441, "damage": 33, "count": 1, "nbt": "" }, { "id": 441, "damage": 34, "count": 1, "nbt": "" }, { "id": 441, "damage": 35, "count": 1, "nbt": "" }, { "id": 441, "damage": 36, "count": 1, "nbt": "" } ] ================================================ FILE: src/pocketmine/resources/genisys_chs.yml ================================================ #Genisys高级配置文件 #配置文件版本 config: version: 22 level: #设置是否变换天气 weather: true #随机天气持续时长最小值,最大值 weather-random-duration-min: 6000 weather-random-duration-max: 12000 #随机闪电间隔,默认10秒,0 = 禁用 lightning-time: 200 #是否启用闪电击中着火 lightning-fire: false #是否启用火焰蔓延 fire-spread: false player: #是否打开饥饿 hunger: true #是否打开经验系统 experience: true #是否开启死亡不掉落 keep-inventory: false #是否开启切换模式自动清除背包 auto-clear-inventory: true #是否开启死亡经验不掉落 keep-experience: false #如果玩家进入游戏时崩溃, 请设置低于10的值. 禁用 = -1 chunk-radius: -1 nether: #是否允许地狱,打开此选项会自动生成地狱地图 allow-nether: true #地狱地图名 level-name: "nether" server: #是否允许生成铁傀儡 allow-iron-golem: false #是否允许生成雪傀儡 allow-snow-golem: false #是否禁用server.log disable-log: false #是否启用反飞行作弊 anti-fly: true #是否启用异步方式发送区块 async-chunk-request: true #玩家进出服务器消息提醒方式。0为Message,1为Tip,2为Popup player-msg-type: 0 login-msg: "§3@player 加入了游戏" logout-msg: "§3@player 退出了游戏" #是否进行移动检测(不再拉回了) check-movement: true #是否限制创造某些功能(禁止丢物品, 禁止操作箱子等等) limited-creative: true #是否开启方块破坏粒子 destroy-block-particle: true #是否允许喷溅型药水 allow-splash-potion: true #是否启用高级指令选择器 advanced-command-selector: false allow-instabreak: false enchantment: #是否允许使用铁砧 enable-anvil: true #是否允许使用附魔台 enable-enchanting-table: true #是否启用计算附魔等级(计算书架数量),可能造成服务器延迟 #如果不启用本项, 附魔等级将在0-15间随机选取 count-bookshelf: false redstone: ############################## #######是否开启红石系统####### ############################## #如果不改为true将无法使用红石# ############################## enable: false #是否允许频率脉冲 frequency-pulse: false #设置脉冲频率, 默认: 1s pulse-frequency: 1 dserver: #多服统统一人数 enable: false #Query自动更新 query-auto-update: false #Query周期更新 query-tick-update: true #Motd最大人数,0为默认 motd-max-players: 0 #Query最大人数,0为默认 query-max-players: 0 #Motd显示总人数 motd-all-players: false #Query显示总人数 query-all-players: false #Motd显示人数 motd-players: false #Query显示人数 query-players: false #更新频率,20 = 1秒 time: 40 #获取失败自动重试次数 retry-times: 3 #服务器列表,用;隔开,例如 1.example.com:19132;2.example.com:19133 server-list: "" inventory: #Set this to true if you have problems with anvils. Will process inventory transactions in a vanilla fashion with no anti-cheats or verification. allow-cheats: false ================================================ FILE: src/pocketmine/resources/genisys_eng.yml ================================================ #Genisys Advanced Configuration File #Version of this file config: version: 22 level: #Set if weather is enabled (rain may cause lag to older devices) weather: true #Weather random duration weather-random-duration-min: 6000 weather-random-duration-max: 12000 #Random lightning interval,default as 10s, 0 = disable lightning-time: 200 #Set if lightning strikes have fire afterwards lightning-fire: false #Set if fire should spread (trees, etc.) fire-spread: false player: #Set if hunger is enabled hunger: true #Choose if experience is enabled experience: true #Choose if to keep a player's inventory after they die keep-inventory: false #Clear the inventory of a player upon them changing their gamemode auto-clear-inventory : true #Choose if to keep a player's experience after they die keep-experience: false #If players crash when joining, change this to less than 10. disable=-1 chunk-radius: -1 nether: #Choose if the nether is allowed. The level of nether will generate automatically allow-nether: true #The name of nether's level level-name: "nether" server: #Choose if spawning iron golem is allowed allow-iron-golem: false #Choose if spawning snow golem is allowed allow-snow-golem: false #Choose if server.log is disabled disable-log: false #Choose if to enable anti-fly anti-fly: true #Choose if async chunks request is enabled async-chunk-request: true #Choose how to remind players when someone joins the game #0 = Message, 1 = Tip, 2 = Popup player-msg-type: 0 login-msg: "§3@player joined the game" logout-msg: "§3@player left the game" #Choose if movement check is enabled (no more pull backs when moving fast) check-movement: true #Set if limited creative is enabled (cannot drop items from hotbar, cannot open chests, and so on) limited-creative: true #Set if add DestroyBlockParticle destroy-block-particle: true #Set if splash potions are enabled allow-splash-potion: true #Set if Advanced Command Selector is enabled advanced-command-selector: false allow-instabreak: false enchantment: #Choose if anvils are enabled enable-anvil: true #Choose if enchantment tables are enabled enable-enchanting-table: true #Choose if to count bookshelves (may cause server lag) #If this option is false, the server will use a random count (0~15) count-bookshelf: false redstone: ################################################ ####Choose if the redstone system is enabled#### ################################################ #If it is false, the redstone system won't work# ################################################ enable: false #Choose if frequency pulses are enabled frequency-pulse: false #Set the frequency of pulse. Default = 1s pulse-frequency: 1 dserver: #The count of all multi-server unified enable: false #Update Query automatically query-auto-update: false #Update Query periodically query-tick-update: true #The max players' on the MOTD motd-max-players: 0 #The max players' on the Query. 0=Default query-max-players: 0 #Show the number of all players on MOTD motd-all-players: false #Show the number of all players on Query query-all-players: false #Show the number of online players on MOTD motd-players: false #Show the number of online players on Query query-players: false #Update Frequency. 20=1s time: 40 #Auto-retry # of times when server fails retry-times: 3 #the server list,Separate by ';',e.g. 1.example.com:19132;2.example.com:19133 server-list: "" inventory: #Set this to true if you have problems with anvils. This will process inventory transactions in a vanilla fashion with no anti-cheats or verification. allow-cheats: false ================================================ FILE: src/pocketmine/resources/genisys_kor.yml ================================================ #Genisys 고급 구성 파일 #Version 파일의 버전 config: version: 22 level: #날씨 시스템을 켜는 경우 설정하세요 weather: true #날씨 랜덤 기간 weather-random-duration-min: 6000 weather-random-duration-max: 12000 #랜덤 번개 치는 주기,기본은 10초, 0 = 비활성화 lightning-time: 200 #번개가 불과 함께 치게 하려는 경우 설정하세요 lightning-fire: false #불이 번지는 것을 활성화 하려는 경우 설정하세요 fire-spread: false player: #배고픔 시스템 스위치 hunger: true #경험치 시스템을 켜는 경우 고르세요 experience: true #플레이어가 사망하였을때 인벤토리를 유지하려는 경우 고르세요 keep-inventory: false #게임 모드를 크리에이티브로 변경하는 경우 자동으로 인벤토리를 비웁니다 auto-clear-inventory : true #플레이어가 사망하였을때 경험치를 유지하려는 경우 고르세요 keep-experience: false #플레이어들이 참여했을때 크래시가 발생하는 경우, 이 값을 10 이하로 설정하세요. 비활성화=-1 chunk-radius: -1 nether: #지옥이 허용된 경우 고르세요. 지옥 레벨이 자동으로 생성됩니다. allow-nether: true #지옥 레벨의 이름 level-name: "nether" server: #철 골램을 생성하는 것이 허용된 경우 고르세요 allow-iron-golem: false #눈 골렘을 생성하는 것이 허용된 경우 고르세요 allow-snow-golem: false #server.log를 비활성화 하는 경우 고르세요 disable-log: false #자동 비행 방지를 활성화 하는 경우 고르세요 anti-fly: true #비동기 청크 요청을 활성화 하는 경우 고르세요 async-chunk-request: true #플레이어가 참여할 때 뜨는 메시지를 고릅니다 #0 = 메시지, 1 = 팁, 2 = 팝업 player-msg-type: 0 login-msg: "§3@player님이 게임에 참여했습니다" logout-msg: "§3@player님이 게임을 떠났습니다" #움직임 확인을 활성화 하는 경우 고르세요 (더 이상 뒤로 당겨지지 않음) check-movement: true #제한된 크리에이티브를 활성화 하는 경우 설정하세요 (아이템 드롭 불가, 상자 열기 불가 등) limited-creative: true #블럭 파괴 파티클을 추가하는 경우 설정하세요 destroy-block-particle: true #투척용 포션을 활성화 하는 경우 고르세요 allow-splash-potion: true #고급 명령어 선택기를 활성화 하는 경우 설정하세요 advanced-command-selector: false allow-instabreak: false enchantment: #모루가 허용된 경우 고르세요 enable-anvil: true #마법 부여대가 허용된 경우 고르세요 enable-enchanting-table: true #책장의 수를 세려는 경우 활성화 하세요. 서버 랙을 유발할 수도 있습니다. #만약 이 옵션이 false인 경우, 서버는 랜덤 카운트를 사용할 것 입니다 (0~15) count-bookshelf: false redstone: ##################################### ######레드스톤 시스템이 허용된 경우 고르세요###### ##################################### #만약 true가 아닌 경우 레드스톤이 작동하지 않습니다# ##################################### enable: false #주파수 펄스를 허용하는 경우 고르세요 frequency-pulse: false #펄스 주파수 설정, 기본: 1초 pulse-frequency: 1 dserver: #모든 멀티 (다중) 서버의 수 통합 enable: false #자동으로 쿼리 업데이트 query-auto-update: false #쿼리 업데이트 주기 query-tick-update: true #Motd 최대 플레이어 숫자 motd-max-players: 0 #쿼리 최대 플레이어 숫자,0=기본 query-max-players: 0 #motd에서 모든 플레이어의 숫자 표시 motd-all-players: false #쿼리에서 모든 플레이어의 숫자 표시 query-all-players: false #motd에서 온라인 플레이어의 숫자 표시 motd-players: false #쿼리에서 온라인 플레이어의 숫자 표시 query-players: false #업데이트 주기, 20=1초 time: 40 #실패한 경우 다시 시도할 빈도 retry-times: 3 #서버 목록,';'를 사용하여 분리하세요,예시: 1.example.com:19132;2.example.com:19133 server-list: "" inventory: #모루와 관련된 문제가 있는 경우 true로 설정하세요. 인벤토리 처리를 vanila fashion (기본 방식)으로 치트 방지나 확인 없이 진행합니다. allow-cheats: false ================================================ FILE: src/pocketmine/resources/genisys_vie.yml ================================================ #Cài đặt Genisys cho server (Genisys VN Version) #Phiên bản file cài đặt config: version: 22 level: #Chỉnh nếu muốn bật/tắt thời tiết (true/false) weather: true #Thời gian ngẫu nhiên điều chỉnh thời tiết weather-random-duration-min: 6000 weather-random-duration-max: 12000 #Thời gian đánh sấm sét ngẫu nhiên,mặc định là 10s, 0 = huỷ sấm sét lightning-time: 200 #Bật/tắt nếu sấm sét đánh có lửa (true/false) lightning-fire: false #Bật/tắt nếu muốn lửa lan (true/false) fire-spread: false player: #Bật/tắt nếu muốn thêm chế độ đói (true/false) hunger: true #Bật/tắt nếu muốn thêm chế độ điểm kinh nghiệm (true/false) experience: true #Bật/tắt nếu muốn server chết không mất đồ (true/false) keep-inventory: false #Bật/tắt nếu tự động quét hết đồ trong kho đồ khi chuyển sang chế độ sáng tạo (true/false) auto-clear-inventory : true #Bật/tắt nếu muốn chết không mất kinh nghiệm (true/false) keep-experience: false #Nếu người chơi vào game bị crash, đặt nó là 10. Hủy chế độ=-1 chunk-radius: -1 nether: #Bật/tắt nếu muốn thêm địa ngục vào server, khi bật server hệ thống sẽ tự tạo thế giới địa ngục (true/false) allow-nether: true #Đặt tên cho thế giới địa ngục của bạn. level-name: "nether" server: #Bật/tắt nếu muốn thêm Iron Golem vào server (true/false) allow-iron-golem: false #Bật/tắt nếu muốn thêm Snow Golem vào server (true/false) allow-snow-golem: false #Bật/tắt nếu muốn hủy server.log (true/false) disable-log: false #Bật/tắt nếu muốn hủy chế độ bay (true/false) anti-fly: true #Bật/tắt nếu muốn yêu cầu khối Async (mặc định: true) (true/false) async-chunk-request: true #Chọn để thông báo người chơi đã vào hoặc rời (nếu bạn không biết có thể bỏ qua) #0 = Tin nhắn, 1 = Mẹo, 2 = Thanh nổi ở thanh chọn đồ player-msg-type: 0 login-msg: "§3@player joined the game" logout-msg: "§3@player left the game" #Bật/tắt nếu muốn kiểm tra tốc độ (không còn đẩy lùi mỗi khi đi) (true/false) check-movement: true #Bật/tắt nếu kích hoạt chế độ sáng tạo giới hạn (Không thể rớt đồ, mở rương và hơn thế nữa) (mặc định: false) (true/false) limited-creative: true #Bật/tắt nếu muốn kích hoạt DestroyBlockParticle (phá block tự nhiên) (true/false) destroy-block-particle: true #Bật/tắt nếu muốn thêm chai ném (true/false) allow-splash-potion: true #Bật/tắt nếu muốn thêm Lệnh Nâng Cao (mặc định: false) (true/false) advanced-command-selector: false allow-instabreak: false enchantment: #Bật/tắt nếu muốn kích hoạt Cái Đe enable-anvil: true #Bật/tắt nếu muốn kích hoạt Bàn Phù Phép enable-enchanting-table: true #Bật/tắt nếu muốn đếm sách trong tủ sách, có thể gây server lag #Nếu để false, server sẽ tự động đếm từ (0~15) count-bookshelf: false redstone: ########################################## ###Bật/tắt nếu muốn thêm chế độ đá đỏ### ########################################## #Nếu không bật thì sẽ không hoạt động# ########################################## enable: false #Bật/tắt nếu muốn chia tỷ lệ hoạt động frequency-pulse: false #Đánh số tỷ lệ hoạt động, mặc định: 1 giây pulse-frequency: 1 dserver: #Tất cả các server gộp vào. Bật/tắt để hoạt động (true/false) enable: false #Query update automatically query-auto-update: false #Query update periodical query-tick-update: true #The max players' number of Motd motd-max-players: 0 #The max players' number of query,0=Default query-max-players: 0 #Show the number of all players on motd motd-all-players: false #Show the number of all players on Query query-all-players: false #Show the number of online players on motd motd-players: false #Show the number of online players on Query query-players: false #Update Frequency ,20=1s time: 40 #Auto-retry times when failed retry-times: 3 #the server list,Separate by ';',e.g. 1.example.com:19132;2.example.com:19133 server-list: "" inventory: #Set this to true if you have problems with anvils. Will process inventory transactions in a vanilla fashion with no anti-cheats or verification. allow-cheats: false ================================================ FILE: src/pocketmine/resources/genisys_zho.yml ================================================ #Genisys進階設定檔案 #設定檔案版本 config: version: 22 level: #設定是否變換天氣 weather: true #隨機天氣持續時長最小值,最大值 weather-random-duration-min: 6000 weather-random-duration-max: 12000 #隨機閃電間隔,預設10秒,0=不啟用 lightning-time: 200 #設定是否閃電擊中起火 lightning-fire: false #設定是否火焰蔓延 fire-spread: false player: #是否打開飢餓 hunger: true #是否打開經驗系統 experience: true #是否開啟死亡不掉落 keep-inventory: false #是否開啟切換模式自動清除背包 auto-clear-inventory: true #是否開啟死亡經驗不掉落 keep-experience: false #如果玩家進入遊戲時崩潰, 請設置該值低於10. 禁用 = -1 chunk-radius: -1 nether: #是否允許地獄,打開此選項會自動生成地獄地圖 allow-nether: true #地獄地圖名 level-name: "nether" server: #是否允許生成鐵傀儡 allow-iron-golem: false #是否允許生成雪傀儡 allow-snow-golem: false #是否停用server.log disable-log: false #是否啟用反飛行作弊 anti-fly: true #是否啟用異步發送區塊 async-chunk-request: true #玩家進出伺服器訊息提醒方式。0為Message,1為Tip,2為Popup player-msg-type: 0 login-msg: "§3@player 加入了遊戲" logout-msg: "§3@player 離開了遊戲" #是否進行移動檢測(不再拉回了) check-movement: true #是否限制創造某些功能(禁止丟物品, 禁止操作箱子等等) limited-creative: true #是否開啟方塊破壞粒子效果 destroy-block-particle: true #是否啟用噴濺型藥水 allow-splash-potion: true #是否啟用進階指令選擇器 advanced-command-selector: false allow-instabreak: false enchantment: #是否允許使用鐵砧 enable-anvil: true #是否允許使用附魔台 enable-enchanting-table: true #是否啟用計算附魔等級(計算書架數量),可能造成伺服器延遲 #如果不啟用本項, 附魔等級將在0-15間隨機選取 count-bookshelf: false redstone: ############################## #######是否開啟紅石系統####### ############################## #如果不改為true將無法使用紅石# ############################## enable: false #是否允許頻率脈衝 frequency-pulse: false #設定脈衝頻率, 預設: 1s pulse-frequency: 1 dserver: #多服統一人數 enable: false #Query自動更新 query-auto-update: false #Query週期更新 query-tick-update: true #Motd最大人數,0為預設 motd-max-players: 0 #Query最大人數,0為預設 query-max-players: 0 #Motd顯示總人數 motd-all-players: false #Query顯示總人數 query-all-players: false #Motd顯示人數 motd-players: false #Query顯示人數 query-players: false #更新頻率,20 = 1秒 time: 40 #獲取失敗自動重試次數 retry-times: 3 #伺服器列表,用;隔開,例如 1.example.com:19132;2.example.com:19133 server-list: "" inventory: #Set this to true if you have problems with anvils. Will process inventory transactions in a vanilla fashion with no anti-cheats or verification. allow-cheats: false ================================================ FILE: src/pocketmine/resources/pocketmine.yml ================================================ # Main configuration file for PocketMine-MP # These settings are the ones that cannot be included in server.properties # Some of these settings are safe, others can break your server if modified incorrectly # New settings/defaults won't appear automatically on this file when upgrading. settings: #Three-letter language code for server-side localization #Check your language code on https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes language: "eng" #Whether to send all strings translated to server locale or let the device handle them force-language: false #When server shut down, the players will get kicked and this is what will show on there screen. shutdown-message: "Server closed" #Allow listing plugins via Query query-plugins: true #Show a console message when a plugin uses deprecated API methods deprecated-verbose: true #Enable plugin and core profiling by default enable-profiling: false #Will only add results when tick measurement is below or equal to given value (default 20) profile-report-trigger: 20 #Number of AsyncTask workers. #Used for plugin asynchronous tasks, world generation, compression and web communication. #Set this approximately to your number of cores. #If set to auto, it'll try to detect the number of cores (or use 2) async-workers: 4 #Enables use of non-production, development builds. WARNING: DO NOT enable this unless you are sure you know what you are doing. enable-testing: false memory: #Global soft memory limit in megabytes. Set to 0 to disable #This will trigger low-memory-triggers and fire an event to free memory when the usage goes over this global-limit: 0 #Main thread soft memory limit in megabytes. Set to 0 to disable #This will trigger low-memory-triggers and fire an event to free memory when the usage goes over this main-limit: 0 #Main thread hard memory limit in megabytes. Set to 0 to disable #This will stop the server when the limit is surpassed main-hard-limit: 1024 #Period in ticks to check memory (default 1 second) check-rate: 20 #Continue firing low-memory-triggers and event while on low memory continuous-trigger: true #Only if memory.continuous-trigger is enabled. Specifies the rate in memory.check-rate steps (default 30 seconds) continuous-trigger-rate: 30 garbage-collection: #Period in ticks to fire the garbage collector manually (default 30 minutes), set to 0 to disable #This only affect the main thread. Other threads should fire their own collections period: 36000 #Fire asynchronous tasks to collect garbage from workers collect-async-worker: true #Trigger on low memory low-memory-trigger: true max-chunks: #Limit of chunks to load per player, overrides chunk-sending.max-chunks trigger-limit: 28 #Do chunk garbage collection on trigger trigger-chunk-collect: true #Trigger on low memory low-memory-trigger: true world-caches: disable-chunk-cache: true low-memory-trigger: true network: #Threshold for batching packets, in bytes. Only these packets will be compressed #Set to 0 to compress everything, -1 to disable. batch-threshold: 256 #Compression level used when sending batched packets. Higher = Uses More CPU, Less = More Bandwidth Usage compression-level: 2 #Use AsyncTasks for compression. Adds half/one tick delay, less CPU load on main thread async-compression: true #Experimental, only for Windows. Tries to use UPnP to automatically port forward upnp-forwarding: false debug: #If > 1, it will show debug messages in the console level: 1 #Enables /status, /gc commands: true player: #Choose whether to enable player data saving. save-player-data: true level-settings: #The default format that levels will use when created convert-format: false default-format: pmanvil #Automatically change levels tick rate to maintain 20 ticks per second auto-tick-rate: false auto-tick-rate-limit: 20 #Sets the base tick rate (1 = 20 ticks per second, 2 = 10 ticks per second, etc.) base-tick-rate: 1 #Tick all players each tick even when other settings disallow this. always-tick-players: false chunk-sending: #Amount of chunks sent to players per tick per-tick: 1 #Amount of chunks sent around each player max-chunks: 32 #Amount of chunks that need to be sent before spawning the player spawn-threshold: 28 #Save a serialized copy of the chunk in memory for faster sending #Useful in mostly-static worlds where lots of players join at the same time cache-chunks: true chunk-ticking: #Max amount of chunks processed each tick per-tick: 16 #Radius of chunks around a player to tick tick-radius: 3 light-updates: false clear-tick-list: true chunk-generation: #Max. amount of chunks in the waiting queue to be generated queue-size: 2 #Max. amount of chunks in the waiting queue to be populated population-queue-size: 2 ticks-per: animal-spawns: 400 monster-spawns: 1 autosave: 6000 cache-cleanup: 900 spawn-limits: monsters: 70 animals: 15 water-animals: 5 ambient: 15 auto-report: #Send crash reports for processing enabled: false send-code: true send-settings: true send-phpinfo: false host: crash.pocketmine.net anonymous-statistics: #Sends anonymous statistics for data aggregation, plugin usage tracking enabled: true host: stats.pocketmine.net commands: #Here you can customize server commands #Specify command names to override the default set here. #If no custom value is defined for a command, the default will be used. #NOTE: Some commands cannot be disabled here, such as the important ones like /stop, /reload, etc. default: true #Set override values per command here #For example, uncommenting the below will disable /plugins and /version #version: false #plugins: false aliases: #Examples: #showtheversion: version #savestop: [save-all, stop] worlds: #These settings will override the generator set in server.properties and allows loading multiple levels #Example: #world: # seed: 404 # generator: FLAT:2;7,59x1,3x3,2;1;decoration(treecount=80 grasscount=45) ================================================ FILE: src/pocketmine/resources/recipes.json ================================================ [ { "type": 1, "width": 3, "height": 3, "input": [ { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 270, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "2a364c43-857c-4fe2-8826-9aebe0d640ba" }, { "type": 1, "width": 1, "height": 3, "input": [ { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 269, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "5f3e5e58-fff0-1e20-8b19-1f8fa069c29a" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 271, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "033e3209-67be-3064-8ee0-4c1df9e3efb0" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 290, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "f039cee0-8ada-16b43-83c2-dba415934cb3" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 274, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "1630d261-e294-3398-88c3-33223a3f3192" }, { "type": 1, "width": 1, "height": 3, "input": [ { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 273, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "2136462f-b302-0288-8658-b9822c17c6b9" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 275, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "82389a40-f1e0-18c31-8c11-5cc451283a9e" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 291, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "e638df84-d1fa-01d1-80ff-1ccfe9e881b5" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 257, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "fc3a9a88-1430-874a-802b-be8f0a2ecba0" }, { "type": 1, "width": 1, "height": 3, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 256, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "21365028-8729-7afa-847a-98b2080703a9" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 258, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "49323a6f-e1f7-831f-898e-fb306e405395" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 292, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "f93f8b91-2310-19b39-89aa-9279360cf6ab" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 278, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "45345231-2070-17dc6-8a48-cc6cb27e2691" }, { "type": 1, "width": 1, "height": 3, "input": [ { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 277, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "2739bac0-3867-33a3-8d9e-1e62754ae4ae" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 279, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "7b333b8c-80f3-192ed-8546-55b2736127b9" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 293, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "323b5a12-bd8b-1a1b-850e-473b0aafc0b9" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 285, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "7839adb8-b73c-8a3b-89fc-44ea4377ac87" }, { "type": 1, "width": 1, "height": 3, "input": [ { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 284, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "cc3b13b7-8176-1867b-8086-3fc9539cd9af" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 286, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "b53b05c1-485c-7198-86fa-85432f4e23b5" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 294, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "823849df-0e00-20fb2-8a2a-2f1637090fb0" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 359, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "073cfc0b-2c8f-122f-829e-1ccfc2911790" }, { "type": 1, "width": 1, "height": 3, "input": [ { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 268, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "c63a607b-8953-4cf2-84d6-89907977aaa9" }, { "type": 1, "width": 1, "height": 3, "input": [ { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 272, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "2436a89b-13a4-867d-8af3-445883ad0e87" }, { "type": 1, "width": 1, "height": 3, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 267, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "4637ec4d-0660-1656b-8347-0a97df65b5ab" }, { "type": 1, "width": 1, "height": 3, "input": [ { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 276, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "413a3a8c-4d3d-196c-897c-8330933081b0" }, { "type": 1, "width": 1, "height": 3, "input": [ { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 283, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "0b3cfd7d-8458-8b42-8cb2-ff773171d496" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 287, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 287, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 287, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 261, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "883ff9ac-dd0a-6b07-8612-927b6ec06dbb" }, { "type": 1, "width": 1, "height": 3, "input": [ { "id": 318, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 288, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "6c3a21ce-d85e-6f9d-8004-3b78e848129b" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 0, "count": 8, "nbt": "" } ], "uuid": "073120b7-bdb4-9e06-83a4-569ec2a4b2be" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 1, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 1, "count": 8, "nbt": "" } ], "uuid": "2b3b2e14-3ed4-216e2-80ee-0363f331618e" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 2, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 2, "count": 8, "nbt": "" } ], "uuid": "5a319d66-a842-16b7f-84b0-3710270db3a0" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 3, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 3, "count": 8, "nbt": "" } ], "uuid": "033cae74-338a-9f98-8b5b-0ee01b17e69a" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 4, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 4, "count": 8, "nbt": "" } ], "uuid": "c93d9615-e637-1639b-824c-f34b656789b3" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 5, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 5, "count": 8, "nbt": "" } ], "uuid": "bc3fcd5f-b589-37a1-832f-9fe8f482ba8f" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 6, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 6, "count": 8, "nbt": "" } ], "uuid": "6a34dd19-cb6d-21416-8028-f95c7be96ba2" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 7, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 7, "count": 8, "nbt": "" } ], "uuid": "f5383a27-c47f-0659-8c8e-346f8a3e678d" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 8, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 8, "count": 8, "nbt": "" } ], "uuid": "1436a1ae-bba9-8425-8d15-83b22e13e993" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 9, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 9, "count": 8, "nbt": "" } ], "uuid": "8a3c7d13-895a-18f47-8a91-4b907a3d6388" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 10, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 10, "count": 8, "nbt": "" } ], "uuid": "d23f51c8-0e67-20d8c-86b9-53a7cf20a2aa" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 11, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 11, "count": 8, "nbt": "" } ], "uuid": "6d38150d-b6ad-3297-8ec3-c214aada8b88" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 12, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 12, "count": 8, "nbt": "" } ], "uuid": "343c2082-e07b-1564-8128-3a478e53818c" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 13, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 13, "count": 8, "nbt": "" } ], "uuid": "9934706d-f11e-7efa-8fdf-039bb0aee69c" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 14, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 14, "count": 8, "nbt": "" } ], "uuid": "7e390cc8-3f5f-231d-8364-b3df92b56099" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 15, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 15, "count": 8, "nbt": "" } ], "uuid": "4e35a75f-4824-27c3-8fcc-5659355b4d8c" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 16, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 16, "count": 8, "nbt": "" } ], "uuid": "94304308-1c10-3a05-8690-63494bfd3f87" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 17, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 17, "count": 8, "nbt": "" } ], "uuid": "68313c2a-5157-5bd1-80e9-af6a82820190" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 18, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 18, "count": 8, "nbt": "" } ], "uuid": "d13ca58e-16d8-5d50-8e2b-65df250cf985" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 19, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 19, "count": 8, "nbt": "" } ], "uuid": "7c3ea290-3fbc-20842-84ba-ec352bf78c98" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 20, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 20, "count": 8, "nbt": "" } ], "uuid": "e935e60e-dce0-23f3-8b97-c0422a57b5a0" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 21, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 21, "count": 8, "nbt": "" } ], "uuid": "e938b4b9-f1e9-2371-86da-cf7425c50ab3" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 22, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 22, "count": 8, "nbt": "" } ], "uuid": "c73d8686-e05f-1756b-8aa7-23a37b88fca9" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 23, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 23, "count": 8, "nbt": "" } ], "uuid": "e930b08f-6178-1f08-8f4d-db23b0a38680" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 24, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 24, "count": 8, "nbt": "" } ], "uuid": "a732ba57-78ea-16ea3-826e-84369dd572ae" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 25, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 25, "count": 8, "nbt": "" } ], "uuid": "ab32db74-1dda-21808-8c9e-62c7c83f5196" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 26, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 26, "count": 8, "nbt": "" } ], "uuid": "503963b9-85ef-16ce0-8950-7148e51704a4" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 27, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 27, "count": 8, "nbt": "" } ], "uuid": "a1380d85-91dd-7a88-8ff6-8dec953ed393" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 28, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 28, "count": 8, "nbt": "" } ], "uuid": "6f3ab76e-116c-3aff-8987-df601dd850ae" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 29, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 29, "count": 8, "nbt": "" } ], "uuid": "eb34727b-312e-7397-8022-f9c78527c288" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 30, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 30, "count": 8, "nbt": "" } ], "uuid": "ed308b60-9771-208d2-8417-daddde3f87b5" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 31, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 31, "count": 8, "nbt": "" } ], "uuid": "c5372590-b199-8cab-850e-7ce4c36c1fbe" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 32, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 32, "count": 8, "nbt": "" } ], "uuid": "9e3003a6-a7d7-2d1c-837e-9a590338e2b1" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 33, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 33, "count": 8, "nbt": "" } ], "uuid": "6e3a310a-5bb7-21b3e-8be2-88919faf46bd" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 34, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 34, "count": 8, "nbt": "" } ], "uuid": "063449a7-a70e-8b67-8f79-2d29acaabdb3" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 35, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 35, "count": 8, "nbt": "" } ], "uuid": "b73909c8-407e-4af1-8631-045baf7896a3" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 441, "damage": 36, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" }, { "id": 262, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 262, "damage": 36, "count": 8, "nbt": "" } ], "uuid": "3f37b026-1cc5-41bd-80d8-e3de2a2a9e87" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 266, "damage": 0, "count": 9, "nbt": "" }, { "id": 266, "damage": 0, "count": 9, "nbt": "" }, { "id": 266, "damage": 0, "count": 9, "nbt": "" }, { "id": 266, "damage": 0, "count": 9, "nbt": "" }, { "id": 266, "damage": 0, "count": 9, "nbt": "" }, { "id": 266, "damage": 0, "count": 9, "nbt": "" }, { "id": 266, "damage": 0, "count": 9, "nbt": "" }, { "id": 266, "damage": 0, "count": 9, "nbt": "" }, { "id": 266, "damage": 0, "count": 9, "nbt": "" } ], "output": [ { "id": 41, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "bf3b509d-29ad-212c4-8394-c2a1449d94b4" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 41, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 266, "damage": 0, "count": 9, "nbt": "" } ], "uuid": "963d5efb-608a-3fc6-812d-2e29907569a9" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 265, "damage": 0, "count": 9, "nbt": "" }, { "id": 265, "damage": 0, "count": 9, "nbt": "" }, { "id": 265, "damage": 0, "count": 9, "nbt": "" }, { "id": 265, "damage": 0, "count": 9, "nbt": "" }, { "id": 265, "damage": 0, "count": 9, "nbt": "" }, { "id": 265, "damage": 0, "count": 9, "nbt": "" }, { "id": 265, "damage": 0, "count": 9, "nbt": "" }, { "id": 265, "damage": 0, "count": 9, "nbt": "" }, { "id": 265, "damage": 0, "count": 9, "nbt": "" } ], "output": [ { "id": 42, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "91319c17-0761-219dd-8365-c3ed85b468b7" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 42, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 265, "damage": 0, "count": 9, "nbt": "" } ], "uuid": "d5333397-9873-48b2-89ef-f289d7370887" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 264, "damage": 0, "count": 9, "nbt": "" }, { "id": 264, "damage": 0, "count": 9, "nbt": "" }, { "id": 264, "damage": 0, "count": 9, "nbt": "" }, { "id": 264, "damage": 0, "count": 9, "nbt": "" }, { "id": 264, "damage": 0, "count": 9, "nbt": "" }, { "id": 264, "damage": 0, "count": 9, "nbt": "" }, { "id": 264, "damage": 0, "count": 9, "nbt": "" }, { "id": 264, "damage": 0, "count": 9, "nbt": "" }, { "id": 264, "damage": 0, "count": 9, "nbt": "" } ], "output": [ { "id": 57, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "9a3c799a-902b-737b-8f9d-0214f80ca680" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 57, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 264, "damage": 0, "count": 9, "nbt": "" } ], "uuid": "8b3af454-68d6-17180-826f-91e60a459aa5" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 388, "damage": 0, "count": 9, "nbt": "" }, { "id": 388, "damage": 0, "count": 9, "nbt": "" }, { "id": 388, "damage": 0, "count": 9, "nbt": "" }, { "id": 388, "damage": 0, "count": 9, "nbt": "" }, { "id": 388, "damage": 0, "count": 9, "nbt": "" }, { "id": 388, "damage": 0, "count": 9, "nbt": "" }, { "id": 388, "damage": 0, "count": 9, "nbt": "" }, { "id": 388, "damage": 0, "count": 9, "nbt": "" }, { "id": 388, "damage": 0, "count": 9, "nbt": "" } ], "output": [ { "id": 133, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "8e3ff10d-11ac-0474-89d9-aa7ac129e2a8" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 133, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 388, "damage": 0, "count": 9, "nbt": "" } ], "uuid": "54327f02-0e77-5bdc-8a84-f30cd653f5bd" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 331, "damage": 0, "count": 9, "nbt": "" }, { "id": 331, "damage": 0, "count": 9, "nbt": "" }, { "id": 331, "damage": 0, "count": 9, "nbt": "" }, { "id": 331, "damage": 0, "count": 9, "nbt": "" }, { "id": 331, "damage": 0, "count": 9, "nbt": "" }, { "id": 331, "damage": 0, "count": 9, "nbt": "" }, { "id": 331, "damage": 0, "count": 9, "nbt": "" }, { "id": 331, "damage": 0, "count": 9, "nbt": "" }, { "id": 331, "damage": 0, "count": 9, "nbt": "" } ], "output": [ { "id": 152, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "2a39b305-0ef0-4e8d-8e0e-5965cde94a81" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 152, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 331, "damage": 0, "count": 9, "nbt": "" } ], "uuid": "c831f836-7f6b-16e22-840e-b9f5b67a45b4" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 351, "damage": 4, "count": 9, "nbt": "" }, { "id": 351, "damage": 4, "count": 9, "nbt": "" }, { "id": 351, "damage": 4, "count": 9, "nbt": "" }, { "id": 351, "damage": 4, "count": 9, "nbt": "" }, { "id": 351, "damage": 4, "count": 9, "nbt": "" }, { "id": 351, "damage": 4, "count": 9, "nbt": "" }, { "id": 351, "damage": 4, "count": 9, "nbt": "" }, { "id": 351, "damage": 4, "count": 9, "nbt": "" }, { "id": 351, "damage": 4, "count": 9, "nbt": "" } ], "output": [ { "id": 22, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "98350af4-6a77-171ed-8e51-3ef161e051a7" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 22, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 4, "count": 9, "nbt": "" } ], "uuid": "3731afc2-4dc4-20d70-8a02-7ca45368a597" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 296, "damage": 0, "count": 9, "nbt": "" }, { "id": 296, "damage": 0, "count": 9, "nbt": "" }, { "id": 296, "damage": 0, "count": 9, "nbt": "" }, { "id": 296, "damage": 0, "count": 9, "nbt": "" }, { "id": 296, "damage": 0, "count": 9, "nbt": "" }, { "id": 296, "damage": 0, "count": 9, "nbt": "" }, { "id": 296, "damage": 0, "count": 9, "nbt": "" }, { "id": 296, "damage": 0, "count": 9, "nbt": "" }, { "id": 296, "damage": 0, "count": 9, "nbt": "" } ], "output": [ { "id": 170, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "093794b2-bb4f-8bc4-8752-6cf199757bb4" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 170, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 296, "damage": 0, "count": 9, "nbt": "" } ], "uuid": "e4377102-bb59-906a-8212-5ef0ef3d0091" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 263, "damage": 0, "count": 9, "nbt": "" }, { "id": 263, "damage": 0, "count": 9, "nbt": "" }, { "id": 263, "damage": 0, "count": 9, "nbt": "" }, { "id": 263, "damage": 0, "count": 9, "nbt": "" }, { "id": 263, "damage": 0, "count": 9, "nbt": "" }, { "id": 263, "damage": 0, "count": 9, "nbt": "" }, { "id": 263, "damage": 0, "count": 9, "nbt": "" }, { "id": 263, "damage": 0, "count": 9, "nbt": "" }, { "id": 263, "damage": 0, "count": 9, "nbt": "" } ], "output": [ { "id": 173, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "8f314a48-8623-209ac-851b-b93635cb87bc" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 173, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 263, "damage": 0, "count": 9, "nbt": "" } ], "uuid": "47319339-f584-2083d-8f9e-7effb212dba7" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 266, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "a93adb54-e898-20594-896c-9789911093bf" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 266, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 371, "damage": 0, "count": 9, "nbt": "" } ], "uuid": "c531db02-2da9-185a9-871d-47ea2757f1bc" }, { "type": 0, "input": [ { "id": 39, "damage": -1, "count": 1, "nbt": "" }, { "id": 40, "damage": 0, "count": 1, "nbt": "" }, { "id": 281, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 282, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "87378c37-a972-20f75-81fe-54073de31ea3" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 457, "damage": 0, "count": 1, "nbt": "" }, { "id": 457, "damage": 0, "count": 1, "nbt": "" }, { "id": 457, "damage": 0, "count": 1, "nbt": "" }, { "id": 457, "damage": 0, "count": 1, "nbt": "" }, { "id": 457, "damage": 0, "count": 1, "nbt": "" }, { "id": 457, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 281, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 459, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "5431cba0-ddad-1991d-8e63-9419018880bb" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 296, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 3, "count": 1, "nbt": "" }, { "id": 296, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 357, "damage": 0, "count": 8, "nbt": "" } ], "uuid": "f33ca2be-983f-211fc-81d2-bdc7b5d4e7b3" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 360, "damage": 0, "count": 1, "nbt": "" }, { "id": 360, "damage": 0, "count": 1, "nbt": "" }, { "id": 360, "damage": 0, "count": 1, "nbt": "" }, { "id": 360, "damage": 0, "count": 1, "nbt": "" }, { "id": 360, "damage": 0, "count": 1, "nbt": "" }, { "id": 360, "damage": 0, "count": 1, "nbt": "" }, { "id": 360, "damage": 0, "count": 1, "nbt": "" }, { "id": 360, "damage": 0, "count": 1, "nbt": "" }, { "id": 360, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 103, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "f73aad80-b98c-23d5-87c1-c48eb3f58aaf" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 360, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 362, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "1a3aac4a-cf8a-18c14-86e4-771936bea08d" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 86, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 361, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "4d374e51-80b9-8d3a-8953-00b3dabbb6ad" }, { "type": 0, "input": [ { "id": 86, "damage": 0, "count": 1, "nbt": "" }, { "id": 353, "damage": 0, "count": 1, "nbt": "" }, { "id": 344, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 400, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "a032e78c-6d03-7ad4-8a8e-9a4374c8d48d" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 375, "damage": 0, "count": 1, "nbt": "" }, { "id": 353, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 39, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 376, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "c3321dca-9cba-6479-8a46-ae2444448796" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 54, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "9b3bfd0d-dfbb-11d1-8bc1-6c4149c8c1ba" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 131, "damage": -1, "count": 1, "nbt": "" }, { "id": 54, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 146, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "1630e376-9a3d-6459-8d8a-754e9c62b3ba" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 61, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "39390d3f-51d9-579a-80df-79845c337595" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 58, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "6035394d-23bd-3b3c-8684-04f702c38996" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 12, "damage": 0, "count": 1, "nbt": "" }, { "id": 12, "damage": 0, "count": 1, "nbt": "" }, { "id": 12, "damage": 0, "count": 1, "nbt": "" }, { "id": 12, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 24, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "473227b7-5231-199c1-8b7c-4abbe9d466b2" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 12, "damage": 1, "count": 1, "nbt": "" }, { "id": 12, "damage": 1, "count": 1, "nbt": "" }, { "id": 12, "damage": 1, "count": 1, "nbt": "" }, { "id": 12, "damage": 1, "count": 1, "nbt": "" } ], "output": [ { "id": 179, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "31371b9e-c049-5f79-8849-c06a9d7e0698" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 24, "damage": -1, "count": 1, "nbt": "" }, { "id": 24, "damage": -1, "count": 1, "nbt": "" }, { "id": 24, "damage": -1, "count": 1, "nbt": "" }, { "id": 24, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 24, "damage": 2, "count": 4, "nbt": "" } ], "uuid": "df3a19da-d1d8-57e9-84b7-55f44b736796" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 179, "damage": -1, "count": 1, "nbt": "" }, { "id": 179, "damage": -1, "count": 1, "nbt": "" }, { "id": 179, "damage": -1, "count": 1, "nbt": "" }, { "id": 179, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 179, "damage": 2, "count": 4, "nbt": "" } ], "uuid": "1a3b5473-0988-8b3a-8bed-e77857fd0eb5" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 44, "damage": 1, "count": 1, "nbt": "" }, { "id": 44, "damage": 1, "count": 1, "nbt": "" } ], "output": [ { "id": 24, "damage": 1, "count": 1, "nbt": "" } ], "uuid": "6d32491f-31ee-218c9-8adc-e89ec9517685" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 182, "damage": 0, "count": 1, "nbt": "" }, { "id": 182, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 179, "damage": 1, "count": 1, "nbt": "" } ], "uuid": "1f33558c-7832-5434-8940-a1bc9acf14a6" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 1, "damage": 0, "count": 1, "nbt": "" }, { "id": 1, "damage": 0, "count": 1, "nbt": "" }, { "id": 1, "damage": 0, "count": 1, "nbt": "" }, { "id": 1, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 98, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "c931d6f1-9957-211ab-87f4-67922b5b44bf" }, { "type": 0, "input": [ { "id": 98, "damage": -1, "count": 1, "nbt": "" }, { "id": 106, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 98, "damage": 1, "count": 1, "nbt": "" } ], "uuid": "2d398a00-38c4-18877-8aeb-0de70e35a7bd" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 44, "damage": 5, "count": 1, "nbt": "" }, { "id": 44, "damage": 5, "count": 1, "nbt": "" } ], "output": [ { "id": 98, "damage": 3, "count": 1, "nbt": "" } ], "uuid": "68318ab3-494e-286d-8af0-265a23ceaba3" }, { "type": 0, "input": [ { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 106, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 48, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "c23a67fd-1522-19c79-8bf5-c6d06cdb04ab" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 101, "damage": 0, "count": 16, "nbt": "" } ], "uuid": "1f353853-95f7-194e2-8a38-388cb2f05bae" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 102, "damage": 0, "count": 16, "nbt": "" } ], "uuid": "e339f60b-58b0-209bf-8f24-005b0b8d2190" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 405, "damage": 0, "count": 1, "nbt": "" }, { "id": 405, "damage": 0, "count": 1, "nbt": "" }, { "id": 405, "damage": 0, "count": 1, "nbt": "" }, { "id": 405, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 112, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "7c3b2324-66f4-17070-8aa0-03173a10e092" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 406, "damage": 0, "count": 1, "nbt": "" }, { "id": 406, "damage": 0, "count": 1, "nbt": "" }, { "id": 406, "damage": 0, "count": 1, "nbt": "" }, { "id": 406, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 155, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "a2350812-19b7-194f8-8143-555f2897b080" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 44, "damage": 6, "count": 1, "nbt": "" }, { "id": 44, "damage": 6, "count": 1, "nbt": "" } ], "output": [ { "id": 155, "damage": 1, "count": 1, "nbt": "" } ], "uuid": "3f3d4f13-3577-5629-8298-01157c61f7b2" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 155, "damage": 0, "count": 1, "nbt": "" }, { "id": 155, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 155, "damage": 2, "count": 2, "nbt": "" } ], "uuid": "4b34fd08-5a27-62d5-8d37-1eb97664c797" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 182, "damage": 1, "count": 1, "nbt": "" }, { "id": 182, "damage": 1, "count": 1, "nbt": "" } ], "output": [ { "id": 201, "damage": 2, "count": 1, "nbt": "" } ], "uuid": "6a34d67f-93c2-55fb-8d6d-ef7ba86f1f90" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 406, "damage": 0, "count": 1, "nbt": "" }, { "id": 406, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 1, "damage": 3, "count": 2, "nbt": "" } ], "uuid": "0a3217e6-410a-8ebc-8e01-e206e8ae5b8a" }, { "type": 0, "input": [ { "id": 1, "damage": 3, "count": 1, "nbt": "" }, { "id": 406, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 1, "damage": 1, "count": 1, "nbt": "" } ], "uuid": "fa398915-6990-38d5-8569-34c79abdc1a3" }, { "type": 0, "input": [ { "id": 1, "damage": 3, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 1, "damage": 5, "count": 2, "nbt": "" } ], "uuid": "5a3a03c2-ac91-21fb0-8569-6d1891195f9f" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 1, "damage": 3, "count": 1, "nbt": "" }, { "id": 1, "damage": 3, "count": 1, "nbt": "" }, { "id": 1, "damage": 3, "count": 1, "nbt": "" }, { "id": 1, "damage": 3, "count": 1, "nbt": "" } ], "output": [ { "id": 1, "damage": 4, "count": 4, "nbt": "" } ], "uuid": "c03f3cf7-c6aa-90a5-8b4b-e304292aa594" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 1, "damage": 1, "count": 1, "nbt": "" }, { "id": 1, "damage": 1, "count": 1, "nbt": "" }, { "id": 1, "damage": 1, "count": 1, "nbt": "" }, { "id": 1, "damage": 1, "count": 1, "nbt": "" } ], "output": [ { "id": 1, "damage": 2, "count": 4, "nbt": "" } ], "uuid": "973b28bb-ea82-9312-8bf9-64a5716e95be" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 1, "damage": 5, "count": 1, "nbt": "" }, { "id": 1, "damage": 5, "count": 1, "nbt": "" }, { "id": 1, "damage": 5, "count": 1, "nbt": "" }, { "id": 1, "damage": 5, "count": 1, "nbt": "" } ], "output": [ { "id": 1, "damage": 6, "count": 4, "nbt": "" } ], "uuid": "5532eb84-5fb8-8203-826f-34a9210f9a8c" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 399, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 49, "damage": 0, "count": 1, "nbt": "" }, { "id": 49, "damage": 0, "count": 1, "nbt": "" }, { "id": 49, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 138, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "323fbcf6-aa7d-55f2-8f88-f0bc76503ca0" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 49, "damage": 0, "count": 1, "nbt": "" }, { "id": 49, "damage": 0, "count": 1, "nbt": "" }, { "id": 49, "damage": 0, "count": 1, "nbt": "" }, { "id": 49, "damage": 0, "count": 1, "nbt": "" }, { "id": 381, "damage": 0, "count": 1, "nbt": "" }, { "id": 49, "damage": 0, "count": 1, "nbt": "" }, { "id": 49, "damage": 0, "count": 1, "nbt": "" }, { "id": 49, "damage": 0, "count": 1, "nbt": "" }, { "id": 49, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 130, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "5b354173-5741-103e-83bc-5269096d5e96" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 298, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "ec3af022-cb41-163d2-8d85-f753e955e7bf" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 299, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "1e3ef3dc-cb37-4f62-8b69-2ab6a76e8980" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 300, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "7a39c1a0-a7c0-19e50-8820-725087862baa" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 301, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "c438d55f-192f-9613-8758-4517bc53eda0" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 306, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "bf38ea3b-e050-536f-84ab-0fad66dccaaf" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 307, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "ee3d8ccf-b46c-3f5f-8dad-69a77f493883" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 308, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "c13d9b21-7852-161d6-8d98-3612336e9bbb" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 309, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "bf3a72cd-edf2-7a72-8467-05c1b4077b8c" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 310, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "ba3047be-2c9b-18d18-8f50-29e06e31c689" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 311, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "6d39d10b-efcb-18d9c-8c8e-5acf4b2604bc" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 312, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "aa311c01-5700-1802f-8e95-b157ffdfcb96" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 313, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "b336ede3-beda-56b1-8c54-b63536665190" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 314, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "0d3c3016-d988-809b-803a-2a4f814a3ead" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 315, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "b230d068-4b94-17cf7-8eec-87e6de63caab" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 316, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "56398605-20d0-19af0-8eaa-8bd40d222ab6" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 317, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "5f3d105b-f4c4-17897-84af-c02f3529b3ac" }, { "type": 0, "input": [ { "id": 351, "damage": 0, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 15, "count": 1, "nbt": "" } ], "uuid": "9933e957-d5d4-19b3e-868d-c10ac97a8b98" }, { "type": 0, "input": [ { "id": 351, "damage": 1, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 14, "count": 1, "nbt": "" } ], "uuid": "613780d3-5589-3697-8597-bd21cdd31199" }, { "type": 0, "input": [ { "id": 351, "damage": 2, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 13, "count": 1, "nbt": "" } ], "uuid": "86321a6e-4638-2849-84af-4a68e1ed919c" }, { "type": 0, "input": [ { "id": 351, "damage": 3, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 12, "count": 1, "nbt": "" } ], "uuid": "b937b884-f9cc-879f-8b33-2c7b4539ce82" }, { "type": 0, "input": [ { "id": 351, "damage": 4, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 11, "count": 1, "nbt": "" } ], "uuid": "61338caa-4ad8-6870-8a6b-91e440827e93" }, { "type": 0, "input": [ { "id": 351, "damage": 5, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 10, "count": 1, "nbt": "" } ], "uuid": "433d6590-a1bd-7287-82ef-f53c876c158a" }, { "type": 0, "input": [ { "id": 351, "damage": 6, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 9, "count": 1, "nbt": "" } ], "uuid": "e633fc1e-87e4-75b8-827d-75ab815388a3" }, { "type": 0, "input": [ { "id": 351, "damage": 7, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 8, "count": 1, "nbt": "" } ], "uuid": "6d348cf3-d258-206ab-8cfb-bd285a4097aa" }, { "type": 0, "input": [ { "id": 351, "damage": 8, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 7, "count": 1, "nbt": "" } ], "uuid": "2d3f1ee4-5023-18215-8768-a54c0c021cac" }, { "type": 0, "input": [ { "id": 351, "damage": 9, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 6, "count": 1, "nbt": "" } ], "uuid": "d737746c-45ac-7bb3-8df6-fb5af07f12b9" }, { "type": 0, "input": [ { "id": 351, "damage": 10, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 5, "count": 1, "nbt": "" } ], "uuid": "c339bf7d-3ccb-706d-8b17-ae83b9c63bbb" }, { "type": 0, "input": [ { "id": 351, "damage": 11, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 4, "count": 1, "nbt": "" } ], "uuid": "953a52c1-2437-18943-8196-3abb5a5ebf90" }, { "type": 0, "input": [ { "id": 351, "damage": 12, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 3, "count": 1, "nbt": "" } ], "uuid": "3836e9a9-9bf8-15cd-8477-678c831d6da3" }, { "type": 0, "input": [ { "id": 351, "damage": 13, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 2, "count": 1, "nbt": "" } ], "uuid": "c133e550-e5e1-5300-81cc-7956803f0aa3" }, { "type": 0, "input": [ { "id": 351, "damage": 14, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 1, "count": 1, "nbt": "" } ], "uuid": "85392541-c362-21bd9-8e4a-adfa4dda1cbb" }, { "type": 0, "input": [ { "id": 351, "damage": 15, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "5a37c96e-6470-17d7-87ba-91b9ffbc939b" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 37, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 11, "count": 1, "nbt": "" } ], "uuid": "a0311c72-9ce8-6e1b-8605-9dc24f80b7bb" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 244, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 1, "count": 2, "nbt": "" } ], "uuid": "6f3fca6d-61be-5f9a-82e5-99d1d16d3a98" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 38, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 1, "count": 1, "nbt": "" } ], "uuid": "d83d8e91-b2eb-27c3-85eb-30dcea81028f" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 352, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 15, "count": 3, "nbt": "" } ], "uuid": "bd3cdf20-9497-1e61-8755-d58b17ec278a" }, { "type": 0, "input": [ { "id": 351, "damage": 1, "count": 1, "nbt": "" }, { "id": 351, "damage": 15, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 9, "count": 2, "nbt": "" } ], "uuid": "663e280b-458a-97bf-814d-d663a9d7e0a0" }, { "type": 0, "input": [ { "id": 351, "damage": 1, "count": 1, "nbt": "" }, { "id": 351, "damage": 11, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 14, "count": 2, "nbt": "" } ], "uuid": "4934ddb7-c5a6-2d08-8086-f8f53b66a38e" }, { "type": 0, "input": [ { "id": 351, "damage": 2, "count": 1, "nbt": "" }, { "id": 351, "damage": 15, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 10, "count": 2, "nbt": "" } ], "uuid": "25315582-32df-6b4c-820f-8dbce73ba9b8" }, { "type": 0, "input": [ { "id": 351, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 15, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 8, "count": 2, "nbt": "" } ], "uuid": "a03b1422-c74a-20d44-8e46-add2110490b8" }, { "type": 0, "input": [ { "id": 351, "damage": 8, "count": 1, "nbt": "" }, { "id": 351, "damage": 15, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 7, "count": 2, "nbt": "" } ], "uuid": "02388bf8-4ecd-7ee3-8b43-04bfbac5169c" }, { "type": 0, "input": [ { "id": 351, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 15, "count": 1, "nbt": "" }, { "id": 351, "damage": 15, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 7, "count": 3, "nbt": "" } ], "uuid": "a33440ff-066e-1834f-8f8e-00c263dcc287" }, { "type": 0, "input": [ { "id": 351, "damage": 4, "count": 1, "nbt": "" }, { "id": 351, "damage": 15, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 12, "count": 2, "nbt": "" } ], "uuid": "f13f73ad-86d3-16b6f-8c30-50b42127bab1" }, { "type": 0, "input": [ { "id": 351, "damage": 4, "count": 1, "nbt": "" }, { "id": 351, "damage": 2, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 6, "count": 2, "nbt": "" } ], "uuid": "df3c3988-562b-6d5d-888c-5788cd1ea79a" }, { "type": 0, "input": [ { "id": 351, "damage": 4, "count": 1, "nbt": "" }, { "id": 351, "damage": 1, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 5, "count": 2, "nbt": "" } ], "uuid": "2c321882-8fe7-7871-8d30-545dc7ad1a9f" }, { "type": 0, "input": [ { "id": 351, "damage": 5, "count": 1, "nbt": "" }, { "id": 351, "damage": 9, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 13, "count": 2, "nbt": "" } ], "uuid": "0b3bd19f-d94d-6f56-8d0d-cf9bcc03e585" }, { "type": 0, "input": [ { "id": 351, "damage": 4, "count": 1, "nbt": "" }, { "id": 351, "damage": 1, "count": 1, "nbt": "" }, { "id": 351, "damage": 9, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 13, "count": 3, "nbt": "" } ], "uuid": "3434c871-fbe3-8320-8de6-2b0f439a9c83" }, { "type": 0, "input": [ { "id": 351, "damage": 4, "count": 1, "nbt": "" }, { "id": 351, "damage": 15, "count": 1, "nbt": "" }, { "id": 351, "damage": 1, "count": 1, "nbt": "" }, { "id": 351, "damage": 1, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 13, "count": 4, "nbt": "" } ], "uuid": "48344599-75f0-2a2d-8c00-626bc5f709ae" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 38, "damage": 1, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 12, "count": 1, "nbt": "" } ], "uuid": "db33ca16-cf29-5786-8f94-ba8d1a5a0d9a" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 38, "damage": 2, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 13, "count": 1, "nbt": "" } ], "uuid": "9e3fca12-3fc3-19d4a-8dd4-38f23649299e" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 38, "damage": 3, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 7, "count": 1, "nbt": "" } ], "uuid": "c53e2234-7399-0986-8e83-90deaa7b9f8a" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 38, "damage": 4, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 1, "count": 1, "nbt": "" } ], "uuid": "743b69c3-caf5-1664b-83f7-46a09fe246b5" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 38, "damage": 5, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 14, "count": 1, "nbt": "" } ], "uuid": "c4398b5e-e4cb-8c50-8701-4c20102482be" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 38, "damage": 6, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 7, "count": 1, "nbt": "" } ], "uuid": "4932b300-aa87-27fb-8aaa-d91cefdfc8a3" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 38, "damage": 7, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 9, "count": 1, "nbt": "" } ], "uuid": "c5321071-e030-19eba-8f45-f9c2c2ab7ca1" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 38, "damage": 8, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 7, "count": 1, "nbt": "" } ], "uuid": "1036849a-ef21-59e5-8c0f-1cc3c440c589" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 175, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 11, "count": 2, "nbt": "" } ], "uuid": "8e38e205-c8f9-1050-8e04-2ba83ba0ad93" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 175, "damage": 1, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 13, "count": 2, "nbt": "" } ], "uuid": "733c9dee-309c-167d9-860b-d477d94d5b9b" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 175, "damage": 4, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 1, "count": 2, "nbt": "" } ], "uuid": "1a38816d-4dbf-1bff-894c-137f7ffc1cbc" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 175, "damage": 5, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 9, "count": 2, "nbt": "" } ], "uuid": "8f30e3db-ef57-429a-8045-bb3acb565699" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 457, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 351, "damage": 1, "count": 1, "nbt": "" } ], "uuid": "6939256c-4719-6edb-869a-8f353c8c4f97" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 395, "damage": 1, "count": 1, "nbt": "" } ], "uuid": "3337999f-0215-6562-89b0-2b9d7c6da9aa" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 345, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 395, "damage": 2, "count": 1, "nbt": "" } ], "uuid": "1b393304-d5f7-3f60-8aec-47bbdcb5b599" }, { "type": 4, "uuid": "9d4d10ba-5597-9385-8443-e9a8b7efcca4" }, { "type": 4, "uuid": "ae40a14b-75b0-92d3-8ef6-568d86af8987" }, { "type": 4, "uuid": "4b43944b-9422-18dae-8793-2cbb99446786" }, { "type": 4, "uuid": "00000000-0000-0000-8100-000000000000" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 338, "damage": 0, "count": 1, "nbt": "" }, { "id": 338, "damage": 0, "count": 1, "nbt": "" }, { "id": 338, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 339, "damage": 0, "count": 3, "nbt": "" } ], "uuid": "a73937c6-d639-8b17-8865-4767f9267288" }, { "type": 0, "input": [ { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 339, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 340, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "9e3e0e74-e445-17ef6-8ede-1db430de158f" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 139, "damage": 0, "count": 6, "nbt": "" } ], "uuid": "f7318ce9-75b1-17f25-8b98-eaa8a73fd587" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 48, "damage": -1, "count": 1, "nbt": "" }, { "id": 48, "damage": -1, "count": 1, "nbt": "" }, { "id": 48, "damage": -1, "count": 1, "nbt": "" }, { "id": 48, "damage": -1, "count": 1, "nbt": "" }, { "id": 48, "damage": -1, "count": 1, "nbt": "" }, { "id": 48, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 139, "damage": 1, "count": 6, "nbt": "" } ], "uuid": "cb37b209-058b-46f6-8fdc-20b857ded9ad" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 112, "damage": 0, "count": 1, "nbt": "" }, { "id": 112, "damage": 0, "count": 1, "nbt": "" }, { "id": 112, "damage": 0, "count": 1, "nbt": "" }, { "id": 112, "damage": 0, "count": 1, "nbt": "" }, { "id": 112, "damage": 0, "count": 1, "nbt": "" }, { "id": 112, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 113, "damage": 0, "count": 6, "nbt": "" } ], "uuid": "f73100a8-ee7c-57a4-8853-879ac522eb9c" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 85, "damage": 0, "count": 3, "nbt": "" } ], "uuid": "fe34dfc5-1af9-0c19-8a61-4071330eac8b" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" } ], "output": [ { "id": 85, "damage": 1, "count": 3, "nbt": "" } ], "uuid": "8538c54e-a7d3-218da-8fcf-88512570ac94" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" } ], "output": [ { "id": 85, "damage": 2, "count": 3, "nbt": "" } ], "uuid": "4837f4ee-921b-439e-8917-43e019564e8c" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" } ], "output": [ { "id": 85, "damage": 3, "count": 3, "nbt": "" } ], "uuid": "fe3a245c-c0fb-4efc-86fe-83d5fd43e38c" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" } ], "output": [ { "id": 85, "damage": 4, "count": 3, "nbt": "" } ], "uuid": "f13795d8-c650-8da6-8020-aa0948006594" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" } ], "output": [ { "id": 85, "damage": 5, "count": 3, "nbt": "" } ], "uuid": "5f3f91c9-0530-21d0f-8e76-26d65e89b68a" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 107, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "573acfce-7ea9-96cc-82f7-375d519af983" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 183, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "1e3ee9ff-ebd3-7018-8f2a-01232076538b" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 184, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "fd3e999a-d1cc-18eac-816c-39118ee14a99" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 185, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "14312113-7afa-5753-89f1-8e26ce4e08b3" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 187, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "5739a6e6-660f-1b39-8aae-d48ee7f4d789" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 186, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "ee31adcb-a433-9eff-81d6-5e11715b00ba" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 42, "damage": 0, "count": 1, "nbt": "" }, { "id": 42, "damage": 0, "count": 1, "nbt": "" }, { "id": 42, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 145, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "fc3957db-dfe3-40c5-8c6e-9847dadf3ba6" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 340, "damage": 0, "count": 1, "nbt": "" }, { "id": 340, "damage": 0, "count": 1, "nbt": "" }, { "id": 340, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 47, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "21367b1b-a507-17013-8492-076ea07e64b7" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 332, "damage": 0, "count": 1, "nbt": "" }, { "id": 332, "damage": 0, "count": 1, "nbt": "" }, { "id": 332, "damage": 0, "count": 1, "nbt": "" }, { "id": 332, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 80, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "6f36dd0d-826e-19843-8824-07ebd6db26a2" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 80, "damage": 0, "count": 1, "nbt": "" }, { "id": 80, "damage": 0, "count": 1, "nbt": "" }, { "id": 80, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 78, "damage": 0, "count": 6, "nbt": "" } ], "uuid": "903894a4-88f7-8f7b-8eaf-22a896dfd69b" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 337, "damage": 0, "count": 1, "nbt": "" }, { "id": 337, "damage": 0, "count": 1, "nbt": "" }, { "id": 337, "damage": 0, "count": 1, "nbt": "" }, { "id": 337, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 82, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "3530d19b-0578-1806-8533-6eddce09ff86" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 336, "damage": 0, "count": 1, "nbt": "" }, { "id": 336, "damage": 0, "count": 1, "nbt": "" }, { "id": 336, "damage": 0, "count": 1, "nbt": "" }, { "id": 336, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 45, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "0432fffc-97b0-20161-891a-19f98e542589" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 348, "damage": 0, "count": 1, "nbt": "" }, { "id": 348, "damage": 0, "count": 1, "nbt": "" }, { "id": 348, "damage": 0, "count": 1, "nbt": "" }, { "id": 348, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 89, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "333ed104-748b-71e3-8ff6-e01fbc0cb399" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 287, "damage": 0, "count": 1, "nbt": "" }, { "id": 287, "damage": 0, "count": 1, "nbt": "" }, { "id": 287, "damage": 0, "count": 1, "nbt": "" }, { "id": 287, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "dc3c67c9-7af1-1649-8c7c-1150a2f4a38f" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 289, "damage": 0, "count": 1, "nbt": "" }, { "id": 12, "damage": -1, "count": 1, "nbt": "" }, { "id": 289, "damage": 0, "count": 1, "nbt": "" }, { "id": 12, "damage": -1, "count": 1, "nbt": "" }, { "id": 289, "damage": 0, "count": 1, "nbt": "" }, { "id": 12, "damage": -1, "count": 1, "nbt": "" }, { "id": 289, "damage": 0, "count": 1, "nbt": "" }, { "id": 12, "damage": -1, "count": 1, "nbt": "" }, { "id": 289, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 46, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "3e370268-f1f4-9e31-8a4e-1238fdf69091" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 44, "damage": 3, "count": 6, "nbt": "" } ], "uuid": "3d397566-a74a-2980-8bcd-640bc7e2eba1" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 1, "damage": -1, "count": 1, "nbt": "" }, { "id": 1, "damage": -1, "count": 1, "nbt": "" }, { "id": 1, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 44, "damage": 0, "count": 6, "nbt": "" } ], "uuid": "3a36aacc-590f-63b5-8bbd-50042d40c09a" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 24, "damage": -1, "count": 1, "nbt": "" }, { "id": 24, "damage": -1, "count": 1, "nbt": "" }, { "id": 24, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 44, "damage": 1, "count": 6, "nbt": "" } ], "uuid": "a03215be-b91f-16313-8a13-4ea653974186" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 179, "damage": -1, "count": 1, "nbt": "" }, { "id": 179, "damage": -1, "count": 1, "nbt": "" }, { "id": 179, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 182, "damage": 0, "count": 6, "nbt": "" } ], "uuid": "4239da78-626b-8210-8935-ddbcaa7ac79d" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 158, "damage": 0, "count": 6, "nbt": "" } ], "uuid": "723ee0e8-99a0-43b6-8dff-7e4760eb7c8e" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" } ], "output": [ { "id": 158, "damage": 1, "count": 6, "nbt": "" } ], "uuid": "b93bf9df-6642-77e9-8f1d-8568004363be" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" } ], "output": [ { "id": 158, "damage": 2, "count": 6, "nbt": "" } ], "uuid": "cc3b51c0-1681-1780e-8267-d45f5d4400b6" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" } ], "output": [ { "id": 158, "damage": 3, "count": 6, "nbt": "" } ], "uuid": "7c332a47-216b-63e2-885f-1504dc96a89a" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" } ], "output": [ { "id": 158, "damage": 4, "count": 6, "nbt": "" } ], "uuid": "d23fa0a9-8252-2026e-8da4-1940a94c6e90" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" } ], "output": [ { "id": 158, "damage": 5, "count": 6, "nbt": "" } ], "uuid": "ba34407c-3d6e-0755-89e7-ac1fcb55739e" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 45, "damage": 0, "count": 1, "nbt": "" }, { "id": 45, "damage": 0, "count": 1, "nbt": "" }, { "id": 45, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 44, "damage": 4, "count": 6, "nbt": "" } ], "uuid": "b0352df1-aefb-161a0-873d-f808dcda0db6" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 98, "damage": -1, "count": 1, "nbt": "" }, { "id": 98, "damage": -1, "count": 1, "nbt": "" }, { "id": 98, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 44, "damage": 5, "count": 6, "nbt": "" } ], "uuid": "a83947c1-27ec-1961b-86c3-d3aadab9a1a2" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 112, "damage": 0, "count": 1, "nbt": "" }, { "id": 112, "damage": 0, "count": 1, "nbt": "" }, { "id": 112, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 44, "damage": 7, "count": 6, "nbt": "" } ], "uuid": "ea34479b-aa9b-17b0e-84a8-f02045094086" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 155, "damage": -1, "count": 1, "nbt": "" }, { "id": 155, "damage": -1, "count": 1, "nbt": "" }, { "id": 155, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 44, "damage": 6, "count": 6, "nbt": "" } ], "uuid": "24327e91-5d25-16d16-8da5-fcbc578fde98" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 201, "damage": -1, "count": 1, "nbt": "" }, { "id": 201, "damage": -1, "count": 1, "nbt": "" }, { "id": 201, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 182, "damage": 1, "count": 6, "nbt": "" } ], "uuid": "e63182ff-034b-0f94-8939-cfad3298bb80" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 433, "damage": 0, "count": 1, "nbt": "" }, { "id": 433, "damage": 0, "count": 1, "nbt": "" }, { "id": 433, "damage": 0, "count": 1, "nbt": "" }, { "id": 433, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 201, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "37365587-25f9-5d5e-802a-af3932d5d7a3" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 369, "damage": 0, "count": 1, "nbt": "" }, { "id": 433, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 208, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "923207a8-78de-1aa8-86ad-8886fb1e4199" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 121, "damage": 0, "count": 1, "nbt": "" }, { "id": 121, "damage": 0, "count": 1, "nbt": "" }, { "id": 121, "damage": 0, "count": 1, "nbt": "" }, { "id": 121, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 206, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "eb365021-ccbe-1976f-8608-78276634d192" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 65, "damage": 0, "count": 3, "nbt": "" } ], "uuid": "39385cb2-e98a-8900-8015-ffe3d537d7ba" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 324, "damage": 0, "count": 3, "nbt": "" } ], "uuid": "b438e747-a24e-1689c-821c-33eb94df1fb1" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" } ], "output": [ { "id": 427, "damage": 0, "count": 3, "nbt": "" } ], "uuid": "b9316aad-b58a-05b8-8a47-f9d20e9b8489" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" } ], "output": [ { "id": 428, "damage": 0, "count": 3, "nbt": "" } ], "uuid": "353e6f30-b426-163d7-8d84-a2a680de8f94" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" } ], "output": [ { "id": 429, "damage": 0, "count": 3, "nbt": "" } ], "uuid": "513e1853-b061-3526-8dad-ae983214d6bc" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" } ], "output": [ { "id": 430, "damage": 0, "count": 3, "nbt": "" } ], "uuid": "2e325d9d-712d-6574-8d11-410137c41caf" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" } ], "output": [ { "id": 431, "damage": 0, "count": 3, "nbt": "" } ], "uuid": "5f3df813-a793-3d86-8014-b876ca8a72be" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 96, "damage": 0, "count": 2, "nbt": "" } ], "uuid": "e03c26b1-0888-00e4-8a77-c9b507d231a3" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 167, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "4e37b91d-3252-9577-85e8-6621adb7ce91" }, { "type": 1, "width": 1, "height": 3, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 131, "damage": 0, "count": 2, "nbt": "" } ], "uuid": "1f324615-d07a-4187-89a5-c7097710ba99" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 25, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "733f23d6-b07b-6fb7-8d5a-bdf4c386ff8a" }, { "type": 1, "width": 2, "height": 3, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 330, "damage": 0, "count": 3, "nbt": "" } ], "uuid": "1d387080-389c-0b6e-8580-29197e5facba" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 323, "damage": 0, "count": 3, "nbt": "" } ], "uuid": "be31a6b2-2cae-19e12-84d2-e7646c6a33bb" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 325, "damage": 1, "count": 1, "nbt": "" }, { "id": 325, "damage": 1, "count": 1, "nbt": "" }, { "id": 325, "damage": 1, "count": 1, "nbt": "" }, { "id": 353, "damage": 0, "count": 1, "nbt": "" }, { "id": 344, "damage": 0, "count": 1, "nbt": "" }, { "id": 353, "damage": 0, "count": 1, "nbt": "" }, { "id": 296, "damage": 0, "count": 1, "nbt": "" }, { "id": 296, "damage": 0, "count": 1, "nbt": "" }, { "id": 296, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 354, "damage": 0, "count": 1, "nbt": "" }, { "id": 325, "damage": 0, "count": 3, "nbt": "" } ], "uuid": "1334f513-691d-1686a-8417-a38090c52681" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 338, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 353, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "0034c266-8669-0964-80f8-0f458cb46b99" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 391, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 396, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "c33e6e91-275f-21cbd-8d38-4d2552758bb6" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 360, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" }, { "id": 371, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 382, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "ca36f024-49df-3369-822a-44facd7001ad" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 369, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 377, "damage": 0, "count": 2, "nbt": "" } ], "uuid": "6930db0b-5556-17875-85a8-4ffacf7decaf" }, { "type": 0, "input": [ { "id": 377, "damage": 0, "count": 1, "nbt": "" }, { "id": 341, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 378, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "f43aa0de-003b-4c02-87f6-c5cc738ee7a1" }, { "type": 0, "input": [ { "id": 39, "damage": -1, "count": 1, "nbt": "" }, { "id": 353, "damage": 0, "count": 1, "nbt": "" }, { "id": 375, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 376, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "74387959-abc9-5d63-8a12-cc7cbd936cb5" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 17, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 5, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "ce314836-7093-7307-889b-067c24cb1382" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 17, "damage": 1, "count": 1, "nbt": "" } ], "output": [ { "id": 5, "damage": 1, "count": 4, "nbt": "" } ], "uuid": "fb3a6224-13d5-7be6-83bc-5a9fedafb8a0" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 17, "damage": 2, "count": 1, "nbt": "" } ], "output": [ { "id": 5, "damage": 2, "count": 4, "nbt": "" } ], "uuid": "3130db34-4c6f-2fe8-8189-366549a0f8bb" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 17, "damage": 3, "count": 1, "nbt": "" } ], "output": [ { "id": 5, "damage": 3, "count": 4, "nbt": "" } ], "uuid": "833fd08d-6502-5813-8d5c-aed2f43b0da1" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 162, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 5, "damage": 4, "count": 4, "nbt": "" } ], "uuid": "dc3bde6b-04de-5faa-8f35-070bdf3557ac" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 162, "damage": 1, "count": 1, "nbt": "" } ], "output": [ { "id": 5, "damage": 5, "count": 4, "nbt": "" } ], "uuid": "f43e848c-d93b-21f30-8de2-4a503e264483" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 280, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "7e3dd7b0-33de-20d4e-89fd-76121efb24be" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 263, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 50, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "073683ae-0d15-7f43-8a9f-99e7f6b111b4" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 263, "damage": 1, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 50, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "9231fdb2-fcf7-1082-8b72-990b7aff34ab" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 281, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "9439f642-21e0-2023f-8859-ffb3a7ce288c" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 412, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 391, "damage": 0, "count": 1, "nbt": "" }, { "id": 393, "damage": 0, "count": 1, "nbt": "" }, { "id": 39, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 281, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 413, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "503c6ea6-ff91-18b85-89b8-3fa4b787a3a1" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 412, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 391, "damage": 0, "count": 1, "nbt": "" }, { "id": 393, "damage": 0, "count": 1, "nbt": "" }, { "id": 40, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 281, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 413, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "9d34f2a0-72fa-4f88-830f-1a8220e87f8b" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 374, "damage": 0, "count": 3, "nbt": "" } ], "uuid": "c1335586-9f71-4c4c-88f9-513cb52114ac" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 336, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 336, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 336, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 390, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "d1397f10-1e7b-19ce3-8ef3-70023ad8f4bd" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 66, "damage": 0, "count": 16, "nbt": "" } ], "uuid": "6c3779d0-04cd-21622-8496-1fef9ad13489" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 27, "damage": 0, "count": 6, "nbt": "" } ], "uuid": "ba30d9e5-d2f3-17e6d-84f7-b2577d766198" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 70, "damage": -1, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 28, "damage": 0, "count": 6, "nbt": "" } ], "uuid": "283a3a66-3344-10cc-8c15-4ef92f3d75a5" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 76, "damage": -1, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 126, "damage": 0, "count": 6, "nbt": "" } ], "uuid": "5030aa72-64b9-083a-84ed-5f27f06297b1" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 328, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "7a3a1ce6-68ad-5df5-8cfe-ccc5aa9c54a0" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 54, "damage": -1, "count": 1, "nbt": "" }, { "id": 328, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 342, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "fb34431b-614c-196a4-8d90-66c12bd1acaa" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 410, "damage": 0, "count": 1, "nbt": "" }, { "id": 328, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 408, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "eb33e423-43f7-4e99-80a0-721b18602aa5" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 380, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "db335d37-df3b-0b0c-8712-ce4a8be7fd8b" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 369, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 379, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "8534fd47-bd02-173d2-843a-887392ce158f" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 86, "damage": 0, "count": 1, "nbt": "" }, { "id": 50, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 91, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "c3399917-f526-207b3-8cf5-c77be90c2ab8" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 269, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 333, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "dc31fb75-767e-175ee-8518-974efd25e59e" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 269, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" } ], "output": [ { "id": 333, "damage": 1, "count": 1, "nbt": "" } ], "uuid": "7e38d12d-0a03-393b-899b-b249aeb8a09c" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 269, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" } ], "output": [ { "id": 333, "damage": 2, "count": 1, "nbt": "" } ], "uuid": "bf352750-670c-75b8-84b3-04ec29e37fb7" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 269, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" } ], "output": [ { "id": 333, "damage": 3, "count": 1, "nbt": "" } ], "uuid": "af385bca-3f16-151f-8850-bce4658d3e9a" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 269, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" } ], "output": [ { "id": 333, "damage": 4, "count": 1, "nbt": "" } ], "uuid": "953ff74e-09b8-911d-82c8-9c1570ed819c" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 269, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" } ], "output": [ { "id": 333, "damage": 5, "count": 1, "nbt": "" } ], "uuid": "5a3ff230-8372-2bd4-8c9b-7d617a70d4bb" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 325, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "5d398efe-c68a-16be8-89a3-44c337bea186" }, { "type": 0, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 318, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 259, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "6f3aee6e-112b-9e6d-8268-55c82b2daa94" }, { "type": 1, "width": 3, "height": 1, "input": [ { "id": 296, "damage": 0, "count": 1, "nbt": "" }, { "id": 296, "damage": 0, "count": 1, "nbt": "" }, { "id": 296, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 297, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "613669ec-0828-210fa-8bbe-7e995a82aca0" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" }, { "id": 5, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 53, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "0c307223-4d55-17f2c-83fe-722c8cb71e8b" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" }, { "id": 5, "damage": 1, "count": 1, "nbt": "" } ], "output": [ { "id": 134, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "cc3f7c08-24f4-3e3b-83bb-c186ca47fd88" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" }, { "id": 5, "damage": 2, "count": 1, "nbt": "" } ], "output": [ { "id": 135, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "ad363157-9aaf-41a0-896c-4c6aa7bf9a97" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" }, { "id": 5, "damage": 3, "count": 1, "nbt": "" } ], "output": [ { "id": 136, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "0732b7ba-85b4-1865b-82a1-fc3c7bd744bd" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" }, { "id": 5, "damage": 4, "count": 1, "nbt": "" } ], "output": [ { "id": 163, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "2b3e7936-8672-865c-846c-d3af2ec5dab5" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" }, { "id": 5, "damage": 5, "count": 1, "nbt": "" } ], "output": [ { "id": 164, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "4938497d-2a90-7ae1-897a-61e236ce9f8a" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 67, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "de302db7-f3c9-19d24-8298-865194189397" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 45, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 45, "damage": 0, "count": 1, "nbt": "" }, { "id": 45, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 45, "damage": 0, "count": 1, "nbt": "" }, { "id": 45, "damage": 0, "count": 1, "nbt": "" }, { "id": 45, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 108, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "cc3cca6d-850d-211c8-82dd-c855e54299b1" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 24, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 24, "damage": -1, "count": 1, "nbt": "" }, { "id": 24, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 24, "damage": -1, "count": 1, "nbt": "" }, { "id": 24, "damage": -1, "count": 1, "nbt": "" }, { "id": 24, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 128, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "983b81db-bbd8-7caf-8837-e30ad0027ea2" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 179, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 179, "damage": -1, "count": 1, "nbt": "" }, { "id": 179, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 179, "damage": -1, "count": 1, "nbt": "" }, { "id": 179, "damage": -1, "count": 1, "nbt": "" }, { "id": 179, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 180, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "8b349b4e-f3b2-01fa-8cfb-092babc8789d" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 98, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 98, "damage": -1, "count": 1, "nbt": "" }, { "id": 98, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 98, "damage": -1, "count": 1, "nbt": "" }, { "id": 98, "damage": -1, "count": 1, "nbt": "" }, { "id": 98, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 109, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "4630b726-66b8-21169-868f-6598eb4f6092" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 155, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 155, "damage": -1, "count": 1, "nbt": "" }, { "id": 155, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 155, "damage": -1, "count": 1, "nbt": "" }, { "id": 155, "damage": -1, "count": 1, "nbt": "" }, { "id": 155, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 156, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "e83a8d32-aed8-20f9a-8539-bb4dd68ec78b" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 112, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 112, "damage": 0, "count": 1, "nbt": "" }, { "id": 112, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 112, "damage": 0, "count": 1, "nbt": "" }, { "id": 112, "damage": 0, "count": 1, "nbt": "" }, { "id": 112, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 114, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "423c75ee-1e14-7af8-87e0-ab7d94280cba" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 201, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 201, "damage": -1, "count": 1, "nbt": "" }, { "id": 201, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 201, "damage": -1, "count": 1, "nbt": "" }, { "id": 201, "damage": -1, "count": 1, "nbt": "" }, { "id": 201, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 203, "damage": 0, "count": 4, "nbt": "" } ], "uuid": "4938adc1-5109-1a88-8c14-122dfb599c84" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 287, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 287, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 346, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "b5353bfc-e1d4-21bb5-84f8-270d485603aa" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 346, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 391, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 398, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "273c1e8c-5a84-16994-89d0-4142c3a9dab7" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 35, "damage": -1, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 321, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "af3b7d22-5df1-0a88-8622-970c7c2b2f8b" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 260, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 322, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "ad334055-5d89-39f1-8eea-182936d22ebf" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 41, "damage": 0, "count": 1, "nbt": "" }, { "id": 41, "damage": 0, "count": 1, "nbt": "" }, { "id": 41, "damage": 0, "count": 1, "nbt": "" }, { "id": 41, "damage": 0, "count": 1, "nbt": "" }, { "id": 260, "damage": 0, "count": 1, "nbt": "" }, { "id": 41, "damage": 0, "count": 1, "nbt": "" }, { "id": 41, "damage": 0, "count": 1, "nbt": "" }, { "id": 41, "damage": 0, "count": 1, "nbt": "" }, { "id": 41, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 466, "damage": 1, "count": 1, "nbt": "" } ], "uuid": "9d34b800-272e-7f26-8b9e-58597210a9b2" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 69, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "f13b6d6c-37d1-2a2a-8f0a-d417f9e23db7" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 76, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "75382bbb-2459-4ad3-8561-7cca72b9f5bd" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 406, "damage": 0, "count": 1, "nbt": "" }, { "id": 406, "damage": 0, "count": 1, "nbt": "" }, { "id": 406, "damage": 0, "count": 1, "nbt": "" }, { "id": 158, "damage": -1, "count": 1, "nbt": "" }, { "id": 158, "damage": -1, "count": 1, "nbt": "" }, { "id": 158, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 151, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "5a320f44-ac26-189da-86cd-83cd3ce5ecaf" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 76, "damage": -1, "count": 1, "nbt": "" }, { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 76, "damage": -1, "count": 1, "nbt": "" }, { "id": 1, "damage": -1, "count": 1, "nbt": "" }, { "id": 1, "damage": -1, "count": 1, "nbt": "" }, { "id": 1, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 356, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "613eae0c-ea31-3e57-8343-553030e5c1b7" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 89, "damage": 0, "count": 1, "nbt": "" }, { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 123, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "fd30edfe-9e39-1785a-82e8-c73790c4c4bd" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 347, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "383ea897-37ac-21128-8fbb-7bcf62058b85" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 345, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "7d3d0f8c-aa43-4a78-8c16-eab8173faeae" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 1, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 77, "damage": 5, "count": 1, "nbt": "" } ], "uuid": "983997f9-99a8-8948-840c-bf158c822188" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 5, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 143, "damage": 5, "count": 1, "nbt": "" } ], "uuid": "aa3a2d4a-ff81-47d7-80ab-20fc11e8a991" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 1, "damage": 0, "count": 1, "nbt": "" }, { "id": 1, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 70, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "f23153b6-733e-232b-8344-a28d9c984d8f" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 72, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "a3308692-b3cb-194b9-857a-18b49bcc25b2" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 266, "damage": 0, "count": 1, "nbt": "" }, { "id": 266, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 147, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "4c3b277a-26bd-769d-877f-94ee90717183" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 148, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "a438aa57-3d49-18cf8-826d-2520aa150dbc" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 261, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 23, "damage": 3, "count": 1, "nbt": "" } ], "uuid": "31368d36-67a8-16bb1-80ee-1aa6b2c933aa" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 125, "damage": 3, "count": 1, "nbt": "" } ], "uuid": "b0314c6d-ad5b-7574-883c-849bb404e481" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 54, "damage": -1, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" } ], "output": [ { "id": 410, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "693c1533-0123-4502-8dec-e8a62071e7b7" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 406, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 251, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "af3295fc-0c43-7a94-84df-ad94a62b16bf" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 265, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" }, { "id": 331, "damage": 0, "count": 1, "nbt": "" }, { "id": 4, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 33, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "7e3430c7-cc99-6e6d-8363-263398dbd8a5" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 341, "damage": 0, "count": 1, "nbt": "" }, { "id": 33, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 29, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "db39e7fa-21b7-19328-842a-86db32ba67bf" }, { "type": 1, "width": 3, "height": 2, "input": [ { "id": 35, "damage": -1, "count": 1, "nbt": "" }, { "id": 35, "damage": -1, "count": 1, "nbt": "" }, { "id": 35, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" }, { "id": 5, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 355, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "613a94d9-f4cc-4e31-84c2-05063b06df94" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 340, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 49, "damage": 0, "count": 1, "nbt": "" }, { "id": 264, "damage": 0, "count": 1, "nbt": "" }, { "id": 49, "damage": 0, "count": 1, "nbt": "" }, { "id": 49, "damage": 0, "count": 1, "nbt": "" }, { "id": 49, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 116, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "ed337196-958b-16dab-8afe-1b56cfd4c080" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 415, "damage": 0, "count": 1, "nbt": "" }, { "id": 415, "damage": 0, "count": 1, "nbt": "" }, { "id": 415, "damage": 0, "count": 1, "nbt": "" }, { "id": 415, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 334, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "07344cf3-8ba2-65eb-8008-0d871deb52bf" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 76, "damage": -1, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 76, "damage": -1, "count": 1, "nbt": "" }, { "id": 406, "damage": 0, "count": 1, "nbt": "" }, { "id": 76, "damage": -1, "count": 1, "nbt": "" }, { "id": 1, "damage": -1, "count": 1, "nbt": "" }, { "id": 1, "damage": -1, "count": 1, "nbt": "" }, { "id": 1, "damage": -1, "count": 1, "nbt": "" } ], "output": [ { "id": 404, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "833f97f3-6922-1975e-821c-1838a2eb689d" }, { "type": 1, "width": 1, "height": 2, "input": [ { "id": 46, "damage": 0, "count": 1, "nbt": "" }, { "id": 328, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 407, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "12312b8a-5015-9353-8212-cc5d89a3a493" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" }, { "id": 280, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 389, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "2e368f41-fb07-3046-8d9f-6e13e2b829af" }, { "type": 0, "input": [ { "id": 377, "damage": 0, "count": 1, "nbt": "" }, { "id": 368, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 381, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "d735089c-8d7b-8f54-846c-6d249119a381" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 381, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" }, { "id": 370, "damage": 0, "count": 1, "nbt": "" }, { "id": 20, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 426, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "e4389d5c-6dd5-5a43-8eba-9cb1a0d5be91" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 35, "damage": 0, "count": 1, "nbt": "" }, { "id": 35, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 171, "damage": 0, "count": 3, "nbt": "" } ], "uuid": "b53a66c9-5e21-5e7d-81d9-e8abc3fa4b9f" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 159, "damage": 15, "count": 8, "nbt": "" } ], "uuid": "81360234-da7c-5f38-8ee8-e222577ab4b8" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 35, "damage": 1, "count": 1, "nbt": "" }, { "id": 35, "damage": 1, "count": 1, "nbt": "" } ], "output": [ { "id": 171, "damage": 1, "count": 3, "nbt": "" } ], "uuid": "913b0f45-165a-207d6-8fb4-35f0002868b6" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 1, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 159, "damage": 14, "count": 8, "nbt": "" } ], "uuid": "5f3d2f35-82c8-17bbd-8bfb-aefcf063ddb4" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 35, "damage": 2, "count": 1, "nbt": "" }, { "id": 35, "damage": 2, "count": 1, "nbt": "" } ], "output": [ { "id": 171, "damage": 2, "count": 3, "nbt": "" } ], "uuid": "8f359326-4cd2-63f8-8c57-ed8698b3ccb6" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 2, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 159, "damage": 13, "count": 8, "nbt": "" } ], "uuid": "d83ccac5-3946-19781-8d7f-b6366554c897" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 35, "damage": 3, "count": 1, "nbt": "" }, { "id": 35, "damage": 3, "count": 1, "nbt": "" } ], "output": [ { "id": 171, "damage": 3, "count": 3, "nbt": "" } ], "uuid": "eb3c241e-3a85-20ac1-863f-c4249b94a98a" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 3, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 159, "damage": 12, "count": 8, "nbt": "" } ], "uuid": "c3359dde-bd13-2171b-815f-b563c4e84fb2" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 35, "damage": 4, "count": 1, "nbt": "" }, { "id": 35, "damage": 4, "count": 1, "nbt": "" } ], "output": [ { "id": 171, "damage": 4, "count": 3, "nbt": "" } ], "uuid": "5d33bd55-d06b-7b04-8ee0-0b960605c1b7" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 4, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 159, "damage": 11, "count": 8, "nbt": "" } ], "uuid": "1835a8d6-c3a1-17e14-81a3-fce3419b61be" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 35, "damage": 5, "count": 1, "nbt": "" }, { "id": 35, "damage": 5, "count": 1, "nbt": "" } ], "output": [ { "id": 171, "damage": 5, "count": 3, "nbt": "" } ], "uuid": "cc381a6e-4291-3fc6-8392-a9ef57d46089" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 5, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 159, "damage": 10, "count": 8, "nbt": "" } ], "uuid": "a63664d3-1466-5a6f-85ec-0149bd2da0a1" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 35, "damage": 6, "count": 1, "nbt": "" }, { "id": 35, "damage": 6, "count": 1, "nbt": "" } ], "output": [ { "id": 171, "damage": 6, "count": 3, "nbt": "" } ], "uuid": "f838fe01-97ba-200cc-8fa3-453335b5e7b8" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 6, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 159, "damage": 9, "count": 8, "nbt": "" } ], "uuid": "f530fc51-83ac-21a23-806e-6eb02cdf95a6" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 35, "damage": 7, "count": 1, "nbt": "" }, { "id": 35, "damage": 7, "count": 1, "nbt": "" } ], "output": [ { "id": 171, "damage": 7, "count": 3, "nbt": "" } ], "uuid": "983fa475-c110-799f-8f68-d8c0c14eb7bb" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 7, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 159, "damage": 8, "count": 8, "nbt": "" } ], "uuid": "83353241-3a70-201a9-87ec-570216878790" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 35, "damage": 8, "count": 1, "nbt": "" }, { "id": 35, "damage": 8, "count": 1, "nbt": "" } ], "output": [ { "id": 171, "damage": 8, "count": 3, "nbt": "" } ], "uuid": "433185e9-5351-16e23-8e81-a9c694a5eda8" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 8, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 159, "damage": 7, "count": 8, "nbt": "" } ], "uuid": "d93fdc79-5c81-286b-856e-c39b02ef2199" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 35, "damage": 9, "count": 1, "nbt": "" }, { "id": 35, "damage": 9, "count": 1, "nbt": "" } ], "output": [ { "id": 171, "damage": 9, "count": 3, "nbt": "" } ], "uuid": "003626c1-1ad9-435c-82bf-8df8098bc38c" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 9, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 159, "damage": 6, "count": 8, "nbt": "" } ], "uuid": "283af721-d78d-2691-8225-d0aeb2bd5dae" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 35, "damage": 10, "count": 1, "nbt": "" }, { "id": 35, "damage": 10, "count": 1, "nbt": "" } ], "output": [ { "id": 171, "damage": 10, "count": 3, "nbt": "" } ], "uuid": "40372dc5-9895-17f22-8eb8-0be5ccf0bb8c" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 10, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 159, "damage": 5, "count": 8, "nbt": "" } ], "uuid": "18355c6d-1bb6-9756-818a-4d02371cc299" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 35, "damage": 11, "count": 1, "nbt": "" }, { "id": 35, "damage": 11, "count": 1, "nbt": "" } ], "output": [ { "id": 171, "damage": 11, "count": 3, "nbt": "" } ], "uuid": "e53cac6f-672d-17f03-886e-3c625e4e5489" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 11, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 159, "damage": 4, "count": 8, "nbt": "" } ], "uuid": "ca3a4889-bc8e-177a3-8000-000a38ff44b0" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 35, "damage": 12, "count": 1, "nbt": "" }, { "id": 35, "damage": 12, "count": 1, "nbt": "" } ], "output": [ { "id": 171, "damage": 12, "count": 3, "nbt": "" } ], "uuid": "66387aa8-6476-633a-83ae-e6e6974a799b" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 12, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 159, "damage": 3, "count": 8, "nbt": "" } ], "uuid": "753ff5c8-5d37-33d6-89b0-87ddc88a789b" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 35, "damage": 13, "count": 1, "nbt": "" }, { "id": 35, "damage": 13, "count": 1, "nbt": "" } ], "output": [ { "id": 171, "damage": 13, "count": 3, "nbt": "" } ], "uuid": "bd318f73-e0a7-18c76-8264-d45e32f9698b" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 13, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 159, "damage": 2, "count": 8, "nbt": "" } ], "uuid": "f737a038-cf49-21b18-8089-b0730c0112b7" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 35, "damage": 14, "count": 1, "nbt": "" }, { "id": 35, "damage": 14, "count": 1, "nbt": "" } ], "output": [ { "id": 171, "damage": 14, "count": 3, "nbt": "" } ], "uuid": "a7351642-e800-5632-83e4-ff3bd4ef3a96" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 14, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 159, "damage": 1, "count": 8, "nbt": "" } ], "uuid": "3f33d74a-4f57-90aa-8b7d-98299339c68b" }, { "type": 1, "width": 2, "height": 1, "input": [ { "id": 35, "damage": 15, "count": 1, "nbt": "" }, { "id": 35, "damage": 15, "count": 1, "nbt": "" } ], "output": [ { "id": 171, "damage": 15, "count": 3, "nbt": "" } ], "uuid": "0e392c18-2b9c-19f2d-8567-be48d8af75a4" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 15, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" }, { "id": 172, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 159, "damage": 0, "count": 8, "nbt": "" } ], "uuid": "313b5847-6292-178df-86db-4123b455f8bf" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 341, "damage": 0, "count": 1, "nbt": "" }, { "id": 341, "damage": 0, "count": 1, "nbt": "" }, { "id": 341, "damage": 0, "count": 1, "nbt": "" }, { "id": 341, "damage": 0, "count": 1, "nbt": "" }, { "id": 341, "damage": 0, "count": 1, "nbt": "" }, { "id": 341, "damage": 0, "count": 1, "nbt": "" }, { "id": 341, "damage": 0, "count": 1, "nbt": "" }, { "id": 341, "damage": 0, "count": 1, "nbt": "" }, { "id": 341, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 165, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "633039e0-8a53-16387-86b3-3cefc5fa39a3" }, { "type": 1, "width": 1, "height": 1, "input": [ { "id": 165, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 341, "damage": 0, "count": 9, "nbt": "" } ], "uuid": "913c4d6f-9059-7033-833c-c2bcf6901680" }, { "type": 0, "input": [ { "id": 377, "damage": 0, "count": 1, "nbt": "" }, { "id": 263, "damage": 0, "count": 1, "nbt": "" }, { "id": 289, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 385, "damage": 0, "count": 3, "nbt": "" } ], "uuid": "353a8303-921a-76a8-87b7-cd1af1fa16ba" }, { "type": 0, "input": [ { "id": 377, "damage": 0, "count": 1, "nbt": "" }, { "id": 263, "damage": 1, "count": 1, "nbt": "" }, { "id": 289, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 385, "damage": 0, "count": 3, "nbt": "" } ], "uuid": "863230be-1ac7-20e33-8cd6-5cd815f7f18b" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 287, "damage": 0, "count": 1, "nbt": "" }, { "id": 287, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 287, "damage": 0, "count": 1, "nbt": "" }, { "id": 341, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 287, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 420, "damage": 0, "count": 2, "nbt": "" } ], "uuid": "7e3709aa-98ef-1850f-8096-bb382802309e" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" }, { "id": 0, "damage": 0, "count": 0, "nbt": "" }, { "id": 334, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 416, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "c03b3f11-8c0b-9744-8042-a2418458e18f" }, { "type": 1, "width": 2, "height": 2, "input": [ { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 168, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "633e16d9-2670-194dc-830a-598735206195" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 168, "damage": 2, "count": 1, "nbt": "" } ], "uuid": "4e3a49bd-d3f5-64f7-8980-03daf64d7ba2" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 351, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 168, "damage": 1, "count": 1, "nbt": "" } ], "uuid": "5134bfb8-a77b-0ac6-85a4-40be668fa794" }, { "type": 1, "width": 3, "height": 3, "input": [ { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 422, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 422, "damage": 0, "count": 1, "nbt": "" }, { "id": 422, "damage": 0, "count": 1, "nbt": "" }, { "id": 422, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" }, { "id": 422, "damage": 0, "count": 1, "nbt": "" }, { "id": 409, "damage": 0, "count": 1, "nbt": "" } ], "output": [ { "id": 169, "damage": 0, "count": 1, "nbt": "" } ], "uuid": "283cde40-627d-67e1-82c9-1a53f242788f" }, { "type": 2, "inputId": 4, "output": { "id": 1, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 12, "output": { "id": 20, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 14, "output": { "id": 266, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 15, "output": { "id": 265, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 16, "output": { "id": 263, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 17, "output": { "id": 263, "damage": 1, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 21, "output": { "id": 351, "damage": 4, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 56, "output": { "id": 264, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 73, "output": { "id": 331, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 81, "output": { "id": 351, "damage": 2, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 82, "output": { "id": 172, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 87, "output": { "id": 405, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 129, "output": { "id": 388, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 153, "output": { "id": 406, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 162, "output": { "id": 263, "damage": 1, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 319, "output": { "id": 320, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 337, "output": { "id": 336, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 349, "output": { "id": 350, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 363, "output": { "id": 364, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 365, "output": { "id": 366, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 392, "output": { "id": 393, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 411, "output": { "id": 412, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 423, "output": { "id": 424, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 432, "output": { "id": 433, "damage": 0, "count": 1, "nbt": "" } }, { "type": 2, "inputId": 460, "output": { "id": 463, "damage": 0, "count": 1, "nbt": "" } }, { "type": 3, "inputId": 19, "inputDamage": 1, "output": { "id": 19, "damage": 0, "count": 1, "nbt": "" } }, { "type": 3, "inputId": 98, "inputDamage": 0, "output": { "id": 98, "damage": 2, "count": 1, "nbt": "" } } ] ================================================ FILE: src/pocketmine/scheduler/AsyncPool.php ================================================ server = $server; $this->size = (int) $size; for($i = 0; $i < $this->size; ++$i){ $this->workerUsage[$i] = 0; $this->workers[$i] = new AsyncWorker($this->server->getLogger(), $i + 1); $this->workers[$i]->setClassLoader($this->server->getLoader()); $this->workers[$i]->start(); } } public function getSize(){ return $this->size; } public function increaseSize($newSize){ $newSize = (int) $newSize; if($newSize > $this->size){ for($i = $this->size; $i < $newSize; ++$i){ $this->workerUsage[$i] = 0; $this->workers[$i] = new AsyncWorker($this->server->getLogger(), $i + 1); $this->workers[$i]->setClassLoader($this->server->getLoader()); $this->workers[$i]->start(); } $this->size = $newSize; } } public function submitTaskToWorker(AsyncTask $task, $worker){ if(isset($this->tasks[$task->getTaskId()]) or $task->isGarbage()){ return; } $worker = (int) $worker; if($worker < 0 or $worker >= $this->size){ throw new \InvalidArgumentException("Invalid worker $worker"); } $this->tasks[$task->getTaskId()] = $task; $this->workers[$worker]->stack($task); $this->workerUsage[$worker]++; $this->taskWorkers[$task->getTaskId()] = $worker; } public function submitTask(AsyncTask $task){ if(isset($this->tasks[$task->getTaskId()]) or $task->isGarbage()){ return; } $selectedWorker = mt_rand(0, $this->size - 1); $selectedTasks = $this->workerUsage[$selectedWorker]; for($i = 0; $i < $this->size; ++$i){ if($this->workerUsage[$i] < $selectedTasks){ $selectedWorker = $i; $selectedTasks = $this->workerUsage[$i]; } } $this->submitTaskToWorker($task, $selectedWorker); } private function removeTask(AsyncTask $task, $force = false){ $task->setGarbage(); if(isset($this->taskWorkers[$task->getTaskId()])){ if(!$force and ($task->isRunning() or !$task->isGarbage())){ return; } $this->workerUsage[$this->taskWorkers[$task->getTaskId()]]--; $this->workers[$this->taskWorkers[$task->getTaskId()]]->collector($task); } unset($this->tasks[$task->getTaskId()]); unset($this->taskWorkers[$task->getTaskId()]); $task->cleanObject(); } public function removeTasks(){ do{ foreach($this->tasks as $task){ $task->cancelRun(); $this->removeTask($task); } if(count($this->tasks) > 0){ Server::microSleep(25000); } }while(count($this->tasks) > 0); for($i = 0; $i < $this->size; ++$i){ $this->workerUsage[$i] = 0; } $this->taskWorkers = []; $this->tasks = []; } public function collectTasks(){ Timings::$schedulerAsyncTimer->startTiming(); foreach($this->tasks as $task){ if($task->isFinished() and !$task->isRunning() and !$task->isCrashed()){ if(!$task->hasCancelledRun()){ $task->onCompletion($this->server); } $this->removeTask($task); }elseif($task->isTerminated() or $task->isCrashed()){ $this->server->getLogger()->critical("Could not execute asynchronous task " . (new \ReflectionClass($task))->getShortName() . ": Task crashed"); $this->removeTask($task, true); } } Timings::$schedulerAsyncTimer->stopTiming(); } } ================================================ FILE: src/pocketmine/scheduler/AsyncTask.php ================================================ isGarbage; } public function setGarbage(){ $this->isGarbage = true; } public function isFinished() : bool{ return $this->isFinished; } public function run(){ $this->result = null; $this->isGarbage = false; if($this->cancelRun !== true){ try{ $this->onRun(); }catch(\Throwable $e){ $this->crashed = true; $this->worker->handleException($e); } } $this->isFinished = true; //$this->setGarbage(); } public function isCrashed(){ return $this->crashed; } /** * @return mixed */ public function getResult(){ return $this->serialized ? unserialize($this->result) : $this->result; } public function cancelRun(){ $this->cancelRun = true; } public function hasCancelledRun(){ return $this->cancelRun === true; } /** * @return bool */ public function hasResult(){ return $this->result !== null; } /** * @param mixed $result * @param bool $serialize */ public function setResult($result, $serialize = true){ $this->result = $serialize ? serialize($result) : $result; $this->serialized = $serialize; } public function setTaskId($taskId){ $this->taskId = $taskId; } public function getTaskId(){ return $this->taskId; } /** * Gets something into the local thread store. * You have to initialize this in some way from the task on run * * @param string $identifier * @return mixed */ public function getFromThreadStore($identifier){ global $store; return $this->isGarbage() ? null : $store[$identifier]; } /** * Saves something into the local thread store. * This might get deleted at any moment. * * @param string $identifier * @param mixed $value */ public function saveToThreadStore($identifier, $value){ global $store; if(!$this->isGarbage()){ $store[$identifier] = $value; } } /** * Actions to execute when run * * @return void */ public abstract function onRun(); /** * Actions to execute when completed (on main thread) * Implement this if you want to handle the data in your AsyncTask after it has been processed * * @param Server $server * * @return void */ public function onCompletion(Server $server){ } /** * Call this method from {@link AsyncTask#onRun} (AsyncTask execution thread) to schedule a call to * {@link AsyncTask#onProgressUpdate} from the main thread with the given progress parameter. * * @param mixed $progress A value that can be safely serialize()'ed. */ public function publishProgress($progress){ $this->progressUpdates[] = serialize($progress); } /** * @internal Only call from AsyncPool.php on the main thread * * @param Server $server */ public function checkProgressUpdates(Server $server){ while($this->progressUpdates->count() !== 0){ $progress = $this->progressUpdates->shift(); $this->onProgressUpdate($server, unserialize($progress)); } } /** * Called from the main thread after {@link AsyncTask#publishProgress} is called. * All {@link AsyncTask#publishProgress} calls should result in {@link AsyncTask#onProgressUpdate} calls before * {@link AsyncTask#onCompletion} is called. * * @param Server $server * @param mixed $progress The parameter passed to {@link AsyncTask#publishProgress}. It is serialize()'ed * and then unserialize()'ed, as if it has been cloned. */ public function onProgressUpdate(Server $server, $progress){ } /** * Call this method from {@link AsyncTask#onCompletion} to fetch the data stored in the constructor, if any, and * clears it from the storage. * * Do not call this method from {@link AsyncTask#onProgressUpdate}, because this method deletes the data and cannot * be used in the next {@link AsyncTask#onProgressUpdate} call or from {@link AsyncTask#onCompletion}. Use * {@link AsyncTask#peekLocal} instead. * * @param Server $server default null * * @return mixed * * @throws \RuntimeException if no data were stored by this AsyncTask instance. */ protected function fetchLocal(Server $server = null){ if($server === null){ $server = Server::getInstance(); assert($server !== null, "Call this method only from the main thread!"); } return $server->getScheduler()->fetchLocalComplex($this); } /** * Call this method from {@link AsyncTask#onProgressUpdate} to fetch the data stored in the constructor. * * Use {@link AsyncTask#peekLocal} instead from {@link AsyncTask#onCompletion}, because this method does not delete * the data, and not clearing the data will result in a warning for memory leak after {@link AsyncTask#onCompletion} * finished executing. * * @param Server|null $server default null * * @return mixed * * @throws \RuntimeException if no data were stored by this AsyncTask instance */ protected function peekLocal(Server $server = null){ if($server === null){ $server = Server::getInstance(); assert($server !== null, "Call this method only from the main thread!"); } return $server->getScheduler()->peekLocalComplex($this); } public function cleanObject(){ foreach($this as $p => $v){ if(!($v instanceof \Threaded) and !in_array($p, ["isFinished", "isGarbage", "cancelRun"])){ $this->{$p} = null; } } } } ================================================ FILE: src/pocketmine/scheduler/AsyncWorker.php ================================================ logger = $logger; $this->id = $id; } public function run(){ $this->registerClassLoader(); gc_enable(); ini_set("memory_limit", -1); global $store; $store = []; } public function handleException(\Throwable $e){ $this->logger->logException($e); } public function getThreadName(){ return "Asynchronous Worker #" . $this->id; } } ================================================ FILE: src/pocketmine/scheduler/CallbackTask.php ================================================ callable = $callable; $this->args = $args; $this->args[] = $this; } /** * @return callable */ public function getCallable(){ return $this->callable; } public function onRun($currentTicks){ call_user_func_array($this->callable, $this->args); } } ================================================ FILE: src/pocketmine/scheduler/DServerTask.php ================================================ data = $data; $this->autotimes = $autotimes; } public function onRun(){ $re = [0, 0]; foreach($this->data as $d){ $data = $this->getInfo($d); $re[0] = $re[0] + $data[0]; $re[1] = $re[1] + $data[1]; } $this->re = (array) $re; } public function getInfo($ds, $time = 1){ $tmp = explode(":", $ds); $ip = $tmp[0]; $port = $tmp[1]; $client = stream_socket_client("udp://" . $ip . ":" . $port, $errno, $errstr); //非阻塞Socket if($client){ stream_set_timeout($client, 1); $Handshake_to = "\xFE\xFD" . chr(9) . pack("N", 233); fwrite($client, $Handshake_to); $Handshake_re_1 = fread($client, 65535); if($Handshake_re_1 != ""){ $Handshake_re = $this->decode($Handshake_re_1); $Status_to = "\xFE\xFD" . chr(0) . pack("N", 233) . pack("N", $Handshake_re["payload"]); fwrite($client, $Status_to); $Status_re_1 = fread($client, 65535); if($Status_re_1 != ""){ $Status_re = $this->decode($Status_re_1); $ServerData = explode("\x00", $Status_re["payload"]); return [$ServerData[3], $ServerData[4]]; } } fclose($client); } if($time < $this->autotimes){ return $this->getInfo($ds, $time + 1); }elseif($time = $this->autotimes) return [0, 0]; return [0, 0]; } public function onCompletion(Server $server){ $re = $this->re; if($re[0] > 0) $server->dserverPlayers = $re[0]; if($re[1] > 0) $server->dserverAllPlayers = $re[1]; //$server->getNetwork()->updateName(); } public function decode($buffer){ $redata = []; $redata["packetType"] = ord($buffer{0}); $redata["sessionID"] = unpack("N", substr($buffer, 1, 4))[1]; $redata["payload"] = rtrim(substr($buffer, 5)); return $redata; } } ================================================ FILE: src/pocketmine/scheduler/FileWriteTask.php ================================================ path = $path; $this->contents = $contents; $this->flags = (int) $flags; } public function onRun(){ try{ file_put_contents($this->path, $this->contents, (int) $this->flags); }catch (\Throwable $e){ } } } ================================================ FILE: src/pocketmine/scheduler/GarbageCollectionTask.php ================================================ owner = $owner; } /** * @return Plugin */ public final function getOwner(){ return $this->owner; } } ================================================ FILE: src/pocketmine/scheduler/SendUsageTask.php ================================================ getProperty("anonymous-statistics.host", "stats.pocketmine.net") . "/"; $data = []; $data["uniqueServerId"] = $server->getServerUniqueId(); $data["uniqueMachineId"] = Utils::getMachineUniqueId(); $data["uniqueRequestId"] = UUID::fromData($server->getServerUniqueId(), microtime(true)); switch($type){ case self::TYPE_OPEN: $data["event"] = "open"; $version = new VersionString(); $data["server"] = [ "port" => $server->getPort(), "software" => $server->getName(), "fullVersion" => $version->get(true), "version" => $version->get(), "build" => $version->getBuild(), "api" => $server->getApiVersion(), "minecraftVersion" => $server->getVersion(), "protocol" => Info::CURRENT_PROTOCOL ]; $data["system"] = [ "operatingSystem" => Utils::getOS(), "cores" => Utils::getCoreCount(), "phpVersion" => PHP_VERSION, "machine" => php_uname("a"), "release" => php_uname("r"), "platform" => php_uname("i") ]; $data["players"] = [ "count" => 0, "limit" => $server->getMaxPlayers() ]; $plugins = []; foreach($server->getPluginManager()->getPlugins() as $p){ $d = $p->getDescription(); $plugins[$d->getName()] = [ "name" => $d->getName(), "version" => $d->getVersion(), "enabled" => $p->isEnabled() ]; } $data["plugins"] = $plugins; break; case self::TYPE_STATUS: $data["event"] = "status"; $data["server"] = [ "ticksPerSecond" => $server->getTicksPerSecondAverage(), "tickUsage" => $server->getTickUsageAverage(), "ticks" => $server->getTick() ]; //This anonymizes the user ids so they cannot be reversed to the original foreach($playerList as $k => $v){ $playerList[$k] = md5($v); } $players = []; foreach($server->getOnlinePlayers() as $p){ if($p->isOnline()){ $players[] = md5($p->getUniqueId()->toBinary()); } } $data["players"] = [ "count" => count($players), "limit" => $server->getMaxPlayers(), "currentList" => $players, "historyList" => array_values($playerList) ]; $info = Utils::getMemoryUsage(true); $data["system"] = [ "mainMemory" => $info[0], "totalMemory" => $info[1], "availableMemory" => $info[2], "threadCount" => Utils::getThreadCount() ]; break; case self::TYPE_CLOSE: $data["event"] = "close"; $data["crashing"] = $server->isRunning(); break; } $this->endpoint = $endpoint . "api/post"; $this->data = json_encode($data/*, JSON_PRETTY_PRINT*/); } public function onRun(){ try{ Utils::postURL($this->endpoint, $this->data, 5, [ "Content-Type: application/json", "Content-Length: ". strlen($this->data) ]); }catch(\Throwable $e){ } } } ================================================ FILE: src/pocketmine/scheduler/ServerScheduler.php ================================================ */ protected $queue; /** * @var TaskHandler[] */ protected $tasks = []; /** @var AsyncPool */ protected $asyncPool; /** @var int */ private $ids = 1; /** @var int */ protected $currentTick = 0; public function __construct(){ $this->queue = new ReversePriorityQueue(); $this->asyncPool = new AsyncPool(Server::getInstance(), self::$WORKERS); } /** * @param Task $task * * @return null|TaskHandler */ public function scheduleTask(Task $task){ return $this->addTask($task, -1, -1); } /** * Submits an asynchronous task to the Worker Pool * * @param AsyncTask $task * * @return void */ public function scheduleAsyncTask(AsyncTask $task){ $id = $this->nextId(); $task->setTaskId($id); $this->asyncPool->submitTask($task); } /** * Submits an asynchronous task to a specific Worker in the Pool * * @param AsyncTask $task * @param int $worker * * @return void */ public function scheduleAsyncTaskToWorker(AsyncTask $task, $worker){ $id = $this->nextId(); $task->setTaskId($id); $this->asyncPool->submitTaskToWorker($task, $worker); } public function getAsyncTaskPoolSize(){ return $this->asyncPool->getSize(); } public function increaseAsyncTaskPoolSize($newSize){ $this->asyncPool->increaseSize($newSize); } /** * @param Task $task * @param int $delay * * @return null|TaskHandler */ public function scheduleDelayedTask(Task $task, $delay){ return $this->addTask($task, (int) $delay, -1); } /** * @param Task $task * @param int $period * * @return null|TaskHandler */ public function scheduleRepeatingTask(Task $task, $period){ return $this->addTask($task, -1, (int) $period); } /** * @param Task $task * @param int $delay * @param int $period * * @return null|TaskHandler */ public function scheduleDelayedRepeatingTask(Task $task, $delay, $period){ return $this->addTask($task, (int) $delay, (int) $period); } /** * @param int $taskId */ public function cancelTask($taskId){ if($taskId !== null and isset($this->tasks[$taskId])){ $this->tasks[$taskId]->cancel(); unset($this->tasks[$taskId]); } } /** * @param Plugin $plugin */ public function cancelTasks(Plugin $plugin){ foreach($this->tasks as $taskId => $task){ $ptask = $task->getTask(); if($ptask instanceof PluginTask and $ptask->getOwner() === $plugin){ $task->cancel(); unset($this->tasks[$taskId]); } } } public function cancelAllTasks(){ foreach($this->tasks as $task){ $task->cancel(); } $this->tasks = []; $this->asyncPool->removeTasks(); while(!$this->queue->isEmpty()){ $this->queue->extract(); } $this->ids = 1; } /** * @param int $taskId * * @return bool */ public function isQueued($taskId){ return isset($this->tasks[$taskId]); } /** * @param Task $task * @param $delay * @param $period * * @return null|TaskHandler * * @throws PluginException */ private function addTask(Task $task, $delay, $period){ if($task instanceof PluginTask){ if(!($task->getOwner() instanceof Plugin)){ throw new PluginException("Invalid owner of PluginTask " . get_class($task)); }elseif(!$task->getOwner()->isEnabled()){ throw new PluginException("Plugin '" . $task->getOwner()->getName() . "' attempted to register a task while disabled"); } }elseif($task instanceof CallbackTask and Server::getInstance()->getProperty("settings.deprecated-verbose", true)){ $callable = $task->getCallable(); if(is_array($callable)){ if(is_object($callable[0])){ $taskName = "Callback#" . get_class($callable[0]) . "::" . $callable[1]; }else{ $taskName = "Callback#" . $callable[0] . "::" . $callable[1]; } }else{ $taskName = "Callback#" . $callable; } //Server::getInstance()->getLogger()->warning("A plugin attempted to register a deprecated CallbackTask ($taskName)"); } if($delay <= 0){ $delay = -1; } if($period <= -1){ $period = -1; }elseif($period < 1){ $period = 1; } return $this->handle(new TaskHandler(get_class($task), $task, $this->nextId(), $delay, $period)); } private function handle(TaskHandler $handler){ if($handler->isDelayed()){ $nextRun = $this->currentTick + $handler->getDelay(); }else{ $nextRun = $this->currentTick; } $handler->setNextRun($nextRun); $this->tasks[$handler->getTaskId()] = $handler; $this->queue->insert($handler, $nextRun); return $handler; } /** * @param int $currentTick */ public function mainThreadHeartbeat($currentTick){ $this->currentTick = $currentTick; while($this->isReady($this->currentTick)){ /** @var TaskHandler $task */ $task = $this->queue->extract(); if($task->isCancelled()){ unset($this->tasks[$task->getTaskId()]); continue; }else{ $task->timings->startTiming(); try{ $task->run($this->currentTick); }catch(\Throwable $e){ Server::getInstance()->getLogger()->critical("Could not execute task " . $task->getTaskName() . ": " . $e->getMessage()); $logger = Server::getInstance()->getLogger(); if($logger instanceof MainLogger){ $logger->logException($e); } } $task->timings->stopTiming(); } if($task->isRepeating()){ $task->setNextRun($this->currentTick + $task->getPeriod()); $this->queue->insert($task, $this->currentTick + $task->getPeriod()); }else{ $task->remove(); unset($this->tasks[$task->getTaskId()]); } } $this->asyncPool->collectTasks(); } private function isReady($currentTicks){ return count($this->tasks) > 0 and $this->queue->current()->getNextRun() <= $currentTicks; } /** * @return int */ private function nextId(){ return $this->ids++; } } ================================================ FILE: src/pocketmine/scheduler/Task.php ================================================ taskHandler; } /** * @return int */ public final function getTaskId(){ if($this->taskHandler !== null){ return $this->taskHandler->getTaskId(); } return -1; } /** * @param TaskHandler $taskHandler */ public final function setHandler($taskHandler){ if($this->taskHandler === null or $taskHandler === null){ $this->taskHandler = $taskHandler; } } /** * Actions to execute when run * * @param $currentTick * * @return void */ public abstract function onRun($currentTick); /** * Actions to execute if the Task is cancelled */ public function onCancel(){ } } ================================================ FILE: src/pocketmine/scheduler/TaskHandler.php ================================================ task = $task; $this->taskId = $taskId; $this->delay = $delay; $this->period = $period; $this->timingName = $timingName === null ? "Unknown" : $timingName; $this->timings = Timings::getPluginTaskTimings($this, $period); $this->task->setHandler($this); } /** * @return bool */ public function isCancelled(){ return $this->cancelled === true; } /** * @return int */ public function getNextRun(){ return $this->nextRun; } /** * @param int $ticks */ public function setNextRun($ticks){ $this->nextRun = $ticks; } /** * @return int */ public function getTaskId(){ return $this->taskId; } /** * @return Task */ public function getTask(){ return $this->task; } /** * @return int */ public function getDelay(){ return $this->delay; } /** * @return bool */ public function isDelayed(){ return $this->delay > 0; } /** * @return bool */ public function isRepeating(){ return $this->period > 0; } /** * @return int */ public function getPeriod(){ return $this->period; } /** * WARNING: Do not use this, it's only for internal use. * Changes to this function won't be recorded on the version. */ public function cancel(){ if(!$this->isCancelled()){ $this->task->onCancel(); } $this->remove(); } public function remove(){ $this->cancelled = true; $this->task->setHandler(null); } /** * @param int $currentTick */ public function run($currentTick){ $this->task->onRun($currentTick); } /** * @return string */ public function getTaskName(){ if($this->timingName !== null){ return $this->timingName; } return get_class($this->task); } } ================================================ FILE: src/pocketmine/tile/Beacon.php ================================================ spawnToAll(); if($this->chunk instanceof Chunk){ $this->chunk->setChanged(); $this->level->clearChunkCache($this->chunk->getX(), $this->chunk->getZ()); } } public function getSpawnCompound(){ return new CompoundTag("", [ new StringTag("id", Tile::BEACON), new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z) ]); } } ================================================ FILE: src/pocketmine/tile/BrewingStand.php ================================================ 0, Item::GLOWSTONE_DUST => 0, Item::REDSTONE => 0, Item::FERMENTED_SPIDER_EYE => 0, Item::MAGMA_CREAM => 0, Item::SUGAR => 0, Item::GLISTERING_MELON => 0, Item::SPIDER_EYE => 0, Item::GHAST_TEAR => 0, Item::BLAZE_POWDER => 0, Item::GOLDEN_CARROT => 0, //Item::RAW_FISH => Fish::FISH_PUFFERFISH, Item::PUFFER_FISH, Item::RABBIT_FOOT => 0, Item::GUNPOWDER => 0, ]; public function __construct(Chunk $chunk, CompoundTag $nbt){ if(!isset($nbt->CookedTime) or !($nbt->CookedTime instanceof ShortTag)){ $nbt->CookedTime = new ShortTag("CookedTime", 0); } parent::__construct($chunk, $nbt); $this->inventory = new BrewingInventory($this); if(!isset($this->namedtag->Items) or !($this->namedtag->Items instanceof ListTag)){ $this->namedtag->Items = new ListTag("Items", []); $this->namedtag->Items->setTagType(NBT::TAG_Compound); } for($i = 0; $i < $this->getSize(); ++$i){ $this->inventory->setItem($i, $this->getItem($i)); } /*if($this->namedtag["CookTime"] < self::MAX_BREW_TIME){ $this->scheduleUpdate(); }*/ } public function getName() : string{ return $this->hasName() ? $this->namedtag->CustomName->getValue() : "Brewing Stand"; } public function hasName(){ return isset($this->namedtag->CustomName); } public function setName($str){ if($str === ""){ unset($this->namedtag->CustomName); return; } $this->namedtag->CustomName = new StringTag("CustomName", $str); } public function close(){ if(!$this->closed){ foreach($this->getInventory()->getViewers() as $player){ $player->removeWindow($this->getInventory()); } parent::close(); } } public function saveNBT(){ $this->namedtag->Items = new ListTag("Items", []); $this->namedtag->Items->setTagType(NBT::TAG_Compound); for($index = 0; $index < $this->getSize(); ++$index){ $this->setItem($index, $this->inventory->getItem($index)); } } /** * @return int */ public function getSize(){ return 4; } /** * @param $index * * @return int */ protected function getSlotIndex($index){ foreach($this->namedtag->Items as $i => $slot){ if($slot["Slot"] === $index){ return $i; } } return -1; } /** * This method should not be used by plugins, use the Inventory * * @param int $index * * @return Item */ public function getItem($index){ $i = $this->getSlotIndex($index); if($i < 0){ return Item::get(Item::AIR, 0, 0); }else{ return Item::nbtDeserialize($this->namedtag->Items[$i]); } } /** * This method should not be used by plugins, use the Inventory * * @param int $index * @param Item $item * * @return bool */ public function setItem($index, Item $item){ $i = $this->getSlotIndex($index); if($item->getId() === Item::AIR or $item->getCount() <= 0){ if($i >= 0){ unset($this->namedtag->Items[$i]); } }elseif($i < 0){ for($i = 0; $i <= $this->getSize(); ++$i){ if(!isset($this->namedtag->Items[$i])){ break; } } $this->namedtag->Items[$i] = $item->nbtSerialize($index); }else{ $this->namedtag->Items[$i] = $item->nbtSerialize($index); } return true; } /** * @return BrewingInventory */ public function getInventory(){ return $this->inventory; } public function checkIngredient(Item $item){ if(isset(self::$ingredients[$item->getId()])){ if(self::$ingredients[$item->getId()] === $item->getDamage()){ return true; } } return false; } public function updateSurface(){ $this->saveNBT(); $this->onChanged(); } public function onUpdate(){ if($this->closed === true){ return false; } $this->timings->startTiming(); $ret = false; $ingredient = $this->inventory->getIngredient(); $canBrew = false; for($i = 1; $i <= 3; $i++){ if($this->inventory->getItem($i)->getId() === Item::POTION or $this->inventory->getItem($i)->getId() === Item::SPLASH_POTION ){ $canBrew = true; } } if($ingredient->getId() !== Item::AIR and $ingredient->getCount() > 0){ if($canBrew){ if(!$this->checkIngredient($ingredient)){ $canBrew = false; } } if($canBrew){ for($i = 1; $i <= 3; $i++){ $potion = $this->inventory->getItem($i); $recipe = Server::getInstance()->getCraftingManager()->matchBrewingRecipe($ingredient, $potion); if($recipe !== null){ $canBrew = true; break; } $canBrew = false; } } }else{ $canBrew = false; } if($canBrew){ $this->namedtag->CookTime = new ShortTag("CookTime", $this->namedtag["CookTime"] - 1); foreach($this->getInventory()->getViewers() as $player){ $windowId = $player->getWindowId($this->getInventory()); if($windowId > 0){ $pk = new ContainerSetDataPacket(); $pk->windowid = $windowId; $pk->property = 0; //Brew $pk->value = $this->namedtag["CookTime"]; $player->dataPacket($pk); } } if($this->namedtag["CookTime"] <= 0){ $this->namedtag->CookTime = new ShortTag("CookTime", self::MAX_BREW_TIME); for($i = 1; $i <= 3; $i++){ $potion = $this->inventory->getItem($i); $recipe = Server::getInstance()->getCraftingManager()->matchBrewingRecipe($ingredient, $potion); if($recipe != null and $potion->getId() !== Item::AIR){ $this->inventory->setItem($i, $recipe->getResult()); } } $ingredient->count--; if($ingredient->getCount() <= 0) $ingredient = Item::get(Item::AIR); $this->inventory->setIngredient($ingredient); } $ret = true; }else{ $this->namedtag->CookTime = new ShortTag("CookTime", self::MAX_BREW_TIME); foreach($this->getInventory()->getViewers() as $player){ $windowId = $player->getWindowId($this->getInventory()); if($windowId > 0){ $pk = new ContainerSetDataPacket(); $pk->windowid = $windowId; $pk->property = 0; //Brew $pk->value = 0; $player->dataPacket($pk); } } } $this->lastUpdate = microtime(true); $this->timings->stopTiming(); return $ret; } public function getSpawnCompound(){ $nbt = new CompoundTag("", [ new StringTag("id", Tile::BREWING_STAND), new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z), new ShortTag("CookTime", self::MAX_BREW_TIME), $this->namedtag->Items, ]); if($this->hasName()){ $nbt->CustomName = $this->namedtag->CustomName; } return $nbt; } } ================================================ FILE: src/pocketmine/tile/Cauldron.php ================================================ PotionId) or !($nbt->PotionId instanceof ShortTag)){ $nbt->PotionId = new ShortTag("PotionId", 0xffff); } if(!isset($nbt->SplashPotion) or !($nbt->SplashPotion instanceof ByteTag)){ $nbt->SplashPotion = new ByteTag("SplashPotion", 0); } if(!isset($nbt->Items) or !($nbt->Items instanceof ListTag)){ $nbt->Items = new ListTag("Items", []); } parent::__construct($chunk, $nbt); } public function getPotionId(){ return $this->namedtag["PotionId"]; } public function setPotionId($potionId){ $this->namedtag->PotionId = new ShortTag("PotionId", $potionId); $this->onChanged(); } public function hasPotion(){ return $this->namedtag["PotionId"] !== 0xffff; } public function getSplashPotion(){ return ($this->namedtag["SplashPotion"] == true); } public function setSplashPotion($bool){ $this->namedtag->SplashPotion = new ByteTag("SplashPotion", ($bool == true) ? 1 : 0); $this->onChanged(); } public function getCustomColor(){// if($this->isCustomColor()){ $color = $this->namedtag["CustomColor"]; $green = ($color >> 8) & 0xff; $red = ($color >> 16) & 0xff; $blue = ($color) & 0xff; return Color::getRGB($red, $green, $blue); } return null; } public function getCustomColorRed(){ return ($this->namedtag["CustomColor"] >> 16) & 0xff; } public function getCustomColorGreen(){ return ($this->namedtag["CustomColor"] >> 8) & 0xff; } public function getCustomColorBlue(){ return ($this->namedtag["CustomColor"]) & 0xff; } public function isCustomColor(){ return isset($this->namedtag->CustomColor); } public function setCustomColor($r, $g = 0xff, $b = 0xff){ if($r instanceof Color){ $color = ($r->getRed() << 16 | $r->getGreen() << 8 | $r->getBlue()) & 0xffffff; }else{ $color = ($r << 16 | $g << 8 | $b) & 0xffffff; } $this->namedtag->CustomColor = new IntTag("CustomColor", $color); $this->onChanged(); } public function clearCustomColor(){ if(isset($this->namedtag->CustomColor)){ unset($this->namedtag->CustomColor); } $this->onChanged(); } public function getSpawnCompound(){ $nbt = new CompoundTag("", [ new StringTag("id", Tile::CAULDRON), new IntTag("x", (Int) $this->x), new IntTag("y", (Int) $this->y), new IntTag("z", (Int) $this->z), new ShortTag("PotionId", $this->namedtag["PotionId"]), new ByteTag("SplashPotion", $this->namedtag["SplashPotion"]), new ListTag("Items", $this->namedtag["Items"])//unused? ]); if($this->getPotionId() === 0xffff and $this->isCustomColor()){ $nbt->CustomColor = $this->namedtag->CustomColor; } return $nbt; } } ================================================ FILE: src/pocketmine/tile/Chest.php ================================================ inventory = new ChestInventory($this); if(!isset($this->namedtag->Items) or !($this->namedtag->Items instanceof ListTag)){ $this->namedtag->Items = new ListTag("Items", []); $this->namedtag->Items->setTagType(NBT::TAG_Compound); } for($i = 0; $i < $this->getSize(); ++$i){ $this->inventory->setItem($i, $this->getItem($i)); } } public function close(){ if($this->closed === false){ foreach($this->getInventory()->getViewers() as $player){ $player->removeWindow($this->getInventory()); } foreach($this->getInventory()->getViewers() as $player){ $player->removeWindow($this->getRealInventory()); } parent::close(); } } public function saveNBT(){ $this->namedtag->Items = new ListTag("Items", []); $this->namedtag->Items->setTagType(NBT::TAG_Compound); for($index = 0; $index < $this->getSize(); ++$index){ $this->setItem($index, $this->inventory->getItem($index)); } } /** * @return int */ public function getSize(){ return 27; } /** * @param $index * * @return int */ protected function getSlotIndex($index){ foreach($this->namedtag->Items as $i => $slot){ if((int) $slot["Slot"] === (int) $index){ return (int) $i; } } return -1; } /** * This method should not be used by plugins, use the Inventory * * @param int $index * * @return Item */ public function getItem($index){ $i = $this->getSlotIndex($index); if($i < 0){ return Item::get(Item::AIR, 0, 0); }else{ return Item::nbtDeserialize($this->namedtag->Items[$i]); } } /** * This method should not be used by plugins, use the Inventory * * @param int $index * @param Item $item * * @return bool */ public function setItem($index, Item $item){ $i = $this->getSlotIndex($index); if($item->getId() === Item::AIR or $item->getCount() <= 0){ if($i >= 0){ unset($this->namedtag->Items[$i]); } }elseif($i < 0){ for($i = 0; $i <= $this->getSize(); ++$i){ if(!isset($this->namedtag->Items[$i])){ break; } } $this->namedtag->Items[$i] = $item->nbtSerialize($index); }else{ $this->namedtag->Items[$i] = $item->nbtSerialize($index); } return true; } /** * @return ChestInventory|DoubleChestInventory */ public function getInventory(){ if($this->isPaired() and $this->doubleInventory === null){ $this->checkPairing(); } return $this->doubleInventory instanceof DoubleChestInventory ? $this->doubleInventory : $this->inventory; } /** * @return ChestInventory */ public function getRealInventory(){ return $this->inventory; } /** * @return DoubleChestInventory|null */ public function getDoubleInventory(){ return $this->doubleInventory; } protected function checkPairing(){ if($this->isPaired() and !$this->getLevel()->isChunkLoaded($this->namedtag->pairx->getValue() >> 4, $this->namedtag->pairz->getValue() >> 4)){ //paired to a tile in an unloaded chunk $this->doubleInventory = null; }elseif(($pair = $this->getPair()) instanceof Chest){ if(!$pair->isPaired()){ $pair->createPair($this); $pair->checkPairing(); } if($this->doubleInventory === null){ if(($p = $pair->getDoubleInventory()) instanceof DoubleChestInventory){ $this->doubleInventory = $p; }else{ if(($pair->x + ($pair->z << 15)) > ($this->x + ($this->z << 15))){ //Order them correctly $this->doubleInventory = new DoubleChestInventory($pair, $this); }else{ $this->doubleInventory = new DoubleChestInventory($this, $pair); } } } }else{ $this->doubleInventory = null; unset($this->namedtag->pairx, $this->namedtag->pairz); } } public function getName() : string{ return isset($this->namedtag->CustomName) ? $this->namedtag->CustomName->getValue() : "Chest"; } public function hasName(){ return isset($this->namedtag->CustomName); } public function setName($str){ if($str === ""){ unset($this->namedtag->CustomName); return; } $this->namedtag->CustomName = new StringTag("CustomName", $str); } public function isPaired(){ if(!isset($this->namedtag->pairx) or !isset($this->namedtag->pairz)){ return false; } return true; } /** * @return Chest */ public function getPair(){ if($this->isPaired()){ $tile = $this->getLevel()->getTile(new Vector3((int) $this->namedtag["pairx"], $this->y, (int) $this->namedtag["pairz"])); if($tile instanceof Chest){ return $tile; } } return null; } public function pairWith(Chest $tile){ if($this->isPaired() or $tile->isPaired()){ return false; } $this->createPair($tile); $this->spawnToAll(); $tile->spawnToAll(); $this->checkPairing(); return true; } private function createPair(Chest $tile){ $this->namedtag->pairx = new IntTag("pairx", $tile->x); $this->namedtag->pairz = new IntTag("pairz", $tile->z); $tile->namedtag->pairx = new IntTag("pairx", $this->x); $tile->namedtag->pairz = new IntTag("pairz", $this->z); } public function unpair(){ if(!$this->isPaired()){ return false; } $tile = $this->getPair(); unset($this->namedtag->pairx, $this->namedtag->pairz); $this->spawnToAll(); if($tile instanceof Chest){ unset($tile->namedtag->pairx, $tile->namedtag->pairz); $tile->checkPairing(); $tile->spawnToAll(); } $this->checkPairing(); return true; } public function getSpawnCompound(){ if($this->isPaired()){ $c = new CompoundTag("", [ new StringTag("id", Tile::CHEST), new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z), new IntTag("pairx", (int) $this->namedtag["pairx"]), new IntTag("pairz", (int) $this->namedtag["pairz"]) ]); }else{ $c = new CompoundTag("", [ new StringTag("id", Tile::CHEST), new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z) ]); } if($this->hasName()){ $c->CustomName = $this->namedtag->CustomName; } return $c; } } ================================================ FILE: src/pocketmine/tile/Container.php ================================================ scheduleUpdate(); } public function getLightByTime(){ /* $strength = 1; $time = $this->getLevel()->getTime(); if(WeatherManager::isRegistered($this->getLevel())) $weather = $this->getLevel()->getWeather()->getWeather(); else $weather = Weather::SUNNY; switch($weather){ case Weather::SUNNY: if($time <= 22340 and $time >= 13680) $strength = 1; if($time <= 22800 and $time >= 13220) $strength = 2; if($time <= 23080 and $time >= 12940) $strength = 3; if($time <= 23300 and $time >= 12720) $strength = 4; if($time <= 23540 and $time >= 12480) $strength = 5; if($time <= 23780 and $time >= 12240) $strength = 6; if($time <= 23960 and $time >= 12040) $strength = 7; if($time >= 180 and $time <= 11840) $strength = 8; if($time >= 540 and $time <= 11480) $strength = 9; if($time >= 940 and $time <= 11080) $strength = 10; if($time >= 1380 and $time <= 10640) $strength = 11; if($time >= 1880 and $time <= 10140) $strength = 12; if($time >= 2460 and $time <= 9560) $strength = 13; if($time >= 3180 and $time <= 8840) $strength = 14; if($time >= 4300 and $time <= 7720) $strength = 15; break; case Weather::RAINY_THUNDER: case Weather::RAINY: if($time <= 22340 and $time >= 13680) $strength = 1; if($time <= 22800 and $time >= 13220) $strength = 2; if($time <= 23240 and $time >= 12780) $strength = 3; if($time <= 23520 and $time >= 12500) $strength = 4; if($time <= 23760 and $time >= 12260) $strength = 5; if($time >= 0 and $time <= 12020) $strength = 6; if($time >= 400 and $time <= 11620) $strength = 7; if($time >= 900 and $time <= 11120) $strength = 8; if($time >= 1440 and $time <= 10580) $strength = 9; if($time >= 2080 and $time <= 9940) $strength = 10; if($time >= 2880 and $time <= 9140) $strength = 11; if($time >= 4120 and $time <= 7990) $strength = 12; break; } return $strength;*/ $time = $this->getLevel()->getTime(); if(($time >= Level::TIME_DAY and $time <= Level::TIME_SUNSET) or ($time >= Level::TIME_SUNRISE and $time <= Level::TIME_FULL)) return 15; return 0; } public function isActivated() : bool{ if($this->getType() == Block::DAYLIGHT_SENSOR) { if($this->getLightByTime() == 15) return true; return false; }else{ if($this->getLightByTime() == 0) return true; return false; } } private function getType() : int{ return $this->getBlock()->getId(); } public function onUpdate(){ if(($this->getLevel()->getServer()->getTick() % 3) == 0){ //Update per 3 ticks if($this->getType() != $this->lastType){ //Update when changed /** @var DaylightDetector $block */ $block = $this->getBlock(); if($this->isActivated()){ $block->activate(); }else{ $block->deactivate(); } $this->lastType = $block->getId(); } } return true; } public function getSpawnCompound(){ return new CompoundTag("", [ new StringTag("id", Tile::DAY_LIGHT_DETECTOR), new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z), ]); } } ================================================ FILE: src/pocketmine/tile/Dispenser.php ================================================ inventory = new DispenserInventory($this); if(!isset($this->namedtag->Items) or !($this->namedtag->Items instanceof ListTag)){ $this->namedtag->Items = new ListTag("Items", []); $this->namedtag->Items->setTagType(NBT::TAG_Compound); } for($i = 0; $i < $this->getSize(); ++$i){ $this->inventory->setItem($i, $this->getItem($i)); } $this->scheduleUpdate(); } public function close(){ if($this->closed === false){ foreach($this->getInventory()->getViewers() as $player){ $player->removeWindow($this->getInventory()); } parent::close(); } } public function saveNBT(){ $this->namedtag->Items = new ListTag("Items", []); $this->namedtag->Items->setTagType(NBT::TAG_Compound); for($index = 0; $index < $this->getSize(); ++$index){ $this->setItem($index, $this->inventory->getItem($index)); } } /** * @return int */ public function getSize(){ return 9; } /** * @param $index * * @return int */ protected function getSlotIndex($index){ foreach($this->namedtag->Items as $i => $slot){ if((int) $slot["Slot"] === (int) $index){ return (int) $i; } } return -1; } /** * This method should not be used by plugins, use the Inventory * * @param int $index * * @return Item */ public function getItem($index){ $i = $this->getSlotIndex($index); if($i < 0){ return Item::get(Item::AIR, 0, 0); }else{ return Item::nbtDeserialize($this->namedtag->Items[$i]); } } /** * This method should not be used by plugins, use the Inventory * * @param int $index * @param Item $item * * @return bool */ public function setItem($index, Item $item){ $i = $this->getSlotIndex($index); if($item->getId() === Item::AIR or $item->getCount() <= 0){ if($i >= 0){ unset($this->namedtag->Items[$i]); } }elseif($i < 0){ for($i = 0; $i <= $this->getSize(); ++$i){ if(!isset($this->namedtag->Items[$i])){ break; } } $this->namedtag->Items[$i] = $item->nbtSerialize($index); }else{ $this->namedtag->Items[$i] = $item->nbtSerialize($index); } return true; } /** * @return DispenserInventory */ public function getInventory(){ return $this->inventory; } public function getName() : string{ return isset($this->namedtag->CustomName) ? $this->namedtag->CustomName->getValue() : "Dispenser"; } public function hasName(){ return isset($this->namedtag->CustomName); } public function setName($str){ if($str === ""){ unset($this->namedtag->CustomName); return; } $this->namedtag->CustomName = new StringTag("CustomName", $str); } public function getMotion(){ $meta = $this->getBlock()->getDamage(); switch($meta){ case Vector3::SIDE_DOWN: return [0, -1, 0]; case Vector3::SIDE_UP: return [0, 1, 0]; case Vector3::SIDE_NORTH: return [0, 0, -1]; case Vector3::SIDE_SOUTH: return [0, 0, 1]; case Vector3::SIDE_WEST: return [-1, 0, 0]; case Vector3::SIDE_EAST: return [1, 0, 0]; default: return [0, 0, 0]; } } public function activate(){ $itemIndex = []; for($i = 0; $i < $this->getSize(); $i++){ $item = $this->getInventory()->getItem($i); if($item->getId() != Item::AIR){ $itemIndex[] = [$i, $item]; } } $max = count($itemIndex) - 1; if($max < 0) $itemArr = null; elseif($max == 0) $itemArr = $itemIndex[0]; else $itemArr = $itemIndex[mt_rand(0, $max)]; if(is_array($itemArr)){ /** @var Item $item */ $item = $itemArr[1]; $item->setCount($item->getCount() - 1); $this->getInventory()->setItem($itemArr[0], $item->getCount() > 0 ? $item : Item::get(Item::AIR)); $motion = $this->getMotion(); $needItem = Item::get($item->getId(), $item->getDamage()); $f = 1.5; $nbt = new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $this->x + $motion[0] * 2 + 0.5), new DoubleTag("", $this->y + ($motion[1] > 0 ? $motion[1] : 0.5)), new DoubleTag("", $this->z + $motion[2] * 2 + 0.5) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", $motion[0]), new DoubleTag("", $motion[1]), new DoubleTag("", $motion[2]) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", lcg_value() * 360), new FloatTag("", 0) ]), ]); switch($needItem->getId()){ case Item::ARROW: $nbt->Fire = new ShortTag("Fire", 0); $entity = Entity::createEntity("Arrow", $this->chunk, $nbt); break; case Item::SNOWBALL: $entity = Entity::createEntity("Snowball", $this->chunk, $nbt); break; case Item::EGG: $entity = Entity::createEntity("Egg", $this->chunk, $nbt); break; case Item::SPLASH_POTION: $nbt->PotionId = new ShortTag("PotionId", $item->getDamage()); $entity = Entity::createEntity("ThrownPotion", $this->chunk, $nbt); break; case Item::ENCHANTING_BOTTLE: $entity = Entity::createEntity("ThrownExpBottle", $this->chunk, $nbt); break; default: $nbt->Health = new ShortTag("Health", 5); $nbt->Item = $needItem->nbtSerialize(-1, "Item"); $nbt->PickupDelay = new ShortTag("PickupDelay", 10); $f = 0.3; $entity = new ItemEntity($this->chunk, $nbt, $this); break; } $entity->setMotion($entity->getMotion()->multiply($f)); $entity->spawnToAll(); for($i = 1; $i < 10; $i++){ $this->getLevel()->addParticle(new SmokeParticle($this->add($motion[0] * $i * 0.3 + 0.5, $motion[1] == 0 ? 0.5 : $motion[1] * $i * 0.3, $motion[2] * $i * 0.3 + 0.5))); } } } public function getSpawnCompound(){ $c = new CompoundTag("", [ new StringTag("id", Tile::DISPENSER), new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z) ]); if($this->hasName()){ $c->CustomName = $this->namedtag->CustomName; } return $c; } } ================================================ FILE: src/pocketmine/tile/Dropper.php ================================================ inventory = new DropperInventory($this); if(!isset($this->namedtag->Items) or !($this->namedtag->Items instanceof ListTag)){ $this->namedtag->Items = new ListTag("Items", []); $this->namedtag->Items->setTagType(NBT::TAG_Compound); } for($i = 0; $i < $this->getSize(); ++$i){ $this->inventory->setItem($i, $this->getItem($i)); } $this->scheduleUpdate(); } public function close(){ if($this->closed === false){ foreach($this->getInventory()->getViewers() as $player){ $player->removeWindow($this->getInventory()); } foreach($this->getInventory()->getViewers() as $player){ $player->removeWindow($this->getInventory()); } parent::close(); } } public function saveNBT(){ $this->namedtag->Items = new ListTag("Items", []); $this->namedtag->Items->setTagType(NBT::TAG_Compound); for($index = 0; $index < $this->getSize(); ++$index){ $this->setItem($index, $this->inventory->getItem($index)); } } /** * @return int */ public function getSize(){ return 9; } /** * @param $index * * @return int */ protected function getSlotIndex($index){ foreach($this->namedtag->Items as $i => $slot){ if((int) $slot["Slot"] === (int) $index){ return (int) $i; } } return -1; } /** * This method should not be used by plugins, use the Inventory * * @param int $index * * @return Item */ public function getItem($index){ $i = $this->getSlotIndex($index); if($i < 0){ return Item::get(Item::AIR, 0, 0); }else{ return Item::nbtDeserialize($this->namedtag->Items[$i]); } } /** * This method should not be used by plugins, use the Inventory * * @param int $index * @param Item $item * * @return bool */ public function setItem($index, Item $item){ $i = $this->getSlotIndex($index); if($item->getId() === Item::AIR or $item->getCount() <= 0){ if($i >= 0){ unset($this->namedtag->Items[$i]); } }elseif($i < 0){ for($i = 0; $i <= $this->getSize(); ++$i){ if(!isset($this->namedtag->Items[$i])){ break; } } $this->namedtag->Items[$i] = $item->nbtSerialize($index); }else{ $this->namedtag->Items[$i] = $item->nbtSerialize($index); } return true; } /** * @return DropperInventory */ public function getInventory(){ return $this->inventory; } public function getName() : string{ return isset($this->namedtag->CustomName) ? $this->namedtag->CustomName->getValue() : "Dropper"; } public function hasName(){ return isset($this->namedtag->CustomName); } public function setName($str){ if($str === ""){ unset($this->namedtag->CustomName); return; } $this->namedtag->CustomName = new StringTag("CustomName", $str); } public function getMotion(){ $meta = $this->getBlock()->getDamage(); switch($meta){ case Vector3::SIDE_DOWN: return [0, -1, 0]; case Vector3::SIDE_UP: return [0, 1, 0]; case Vector3::SIDE_NORTH: return [0, 0, -1]; case Vector3::SIDE_SOUTH: return [0, 0, 1]; case Vector3::SIDE_WEST: return [-1, 0, 0]; case Vector3::SIDE_EAST: return [1, 0, 0]; default: return [0, 0, 0]; } } public function activate(){ $itemIndex = []; for($i = 0; $i < $this->getSize(); $i++){ $item = $this->getInventory()->getItem($i); if($item->getId() != Item::AIR){ $itemIndex[] = [$i, $item]; } } $max = count($itemIndex) - 1; if($max < 0) $itemArr = null; elseif($max == 0) $itemArr = $itemIndex[0]; else $itemArr = $itemIndex[mt_rand(0, $max)]; if(is_array($itemArr)){ /** @var Item $item */ $item = $itemArr[1]; $item->setCount($item->getCount() - 1); $this->getInventory()->setItem($itemArr[0], $item->getCount() > 0 ? $item : Item::get(Item::AIR)); $motion = $this->getMotion(); $needItem = Item::get($item->getId(), $item->getDamage()); $block = $this->getLevel()->getBlock($this->add($motion[0], $motion[1], $motion[2])); switch($block->getId()){ case Block::CHEST: case Block::TRAPPED_CHEST: case Block::DROPPER: case Block::DISPENSER: case Block::BREWING_STAND_BLOCK: case Block::FURNACE: $t = $this->getLevel()->getTile($block); /** @var Chest|Dispenser|Dropper|BrewingStand|Furnace $t */ if($t instanceof Tile){ if($t->getInventory()->canAddItem($needItem)){ $t->getInventory()->addItem($needItem); return; } } } $nbt = new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $this->x + $motion[0] * 2 + 0.5), new DoubleTag("", $this->y + ($motion[1] > 0 ? $motion[1] : 0.5)), new DoubleTag("", $this->z + $motion[2] * 2 + 0.5) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", $motion[0]), new DoubleTag("", $motion[1]), new DoubleTag("", $motion[2]) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", lcg_value() * 360), new FloatTag("", 0) ]), "Health" => new ShortTag("Health", 5), "Item" => $needItem->nbtSerialize(-1, "Item"), "PickupDelay" => new ShortTag("PickupDelay", 10) ]); $f = 0.3; $itemEntity = new ItemEntity($this->chunk, $nbt, $this); $itemEntity->setMotion($itemEntity->getMotion()->multiply($f)); $itemEntity->spawnToAll(); for($i = 1; $i < 10; $i++){ $this->getLevel()->addParticle(new SmokeParticle($this->add($motion[0] * $i * 0.3 + 0.5, $motion[1] == 0 ? 0.5 : $motion[1] * $i * 0.3, $motion[2] * $i * 0.3 + 0.5))); } } } public function getSpawnCompound(){ $c = new CompoundTag("", [ new StringTag("id", Tile::DROPPER), new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z) ]); if($this->hasName()){ $c->CustomName = $this->namedtag->CustomName; } return $c; } } ================================================ FILE: src/pocketmine/tile/EnchantTable.php ================================================ hasName() ? $this->namedtag->CustomName->getValue() : "Enchanting Table"; } public function hasName(){ return isset($this->namedtag->CustomName); } public function setName($str){ if($str === ""){ unset($this->namedtag->CustomName); return; } $this->namedtag->CustomName = new StringTag("CustomName", $str); } public function getSpawnCompound(){ $nbt = new CompoundTag("", [ new StringTag("id", Tile::ENCHANT_TABLE), new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z) ]); if($this->hasName()){ $nbt->CustomName = $this->namedtag->CustomName; } return $nbt; } } ================================================ FILE: src/pocketmine/tile/EnderChest.php ================================================ namedtag->CustomName) ? $this->namedtag->CustomName->getValue() : "Ender Chest"; } public function hasName(){ return isset($this->namedtag->CustomName); } public function setName($str){ if($str === ""){ unset($this->namedtag->CustomName); return; } $this->namedtag->CustomName = new StringTag("CustomName", $str); } public function getSpawnCompound(){ $enderchest = new CompoundTag("", [ new StringTag("id", Tile::ENDER_CHEST), new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z) ]); if($this->hasName()){ $enderchest->CustomName = $this->namedtag->CustomName; } return $enderchest; } } ================================================ FILE: src/pocketmine/tile/FlowerPot.php ================================================ item) or !($nbt->item instanceof ShortTag)){ $nbt->item = new ShortTag("item", 0); } if(!isset($nbt->mData) or !($nbt->mData instanceof IntTag)){ $nbt->mData = new IntTag("mData", 0); } parent::__construct($chunk, $nbt); } public function getItem() : Item{ return Item::get((int) ($this->namedtag["item"] ?? 0), (int) ($this->namedtag["mData"] ?? 0)); } public function setItem(Item $item){ $this->namedtag["item"] = $item->getId(); $this->namedtag["mData"] = $item->getDamage(); $this->onChanged(); } public function getSpawnCompound(){ return new CompoundTag("", [ new StringTag("id", Tile::FLOWER_POT), new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z), $this->namedtag->item, $this->namedtag->mData ]); } } ================================================ FILE: src/pocketmine/tile/Furnace.php ================================================ BurnTime) or !($nbt->BurnTime instanceof ShortTag) or $nbt["BurnTime"] < 0){ $nbt->BurnTime = new ShortTag("BurnTime", 0); } if(!isset($nbt->CookTime) or !($nbt->CookTime instanceof ShortTag) or $nbt["CookTime"] < 0 or ($nbt["BurnTime"] === 0 and $nbt["CookTime"] > 0)){ $nbt->CookTime = new ShortTag("CookTime", 0); } if(!isset($nbt->MaxTime) or !($nbt->MaxTime instanceof ShortTag)){ $nbt->MaxTime = new ShortTag("BurnTime", $nbt["BurnTime"]); $nbt->BurnTicks = new ShortTag("BurnTicks", 0); } parent::__construct($chunk, $nbt); $this->inventory = new FurnaceInventory($this); if(!isset($this->namedtag->Items) or !($this->namedtag->Items instanceof ListTag)){ $this->namedtag->Items = new ListTag("Items", []); $this->namedtag->Items->setTagType(NBT::TAG_Compound); } for($i = 0; $i < $this->getSize(); ++$i){ $this->inventory->setItem($i, $this->getItem($i)); } if($this->namedtag["BurnTime"] > 0){ $this->scheduleUpdate(); } } public function getName() : string{ return isset($this->namedtag->CustomName) ? $this->namedtag->CustomName->getValue() : "Furnace"; } public function hasName(){ return isset($this->namedtag->CustomName); } public function setName($str){ if($str === ""){ unset($this->namedtag->CustomName); return; } $this->namedtag->CustomName = new StringTag("CustomName", $str); } public function close(){ if($this->closed === false){ foreach($this->getInventory()->getViewers() as $player){ $player->removeWindow($this->getInventory()); } parent::close(); } } public function saveNBT(){ $this->namedtag->Items = new ListTag("Items", []); $this->namedtag->Items->setTagType(NBT::TAG_Compound); for($index = 0; $index < $this->getSize(); ++$index){ $this->setItem($index, $this->inventory->getItem($index)); } } /** * @return int */ public function getSize(){ return 3; } /** * @param $index * * @return int */ protected function getSlotIndex($index){ foreach($this->namedtag->Items as $i => $slot){ if($slot["Slot"] === $index){ return $i; } } return -1; } /** * This method should not be used by plugins, use the Inventory * * @param int $index * * @return Item */ public function getItem($index){ $i = $this->getSlotIndex($index); if($i < 0){ return Item::get(Item::AIR, 0, 0); }else{ return Item::nbtDeserialize($this->namedtag->Items[$i]); } } /** * This method should not be used by plugins, use the Inventory * * @param int $index * @param Item $item * * @return bool */ public function setItem($index, Item $item){ $i = $this->getSlotIndex($index); if($item->getId() === Item::AIR or $item->getCount() <= 0){ if($i >= 0){ unset($this->namedtag->Items[$i]); } }elseif($i < 0){ for($i = 0; $i <= $this->getSize(); ++$i){ if(!isset($this->namedtag->Items[$i])){ break; } } $this->namedtag->Items[$i] = $item->nbtSerialize($index); }else{ $this->namedtag->Items[$i] = $item->nbtSerialize($index); } return true; } /** * @return FurnaceInventory */ public function getInventory(){ return $this->inventory; } protected function checkFuel(Item $fuel){ $this->server->getPluginManager()->callEvent($ev = new FurnaceBurnEvent($this, $fuel, $fuel->getFuelTime())); if($ev->isCancelled()){ return; } $this->namedtag->MaxTime = new ShortTag("MaxTime", $ev->getBurnTime()); $this->namedtag->BurnTime = new ShortTag("BurnTime", $ev->getBurnTime()); $this->namedtag->BurnTicks = new ShortTag("BurnTicks", 0); if($this->getBlock()->getId() === Item::FURNACE){ $this->getLevel()->setBlock($this, Block::get(Item::BURNING_FURNACE, $this->getBlock()->getDamage()), true); } if($this->namedtag["BurnTime"] > 0 and $ev->isBurning()){ $fuel->setCount($fuel->getCount() - 1); if($fuel->getCount() === 0){ $fuel = Item::get(Item::AIR, 0, 0); } $this->inventory->setFuel($fuel); } } public function onUpdate(){ if($this->closed === true){ return false; } $this->timings->startTiming(); $ret = false; $fuel = $this->inventory->getFuel(); $raw = $this->inventory->getSmelting(); $product = $this->inventory->getResult(); $smelt = $this->server->getCraftingManager()->matchFurnaceRecipe($raw); $canSmelt = ($smelt instanceof FurnaceRecipe and $raw->getCount() > 0 and (($smelt->getResult()->equals($product) and $product->getCount() < $product->getMaxStackSize()) or $product->getId() === Item::AIR)); if($this->namedtag["BurnTime"] <= 0 and $canSmelt and $fuel->getFuelTime() !== null and $fuel->getCount() > 0){ $this->checkFuel($fuel); } if($this->namedtag["BurnTime"] > 0){ $this->namedtag->BurnTime = new ShortTag("BurnTime", $this->namedtag["BurnTime"] - 1); $this->namedtag->BurnTicks = new ShortTag("BurnTicks", ceil(($this->namedtag["BurnTime"] / $this->namedtag["MaxTime"] * 200))); if($smelt instanceof FurnaceRecipe and $canSmelt){ $this->namedtag->CookTime = new ShortTag("CookTime", $this->namedtag["CookTime"] + 1); if($this->namedtag["CookTime"] >= 200){ //10 seconds $product = Item::get($smelt->getResult()->getId(), $smelt->getResult()->getDamage(), $product->getCount() + 1); $this->server->getPluginManager()->callEvent($ev = new FurnaceSmeltEvent($this, $raw, $product)); if(!$ev->isCancelled()){ $this->inventory->setResult($ev->getResult()); $raw->setCount($raw->getCount() - 1); if($raw->getCount() === 0){ $raw = Item::get(Item::AIR, 0, 0); } $this->inventory->setSmelting($raw); } $this->namedtag->CookTime = new ShortTag("CookTime", $this->namedtag["CookTime"] - 200); } }elseif($this->namedtag["BurnTime"] <= 0){ $this->namedtag->BurnTime = new ShortTag("BurnTime", 0); $this->namedtag->CookTime = new ShortTag("CookTime", 0); $this->namedtag->BurnTicks = new ShortTag("BurnTicks", 0); }else{ $this->namedtag->CookTime = new ShortTag("CookTime", 0); } $ret = true; }else{ if($this->getBlock()->getId() === Item::BURNING_FURNACE){ $this->getLevel()->setBlock($this, Block::get(Item::FURNACE, $this->getBlock()->getDamage()), true); } $this->namedtag->BurnTime = new ShortTag("BurnTime", 0); $this->namedtag->CookTime = new ShortTag("CookTime", 0); $this->namedtag->BurnTicks = new ShortTag("BurnTicks", 0); } foreach($this->getInventory()->getViewers() as $player){ $windowId = $player->getWindowId($this->getInventory()); if($windowId > 0){ $pk = new ContainerSetDataPacket(); $pk->windowid = $windowId; $pk->property = 0; //Smelting $pk->value = floor($this->namedtag["CookTime"]); $player->dataPacket($pk); $pk = new ContainerSetDataPacket(); $pk->windowid = $windowId; $pk->property = 1; //Fire icon $pk->value = $this->namedtag["BurnTicks"]; $player->dataPacket($pk); } } $this->lastUpdate = microtime(true); $this->timings->stopTiming(); return $ret; } public function getSpawnCompound(){ $nbt = new CompoundTag("", [ new StringTag("id", Tile::FURNACE), new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z), new ShortTag("BurnTime", $this->namedtag["BurnTime"]), new ShortTag("CookTime", $this->namedtag["CookTime"]), //new ShortTag("BurnDuration", $this->namedtag["BurnTicks"]) ]); if($this->hasName()){ $nbt->CustomName = $this->namedtag->CustomName; } return $nbt; } } ================================================ FILE: src/pocketmine/tile/Hopper.php ================================================ TransferCooldown) or !($nbt->TransferCooldown instanceof IntTag)){ $nbt->TransferCooldown = new IntTag("TransferCooldown", 0); } parent::__construct($chunk, $nbt); $this->inventory = new HopperInventory($this); if(!isset($this->namedtag->Items) or !($this->namedtag->Items instanceof ListTag)){ $this->namedtag->Items = new ListTag("Items", []); $this->namedtag->Items->setTagType(NBT::TAG_Compound); } for($i = 0; $i < $this->getSize(); ++$i){ $this->inventory->setItem($i, $this->getItem($i)); } $this->scheduleUpdate(); } public function close(){ if($this->closed === false){ foreach($this->getInventory()->getViewers() as $player){ $player->removeWindow($this->getInventory()); } parent::close(); } } public function activate(){ $this->isPowered = true; } public function deactivate(){ $this->isPowered = false; } public function canUpdate(){ return $this->namedtag->TransferCooldown->getValue() === 0 and !$this->isPowered; } public function resetCooldownTicks(){ $this->namedtag->TransferCooldown->setValue(8); } public function onUpdate(){ if(!($this->getBlock() instanceof HopperBlock)){ return false; } //Pickup dropped items //This can happen at any time regardless of cooldown $area = clone $this->getBlock()->getBoundingBox(); //Area above hopper to draw items from $area->maxY = ceil($area->maxY) + 1; //Account for full block above, not just 1 + 5/8 foreach($this->getLevel()->getChunkEntities($this->getBlock()->x >> 4, $this->getBlock()->z >> 4) as $entity){ if(!($entity instanceof DroppedItem) or !$entity->isAlive()){ continue; } if(!$entity->boundingBox->intersectsWith($area)){ continue; } $item = $entity->getItem(); if(!$item instanceof Item){ continue; } if($item->getCount() < 1){ $entity->kill(); continue; } if($this->inventory->canAddItem($item)){ $this->inventory->addItem($item); $entity->kill(); } } if(!$this->canUpdate()){ //Hoppers only update CONTENTS every 8th tick $this->namedtag->TransferCooldown->setValue($this->namedtag->TransferCooldown->getValue() - 1); return true; } //Suck items from above tile inventories $source = $this->getLevel()->getTile($this->getBlock()->getSide(Vector3::SIDE_UP)); if($source instanceof Tile and $source instanceof InventoryHolder){ $inventory = $source->getInventory(); $item = clone $inventory->getItem($inventory->firstOccupied()); $item->setCount(1); if($this->inventory->canAddItem($item)){ $this->inventory->addItem($item); $inventory->removeItem($item); $this->resetCooldownTicks(); if($source instanceof Hopper){ $source->resetCooldownTicks(); } } } //Feed item into target inventory //Do not do this if there's a hopper underneath this hopper, to follow vanilla behaviour if(!($this->getLevel()->getTile($this->getBlock()->getSide(Vector3::SIDE_DOWN)) instanceof Hopper)){ $target = $this->getLevel()->getTile($this->getBlock()->getSide($this->getBlock()->getDamage())); if($target instanceof Tile and $target instanceof InventoryHolder){ $inv = $target->getInventory(); foreach($this->inventory->getContents() as $item){ if($item->getId() === Item::AIR or $item->getCount() < 1){ continue; } $targetItem = clone $item; $targetItem->setCount(1); if($inv->canAddItem($targetItem)){ $inv->addItem($targetItem); $this->inventory->removeItem($targetItem); $this->resetCooldownTicks(); if($target instanceof Hopper){ $target->resetCooldownTicks(); } break; } } } } return true; } /** * @return HopperInventory */ public function getInventory(){ return $this->inventory; } /** * @return int */ public function getSize(){ return 5; } /** * This method should not be used by plugins, use the Inventory * * @param int $index * * @return Item */ public function getItem($index){ $i = $this->getSlotIndex($index); if($i < 0){ return Item::get(Item::AIR, 0, 0); }else{ return Item::nbtDeserialize($this->namedtag->Items[$i]); } } /** * This method should not be used by plugins, use the Inventory * * @param int $index * @param Item $item * * @return bool */ public function setItem($index, Item $item){ $i = $this->getSlotIndex($index); if($item->getId() === Item::AIR or $item->getCount() <= 0){ if($i >= 0){ unset($this->namedtag->Items[$i]); } }elseif($i < 0){ for($i = 0; $i <= $this->getSize(); ++$i){ if(!isset($this->namedtag->Items[$i])){ break; } } $this->namedtag->Items[$i] = $item->nbtSerialize($index); }else{ $this->namedtag->Items[$i] = $item->nbtSerialize($index); } return true; } /** * @param $index * * @return int */ protected function getSlotIndex($index){ foreach($this->namedtag->Items as $i => $slot){ if((int) $slot["Slot"] === (int) $index){ return (int) $i; } } return -1; } public function saveNBT(){ $this->namedtag->Items = new ListTag("Items", []); $this->namedtag->Items->setTagType(NBT::TAG_Compound); for($index = 0; $index < $this->getSize(); ++$index){ $this->setItem($index, $this->inventory->getItem($index)); } } public function getName() : string{ return isset($this->namedtag->CustomName) ? $this->namedtag->CustomName->getValue() : "Hopper"; } public function hasName(){ return isset($this->namedtag->CustomName); } public function setName($str){ if($str === ""){ unset($this->namedtag->CustomName); return; } $this->namedtag->CustomName = new StringTag("CustomName", $str); } public function hasLock(){ return isset($this->namedtag->Lock); } public function setLock(string $itemName = ""){ if($itemName === ""){ unset($this->namedtag->Lock); return; } $this->namedtag->Lock = new StringTag("Lock", $itemName); } public function checkLock(string $key){ return $this->namedtag->Lock->getValue() === $key; } public function getSpawnCompound(){ $c = new CompoundTag("", [ new StringTag("id", Tile::HOPPER), new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z) ]); if($this->hasName()){ $c->CustomName = $this->namedtag->CustomName; } if($this->hasLock()){ $c->Lock = $this->namedtag->Lock; } return $c; } } ================================================ FILE: src/pocketmine/tile/ItemFrame.php ================================================ Item) or !($nbt->Item instanceof CompoundTag)) { $nbt->Item = Item::get(Item::AIR)->nbtSerialize(-1, "Item"); } parent::__construct($chunk, $nbt); } public function getName() : string{ return "Item Frame"; } public function getItemRotation() { return $this->namedtag["ItemRotation"]; } public function hasItem() : bool{ return $this->getItem()->getId() !== Item::AIR; } public function setItemRotation(int $itemRotation){ $this->namedtag->ItemRotation = new ByteTag("ItemRotation", $itemRotation); $this->onChanged(); } public function getItem(){ return Item::nbtDeserialize($this->namedtag->Item); } public function setItem(Item $item, bool $setChanged = true){ $this->namedtag->Item = $item->nbtSerialize(-1, "Item"); if($setChanged) { $this->onChanged(); } } public function getItemDropChance(){ return $this->namedtag["ItemDropChance"]; } public function setItemDropChance(float $chance = 1.0){ $this->namedtag->ItemDropChance = new FloatTag("ItemDropChance", $chance); } public function getSpawnCompound() { if(!isset($this->namedtag->Item)) { $this->setItem(Item::get(Item::AIR), false); } /** @var CompoundTag $nbtItem */ $nbtItem = clone $this->namedtag->Item; $nbtItem->setName("Item"); if($nbtItem["id"] == 0) { return new CompoundTag("", [ new StringTag("id", Tile::ITEM_FRAME), new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z), new ByteTag("ItemRotation", 0), new FloatTag("ItemDropChance", (float) $this->getItemDropChance()) ]); } else { return new CompoundTag("", [ new StringTag("id", Tile::ITEM_FRAME), new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z), $nbtItem, new ByteTag("ItemRotation", (int) $this->getItemRotation()), new FloatTag("ItemDropChance", (float) $this->getItemDropChance()) ]); } } } ================================================ FILE: src/pocketmine/tile/MobSpawner.php ================================================ EntityId) or !($nbt->EntityId instanceof IntTag)){ $nbt->EntityId = new IntTag("EntityId", 0); } if(!isset($nbt->SpawnCount) or !($nbt->SpawnCount instanceof IntTag)){ $nbt->SpawnCount = new IntTag("SpawnCount", 4); } if(!isset($nbt->SpawnRange) or !($nbt->SpawnRange instanceof IntTag)){ $nbt->SpawnRange = new IntTag("SpawnRange", 4); } if(!isset($nbt->MinSpawnDelay) or !($nbt->MinSpawnDelay instanceof IntTag)){ $nbt->MinSpawnDelay = new IntTag("MinSpawnDelay", 200); } if(!isset($nbt->MaxSpawnDelay) or !($nbt->MaxSpawnDelay instanceof IntTag)){ $nbt->MaxSpawnDelay = new IntTag("MaxSpawnDelay", 799); } if(!isset($nbt->Delay) or !($nbt->Delay instanceof IntTag)){ $nbt->Delay = new IntTag("Delay", mt_rand($nbt->MinSpawnDelay->getValue(), $nbt->MaxSpawnDelay->getValue())); } parent::__construct($chunk, $nbt); if($this->getEntityId() > 0){ $this->scheduleUpdate(); } } public function getEntityId(){ return $this->namedtag["EntityId"]; } public function setEntityId(int $id){ $this->namedtag->EntityId->setValue($id); $this->onChanged(); $this->scheduleUpdate(); } public function getSpawnCount(){ return $this->namedtag["SpawnCount"]; } public function setSpawnCount(int $value){ $this->namedtag->SpawnCount->setValue($value); } public function getSpawnRange(){ return $this->namedtag["SpawnRange"]; } public function setSpawnRange(int $value){ $this->namedtag->SpawnRange->setValue($value); } public function getMinSpawnDelay(){ return $this->namedtag["MinSpawnDelay"]; } public function setMinSpawnDelay(int $value){ $this->namedtag->MinSpawnDelay->setValue($value); } public function getMaxSpawnDelay(){ return $this->namedtag["MaxSpawnDelay"]; } public function setMaxSpawnDelay(int $value){ $this->namedtag->MaxSpawnDelay->setValue($value); } public function getDelay(){ return $this->namedtag["Delay"]; } public function setDelay(int $value){ $this->namedtag->Delay->setValue($value); } public function getName() : string{ return "Monster Spawner"; } public function canUpdate() : bool{ if($this->getEntityId() === 0) return false; $hasPlayer = false; $count = 0; foreach($this->getLevel()->getEntities() as $e){ if($e instanceof Player){ if($e->distance($this->getBlock()) <= 15) $hasPlayer = true; } if($e::NETWORK_ID == $this->getEntityId()){ $count++; } } if($hasPlayer and $count < 15){ // Spawn limit = 15 return true; } return false; } public function onUpdate(){ if($this->closed === true){ return false; } $this->timings->startTiming(); if(!($this->chunk instanceof Chunk)){ return false; } if($this->canUpdate()){ if($this->getDelay() <= 0){ $success = 0; for($i = 0; $i < $this->getSpawnCount(); $i++){ $pos = $this->add(mt_rand() / mt_getrandmax() * $this->getSpawnRange(), mt_rand(-1, 1), mt_rand() / mt_getrandmax() * $this->getSpawnRange()); $target = $this->getLevel()->getBlock($pos); $ground = $target->getSide(Vector3::SIDE_DOWN); if($target->getId() == Item::AIR && $ground->isTopFacingSurfaceSolid()){ $success++; $this->getLevel()->getServer()->getPluginManager()->callEvent($ev = new EntityGenerateEvent($pos, $this->getEntityId(), EntityGenerateEvent::CAUSE_MOB_SPAWNER)); if(!$ev->isCancelled()){ $nbt = new CompoundTag("", [ "Pos" => new ListTag("Pos", [ new DoubleTag("", $pos->x), new DoubleTag("", $pos->y), new DoubleTag("", $pos->z) ]), "Motion" => new ListTag("Motion", [ new DoubleTag("", 0), new DoubleTag("", 0), new DoubleTag("", 0) ]), "Rotation" => new ListTag("Rotation", [ new FloatTag("", mt_rand() / mt_getrandmax() * 360), new FloatTag("", 0) ]), ]); $entity = Entity::createEntity($this->getEntityId(), $this->chunk, $nbt); $entity->spawnToAll(); } } } if($success > 0){ $this->setDelay(mt_rand($this->getMinSpawnDelay(), $this->getMaxSpawnDelay())); } }else{ $this->setDelay($this->getDelay() - 1); } } $this->timings->stopTiming(); return true; } public function getSpawnCompound(){ $c = new CompoundTag("", [ new StringTag("id", Tile::MOB_SPAWNER), new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z), new IntTag("EntityId", (int) $this->getEntityId()) ]); return $c; } } ================================================ FILE: src/pocketmine/tile/Nameable.php ================================================ Text1) or !($nbt->Text1 instanceof StringTag)){ $nbt->Text1 = new StringTag("Text1", ""); } if(!isset($nbt->Text2) or !($nbt->Text2 instanceof StringTag)){ $nbt->Text2 = new StringTag("Text2", ""); } if(!isset($nbt->Text3) or !($nbt->Text3 instanceof StringTag)){ $nbt->Text3 = new StringTag("Text3", ""); } if(!isset($nbt->Text4) or !($nbt->Text4 instanceof StringTag)){ $nbt->Text4 = new StringTag("Text4", ""); } parent::__construct($chunk, $nbt); } public function saveNBT(){ parent::saveNBT(); unset($this->namedtag->Creator); } public function setText($line1 = "", $line2 = "", $line3 = "", $line4 = ""){ $this->namedtag->Text1 = new StringTag("Text1", $line1); $this->namedtag->Text2 = new StringTag("Text2", $line2); $this->namedtag->Text3 = new StringTag("Text3", $line3); $this->namedtag->Text4 = new StringTag("Text4", $line4); $this->onChanged(); return true; } public function getText(){ return [ $this->namedtag["Text1"], $this->namedtag["Text2"], $this->namedtag["Text3"], $this->namedtag["Text4"] ]; } public function getSpawnCompound(){ return new CompoundTag("", [ new StringTag("id", Tile::SIGN), $this->namedtag->Text1, $this->namedtag->Text2, $this->namedtag->Text3, $this->namedtag->Text4, new IntTag("x", (int) $this->x), new IntTag("y", (int) $this->y), new IntTag("z", (int) $this->z) ]); } public function updateCompoundTag(CompoundTag $nbt, Player $player) : bool{ if($nbt["id"] !== Tile::SIGN){ return false; } $ev = new SignChangeEvent($this->getBlock(), $player, [ TextFormat::clean($nbt["Text1"], ($removeFormat = $player->getRemoveFormat())), TextFormat::clean($nbt["Text2"], $removeFormat), TextFormat::clean($nbt["Text3"], $removeFormat), TextFormat::clean($nbt["Text4"], $removeFormat) ]); if(!isset($this->namedtag->Creator) or $this->namedtag["Creator"] !== $player->getRawUniqueId()){ $ev->setCancelled(); } $this->level->getServer()->getPluginManager()->callEvent($ev); if(!$ev->isCancelled()){ $this->setText(...$ev->getLines()); return true; }else{ return false; } } } ================================================ FILE: src/pocketmine/tile/Skull.php ================================================ SkullType) or !($nbt->SkullType instanceof ByteTag)){ $nbt->SkullType = new ByteTag("SkullType", self::TYPE_SKELETON); } if(!isset($nbt->Rot) or !($nbt->Rot instanceof ByteTag)) { $nbt->Rot = new ByteTag("Rot", 0); } parent::__construct($chunk, $nbt); } public function setType(int $type){ if($type >= 0 && $type <= 4){ $this->namedtag->SkullType = new ByteTag("SkullType", $type); $this->onChanged(); return true; } return false; } public function getType() { return $this->namedtag["SkullType"]; } public function saveNBT(){ parent::saveNBT(); unset($this->namedtag->Creator); } public function getSpawnCompound(){ return new CompoundTag("", [ new StringTag("id", Tile::SKULL), $this->namedtag->SkullType, $this->namedtag->Rot, new IntTag("x", (int)$this->x), new IntTag("y", (int)$this->y), new IntTag("z", (int)$this->z), ]); } } ================================================ FILE: src/pocketmine/tile/Spawnable.php ================================================ closed){ return false; } $nbt = new NBT(NBT::LITTLE_ENDIAN); $nbt->setData($this->getSpawnCompound()); $pk = new BlockEntityDataPacket(); $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; $pk->namedtag = $nbt->write(true); $player->dataPacket($pk); return true; } public function __construct(Chunk $chunk, CompoundTag $nbt){ parent::__construct($chunk, $nbt); $this->spawnToAll(); } public function spawnToAll(){ if($this->closed){ return; } foreach($this->getLevel()->getChunkPlayers($this->chunk->getX(), $this->chunk->getZ()) as $player){ if($player->spawned === true){ $this->spawnTo($player); } } } protected function onChanged(){ $this->spawnToAll(); if($this->chunk !== null){ $this->chunk->setChanged(); $this->level->clearChunkCache($this->chunk->getX(), $this->chunk->getZ()); } } /** * @return CompoundTag */ public abstract function getSpawnCompound(); /** * Called when a player updates a block entity's NBT data * for example when writing on a sign. * * @param CompoundTag $nbt * @param Player $player * * @return bool indication of success, will respawn the tile to the player if false. */ public function updateCompoundTag(CompoundTag $nbt, Player $player) : bool{ return false; } } ================================================ FILE: src/pocketmine/tile/Tile.php ================================================ isAbstract()){ self::$knownTiles[$class->getShortName()] = $className; self::$shortNames[$className] = $class->getShortName(); return true; } return false; } /** * Returns the short save name * * @return string */ public function getSaveId(){ return self::$shortNames[static::class]; } public function __construct(Chunk $chunk, CompoundTag $nbt){ if($chunk === null or $chunk->getProvider() === null){ throw new ChunkException("Invalid garbage Chunk given to Tile"); } $this->timings = Timings::getTileEntityTimings($this); $this->server = $chunk->getProvider()->getLevel()->getServer(); $this->chunk = $chunk; $this->setLevel($chunk->getProvider()->getLevel()); $this->namedtag = $nbt; $this->name = ""; $this->lastUpdate = microtime(true); $this->id = Tile::$tileCount++; $this->x = (int) $this->namedtag["x"]; $this->y = (int) $this->namedtag["y"]; $this->z = (int) $this->namedtag["z"]; $this->chunk->addTile($this); $this->getLevel()->addTile($this); $this->tickTimer = Timings::getTileEntityTimings($this); } public function getId(){ return $this->id; } public function saveNBT(){ $this->namedtag->id = new StringTag("id", $this->getSaveId()); $this->namedtag->x = new IntTag("x", $this->x); $this->namedtag->y = new IntTag("y", $this->y); $this->namedtag->z = new IntTag("z", $this->z); } /** * @return \pocketmine\block\Block */ public function getBlock(){ return $this->level->getBlock($this); } public function onUpdate(){ return false; } public final function scheduleUpdate(){ $this->level->updateTiles[$this->id] = $this; } public function __destruct(){ $this->close(); } public function close(){ if(!$this->closed){ $this->closed = true; unset($this->level->updateTiles[$this->id]); if($this->chunk instanceof Chunk){ $this->chunk->removeTile($this); } if(($level = $this->getLevel()) instanceof Level){ $level->removeTile($this); } $this->level = null; } } public function getName() : string{ return $this->name; } } ================================================ FILE: src/pocketmine/utils/Binary.php ================================================ putUnsignedVarInt(count($data)); foreach($data as $key => $d){ $stream->putUnsignedVarInt($key); //data key $stream->putUnsignedVarInt($d[0]); //data type switch($d[0]){ case Entity::DATA_TYPE_BYTE: $stream->putByte($d[1]); break; case Entity::DATA_TYPE_SHORT: $stream->putLShort($d[1]); //SIGNED short! break; case Entity::DATA_TYPE_INT: $stream->putVarInt($d[1]); break; case Entity::DATA_TYPE_FLOAT: $stream->putLFloat($d[1]); break; case Entity::DATA_TYPE_STRING: $stream->putString($d[1]); break; case Entity::DATA_TYPE_SLOT: //TODO: change this implementation (use objects) $stream->putSlot(Item::get($d[1][0], $d[1][2], $d[1][1])); //ID, damage, count break; case Entity::DATA_TYPE_POS: //TODO: change this implementation (use objects) $stream->putVarInt($d[1][0]); //x $stream->putVarInt($d[1][1]); //y (SIGNED) $stream->putVarInt($d[1][2]); //z break; case Entity::DATA_TYPE_LONG: $stream->putVarInt($d[1]); //TODO: varint64 support break; case Entity::DATA_TYPE_VECTOR3F: //TODO: change this implementation (use objects) $stream->putVector3f($d[1][0], $d[1][1], $d[1][2]); //x, y, z } } return $stream->getBuffer(); } /** * Reads a metadata coded string * * @param $value * @param bool $types * * @return array */ public static function readMetadata($value, $types = false){ $stream = new BinaryStream(); $stream->setBuffer($value); $count = $stream->getUnsignedVarInt(); $data = []; for($i = 0; $i < $count; ++$i){ $key = $stream->getUnsignedVarInt(); $type = $stream->getUnsignedVarInt(); $value = null; switch($type){ case Entity::DATA_TYPE_BYTE: $value = $stream->getByte(); break; case Entity::DATA_TYPE_SHORT: $value = $stream->getLShort(true); //signed break; case Entity::DATA_TYPE_INT: $value = $stream->getVarInt(); break; case Entity::DATA_TYPE_FLOAT: $value = $stream->getLFloat(); break; case Entity::DATA_TYPE_STRING: $value = $stream->getString(); break; case Entity::DATA_TYPE_SLOT: //TODO: use objects directly $value = []; $item = $stream->getSlot(); $value[0] = $item->getId(); $value[1] = $item->getCount(); $value[2] = $item->getDamage(); break; case Entity::DATA_TYPE_POS: $value = []; $value[0] = $stream->getVarInt(); //x $value[1] = $stream->getVarInt(); //y (SIGNED) $value[2] = $stream->getVarInt(); //z break; case Entity::DATA_TYPE_LONG: $value = $stream->getVarInt(); //TODO: varint64 proper support break; case Entity::DATA_TYPE_VECTOR3F: $value = [0.0, 0.0, 0.0]; $stream->getVector3f($value[0], $value[1], $value[2]); break; default: $value = []; } if($types === true){ $data[$key] = [$value, $type]; }else{ $data[$key] = $value; } } return $data; } /** * Reads a byte boolean * * @param $b * * @return bool */ public static function readBool($b){ return self::readByte($b, false) === 0 ? false : true; } /** * Writes a byte boolean * * @param $b * * @return bool|string */ public static function writeBool($b){ return self::writeByte($b === true ? 1 : 0); } /** * Reads an unsigned/signed byte * * @param string $c * @param bool $signed * * @return int */ public static function readByte($c, $signed = true){ self::checkLength($c, 1); $b = ord($c{0}); if($signed){ if(PHP_INT_SIZE === 8){ return $b << 56 >> 56; }else{ return $b << 24 >> 24; } }else{ return $b; } } /** * Writes an unsigned/signed byte * * @param $c * * @return string */ public static function writeByte($c){ return chr($c); } /** * Reads a 16-bit unsigned big-endian number * * @param $str * * @return int */ public static function readShort($str){ self::checkLength($str, 2); return unpack("n", $str)[1]; } /** * Reads a 16-bit signed big-endian number * * @param $str * * @return int */ public static function readSignedShort($str){ self::checkLength($str, 2); if(PHP_INT_SIZE === 8){ return unpack("n", $str)[1] << 48 >> 48; }else{ return unpack("n", $str)[1] << 16 >> 16; } } /** * Writes a 16-bit signed/unsigned big-endian number * * @param $value * * @return string */ public static function writeShort($value){ return pack("n", $value); } /** * Reads a 16-bit unsigned little-endian number * * @param $str * * @return int */ public static function readLShort($str){ self::checkLength($str, 2); return unpack("v", $str)[1]; } /** * Reads a 16-bit signed little-endian number * * @param $str * * @return int */ public static function readSignedLShort($str){ self::checkLength($str, 2); if(PHP_INT_SIZE === 8){ return unpack("v", $str)[1] << 48 >> 48; }else{ return unpack("v", $str)[1] << 16 >> 16; } } /** * Writes a 16-bit signed/unsigned little-endian number * * @param $value * * @return string */ public static function writeLShort($value){ return pack("v", $value); } public static function readInt($str){ self::checkLength($str, 4); if(PHP_INT_SIZE === 8){ return unpack("N", $str)[1] << 32 >> 32; }else{ return unpack("N", $str)[1]; } } public static function writeInt($value){ return pack("N", $value); } public static function readLInt($str){ self::checkLength($str, 4); if(PHP_INT_SIZE === 8){ return unpack("V", $str)[1] << 32 >> 32; }else{ return unpack("V", $str)[1]; } } public static function writeLInt($value){ return pack("V", $value); } public static function readFloat($str, int $accuracy = -1){ self::checkLength($str, 4); $value = ENDIANNESS === self::BIG_ENDIAN ? unpack("f", $str)[1] : unpack("f", strrev($str))[1]; if($accuracy > -1){ return round($value, $accuracy); }else{ return $value; } } public static function writeFloat($value){ return ENDIANNESS === self::BIG_ENDIAN ? pack("f", $value) : strrev(pack("f", $value)); } public static function readLFloat($str, int $accuracy = -1){ self::checkLength($str, 4); $value = ENDIANNESS === self::BIG_ENDIAN ? unpack("f", strrev($str))[1] : unpack("f", $str)[1]; if($accuracy > -1){ return round($value, $accuracy); }else{ return $value; } } public static function writeLFloat($value){ return ENDIANNESS === self::BIG_ENDIAN ? strrev(pack("f", $value)) : pack("f", $value); } public static function printFloat($value){ return preg_replace("/(\\.\\d+?)0+$/", "$1", sprintf("%F", $value)); } public static function readDouble($str){ self::checkLength($str, 8); return ENDIANNESS === self::BIG_ENDIAN ? unpack("d", $str)[1] : unpack("d", strrev($str))[1]; } public static function writeDouble($value){ return ENDIANNESS === self::BIG_ENDIAN ? pack("d", $value) : strrev(pack("d", $value)); } public static function readLDouble($str){ self::checkLength($str, 8); return ENDIANNESS === self::BIG_ENDIAN ? unpack("d", strrev($str))[1] : unpack("d", $str)[1]; } public static function writeLDouble($value){ return ENDIANNESS === self::BIG_ENDIAN ? strrev(pack("d", $value)) : pack("d", $value); } public static function readLong($x){ self::checkLength($x, 8); if(PHP_INT_SIZE === 8){ $int = unpack("N*", $x); return ($int[1] << 32) | $int[2]; }else{ $value = "0"; for($i = 0; $i < 8; $i += 2){ $value = bcmul($value, "65536", 0); $value = bcadd($value, self::readShort(substr($x, $i, 2)), 0); } if(bccomp($value, "9223372036854775807") == 1){ $value = bcadd($value, "-18446744073709551616"); } return $value; } } public static function writeLong($value){ if(PHP_INT_SIZE === 8){ return pack("NN", $value >> 32, $value & 0xFFFFFFFF); }else{ $x = ""; if(bccomp($value, "0") == -1){ $value = bcadd($value, "18446744073709551616"); } $x .= self::writeShort(bcmod(bcdiv($value, "281474976710656"), "65536")); $x .= self::writeShort(bcmod(bcdiv($value, "4294967296"), "65536")); $x .= self::writeShort(bcmod(bcdiv($value, "65536"), "65536")); $x .= self::writeShort(bcmod($value, "65536")); return $x; } } public static function readLLong($str){ return self::readLong(strrev($str)); } public static function writeLLong($value){ return strrev(self::writeLong($value)); } //TODO: proper varlong support public static function readVarInt($stream){ $shift = PHP_INT_SIZE === 8 ? 63 : 31; $raw = self::readUnsignedVarInt($stream); $temp = ((($raw << $shift) >> $shift) ^ $raw) >> 1; return $temp ^ ($raw & (1 << $shift)); } public static function readUnsignedVarInt($stream){ $value = 0; $i = 0; do{ if($i > 63){ throw new \InvalidArgumentException("Varint did not terminate after 10 bytes!"); } $value |= ((($b = $stream->getByte()) & 0x7f) << $i); $i += 7; }while($b & 0x80); return $value; } public static function writeVarInt($v){ return self::writeUnsignedVarInt(($v << 1) ^ ($v >> (PHP_INT_SIZE === 8 ? 63 : 31))); } public static function writeUnsignedVarInt($value){ $buf = ""; for($i = 0; $i < 10; ++$i){ if(($value >> 7) !== 0){ $buf .= chr($value | 0x80); //Let chr() take the last byte of this, it's faster than adding another & 0x7f. }else{ $buf .= chr($value & 0x7f); return $buf; } $value = (($value >> 7) & (PHP_INT_MAX >> 6)); //PHP really needs a logical right-shift operator } throw new \InvalidArgumentException("Value too large to be encoded as a varint"); } } ================================================ FILE: src/pocketmine/utils/BinaryStream.php ================================================ #ifndef COMPILE #endif use pocketmine\item\Item; class BinaryStream extends \stdClass{ public $offset; public $buffer; public function __construct($buffer = "", $offset = 0){ $this->buffer = $buffer; $this->offset = $offset; } public function reset(){ $this->buffer = ""; $this->offset = 0; } public function setBuffer($buffer = null, $offset = 0){ $this->buffer = $buffer; $this->offset = (int) $offset; } public function getOffset(){ return $this->offset; } public function getBuffer(){ return $this->buffer; } public function get($len){ if($len < 0){ $this->offset = strlen($this->buffer) - 1; return ""; }elseif($len === true){ $str = substr($this->buffer, $this->offset); $this->offset = strlen($this->buffer); return $str; } return $len === 1 ? $this->buffer{$this->offset++} : substr($this->buffer, ($this->offset += $len) - $len, $len); } public function put($str){ $this->buffer .= $str; } public function getBool() : bool{ return (bool) $this->getByte(); } public function putBool($v){ $this->putByte((bool) $v); } public function getLong(){ return Binary::readLong($this->get(8)); } public function putLong($v){ $this->buffer .= Binary::writeLong($v); } public function getInt(){ return Binary::readInt($this->get(4)); } public function putInt($v){ $this->buffer .= Binary::writeInt($v); } public function getLLong(){ return Binary::readLLong($this->get(8)); } public function putLLong($v){ $this->buffer .= Binary::writeLLong($v); } public function getLInt(){ return Binary::readLInt($this->get(4)); } public function putLInt($v){ $this->buffer .= Binary::writeLInt($v); } public function getSignedShort(){ return Binary::readSignedShort($this->get(2)); } public function putShort($v){ $this->buffer .= Binary::writeShort($v); } public function getShort(){ return Binary::readShort($this->get(2)); } public function putSignedShort($v){ $this->buffer .= Binary::writeShort($v); } public function getFloat(int $accuracy = -1){ return Binary::readFloat($this->get(4), $accuracy); } public function putFloat($v){ $this->buffer .= Binary::writeFloat($v); } public function getLShort($signed = true){ return $signed ? Binary::readSignedLShort($this->get(2)) : Binary::readLShort($this->get(2)); } public function putLShort($v){ $this->buffer .= Binary::writeLShort($v); } public function getLFloat(int $accuracy = -1){ return Binary::readLFloat($this->get(4), $accuracy); } public function putLFloat($v){ $this->buffer .= Binary::writeLFloat($v); } public function getTriad(){ return Binary::readTriad($this->get(3)); } public function putTriad($v){ $this->buffer .= Binary::writeTriad($v); } public function getLTriad(){ return Binary::readLTriad($this->get(3)); } public function putLTriad($v){ $this->buffer .= Binary::writeLTriad($v); } public function getByte(){ return ord($this->buffer{$this->offset++}); } public function putByte($v){ $this->buffer .= chr($v); } public function getDataArray($len = 10){ $data = []; for($i = 1; $i <= $len and !$this->feof(); ++$i){ $data[] = $this->get($this->getTriad()); } return $data; } public function putDataArray(array $data = []){ foreach($data as $v){ $this->putTriad(strlen($v)); $this->put($v); } } public function getUUID(){ return UUID::fromBinary($this->get(16)); } public function putUUID(UUID $uuid){ $this->put($uuid->toBinary()); } public function getSlot(){ $id = $this->getVarInt(); if($id <= 0){ return Item::get(0, 0, 0); } $auxValue = $this->getVarInt(); $data = $auxValue >> 8; $cnt = $auxValue & 0xff; $nbtLen = $this->getLShort(); $nbt = ""; if($nbtLen > 0){ $nbt = $this->get($nbtLen); } return Item::get( $id, $data, $cnt, $nbt ); } public function putSlot(Item $item){ if($item->getId() === 0){ $this->putVarInt(0); return; } $this->putVarInt($item->getId()); $auxValue = (($item->getDamage() ?? -1) << 8) | $item->getCount(); $this->putVarInt($auxValue); $nbt = $item->getCompoundTag(); $this->putLShort(strlen($nbt)); $this->put($nbt); } public function getString(){ return $this->get($this->getUnsignedVarInt()); } public function putString($v){ $this->putUnsignedVarInt(strlen($v)); $this->put($v); } //TODO: varint64 /** * Reads an unsigned varint32 from the stream. */ public function getUnsignedVarInt(){ return Binary::readUnsignedVarInt($this); } /** * Writes an unsigned varint32 to the stream. */ public function putUnsignedVarInt($v){ $this->put(Binary::writeUnsignedVarInt($v)); } /** * Reads a signed varint32 from the stream. */ public function getVarInt(){ return Binary::readVarInt($this); } /** * Writes a signed varint32 to the stream. */ public function putVarInt($v){ $this->put(Binary::writeVarInt($v)); } public function getEntityId(){ return $this->getVarInt(); } public function putEntityId($v){ $this->putVarInt($v); } public function getBlockCoords(&$x, &$y, &$z){ $x = $this->getVarInt(); $y = $this->getUnsignedVarInt(); $z = $this->getVarInt(); } public function putBlockCoords($x, $y, $z){ $this->putVarInt($x); $this->putUnsignedVarInt($y); $this->putVarInt($z); } public function getVector3f(&$x, &$y, &$z){ $x = $this->getLFloat(4); $y = $this->getLFloat(4); $z = $this->getLFloat(4); } public function putVector3f($x, $y, $z){ $this->putLFloat($x); $this->putLFloat($y); $this->putLFloat($z); } public function feof(){ return !isset($this->buffer{$this->offset}); } } ================================================ FILE: src/pocketmine/utils/BlockIterator.php ================================================ [3] */ private $blockQueue; private $currentBlock = 0; /** @var Block */ private $currentBlockObject = null; private $currentDistance = 0; private $maxDistanceInt = 0; private $secondError; private $thirdError; private $secondStep; private $thirdStep; private $mainFace; private $secondFace; private $thirdFace; public function __construct(Level $level, Vector3 $start, Vector3 $direction, $yOffset = 0, $maxDistance = 0){ $this->level = $level; $this->maxDistance = (int) $maxDistance; $this->blockQueue = new \SplFixedArray(3); $startClone = new Vector3($start->x, $start->y, $start->z); $startClone->y += $yOffset; $this->currentDistance = 0; $mainDirection = 0; $secondDirection = 0; $thirdDirection = 0; $mainPosition = 0; $secondPosition = 0; $thirdPosition = 0; $pos = new Vector3($startClone->x, $startClone->y, $startClone->z); $startBlock = $this->level->getBlock(new Vector3(floor($pos->x), floor($pos->y), floor($pos->z))); if($this->getXLength($direction) > $mainDirection){ $this->mainFace = $this->getXFace($direction); $mainDirection = $this->getXLength($direction); $mainPosition = $this->getXPosition($direction, $startClone, $startBlock); $this->secondFace = $this->getYFace($direction); $secondDirection = $this->getYLength($direction); $secondPosition = $this->getYPosition($direction, $startClone, $startBlock); $this->thirdFace = $this->getZFace($direction); $thirdDirection = $this->getZLength($direction); $thirdPosition = $this->getZPosition($direction, $startClone, $startBlock); } if($this->getYLength($direction) > $mainDirection){ $this->mainFace = $this->getYFace($direction); $mainDirection = $this->getYLength($direction); $mainPosition = $this->getYPosition($direction, $startClone, $startBlock); $this->secondFace = $this->getZFace($direction); $secondDirection = $this->getZLength($direction); $secondPosition = $this->getZPosition($direction, $startClone, $startBlock); $this->thirdFace = $this->getXFace($direction); $thirdDirection = $this->getXLength($direction); $thirdPosition = $this->getXPosition($direction, $startClone, $startBlock); } if($this->getZLength($direction) > $mainDirection){ $this->mainFace = $this->getZFace($direction); $mainDirection = $this->getZLength($direction); $mainPosition = $this->getZPosition($direction, $startClone, $startBlock); $this->secondFace = $this->getXFace($direction); $secondDirection = $this->getXLength($direction); $secondPosition = $this->getXPosition($direction, $startClone, $startBlock); $this->thirdFace = $this->getYFace($direction); $thirdDirection = $this->getYLength($direction); $thirdPosition = $this->getYPosition($direction, $startClone, $startBlock); } $d = $mainPosition / $mainDirection; $secondd = $secondPosition - $secondDirection * $d; $thirdd = $thirdPosition - $thirdDirection * $d; $this->secondError = floor($secondd * self::$gridSize); $this->secondStep = round($secondDirection / $mainDirection * self::$gridSize); $this->thirdError = floor($thirdd * self::$gridSize); $this->thirdStep = round($thirdDirection / $mainDirection * self::$gridSize); if($this->secondError + $this->secondStep <= 0){ $this->secondError = -$this->secondStep + 1; } if($this->thirdError + $this->thirdStep <= 0){ $this->thirdError = -$this->thirdStep + 1; } $lastBlock = $startBlock->getSide(Vector3::getOppositeSide($this->mainFace)); if($this->secondError < 0){ $this->secondError += self::$gridSize; $lastBlock = $lastBlock->getSide(Vector3::getOppositeSide($this->secondFace)); } if($this->thirdError < 0){ $this->thirdError += self::$gridSize; $lastBlock = $lastBlock->getSide(Vector3::getOppositeSide($this->thirdFace)); } $this->secondError -= self::$gridSize; $this->thirdError -= self::$gridSize; $this->blockQueue[0] = $lastBlock; $this->currentBlock = -1; $this->scan(); $startBlockFound = false; for($cnt = $this->currentBlock; $cnt >= 0; --$cnt){ if($this->blockEquals($this->blockQueue[$cnt], $startBlock)){ $this->currentBlock = $cnt; $startBlockFound = true; break; } } if(!$startBlockFound){ throw new \InvalidStateException("Start block missed in BlockIterator"); } $this->maxDistanceInt = round($maxDistance / (sqrt($mainDirection ** 2 + $secondDirection ** 2 + $thirdDirection ** 2) / $mainDirection)); } private function blockEquals(Block $a, Block $b){ return $a->x === $b->x and $a->y === $b->y and $a->z === $b->z; } private function getXFace(Vector3 $direction){ return (($direction->x) > 0) ? Vector3::SIDE_EAST : Vector3::SIDE_WEST; } private function getYFace(Vector3 $direction){ return (($direction->y) > 0) ? Vector3::SIDE_UP : Vector3::SIDE_DOWN; } private function getZFace(Vector3 $direction){ return (($direction->z) > 0) ? Vector3::SIDE_SOUTH : Vector3::SIDE_NORTH; } private function getXLength(Vector3 $direction){ return abs($direction->x); } private function getYLength(Vector3 $direction){ return abs($direction->y); } private function getZLength(Vector3 $direction){ return abs($direction->z); } private function getPosition($direction, $position, $blockPosition){ return $direction > 0 ? ($position - $blockPosition) : ($blockPosition + 1 - $position); } private function getXPosition(Vector3 $direction, Vector3 $position, Block $block){ return $this->getPosition($direction->x, $position->x, $block->x); } private function getYPosition(Vector3 $direction, Vector3 $position, Block $block){ return $this->getPosition($direction->y, $position->y, $block->y); } private function getZPosition(Vector3 $direction, Vector3 $position, Block $block){ return $this->getPosition($direction->z, $position->z, $block->z); } public function next(){ $this->scan(); if($this->currentBlock <= -1){ throw new \OutOfBoundsException; }else{ $this->currentBlockObject = $this->blockQueue[$this->currentBlock--]; } } /** * @return Block * * @throws \OutOfBoundsException */ public function current(){ if($this->currentBlockObject === null){ throw new \OutOfBoundsException; } return $this->currentBlockObject; } public function rewind(){ throw new \InvalidStateException("BlockIterator doesn't support rewind()"); } public function key(){ return $this->currentBlock - 1; } public function valid(){ $this->scan(); return $this->currentBlock !== -1; } private function scan(){ if($this->currentBlock >= 0){ return; } if($this->maxDistance !== 0 and $this->currentDistance > $this->maxDistanceInt){ $this->end = true; return; } if($this->end){ return; } ++$this->currentDistance; $this->secondError += $this->secondStep; $this->thirdError += $this->thirdStep; if($this->secondError > 0 and $this->thirdError > 0){ $this->blockQueue[2] = $this->blockQueue[0]->getSide($this->mainFace); if(($this->secondStep * $this->thirdError) < ($this->thirdStep * $this->secondError)){ $this->blockQueue[1] = $this->blockQueue[2]->getSide($this->secondFace); $this->blockQueue[0] = $this->blockQueue[1]->getSide($this->thirdFace); }else{ $this->blockQueue[1] = $this->blockQueue[2]->getSide($this->thirdFace); $this->blockQueue[0] = $this->blockQueue[1]->getSide($this->secondFace); } $this->thirdError -= self::$gridSize; $this->secondError -= self::$gridSize; $this->currentBlock = 2; }elseif($this->secondError > 0){ $this->blockQueue[1] = $this->blockQueue[0]->getSide($this->mainFace); $this->blockQueue[0] = $this->blockQueue[1]->getSide($this->secondFace); $this->secondError -= self::$gridSize; $this->currentBlock = 1; }elseif($this->thirdError > 0){ $this->blockQueue[1] = $this->blockQueue[0]->getSide($this->mainFace); $this->blockQueue[0] = $this->blockQueue[1]->getSide($this->thirdFace); $this->thirdError -= self::$gridSize; $this->currentBlock = 1; }else{ $this->blockQueue[0] = $this->blockQueue[0]->getSide($this->mainFace); $this->currentBlock = 0; } } } ================================================ FILE: src/pocketmine/utils/ChunkException.php ================================================ getRed(); $tg += $c->getGreen(); $tb += $c->getBlue(); ++$count; } return Color::getRGB($tr / $count, $tg / $count, $tb / $count); } public static function getDyeColor($id){ if(isset(self::$dyeColors[$id])){ return clone self::$dyeColors[$id]; } return Color::getRGB(0, 0, 0); } public function __construct($r, $g, $b){ $this->red = $r; $this->green = $g; $this->blue = $b; } public function getRed(){ return (int) $this->red; } public function getBlue(){ return (int) $this->blue; } public function getGreen(){ return (int) $this->green; } public function getColorCode(){ return ($this->red << 16 | $this->green << 8 | $this->blue) & 0xffffff; } public function __toString(){ return "Color(red:" . $this->red . ", green:" . $this->green . ", blue:" . $this->blue . ")"; } } ================================================ FILE: src/pocketmine/utils/Config.php ================================================ Config::PROPERTIES, "cnf" => Config::CNF, "conf" => Config::CNF, "config" => Config::CNF, "json" => Config::JSON, "js" => Config::JSON, "yml" => Config::YAML, "yaml" => Config::YAML, //"export" => Config::EXPORT, //"xport" => Config::EXPORT, "sl" => Config::SERIALIZED, "serialize" => Config::SERIALIZED, "txt" => Config::ENUM, "list" => Config::ENUM, "enum" => Config::ENUM, ]; /** * @param string $file Path of the file to be loaded * @param int $type Config type to load, -1 by default (detect) * @param array $default Array with the default values that will be written to the file if it did not exist * @param null &$correct Sets correct to true if everything has been loaded correctly */ public function __construct($file, $type = Config::DETECT, $default = [], &$correct = null){ $this->load($file, $type, $default); $correct = $this->correct; } /** * Removes all the changes in memory and loads the file again */ public function reload(){ $this->config = []; $this->nestedCache = []; $this->correct = false; $this->load($this->file, $this->type); } /** * @param $str * * @return mixed */ public static function fixYAMLIndexes($str){ return preg_replace("#^([ ]*)([a-zA-Z_]{1}[ ]*)\\:$#m", "$1\"$2\":", $str); } /** * @param $file * @param int $type * @param array $default * * @return bool */ public function load($file, $type = Config::DETECT, $default = []){ $this->correct = true; $this->type = (int) $type; $this->file = $file; if(!is_array($default)){ $default = []; } if(!file_exists($file)){ $this->config = $default; $this->save(); }else{ if($this->type === Config::DETECT){ $extension = explode(".", basename($this->file)); $extension = strtolower(trim(array_pop($extension))); if(isset(Config::$formats[$extension])){ $this->type = Config::$formats[$extension]; }else{ $this->correct = false; } } if($this->correct === true){ $content = file_get_contents($this->file); switch($this->type){ case Config::PROPERTIES: case Config::CNF: $this->parseProperties($content); break; case Config::JSON: $this->config = json_decode($content, true); break; case Config::YAML: $content = self::fixYAMLIndexes($content); $this->config = yaml_parse($content); break; case Config::SERIALIZED: $this->config = unserialize($content); break; case Config::ENUM: $this->parseList($content); break; default: $this->correct = false; return false; } if(!is_array($this->config)){ $this->config = $default; } if($this->fillDefaults($default, $this->config) > 0){ $this->save(); } }else{ return false; } } return true; } /** * @return boolean */ public function check(){ return $this->correct === true; } /** * @param bool $async * * @return boolean */ public function save($async = false){ if($this->correct === true){ try{ $content = null; switch($this->type){ case Config::PROPERTIES: case Config::CNF: $content = $this->writeProperties(); break; case Config::JSON: $content = json_encode($this->config, JSON_PRETTY_PRINT | JSON_BIGINT_AS_STRING); break; case Config::YAML: $content = yaml_emit($this->config, YAML_UTF8_ENCODING); break; case Config::SERIALIZED: $content = serialize($this->config); break; case Config::ENUM: $content = implode("\r\n", array_keys($this->config)); break; } if($async){ Server::getInstance()->getScheduler()->scheduleAsyncTask(new FileWriteTask($this->file, $content)); }else{ file_put_contents($this->file, $content); } }catch(\Throwable $e){ $logger = Server::getInstance()->getLogger(); $logger->critical("Could not save Config " . $this->file . ": " . $e->getMessage()); if(\pocketmine\DEBUG > 1 and $logger instanceof MainLogger){ $logger->logException($e); } } return true; }else{ return false; } } /** * @param $k * * @return boolean|mixed */ public function __get($k){ return $this->get($k); } /** * @param $k * @param $v */ public function __set($k, $v){ $this->set($k, $v); } /** * @param $k * * @return boolean */ public function __isset($k){ return $this->exists($k); } /** * @param $k */ public function __unset($k){ $this->remove($k); } /** * @param $key * @param $value */ public function setNested($key, $value){ $vars = explode(".", $key); $base = array_shift($vars); if(!isset($this->config[$base])){ $this->config[$base] = []; } $base =& $this->config[$base]; while(count($vars) > 0){ $baseKey = array_shift($vars); if(!isset($base[$baseKey])){ $base[$baseKey] = []; } $base =& $base[$baseKey]; } $base = $value; $this->nestedCache[$key] = $value; } /** * @param $key * @param mixed $default * * @return mixed */ public function getNested($key, $default = null){ if(isset($this->nestedCache[$key])){ return $this->nestedCache[$key]; } $vars = explode(".", $key); $base = array_shift($vars); if(isset($this->config[$base])){ $base = $this->config[$base]; }else{ return $default; } while(count($vars) > 0){ $baseKey = array_shift($vars); if(is_array($base) and isset($base[$baseKey])){ $base = $base[$baseKey]; }else{ return $default; } } return $this->nestedCache[$key] = $base; } /** * @param $k * @param mixed $default * * @return boolean|mixed */ public function get($k, $default = false){ return ($this->correct and isset($this->config[$k])) ? $this->config[$k] : $default; } /** * @param string $k key to be set * @param mixed $v value to set key */ public function set($k, $v = true){ $this->config[$k] = $v; foreach($this->nestedCache as $nestedKey => $nvalue){ if(substr($nestedKey, 0, strlen($k) + 1) === ($k . ".")){ unset($this->nestedCache[$nestedKey]); } } } /** * @param array $v */ public function setAll($v){ $this->config = $v; } /** * @param $k * @param bool $lowercase If set, searches Config in single-case / lowercase. * * @return boolean */ public function exists($k, $lowercase = false){ if($lowercase === true){ $k = strtolower($k); //Convert requested key to lower $array = array_change_key_case($this->config, CASE_LOWER); //Change all keys in array to lower return isset($array[$k]); //Find $k in modified array }else{ return isset($this->config[$k]); } } /** * @param $k */ public function remove($k){ unset($this->config[$k]); } /** * @param bool $keys * * @return array */ public function getAll($keys = false){ return ($keys === true ? array_keys($this->config) : $this->config); } /** * @param array $defaults */ public function setDefaults(array $defaults){ $this->fillDefaults($defaults, $this->config); } /** * @param $default * @param $data * * @return integer */ private function fillDefaults($default, &$data){ $changed = 0; foreach($default as $k => $v){ if(is_array($v)){ if(!isset($data[$k]) or !is_array($data[$k])){ $data[$k] = []; } $changed += $this->fillDefaults($v, $data[$k]); }elseif(!isset($data[$k])){ $data[$k] = $v; ++$changed; } } return $changed; } /** * @param $content */ private function parseList($content){ foreach(explode("\n", trim(str_replace("\r\n", "\n", $content))) as $v){ $v = trim($v); if($v == ""){ continue; } $this->config[$v] = true; } } /** * @return string */ private function writeProperties(){ $content = "#Properties Config file\r\n#" . date("D M j H:i:s T Y") . "\r\n"; foreach($this->config as $k => $v){ if(is_bool($v) === true){ $v = $v === true ? "on" : "off"; }elseif(is_array($v)){ $v = implode(";", $v); } $content .= $k . "=" . $v . "\r\n"; } return $content; } /** * @param $content */ private function parseProperties($content){ if(preg_match_all('/([a-zA-Z0-9\-_\.]*)=([^\r\n]*)/u', $content, $matches) > 0){ //false or 0 matches foreach($matches[1] as $i => $k){ $v = trim($matches[2][$i]); switch(strtolower($v)){ case "on": case "true": case "yes": $v = true; break; case "off": case "false": case "no": $v = false; break; } if(isset($this->config[$k])){ MainLogger::getLogger()->debug("[Config] Repeated property " . $k . " on file " . $this->file); } $this->config[$k] = $v; } } } } ================================================ FILE: src/pocketmine/utils/LevelException.php ================================================ shouldRecordMsg = $b; $this->lastGet = time(); } public function getMessages(){ $msg = $this->shouldSendMsg; $this->shouldSendMsg = ""; $this->lastGet = time(); return $msg; } /** * @param string $logFile * @param bool $logDebug * * @throws \RuntimeException */ public function __construct($logFile, $logDebug = false){ if(static::$logger instanceof MainLogger){ throw new \RuntimeException("MainLogger has been already created"); } static::$logger = $this; touch($logFile); $this->logFile = $logFile; $this->logDebug = (bool) $logDebug; $this->logStream = new \Threaded; $this->start(); } /** * @return MainLogger */ public static function getLogger(){ return static::$logger; } public function emergency($message, $name = "EMERGENCY"){ $this->send($message, \LogLevel::EMERGENCY, $name, TextFormat::RED); } public function alert($message, $name = "ALERT"){ $this->send($message, \LogLevel::ALERT, $name, TextFormat::RED); } public function critical($message, $name = "CRITICAL"){ $this->send($message, \LogLevel::CRITICAL, $name, TextFormat::RED); } public function error($message, $name = "ERROR"){ $this->send($message, \LogLevel::ERROR, $name, TextFormat::DARK_RED); } public function warning($message, $name = "WARNING"){ $this->send($message, \LogLevel::WARNING, $name, TextFormat::YELLOW); } public function notice($message, $name = "NOTICE"){ $this->send($message, \LogLevel::NOTICE, $name, TextFormat::AQUA); } public function info($message, $name = "INFO"){ $this->send($message, \LogLevel::INFO, $name, TextFormat::WHITE); } public function debug($message, $name = "DEBUG"){ if($this->logDebug === false){ return; } $this->send($message, \LogLevel::DEBUG, $name, TextFormat::GRAY); } /** * @param bool $logDebug */ public function setLogDebug($logDebug){ $this->logDebug = (bool) $logDebug; } public function logException(\Throwable $e, $trace = null){ if($trace === null){ $trace = $e->getTrace(); } $errstr = $e->getMessage(); $errfile = $e->getFile(); $errno = $e->getCode(); $errline = $e->getLine(); $errorConversion = [ 0 => "EXCEPTION", E_ERROR => "E_ERROR", E_WARNING => "E_WARNING", E_PARSE => "E_PARSE", E_NOTICE => "E_NOTICE", E_CORE_ERROR => "E_CORE_ERROR", E_CORE_WARNING => "E_CORE_WARNING", E_COMPILE_ERROR => "E_COMPILE_ERROR", E_COMPILE_WARNING => "E_COMPILE_WARNING", E_USER_ERROR => "E_USER_ERROR", E_USER_WARNING => "E_USER_WARNING", E_USER_NOTICE => "E_USER_NOTICE", E_STRICT => "E_STRICT", E_RECOVERABLE_ERROR => "E_RECOVERABLE_ERROR", E_DEPRECATED => "E_DEPRECATED", E_USER_DEPRECATED => "E_USER_DEPRECATED", ]; if($errno === 0){ $type = LogLevel::CRITICAL; }else{ $type = ($errno === E_ERROR or $errno === E_USER_ERROR) ? LogLevel::ERROR : (($errno === E_USER_WARNING or $errno === E_WARNING) ? LogLevel::WARNING : LogLevel::NOTICE); } $errno = isset($errorConversion[$errno]) ? $errorConversion[$errno] : $errno; if(($pos = strpos($errstr, "\n")) !== false){ $errstr = substr($errstr, 0, $pos); } $errfile = \pocketmine\cleanPath($errfile); $this->log($type, get_class($e) . ": \"$errstr\" ($errno) in \"$errfile\" at line $errline"); foreach(@\pocketmine\getTrace(1, $trace) as $i => $line){ $this->debug($line); } } public function log($level, $message){ switch($level){ case LogLevel::EMERGENCY: $this->emergency($message); break; case LogLevel::ALERT: $this->alert($message); break; case LogLevel::CRITICAL: $this->critical($message); break; case LogLevel::ERROR: $this->error($message); break; case LogLevel::WARNING: $this->warning($message); break; case LogLevel::NOTICE: $this->notice($message); break; case LogLevel::INFO: $this->info($message); break; case LogLevel::DEBUG: $this->debug($message); break; } } public function shutdown(){ $this->shutdown = true; } protected function send($message, $level, $prefix, $color){ $now = time(); $thread = \Thread::getCurrentThread(); if($thread === null){ $threadName = "Elywing thread"; }elseif($thread instanceof Thread or $thread instanceof Worker){ $threadName = $thread->getThreadName() . " thread"; }else{ $threadName = (new \ReflectionClass($thread))->getShortName() . " thread"; } if($this->shouldRecordMsg){ if((time() - $this->lastGet) >= 10) $this->shouldRecordMsg = false; // 10 secs timeout else{ if(strlen($this->shouldSendMsg) >= 10000) $this->shouldSendMsg = ""; $this->shouldSendMsg .= $color . "|" . $prefix . "|" . trim($message, "\r\n") . "\n"; } } $message = TextFormat::toANSI(TextFormat::AQUA . date("H:i:s", $now) . TextFormat::DARK_AQUA . " | " . TextFormat::RESET . $color . $threadName . ":" . $prefix . TextFormat::DARK_AQUA ." > " . TextFormat::RESET . $color . $message . TextFormat::RESET); //$message = TextFormat::toANSI(TextFormat::AQUA . "[" . date("H:i:s") . "] ". TextFormat::RESET . $color ."<".$prefix . ">" . " " . $message . TextFormat::RESET); $cleanMessage = TextFormat::clean($message); if(!Terminal::hasFormattingCodes()){ echo $cleanMessage . PHP_EOL; }else{ echo $message . PHP_EOL; } if(isset($this->consoleCallback)){ call_user_func($this->consoleCallback); } if($this->attachment instanceof \ThreadedLoggerAttachment){ $this->attachment->call($level, $message); } $this->logStream[] = date("Y-m-d", $now) . " " . $cleanMessage . "\n"; if($this->logStream->count() === 1){ $this->synchronized(function(){ $this->notify(); }); } } /*public function run(){ $this->shutdown = false; if($this->write){ $this->logResource = fopen($this->logFile, "a+b"); if(!is_resource($this->logResource)){ throw new \RuntimeException("Couldn't open log file"); } while($this->shutdown === false){ if(!$this->write) { fclose($this->logResource); break; } $this->synchronized(function(){ while($this->logStream->count() > 0){ $chunk = $this->logStream->shift(); fwrite($this->logResource, $chunk); } $this->wait(25000); }); } if($this->logStream->count() > 0){ while($this->logStream->count() > 0){ $chunk = $this->logStream->shift(); fwrite($this->logResource, $chunk); } } fclose($this->logResource); } }*/ public function run(){ $this->shutdown = false; while($this->shutdown === false){ $this->synchronized(function(){ while($this->logStream->count() > 0){ $chunk = $this->logStream->shift(); if($this->write){ $this->logResource = file_put_contents($this->logFile, $chunk, FILE_APPEND); } } $this->wait(200000); }); } if($this->logStream->count() > 0){ while($this->logStream->count() > 0){ $chunk = $this->logStream->shift(); if($this->write){ $this->logResource = file_put_contents($this->logFile, $chunk, FILE_APPEND); } } } } public function setWrite($write){ $this->write = $write; } public function setConsoleCallback($callback){ $this->consoleCallback = $callback; } } ================================================ FILE: src/pocketmine/utils/MonkeyPatch.php ================================================ setSeed($seed); } /** * @param int $seed Integer to be used as seed. */ public function setSeed($seed){ $this->seed = $seed; $this->x = self::X ^ $seed; $this->y = self::Y ^ ($seed << 17) | (($seed >> 15) & 0x7fffffff) & 0xffffffff; $this->z = self::Z ^ ($seed << 31) | (($seed >> 1) & 0x7fffffff) & 0xffffffff; $this->w = self::W ^ ($seed << 18) | (($seed >> 14) & 0x7fffffff) & 0xffffffff; } public function getSeed(){ return $this->seed; } /** * Returns an 31-bit integer (not signed) * * @return int */ public function nextInt(){ return $this->nextSignedInt() & 0x7fffffff; } /** * Returns a 32-bit integer (signed) * * @return int */ public function nextSignedInt(){ $t = ($this->x ^ ($this->x << 11)) & 0xffffffff; $this->x = $this->y; $this->y = $this->z; $this->z = $this->w; $this->w = ($this->w ^ (($this->w >> 19) & 0x7fffffff) ^ ($t ^ (($t >> 8) & 0x7fffffff))) & 0xffffffff; return $this->w; } /** * Returns a float between 0.0 and 1.0 (inclusive) * * @return float */ public function nextFloat(){ return $this->nextInt() / 0x7fffffff; } /** * Returns a float between -1.0 and 1.0 (inclusive) * * @return float */ public function nextSignedFloat(){ return $this->nextSignedInt() / 0x7fffffff; } /** * Returns a random boolean * * @return bool */ public function nextBoolean(){ return ($this->nextSignedInt() & 0x01) === 0; } /** * Returns a random integer between $start and $end * * @param int $start default 0 * @param int $end default 0x7fffffff * * @return int */ public function nextRange($start = 0, $end = 0x7fffffff){ return $start + ($this->nextInt() % ($end + 1 - $start)); } public function nextBoundedInt($bound){ return $this->nextInt() % $bound; } } ================================================ FILE: src/pocketmine/utils/Range.php ================================================ minValue = $min; $this->maxValue = $max; } public function isInRange(int $v) : bool{ return $v >= $this->minValue && $v <= $this->maxValue; } } ================================================ FILE: src/pocketmine/utils/ReversePriorityQueue.php ================================================ time = $time; } public function run(){ $start = time() + 1; $this->synchronized(function(){ $this->wait($this->time * 1000000); }); if(time() - $start >= $this->time){ echo "\nTook too long to stop, server was killed forcefully!\n"; @\pocketmine\kill(getmypid()); } } public function getThreadName(){ return "Server Killer"; } } ================================================ FILE: src/pocketmine/utils/Terminal.php ================================================ 8){ self::$COLOR_BLACK = $colors >= 256 ? `tput setaf 16` : `tput setaf 0`; self::$COLOR_DARK_BLUE = $colors >= 256 ? `tput setaf 19` : `tput setaf 4`; self::$COLOR_DARK_GREEN = $colors >= 256 ? `tput setaf 34` : `tput setaf 2`; self::$COLOR_DARK_AQUA = $colors >= 256 ? `tput setaf 37` : `tput setaf 6`; self::$COLOR_DARK_RED = $colors >= 256 ? `tput setaf 124` : `tput setaf 1`; self::$COLOR_PURPLE = $colors >= 256 ? `tput setaf 127` : `tput setaf 5`; self::$COLOR_GOLD = $colors >= 256 ? `tput setaf 214` : `tput setaf 3`; self::$COLOR_GRAY = $colors >= 256 ? `tput setaf 145` : `tput setaf 7`; self::$COLOR_DARK_GRAY = $colors >= 256 ? `tput setaf 59` : `tput setaf 8`; self::$COLOR_BLUE = $colors >= 256 ? `tput setaf 63` : `tput setaf 12`; self::$COLOR_GREEN = $colors >= 256 ? `tput setaf 83` : `tput setaf 10`; self::$COLOR_AQUA = $colors >= 256 ? `tput setaf 87` : `tput setaf 14`; self::$COLOR_RED = $colors >= 256 ? `tput setaf 203` : `tput setaf 9`; self::$COLOR_LIGHT_PURPLE = $colors >= 256 ? `tput setaf 207` : `tput setaf 13`; self::$COLOR_YELLOW = $colors >= 256 ? `tput setaf 227` : `tput setaf 11`; self::$COLOR_WHITE = $colors >= 256 ? `tput setaf 231` : `tput setaf 15`; }else{ self::$COLOR_BLACK = self::$COLOR_DARK_GRAY = `tput setaf 0`; self::$COLOR_RED = self::$COLOR_DARK_RED = `tput setaf 1`; self::$COLOR_GREEN = self::$COLOR_DARK_GREEN = `tput setaf 2`; self::$COLOR_YELLOW = self::$COLOR_GOLD = `tput setaf 3`; self::$COLOR_BLUE = self::$COLOR_DARK_BLUE = `tput setaf 4`; self::$COLOR_LIGHT_PURPLE = self::$COLOR_PURPLE = `tput setaf 5`; self::$COLOR_AQUA = self::$COLOR_DARK_AQUA = `tput setaf 6`; self::$COLOR_GRAY = self::$COLOR_WHITE = `tput setaf 7`; } } public static function init(){ if(!self::hasFormattingCodes()){ return; } switch(Utils::getOS()){ case "linux": case "mac": case "bsd": self::getEscapeCodes(); return; case "win": case "android": self::getFallbackEscapeCodes(); return; } //TODO: iOS } } ================================================ FILE: src/pocketmine/utils/TextFormat.php ================================================ $d){ if(!isset($d["text"])){ unset($newString["extra"][$k]); } } } return json_encode($newString, JSON_UNESCAPED_SLASHES); } /** * Returns an HTML-formatted string with colors/markup * * @param string|array $string * * @return string */ public static function toHTML($string){ if(!is_array($string)){ $string = self::tokenize($string); } $newString = ""; $tokens = 0; foreach($string as $token){ switch($token){ case TextFormat::BOLD: $newString .= ""; ++$tokens; break; case TextFormat::OBFUSCATED: //$newString .= ""; //++$tokens; break; case TextFormat::ITALIC: $newString .= ""; ++$tokens; break; case TextFormat::UNDERLINE: $newString .= ""; ++$tokens; break; case TextFormat::STRIKETHROUGH: $newString .= ""; ++$tokens; break; case TextFormat::RESET: $newString .= str_repeat("", $tokens); $tokens = 0; break; //Colors case TextFormat::BLACK: $newString .= ""; ++$tokens; break; case TextFormat::DARK_BLUE: $newString .= ""; ++$tokens; break; case TextFormat::DARK_GREEN: $newString .= ""; ++$tokens; break; case TextFormat::DARK_AQUA: $newString .= ""; ++$tokens; break; case TextFormat::DARK_RED: $newString .= ""; ++$tokens; break; case TextFormat::DARK_PURPLE: $newString .= ""; ++$tokens; break; case TextFormat::GOLD: $newString .= ""; ++$tokens; break; case TextFormat::GRAY: $newString .= ""; ++$tokens; break; case TextFormat::DARK_GRAY: $newString .= ""; ++$tokens; break; case TextFormat::BLUE: $newString .= ""; ++$tokens; break; case TextFormat::GREEN: $newString .= ""; ++$tokens; break; case TextFormat::AQUA: $newString .= ""; ++$tokens; break; case TextFormat::RED: $newString .= ""; ++$tokens; break; case TextFormat::LIGHT_PURPLE: $newString .= ""; ++$tokens; break; case TextFormat::YELLOW: $newString .= ""; ++$tokens; break; case TextFormat::WHITE: $newString .= ""; ++$tokens; break; default: $newString .= $token; break; } } $newString .= str_repeat("", $tokens); return $newString; } /** * Returns a string with colorized ANSI Escape codes * * @param $string * * @return string */ public static function toANSI($string){ if(!is_array($string)){ $string = self::tokenize($string); } $newString = ""; foreach($string as $token){ switch($token){ case TextFormat::BOLD: $newString .= Terminal::$FORMAT_BOLD; break; case TextFormat::OBFUSCATED: $newString .= Terminal::$FORMAT_OBFUSCATED; break; case TextFormat::ITALIC: $newString .= Terminal::$FORMAT_ITALIC; break; case TextFormat::UNDERLINE: $newString .= Terminal::$FORMAT_UNDERLINE; break; case TextFormat::STRIKETHROUGH: $newString .= Terminal::$FORMAT_STRIKETHROUGH; break; case TextFormat::RESET: $newString .= Terminal::$FORMAT_RESET; break; //Colors case TextFormat::BLACK: $newString .= Terminal::$COLOR_BLACK; break; case TextFormat::DARK_BLUE: $newString .= Terminal::$COLOR_DARK_BLUE; break; case TextFormat::DARK_GREEN: $newString .= Terminal::$COLOR_DARK_GREEN; break; case TextFormat::DARK_AQUA: $newString .= Terminal::$COLOR_DARK_AQUA; break; case TextFormat::DARK_RED: $newString .= Terminal::$COLOR_DARK_RED; break; case TextFormat::DARK_PURPLE: $newString .= Terminal::$COLOR_PURPLE; break; case TextFormat::GOLD: $newString .= Terminal::$COLOR_GOLD; break; case TextFormat::GRAY: $newString .= Terminal::$COLOR_GRAY; break; case TextFormat::DARK_GRAY: $newString .= Terminal::$COLOR_DARK_GRAY; break; case TextFormat::BLUE: $newString .= Terminal::$COLOR_BLUE; break; case TextFormat::GREEN: $newString .= Terminal::$COLOR_GREEN; break; case TextFormat::AQUA: $newString .= Terminal::$COLOR_AQUA; break; case TextFormat::RED: $newString .= Terminal::$COLOR_RED; break; case TextFormat::LIGHT_PURPLE: $newString .= Terminal::$COLOR_LIGHT_PURPLE; break; case TextFormat::YELLOW: $newString .= Terminal::$COLOR_YELLOW; break; case TextFormat::WHITE: $newString .= Terminal::$COLOR_WHITE; break; default: $newString .= $token; break; } } return $newString; } } ================================================ FILE: src/pocketmine/utils/UUID.php ================================================ parts[0] = (int) $part1; $this->parts[1] = (int) $part2; $this->parts[2] = (int) $part3; $this->parts[3] = (int) $part4; $this->version = $version === null ? ($this->parts[1] & 0xf000) >> 12 : (int) $version; } public function getVersion(){ return $this->version; } public function equals(UUID $uuid){ return $uuid->parts[0] === $this->parts[0] and $uuid->parts[1] === $this->parts[1] and $uuid->parts[2] === $this->parts[2] and $uuid->parts[3] === $this->parts[3]; } /** * Creates an UUID from an hexadecimal representation * * @param string $uuid * @param int $version * @return UUID */ public static function fromString($uuid, $version = null){ return self::fromBinary(hex2bin(str_replace("-", "", trim($uuid))), $version); } /** * Creates an UUID from a binary representation * * @param string $uuid * @param int $version * @return UUID */ public static function fromBinary($uuid, $version = null){ if(strlen($uuid) !== 16){ throw new \InvalidArgumentException("Must have exactly 16 bytes"); } return new UUID(Binary::readInt(substr($uuid, 0, 4)), Binary::readInt(substr($uuid, 4, 4)), Binary::readInt(substr($uuid, 8, 4)), Binary::readInt(substr($uuid, 12, 4)), $version); } /** * Creates an UUIDv3 from binary data or list of binary data * * @param string ...$data * @return UUID */ public static function fromData(...$data){ $hash = hash("md5", implode($data), true); return self::fromBinary($hash, 3); } public static function fromRandom(){ return self::fromData(Binary::writeInt(time()), Binary::writeShort(getmypid()), Binary::writeShort(getmyuid()), Binary::writeInt(mt_rand(-0x7fffffff, 0x7fffffff)), Binary::writeInt(mt_rand(-0x7fffffff, 0x7fffffff))); } public function toBinary(){ return Binary::writeInt($this->parts[0]) . Binary::writeInt($this->parts[1]) . Binary::writeInt($this->parts[2]) . Binary::writeInt($this->parts[3]); } public function toString(){ $hex = bin2hex(self::toBinary()); //xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx 8-4-4-12 if($this->version !== null){ return substr($hex, 0, 8) . "-" . substr($hex, 8, 4) . "-" . hexdec($this->version) . substr($hex, 13, 3) . "-8" . substr($hex, 17, 3) . "-" . substr($hex, 20, 12); } return substr($hex, 0, 8) . "-" . substr($hex, 8, 4) . "-" . substr($hex, 12, 4) . "-" . substr($hex, 16, 4) . "-" . substr($hex, 20, 12); } public function __toString(){ return $this->toString(); } } ================================================ FILE: src/pocketmine/utils/Utils.php ================================================ $v){ if($v == "00-00-00-00-00-00"){ unset($matches[1][$i]); } } $machine .= implode(" ", $matches[1]); //Mac Addresses } }elseif($os === "linux"){ if(file_exists("/etc/machine-id")){ $machine .= file_get_contents("/etc/machine-id"); }else{ @exec("ifconfig", $mac); $mac = implode("\n", $mac); if(preg_match_all("#HWaddr[ \t]{1,}([0-9a-f:]{17})#", $mac, $matches)){ foreach($matches[1] as $i => $v){ if($v == "00:00:00:00:00:00"){ unset($matches[1][$i]); } } $machine .= implode(" ", $matches[1]); //Mac Addresses } } }elseif($os === "android"){ $machine .= @file_get_contents("/system/build.prop"); }elseif($os === "mac"){ $machine .= `system_profiler SPHardwareDataType | grep UUID`; } $data = $machine . PHP_MAXPATHLEN; $data .= PHP_INT_MAX; $data .= PHP_INT_SIZE; $data .= get_current_user(); foreach(get_loaded_extensions() as $ext){ $data .= $ext . ":" . phpversion($ext); } $uuid = UUID::fromData($machine, $data); if($extra === ""){ self::$serverUniqueId = $uuid; } return $uuid; } /** * Gets the External IP using an external service, it is cached * * @param bool $force default false, force IP check even when cached * * @return string */ public static function getIP($force = false){ if(Utils::$online === false){ return false; }elseif(Utils::$ip !== false and $force !== true){ return Utils::$ip; } $ip = trim(strip_tags(Utils::getURL("https://api.ipify.org"))); if($ip){ Utils::$ip = $ip; }else{ $ip = Utils::getURL("http://www.checkip.org/"); if(preg_match('#">([0-9a-fA-F\:\.]*)#', $ip, $matches) > 0){ Utils::$ip = $matches[1]; }else{ $ip = Utils::getURL("http://checkmyip.org/"); if(preg_match('#Your IP address is ([0-9a-fA-F\:\.]*)#', $ip, $matches) > 0){ Utils::$ip = $matches[1]; }else{ $ip = trim(Utils::getURL("http://ifconfig.me/ip")); if($ip != ""){ Utils::$ip = $ip; }else{ return false; } } } } return Utils::$ip; } /** * Returns the current Operating System * Windows => win * MacOS => mac * iOS => ios * Android => android * Linux => Linux * BSD => bsd * Other => other * * @return string */ public static function getOS($recalculate = false){ if(self::$os === null or $recalculate){ $uname = php_uname("s"); if(stripos($uname, "Darwin") !== false){ if(strpos(php_uname("m"), "iP") === 0){ self::$os = "ios"; }else{ self::$os = "mac"; } }elseif(stripos($uname, "Win") !== false or $uname === "Msys"){ self::$os = "win"; }elseif(stripos($uname, "Linux") !== false){ if(@file_exists("/system/build.prop")){ self::$os = "android"; }else{ self::$os = "linux"; } }elseif(stripos($uname, "BSD") !== false or $uname === "DragonFly"){ self::$os = "bsd"; }else{ self::$os = "other"; } } return self::$os; } public static function getRealMemoryUsage(){ $stack = 0; $heap = 0; if(Utils::getOS() === "linux" or Utils::getOS() === "android"){ $mappings = file("/proc/self/maps"); foreach($mappings as $line){ if(preg_match("#([a-z0-9]+)\\-([a-z0-9]+) [rwxp\\-]{4} [a-z0-9]+ [^\\[]*\\[([a-zA-z0-9]+)\\]#", trim($line), $matches) > 0){ if(strpos($matches[3], "heap") === 0){ $heap += hexdec($matches[2]) - hexdec($matches[1]); }elseif(strpos($matches[3], "stack") === 0){ $stack += hexdec($matches[2]) - hexdec($matches[1]); } } } } return [$heap, $stack]; } public static function getMemoryUsage($advanced = false){ $reserved = memory_get_usage(); $VmSize = null; $VmRSS = null; if(Utils::getOS() === "linux" or Utils::getOS() === "android"){ $status = file_get_contents("/proc/self/status"); if(preg_match("/VmRSS:[ \t]+([0-9]+) kB/", $status, $matches) > 0){ $VmRSS = $matches[1] * 1024; } if(preg_match("/VmSize:[ \t]+([0-9]+) kB/", $status, $matches) > 0){ $VmSize = $matches[1] * 1024; } } //TODO: more OS if($VmRSS === null){ $VmRSS = memory_get_usage(); } if(!$advanced){ return $VmRSS; } if($VmSize === null){ $VmSize = memory_get_usage(true); } return [$reserved, $VmRSS, $VmSize]; } public static function getThreadCount(){ if(Utils::getOS() === "linux" or Utils::getOS() === "android"){ if(preg_match("/Threads:[ \t]+([0-9]+)/", file_get_contents("/proc/self/status"), $matches) > 0){ return (int) $matches[1]; } } //TODO: more OS return count(ThreadManager::getInstance()->getAll()) + 3; //RakLib + MainLogger + Main Thread } public static function getCoreCount($recalculate = false){ static $processors = 0; if($processors > 0 and !$recalculate){ return $processors; }else{ $processors = 0; } switch(Utils::getOS()){ case "linux": case "android": if(file_exists("/proc/cpuinfo")){ foreach(file("/proc/cpuinfo") as $l){ if(preg_match('/^processor[ \t]*:[ \t]*[0-9]+$/m', $l) > 0){ ++$processors; } } }else{ if(preg_match("/^([0-9]+)\\-([0-9]+)$/", trim(@file_get_contents("/sys/devices/system/cpu/present")), $matches) > 0){ $processors = (int) ($matches[2] - $matches[1]); } } break; case "bsd": case "mac": $processors = (int) `sysctl -n hw.ncpu`; $processors = (int) `sysctl -n hw.ncpu`; break; case "win": $processors = (int) getenv("NUMBER_OF_PROCESSORS"); break; } return $processors; } /** * Returns a prettified hexdump * * @param string $bin * * @return string */ public static function hexdump($bin){ $output = ""; $bin = str_split($bin, 16); foreach($bin as $counter => $line){ $hex = chunk_split(chunk_split(str_pad(bin2hex($line), 32, " ", STR_PAD_RIGHT), 2, " "), 24, " "); $ascii = preg_replace('#([^\x20-\x7E])#', ".", $line); $output .= str_pad(dechex($counter << 4), 4, "0", STR_PAD_LEFT) . " " . $hex . " " . $ascii . PHP_EOL; } return $output; } /** * Returns a string that can be printed, replaces non-printable characters * * @param $str * * @return string */ public static function printable($str){ if(!is_string($str)){ return gettype($str); } return preg_replace('#([^\x20-\x7E])#', '.', $str); } /** * This function tries to get all the entropy available in PHP, and distills it to get a good RNG. * * This function simply forwards to the PHP random_bytes function. * * @param int $length default 16, Number of bytes to generate * @param bool $secure default true, Generate secure distilled bytes, slower * @param bool $raw default true, returns a binary string if true, or an hexadecimal one * @param string $startEntropy default null, adds more initial entropy * @param int &$rounds Will be set to the number of rounds taken * @param int &$drop Will be set to the amount of dropped bytes * * @deprecated prefer PHP 7 random_bytes() * @return string */ public static function getRandomBytes($length = 16, $secure = true, $raw = true, $startEntropy = "", &$rounds = 0, &$drop = 0){ $raw_output = random_bytes($length); if ($raw) { return $raw_output; } else { return bin2hex($raw_output); } } /* public static function angle3D($pos1, $pos2){ $X = $pos1["x"] - $pos2["x"]; $Z = $pos1["z"] - $pos2["z"]; $dXZ = sqrt(pow($X, 2) + pow($Z, 2)); $Y = $pos1["y"] - $pos2["y"]; $hAngle = rad2deg(atan2($Z, $X) - M_PI_2); $vAngle = rad2deg(-atan2($Y, $dXZ)); return array("yaw" => $hAngle, "pitch" => $vAngle); }*/ /** * GETs an URL using cURL * * @param $page * @param int $timeout default 10 * @param array $extraHeaders * * @return bool|mixed */ public static function getURL($page, $timeout = 10, array $extraHeaders = []){ if(Utils::$online === false){ return false; } $ch = curl_init($page); curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge(["User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0 PocketMine-MP"], $extraHeaders)); curl_setopt($ch, CURLOPT_AUTOREFERER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_FORBID_REUSE, 1); curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (int) $timeout); curl_setopt($ch, CURLOPT_TIMEOUT, (int) $timeout); $ret = curl_exec($ch); curl_close($ch); return $ret; } /** * POSTs data to an URL * * @param $page * @param array|string $args * @param int $timeout * @param array $extraHeaders * * @return bool|mixed */ public static function postURL($page, $args, $timeout = 10, array $extraHeaders = []){ if(Utils::$online === false){ return false; } $ch = curl_init($page); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_FORBID_REUSE, 1); curl_setopt($ch, CURLOPT_FRESH_CONNECT, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $args); curl_setopt($ch, CURLOPT_AUTOREFERER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge(["User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0 PocketMine-MP"], $extraHeaders)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (int) $timeout); curl_setopt($ch, CURLOPT_TIMEOUT, (int) $timeout); $ret = curl_exec($ch); curl_close($ch); return $ret; } public static function javaStringHash($string){ $hash = 0; for($i = 0; $i < strlen($string); $i++){ $ord = ord($string{$i}); if($ord & 0x80){ $ord -= 0x100; } $hash = 31 * $hash + $ord; while($hash > 0x7FFFFFFF){ $hash -= 0x100000000; } while($hash < -0x80000000){ $hash += 0x100000000; } $hash &= 0xFFFFFFFF; } return $hash; } } ================================================ FILE: src/pocketmine/utils/VectorIterator.php ================================================ [3] */ private $positionQueue; private $currentBlock = 0; /** @var Vector3 */ private $currentBlockObject = null; private $currentDistance = 0; private $maxDistanceInt = 0; private $secondError; private $thirdError; private $secondStep; private $thirdStep; private $mainFace; private $secondFace; private $thirdFace; public function __construct(ChunkManager $level, Vector3 $from, Vector3 $to){ if($from->equals($to)){ $this->end = true; $this->currentBlock = -1; return; } $direction = $to->subtract($from)->normalize(); $maxDistance = $from->distance($to); $this->level = $level; $this->maxDistance = (int) $maxDistance; $this->positionQueue = new \SplFixedArray(3); $startClone = new Vector3($from->x, $from->y, $from->z); $this->currentDistance = 0; $mainDirection = 0; $secondDirection = 0; $thirdDirection = 0; $mainPosition = 0; $secondPosition = 0; $thirdPosition = 0; $pos = new Vector3($startClone->x, $startClone->y, $startClone->z); $startBlock = new Vector3(floor($pos->x), floor($pos->y), floor($pos->z)); if($this->getXLength($direction) > $mainDirection){ $this->mainFace = $this->getXFace($direction); $mainDirection = $this->getXLength($direction); $mainPosition = $this->getXPosition($direction, $startClone, $startBlock); $this->secondFace = $this->getYFace($direction); $secondDirection = $this->getYLength($direction); $secondPosition = $this->getYPosition($direction, $startClone, $startBlock); $this->thirdFace = $this->getZFace($direction); $thirdDirection = $this->getZLength($direction); $thirdPosition = $this->getZPosition($direction, $startClone, $startBlock); } if($this->getYLength($direction) > $mainDirection){ $this->mainFace = $this->getYFace($direction); $mainDirection = $this->getYLength($direction); $mainPosition = $this->getYPosition($direction, $startClone, $startBlock); $this->secondFace = $this->getZFace($direction); $secondDirection = $this->getZLength($direction); $secondPosition = $this->getZPosition($direction, $startClone, $startBlock); $this->thirdFace = $this->getXFace($direction); $thirdDirection = $this->getXLength($direction); $thirdPosition = $this->getXPosition($direction, $startClone, $startBlock); } if($this->getZLength($direction) > $mainDirection){ $this->mainFace = $this->getZFace($direction); $mainDirection = $this->getZLength($direction); $mainPosition = $this->getZPosition($direction, $startClone, $startBlock); $this->secondFace = $this->getXFace($direction); $secondDirection = $this->getXLength($direction); $secondPosition = $this->getXPosition($direction, $startClone, $startBlock); $this->thirdFace = $this->getYFace($direction); $thirdDirection = $this->getYLength($direction); $thirdPosition = $this->getYPosition($direction, $startClone, $startBlock); } $d = $mainPosition / $mainDirection; $secondd = $secondPosition - $secondDirection * $d; $thirdd = $thirdPosition - $thirdDirection * $d; $this->secondError = floor($secondd * self::$gridSize); $this->secondStep = round($secondDirection / $mainDirection * self::$gridSize); $this->thirdError = floor($thirdd * self::$gridSize); $this->thirdStep = round($thirdDirection / $mainDirection * self::$gridSize); if($this->secondError + $this->secondStep <= 0){ $this->secondError = -$this->secondStep + 1; } if($this->thirdError + $this->thirdStep <= 0){ $this->thirdError = -$this->thirdStep + 1; } $lastBlock = $startBlock->getSide(Vector3::getOppositeSide($this->mainFace)); if($this->secondError < 0){ $this->secondError += self::$gridSize; $lastBlock = $lastBlock->getSide(Vector3::getOppositeSide($this->secondFace)); } if($this->thirdError < 0){ $this->thirdError += self::$gridSize; $lastBlock = $lastBlock->getSide(Vector3::getOppositeSide($this->thirdFace)); } $this->secondError -= self::$gridSize; $this->thirdError -= self::$gridSize; $this->positionQueue[0] = $lastBlock; $this->currentBlock = -1; $this->scan(); $startBlockFound = false; for($cnt = $this->currentBlock; $cnt >= 0; --$cnt){ if($this->posEquals($this->positionQueue[$cnt], $startBlock)){ $this->currentBlock = $cnt; $startBlockFound = true; break; } } if(!$startBlockFound){ throw new \InvalidStateException("Start block missed in BlockIterator"); } $this->maxDistanceInt = round($maxDistance / (sqrt($mainDirection ** 2 + $secondDirection ** 2 + $thirdDirection ** 2) / $mainDirection)); } private function posEquals(Vector3 $a, Vector3 $b){ return $a->x === $b->x and $a->y === $b->y and $a->z === $b->z; } private function getXFace(Vector3 $direction){ return (($direction->x) > 0) ? Vector3::SIDE_EAST : Vector3::SIDE_WEST; } private function getYFace(Vector3 $direction){ return (($direction->y) > 0) ? Vector3::SIDE_UP : Vector3::SIDE_DOWN; } private function getZFace(Vector3 $direction){ return (($direction->z) > 0) ? Vector3::SIDE_SOUTH : Vector3::SIDE_NORTH; } private function getXLength(Vector3 $direction){ return abs($direction->x); } private function getYLength(Vector3 $direction){ return abs($direction->y); } private function getZLength(Vector3 $direction){ return abs($direction->z); } private function getPosition($direction, $position, $blockPosition){ return $direction > 0 ? ($position - $blockPosition) : ($blockPosition + 1 - $position); } private function getXPosition(Vector3 $direction, Vector3 $position, Vector3 $block){ return $this->getPosition($direction->x, $position->x, $block->x); } private function getYPosition(Vector3 $direction, Vector3 $position, Vector3 $block){ return $this->getPosition($direction->y, $position->y, $block->y); } private function getZPosition(Vector3 $direction, Vector3 $position, Vector3 $block){ return $this->getPosition($direction->z, $position->z, $block->z); } public function next(){ $this->scan(); if($this->currentBlock <= -1){ throw new \OutOfBoundsException; }else{ $this->currentBlockObject = $this->positionQueue[$this->currentBlock--]; } } /** * @return Block * * @throws \OutOfBoundsException */ public function current(){ if($this->currentBlockObject === null){ throw new \OutOfBoundsException; } return $this->currentBlockObject; } public function rewind(){ throw new \InvalidStateException("BlockIterator doesn't support rewind()"); } public function key(){ return $this->currentBlock - 1; } public function valid(){ $this->scan(); return $this->currentBlock !== -1; } private function scan(){ if($this->currentBlock >= 0){ return; } if($this->maxDistance !== 0 and $this->currentDistance > $this->maxDistanceInt){ $this->end = true; return; } if($this->end){ return; } ++$this->currentDistance; $this->secondError += $this->secondStep; $this->thirdError += $this->thirdStep; if($this->secondError > 0 and $this->thirdError > 0){ $this->positionQueue[2] = $this->positionQueue[0]->getSide($this->mainFace); if(($this->secondStep * $this->thirdError) < ($this->thirdStep * $this->secondError)){ $this->positionQueue[1] = $this->positionQueue[2]->getSide($this->secondFace); $this->positionQueue[0] = $this->positionQueue[1]->getSide($this->thirdFace); }else{ $this->positionQueue[1] = $this->positionQueue[2]->getSide($this->thirdFace); $this->positionQueue[0] = $this->positionQueue[1]->getSide($this->secondFace); } $this->thirdError -= self::$gridSize; $this->secondError -= self::$gridSize; $this->currentBlock = 2; }elseif($this->secondError > 0){ $this->positionQueue[1] = $this->positionQueue[0]->getSide($this->mainFace); $this->positionQueue[0] = $this->positionQueue[1]->getSide($this->secondFace); $this->secondError -= self::$gridSize; $this->currentBlock = 1; }elseif($this->thirdError > 0){ $this->positionQueue[1] = $this->positionQueue[0]->getSide($this->mainFace); $this->positionQueue[0] = $this->positionQueue[1]->getSide($this->thirdFace); $this->thirdError -= self::$gridSize; $this->currentBlock = 1; }else{ $this->positionQueue[0] = $this->positionQueue[0]->getSide($this->mainFace); $this->currentBlock = 0; } } } ================================================ FILE: src/pocketmine/utils/VersionString.php ================================================ minor = $version & 0x1F; $this->major = ($version >> 5) & 0x0F; $this->generation = ($version >> 9) & 0x0F; }else{ $this->generation = 0; $this->major = 0; $this->minor = 0; $this->development = true; $this->build = 0; } } public function getNumber(){ return (int) (($this->generation << 9) + ($this->major << 5) + $this->minor); } /** * @deprecated */ public function getStage(){ return "final"; } public function getGeneration(){ return $this->generation; } public function getMajor(){ return $this->major; } public function getMinor(){ return $this->minor; } public function getRelease(){ return $this->generation . "." . $this->major . ($this->minor > 0 ? "." . $this->minor : ""); } public function getBuild(){ return $this->build; } public function isDev(){ return $this->development === true; } public function get($build = false){ return $this->getRelease() . ($this->development === true ? "dev" : "") . (($this->build > 0 and $build === true) ? "-" . $this->build : ""); } public function __toString(){ return $this->get(); } public function compare($target, $diff = false){ if(($target instanceof VersionString) === false){ $target = new VersionString($target); } $number = $this->getNumber(); $tNumber = $target->getNumber(); if($diff === true){ return $tNumber - $number; } if($number > $tNumber){ return -1; //Target is older }elseif($number < $tNumber){ return 1; //Target is newer }elseif($target->getBuild() > $this->getBuild()){ return 1; }elseif($target->getBuild() < $this->getBuild()){ return -1; }else{ return 0; //Same version } } } ================================================ FILE: src/pocketmine/wizard/Installer.php ================================================ $native){ echo " $native => $short\n"; } do{ echo "[?] Language (eng): "; $lang = strtolower($this->getInput("eng")); if(!isset(InstallerLang::$languages[$lang])){ echo "[!] Couldn't find the language\n"; $lang = false; } $this->defaultLang = $lang; }while($lang == false); $this->lang = new InstallerLang($lang); echo "[*] " . $this->lang->language_has_been_selected . "\n"; if(!$this->showLicense()){ @\pocketmine\kill(getmypid()); exit(-1); } echo "[?] " . $this->lang->skip_installer . " (y/N): "; if(strtolower($this->getInput()) === "y"){ return; } echo "\n"; $this->welcome(); $this->generateBaseConfig(); $this->generateUserFiles(); $this->networkFunctions(); $this->endWizard(); } public function getDefaultLang(){ return $this->defaultLang; } private function showLicense(){ echo $this->lang->welcome_to_pocketmine . "\n"; echo <<lang->accept_license . " (y/N): "; if(strtolower($this->getInput("n")) != "y"){ echo "[!] " . $this->lang->you_have_to_accept_the_license . "\n"; sleep(5); return false; } return true; } private function welcome(){ echo "[*] " . $this->lang->setting_up_server_now . "\n"; echo "[*] " . $this->lang->default_values_info . "\n"; echo "[*] " . $this->lang->server_properties . "\n"; } private function generateBaseConfig(){ $config = new Config(\pocketmine\DATA . "server.properties", Config::PROPERTIES); echo "[?] " . $this->lang->name_your_server . " (" . self::DEFAULT_NAME . "): "; $server_name = $this->getInput(self::DEFAULT_NAME); $config->set("server-name", $server_name); $config->set("motd", $server_name); //MOTD is now used as server name echo "[*] " . $this->lang->port_warning . "\n"; do{ echo "[?] " . $this->lang->server_port . " (" . self::DEFAULT_PORT . "): "; $port = (int) $this->getInput(self::DEFAULT_PORT); if($port <= 0 or $port > 65535){ echo "[!] " . $this->lang->invalid_port . "\n"; } }while($port <= 0 or $port > 65535); $config->set("server-port", $port); echo "[*] " . $this->lang->online_mode_info . "\n"; echo "[?] " . $this->lang->online_mode . " (y/N): "; $config->set("online-mode", strtolower($this->getInput("y")) == "y"); echo "[?] " . $this->lang->level_name . " (" . self::DEFAULT_LEVEL_NAME . "): "; $config->set("level-name", $this->getInput(self::DEFAULT_LEVEL_NAME)); do{ echo "[?] " . $this->lang->level_type . " (" . self::DEFAULT_LEVEL_TYPE . "): "; $type = strtoupper((string) $this->getInput(self::DEFAULT_LEVEL_TYPE)); if(!in_array($type, self::LEVEL_TYPES)){ echo "[!] " . $this->lang->invalid_level_type . "\n"; } }while(!in_array($type, self::LEVEL_TYPES)); $config->set("level-type", $type); /*echo "[*] " . $this->lang->ram_warning . "\n"; echo "[?] " . $this->lang->server_ram . " (" . self::DEFAULT_MEMORY . "): "; $config->set("memory-limit", ((int) $this->getInput(self::DEFAULT_MEMORY)) . "M");*/ echo "[*] " . $this->lang->gamemode_info . "\n"; do{ echo "[?] " . $this->lang->default_gamemode . ": (" . self::DEFAULT_GAMEMODE . "): "; $gamemode = (int) $this->getInput(self::DEFAULT_GAMEMODE); }while($gamemode < 0 or $gamemode > 3); $config->set("gamemode", $gamemode); echo "[?] " . $this->lang->max_players . " (" . self::DEFAULT_PLAYERS . "): "; $config->set("max-players", (int) $this->getInput(self::DEFAULT_PLAYERS)); echo "[*] " . $this->lang->spawn_protection_info . "\n"; echo "[?] " . $this->lang->spawn_protection . " (Y/n): "; if(strtolower($this->getInput("y")) == "n"){ $config->set("spawn-protection", -1); }else{ $config->set("spawn-protection", 16); } echo "[?] " . $this->lang->announce_player_achievements . " (y/N): "; if(strtolower($this->getInput("n")) === "y"){ $config->set("announce-player-achievements", "on"); }else{ $config->set("announce-player-achievements", "off"); } $config->save(); } private function generateUserFiles(){ echo "[*] " . $this->lang->op_info . "\n"; echo "[?] " . $this->lang->op_who . ": "; $op = strtolower($this->getInput("")); if($op === ""){ echo "[!] " . $this->lang->op_warning . "\n"; }else{ $ops = new Config(\pocketmine\DATA . "ops.txt", Config::ENUM); $ops->set($op, true); $ops->save(); } echo "[*] " . $this->lang->whitelist_info . "\n"; echo "[?] " . $this->lang->whitelist_enable . " (y/N): "; $config = new Config(\pocketmine\DATA . "server.properties", Config::PROPERTIES); if(strtolower($this->getInput("n")) === "y"){ echo "[!] " . $this->lang->whitelist_warning . "\n"; $config->set("white-list", true); }else{ $config->set("white-list", false); } $config->save(); } private function networkFunctions(){ $config = new Config(\pocketmine\DATA . "server.properties", Config::PROPERTIES); echo "[!] " . $this->lang->query_warning1 . "\n"; echo "[!] " . $this->lang->query_warning2 . "\n"; echo "[?] " . $this->lang->query_disable . " (y/N): "; if(strtolower($this->getInput("n")) === "y"){ $config->set("enable-query", false); }else{ $config->set("enable-query", true); } echo "[*] " . $this->lang->rcon_info . "\n"; echo "[?] " . $this->lang->rcon_enable . " (y/N): "; if(strtolower($this->getInput("n")) === "y"){ $config->set("enable-rcon", true); $password = substr(base64_encode(random_bytes(20)), 3, 10); $config->set("rcon.password", $password); echo "[*] " . $this->lang->rcon_password . ": " . $password . "\n"; }else{ $config->set("enable-rcon", false); } /*echo "[*] " . $this->lang->usage_info . "\n"; echo "[?] " . $this->lang->usage_disable . " (y/N): "; if(strtolower($this->getInput("n")) === "y"){ $config->set("send-usage", false); }else{ $config->set("send-usage", true); }*/ $config->save(); echo "[*] " . $this->lang->ip_get . "\n"; $externalIP = Utils::getIP(); $internalIP = gethostbyname(trim(`hostname`)); echo "[!] " . $this->lang->get("ip_warning", ["{{EXTERNAL_IP}}", "{{INTERNAL_IP}}"], [$externalIP, $internalIP]) . "\n"; echo "[!] " . $this->lang->ip_confirm; $this->getInput(); } private function endWizard(){ echo "[*] " . $this->lang->you_have_finished . "\n"; echo "[*] " . $this->lang->pocketmine_plugins . "\n"; echo "[*] " . $this->lang->pocketmine_will_start . "\n\n\n"; sleep(4); } private function getInput($default = ""){ $input = trim(fgets(STDIN)); return $input === "" ? $default : $input; } } ================================================ FILE: src/pocketmine/wizard/InstallerLang.php ================================================ "English", "chs" => "简体中文", "zho" => "繁體中文", "jpn" => "日本語", "rus" => "Русский", "ita" => "Italiano", "kor" => "한국어", "deu" => "Deutsch" ]; private $texts = []; private $lang; private $langfile; public function __construct($lang = ""){ if(file_exists(\pocketmine\PATH . "src/pocketmine/lang/Installer/" . $lang . ".ini")){ $this->lang = $lang; $this->langfile = \pocketmine\PATH . "src/pocketmine/lang/Installer/" . $lang . ".ini"; }else{ $files = []; foreach(new \DirectoryIterator(\pocketmine\PATH . "src/pocketmine/lang/Installer/") as $file){ if($file->getExtension() === "ini" and substr($file->getFilename(), 0, 2) === $lang){ $files[$file->getFilename()] = $file->getSize(); } } if(count($files) > 0){ arsort($files); reset($files); $l = key($files); $l = substr($l, 0, -4); $this->lang = isset(self::$languages[$l]) ? $l : $lang; $this->langfile = \pocketmine\PATH . "src/pocketmine/lang/Installer/" . $l . ".ini"; }else{ $this->lang = "en"; $this->langfile = \pocketmine\PATH . "src/pocketmine/lang/Installer/eng.ini"; } } $this->loadLang(\pocketmine\PATH . "src/pocketmine/lang/Installer/eng.ini", "eng"); if($this->lang !== "en"){ $this->loadLang($this->langfile, $this->lang); } } public function getLang(){ return ($this->lang); } public function loadLang($langfile, $lang = "en"){ $this->texts[$lang] = []; $texts = explode("\n", str_replace(["\r", "\\/\\/"], ["", "//"], file_get_contents($langfile))); foreach($texts as $line){ $line = trim($line); if($line === ""){ continue; } $line = explode("=", $line); $this->texts[$lang][trim(array_shift($line))] = trim(str_replace(["\\n", "\\N",], "\n", implode("=", $line))); } } public function get($name, $search = [], $replace = []){ if(!isset($this->texts[$this->lang][$name])){ if($this->lang !== "en" and isset($this->texts["en"][$name])){ return $this->texts["en"][$name]; }else{ return $name; } }elseif(count($search) > 0){ return str_replace($search, $replace, $this->texts[$this->lang][$name]); }else{ return $this->texts[$this->lang][$name]; } } public function __get($name){ return $this->get($name); } } ================================================ FILE: src/raklib/Binary.php ================================================ > 56; }else{ return $b << 24 >> 24; } }else{ return $b; } } /** * Writes an unsigned/signed byte * * @param $c * * @return string */ public static function writeByte($c){ return chr($c); } /** * Reads a 16-bit unsigned big-endian number * * @param $str * * @return int */ public static function readShort($str){ return unpack("n", $str)[1]; } /** * Reads a 16-bit signed big-endian number * * @param $str * * @return int */ public static function readSignedShort($str){ if(PHP_INT_SIZE === 8){ return unpack("n", $str)[1] << 48 >> 48; }else{ return unpack("n", $str)[1] << 16 >> 16; } } /** * Writes a 16-bit signed/unsigned big-endian number * * @param $value * * @return string */ public static function writeShort($value){ return pack("n", $value); } /** * Reads a 16-bit signed/unsigned little-endian number * * @param $str * @param bool $signed * * @return int */ public static function readLShort($str, $signed = true){ $unpacked = unpack("v", $str)[1]; if($signed){ if(PHP_INT_SIZE === 8){ return $unpacked << 48 >> 48; }else{ return $unpacked << 16 >> 16; } }else{ return $unpacked; } } /** * Writes a 16-bit signed/unsigned little-endian number * * @param $value * * @return string */ public static function writeLShort($value){ return pack("v", $value); } public static function readInt($str){ if(PHP_INT_SIZE === 8){ return unpack("N", $str)[1] << 32 >> 32; }else{ return unpack("N", $str)[1]; } } public static function writeInt($value){ return pack("N", $value); } public static function readLInt($str){ if(PHP_INT_SIZE === 8){ return unpack("V", $str)[1] << 32 >> 32; }else{ return unpack("V", $str)[1]; } } public static function writeLInt($value){ return pack("V", $value); } public static function readFloat($str){ return ENDIANNESS === self::BIG_ENDIAN ? unpack("f", $str)[1] : unpack("f", strrev($str))[1]; } public static function writeFloat($value){ return ENDIANNESS === self::BIG_ENDIAN ? pack("f", $value) : strrev(pack("f", $value)); } public static function readLFloat($str){ return ENDIANNESS === self::BIG_ENDIAN ? unpack("f", strrev($str))[1] : unpack("f", $str)[1]; } public static function writeLFloat($value){ return ENDIANNESS === self::BIG_ENDIAN ? strrev(pack("f", $value)) : pack("f", $value); } public static function readDouble($str){ return ENDIANNESS === self::BIG_ENDIAN ? unpack("d", $str)[1] : unpack("d", strrev($str))[1]; } public static function writeDouble($value){ return ENDIANNESS === self::BIG_ENDIAN ? pack("d", $value) : strrev(pack("d", $value)); } public static function readLDouble($str){ return ENDIANNESS === self::BIG_ENDIAN ? unpack("d", strrev($str))[1] : unpack("d", $str)[1]; } public static function writeLDouble($value){ return ENDIANNESS === self::BIG_ENDIAN ? strrev(pack("d", $value)) : pack("d", $value); } public static function readLong($x){ if(PHP_INT_SIZE === 8){ list(, $int1, $int2) = unpack("N*", $x); return ($int1 << 32) | $int2; }else{ $value = "0"; for($i = 0; $i < 8; $i += 2){ $value = bcmul($value, "65536", 0); $value = bcadd($value, self::readShort(substr($x, $i, 2)), 0); } if(bccomp($value, "9223372036854775807") == 1){ $value = bcadd($value, "-18446744073709551616"); } return $value; } } public static function writeLong($value){ if(PHP_INT_SIZE === 8){ return pack("NN", $value >> 32, $value & 0xFFFFFFFF); }else{ $x = ""; if(bccomp($value, "0") == -1){ $value = bcadd($value, "18446744073709551616"); } $x .= self::writeShort(bcmod(bcdiv($value, "281474976710656"), "65536")); $x .= self::writeShort(bcmod(bcdiv($value, "4294967296"), "65536")); $x .= self::writeShort(bcmod(bcdiv($value, "65536"), "65536")); $x .= self::writeShort(bcmod($value, "65536")); return $x; } } public static function readLLong($str){ return self::readLong(strrev($str)); } public static function writeLLong($value){ return strrev(self::writeLong($value)); } } ================================================ FILE: src/raklib/RakLib.php ================================================ 0){ echo "[CRITICAL] Use PHP >= 7.0" . PHP_EOL; ++$errors; } if(!extension_loaded("sockets")){ echo "[CRITICAL] Unable to find the Socket extension." . PHP_EOL; ++$errors; } if(!extension_loaded("pthreads")){ echo "[CRITICAL] Unable to find the pthreads extension." . PHP_EOL; ++$errors; }else{ $pthreads_version = phpversion("pthreads"); if(substr_count($pthreads_version, ".") < 2){ $pthreads_version = "0.$pthreads_version"; } if(version_compare($pthreads_version, "3.0.0") < 0){ echo "[CRITICAL] pthreads >= 3.0.0 is required, while you have $pthreads_version."; ++$errors; } } if($errors > 0){ exit(1); //Exit with error } unset($errors); abstract class RakLib{ const VERSION = "0.8.0"; const PROTOCOL = 6; const MAGIC = "\x00\xff\xff\x00\xfe\xfe\xfe\xfe\xfd\xfd\xfd\xfd\x12\x34\x56\x78"; const PRIORITY_NORMAL = 0; const PRIORITY_IMMEDIATE = 1; const FLAG_NEED_ACK = 0b00001000; /* * Internal Packet: * int32 (length without this field) * byte (packet ID) * payload */ /* * ENCAPSULATED payload: * byte (identifier length) * byte[] (identifier) * byte (flags, last 3 bits, priority) * payload (binary internal EncapsulatedPacket) */ const PACKET_ENCAPSULATED = 0x01; /* * OPEN_SESSION payload: * byte (identifier length) * byte[] (identifier) * byte (address length) * byte[] (address) * short (port) * long (clientID) */ const PACKET_OPEN_SESSION = 0x02; /* * CLOSE_SESSION payload: * byte (identifier length) * byte[] (identifier) * string (reason) */ const PACKET_CLOSE_SESSION = 0x03; /* * INVALID_SESSION payload: * byte (identifier length) * byte[] (identifier) */ const PACKET_INVALID_SESSION = 0x04; /* TODO: implement this * SEND_QUEUE payload: * byte (identifier length) * byte[] (identifier) */ const PACKET_SEND_QUEUE = 0x05; /* * ACK_NOTIFICATION payload: * byte (identifier length) * byte[] (identifier) * int (identifierACK) */ const PACKET_ACK_NOTIFICATION = 0x06; /* * SET_OPTION payload: * byte (option name length) * byte[] (option name) * byte[] (option value) */ const PACKET_SET_OPTION = 0x07; /* * RAW payload: * byte (address length) * byte[] (address from/to) * short (port) * byte[] (payload) */ const PACKET_RAW = 0x08; /* * RAW payload: * byte (address length) * byte[] (address) * int (timeout) */ const PACKET_BLOCK_ADDRESS = 0x09; /* * RAW payload: * byte (address length) * byte[] (address) */ const PACKET_UNBLOCK_ADDRESS = 0x0a; /* * No payload * * Sends the disconnect message, removes sessions correctly, closes sockets. */ const PACKET_SHUTDOWN = 0x7e; /* * No payload * * Leaves everything as-is and halts, other Threads can be in a post-crash condition. */ const PACKET_EMERGENCY_SHUTDOWN = 0x7f; public static function bootstrap(\ClassLoader $loader){ $loader->addPath(dirname(__FILE__) . DIRECTORY_SEPARATOR . ".."); } } ================================================ FILE: src/raklib/protocol/ACK.php ================================================ abstract class AcknowledgePacket extends Packet{ /** @var int[] */ public $packets = []; public function encode(){ parent::encode(); $payload = ""; sort($this->packets, SORT_NUMERIC); $count = count($this->packets); $records = 0; if($count > 0){ $pointer = 1; $start = $this->packets[0]; $last = $this->packets[0]; while($pointer < $count){ $current = $this->packets[$pointer++]; $diff = $current - $last; if($diff === 1){ $last = $current; }elseif($diff > 1){ //Forget about duplicated packets (bad queues?) if($start === $last){ $payload .= "\x01"; $payload .= Binary::writeLTriad($start); $start = $last = $current; }else{ $payload .= "\x00"; $payload .= Binary::writeLTriad($start); $payload .= Binary::writeLTriad($last); $start = $last = $current; } ++$records; } } if($start === $last){ $payload .= "\x01"; $payload .= Binary::writeLTriad($start); }else{ $payload .= "\x00"; $payload .= Binary::writeLTriad($start); $payload .= Binary::writeLTriad($last); } ++$records; } $this->putShort($records); $this->buffer .= $payload; } public function decode(){ parent::decode(); $count = $this->getShort(); $this->packets = []; $cnt = 0; for($i = 0; $i < $count and !$this->feof() and $cnt < 4096; ++$i){ if($this->getByte() === 0){ $start = $this->getLTriad(); $end = $this->getLTriad(); if(($end - $start) > 512){ $end = $start + 512; } for($c = $start; $c <= $end; ++$c){ $this->packets[$cnt++] = $c; } }else{ $this->packets[$cnt++] = $this->getLTriad(); } } } public function clean(){ $this->packets = []; return parent::clean(); } } ================================================ FILE: src/raklib/protocol/CLIENT_CONNECT_DataPacket.php ================================================ class CLIENT_CONNECT_DataPacket extends Packet{ public static $ID = 0x09; public $clientID; public $sendPing; public $useSecurity = false; public function encode(){ parent::encode(); $this->putLong($this->clientID); $this->putLong($this->sendPing); $this->putByte($this->useSecurity ? 1 : 0); } public function decode(){ parent::decode(); $this->clientID = $this->getLong(); $this->sendPing = $this->getLong(); $this->useSecurity = $this->getByte() > 0; } } ================================================ FILE: src/raklib/protocol/CLIENT_DISCONNECT_DataPacket.php ================================================ class CLIENT_HANDSHAKE_DataPacket extends Packet{ public static $ID = 0x13; public $address; public $port; public $systemAddresses = []; public $sendPing; public $sendPong; public function encode(){ } public function decode(){ parent::decode(); $this->getAddress($this->address, $this->port); for($i = 0; $i < 10; ++$i){ $this->getAddress($addr, $port, $version); $this->systemAddresses[$i] = [$addr, $port, $version]; } $this->sendPing = $this->getLong(); $this->sendPong = $this->getLong(); } } ================================================ FILE: src/raklib/protocol/DATA_PACKET_0.php ================================================ abstract class DataPacket extends Packet{ /** @var EncapsulatedPacket[] */ public $packets = []; public $seqNumber; public function encode(){ parent::encode(); $this->putLTriad($this->seqNumber); foreach($this->packets as $packet){ $this->put($packet instanceof EncapsulatedPacket ? $packet->toBinary() : (string) $packet); } } public function length(){ $length = 4; foreach($this->packets as $packet){ $length += $packet instanceof EncapsulatedPacket ? $packet->getTotalLength() : strlen($packet); } return $length; } public function decode(){ parent::decode(); $this->seqNumber = $this->getLTriad(); while(!$this->feof()){ $offset = 0; $data = substr($this->buffer, $this->offset); $packet = EncapsulatedPacket::fromBinary($data, false, $offset); $this->offset += $offset; if(strlen($packet->buffer) === 0){ break; } $this->packets[] = $packet; } } public function clean(){ $this->packets = []; $this->seqNumber = null; return parent::clean(); } } ================================================ FILE: src/raklib/protocol/EncapsulatedPacket.php ================================================ class EncapsulatedPacket{ public $reliability; public $hasSplit = false; public $length = 0; public $messageIndex = null; public $orderIndex = null; public $orderChannel = null; public $splitCount = null; public $splitID = null; public $splitIndex = null; public $buffer; public $needACK = false; public $identifierACK = null; /** * @param string $binary * @param bool $internal * @param int &$offset * * @return EncapsulatedPacket */ public static function fromBinary($binary, $internal = false, &$offset = null){ $packet = new EncapsulatedPacket(); $flags = ord($binary{0}); $packet->reliability = $reliability = ($flags & 0b11100000) >> 5; $packet->hasSplit = $hasSplit = ($flags & 0b00010000) > 0; if($internal){ $length = Binary::readInt(substr($binary, 1, 4)); $packet->identifierACK = Binary::readInt(substr($binary, 5, 4)); $offset = 9; }else{ $length = (int) ceil(Binary::readShort(substr($binary, 1, 2)) / 8); $offset = 3; $packet->identifierACK = null; } if($reliability > PacketReliability::UNRELIABLE){ if($reliability >= PacketReliability::RELIABLE and $reliability !== PacketReliability::UNRELIABLE_WITH_ACK_RECEIPT){ $packet->messageIndex = Binary::readLTriad(substr($binary, $offset, 3)); $offset += 3; } if($reliability <= PacketReliability::RELIABLE_SEQUENCED and $reliability !== PacketReliability::RELIABLE){ $packet->orderIndex = Binary::readLTriad(substr($binary, $offset, 3)); $offset += 3; $packet->orderChannel = ord($binary{$offset++}); } } if($hasSplit){ $packet->splitCount = Binary::readInt(substr($binary, $offset, 4)); $offset += 4; $packet->splitID = Binary::readShort(substr($binary, $offset, 2)); $offset += 2; $packet->splitIndex = Binary::readInt(substr($binary, $offset, 4)); $offset += 4; } $packet->buffer = substr($binary, $offset, $length); $offset += $length; return $packet; } public function getTotalLength(){ return 3 + strlen($this->buffer) + ($this->messageIndex !== null ? 3 : 0) + ($this->orderIndex !== null ? 4 : 0) + ($this->hasSplit ? 10 : 0); } /** * @param bool $internal * * @return string */ public function toBinary($internal = false){ return chr(($this->reliability << 5) | ($this->hasSplit ? 0b00010000 : 0)) . ($internal ? Binary::writeInt(strlen($this->buffer)) . Binary::writeInt($this->identifierACK) : Binary::writeShort(strlen($this->buffer) << 3)) . ($this->reliability > PacketReliability::UNRELIABLE ? (($this->reliability >= PacketReliability::RELIABLE and $this->reliability !== PacketReliability::UNRELIABLE_WITH_ACK_RECEIPT) ? Binary::writeLTriad($this->messageIndex) : "") . (($this->reliability <= PacketReliability::RELIABLE_SEQUENCED and $this->reliability !== PacketReliability::RELIABLE) ? Binary::writeLTriad($this->orderIndex) . chr($this->orderChannel) : "") : "" ) . ($this->hasSplit ? Binary::writeInt($this->splitCount) . Binary::writeShort($this->splitID) . Binary::writeInt($this->splitIndex) : "") . $this->buffer; } public function __toString(){ return $this->toBinary(); } } ================================================ FILE: src/raklib/protocol/NACK.php ================================================ use raklib\RakLib; class OPEN_CONNECTION_REPLY_1 extends Packet{ public static $ID = 0x06; public $serverID; public $mtuSize; public function encode(){ parent::encode(); $this->put(RakLib::MAGIC); $this->putLong($this->serverID); $this->putByte(0); //Server security $this->putShort($this->mtuSize); } public function decode(){ parent::decode(); $this->offset += 16; //Magic $this->serverID = $this->getLong(); $this->getByte(); //security $this->mtuSize = $this->getShort(); } } ================================================ FILE: src/raklib/protocol/OPEN_CONNECTION_REPLY_2.php ================================================ use raklib\RakLib; class OPEN_CONNECTION_REPLY_2 extends Packet{ public static $ID = 0x08; public $serverID; public $clientAddress; public $clientPort; public $mtuSize; public function encode(){ parent::encode(); $this->put(RakLib::MAGIC); $this->putLong($this->serverID); $this->putAddress($this->clientAddress, $this->clientPort, 4); $this->putShort($this->mtuSize); $this->putByte(0); //server security } public function decode(){ parent::decode(); $this->offset += 16; //Magic $this->serverID = $this->getLong(); $this->getAddress($this->clientAddress, $this->clientPort); $this->mtuSize = $this->getShort(); //server security } } ================================================ FILE: src/raklib/protocol/OPEN_CONNECTION_REQUEST_1.php ================================================ use raklib\RakLib; class OPEN_CONNECTION_REQUEST_1 extends Packet{ public static $ID = 0x05; public $protocol = RakLib::PROTOCOL; public $mtuSize; public function encode(){ parent::encode(); $this->put(RakLib::MAGIC); $this->putByte($this->protocol); $this->put(str_repeat(chr(0x00), $this->mtuSize - 18)); } public function decode(){ parent::decode(); $this->offset += 16; //Magic $this->protocol = $this->getByte(); $this->mtuSize = strlen($this->get(true)) + 18; } } ================================================ FILE: src/raklib/protocol/OPEN_CONNECTION_REQUEST_2.php ================================================ use raklib\RakLib; class OPEN_CONNECTION_REQUEST_2 extends Packet{ public static $ID = 0x07; public $clientID; public $serverAddress; public $serverPort; public $mtuSize; public function encode(){ parent::encode(); $this->put(RakLib::MAGIC); $this->putAddress($this->serverAddress, $this->serverPort, 4); $this->putShort($this->mtuSize); $this->putLong($this->clientID); } public function decode(){ parent::decode(); $this->offset += 16; //Magic $this->getAddress($this->serverAddress, $this->serverPort); $this->mtuSize = $this->getShort(); $this->clientID = $this->getLong(); } } ================================================ FILE: src/raklib/protocol/PING_DataPacket.php ================================================ class PING_DataPacket extends Packet{ public static $ID = 0x00; public $pingID; public function encode(){ parent::encode(); $this->putLong($this->pingID); } public function decode(){ parent::decode(); $this->pingID = $this->getLong(); } } ================================================ FILE: src/raklib/protocol/PONG_DataPacket.php ================================================ class PONG_DataPacket extends Packet{ public static $ID = 0x03; public $pingID; public function encode(){ parent::encode(); $this->putLong($this->pingID); } public function decode(){ parent::decode(); $this->pingID = $this->getLong(); } } ================================================ FILE: src/raklib/protocol/Packet.php ================================================ abstract class Packet{ public static $ID = -1; protected $offset = 0; public $buffer; public $sendTime; protected function get($len){ if($len < 0){ $this->offset = strlen($this->buffer) - 1; return ""; }elseif($len === true){ return substr($this->buffer, $this->offset); } $buffer = ""; for(; $len > 0; --$len, ++$this->offset){ $buffer .= $this->buffer{$this->offset}; } return $buffer; } protected function getLong($signed = true){ return Binary::readLong($this->get(8)); } protected function getInt(){ return Binary::readInt($this->get(4)); } protected function getShort($signed = true){ return $signed ? Binary::readSignedShort($this->get(2)) : Binary::readShort($this->get(2)); } protected function getTriad(){ return Binary::readTriad($this->get(3)); } protected function getLTriad(){ return Binary::readLTriad($this->get(3)); } protected function getByte(){ return ord($this->buffer{$this->offset++}); } protected function getString(){ return $this->get($this->getShort()); } protected function getAddress(&$addr, &$port, &$version = null){ $version = $this->getByte(); if($version === 4){ $addr = ((~$this->getByte()) & 0xff) .".". ((~$this->getByte()) & 0xff) .".". ((~$this->getByte()) & 0xff) .".". ((~$this->getByte()) & 0xff); $port = $this->getShort(false); }else{ //TODO: IPv6 } } protected function feof(){ return !isset($this->buffer{$this->offset}); } protected function put($str){ $this->buffer .= $str; } protected function putLong($v){ $this->buffer .= Binary::writeLong($v); } protected function putInt($v){ $this->buffer .= Binary::writeInt($v); } protected function putShort($v){ $this->buffer .= Binary::writeShort($v); } protected function putTriad($v){ $this->buffer .= Binary::writeTriad($v); } protected function putLTriad($v){ $this->buffer .= Binary::writeLTriad($v); } protected function putByte($v){ $this->buffer .= chr($v); } protected function putString($v){ $this->putShort(strlen($v)); $this->put($v); } protected function putAddress($addr, $port, $version = 4){ $this->putByte($version); if($version === 4){ foreach(explode(".", $addr) as $b){ $this->putByte((~((int) $b)) & 0xff); } $this->putShort($port); }else{ //IPv6 } } public function encode(){ $this->buffer = chr(static::$ID); } public function decode(){ $this->offset = 1; } public function clean(){ $this->buffer = null; $this->offset = 0; $this->sendTime = null; return $this; } } ================================================ FILE: src/raklib/protocol/PacketReliability.php ================================================ class SERVER_HANDSHAKE_DataPacket extends Packet{ public static $ID = 0x10; public $address; public $port; public $systemAddresses = [ ["127.0.0.1", 0, 4], ["0.0.0.0", 0, 4], ["0.0.0.0", 0, 4], ["0.0.0.0", 0, 4], ["0.0.0.0", 0, 4], ["0.0.0.0", 0, 4], ["0.0.0.0", 0, 4], ["0.0.0.0", 0, 4], ["0.0.0.0", 0, 4], ["0.0.0.0", 0, 4] ]; public $sendPing; public $sendPong; public function encode(){ parent::encode(); $this->putAddress($this->address, $this->port, 4); $this->putShort(0); for($i = 0; $i < 10; ++$i){ $this->putAddress($this->systemAddresses[$i][0], $this->systemAddresses[$i][1], $this->systemAddresses[$i][2]); } $this->putLong($this->sendPing); $this->putLong($this->sendPong); } public function decode(){ parent::decode(); //TODO, not needed yet } } ================================================ FILE: src/raklib/protocol/UNCONNECTED_PING.php ================================================ use raklib\RakLib; class UNCONNECTED_PING extends Packet{ public static $ID = 0x01; public $pingID; public function encode(){ parent::encode(); $this->putLong($this->pingID); $this->put(RakLib::MAGIC); } public function decode(){ parent::decode(); $this->pingID = $this->getLong(); //magic } } ================================================ FILE: src/raklib/protocol/UNCONNECTED_PING_OPEN_CONNECTIONS.php ================================================ class UNCONNECTED_PING_OPEN_CONNECTIONS extends UNCONNECTED_PING{ public static $ID = 0x02; } ================================================ FILE: src/raklib/protocol/UNCONNECTED_PONG.php ================================================ use raklib\RakLib; class UNCONNECTED_PONG extends Packet{ public static $ID = 0x1c; public $pingID; public $serverID; public $serverName; public function encode(){ parent::encode(); $this->putLong($this->pingID); $this->putLong($this->serverID); $this->put(RakLib::MAGIC); $this->putString($this->serverName); } public function decode(){ parent::decode(); $this->pingID = $this->getLong(); $this->serverID = $this->getLong(); $this->offset += 16; //magic $this->serverName = $this->getString(); } } ================================================ FILE: src/raklib/server/RakLibServer.php ================================================ port = (int) $port; if($port < 1 or $port > 65536){ throw new \Exception("Invalid port range"); } $this->interface = $interface; $this->logger = $logger; $this->loader = $loader; $loadPaths = []; $this->addDependency($loadPaths, new \ReflectionClass($logger)); $this->addDependency($loadPaths, new \ReflectionClass($loader)); $this->loadPaths = array_reverse($loadPaths); $this->shutdown = false; $this->externalQueue = new \Threaded; $this->internalQueue = new \Threaded; if(\Phar::running(true) !== ""){ $this->mainPath = \Phar::running(true); }else{ $this->mainPath = \getcwd() . DIRECTORY_SEPARATOR; } $this->start(); } protected function addDependency(array &$loadPaths, \ReflectionClass $dep){ if($dep->getFileName() !== false){ $loadPaths[$dep->getName()] = $dep->getFileName(); } if($dep->getParentClass() instanceof \ReflectionClass){ $this->addDependency($loadPaths, $dep->getParentClass()); } foreach($dep->getInterfaces() as $interface){ $this->addDependency($loadPaths, $interface); } } public function isShutdown(){ return $this->shutdown === true; } public function shutdown(){ $this->shutdown = true; } public function getPort(){ return $this->port; } public function getInterface(){ return $this->interface; } /** * @return \ThreadedLogger */ public function getLogger(){ return $this->logger; } /** * @return \Threaded */ public function getExternalQueue(){ return $this->externalQueue; } /** * @return \Threaded */ public function getInternalQueue(){ return $this->internalQueue; } public function pushMainToThreadPacket($str){ $this->internalQueue[] = $str; } public function readMainToThreadPacket(){ return $this->internalQueue->shift(); } public function pushThreadToMainPacket($str){ $this->externalQueue[] = $str; } public function readThreadToMainPacket(){ return $this->externalQueue->shift(); } public function shutdownHandler(){ if($this->shutdown !== true){ $this->getLogger()->emergency("RakLib crashed!"); } } public function errorHandler($errno, $errstr, $errfile, $errline, $context, $trace = null){ if(error_reporting() === 0){ return false; } $errorConversion = [ E_ERROR => "E_ERROR", E_WARNING => "E_WARNING", E_PARSE => "E_PARSE", E_NOTICE => "E_NOTICE", E_CORE_ERROR => "E_CORE_ERROR", E_CORE_WARNING => "E_CORE_WARNING", E_COMPILE_ERROR => "E_COMPILE_ERROR", E_COMPILE_WARNING => "E_COMPILE_WARNING", E_USER_ERROR => "E_USER_ERROR", E_USER_WARNING => "E_USER_WARNING", E_USER_NOTICE => "E_USER_NOTICE", E_STRICT => "E_STRICT", E_RECOVERABLE_ERROR => "E_RECOVERABLE_ERROR", E_DEPRECATED => "E_DEPRECATED", E_USER_DEPRECATED => "E_USER_DEPRECATED", ]; $errno = isset($errorConversion[$errno]) ? $errorConversion[$errno] : $errno; if(($pos = strpos($errstr, "\n")) !== false){ $errstr = substr($errstr, 0, $pos); } $oldFile = $errfile; $errfile = $this->cleanPath($errfile); $this->getLogger()->debug("An $errno error happened: \"$errstr\" in \"$errfile\" at line $errline"); foreach(($trace = $this->getTrace($trace === null ? 3 : 0, $trace)) as $i => $line){ $this->getLogger()->debug($line); } return true; } public function getTrace($start = 1, $trace = null){ if($trace === null){ if(function_exists("xdebug_get_function_stack")){ $trace = array_reverse(xdebug_get_function_stack()); }else{ $e = new \Exception(); $trace = $e->getTrace(); } } $messages = []; $j = 0; for($i = (int) $start; isset($trace[$i]); ++$i, ++$j){ $params = ""; if(isset($trace[$i]["args"]) or isset($trace[$i]["params"])){ if(isset($trace[$i]["args"])){ $args = $trace[$i]["args"]; }else{ $args = $trace[$i]["params"]; } foreach($args as $name => $value){ $params .= (is_object($value) ? get_class($value) . " " . (method_exists($value, "__toString") ? $value->__toString() : "object") : gettype($value) . " " . @strval($value)) . ", "; } } $messages[] = "#$j " . (isset($trace[$i]["file"]) ? $this->cleanPath($trace[$i]["file"]) : "") . "(" . (isset($trace[$i]["line"]) ? $trace[$i]["line"] : "") . "): " . (isset($trace[$i]["class"]) ? $trace[$i]["class"] . (($trace[$i]["type"] === "dynamic" or $trace[$i]["type"] === "->") ? "->" : "::") : "") . $trace[$i]["function"] . "(" . substr($params, 0, -2) . ")"; } return $messages; } public function cleanPath($path){ return rtrim(str_replace(["\\", ".php", "phar://", rtrim(str_replace(["\\", "phar://"], ["/", ""], $this->mainPath), "/")], ["/", "", "", ""], $path), "/"); } public function run(){ //Load removed dependencies, can't use require_once() foreach($this->loadPaths as $name => $path){ if(!class_exists($name, false) and !interface_exists($name, false)){ require($path); } } $this->loader->register(true); gc_enable(); error_reporting(-1); ini_set("display_errors", 1); ini_set("display_startup_errors", 1); set_error_handler([$this, "errorHandler"], E_ALL); register_shutdown_function([$this, "shutdownHandler"]); $socket = new UDPServerSocket($this->getLogger(), $this->port, $this->interface); new SessionManager($this, $socket); } } ================================================ FILE: src/raklib/server/ServerHandler.php ================================================ server = $server; $this->instance = $instance; } public function sendEncapsulated($identifier, EncapsulatedPacket $packet, $flags = RakLib::PRIORITY_NORMAL){ $buffer = chr(RakLib::PACKET_ENCAPSULATED) . chr(strlen($identifier)) . $identifier . chr($flags) . $packet->toBinary(true); $this->server->pushMainToThreadPacket($buffer); } public function sendRaw($address, $port, $payload){ $buffer = chr(RakLib::PACKET_RAW) . chr(strlen($address)) . $address . Binary::writeShort($port) . $payload; $this->server->pushMainToThreadPacket($buffer); } public function closeSession($identifier, $reason){ $buffer = chr(RakLib::PACKET_CLOSE_SESSION) . chr(strlen($identifier)) . $identifier . chr(strlen($reason)) . $reason; $this->server->pushMainToThreadPacket($buffer); } public function sendOption($name, $value){ $buffer = chr(RakLib::PACKET_SET_OPTION) . chr(strlen($name)) . $name . $value; $this->server->pushMainToThreadPacket($buffer); } public function blockAddress($address, $timeout){ $buffer = chr(RakLib::PACKET_BLOCK_ADDRESS) . chr(strlen($address)) . $address . Binary::writeInt($timeout); $this->server->pushMainToThreadPacket($buffer); } public function unblockAddress($address){ $buffer = chr(RakLib::PACKET_UNBLOCK_ADDRESS) . chr(strlen($address)) . $address; $this->server->pushMainToThreadPacket($buffer); } public function shutdown(){ $buffer = chr(RakLib::PACKET_SHUTDOWN); $this->server->pushMainToThreadPacket($buffer); $this->server->shutdown(); $this->server->synchronized(function(){ if($this->server !== null){ #pthreadssucks $this->server->wait(20000); } }); $this->server->join(); } public function emergencyShutdown(){ $this->server->shutdown(); $this->server->pushMainToThreadPacket("\x7f"); //RakLib::PACKET_EMERGENCY_SHUTDOWN } protected function invalidSession($identifier){ $buffer = chr(RakLib::PACKET_INVALID_SESSION) . chr(strlen($identifier)) . $identifier; $this->server->pushMainToThreadPacket($buffer); } /** * @return bool */ public function handlePacket(){ if(strlen($packet = $this->server->readThreadToMainPacket()) > 0){ $id = ord($packet{0}); $offset = 1; if($id === RakLib::PACKET_ENCAPSULATED){ $len = ord($packet{$offset++}); $identifier = substr($packet, $offset, $len); $offset += $len; $flags = ord($packet{$offset++}); $buffer = substr($packet, $offset); $this->instance->handleEncapsulated($identifier, EncapsulatedPacket::fromBinary($buffer, true), $flags); }elseif($id === RakLib::PACKET_RAW){ $len = ord($packet{$offset++}); $address = substr($packet, $offset, $len); $offset += $len; $port = Binary::readShort(substr($packet, $offset, 2)); $offset += 2; $payload = substr($packet, $offset); $this->instance->handleRaw($address, $port, $payload); }elseif($id === RakLib::PACKET_SET_OPTION){ $len = ord($packet{$offset++}); $name = substr($packet, $offset, $len); $offset += $len; $value = substr($packet, $offset); $this->instance->handleOption($name, $value); }elseif($id === RakLib::PACKET_OPEN_SESSION){ $len = ord($packet{$offset++}); $identifier = substr($packet, $offset, $len); $offset += $len; $len = ord($packet{$offset++}); $address = substr($packet, $offset, $len); $offset += $len; $port = Binary::readShort(substr($packet, $offset, 2)); $offset += 2; $clientID = Binary::readLong(substr($packet, $offset, 8)); $this->instance->openSession($identifier, $address, $port, $clientID); }elseif($id === RakLib::PACKET_CLOSE_SESSION){ $len = ord($packet{$offset++}); $identifier = substr($packet, $offset, $len); $offset += $len; $len = ord($packet{$offset++}); $reason = substr($packet, $offset, $len); $this->instance->closeSession($identifier, $reason); }elseif($id === RakLib::PACKET_INVALID_SESSION){ $len = ord($packet{$offset++}); $identifier = substr($packet, $offset, $len); $this->instance->closeSession($identifier, "Invalid session"); }elseif($id === RakLib::PACKET_ACK_NOTIFICATION){ $len = ord($packet{$offset++}); $identifier = substr($packet, $offset, $len); $offset += $len; $identifierACK = Binary::readInt(substr($packet, $offset, 4)); $this->instance->notifyACK($identifier, $identifierACK); } return true; } return false; } } ================================================ FILE: src/raklib/server/ServerInstance.php ================================================ sessionManager = $sessionManager; $this->address = $address; $this->port = $port; $this->sendQueue = new DATA_PACKET_4(); $this->lastUpdate = microtime(true); $this->startTime = microtime(true); $this->isActive = false; $this->windowStart = -1; $this->windowEnd = self::$WINDOW_SIZE; $this->reliableWindowStart = 0; $this->reliableWindowEnd = self::$WINDOW_SIZE; for($i = 0; $i < 32; ++$i){ $this->channelIndex[$i] = 0; } } public function getAddress(){ return $this->address; } public function getPort(){ return $this->port; } public function getID(){ return $this->id; } public function update($time){ if(!$this->isActive and ($this->lastUpdate + 10) < $time){ $this->disconnect("timeout"); return; } $this->isActive = false; if(count($this->ACKQueue) > 0){ $pk = new ACK(); $pk->packets = $this->ACKQueue; $this->sendPacket($pk); $this->ACKQueue = []; } if(count($this->NACKQueue) > 0){ $pk = new NACK(); $pk->packets = $this->NACKQueue; $this->sendPacket($pk); $this->NACKQueue = []; } if(count($this->packetToSend) > 0){ $limit = 16; foreach($this->packetToSend as $k => $pk){ $pk->sendTime = $time; $pk->encode(); $this->recoveryQueue[$pk->seqNumber] = $pk; unset($this->packetToSend[$k]); $this->sendPacket($pk); if(--$limit <= 0){ break; } } if(count($this->packetToSend) > self::$WINDOW_SIZE){ $this->packetToSend = []; } } if(count($this->needACK) > 0){ foreach($this->needACK as $identifierACK => $indexes){ if(count($indexes) === 0){ unset($this->needACK[$identifierACK]); $this->sessionManager->notifyACK($this, $identifierACK); } } } foreach($this->recoveryQueue as $seq => $pk){ if($pk->sendTime < (time() - 8)){ $this->packetToSend[] = $pk; unset($this->recoveryQueue[$seq]); }else{ break; } } foreach($this->receivedWindow as $seq => $bool){ if($seq < $this->windowStart){ unset($this->receivedWindow[$seq]); }else{ break; } } $this->sendQueue(); } public function disconnect($reason = "unknown"){ $this->sessionManager->removeSession($this, $reason); } private function sendPacket(Packet $packet){ $this->sessionManager->sendPacket($packet, $this->address, $this->port); } public function sendQueue(){ if(count($this->sendQueue->packets) > 0){ $this->sendQueue->seqNumber = $this->sendSeqNumber++; $this->sendPacket($this->sendQueue); $this->sendQueue->sendTime = microtime(true); $this->recoveryQueue[$this->sendQueue->seqNumber] = $this->sendQueue; $this->sendQueue = new DATA_PACKET_4(); } } /** * @param EncapsulatedPacket $pk * @param int $flags */ private function addToQueue(EncapsulatedPacket $pk, $flags = RakLib::PRIORITY_NORMAL){ $priority = $flags & 0b0000111; if($pk->needACK and $pk->messageIndex !== null){ $this->needACK[$pk->identifierACK][$pk->messageIndex] = $pk->messageIndex; } if($priority === RakLib::PRIORITY_IMMEDIATE){ //Skip queues $packet = new DATA_PACKET_0(); $packet->seqNumber = $this->sendSeqNumber++; if($pk->needACK){ $packet->packets[] = clone $pk; $pk->needACK = false; }else{ $packet->packets[] = $pk->toBinary(); } $this->sendPacket($packet); $packet->sendTime = microtime(true); $this->recoveryQueue[$packet->seqNumber] = $packet; return; } $length = $this->sendQueue->length(); if($length + $pk->getTotalLength() > $this->mtuSize){ $this->sendQueue(); } if($pk->needACK){ $this->sendQueue->packets[] = clone $pk; $pk->needACK = false; }else{ $this->sendQueue->packets[] = $pk->toBinary(); } } /** * @param EncapsulatedPacket $packet * @param int $flags */ public function addEncapsulatedToQueue(EncapsulatedPacket $packet, $flags = RakLib::PRIORITY_NORMAL){ if(($packet->needACK = ($flags & RakLib::FLAG_NEED_ACK) > 0) === true){ $this->needACK[$packet->identifierACK] = []; } if( $packet->reliability === PacketReliability::RELIABLE or $packet->reliability === PacketReliability::RELIABLE_ORDERED or $packet->reliability === PacketReliability::RELIABLE_SEQUENCED or $packet->reliability === PacketReliability::RELIABLE_WITH_ACK_RECEIPT or $packet->reliability === PacketReliability::RELIABLE_ORDERED_WITH_ACK_RECEIPT ){ $packet->messageIndex = $this->messageIndex++; if($packet->reliability === PacketReliability::RELIABLE_ORDERED){ $packet->orderIndex = $this->channelIndex[$packet->orderChannel]++; } } if($packet->getTotalLength() + 4 > $this->mtuSize){ $buffers = str_split($packet->buffer, $this->mtuSize - 34); $splitID = ++$this->splitID % 65536; foreach($buffers as $count => $buffer){ $pk = new EncapsulatedPacket(); $pk->splitID = $splitID; $pk->hasSplit = true; $pk->splitCount = count($buffers); $pk->reliability = $packet->reliability; $pk->splitIndex = $count; $pk->buffer = $buffer; if($count > 0){ $pk->messageIndex = $this->messageIndex++; }else{ $pk->messageIndex = $packet->messageIndex; } if($pk->reliability === PacketReliability::RELIABLE_ORDERED){ $pk->orderChannel = $packet->orderChannel; $pk->orderIndex = $packet->orderIndex; } $this->addToQueue($pk, $flags | RakLib::PRIORITY_IMMEDIATE); } }else{ $this->addToQueue($packet, $flags); } } private function handleSplit(EncapsulatedPacket $packet){ if($packet->splitCount >= self::MAX_SPLIT_SIZE or $packet->splitIndex >= self::MAX_SPLIT_SIZE or $packet->splitIndex < 0){ return; } if(!isset($this->splitPackets[$packet->splitID])){ if(count($this->splitPackets) >= self::MAX_SPLIT_COUNT){ return; } $this->splitPackets[$packet->splitID] = [$packet->splitIndex => $packet]; }else{ $this->splitPackets[$packet->splitID][$packet->splitIndex] = $packet; } if(count($this->splitPackets[$packet->splitID]) === $packet->splitCount){ $pk = new EncapsulatedPacket(); $pk->buffer = ""; for($i = 0; $i < $packet->splitCount; ++$i){ $pk->buffer .= $this->splitPackets[$packet->splitID][$i]->buffer; } $pk->length = strlen($pk->buffer); unset($this->splitPackets[$packet->splitID]); $this->handleEncapsulatedPacketRoute($pk); } } private function handleEncapsulatedPacket(EncapsulatedPacket $packet){ if($packet->messageIndex === null){ $this->handleEncapsulatedPacketRoute($packet); }else{ if($packet->messageIndex < $this->reliableWindowStart or $packet->messageIndex > $this->reliableWindowEnd){ return; } if(($packet->messageIndex - $this->lastReliableIndex) === 1){ $this->lastReliableIndex++; $this->reliableWindowStart++; $this->reliableWindowEnd++; $this->handleEncapsulatedPacketRoute($packet); if(count($this->reliableWindow) > 0){ ksort($this->reliableWindow); foreach($this->reliableWindow as $index => $pk){ if(($index - $this->lastReliableIndex) !== 1){ break; } $this->lastReliableIndex++; $this->reliableWindowStart++; $this->reliableWindowEnd++; $this->handleEncapsulatedPacketRoute($pk); unset($this->reliableWindow[$index]); } } }else{ $this->reliableWindow[$packet->messageIndex] = $packet; } } } public function getState(){ return $this->state; } public function isTemporal(){ return $this->isTemporal; } private function handleEncapsulatedPacketRoute(EncapsulatedPacket $packet){ if($this->sessionManager === null){ return; } if($packet->hasSplit){ if($this->state === self::STATE_CONNECTED){ $this->handleSplit($packet); } return; } $id = ord($packet->buffer{0}); if($id < 0x80){ //internal data packet if($this->state === self::STATE_CONNECTING_2){ if($id === CLIENT_CONNECT_DataPacket::$ID){ $dataPacket = new CLIENT_CONNECT_DataPacket; $dataPacket->buffer = $packet->buffer; $dataPacket->decode(); $pk = new SERVER_HANDSHAKE_DataPacket; $pk->address = $this->address; $pk->port = $this->port; $pk->sendPing = $dataPacket->sendPing; $pk->sendPong = bcadd($pk->sendPing, "1000"); $pk->encode(); $sendPacket = new EncapsulatedPacket(); $sendPacket->reliability = PacketReliability::UNRELIABLE; $sendPacket->buffer = $pk->buffer; $this->addToQueue($sendPacket, RakLib::PRIORITY_IMMEDIATE); }elseif($id === CLIENT_HANDSHAKE_DataPacket::$ID){ $dataPacket = new CLIENT_HANDSHAKE_DataPacket; $dataPacket->buffer = $packet->buffer; $dataPacket->decode(); if($dataPacket->port === $this->sessionManager->getPort() or !$this->sessionManager->portChecking){ $this->state = self::STATE_CONNECTED; //FINALLY! $this->isTemporal = false; $this->sessionManager->openSession($this); } } }elseif($id === CLIENT_DISCONNECT_DataPacket::$ID){ $this->disconnect("client disconnect"); }elseif($id === PING_DataPacket::$ID){ $dataPacket = new PING_DataPacket; $dataPacket->buffer = $packet->buffer; $dataPacket->decode(); $pk = new PONG_DataPacket; $pk->pingID = $dataPacket->pingID; $pk->encode(); $sendPacket = new EncapsulatedPacket(); $sendPacket->reliability = PacketReliability::UNRELIABLE; $sendPacket->buffer = $pk->buffer; $this->addToQueue($sendPacket); }//TODO: add PING/PONG (0x00/0x03) automatic latency measure }elseif($this->state === self::STATE_CONNECTED){ $this->sessionManager->streamEncapsulated($this, $packet); //TODO: stream channels }else{ //$this->sessionManager->getLogger()->notice("Received packet before connection: " . bin2hex($packet->buffer)); } } public function handlePacket(Packet $packet){ $this->isActive = true; $this->lastUpdate = microtime(true); if($this->state === self::STATE_CONNECTED or $this->state === self::STATE_CONNECTING_2){ if($packet::$ID >= 0x80 and $packet::$ID <= 0x8f and $packet instanceof DataPacket){ //Data packet $packet->decode(); if($packet->seqNumber < $this->windowStart or $packet->seqNumber > $this->windowEnd or isset($this->receivedWindow[$packet->seqNumber])){ return; } $diff = $packet->seqNumber - $this->lastSeqNumber; unset($this->NACKQueue[$packet->seqNumber]); $this->ACKQueue[$packet->seqNumber] = $packet->seqNumber; $this->receivedWindow[$packet->seqNumber] = $packet->seqNumber; if($diff !== 1){ for($i = $this->lastSeqNumber + 1; $i < $packet->seqNumber; ++$i){ if(!isset($this->receivedWindow[$i])){ $this->NACKQueue[$i] = $i; } } } if($diff >= 1){ $this->lastSeqNumber = $packet->seqNumber; $this->windowStart += $diff; $this->windowEnd += $diff; } foreach($packet->packets as $pk){ $this->handleEncapsulatedPacket($pk); } }else{ if($packet instanceof ACK){ $packet->decode(); foreach($packet->packets as $seq){ if(isset($this->recoveryQueue[$seq])){ foreach($this->recoveryQueue[$seq]->packets as $pk){ if($pk instanceof EncapsulatedPacket and $pk->needACK and $pk->messageIndex !== null){ unset($this->needACK[$pk->identifierACK][$pk->messageIndex]); } } unset($this->recoveryQueue[$seq]); } } }elseif($packet instanceof NACK){ $packet->decode(); foreach($packet->packets as $seq){ if(isset($this->recoveryQueue[$seq])){ $pk = $this->recoveryQueue[$seq]; $pk->seqNumber = $this->sendSeqNumber++; $this->packetToSend[] = $pk; unset($this->recoveryQueue[$seq]); } } } } }elseif($packet::$ID > 0x00 and $packet::$ID < 0x80){ //Not Data packet :) $packet->decode(); if($packet instanceof OPEN_CONNECTION_REQUEST_1){ $packet->protocol; //TODO: check protocol number and refuse connections $pk = new OPEN_CONNECTION_REPLY_1(); $pk->mtuSize = $packet->mtuSize; $pk->serverID = $this->sessionManager->getID(); $this->sendPacket($pk); $this->state = self::STATE_CONNECTING_1; }elseif($this->state === self::STATE_CONNECTING_1 and $packet instanceof OPEN_CONNECTION_REQUEST_2){ $this->id = $packet->clientID; if($packet->serverPort === $this->sessionManager->getPort() or !$this->sessionManager->portChecking){ $this->mtuSize = min(abs($packet->mtuSize), 1432); //MTU — (Max IP Header Size) — (UDP Header Size) = 1500 — 60 — 8 = 1432 $pk = new OPEN_CONNECTION_REPLY_2(); $pk->mtuSize = $this->mtuSize; $pk->serverID = $this->sessionManager->getID(); $pk->clientAddress = $this->address; $pk->clientPort = $this->port; $this->sendPacket($pk); $this->state = self::STATE_CONNECTING_2; } } } } public function close(){ $this->addEncapsulatedToQueue(EncapsulatedPacket::fromBinary("\x60\x00\x08\x00\x00\x00\x00\x00\x00\x00\x15")); //CLIENT_DISCONNECT packet 0x15 $this->sessionManager = null; } } ================================================ FILE: src/raklib/server/SessionManager.php ================================================ server = $server; $this->socket = $socket; $this->registerPackets(); $this->serverId = mt_rand(0, PHP_INT_MAX); $this->run(); } public function getPort(){ return $this->server->getPort(); } public function getLogger(){ return $this->server->getLogger(); } public function run(){ $this->tickProcessor(); } private function tickProcessor(){ $this->lastMeasure = microtime(true); while(!$this->shutdown){ $start = microtime(true); $max = 5000; while(--$max and $this->receivePacket()) ; while($this->receiveStream()) ; $time = microtime(true) - $start; if($time < 0.05){ @time_sleep_until(microtime(true) + 0.05 - $time); } $this->tick(); } } private function tick(){ $time = microtime(true); foreach($this->sessions as $session){ $session->update($time); } foreach($this->ipSec as $address => $count){ if($count >= $this->packetLimit){ $this->blockAddress($address); } } $this->ipSec = []; if(($this->ticks & 0b1111) === 0){ $diff = max(0.005, $time - $this->lastMeasure); $this->streamOption("bandwidth", serialize([ "up" => $this->sendBytes / $diff, "down" => $this->receiveBytes / $diff ])); $this->lastMeasure = $time; $this->sendBytes = 0; $this->receiveBytes = 0; if(count($this->block) > 0){ asort($this->block); $now = microtime(true); foreach($this->block as $address => $timeout){ if($timeout <= $now){ unset($this->block[$address]); }else{ break; } } } } ++$this->ticks; } private function receivePacket(){ $len = $this->socket->readPacket($buffer, $source, $port); if($buffer !== null){ $this->receiveBytes += $len; if(isset($this->block[$source])){ return true; } if(isset($this->ipSec[$source])){ $this->ipSec[$source]++; }else{ $this->ipSec[$source] = 1; } if($len > 0){ $pid = ord($buffer{0}); if($pid === UNCONNECTED_PING::$ID){ //No need to create a session for just pings $packet = new UNCONNECTED_PING; $packet->buffer = $buffer; $packet->decode(); $pk = new UNCONNECTED_PONG(); $pk->serverID = $this->getID(); $pk->pingID = $packet->pingID; $pk->serverName = $this->getName(); $this->sendPacket($pk, $source, $port); }elseif($pid === UNCONNECTED_PONG::$ID){ //ignored }elseif(($packet = $this->getPacketFromPool($pid)) !== null){ $packet->buffer = $buffer; $this->getSession($source, $port)->handlePacket($packet); }else{ $this->streamRaw($source, $port, $buffer); } } return true; } return false; } public function sendPacket(Packet $packet, $dest, $port){ $packet->encode(); $this->sendBytes += $this->socket->writePacket($packet->buffer, $dest, $port); } public function streamEncapsulated(Session $session, EncapsulatedPacket $packet, $flags = RakLib::PRIORITY_NORMAL){ $id = $session->getAddress() . ":" . $session->getPort(); $buffer = chr(RakLib::PACKET_ENCAPSULATED) . chr(strlen($id)) . $id . chr($flags) . $packet->toBinary(true); $this->server->pushThreadToMainPacket($buffer); } public function streamRaw($address, $port, $payload){ $buffer = chr(RakLib::PACKET_RAW) . chr(strlen($address)) . $address . Binary::writeShort($port) . $payload; $this->server->pushThreadToMainPacket($buffer); } protected function streamClose($identifier, $reason){ $buffer = chr(RakLib::PACKET_CLOSE_SESSION) . chr(strlen($identifier)) . $identifier . chr(strlen($reason)) . $reason; $this->server->pushThreadToMainPacket($buffer); } protected function streamInvalid($identifier){ $buffer = chr(RakLib::PACKET_INVALID_SESSION) . chr(strlen($identifier)) . $identifier; $this->server->pushThreadToMainPacket($buffer); } protected function streamOpen(Session $session){ $identifier = $session->getAddress() . ":" . $session->getPort(); $buffer = chr(RakLib::PACKET_OPEN_SESSION) . chr(strlen($identifier)) . $identifier . chr(strlen($session->getAddress())) . $session->getAddress() . Binary::writeShort($session->getPort()) . Binary::writeLong($session->getID()); $this->server->pushThreadToMainPacket($buffer); } protected function streamACK($identifier, $identifierACK){ $buffer = chr(RakLib::PACKET_ACK_NOTIFICATION) . chr(strlen($identifier)) . $identifier . Binary::writeInt($identifierACK); $this->server->pushThreadToMainPacket($buffer); } protected function streamOption($name, $value){ $buffer = chr(RakLib::PACKET_SET_OPTION) . chr(strlen($name)) . $name . $value; $this->server->pushThreadToMainPacket($buffer); } private function checkSessions(){ if(count($this->sessions) > 4096){ foreach($this->sessions as $i => $s){ if($s->isTemporal()){ unset($this->sessions[$i]); if(count($this->sessions) <= 4096){ break; } } } } } public function receiveStream(){ if(strlen($packet = $this->server->readMainToThreadPacket()) > 0){ $id = ord($packet{0}); $offset = 1; if($id === RakLib::PACKET_ENCAPSULATED){ $len = ord($packet{$offset++}); $identifier = substr($packet, $offset, $len); $offset += $len; if(isset($this->sessions[$identifier])){ $flags = ord($packet{$offset++}); $buffer = substr($packet, $offset); $this->sessions[$identifier]->addEncapsulatedToQueue(EncapsulatedPacket::fromBinary($buffer, true), $flags); }else{ $this->streamInvalid($identifier); } }elseif($id === RakLib::PACKET_RAW){ $len = ord($packet{$offset++}); $address = substr($packet, $offset, $len); $offset += $len; $port = Binary::readShort(substr($packet, $offset, 2)); $offset += 2; $payload = substr($packet, $offset); $this->socket->writePacket($payload, $address, $port); }elseif($id === RakLib::PACKET_CLOSE_SESSION){ $len = ord($packet{$offset++}); $identifier = substr($packet, $offset, $len); if(isset($this->sessions[$identifier])){ $this->removeSession($this->sessions[$identifier]); }else{ $this->streamInvalid($identifier); } }elseif($id === RakLib::PACKET_INVALID_SESSION){ $len = ord($packet{$offset++}); $identifier = substr($packet, $offset, $len); if(isset($this->sessions[$identifier])){ $this->removeSession($this->sessions[$identifier]); } }elseif($id === RakLib::PACKET_SET_OPTION){ $len = ord($packet{$offset++}); $name = substr($packet, $offset, $len); $offset += $len; $value = substr($packet, $offset); switch($name){ case "name": $this->name = $value; break; case "portChecking": $this->portChecking = (bool) $value; break; case "packetLimit": $this->packetLimit = (int) $value; break; } }elseif($id === RakLib::PACKET_BLOCK_ADDRESS){ $len = ord($packet{$offset++}); $address = substr($packet, $offset, $len); $offset += $len; $timeout = Binary::readInt(substr($packet, $offset, 4)); $this->blockAddress($address, $timeout); }elseif($id === RakLib::PACKET_UNBLOCK_ADDRESS){ $len = ord($packet{$offset++}); $address = substr($packet, $offset, $len); $offset += $len; $this->unblockAddress($address); }elseif($id === RakLib::PACKET_SHUTDOWN){ foreach($this->sessions as $session){ $this->removeSession($session); } $this->socket->close(); $this->shutdown = true; }elseif($id === RakLib::PACKET_EMERGENCY_SHUTDOWN){ $this->shutdown = true; }else{ return false; } return true; } return false; } public function blockAddress($address, $timeout = 300){ $final = microtime(true) + $timeout; if(!isset($this->block[$address]) or $timeout === -1){ if($timeout === -1){ $final = PHP_INT_MAX; }else{ $this->getLogger()->notice("Blocked $address for $timeout seconds"); } $this->block[$address] = $final; }elseif($this->block[$address] < $final){ $this->block[$address] = $final; } } public function unblockAddress($address){ unset($this->block[$address]); } /** * @param string $ip * @param int $port * * @return Session */ public function getSession($ip, $port){ $id = $ip . ":" . $port; if(!isset($this->sessions[$id])){ $this->checkSessions(); $this->sessions[$id] = new Session($this, $ip, $port); } return $this->sessions[$id]; } public function removeSession(Session $session, $reason = "unknown"){ $id = $session->getAddress() . ":" . $session->getPort(); if(isset($this->sessions[$id])){ $this->sessions[$id]->close(); unset($this->sessions[$id]); $this->streamClose($id, $reason); } } public function openSession(Session $session){ $this->streamOpen($session); } public function notifyACK(Session $session, $identifierACK){ $this->streamACK($session->getAddress() . ":" . $session->getPort(), $identifierACK); } public function getName() : string{ return $this->name; } public function getID(){ return $this->serverId; } private function registerPacket($id, $class){ $this->packetPool[$id] = new $class; } /** * @param $id * * @return Packet */ public function getPacketFromPool($id){ if(isset($this->packetPool[$id])){ return clone $this->packetPool[$id]; } return null; } private function registerPackets(){ //$this->registerPacket(UNCONNECTED_PING::$ID, UNCONNECTED_PING::class); $this->registerPacket(UNCONNECTED_PING_OPEN_CONNECTIONS::$ID, UNCONNECTED_PING_OPEN_CONNECTIONS::class); $this->registerPacket(OPEN_CONNECTION_REQUEST_1::$ID, OPEN_CONNECTION_REQUEST_1::class); $this->registerPacket(OPEN_CONNECTION_REPLY_1::$ID, OPEN_CONNECTION_REPLY_1::class); $this->registerPacket(OPEN_CONNECTION_REQUEST_2::$ID, OPEN_CONNECTION_REQUEST_2::class); $this->registerPacket(OPEN_CONNECTION_REPLY_2::$ID, OPEN_CONNECTION_REPLY_2::class); $this->registerPacket(UNCONNECTED_PONG::$ID, UNCONNECTED_PONG::class); $this->registerPacket(ADVERTISE_SYSTEM::$ID, ADVERTISE_SYSTEM::class); $this->registerPacket(DATA_PACKET_0::$ID, DATA_PACKET_0::class); $this->registerPacket(DATA_PACKET_1::$ID, DATA_PACKET_1::class); $this->registerPacket(DATA_PACKET_2::$ID, DATA_PACKET_2::class); $this->registerPacket(DATA_PACKET_3::$ID, DATA_PACKET_3::class); $this->registerPacket(DATA_PACKET_4::$ID, DATA_PACKET_4::class); $this->registerPacket(DATA_PACKET_5::$ID, DATA_PACKET_5::class); $this->registerPacket(DATA_PACKET_6::$ID, DATA_PACKET_6::class); $this->registerPacket(DATA_PACKET_7::$ID, DATA_PACKET_7::class); $this->registerPacket(DATA_PACKET_8::$ID, DATA_PACKET_8::class); $this->registerPacket(DATA_PACKET_9::$ID, DATA_PACKET_9::class); $this->registerPacket(DATA_PACKET_A::$ID, DATA_PACKET_A::class); $this->registerPacket(DATA_PACKET_B::$ID, DATA_PACKET_B::class); $this->registerPacket(DATA_PACKET_C::$ID, DATA_PACKET_C::class); $this->registerPacket(DATA_PACKET_D::$ID, DATA_PACKET_D::class); $this->registerPacket(DATA_PACKET_E::$ID, DATA_PACKET_E::class); $this->registerPacket(DATA_PACKET_F::$ID, DATA_PACKET_F::class); $this->registerPacket(NACK::$ID, NACK::class); $this->registerPacket(ACK::$ID, ACK::class); } } ================================================ FILE: src/raklib/server/UDPServerSocket.php ================================================ socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); //socket_set_option($this->socket, SOL_SOCKET, SO_BROADCAST, 1); //Allow sending broadcast messages if(@socket_bind($this->socket, $interface, $port) === true){ socket_set_option($this->socket, SOL_SOCKET, SO_REUSEADDR, 0); $this->setSendBuffer(1024 * 1024 * 8)->setRecvBuffer(1024 * 1024 * 8); }else{ $logger->critical("**** FAILED TO BIND TO " . $interface . ":" . $port . "!", true, true, 0); $logger->critical("Perhaps a server is already running on that port?", true, true, 0); exit(1); } socket_set_nonblock($this->socket); } public function getSocket(){ return $this->socket; } public function close(){ socket_close($this->socket); } /** * @param string &$buffer * @param string &$source * @param int &$port * * @return int */ public function readPacket(&$buffer, &$source, &$port){ return socket_recvfrom($this->socket, $buffer, 65535, 0, $source, $port); } /** * @param string $buffer * @param string $dest * @param int $port * * @return int */ public function writePacket($buffer, $dest, $port){ return socket_sendto($this->socket, $buffer, strlen($buffer), 0, $dest, $port); } /** * @param int $size * * @return $this */ public function setSendBuffer($size){ @socket_set_option($this->socket, SOL_SOCKET, SO_SNDBUF, $size); return $this; } /** * @param int $size * * @return $this */ public function setRecvBuffer($size){ @socket_set_option($this->socket, SOL_SOCKET, SO_RCVBUF, $size); return $this; } } ?> ================================================ FILE: src/spl/ArrayOutOfBoundsException.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ class ArrayOutOfBoundsException extends OutOfBoundsException{ } ================================================ FILE: src/spl/AttachableLogger.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ interface AttachableLogger extends \Logger{ /** * @param LoggerAttachment $attachment */ public function addAttachment(\LoggerAttachment $attachment); /** * @param LoggerAttachment $attachment */ public function removeAttachment(\LoggerAttachment $attachment); public function removeAttachments(); /** * @return \LoggerAttachment[] */ public function getAttachments(); } ================================================ FILE: src/spl/AttachableThreadedLogger.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ abstract class AttachableThreadedLogger extends \ThreadedLogger{ /** @var \ThreadedLoggerAttachment */ protected $attachment = null; /** * @param ThreadedLoggerAttachment $attachment */ public function addAttachment(\ThreadedLoggerAttachment $attachment){ if($this->attachment instanceof \ThreadedLoggerAttachment){ $this->attachment->addAttachment($attachment); }else{ $this->attachment = $attachment; } } /** * @param ThreadedLoggerAttachment $attachment */ public function removeAttachment(\ThreadedLoggerAttachment $attachment){ if($this->attachment instanceof \ThreadedLoggerAttachment){ if($this->attachment === $attachment){ $this->attachment = null; foreach($attachment->getAttachments() as $attachment){ $this->addAttachment($attachment); } } } } public function removeAttachments(){ if($this->attachment instanceof \ThreadedLoggerAttachment){ $this->attachment->removeAttachments(); $this->attachment = null; } } /** * @return \ThreadedLoggerAttachment[] */ public function getAttachments(){ $attachments = []; if($this->attachment instanceof \ThreadedLoggerAttachment){ $attachments[] = $this->attachment; $attachments += $this->attachment->getAttachments(); } return $attachments; } } ================================================ FILE: src/spl/BaseClassLoader.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ class BaseClassLoader extends \Threaded implements ClassLoader{ /** @var \ClassLoader */ private $parent; /** @var string[] */ private $lookup; /** @var string[] */ private $classes; /** * @param ClassLoader $parent */ public function __construct(ClassLoader $parent = null){ $this->parent = $parent; $this->lookup = new \Threaded; $this->classes = new \Threaded; } /** * Adds a path to the lookup list * * @param string $path * @param bool $prepend */ public function addPath($path, $prepend = false){ foreach($this->lookup as $p){ if($p === $path){ return; } } if($prepend){ $this->synchronized(function($path){ $entries = $this->getAndRemoveLookupEntries(); $this->lookup[] = $path; foreach($entries as $entry){ $this->lookup[] = $entry; } }, $path); }else{ $this->lookup[] = $path; } } protected function getAndRemoveLookupEntries(){ $entries = []; while($this->count() > 0){ $entries[] = $this->shift(); } return $entries; } /** * Removes a path from the lookup list * * @param $path */ public function removePath($path){ foreach($this->lookup as $i => $p){ if($p === $path){ unset($this->lookup[$i]); } } } /** * Returns an array of the classes loaded * * @return string[] */ public function getClasses(){ $classes = []; foreach($this->classes as $class){ $classes[] = $class; } return $classes; } /** * Returns the parent ClassLoader, if any * * @return ClassLoader */ public function getParent(){ return $this->parent; } /** * Attaches the ClassLoader to the PHP runtime * * @param bool $prepend * * @return bool */ public function register($prepend = false){ spl_autoload_register([$this, "loadClass"], true, $prepend); } /** * Called when there is a class to load * * @param string $name * * @return bool */ public function loadClass($name){ $path = $this->findClass($name); if($path !== null){ include($path); if(!class_exists($name, false) and !interface_exists($name, false) and !trait_exists($name, false)){ if($this->getParent() === null){ throw new ClassNotFoundException("Class $name not found"); } return false; } if(method_exists($name, "onClassLoaded") and (new ReflectionClass($name))->getMethod("onClassLoaded")->isStatic()){ $name::onClassLoaded(); } $this->classes[] = $name; return true; }elseif($this->getParent() === null){ throw new ClassNotFoundException("Class $name not found"); } return false; } /** * Returns the path for the class, if any * * @param string $name * * @return string|null */ public function findClass($name){ $components = explode("\\", $name); $baseName = implode(DIRECTORY_SEPARATOR, $components); foreach($this->lookup as $path){ if(PHP_INT_SIZE === 8 and file_exists($path . DIRECTORY_SEPARATOR . $baseName . "__64bit.php")){ return $path . DIRECTORY_SEPARATOR . $baseName . "__64bit.php"; }elseif(PHP_INT_SIZE === 4 and file_exists($path . DIRECTORY_SEPARATOR . $baseName . "__32bit.php")){ return $path . DIRECTORY_SEPARATOR . $baseName . "__32bit.php"; }elseif(file_exists($path . DIRECTORY_SEPARATOR . $baseName . ".php")){ return $path . DIRECTORY_SEPARATOR . $baseName . ".php"; } } return null; } } ================================================ FILE: src/spl/ClassCastException.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ class ClassCastException extends InvalidArgumentException{ } ================================================ FILE: src/spl/ClassLoader.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ interface ClassLoader{ /** * @param ClassLoader $parent */ public function __construct(ClassLoader $parent = null); /** * Adds a path to the lookup list * * @param string $path * @param bool $prepend */ public function addPath($path, $prepend = false); /** * Removes a path from the lookup list * * @param $path */ public function removePath($path); /** * Returns an array of the classes loaded * * @return string[] */ public function getClasses(); /** * Returns the parent ClassLoader, if any * * @return ClassLoader */ public function getParent(); /** * Attaches the ClassLoader to the PHP runtime * * @param bool $prepend * * @return bool */ public function register($prepend = false); /** * Called when there is a class to load * * @param string $name * * @return bool * * @throws ClassNotFoundException */ public function loadClass($name); /** * Returns the path for the class, if any * * @param string $name * * @return string|null */ public function findClass($name); } ================================================ FILE: src/spl/ClassNotFoundException.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ class ClassNotFoundException extends LogicException{ } ================================================ FILE: src/spl/InvalidArgumentCountException.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ class InvalidArgumentCountException extends InvalidArgumentException{ } ================================================ FILE: src/spl/InvalidKeyException.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ class InvalidKeyException extends InvalidArgumentException{ } ================================================ FILE: src/spl/InvalidStateException.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ class InvalidStateException extends InvalidArgumentException{ } ================================================ FILE: src/spl/LogLevel.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ interface LogLevel{ const EMERGENCY = "emergency"; const ALERT = "alert"; const CRITICAL = "critical"; const ERROR = "error"; const WARNING = "warning"; const NOTICE = "notice"; const INFO = "info"; const DEBUG = "debug"; } ================================================ FILE: src/spl/Logger.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ interface Logger{ /** * System is unusable * * @param string $message */ public function emergency($message); /** * Action must me taken immediately * * @param string $message */ public function alert($message); /** * Critical conditions * * @param string $message */ public function critical($message); /** * Runtime errors that do not require immediate action but should typically * be logged and monitored. * * @param string $message */ public function error($message); /** * Exceptional occurrences that are not errors. * * Example: Use of deprecated APIs, poor use of an API, undesirable things * that are not necessarily wrong. * * @param string $message */ public function warning($message); /** * Normal but significant events. * * @param string $message */ public function notice($message); /** * Inersting events. * * @param string $message */ public function info($message); /** * Detailed debug information. * * @param string $message */ public function debug($message); /** * Logs with an arbitrary level. * * @param mixed $level * @param string $message */ public function log($level, $message); /** * Logs a Throwable object * * @param Throwable $e * @param $trace */ public function logException(\Throwable $e, $trace = null); } ================================================ FILE: src/spl/LoggerAttachment.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ interface LoggerAttachment{ /** * @param mixed $level * @param string $message */ public function log($level, $message); } ================================================ FILE: src/spl/SplFixedByteArray.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ class SplFixedByteArray extends SplFixedArray{ private $convert; public function __construct($size, $convert = false){ parent::__construct($size); $this->convert = (bool) $convert; } public function chunk($start, $size, $normalize = true){ $end = $start + $size; if($normalize and $this->convert){ $d = ""; for($i = $start; $i < $end; ++$i){ $d .= chr($this[$i]); } }else{ $d = []; for($i = $start; $i < $end; ++$i){ $d[] = $this[$i]; } } return $d; } /** * @param string $str * @param bool $convert * * @return SplFixedByteArray */ public static function fromString($str, $convert = false){ $len = strlen($str); $ob = new SplFixedByteArray($len, $convert); if($convert){ for($i = 0; $i < $len; ++$i){ $ob[$i] = ord($str{$i}); } }else{ for($i = 0; $i < $len; ++$i){ $ob[$i] = $str{$i}; } } return $ob; } /** * @param string $str * @param int $size * @param int $start * @param bool $convert * * @return SplFixedByteArray */ public static function fromStringChunk($str, $size, $start = 0, $convert = false){ $ob = new SplFixedByteArray($size, $convert); if($convert){ for($i = 0; $i < $size; ++$i){ $ob[$i] = ord($str{$i + $start}); } }else{ for($i = 0; $i < $size; ++$i){ $ob[$i] = $str{$i + $start}; } } return $ob; } public function toString(){ $result = ""; if($this->convert){ for($i = 0; $i < $this->getSize(); ++$i){ $result .= chr($this[$i]); } }else{ for($i = 0; $i < $this->getSize(); ++$i){ $result .= $this[$i]; } } return $result; } public function __toString(){ return $this->toString(); } } ================================================ FILE: src/spl/StringOutOfBoundsException.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ class StringOutOfBoundsException extends OutOfBoundsException{ } ================================================ FILE: src/spl/ThreadedLogger.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ abstract class ThreadedLogger extends \Thread implements Logger{ } ================================================ FILE: src/spl/ThreadedLoggerAttachment.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ abstract class ThreadedLoggerAttachment extends \Threaded implements \LoggerAttachment{ /** @var \ThreadedLoggerAttachment */ protected $attachment = null; /** * @param mixed $level * @param string $message */ public final function call($level, $message){ $this->log($level, $message); if($this->attachment instanceof \ThreadedLoggerAttachment){ $this->attachment->call($level, $message); } } /** * @param ThreadedLoggerAttachment $attachment */ public function addAttachment(\ThreadedLoggerAttachment $attachment){ if($this->attachment instanceof \ThreadedLoggerAttachment){ $this->attachment->addAttachment($attachment); }else{ $this->attachment = $attachment; } } /** * @param ThreadedLoggerAttachment $attachment */ public function removeAttachment(\ThreadedLoggerAttachment $attachment){ if($this->attachment instanceof \ThreadedLoggerAttachment){ if($this->attachment === $attachment){ $this->attachment = null; foreach($attachment->getAttachments() as $attachment){ $this->addAttachment($attachment); } } } } public function removeAttachments(){ if($this->attachment instanceof \ThreadedLoggerAttachment){ $this->attachment->removeAttachments(); $this->attachment = null; } } /** * @return \ThreadedLoggerAttachment[] */ public function getAttachments(){ $attachments = []; if($this->attachment instanceof \ThreadedLoggerAttachment){ $attachments[] = $this->attachment; $attachments += $this->attachment->getAttachments(); } return $attachments; } } ================================================ FILE: src/spl/UndefinedConstantException.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ class UndefinedConstantException extends InvalidStateException{ } ================================================ FILE: src/spl/UndefinedPropertyException.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ class UndefinedPropertyException extends LogicException{ } ================================================ FILE: src/spl/UndefinedVariableException.php ================================================ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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. */ class UndefinedVariableException extends InvalidStateException{ } ================================================ FILE: start.cmd ================================================ @echo off TITLE Elywing server software for Minecraft: Pocket Edition // No Real utility cd /d %~dp0 if exist bin\php\php.exe ( set PHPRC="" set PHP_BINARY=bin\php\php.exe ) else ( set PHP_BINARY=php ) if exist Elywing*.phar ( set POCKETMINE_FILE=Elywing*.phar ) else ( if exist Elywing.phar ( set POCKETMINE_FILE=Elywing.phar ) else ( if exist src\pocketmine\PocketMine.php ( set POCKETMINE_FILE=src\pocketmine\PocketMine.php ) else ( echo "Couldn't find a valid Elywing installation" pause exit 1 ) ) REM if exist bin\php\php_wxwidgets.dll ( REM %PHP_BINARY% %POCKETMINE_FILE% --enable-gui %* REM ) else ( if exist bin\mintty.exe ( start "" bin\mintty.exe -o Columns=88 -o Rows=32 -o AllowBlinking=0 -o FontQuality=3 -o Font="Consolas" -o FontHeight=10 -o CursorType=0 -o CursorBlinks=1 -h error -t "Elywing" -w max %PHP_BINARY% %POCKETMINE_FILE% --enable-ansi %* ) else ( %PHP_BINARY% -c bin\php %POCKETMINE_FILE% %* ) REM ) ================================================ FILE: start.sh ================================================ #!/bin/bash DIR="$(cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd)" cd "$DIR" DO_LOOP="no" while getopts "p:f:l" OPTION 2> /dev/null; do case ${OPTION} in p) PHP_BINARY="$OPTARG" ;; f) POCKETMINE_FILE="$OPTARG" ;; l) DO_LOOP="yes" ;; \?) break ;; esac done if [ "$PHP_BINARY" == "" ]; then if [ -f ./bin/php7/bin/php ]; then export PHPRC="" PHP_BINARY="./bin/php7/bin/php" elif [ type php 2>/dev/null ]; then PHP_BINARY=$(type -p php) else echo "Couldn't find a working PHP 7 binary, please use the installer." exit 1 fi fi if [ "$POCKETMINE_FILE" == "" ]; then if [ -f ./PocketMine-MP.phar ]; then POCKETMINE_FILE="./PocketMine-MP.phar" elif [ -f ./Elywing.phar ]; then POCKETMINE_FILE="./Elywing.phar" elif [ -f ./Elywing*.phar ]; then POCKETMINE_FILE="./Elywing*.phar" elif [ -f ./src/pocketmine/PocketMine.php ]; then POCKETMINE_FILE="./src/pocketmine/PocketMine.php" else echo "Couldn't find a valid Elywing installation" exit 1 fi fi LOOPS=0 set +e while [ "$LOOPS" -eq 0 ] || [ "$DO_LOOP" == "yes" ]; do if [ "$DO_LOOP" == "yes" ]; then "$PHP_BINARY" "$POCKETMINE_FILE" $@ else exec "$PHP_BINARY" "$POCKETMINE_FILE" $@ fi ((LOOPS++)) done if [ ${LOOPS} -gt 1 ]; then echo "Restarted $LOOPS times" fi