├── .editorconfig ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .poggit.yml ├── .travis.yml ├── LICENSE ├── README.md ├── icon.png ├── plugin.yml ├── src └── slapper │ ├── Main.php │ ├── SlapperTrait.php │ ├── entities │ ├── SlapperBat.php │ ├── SlapperBlaze.php │ ├── SlapperCaveSpider.php │ ├── SlapperChicken.php │ ├── SlapperCow.php │ ├── SlapperCreeper.php │ ├── SlapperDonkey.php │ ├── SlapperElderGuardian.php │ ├── SlapperEnderman.php │ ├── SlapperEndermite.php │ ├── SlapperEntity.php │ ├── SlapperEvoker.php │ ├── SlapperGhast.php │ ├── SlapperGuardian.php │ ├── SlapperHorse.php │ ├── SlapperHuman.php │ ├── SlapperHusk.php │ ├── SlapperIronGolem.php │ ├── SlapperLavaSlime.php │ ├── SlapperLlama.php │ ├── SlapperMule.php │ ├── SlapperMushroomCow.php │ ├── SlapperOcelot.php │ ├── SlapperPig.php │ ├── SlapperPigZombie.php │ ├── SlapperPolarBear.php │ ├── SlapperRabbit.php │ ├── SlapperSheep.php │ ├── SlapperShulker.php │ ├── SlapperSilverfish.php │ ├── SlapperSkeleton.php │ ├── SlapperSkeletonHorse.php │ ├── SlapperSlime.php │ ├── SlapperSnowman.php │ ├── SlapperSpider.php │ ├── SlapperSquid.php │ ├── SlapperStray.php │ ├── SlapperVex.php │ ├── SlapperVillager.php │ ├── SlapperVindicator.php │ ├── SlapperWitch.php │ ├── SlapperWither.php │ ├── SlapperWitherSkeleton.php │ ├── SlapperWolf.php │ ├── SlapperZombie.php │ ├── SlapperZombieHorse.php │ ├── SlapperZombieVillager.php │ └── other │ │ ├── SlapperBoat.php │ │ ├── SlapperFallingSand.php │ │ ├── SlapperMinecart.php │ │ └── SlapperPrimedTNT.php │ └── events │ ├── SlapperCreationEvent.php │ ├── SlapperDeletionEvent.php │ └── SlapperHitEvent.php └── tests ├── ConsoleScript.php └── travis.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | root = yes 3 | 4 | [*] 5 | indent_size = 4 6 | indent_style = tab 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ -------------------------------------------------------------------------------- /.poggit.yml: -------------------------------------------------------------------------------- 1 | --- # Poggit-CI Manifest. Open the CI at https://poggit.pmmp.io/ci/jojoe77777/Slapper 2 | branches: 3 | - master 4 | projects: 5 | Slapper: 6 | path: "" 7 | ... 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.2 5 | 6 | before_script: 7 | - echo | pecl install channel://pecl.php.net/yaml-2.0.2 8 | 9 | script: 10 | - ./tests/travis.sh 11 | 12 | notifications: 13 | email: false 14 | webhooks: http://melban.com/webhook/discord.php 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Slapper 2 | Discord server 3 | 4 | The new home of Slapper, the NPC plugin for PocketMine-MP. 5 | 6 | ## NOTE 7 | This plugin is designed for **[PocketMine-MP](https://github.com/pmmp/PocketMine-MP)** only and we do not support any other variations, forks or spoons. 8 | Compatibility with unofficial variants can occasionally be found, but **do not expect support if you are using anything other than PocketMine-MP**. 9 | 10 | ## Addons 11 | 12 | Official addons: 13 | - [SlapBack](https://github.com/jojoe77777/SlapBack) 14 | - [SlapperRotation](https://github.com/jojoe77777/SlapperRotation) 15 | - [SlapperCache](https://github.com/jojoe77777/SlapperCache) 16 | - [SlapperPlus](https://github.com/jojoe77777/SlapperPlus) 17 | - [SlapperCooldown](https://github.com/jojoe77777/SlapperCooldown) 18 | 19 | 20 | 21 | # Basic documentation 22 | 23 | ## Commands: 24 | 25 | - /slapper [args...] 26 | - /rca - Run command as another player! This can be used to only run the command if the player has permission. 27 | 28 | ## Main level commands: 29 | - help: /slapper help 30 | - spawn: /slapper spawn [name] 31 | - edit: /slapper edit [id] [args...] 32 | - id: /slapper id 33 | - remove: /slapper remove [id] 34 | - version: /slapper version 35 | - cancel: /slapper cancel 36 | - updateall: /slapper updateall 37 | 38 | ### Edit args: 39 | - helmet: /slapper edit helmet 40 | - chestplate: /slapper edit 41 | - leggings: /slapper edit leggings 42 | - boots: /slapper edit boots 43 | - skin: /slapper edit skin 44 | - name: /slapper edit name 45 | - addcommand: /slapper edit addcommand 46 | - delcommand: /slapper edit delcommand 47 | - listcommands: /slapper edit listcommands 48 | - update: /slapper edit update 49 | - block: /slapper edit block 50 | - tphere: /slapper edit tphere 51 | - tpto: /slapper edit tpto 52 | - menuname: /slapper edit menuname 53 | 54 | 55 | ### Aliases for edit args 56 | helmet: helm, helmet, head, hat, cap 57 | chestplate: chest, shirt, chestplate 58 | leggings: pants, legs, leggings 59 | boots: feet, boots, shoes 60 | item: hand, item, holding, arm, held 61 | skin: setskin, changeskin, editskin, skin 62 | name: name, customname 63 | menuname: listname, nameonlist, menuname 64 | namevisible: namevisible, customnamevisible, tagvisible, name_visible, custom_name_visible, tag_visible 65 | addcommand: addc, adduced, add command 66 | delcommand: delc, delcmd, delcommand, remove command 67 | listcommands: listcommands, listcmds, listcs 68 | fix: update, fix, migrate 69 | block: block, tile, blockid, tileid 70 | tphere: teleporthere, tphere, movehere, bringer 71 | tpto: teleportto, tpto, goto, teleport, tp 72 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jojoe77777/Slapper/440f9aa853ea42e753d1d5f1cd478bdd1b2cc0cd/icon.png -------------------------------------------------------------------------------- /plugin.yml: -------------------------------------------------------------------------------- 1 | name: Slapper 2 | author: jojoe77777 3 | version: 1.6.1 4 | description: Slapper, the NPC plugin for PocketMine-MP 5 | main: slapper\Main 6 | api: 3.13.0 7 | mcpe-protocol: [361, 388, 389, 390, 407, 408] 8 | website: https://github.com/jojoe77777/Slapper 9 | 10 | commands: 11 | slapper: 12 | description: Create a Slapper NPC, or edit an existing one! 13 | usage: "/slapper [args]..." 14 | permission: slapper.command 15 | rca: 16 | description: Execute a command as someone else! 17 | usage: "/rca " 18 | permission: slapper.rca 19 | nothing: 20 | description: Do nothing! 21 | usage: "/nothing" 22 | permission: slapper.nothing 23 | 24 | permissions: 25 | slapper: 26 | description: Allow using all Slapper commands 27 | default: op 28 | children: 29 | slapper.command: 30 | description: Allow using /slapper 31 | default: op 32 | slapper.create: 33 | description: Allow using command /slapper create 34 | default: op 35 | slapper.edit: 36 | description: Allow editing Slapper entity data with /slapper edit 37 | default: op 38 | slapper.nothing: 39 | description: Allow doing nothing 40 | default: op 41 | slapper.rca: 42 | description: Allow running commands as other players 43 | default: op 44 | slapper.remove: 45 | description: Allow removing Slapper entities with /slapper remove [id] 46 | default: op 47 | slapper.id: 48 | description: Allow viewing entity ID 49 | default: op 50 | slapper.help: 51 | description: Allow viewing Slapper help page 52 | default: op 53 | -------------------------------------------------------------------------------- /src/slapper/Main.php: -------------------------------------------------------------------------------- 1 | "LavaSlime", 68 | "ZombiePigman" => "PigZombie", 69 | "Mooshroom" => "MushroomCow", 70 | "Player" => "Human", 71 | "VillagerZombie" => "ZombieVillager", 72 | "SnowGolem" => "Snowman", 73 | "FallingBlock" => "FallingSand", 74 | "FakeBlock" => "FallingSand", 75 | "VillagerGolem" => "IronGolem", 76 | "EGuardian" => "ElderGuardian", 77 | "Emite" => "Endermite" 78 | ]; 79 | 80 | /** @var array */ 81 | public $hitSessions = []; 82 | /** @var array */ 83 | public $idSessions = []; 84 | /** @var string */ 85 | public $prefix = TextFormat::GREEN . "[" . TextFormat::YELLOW . "Slapper" . TextFormat::GREEN . "] "; 86 | /** @var string */ 87 | public $noperm = TextFormat::GREEN . "[" . TextFormat::YELLOW . "Slapper" . TextFormat::GREEN . "] You don't have permission."; 88 | /** @var string */ 89 | public $helpHeader = 90 | TextFormat::YELLOW . "---------- " . 91 | TextFormat::GREEN . "[" . TextFormat::YELLOW . "Slapper Help" . TextFormat::GREEN . "] " . 92 | TextFormat::YELLOW . "----------"; 93 | 94 | /** @var string[] */ 95 | public $mainArgs = [ 96 | "help: /slapper help", 97 | "spawn: /slapper spawn [name]", 98 | "edit: /slapper edit [id] [args...]", 99 | "id: /slapper id", 100 | "remove: /slapper remove [id]", 101 | "version: /slapper version", 102 | "cancel: /slapper cancel", 103 | ]; 104 | /** @var string[] */ 105 | public $editArgs = [ 106 | "helmet: /slapper edit helmet ", 107 | "chestplate: /slapper edit chestplate ", 108 | "leggings: /slapper edit leggings ", 109 | "boots: /slapper edit boots ", 110 | "skin: /slapper edit skin", 111 | "name: /slapper edit name ", 112 | "addcommand: /slapper edit addcommand ", 113 | "delcommand: /slapper edit delcommand ", 114 | "listcommands: /slapper edit listcommands", 115 | "blockid: /slapper edit block ", 116 | "scale: /slapper edit scale ", 117 | "tphere: /slapper edit tphere", 118 | "tpto: /slapper edit tpto", 119 | "menuname: /slapper edit menuname " 120 | ]; 121 | 122 | /** 123 | * @return void 124 | */ 125 | public function onEnable(): void { 126 | foreach ([ 127 | SlapperCreeper::class, SlapperBat::class, SlapperSheep::class, 128 | SlapperPigZombie::class, SlapperGhast::class, SlapperBlaze::class, 129 | SlapperIronGolem::class, SlapperSnowman::class, SlapperOcelot::class, 130 | SlapperZombieVillager::class, SlapperHuman::class, SlapperCow::class, 131 | SlapperZombie::class, SlapperSquid::class, SlapperVillager::class, 132 | SlapperSpider::class, SlapperPig::class, SlapperMushroomCow::class, 133 | SlapperWolf::class, SlapperLavaSlime::class, SlapperSilverfish::class, 134 | SlapperSkeleton::class, SlapperSlime::class, SlapperChicken::class, 135 | SlapperEnderman::class, SlapperCaveSpider::class, SlapperBoat::class, 136 | SlapperMinecart::class, SlapperMule::class, SlapperWitch::class, 137 | SlapperPrimedTNT::class, SlapperHorse::class, SlapperDonkey::class, 138 | SlapperSkeletonHorse::class, SlapperZombieHorse::class, SlapperRabbit::class, 139 | SlapperStray::class, SlapperHusk::class, SlapperWitherSkeleton::class, 140 | SlapperFallingSand::class, SlapperElderGuardian::class, SlapperEndermite::class, 141 | SlapperEvoker::class, SlapperGuardian::class, SlapperLlama::class, 142 | SlapperPolarBear::class, SlapperShulker::class, SlapperVex::class, 143 | SlapperVindicator::class, SlapperWither::class 144 | ] as $className) { 145 | Entity::registerEntity($className, true); 146 | } 147 | $this->getServer()->getPluginManager()->registerEvents($this, $this); 148 | } 149 | 150 | /** 151 | * @param CommandSender $sender 152 | * @param Command $command 153 | * @param string $label 154 | * @param string[] $args 155 | * 156 | * @return bool 157 | */ 158 | public function onCommand(CommandSender $sender, Command $command, string $label, array $args): bool { 159 | switch (strtolower($command->getName())) { 160 | case "nothing": 161 | return true; 162 | case "rca": 163 | if (count($args) < 2) { 164 | $sender->sendMessage($this->prefix . "Please enter a player and a command."); 165 | return true; 166 | } 167 | $player = $this->getServer()->getPlayer(array_shift($args)); 168 | if ($player instanceof Player) { 169 | $this->getServer()->dispatchCommand($player, trim(implode(" ", $args))); 170 | return true; 171 | } else { 172 | $sender->sendMessage($this->prefix . "Player not found."); 173 | return true; 174 | } 175 | case "slapper": 176 | if ($sender instanceof Player) { 177 | if (!isset($args[0])) { 178 | if (!$sender->hasPermission("slapper.command")) { 179 | $sender->sendMessage($this->noperm); 180 | return true; 181 | } else { 182 | $sender->sendMessage($this->prefix . "Please type '/slapper help'."); 183 | return true; 184 | } 185 | } 186 | $arg = array_shift($args); 187 | switch ($arg) { 188 | case "id": 189 | if (!$sender->hasPermission("slapper.id")) { 190 | $sender->sendMessage($this->noperm); 191 | return true; 192 | } 193 | $this->idSessions[$sender->getName()] = true; 194 | $sender->sendMessage($this->prefix . "Hit an entity to get its ID!"); 195 | return true; 196 | case "version": 197 | if (!$sender->hasPermission("slapper.version")) { 198 | $sender->sendMessage($this->noperm); 199 | return true; 200 | } 201 | $desc = $this->getDescription(); 202 | $sender->sendMessage($this->prefix . TextFormat::BLUE . $desc->getName() . " " . $desc->getVersion() . " " . TextFormat::GREEN . "by " . TextFormat::GOLD . "jojoe77777"); 203 | return true; 204 | case "cancel": 205 | case "stopremove": 206 | case "stopid": 207 | unset($this->hitSessions[$sender->getName()]); 208 | unset($this->idSessions[$sender->getName()]); 209 | $sender->sendMessage($this->prefix . "Cancelled."); 210 | return true; 211 | case "remove": 212 | if (!$sender->hasPermission("slapper.remove")) { 213 | $sender->sendMessage($this->noperm); 214 | return true; 215 | } 216 | if (!isset($args[0])) { 217 | $this->hitSessions[$sender->getName()] = true; 218 | $sender->sendMessage($this->prefix . "Hit an entity to remove it."); 219 | return true; 220 | } 221 | $entity = $sender->getLevel()->getEntity((int) $args[0]); 222 | if ($entity !== null) { 223 | if ($entity instanceof SlapperEntity || $entity instanceof SlapperHuman) { 224 | $this->getServer()->getPluginManager()->callEvent(new SlapperDeletionEvent($entity)); 225 | $entity->close(); 226 | $sender->sendMessage($this->prefix . "Entity removed."); 227 | } else { 228 | $sender->sendMessage($this->prefix . "That entity is not handled by Slapper."); 229 | } 230 | } else { 231 | $sender->sendMessage($this->prefix . "Entity does not exist."); 232 | } 233 | return true; 234 | case "edit": 235 | if (!$sender->hasPermission("slapper.edit")) { 236 | $sender->sendMessage($this->noperm); 237 | return true; 238 | } 239 | if (isset($args[0])) { 240 | $level = $sender->getLevel(); 241 | $entity = $level->getEntity((int) $args[0]); 242 | if ($entity !== null) { 243 | if ($entity instanceof SlapperEntity || $entity instanceof SlapperHuman) { 244 | if (isset($args[1])) { 245 | switch ($args[1]) { 246 | case "helm": 247 | case "helmet": 248 | case "head": 249 | case "hat": 250 | case "cap": 251 | if ($entity instanceof SlapperHuman) { 252 | if (isset($args[2])) { 253 | $entity->getArmorInventory()->setHelmet(Item::fromString($args[2])); 254 | $sender->sendMessage($this->prefix . "Helmet updated."); 255 | } else { 256 | $sender->sendMessage($this->prefix . "Please enter an item ID."); 257 | } 258 | } else { 259 | $sender->sendMessage($this->prefix . "That entity can not wear armor."); 260 | } 261 | return true; 262 | case "chest": 263 | case "shirt": 264 | case "chestplate": 265 | if ($entity instanceof SlapperHuman) { 266 | if (isset($args[2])) { 267 | $entity->getArmorInventory()->setChestplate(Item::fromString($args[2])); 268 | $sender->sendMessage($this->prefix . "Chestplate updated."); 269 | } else { 270 | $sender->sendMessage($this->prefix . "Please enter an item ID."); 271 | } 272 | } else { 273 | $sender->sendMessage($this->prefix . "That entity can not wear armor."); 274 | } 275 | return true; 276 | case "pants": 277 | case "legs": 278 | case "leggings": 279 | if ($entity instanceof SlapperHuman) { 280 | if (isset($args[2])) { 281 | $entity->getArmorInventory()->setLeggings(Item::fromString($args[2])); 282 | $sender->sendMessage($this->prefix . "Leggings updated."); 283 | } else { 284 | $sender->sendMessage($this->prefix . "Please enter an item ID."); 285 | } 286 | } else { 287 | $sender->sendMessage($this->prefix . "That entity can not wear armor."); 288 | } 289 | return true; 290 | case "feet": 291 | case "boots": 292 | case "shoes": 293 | if ($entity instanceof SlapperHuman) { 294 | if (isset($args[2])) { 295 | $entity->getArmorInventory()->setBoots(Item::fromString($args[2])); 296 | $sender->sendMessage($this->prefix . "Boots updated."); 297 | } else { 298 | $sender->sendMessage($this->prefix . "Please enter an item ID."); 299 | } 300 | } else { 301 | $sender->sendMessage($this->prefix . "That entity can not wear armor."); 302 | } 303 | return true; 304 | case "hand": 305 | case "item": 306 | case "holding": 307 | case "arm": 308 | case "held": 309 | if ($entity instanceof SlapperHuman) { 310 | if (isset($args[2])) { 311 | $entity->getInventory()->setItemInHand(Item::fromString($args[2])); 312 | $entity->getInventory()->sendHeldItem($entity->getViewers()); 313 | $sender->sendMessage($this->prefix . "Item updated."); 314 | } else { 315 | $sender->sendMessage($this->prefix . "Please enter an item ID."); 316 | } 317 | } else { 318 | $sender->sendMessage($this->prefix . "That entity can not wear armor."); 319 | } 320 | return true; 321 | case "setskin": 322 | case "changeskin": 323 | case "editskin"; 324 | case "skin": 325 | if ($entity instanceof SlapperHuman) { 326 | $entity->setSkin($sender->getSkin()); 327 | $entity->sendData($entity->getViewers()); 328 | $sender->sendMessage($this->prefix . "Skin updated."); 329 | } else { 330 | $sender->sendMessage($this->prefix . "That entity can't have a skin."); 331 | } 332 | return true; 333 | case "name": 334 | case "customname": 335 | if (isset($args[2])) { 336 | array_shift($args); 337 | array_shift($args); 338 | $entity->setNameTag(str_replace(["{color}", "{line}"], ["§", "\n"], trim(implode(" ", $args)))); 339 | $entity->sendData($entity->getViewers()); 340 | $sender->sendMessage($this->prefix . "Name updated."); 341 | } else { 342 | $sender->sendMessage($this->prefix . "Please enter a name."); 343 | } 344 | return true; 345 | case "listname": 346 | case "nameonlist": 347 | case "menuname": 348 | if ($entity instanceof SlapperHuman) { 349 | if (isset($args[2])) { 350 | $type = 0; 351 | array_shift($args); 352 | array_shift($args); 353 | $input = trim(implode(" ", $args)); 354 | switch (strtolower($input)) { 355 | case "remove": 356 | case "": 357 | case "disable": 358 | case "off": 359 | case "hide": 360 | $type = 1; 361 | } 362 | if ($type === 0) { 363 | $entity->namedtag->setString("MenuName", $input); 364 | } else { 365 | $entity->namedtag->setString("MenuName", ""); 366 | } 367 | $entity->respawnToAll(); 368 | $sender->sendMessage($this->prefix . "Menu name updated."); 369 | } else { 370 | $sender->sendMessage($this->prefix . "Please enter a menu name."); 371 | return true; 372 | } 373 | } else { 374 | $sender->sendMessage($this->prefix . "That entity can not have a menu name."); 375 | } 376 | return true; 377 | case "addc": 378 | case "addcmd": 379 | case "addcommand": 380 | if (isset($args[2])) { 381 | array_shift($args); 382 | array_shift($args); 383 | $input = trim(implode(" ", $args)); 384 | 385 | $commands = $entity->namedtag->getCompoundTag("Commands") ?? new CompoundTag("Commands"); 386 | 387 | if ($commands->hasTag($input)) { 388 | $sender->sendMessage($this->prefix . "That command has already been added."); 389 | return true; 390 | } 391 | $commands->setString($input, $input); 392 | $entity->namedtag->setTag($commands); //in case a new CompoundTag was created 393 | $sender->sendMessage($this->prefix . "Command added."); 394 | } else { 395 | $sender->sendMessage($this->prefix . "Please enter a command."); 396 | } 397 | return true; 398 | case "delc": 399 | case "delcmd": 400 | case "delcommand": 401 | case "removecommand": 402 | if (isset($args[2])) { 403 | array_shift($args); 404 | array_shift($args); 405 | $input = trim(implode(" ", $args)); 406 | 407 | $commands = $entity->namedtag->getCompoundTag("Commands") ?? new CompoundTag("Commands"); 408 | 409 | $commands->removeTag($input); 410 | $entity->namedtag->setTag($commands); //in case a new CompoundTag was created 411 | $sender->sendMessage($this->prefix . "Command removed."); 412 | } else { 413 | $sender->sendMessage($this->prefix . "Please enter a command."); 414 | } 415 | return true; 416 | case "listcommands": 417 | case "listcmds": 418 | case "listcs": 419 | $commands = $entity->namedtag->getCompoundTag("Commands"); 420 | if ($commands !== null and $commands->getCount() > 0) { 421 | $id = 0; 422 | 423 | /** @var StringTag $stringTag */ 424 | foreach ($commands as $stringTag) { 425 | $id++; 426 | $sender->sendMessage(TextFormat::GREEN . "[" . TextFormat::YELLOW . "S" . TextFormat::GREEN . "] " . TextFormat::YELLOW . $id . ". " . TextFormat::GREEN . $stringTag->getValue() . "\n"); 427 | } 428 | } else { 429 | $sender->sendMessage($this->prefix . "That entity does not have any commands."); 430 | } 431 | return true; 432 | case "block": 433 | case "tile": 434 | case "blockid": 435 | case "tileid": 436 | if (isset($args[2])) { 437 | if ($entity instanceof SlapperFallingSand) { 438 | $data = explode(":", $args[2]); 439 | //haxx: we shouldn't use toStaticRuntimeId() because it's internal, but there isn't really any better option at the moment 440 | $entity->getDataPropertyManager()->setInt(Entity::DATA_VARIANT, BlockFactory::toStaticRuntimeId((int) ($data[0] ?? 1), (int) ($data[1] ?? 0))); 441 | $entity->sendData($entity->getViewers()); 442 | $sender->sendMessage($this->prefix . "Block updated."); 443 | } else { 444 | $sender->sendMessage($this->prefix . "That entity is not a block."); 445 | } 446 | } else { 447 | $sender->sendMessage($this->prefix . "Please enter a value."); 448 | } 449 | return true; 450 | case "teleporthere": 451 | case "tphere": 452 | case "movehere": 453 | case "bringhere": 454 | $entity->teleport($sender); 455 | $sender->sendMessage($this->prefix . "Teleported entity to you."); 456 | $entity->respawnToAll(); 457 | return true; 458 | case "teleportto": 459 | case "tpto": 460 | case "goto": 461 | case "teleport": 462 | case "tp": 463 | $sender->teleport($entity); 464 | $sender->sendMessage($this->prefix . "Teleported you to entity."); 465 | return true; 466 | case "scale": 467 | case "size": 468 | if (isset($args[2])) { 469 | $scale = (float) $args[2]; 470 | $entity->getDataPropertyManager()->setFloat(Entity::DATA_SCALE, $scale); 471 | $entity->sendData($entity->getViewers()); 472 | $sender->sendMessage($this->prefix . "Updated scale."); 473 | } else { 474 | $sender->sendMessage($this->prefix . "Please enter a value."); 475 | } 476 | return true; 477 | default: 478 | $sender->sendMessage($this->prefix . "Unknown command."); 479 | return true; 480 | } 481 | } else { 482 | $sender->sendMessage($this->helpHeader); 483 | foreach ($this->editArgs as $msgArg) { 484 | $sender->sendMessage(str_replace("", $args[0], TextFormat::GREEN . " - " . $msgArg . "\n")); 485 | } 486 | return true; 487 | } 488 | } else { 489 | $sender->sendMessage($this->prefix . "That entity is not handled by Slapper."); 490 | } 491 | } else { 492 | $sender->sendMessage($this->prefix . "Entity does not exist."); 493 | } 494 | return true; 495 | } else { 496 | $sender->sendMessage($this->helpHeader); 497 | foreach ($this->editArgs as $msgArg) { 498 | $sender->sendMessage(TextFormat::GREEN . " - " . $msgArg . "\n"); 499 | } 500 | return true; 501 | } 502 | case "help": 503 | case "?": 504 | $sender->sendMessage($this->helpHeader); 505 | foreach ($this->mainArgs as $msgArg) { 506 | $sender->sendMessage(TextFormat::GREEN . " - " . $msgArg . "\n"); 507 | } 508 | return true; 509 | case "add": 510 | case "make": 511 | case "create": 512 | case "spawn": 513 | case "apawn": 514 | case "spanw": 515 | if (!$sender->hasPermission("slapper.create")) { 516 | $sender->sendMessage($this->noperm); 517 | return true; 518 | } 519 | $type = array_shift($args); 520 | $name = str_replace(["{color}", "{line}"], ["§", "\n"], trim(implode(" ", $args))); 521 | if ($type === null || empty(trim($type))) { 522 | $sender->sendMessage($this->prefix . "Please enter an entity type."); 523 | return true; 524 | } 525 | if (empty($name)) { 526 | $name = $sender->getDisplayName(); 527 | } 528 | $types = self::ENTITY_TYPES; 529 | $aliases = self::ENTITY_ALIASES; 530 | $chosenType = null; 531 | foreach ($types as $t) { 532 | if (strtolower($type) === strtolower($t)) { 533 | $chosenType = $t; 534 | } 535 | } 536 | if ($chosenType === null) { 537 | foreach ($aliases as $alias => $t) { 538 | if (strtolower($type) === strtolower($alias)) { 539 | $chosenType = $t; 540 | } 541 | } 542 | } 543 | if ($chosenType === null) { 544 | $sender->sendMessage($this->prefix . "Invalid entity type."); 545 | return true; 546 | } 547 | $nbt = $this->makeNBT($chosenType, $sender, $name); 548 | /** @var SlapperEntity $entity */ 549 | $entity = Entity::createEntity("Slapper" . $chosenType, $sender->getLevel(), $nbt); 550 | $this->getServer()->getPluginManager()->callEvent(new SlapperCreationEvent($entity, "Slapper" . $chosenType, $sender, SlapperCreationEvent::CAUSE_COMMAND)); 551 | $entity->spawnToAll(); 552 | $sender->sendMessage($this->prefix . $chosenType . " entity spawned with name " . TextFormat::WHITE . "\"" . TextFormat::BLUE . $name . TextFormat::WHITE . "\"" . TextFormat::GREEN . " and entity ID " . TextFormat::BLUE . $entity->getId()); 553 | return true; 554 | default: 555 | $sender->sendMessage($this->prefix . "Unknown command. Type '/slapper help' for help."); 556 | return true; 557 | } 558 | } else { 559 | $sender->sendMessage($this->prefix . "This command only works in game."); 560 | return true; 561 | } 562 | } 563 | return true; 564 | } 565 | 566 | /** 567 | * @param string $type 568 | * @param Player $player 569 | * @param string $name 570 | * 571 | * @return CompoundTag 572 | */ 573 | private function makeNBT($type, Player $player, string $name): CompoundTag { 574 | $nbt = Entity::createBaseNBT($player, null, $player->getYaw(), $player->getPitch()); 575 | $nbt->setShort("Health", 1); 576 | $nbt->setTag(new CompoundTag("Commands", [])); 577 | $nbt->setString("MenuName", ""); 578 | $nbt->setString("CustomName", $name); 579 | $nbt->setString("SlapperVersion", $this->getDescription()->getVersion()); 580 | if ($type === "Human") { 581 | $player->saveNBT(); 582 | 583 | $inventoryTag = $player->namedtag->getListTag("Inventory"); 584 | assert($inventoryTag !== null); 585 | $nbt->setTag(clone $inventoryTag); 586 | 587 | $skinTag = $player->namedtag->getCompoundTag("Skin"); 588 | assert($skinTag !== null); 589 | $nbt->setTag(clone $skinTag); 590 | } 591 | return $nbt; 592 | } 593 | 594 | /** 595 | * @param EntityDamageEvent $event 596 | * 597 | * @ignoreCancelled true 598 | * 599 | * @return void 600 | */ 601 | public function onEntityDamage(EntityDamageEvent $event): void { 602 | $entity = $event->getEntity(); 603 | if ($entity instanceof SlapperEntity || $entity instanceof SlapperHuman) { 604 | $event->setCancelled(true); 605 | if (!$event instanceof EntityDamageByEntityEvent) { 606 | return; 607 | } 608 | $damager = $event->getDamager(); 609 | if (!$damager instanceof Player) { 610 | return; 611 | } 612 | $this->getServer()->getPluginManager()->callEvent($event = new SlapperHitEvent($entity, $damager)); 613 | if ($event->isCancelled()) { 614 | return; 615 | } 616 | $damagerName = $damager->getName(); 617 | if (isset($this->hitSessions[$damagerName])) { 618 | if ($entity instanceof SlapperHuman) { 619 | $entity->getInventory()->clearAll(); 620 | } 621 | $entity->close(); 622 | unset($this->hitSessions[$damagerName]); 623 | $damager->sendMessage($this->prefix . "Entity removed."); 624 | return; 625 | } 626 | if (isset($this->idSessions[$damagerName])) { 627 | $damager->sendMessage($this->prefix . "Entity ID: " . $entity->getId()); 628 | unset($this->idSessions[$damagerName]); 629 | return; 630 | } 631 | 632 | if (($commands = $entity->namedtag->getCompoundTag("Commands")) !== null) { 633 | $server = $this->getServer(); 634 | /** @var StringTag $stringTag */ 635 | foreach ($commands as $stringTag) { 636 | $server->dispatchCommand(new ConsoleCommandSender(), str_replace("{player}", '"' . $damagerName . '"', $stringTag->getValue())); 637 | } 638 | } 639 | } 640 | } 641 | 642 | /** 643 | * @param EntitySpawnEvent $ev 644 | * 645 | * @return void 646 | */ 647 | public function onEntitySpawn(EntitySpawnEvent $ev): void { 648 | $entity = $ev->getEntity(); 649 | if ($entity instanceof SlapperEntity || $entity instanceof SlapperHuman) { 650 | $clearLagg = $this->getServer()->getPluginManager()->getPlugin("ClearLagg"); 651 | if ($clearLagg !== null) { 652 | $clearLagg->exemptEntity($entity); 653 | } 654 | } 655 | } 656 | 657 | /** 658 | * @param EntityMotionEvent $event 659 | * 660 | * @return void 661 | */ 662 | public function onEntityMotion(EntityMotionEvent $event): void { 663 | $entity = $event->getEntity(); 664 | if ($entity instanceof SlapperEntity || $entity instanceof SlapperHuman) { 665 | $event->setCancelled(true); 666 | } 667 | } 668 | } 669 | -------------------------------------------------------------------------------- /src/slapper/SlapperTrait.php: -------------------------------------------------------------------------------- 1 | setGenericFlag(Entity::DATA_FLAG_IMMOBILE, true); 38 | if (!$this->namedtag->hasTag("Scale", FloatTag::class)) { 39 | $this->namedtag->setFloat("Scale", 1.0, true); 40 | } 41 | $this->getDataPropertyManager()->setFloat(Entity::DATA_SCALE, $this->namedtag->getFloat("Scale")); 42 | } 43 | 44 | public function tryChangeMovement(): void { 45 | 46 | } 47 | 48 | public function sendData($playerList, array $data = null): void { 49 | if(!is_array($playerList)){ 50 | $playerList = [$playerList]; 51 | } 52 | 53 | foreach($playerList as $p){ 54 | $playerData = $data ?? $this->getDataPropertyManager()->getAll(); 55 | unset($playerData[self::DATA_NAMETAG]); 56 | $pk = new SetEntityDataPacket(); 57 | $pk->entityRuntimeId = $this->getId(); 58 | $pk->metadata = $playerData; 59 | $p->dataPacket($pk); 60 | 61 | $this->sendNameTag($p); 62 | } 63 | } 64 | 65 | public function saveSlapperNbt(): void { 66 | $visibility = 0; 67 | if ($this->isNameTagVisible()) { 68 | $visibility = 1; 69 | if ($this->isNameTagAlwaysVisible()) { 70 | $visibility = 2; 71 | } 72 | } 73 | $scale = $this->getDataPropertyManager()->getFloat(Entity::DATA_SCALE); 74 | $this->namedtag->setInt("NameVisibility", $visibility, true); 75 | $this->namedtag->setFloat("Scale", $scale, true); 76 | } 77 | 78 | public function getDisplayName(Player $player): string { 79 | $vars = [ 80 | "{name}" => $player->getName(), 81 | "{display_name}" => $player->getName(), 82 | "{nametag}" => $player->getNameTag() 83 | ]; 84 | return str_replace(array_keys($vars), array_values($vars), $this->getNameTag()); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/slapper/entities/SlapperBat.php: -------------------------------------------------------------------------------- 1 | setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ELDER, true); 14 | parent::prepareMetadata(); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/slapper/entities/SlapperEnderman.php: -------------------------------------------------------------------------------- 1 | height = static::HEIGHT; 32 | $this->width = $this->width ?? 1; //polyfill 33 | $this->tagId = Entity::$entityCount++; 34 | parent::__construct($level, $nbt); 35 | $this->prepareMetadata(); 36 | $this->setNameTagVisible(false); 37 | } 38 | 39 | public function saveNBT(): void { 40 | parent::saveNBT(); 41 | $this->saveSlapperNbt(); 42 | } 43 | 44 | protected function sendSpawnPacket(Player $player): void { 45 | $pk = new AddEntityPacket(); 46 | $pk->entityRuntimeId = $this->getId(); 47 | $pk->type = AddEntityPacket::LEGACY_ID_MAP_BC[static::TYPE_ID]; 48 | $pk->position = $this->asVector3(); 49 | $pk->yaw = $pk->headYaw = $this->yaw; 50 | $pk->pitch = $this->pitch; 51 | $pk->metadata = $this->getDataPropertyManager()->getAll(); 52 | unset($pk->metadata[self::DATA_NAMETAG]); 53 | 54 | $player->dataPacket($pk); 55 | 56 | $pk2 = new AddPlayerPacket(); 57 | $pk2->entityRuntimeId = $this->tagId; 58 | $pk2->uuid = UUID::fromRandom(); 59 | $pk2->username = $this->getDisplayName($player); 60 | $pk2->position = $this->asVector3()->add(0, static::HEIGHT); 61 | $pk2->item = ItemFactory::get(ItemIds::AIR); 62 | $pk2->metadata = [self::DATA_SCALE => [self::DATA_TYPE_FLOAT, 0.0]]; 63 | 64 | $player->dataPacket($pk2); 65 | } 66 | 67 | public function sendNameTag(Player $player): void { 68 | $pk = new SetEntityDataPacket(); 69 | $pk->entityRuntimeId = $this->tagId; 70 | $pk->metadata = [self::DATA_NAMETAG => [self::DATA_TYPE_STRING, $this->getDisplayName($player)]]; 71 | $player->dataPacket($pk); 72 | } 73 | 74 | public function despawnFrom(Player $player, bool $send = true): void { 75 | parent::despawnFrom($player, $send); 76 | $pk = new RemoveEntityPacket(); 77 | $pk->entityUniqueId = $this->tagId; 78 | $player->dataPacket($pk); 79 | } 80 | 81 | public function broadcastMovement(bool $teleport = false): void { 82 | if($this->chunk !== null) { 83 | parent::broadcastMovement($teleport); 84 | $pk = new MoveEntityAbsolutePacket(); 85 | $pk->entityRuntimeId = $this->tagId; 86 | $pk->position = $this->asVector3()->add(0, static::HEIGHT + 1.62); 87 | $pk->xRot = $pk->yRot = $pk->zRot = 0; 88 | 89 | $this->level->addChunkPacket($this->chunk->getX(), $this->chunk->getZ(), $pk); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/slapper/entities/SlapperEvoker.php: -------------------------------------------------------------------------------- 1 | prepareMetadata(); 20 | } 21 | 22 | public function saveNBT(): void { 23 | parent::saveNBT(); 24 | $this->saveSlapperNbt(); 25 | } 26 | 27 | public function sendNameTag(Player $player): void { 28 | $pk = new SetEntityDataPacket(); 29 | $pk->entityRuntimeId = $this->getId(); 30 | $pk->metadata = [self::DATA_NAMETAG => [self::DATA_TYPE_STRING, $this->getDisplayName($player)]]; 31 | $player->dataPacket($pk); 32 | } 33 | 34 | protected function sendSpawnPacket(Player $player): void { 35 | parent::sendSpawnPacket($player); 36 | 37 | if (($menuName = $this->namedtag->getString("MenuName", "", true)) !== "") { 38 | $player->getServer()->updatePlayerListData($this->getUniqueId(), $this->getId(), $menuName, $this->skin, "", [$player]); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/slapper/entities/SlapperHusk.php: -------------------------------------------------------------------------------- 1 | namedtag->hasTag("BlockID", IntTag::class)) { 21 | $this->namedtag->setInt("BlockID", 1, true); 22 | } 23 | 24 | //haxx: we shouldn't use toStaticRuntimeId() because it's internal, but there isn't really any better option at the moment 25 | $this->getDataPropertyManager()->setInt(self::DATA_VARIANT, BlockFactory::toStaticRuntimeId($this->namedtag->getInt("BlockID"))); 26 | } 27 | 28 | public function saveNBT(): void { 29 | parent::saveNBT(); 30 | $this->namedtag->setInt("BlockID", $this->getDataPropertyManager()->getInt(self::DATA_VARIANT), true); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/slapper/entities/other/SlapperMinecart.php: -------------------------------------------------------------------------------- 1 | entity = $entity; 33 | $this->type = $type; 34 | $this->creator = $creator; 35 | $this->cause = $cause; 36 | } 37 | 38 | public function getEntity(): Entity { 39 | return $this->entity; 40 | } 41 | 42 | public function getCreator(): ?Player { 43 | return $this->creator; 44 | } 45 | 46 | public function hasCreator(): bool { 47 | return $this->creator !== null; 48 | } 49 | 50 | public function getCause(): int { 51 | return $this->cause; 52 | } 53 | 54 | public function getType(): string { 55 | return $this->type; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/slapper/events/SlapperDeletionEvent.php: -------------------------------------------------------------------------------- 1 | entity = $entity; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/slapper/events/SlapperHitEvent.php: -------------------------------------------------------------------------------- 1 | entity = $entity; 19 | $this->damager = $damager; 20 | } 21 | 22 | /** 23 | * @return Player 24 | */ 25 | public function getDamager(): Player { 26 | return $this->damager; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/ConsoleScript.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Lesser General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | */ 17 | 18 | const VERSION = "1.11.1"; 19 | 20 | $opts = getopt("", ["make:", "relative:", "out:", "entry:", "compress", "stub:"]); 21 | 22 | if(!isset($opts["make"])){ 23 | echo "== PocketMine-MP DevTools CLI interface ==\n\n"; 24 | echo "Usage: " . PHP_BINARY . " -dphar.readonly=0 " . $argv[0] . " --make --relative --entry \"relativeSourcePath.php\" --out \n"; 25 | exit(0); 26 | } 27 | 28 | if(ini_get("phar.readonly") == 1){ 29 | echo "Set phar.readonly to 0 with -dphar.readonly=0\n"; 30 | exit(1); 31 | } 32 | 33 | $folderPath = rtrim(str_replace("\\", "/", realpath($opts["make"])), "/") . "/"; 34 | $relativePath = isset($opts["relative"]) ? rtrim(str_replace("\\", "/", realpath($opts["relative"])), "/") . "/" : $folderPath; 35 | $pharName = $opts["out"] ?? "output.phar"; 36 | $stubPath = $opts["stub"] ?? "stub.php"; 37 | 38 | 39 | if(!is_dir($folderPath)){ 40 | echo $folderPath . " is not a folder\n"; 41 | exit(1); 42 | } 43 | 44 | echo "\nCreating " . $pharName . "...\n"; 45 | $phar = new \Phar($pharName); 46 | 47 | if(file_exists($relativePath . $stubPath)){ 48 | echo "Using stub " . $relativePath . $stubPath . "\n"; 49 | $phar->setStub('setStub('setMetadata([ 63 | "name" => $metadata["name"], 64 | "version" => $metadata["version"], 65 | "main" => $metadata["main"], 66 | "api" => $metadata["api"], 67 | "depend" => ($metadata["depend"] ?? ""), 68 | "description" => ($metadata["description"] ?? ""), 69 | "authors" => ($metadata["authors"] ?? ""), 70 | "website" => ($metadata["website"] ?? ""), 71 | "creationDate" => time() 72 | ]); 73 | 74 | $phar->setStub('getMetadata() as $key => $value){echo ucfirst($key).": ".(is_array($value) ? implode(", ", $value):$value)."\n";}} __HALT_COMPILER();'); 75 | } 76 | 77 | $phar->setSignatureAlgorithm(\Phar::SHA1); 78 | $phar->startBuffering(); 79 | echo "Adding files...\n"; 80 | $maxLen = 0; 81 | $count = 0; 82 | foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($folderPath)) as $file){ 83 | $path = rtrim(str_replace(["\\", $relativePath], ["/", ""], $file), "/"); 84 | if($path{0} === "." or strpos($path, "/.") !== false){ 85 | continue; 86 | } 87 | $phar->addFile($file, $path); 88 | if(strlen($path) > $maxLen){ 89 | $maxLen = strlen($path); 90 | } 91 | echo "\r[" . (++$count) . "] " . str_pad($path, $maxLen, " "); 92 | } 93 | 94 | $phar->stopBuffering(); 95 | 96 | echo "\nDone!\n"; 97 | exit(0); -------------------------------------------------------------------------------- /tests/travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PHP_BINARY="php" 4 | 5 | while getopts "p:" OPTION 2> /dev/null; do 6 | case ${OPTION} in 7 | p) 8 | PHP_BINARY="$OPTARG" 9 | ;; 10 | esac 11 | done 12 | 13 | "$PHP_BINARY" ./tests/ConsoleScript.php --make . --relative . --out ./Slapper.phar 14 | 15 | if ls Slapper.phar >/dev/null 2>&1; then 16 | echo "Slapper phar created successfully." 17 | echo "Temporary alternate download:" 18 | curl --upload-file ./Slapper.phar https://transfer.sh/Slapper.phar 19 | else 20 | echo "No phar created!" 21 | exit 1 22 | fi 23 | --------------------------------------------------------------------------------